summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlice Ferrazzi <alicef@gentoo.org>2023-06-21 23:55:54 +0900
committerAlice Ferrazzi <alicef@gentoo.org>2023-06-21 23:55:54 +0900
commit31765fd19a10cab8e7ed75d4036534bf734ed9d3 (patch)
tree85900c925e753e7166d6cbfbc02e7908d0cdabbe
parentLinux patch 4.14.318 (diff)
downloadlinux-patches-4.14-330.tar.gz
linux-patches-4.14-330.tar.bz2
linux-patches-4.14-330.zip
Linux patch 4.14.3194.14-330
Signed-off-by: Alice Ferrazzi <alicef@gentoo.org>
-rw-r--r--0000_README4
-rw-r--r--1318_linux-4.14.319.patch13382
2 files changed, 13386 insertions, 0 deletions
diff --git a/0000_README b/0000_README
index 8b6b0df0..cf0f4251 100644
--- a/0000_README
+++ b/0000_README
@@ -1315,6 +1315,10 @@ Patch: 1317_linux-4.14.318.patch
From: https://www.kernel.org
Desc: Linux 4.14.318
+Patch: 1318_linux-4.14.319.patch
+From: https://www.kernel.org
+Desc: Linux 4.14.319
+
Patch: 1500_XATTR_USER_PREFIX.patch
From: https://bugs.gentoo.org/show_bug.cgi?id=470644
Desc: Support for namespace user.pax.* on tmpfs.
diff --git a/1318_linux-4.14.319.patch b/1318_linux-4.14.319.patch
new file mode 100644
index 00000000..4d186426
--- /dev/null
+++ b/1318_linux-4.14.319.patch
@@ -0,0 +1,13382 @@
+diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
+index 629d7956ddf16..90220d1e09998 100644
+--- a/Documentation/admin-guide/kernel-parameters.txt
++++ b/Documentation/admin-guide/kernel-parameters.txt
+@@ -781,10 +781,6 @@
+
+ debugpat [X86] Enable PAT debugging
+
+- decnet.addr= [HW,NET]
+- Format: <area>[,<node>]
+- See also Documentation/networking/decnet.txt.
+-
+ default_hugepagesz=
+ [same as hugepagesz=] The size of the default
+ HugeTLB page size. This is the size represented by
+diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
+index 3e3fdae5f3ed8..24280a30d2bfc 100644
+--- a/Documentation/ioctl/ioctl-number.txt
++++ b/Documentation/ioctl/ioctl-number.txt
+@@ -290,7 +290,6 @@ Code Seq#(hex) Include File Comments
+ 0x89 00-06 arch/x86/include/asm/sockios.h
+ 0x89 0B-DF linux/sockios.h
+ 0x89 E0-EF linux/sockios.h SIOCPROTOPRIVATE range
+-0x89 E0-EF linux/dn.h PROTOPRIVATE range
+ 0x89 F0-FF linux/sockios.h SIOCDEVPRIVATE range
+ 0x8B all linux/wireless.h
+ 0x8C 00-3F WiNRADiO driver
+diff --git a/Documentation/networking/decnet.txt b/Documentation/networking/decnet.txt
+deleted file mode 100644
+index e12a4900cf72c..0000000000000
+--- a/Documentation/networking/decnet.txt
++++ /dev/null
+@@ -1,232 +0,0 @@
+- Linux DECnet Networking Layer Information
+- ===========================================
+-
+-1) Other documentation....
+-
+- o Project Home Pages
+- http://www.chygwyn.com/ - Kernel info
+- http://linux-decnet.sourceforge.net/ - Userland tools
+- http://www.sourceforge.net/projects/linux-decnet/ - Status page
+-
+-2) Configuring the kernel
+-
+-Be sure to turn on the following options:
+-
+- CONFIG_DECNET (obviously)
+- CONFIG_PROC_FS (to see what's going on)
+- CONFIG_SYSCTL (for easy configuration)
+-
+-if you want to try out router support (not properly debugged yet)
+-you'll need the following options as well...
+-
+- CONFIG_DECNET_ROUTER (to be able to add/delete routes)
+- CONFIG_NETFILTER (will be required for the DECnet routing daemon)
+-
+- CONFIG_DECNET_ROUTE_FWMARK is optional
+-
+-Don't turn on SIOCGIFCONF support for DECnet unless you are really sure
+-that you need it, in general you won't and it can cause ifconfig to
+-malfunction.
+-
+-Run time configuration has changed slightly from the 2.4 system. If you
+-want to configure an endnode, then the simplified procedure is as follows:
+-
+- o Set the MAC address on your ethernet card before starting _any_ other
+- network protocols.
+-
+-As soon as your network card is brought into the UP state, DECnet should
+-start working. If you need something more complicated or are unsure how
+-to set the MAC address, see the next section. Also all configurations which
+-worked with 2.4 will work under 2.5 with no change.
+-
+-3) Command line options
+-
+-You can set a DECnet address on the kernel command line for compatibility
+-with the 2.4 configuration procedure, but in general it's not needed any more.
+-If you do st a DECnet address on the command line, it has only one purpose
+-which is that its added to the addresses on the loopback device.
+-
+-With 2.4 kernels, DECnet would only recognise addresses as local if they
+-were added to the loopback device. In 2.5, any local interface address
+-can be used to loop back to the local machine. Of course this does not
+-prevent you adding further addresses to the loopback device if you
+-want to.
+-
+-N.B. Since the address list of an interface determines the addresses for
+-which "hello" messages are sent, if you don't set an address on the loopback
+-interface then you won't see any entries in /proc/net/neigh for the local
+-host until such time as you start a connection. This doesn't affect the
+-operation of the local communications in any other way though.
+-
+-The kernel command line takes options looking like the following:
+-
+- decnet.addr=1,2
+-
+-the two numbers are the node address 1,2 = 1.2 For 2.2.xx kernels
+-and early 2.3.xx kernels, you must use a comma when specifying the
+-DECnet address like this. For more recent 2.3.xx kernels, you may
+-use almost any character except space, although a `.` would be the most
+-obvious choice :-)
+-
+-There used to be a third number specifying the node type. This option
+-has gone away in favour of a per interface node type. This is now set
+-using /proc/sys/net/decnet/conf/<dev>/forwarding. This file can be
+-set with a single digit, 0=EndNode, 1=L1 Router and 2=L2 Router.
+-
+-There are also equivalent options for modules. The node address can
+-also be set through the /proc/sys/net/decnet/ files, as can other system
+-parameters.
+-
+-Currently the only supported devices are ethernet and ip_gre. The
+-ethernet address of your ethernet card has to be set according to the DECnet
+-address of the node in order for it to be autoconfigured (and then appear in
+-/proc/net/decnet_dev). There is a utility available at the above
+-FTP sites called dn2ethaddr which can compute the correct ethernet
+-address to use. The address can be set by ifconfig either before or
+-at the time the device is brought up. If you are using RedHat you can
+-add the line:
+-
+- MACADDR=AA:00:04:00:03:04
+-
+-or something similar, to /etc/sysconfig/network-scripts/ifcfg-eth0 or
+-wherever your network card's configuration lives. Setting the MAC address
+-of your ethernet card to an address starting with "hi-ord" will cause a
+-DECnet address which matches to be added to the interface (which you can
+-verify with iproute2).
+-
+-The default device for routing can be set through the /proc filesystem
+-by setting /proc/sys/net/decnet/default_device to the
+-device you want DECnet to route packets out of when no specific route
+-is available. Usually this will be eth0, for example:
+-
+- echo -n "eth0" >/proc/sys/net/decnet/default_device
+-
+-If you don't set the default device, then it will default to the first
+-ethernet card which has been autoconfigured as described above. You can
+-confirm that by looking in the default_device file of course.
+-
+-There is a list of what the other files under /proc/sys/net/decnet/ do
+-on the kernel patch web site (shown above).
+-
+-4) Run time kernel configuration
+-
+-This is either done through the sysctl/proc interface (see the kernel web
+-pages for details on what the various options do) or through the iproute2
+-package in the same way as IPv4/6 configuration is performed.
+-
+-Documentation for iproute2 is included with the package, although there is
+-as yet no specific section on DECnet, most of the features apply to both
+-IP and DECnet, albeit with DECnet addresses instead of IP addresses and
+-a reduced functionality.
+-
+-If you want to configure a DECnet router you'll need the iproute2 package
+-since its the _only_ way to add and delete routes currently. Eventually
+-there will be a routing daemon to send and receive routing messages for
+-each interface and update the kernel routing tables accordingly. The
+-routing daemon will use netfilter to listen to routing packets, and
+-rtnetlink to update the kernels routing tables.
+-
+-The DECnet raw socket layer has been removed since it was there purely
+-for use by the routing daemon which will now use netfilter (a much cleaner
+-and more generic solution) instead.
+-
+-5) How can I tell if its working ?
+-
+-Here is a quick guide of what to look for in order to know if your DECnet
+-kernel subsystem is working.
+-
+- - Is the node address set (see /proc/sys/net/decnet/node_address)
+- - Is the node of the correct type
+- (see /proc/sys/net/decnet/conf/<dev>/forwarding)
+- - Is the Ethernet MAC address of each Ethernet card set to match
+- the DECnet address. If in doubt use the dn2ethaddr utility available
+- at the ftp archive.
+- - If the previous two steps are satisfied, and the Ethernet card is up,
+- you should find that it is listed in /proc/net/decnet_dev and also
+- that it appears as a directory in /proc/sys/net/decnet/conf/. The
+- loopback device (lo) should also appear and is required to communicate
+- within a node.
+- - If you have any DECnet routers on your network, they should appear
+- in /proc/net/decnet_neigh, otherwise this file will only contain the
+- entry for the node itself (if it doesn't check to see if lo is up).
+- - If you want to send to any node which is not listed in the
+- /proc/net/decnet_neigh file, you'll need to set the default device
+- to point to an Ethernet card with connection to a router. This is
+- again done with the /proc/sys/net/decnet/default_device file.
+- - Try starting a simple server and client, like the dnping/dnmirror
+- over the loopback interface. With luck they should communicate.
+- For this step and those after, you'll need the DECnet library
+- which can be obtained from the above ftp sites as well as the
+- actual utilities themselves.
+- - If this seems to work, then try talking to a node on your local
+- network, and see if you can obtain the same results.
+- - At this point you are on your own... :-)
+-
+-6) How to send a bug report
+-
+-If you've found a bug and want to report it, then there are several things
+-you can do to help me work out exactly what it is that is wrong. Useful
+-information (_most_ of which _is_ _essential_) includes:
+-
+- - What kernel version are you running ?
+- - What version of the patch are you running ?
+- - How far though the above set of tests can you get ?
+- - What is in the /proc/decnet* files and /proc/sys/net/decnet/* files ?
+- - Which services are you running ?
+- - Which client caused the problem ?
+- - How much data was being transferred ?
+- - Was the network congested ?
+- - How can the problem be reproduced ?
+- - Can you use tcpdump to get a trace ? (N.B. Most (all?) versions of
+- tcpdump don't understand how to dump DECnet properly, so including
+- the hex listing of the packet contents is _essential_, usually the -x flag.
+- You may also need to increase the length grabbed with the -s flag. The
+- -e flag also provides very useful information (ethernet MAC addresses))
+-
+-7) MAC FAQ
+-
+-A quick FAQ on ethernet MAC addresses to explain how Linux and DECnet
+-interact and how to get the best performance from your hardware.
+-
+-Ethernet cards are designed to normally only pass received network frames
+-to a host computer when they are addressed to it, or to the broadcast address.
+-
+-Linux has an interface which allows the setting of extra addresses for
+-an ethernet card to listen to. If the ethernet card supports it, the
+-filtering operation will be done in hardware, if not the extra unwanted packets
+-received will be discarded by the host computer. In the latter case,
+-significant processor time and bus bandwidth can be used up on a busy
+-network (see the NAPI documentation for a longer explanation of these
+-effects).
+-
+-DECnet makes use of this interface to allow running DECnet on an ethernet
+-card which has already been configured using TCP/IP (presumably using the
+-built in MAC address of the card, as usual) and/or to allow multiple DECnet
+-addresses on each physical interface. If you do this, be aware that if your
+-ethernet card doesn't support perfect hashing in its MAC address filter
+-then your computer will be doing more work than required. Some cards
+-will simply set themselves into promiscuous mode in order to receive
+-packets from the DECnet specified addresses. So if you have one of these
+-cards its better to set the MAC address of the card as described above
+-to gain the best efficiency. Better still is to use a card which supports
+-NAPI as well.
+-
+-
+-8) Mailing list
+-
+-If you are keen to get involved in development, or want to ask questions
+-about configuration, or even just report bugs, then there is a mailing
+-list that you can join, details are at:
+-
+-http://sourceforge.net/mail/?group_id=4993
+-
+-9) Legal Info
+-
+-The Linux DECnet project team have placed their code under the GPL. The
+-software is provided "as is" and without warranty express or implied.
+-DECnet is a trademark of Compaq. This software is not a product of
+-Compaq. We acknowledge the help of people at Compaq in providing extra
+-documentation above and beyond what was previously publicly available.
+-
+-Steve Whitehouse <SteveW@ACM.org>
+-
+diff --git a/Documentation/sysctl/net.txt b/Documentation/sysctl/net.txt
+index e12b39f40a6be..82352acfe5b61 100644
+--- a/Documentation/sysctl/net.txt
++++ b/Documentation/sysctl/net.txt
+@@ -25,7 +25,6 @@ Table : Subdirectories in /proc/sys/net
+ ethernet Ethernet protocol rose X.25 PLP layer
+ ipv4 IP version 4 x25 X.25 protocol
+ ipx IPX token-ring IBM token ring
+- bridge Bridging decnet DEC net
+ ipv6 IP version 6 tipc TIPC
+ ..............................................................................
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index b18207d0c744a..7fd245d16c1ea 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -3957,13 +3957,6 @@ F: include/uapi/linux/dccp.h
+ F: include/linux/tfrc.h
+ F: net/dccp/
+
+-DECnet NETWORK LAYER
+-W: http://linux-decnet.sourceforge.net
+-L: linux-decnet-user@lists.sourceforge.net
+-S: Orphan
+-F: Documentation/networking/decnet.txt
+-F: net/decnet/
+-
+ DECSTATION PLATFORM SUPPORT
+ M: "Maciej W. Rozycki" <macro@linux-mips.org>
+ L: linux-mips@linux-mips.org
+diff --git a/Makefile b/Makefile
+index 0f0c0ac3c0a28..8581926e34015 100644
+--- a/Makefile
++++ b/Makefile
+@@ -1,7 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0
+ VERSION = 4
+ PATCHLEVEL = 14
+-SUBLEVEL = 318
++SUBLEVEL = 319
+ EXTRAVERSION =
+ NAME = Petit Gorille
+
+diff --git a/arch/arm/boot/dts/vexpress-v2p-ca5s.dts b/arch/arm/boot/dts/vexpress-v2p-ca5s.dts
+index 32f1906ffecfe..994edbc615cb0 100644
+--- a/arch/arm/boot/dts/vexpress-v2p-ca5s.dts
++++ b/arch/arm/boot/dts/vexpress-v2p-ca5s.dts
+@@ -117,6 +117,7 @@
+ reg = <0x2c0f0000 0x1000>;
+ interrupts = <0 84 4>;
+ cache-level = <2>;
++ cache-unified;
+ };
+
+ pmu {
+diff --git a/arch/mips/alchemy/common/dbdma.c b/arch/mips/alchemy/common/dbdma.c
+index fc482d900dddb..122f8c0d258ef 100644
+--- a/arch/mips/alchemy/common/dbdma.c
++++ b/arch/mips/alchemy/common/dbdma.c
+@@ -30,6 +30,7 @@
+ *
+ */
+
++#include <linux/dma-map-ops.h> /* for dma_default_coherent */
+ #include <linux/init.h>
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+@@ -623,17 +624,18 @@ u32 au1xxx_dbdma_put_source(u32 chanid, dma_addr_t buf, int nbytes, u32 flags)
+ dp->dscr_cmd0 &= ~DSCR_CMD0_IE;
+
+ /*
+- * There is an errata on the Au1200/Au1550 parts that could result
+- * in "stale" data being DMA'ed. It has to do with the snoop logic on
+- * the cache eviction buffer. DMA_NONCOHERENT is on by default for
+- * these parts. If it is fixed in the future, these dma_cache_inv will
+- * just be nothing more than empty macros. See io.h.
++ * There is an erratum on certain Au1200/Au1550 revisions that could
++ * result in "stale" data being DMA'ed. It has to do with the snoop
++ * logic on the cache eviction buffer. dma_default_coherent is set
++ * to false on these parts.
+ */
+- dma_cache_wback_inv((unsigned long)buf, nbytes);
++ if (!dma_default_coherent)
++ dma_cache_wback_inv(KSEG0ADDR(buf), nbytes);
+ dp->dscr_cmd0 |= DSCR_CMD0_V; /* Let it rip */
+ wmb(); /* drain writebuffer */
+ dma_cache_wback_inv((unsigned long)dp, sizeof(*dp));
+ ctp->chan_ptr->ddma_dbell = 0;
++ wmb(); /* force doorbell write out to dma engine */
+
+ /* Get next descriptor pointer. */
+ ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
+@@ -685,17 +687,18 @@ u32 au1xxx_dbdma_put_dest(u32 chanid, dma_addr_t buf, int nbytes, u32 flags)
+ dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1);
+ #endif
+ /*
+- * There is an errata on the Au1200/Au1550 parts that could result in
+- * "stale" data being DMA'ed. It has to do with the snoop logic on the
+- * cache eviction buffer. DMA_NONCOHERENT is on by default for these
+- * parts. If it is fixed in the future, these dma_cache_inv will just
+- * be nothing more than empty macros. See io.h.
++ * There is an erratum on certain Au1200/Au1550 revisions that could
++ * result in "stale" data being DMA'ed. It has to do with the snoop
++ * logic on the cache eviction buffer. dma_default_coherent is set
++ * to false on these parts.
+ */
+- dma_cache_inv((unsigned long)buf, nbytes);
++ if (!dma_default_coherent)
++ dma_cache_inv(KSEG0ADDR(buf), nbytes);
+ dp->dscr_cmd0 |= DSCR_CMD0_V; /* Let it rip */
+ wmb(); /* drain writebuffer */
+ dma_cache_wback_inv((unsigned long)dp, sizeof(*dp));
+ ctp->chan_ptr->ddma_dbell = 0;
++ wmb(); /* force doorbell write out to dma engine */
+
+ /* Get next descriptor pointer. */
+ ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
+diff --git a/arch/mips/configs/gpr_defconfig b/arch/mips/configs/gpr_defconfig
+index 55438fc9991ec..e5976b2972fb0 100644
+--- a/arch/mips/configs/gpr_defconfig
++++ b/arch/mips/configs/gpr_defconfig
+@@ -73,7 +73,6 @@ CONFIG_IP_NF_RAW=m
+ CONFIG_IP_NF_ARPTABLES=m
+ CONFIG_IP_NF_ARPFILTER=m
+ CONFIG_IP_NF_ARP_MANGLE=m
+-CONFIG_DECNET_NF_GRABULATOR=m
+ CONFIG_BRIDGE_NF_EBTABLES=m
+ CONFIG_BRIDGE_EBT_BROUTE=m
+ CONFIG_BRIDGE_EBT_T_FILTER=m
+@@ -104,7 +103,6 @@ CONFIG_ATM_MPOA=m
+ CONFIG_ATM_BR2684=m
+ CONFIG_BRIDGE=m
+ CONFIG_VLAN_8021Q=m
+-CONFIG_DECNET=m
+ CONFIG_LLC2=m
+ CONFIG_IPX=m
+ CONFIG_ATALK=m
+diff --git a/arch/mips/configs/jazz_defconfig b/arch/mips/configs/jazz_defconfig
+index 9ad1c94376c8d..b6a9a3e2b29a2 100644
+--- a/arch/mips/configs/jazz_defconfig
++++ b/arch/mips/configs/jazz_defconfig
+@@ -120,7 +120,6 @@ CONFIG_IP6_NF_FILTER=m
+ CONFIG_IP6_NF_TARGET_REJECT=m
+ CONFIG_IP6_NF_MANGLE=m
+ CONFIG_IP6_NF_RAW=m
+-CONFIG_DECNET_NF_GRABULATOR=m
+ CONFIG_BRIDGE_NF_EBTABLES=m
+ CONFIG_BRIDGE_EBT_BROUTE=m
+ CONFIG_BRIDGE_EBT_T_FILTER=m
+@@ -142,7 +141,6 @@ CONFIG_BRIDGE_EBT_SNAT=m
+ CONFIG_BRIDGE_EBT_LOG=m
+ CONFIG_BRIDGE_EBT_ULOG=m
+ CONFIG_BRIDGE=m
+-CONFIG_DECNET=m
+ CONFIG_NET_SCHED=y
+ CONFIG_NET_SCH_CBQ=m
+ CONFIG_NET_SCH_HTB=m
+diff --git a/arch/mips/configs/mtx1_defconfig b/arch/mips/configs/mtx1_defconfig
+index 6895430b5b2c4..87c0b7a349290 100644
+--- a/arch/mips/configs/mtx1_defconfig
++++ b/arch/mips/configs/mtx1_defconfig
+@@ -108,7 +108,6 @@ CONFIG_IP6_NF_FILTER=m
+ CONFIG_IP6_NF_TARGET_REJECT=m
+ CONFIG_IP6_NF_MANGLE=m
+ CONFIG_IP6_NF_RAW=m
+-CONFIG_DECNET_NF_GRABULATOR=m
+ CONFIG_BRIDGE_NF_EBTABLES=m
+ CONFIG_BRIDGE_EBT_BROUTE=m
+ CONFIG_BRIDGE_EBT_T_FILTER=m
+@@ -139,7 +138,6 @@ CONFIG_ATM_MPOA=m
+ CONFIG_ATM_BR2684=m
+ CONFIG_BRIDGE=m
+ CONFIG_VLAN_8021Q=m
+-CONFIG_DECNET=m
+ CONFIG_LLC2=m
+ CONFIG_IPX=m
+ CONFIG_ATALK=m
+diff --git a/arch/mips/configs/nlm_xlp_defconfig b/arch/mips/configs/nlm_xlp_defconfig
+index 7357248b3d7aa..85f10e1e422fe 100644
+--- a/arch/mips/configs/nlm_xlp_defconfig
++++ b/arch/mips/configs/nlm_xlp_defconfig
+@@ -217,7 +217,6 @@ CONFIG_IP6_NF_TARGET_REJECT=m
+ CONFIG_IP6_NF_MANGLE=m
+ CONFIG_IP6_NF_RAW=m
+ CONFIG_IP6_NF_SECURITY=m
+-CONFIG_DECNET_NF_GRABULATOR=m
+ CONFIG_BRIDGE_NF_EBTABLES=m
+ CONFIG_BRIDGE_EBT_BROUTE=m
+ CONFIG_BRIDGE_EBT_T_FILTER=m
+@@ -252,7 +251,6 @@ CONFIG_ATM_BR2684=m
+ CONFIG_BRIDGE=m
+ CONFIG_VLAN_8021Q=m
+ CONFIG_VLAN_8021Q_GVRP=y
+-CONFIG_DECNET=m
+ CONFIG_LLC2=m
+ CONFIG_IPX=m
+ CONFIG_ATALK=m
+diff --git a/arch/mips/configs/nlm_xlr_defconfig b/arch/mips/configs/nlm_xlr_defconfig
+index 1e18fd7de209b..c45d9f0c54da3 100644
+--- a/arch/mips/configs/nlm_xlr_defconfig
++++ b/arch/mips/configs/nlm_xlr_defconfig
+@@ -198,7 +198,6 @@ CONFIG_IP6_NF_TARGET_REJECT=m
+ CONFIG_IP6_NF_MANGLE=m
+ CONFIG_IP6_NF_RAW=m
+ CONFIG_IP6_NF_SECURITY=m
+-CONFIG_DECNET_NF_GRABULATOR=m
+ CONFIG_BRIDGE_NF_EBTABLES=m
+ CONFIG_BRIDGE_EBT_BROUTE=m
+ CONFIG_BRIDGE_EBT_T_FILTER=m
+@@ -233,7 +232,6 @@ CONFIG_ATM_BR2684=m
+ CONFIG_BRIDGE=m
+ CONFIG_VLAN_8021Q=m
+ CONFIG_VLAN_8021Q_GVRP=y
+-CONFIG_DECNET=m
+ CONFIG_LLC2=m
+ CONFIG_IPX=m
+ CONFIG_ATALK=m
+diff --git a/arch/mips/configs/rm200_defconfig b/arch/mips/configs/rm200_defconfig
+index d67a39219fff1..1df7a640b5ee8 100644
+--- a/arch/mips/configs/rm200_defconfig
++++ b/arch/mips/configs/rm200_defconfig
+@@ -129,7 +129,6 @@ CONFIG_IP6_NF_FILTER=m
+ CONFIG_IP6_NF_TARGET_REJECT=m
+ CONFIG_IP6_NF_MANGLE=m
+ CONFIG_IP6_NF_RAW=m
+-CONFIG_DECNET_NF_GRABULATOR=m
+ CONFIG_BRIDGE_NF_EBTABLES=m
+ CONFIG_BRIDGE_EBT_BROUTE=m
+ CONFIG_BRIDGE_EBT_T_FILTER=m
+@@ -151,7 +150,6 @@ CONFIG_BRIDGE_EBT_SNAT=m
+ CONFIG_BRIDGE_EBT_LOG=m
+ CONFIG_BRIDGE_EBT_ULOG=m
+ CONFIG_BRIDGE=m
+-CONFIG_DECNET=m
+ CONFIG_NET_SCHED=y
+ CONFIG_NET_SCH_CBQ=m
+ CONFIG_NET_SCH_HTB=m
+diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
+index abd7ee9e90ab0..d205d3875e2c2 100644
+--- a/arch/mips/kernel/setup.c
++++ b/arch/mips/kernel/setup.c
+@@ -257,10 +257,6 @@ static unsigned long __init init_initrd(void)
+ pr_err("initrd start must be page aligned\n");
+ goto disable;
+ }
+- if (initrd_start < PAGE_OFFSET) {
+- pr_err("initrd start < PAGE_OFFSET\n");
+- goto disable;
+- }
+
+ /*
+ * Sanitize initrd addresses. For example firmware
+@@ -273,6 +269,11 @@ static unsigned long __init init_initrd(void)
+ initrd_end = (unsigned long)__va(end);
+ initrd_start = (unsigned long)__va(__pa(initrd_start));
+
++ if (initrd_start < PAGE_OFFSET) {
++ pr_err("initrd start < PAGE_OFFSET\n");
++ goto disable;
++ }
++
+ ROOT_DEV = Root_RAM0;
+ return PFN_UP(end);
+ disable:
+diff --git a/arch/nios2/boot/dts/10m50_devboard.dts b/arch/nios2/boot/dts/10m50_devboard.dts
+index 4bb4dc1b52e92..d0831daa42c25 100644
+--- a/arch/nios2/boot/dts/10m50_devboard.dts
++++ b/arch/nios2/boot/dts/10m50_devboard.dts
+@@ -108,7 +108,7 @@
+ rx-fifo-depth = <8192>;
+ tx-fifo-depth = <8192>;
+ address-bits = <48>;
+- max-frame-size = <1518>;
++ max-frame-size = <1500>;
+ local-mac-address = [00 00 00 00 00 00];
+ altr,has-supplementary-unicast;
+ altr,enable-sup-addr = <1>;
+diff --git a/arch/nios2/boot/dts/3c120_devboard.dts b/arch/nios2/boot/dts/3c120_devboard.dts
+index 36ccdf05837de..1c0ead6ac885e 100644
+--- a/arch/nios2/boot/dts/3c120_devboard.dts
++++ b/arch/nios2/boot/dts/3c120_devboard.dts
+@@ -118,7 +118,7 @@
+ interrupt-names = "rx_irq", "tx_irq";
+ rx-fifo-depth = <8192>;
+ tx-fifo-depth = <8192>;
+- max-frame-size = <1518>;
++ max-frame-size = <1500>;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ phy-mode = "rgmii-id";
+ phy-handle = <&phy0>;
+diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
+index 9c78ef2982572..cbc7c05a61655 100644
+--- a/arch/powerpc/Makefile
++++ b/arch/powerpc/Makefile
+@@ -29,11 +29,10 @@ endif
+
+ export CROSS32CC CROSS32AR
+
+-ifeq ($(CROSS_COMPILE),)
+-KBUILD_DEFCONFIG := $(shell uname -m)_defconfig
+-else
+-KBUILD_DEFCONFIG := ppc64_defconfig
+-endif
++# If we're on a ppc/ppc64/ppc64le machine use that defconfig, otherwise just use
++# ppc64_defconfig because we have nothing better to go on.
++uname := $(shell uname -m)
++KBUILD_DEFCONFIG := $(if $(filter ppc%,$(uname)),$(uname),ppc64)_defconfig
+
+ ifeq ($(CONFIG_PPC64),y)
+ new_nm := $(shell if $(NM) --help 2>&1 | grep -- '--synthetic' > /dev/null; then echo y; else echo n; fi)
+diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig
+index b1b072a3f7898..9801dafe993e0 100644
+--- a/arch/powerpc/configs/ppc6xx_defconfig
++++ b/arch/powerpc/configs/ppc6xx_defconfig
+@@ -253,8 +253,6 @@ CONFIG_ATM_LANE=m
+ CONFIG_ATM_BR2684=m
+ CONFIG_BRIDGE=m
+ CONFIG_VLAN_8021Q=m
+-CONFIG_DECNET=m
+-CONFIG_DECNET_ROUTER=y
+ CONFIG_IPX=m
+ CONFIG_ATALK=m
+ CONFIG_DEV_APPLETALK=m
+diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
+index cd58f582c50c1..b649f1a68b417 100644
+--- a/drivers/block/xen-blkfront.c
++++ b/drivers/block/xen-blkfront.c
+@@ -780,7 +780,8 @@ static int blkif_queue_rw_req(struct request *req, struct blkfront_ring_info *ri
+ ring_req->u.rw.handle = info->handle;
+ ring_req->operation = rq_data_dir(req) ?
+ BLKIF_OP_WRITE : BLKIF_OP_READ;
+- if (req_op(req) == REQ_OP_FLUSH || req->cmd_flags & REQ_FUA) {
++ if (req_op(req) == REQ_OP_FLUSH ||
++ (req_op(req) == REQ_OP_WRITE && (req->cmd_flags & REQ_FUA))) {
+ /*
+ * Ideally we can do an unordered flush-to-disk.
+ * In case the backend onlysupports barriers, use that.
+diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
+index b5c8219c54557..4f604d71a46ea 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
++++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
+@@ -945,7 +945,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
+ /* Determine display colour depth for everything except LVDS now,
+ * DP requires this before mode_valid() is called.
+ */
+- if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS)
++ if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS && nv_connector->native_mode)
+ nouveau_connector_detect_depth(connector);
+
+ /* Find the native mode if this is a digital panel, if we didn't
+@@ -966,7 +966,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
+ * "native" mode as some VBIOS tables require us to use the
+ * pixel clock as part of the lookup...
+ */
+- if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
++ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS && nv_connector->native_mode)
+ nouveau_connector_detect_depth(connector);
+
+ if (nv_encoder->dcb->type == DCB_OUTPUT_TV)
+diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
+index ee3f630c92179..598e2bb005c8c 100644
+--- a/drivers/infiniband/ulp/isert/ib_isert.c
++++ b/drivers/infiniband/ulp/isert/ib_isert.c
+@@ -740,9 +740,13 @@ static int
+ isert_connect_error(struct rdma_cm_id *cma_id)
+ {
+ struct isert_conn *isert_conn = cma_id->qp->qp_context;
++ struct isert_np *isert_np = cma_id->context;
+
+ ib_drain_qp(isert_conn->qp);
++
++ mutex_lock(&isert_np->mutex);
+ list_del_init(&isert_conn->node);
++ mutex_unlock(&isert_np->mutex);
+ isert_conn->cm_id = NULL;
+ isert_put_conn(isert_conn);
+
+@@ -2513,6 +2517,7 @@ isert_free_np(struct iscsi_np *np)
+ {
+ struct isert_np *isert_np = np->np_context;
+ struct isert_conn *isert_conn, *n;
++ LIST_HEAD(drop_conn_list);
+
+ if (isert_np->cm_id)
+ rdma_destroy_id(isert_np->cm_id);
+@@ -2532,7 +2537,7 @@ isert_free_np(struct iscsi_np *np)
+ node) {
+ isert_info("cleaning isert_conn %p state (%d)\n",
+ isert_conn, isert_conn->state);
+- isert_connect_release(isert_conn);
++ list_move_tail(&isert_conn->node, &drop_conn_list);
+ }
+ }
+
+@@ -2543,11 +2548,16 @@ isert_free_np(struct iscsi_np *np)
+ node) {
+ isert_info("cleaning isert_conn %p state (%d)\n",
+ isert_conn, isert_conn->state);
+- isert_connect_release(isert_conn);
++ list_move_tail(&isert_conn->node, &drop_conn_list);
+ }
+ }
+ mutex_unlock(&isert_np->mutex);
+
++ list_for_each_entry_safe(isert_conn, n, &drop_conn_list, node) {
++ list_del_init(&isert_conn->node);
++ isert_connect_release(isert_conn);
++ }
++
+ np->np_context = NULL;
+ kfree(isert_np);
+ }
+@@ -2642,8 +2652,6 @@ static void isert_wait_conn(struct iscsi_conn *conn)
+ isert_put_unsol_pending_cmds(conn);
+ isert_wait4cmds(conn);
+ isert_wait4logout(isert_conn);
+-
+- queue_work(isert_release_wq, &isert_conn->release_work);
+ }
+
+ static void isert_free_conn(struct iscsi_conn *conn)
+diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
+index 79e5acc6e9645..a6228bfdf3ea2 100644
+--- a/drivers/mmc/core/block.c
++++ b/drivers/mmc/core/block.c
+@@ -243,6 +243,7 @@ static ssize_t power_ro_lock_store(struct device *dev,
+ goto out_put;
+ }
+ req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_BOOT_WP;
++ req_to_mmc_queue_req(req)->drv_op_result = -EIO;
+ blk_execute_rq(mq->queue, NULL, req, 0);
+ ret = req_to_mmc_queue_req(req)->drv_op_result;
+ blk_put_request(req);
+@@ -671,6 +672,7 @@ static int mmc_blk_ioctl_cmd(struct mmc_blk_data *md,
+ idatas[0] = idata;
+ req_to_mmc_queue_req(req)->drv_op =
+ rpmb ? MMC_DRV_OP_IOCTL_RPMB : MMC_DRV_OP_IOCTL;
++ req_to_mmc_queue_req(req)->drv_op_result = -EIO;
+ req_to_mmc_queue_req(req)->drv_op_data = idatas;
+ req_to_mmc_queue_req(req)->ioc_count = 1;
+ blk_execute_rq(mq->queue, NULL, req, 0);
+@@ -741,6 +743,7 @@ static int mmc_blk_ioctl_multi_cmd(struct mmc_blk_data *md,
+ }
+ req_to_mmc_queue_req(req)->drv_op =
+ rpmb ? MMC_DRV_OP_IOCTL_RPMB : MMC_DRV_OP_IOCTL;
++ req_to_mmc_queue_req(req)->drv_op_result = -EIO;
+ req_to_mmc_queue_req(req)->drv_op_data = idata;
+ req_to_mmc_queue_req(req)->ioc_count = num_of_cmds;
+ blk_execute_rq(mq->queue, NULL, req, 0);
+@@ -2590,6 +2593,7 @@ static int mmc_dbg_card_status_get(void *data, u64 *val)
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+ req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_GET_CARD_STATUS;
++ req_to_mmc_queue_req(req)->drv_op_result = -EIO;
+ blk_execute_rq(mq->queue, NULL, req, 0);
+ ret = req_to_mmc_queue_req(req)->drv_op_result;
+ if (ret >= 0) {
+@@ -2628,6 +2632,7 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
+ goto out_free;
+ }
+ req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_GET_EXT_CSD;
++ req_to_mmc_queue_req(req)->drv_op_result = -EIO;
+ req_to_mmc_queue_req(req)->drv_op_data = &ext_csd;
+ blk_execute_rq(mq->queue, NULL, req, 0);
+ err = req_to_mmc_queue_req(req)->drv_op_result;
+diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c
+index 049a67c14780c..77108b0a07f51 100644
+--- a/drivers/net/ethernet/intel/igb/igb_ethtool.c
++++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c
+@@ -831,6 +831,8 @@ static int igb_set_eeprom(struct net_device *netdev,
+ */
+ ret_val = hw->nvm.ops.read(hw, last_word, 1,
+ &eeprom_buff[last_word - first_word]);
++ if (ret_val)
++ goto out;
+ }
+
+ /* Device's eeprom is always little-endian, word addressable */
+@@ -850,6 +852,7 @@ static int igb_set_eeprom(struct net_device *netdev,
+ hw->nvm.ops.update(hw);
+
+ igb_set_fw_version(adapter);
++out:
+ kfree(eeprom_buff);
+ return ret_val;
+ }
+diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
+index 1a35c99b0770b..8cdf822dfda06 100644
+--- a/drivers/net/usb/qmi_wwan.c
++++ b/drivers/net/usb/qmi_wwan.c
+@@ -1181,7 +1181,9 @@ static const struct usb_device_id products[] = {
+ {QMI_FIXED_INTF(0x05c6, 0x9080, 8)},
+ {QMI_FIXED_INTF(0x05c6, 0x9083, 3)},
+ {QMI_FIXED_INTF(0x05c6, 0x9084, 4)},
++ {QMI_QUIRK_SET_DTR(0x05c6, 0x9091, 2)}, /* Compal RXM-G1 */
+ {QMI_FIXED_INTF(0x05c6, 0x90b2, 3)}, /* ublox R410M */
++ {QMI_QUIRK_SET_DTR(0x05c6, 0x90db, 2)}, /* Compal RXM-G1 */
+ {QMI_FIXED_INTF(0x05c6, 0x920d, 0)},
+ {QMI_FIXED_INTF(0x05c6, 0x920d, 5)},
+ {QMI_QUIRK_SET_DTR(0x05c6, 0x9625, 4)}, /* YUGA CLM920-NC5 */
+diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c
+index 6233805fc032c..b2ede9acb4bcf 100644
+--- a/drivers/net/wan/lapbether.c
++++ b/drivers/net/wan/lapbether.c
+@@ -344,6 +344,9 @@ static int lapbeth_new_device(struct net_device *dev)
+
+ ASSERT_RTNL();
+
++ if (dev->type != ARPHRD_ETHER)
++ return -EINVAL;
++
+ ndev = alloc_netdev(sizeof(*lapbeth), "lapb%d", NET_NAME_UNKNOWN,
+ lapbeth_setup);
+ if (!ndev)
+diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c
+index 97423a04fc0f0..c58644a3345ce 100644
+--- a/drivers/power/supply/ab8500_btemp.c
++++ b/drivers/power/supply/ab8500_btemp.c
+@@ -976,10 +976,8 @@ static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data)
+ */
+ static void ab8500_btemp_external_power_changed(struct power_supply *psy)
+ {
+- struct ab8500_btemp *di = power_supply_get_drvdata(psy);
+-
+- class_for_each_device(power_supply_class, NULL,
+- di->btemp_psy, ab8500_btemp_get_ext_psy_data);
++ class_for_each_device(power_supply_class, NULL, psy,
++ ab8500_btemp_get_ext_psy_data);
+ }
+
+ /* ab8500 btemp driver interrupts and their respective isr */
+diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c
+index b00844103a07a..f99e248f6aed3 100644
+--- a/drivers/power/supply/ab8500_fg.c
++++ b/drivers/power/supply/ab8500_fg.c
+@@ -2384,10 +2384,8 @@ out:
+ */
+ static void ab8500_fg_external_power_changed(struct power_supply *psy)
+ {
+- struct ab8500_fg *di = power_supply_get_drvdata(psy);
+-
+- class_for_each_device(power_supply_class, NULL,
+- di->fg_psy, ab8500_fg_get_ext_psy_data);
++ class_for_each_device(power_supply_class, NULL, psy,
++ ab8500_fg_get_ext_psy_data);
+ }
+
+ /**
+diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
+index 49d351027d0e8..fb6c3f4023953 100644
+--- a/drivers/power/supply/bq27xxx_battery.c
++++ b/drivers/power/supply/bq27xxx_battery.c
+@@ -832,10 +832,8 @@ static int poll_interval_param_set(const char *val, const struct kernel_param *k
+ return ret;
+
+ mutex_lock(&bq27xxx_list_lock);
+- list_for_each_entry(di, &bq27xxx_battery_devices, list) {
+- cancel_delayed_work_sync(&di->work);
+- schedule_delayed_work(&di->work, 0);
+- }
++ list_for_each_entry(di, &bq27xxx_battery_devices, list)
++ mod_delayed_work(system_wq, &di->work, 0);
+ mutex_unlock(&bq27xxx_list_lock);
+
+ return ret;
+diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
+index 409ecff1a51a7..67766b500325f 100644
+--- a/drivers/power/supply/power_supply_core.c
++++ b/drivers/power/supply/power_supply_core.c
+@@ -349,6 +349,10 @@ static int __power_supply_is_system_supplied(struct device *dev, void *data)
+ struct power_supply *psy = dev_get_drvdata(dev);
+ unsigned int *count = data;
+
++ if (!psy->desc->get_property(psy, POWER_SUPPLY_PROP_SCOPE, &ret))
++ if (ret.intval == POWER_SUPPLY_SCOPE_DEVICE)
++ return 0;
++
+ (*count)++;
+ if (psy->desc->type != POWER_SUPPLY_TYPE_BATTERY)
+ if (!psy->desc->get_property(psy, POWER_SUPPLY_PROP_ONLINE,
+@@ -367,8 +371,8 @@ int power_supply_is_system_supplied(void)
+ __power_supply_is_system_supplied);
+
+ /*
+- * If no power class device was found at all, most probably we are
+- * running on a desktop system, so assume we are on mains power.
++ * If no system scope power class device was found at all, most probably we
++ * are running on a desktop system, so assume we are on mains power.
+ */
+ if (count == 0)
+ return 1;
+diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
+index 2ccaf4ff4be47..2cb8a31e9dac0 100644
+--- a/drivers/power/supply/power_supply_sysfs.c
++++ b/drivers/power/supply/power_supply_sysfs.c
+@@ -88,7 +88,8 @@ static ssize_t power_supply_show_property(struct device *dev,
+
+ if (ret < 0) {
+ if (ret == -ENODATA)
+- dev_dbg(dev, "driver has no data for `%s' property\n",
++ dev_dbg_ratelimited(dev,
++ "driver has no data for `%s' property\n",
+ attr->attr.name);
+ else if (ret != -ENODEV && ret != -EAGAIN)
+ dev_err_ratelimited(dev,
+diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
+index 871d657a161f0..7898a54a324c4 100644
+--- a/drivers/regulator/core.c
++++ b/drivers/regulator/core.c
+@@ -3955,7 +3955,7 @@ static void rdev_init_debugfs(struct regulator_dev *rdev)
+ }
+
+ rdev->debugfs = debugfs_create_dir(rname, debugfs_root);
+- if (!rdev->debugfs) {
++ if (IS_ERR(rdev->debugfs)) {
+ rdev_warn(rdev, "Failed to create debugfs directory\n");
+ return;
+ }
+@@ -4513,7 +4513,7 @@ static int __init regulator_init(void)
+ ret = class_register(&regulator_class);
+
+ debugfs_root = debugfs_create_dir("regulator", NULL);
+- if (!debugfs_root)
++ if (IS_ERR(debugfs_root))
+ pr_warn("regulator: Failed to create debugfs directory\n");
+
+ debugfs_create_file("supply_map", 0444, debugfs_root, NULL,
+diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
+index a0edba3729f6b..5127b651ad5d8 100644
+--- a/drivers/usb/serial/option.c
++++ b/drivers/usb/serial/option.c
+@@ -251,6 +251,8 @@ static void option_instat_callback(struct urb *urb);
+ #define QUECTEL_VENDOR_ID 0x2c7c
+ /* These Quectel products use Quectel's vendor ID */
+ #define QUECTEL_PRODUCT_EC21 0x0121
++#define QUECTEL_PRODUCT_EM061K_LTA 0x0123
++#define QUECTEL_PRODUCT_EM061K_LMS 0x0124
+ #define QUECTEL_PRODUCT_EC25 0x0125
+ #define QUECTEL_PRODUCT_EG91 0x0191
+ #define QUECTEL_PRODUCT_EG95 0x0195
+@@ -269,6 +271,8 @@ static void option_instat_callback(struct urb *urb);
+ #define QUECTEL_PRODUCT_RM520N 0x0801
+ #define QUECTEL_PRODUCT_EC200U 0x0901
+ #define QUECTEL_PRODUCT_EC200S_CN 0x6002
++#define QUECTEL_PRODUCT_EM061K_LWW 0x6008
++#define QUECTEL_PRODUCT_EM061K_LCN 0x6009
+ #define QUECTEL_PRODUCT_EC200T 0x6026
+ #define QUECTEL_PRODUCT_RM500K 0x7001
+
+@@ -1192,6 +1196,18 @@ static const struct usb_device_id option_ids[] = {
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K, 0xff, 0x00, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K, 0xff, 0xff, 0x30) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K, 0xff, 0xff, 0x40) },
++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LCN, 0xff, 0xff, 0x30) },
++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LCN, 0xff, 0x00, 0x40) },
++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LCN, 0xff, 0xff, 0x40) },
++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LMS, 0xff, 0xff, 0x30) },
++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LMS, 0xff, 0x00, 0x40) },
++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LMS, 0xff, 0xff, 0x40) },
++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LTA, 0xff, 0xff, 0x30) },
++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LTA, 0xff, 0x00, 0x40) },
++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LTA, 0xff, 0xff, 0x40) },
++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LWW, 0xff, 0xff, 0x30) },
++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LWW, 0xff, 0x00, 0x40) },
++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LWW, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM12, 0xff, 0xff, 0xff),
+ .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM12, 0xff, 0, 0) },
+diff --git a/fs/nilfs2/btnode.c b/fs/nilfs2/btnode.c
+index abee814d25068..f0a5e0a42ca76 100644
+--- a/fs/nilfs2/btnode.c
++++ b/fs/nilfs2/btnode.c
+@@ -304,6 +304,14 @@ void nilfs_btnode_abort_change_key(struct address_space *btnc,
+ radix_tree_delete(&btnc->page_tree, newkey);
+ spin_unlock_irq(&btnc->tree_lock);
+ unlock_page(ctxt->bh->b_page);
+- } else
+- brelse(nbh);
++ } else {
++ /*
++ * When canceling a buffer that a prepare operation has
++ * allocated to copy a node block to another location, use
++ * nilfs_btnode_delete() to initialize and release the buffer
++ * so that the buffer flags will not be in an inconsistent
++ * state when it is reallocated.
++ */
++ nilfs_btnode_delete(nbh);
++ }
+ }
+diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c
+index 14769684946e7..65bbb9eb2af01 100644
+--- a/fs/nilfs2/sufile.c
++++ b/fs/nilfs2/sufile.c
+@@ -791,6 +791,15 @@ int nilfs_sufile_resize(struct inode *sufile, __u64 newnsegs)
+ goto out_header;
+
+ sui->ncleansegs -= nsegs - newnsegs;
++
++ /*
++ * If the sufile is successfully truncated, immediately adjust
++ * the segment allocation space while locking the semaphore
++ * "mi_sem" so that nilfs_sufile_alloc() never allocates
++ * segments in the truncated space.
++ */
++ sui->allocmax = newnsegs - 1;
++ sui->allocmin = 0;
+ }
+
+ kaddr = kmap_atomic(header_bh->b_page);
+diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
+index e671fbdd4b863..60b7098f7a11d 100644
+--- a/fs/ocfs2/file.c
++++ b/fs/ocfs2/file.c
+@@ -2109,14 +2109,20 @@ static long ocfs2_fallocate(struct file *file, int mode, loff_t offset,
+ struct ocfs2_space_resv sr;
+ int change_size = 1;
+ int cmd = OCFS2_IOC_RESVSP64;
++ int ret = 0;
+
+ if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
+ return -EOPNOTSUPP;
+ if (!ocfs2_writes_unwritten_extents(osb))
+ return -EOPNOTSUPP;
+
+- if (mode & FALLOC_FL_KEEP_SIZE)
++ if (mode & FALLOC_FL_KEEP_SIZE) {
+ change_size = 0;
++ } else {
++ ret = inode_newsize_ok(inode, offset + len);
++ if (ret)
++ return ret;
++ }
+
+ if (mode & FALLOC_FL_PUNCH_HOLE)
+ cmd = OCFS2_IOC_UNRESVSP64;
+diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
+index 4d76321cf722b..48878ec0a1b18 100644
+--- a/fs/ocfs2/super.c
++++ b/fs/ocfs2/super.c
+@@ -985,8 +985,10 @@ static void ocfs2_disable_quotas(struct ocfs2_super *osb)
+ for (type = 0; type < OCFS2_MAXQUOTAS; type++) {
+ if (!sb_has_quota_loaded(sb, type))
+ continue;
+- oinfo = sb_dqinfo(sb, type)->dqi_priv;
+- cancel_delayed_work_sync(&oinfo->dqi_sync_work);
++ if (!sb_has_quota_suspended(sb, type)) {
++ oinfo = sb_dqinfo(sb, type)->dqi_priv;
++ cancel_delayed_work_sync(&oinfo->dqi_sync_work);
++ }
+ inode = igrab(sb->s_dquot.files[type]);
+ /* Turn off quotas. This will remove all dquot structures from
+ * memory and so they will be automatically synced to global
+diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
+index d2fc7e6ca9ccc..52f5260183496 100644
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -1545,7 +1545,6 @@ enum netdev_priv_flags {
+ * @tipc_ptr: TIPC specific data
+ * @atalk_ptr: AppleTalk link
+ * @ip_ptr: IPv4 specific data
+- * @dn_ptr: DECnet specific data
+ * @ip6_ptr: IPv6 specific data
+ * @ax25_ptr: AX.25 specific data
+ * @ieee80211_ptr: IEEE 802.11 specific data, assign before registering
+@@ -1773,7 +1772,6 @@ struct net_device {
+ #endif
+ void *atalk_ptr;
+ struct in_device __rcu *ip_ptr;
+- struct dn_dev __rcu *dn_ptr;
+ struct inet6_dev __rcu *ip6_ptr;
+ void *ax25_ptr;
+ struct wireless_dev *ieee80211_ptr;
+diff --git a/include/linux/netfilter_defs.h b/include/linux/netfilter_defs.h
+index dc6111adea06d..447b643bb525a 100644
+--- a/include/linux/netfilter_defs.h
++++ b/include/linux/netfilter_defs.h
+@@ -4,7 +4,6 @@
+
+ #include <uapi/linux/netfilter.h>
+
+-/* Largest hook number + 1, see uapi/linux/netfilter_decnet.h */
+-#define NF_MAX_HOOKS 8
++#define NF_MAX_HOOKS NF_INET_NUMHOOKS
+
+ #endif
+diff --git a/include/net/dn.h b/include/net/dn.h
+deleted file mode 100644
+index fc0036228d207..0000000000000
+--- a/include/net/dn.h
++++ /dev/null
+@@ -1,238 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
+-#ifndef _NET_DN_H
+-#define _NET_DN_H
+-
+-#include <linux/dn.h>
+-#include <net/sock.h>
+-#include <net/flow.h>
+-#include <asm/byteorder.h>
+-#include <asm/unaligned.h>
+-
+-struct dn_scp /* Session Control Port */
+-{
+- unsigned char state;
+-#define DN_O 1 /* Open */
+-#define DN_CR 2 /* Connect Receive */
+-#define DN_DR 3 /* Disconnect Reject */
+-#define DN_DRC 4 /* Discon. Rej. Complete*/
+-#define DN_CC 5 /* Connect Confirm */
+-#define DN_CI 6 /* Connect Initiate */
+-#define DN_NR 7 /* No resources */
+-#define DN_NC 8 /* No communication */
+-#define DN_CD 9 /* Connect Delivery */
+-#define DN_RJ 10 /* Rejected */
+-#define DN_RUN 11 /* Running */
+-#define DN_DI 12 /* Disconnect Initiate */
+-#define DN_DIC 13 /* Disconnect Complete */
+-#define DN_DN 14 /* Disconnect Notificat */
+-#define DN_CL 15 /* Closed */
+-#define DN_CN 16 /* Closed Notification */
+-
+- __le16 addrloc;
+- __le16 addrrem;
+- __u16 numdat;
+- __u16 numoth;
+- __u16 numoth_rcv;
+- __u16 numdat_rcv;
+- __u16 ackxmt_dat;
+- __u16 ackxmt_oth;
+- __u16 ackrcv_dat;
+- __u16 ackrcv_oth;
+- __u8 flowrem_sw;
+- __u8 flowloc_sw;
+-#define DN_SEND 2
+-#define DN_DONTSEND 1
+-#define DN_NOCHANGE 0
+- __u16 flowrem_dat;
+- __u16 flowrem_oth;
+- __u16 flowloc_dat;
+- __u16 flowloc_oth;
+- __u8 services_rem;
+- __u8 services_loc;
+- __u8 info_rem;
+- __u8 info_loc;
+-
+- __u16 segsize_rem;
+- __u16 segsize_loc;
+-
+- __u8 nonagle;
+- __u8 multi_ireq;
+- __u8 accept_mode;
+- unsigned long seg_total; /* Running total of current segment */
+-
+- struct optdata_dn conndata_in;
+- struct optdata_dn conndata_out;
+- struct optdata_dn discdata_in;
+- struct optdata_dn discdata_out;
+- struct accessdata_dn accessdata;
+-
+- struct sockaddr_dn addr; /* Local address */
+- struct sockaddr_dn peer; /* Remote address */
+-
+- /*
+- * In this case the RTT estimation is not specified in the
+- * docs, nor is any back off algorithm. Here we follow well
+- * known tcp algorithms with a few small variations.
+- *
+- * snd_window: Max number of packets we send before we wait for
+- * an ack to come back. This will become part of a
+- * more complicated scheme when we support flow
+- * control.
+- *
+- * nsp_srtt: Round-Trip-Time (x8) in jiffies. This is a rolling
+- * average.
+- * nsp_rttvar: Round-Trip-Time-Varience (x4) in jiffies. This is the
+- * varience of the smoothed average (but calculated in
+- * a simpler way than for normal statistical varience
+- * calculations).
+- *
+- * nsp_rxtshift: Backoff counter. Value is zero normally, each time
+- * a packet is lost is increases by one until an ack
+- * is received. Its used to index an array of backoff
+- * multipliers.
+- */
+-#define NSP_MIN_WINDOW 1
+-#define NSP_MAX_WINDOW (0x07fe)
+- unsigned long max_window;
+- unsigned long snd_window;
+-#define NSP_INITIAL_SRTT (HZ)
+- unsigned long nsp_srtt;
+-#define NSP_INITIAL_RTTVAR (HZ*3)
+- unsigned long nsp_rttvar;
+-#define NSP_MAXRXTSHIFT 12
+- unsigned long nsp_rxtshift;
+-
+- /*
+- * Output queues, one for data, one for otherdata/linkservice
+- */
+- struct sk_buff_head data_xmit_queue;
+- struct sk_buff_head other_xmit_queue;
+-
+- /*
+- * Input queue for other data
+- */
+- struct sk_buff_head other_receive_queue;
+- int other_report;
+-
+- /*
+- * Stuff to do with the slow timer
+- */
+- unsigned long stamp; /* time of last transmit */
+- unsigned long persist;
+- int (*persist_fxn)(struct sock *sk);
+- unsigned long keepalive;
+- void (*keepalive_fxn)(struct sock *sk);
+-
+- /*
+- * This stuff is for the fast timer for delayed acks
+- */
+- struct timer_list delack_timer;
+- int delack_pending;
+- void (*delack_fxn)(struct sock *sk);
+-
+-};
+-
+-static inline struct dn_scp *DN_SK(struct sock *sk)
+-{
+- return (struct dn_scp *)(sk + 1);
+-}
+-
+-/*
+- * src,dst : Source and Destination DECnet addresses
+- * hops : Number of hops through the network
+- * dst_port, src_port : NSP port numbers
+- * services, info : Useful data extracted from conninit messages
+- * rt_flags : Routing flags byte
+- * nsp_flags : NSP layer flags byte
+- * segsize : Size of segment
+- * segnum : Number, for data, otherdata and linkservice
+- * xmit_count : Number of times we've transmitted this skb
+- * stamp : Time stamp of most recent transmission, used in RTT calculations
+- * iif: Input interface number
+- *
+- * As a general policy, this structure keeps all addresses in network
+- * byte order, and all else in host byte order. Thus dst, src, dst_port
+- * and src_port are in network order. All else is in host order.
+- *
+- */
+-#define DN_SKB_CB(skb) ((struct dn_skb_cb *)(skb)->cb)
+-struct dn_skb_cb {
+- __le16 dst;
+- __le16 src;
+- __u16 hops;
+- __le16 dst_port;
+- __le16 src_port;
+- __u8 services;
+- __u8 info;
+- __u8 rt_flags;
+- __u8 nsp_flags;
+- __u16 segsize;
+- __u16 segnum;
+- __u16 xmit_count;
+- unsigned long stamp;
+- int iif;
+-};
+-
+-static inline __le16 dn_eth2dn(unsigned char *ethaddr)
+-{
+- return get_unaligned((__le16 *)(ethaddr + 4));
+-}
+-
+-static inline __le16 dn_saddr2dn(struct sockaddr_dn *saddr)
+-{
+- return *(__le16 *)saddr->sdn_nodeaddr;
+-}
+-
+-static inline void dn_dn2eth(unsigned char *ethaddr, __le16 addr)
+-{
+- __u16 a = le16_to_cpu(addr);
+- ethaddr[0] = 0xAA;
+- ethaddr[1] = 0x00;
+- ethaddr[2] = 0x04;
+- ethaddr[3] = 0x00;
+- ethaddr[4] = (__u8)(a & 0xff);
+- ethaddr[5] = (__u8)(a >> 8);
+-}
+-
+-static inline void dn_sk_ports_copy(struct flowidn *fld, struct dn_scp *scp)
+-{
+- fld->fld_sport = scp->addrloc;
+- fld->fld_dport = scp->addrrem;
+-}
+-
+-unsigned int dn_mss_from_pmtu(struct net_device *dev, int mtu);
+-void dn_register_sysctl(void);
+-void dn_unregister_sysctl(void);
+-
+-#define DN_MENUVER_ACC 0x01
+-#define DN_MENUVER_USR 0x02
+-#define DN_MENUVER_PRX 0x04
+-#define DN_MENUVER_UIC 0x08
+-
+-struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr);
+-struct sock *dn_find_by_skb(struct sk_buff *skb);
+-#define DN_ASCBUF_LEN 9
+-char *dn_addr2asc(__u16, char *);
+-int dn_destroy_timer(struct sock *sk);
+-
+-int dn_sockaddr2username(struct sockaddr_dn *addr, unsigned char *buf,
+- unsigned char type);
+-int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *addr,
+- unsigned char *type);
+-
+-void dn_start_slow_timer(struct sock *sk);
+-void dn_stop_slow_timer(struct sock *sk);
+-
+-extern __le16 decnet_address;
+-extern int decnet_debug_level;
+-extern int decnet_time_wait;
+-extern int decnet_dn_count;
+-extern int decnet_di_count;
+-extern int decnet_dr_count;
+-extern int decnet_no_fc_max_cwnd;
+-
+-extern long sysctl_decnet_mem[3];
+-extern int sysctl_decnet_wmem[3];
+-extern int sysctl_decnet_rmem[3];
+-
+-#endif /* _NET_DN_H */
+diff --git a/include/net/dn_dev.h b/include/net/dn_dev.h
+deleted file mode 100644
+index 595b4f6c1eb10..0000000000000
+--- a/include/net/dn_dev.h
++++ /dev/null
+@@ -1,199 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
+-#ifndef _NET_DN_DEV_H
+-#define _NET_DN_DEV_H
+-
+-
+-struct dn_dev;
+-
+-struct dn_ifaddr {
+- struct dn_ifaddr __rcu *ifa_next;
+- struct dn_dev *ifa_dev;
+- __le16 ifa_local;
+- __le16 ifa_address;
+- __u32 ifa_flags;
+- __u8 ifa_scope;
+- char ifa_label[IFNAMSIZ];
+- struct rcu_head rcu;
+-};
+-
+-#define DN_DEV_S_RU 0 /* Run - working normally */
+-#define DN_DEV_S_CR 1 /* Circuit Rejected */
+-#define DN_DEV_S_DS 2 /* Data Link Start */
+-#define DN_DEV_S_RI 3 /* Routing Layer Initialize */
+-#define DN_DEV_S_RV 4 /* Routing Layer Verify */
+-#define DN_DEV_S_RC 5 /* Routing Layer Complete */
+-#define DN_DEV_S_OF 6 /* Off */
+-#define DN_DEV_S_HA 7 /* Halt */
+-
+-
+-/*
+- * The dn_dev_parms structure contains the set of parameters
+- * for each device (hence inclusion in the dn_dev structure)
+- * and an array is used to store the default types of supported
+- * device (in dn_dev.c).
+- *
+- * The type field matches the ARPHRD_ constants and is used in
+- * searching the list for supported devices when new devices
+- * come up.
+- *
+- * The mode field is used to find out if a device is broadcast,
+- * multipoint, or pointopoint. Please note that DECnet thinks
+- * different ways about devices to the rest of the kernel
+- * so the normal IFF_xxx flags are invalid here. For devices
+- * which can be any combination of the previously mentioned
+- * attributes, you can set this on a per device basis by
+- * installing an up() routine.
+- *
+- * The device state field, defines the initial state in which the
+- * device will come up. In the dn_dev structure, it is the actual
+- * state.
+- *
+- * Things have changed here. I've killed timer1 since it's a user space
+- * issue for a user space routing deamon to sort out. The kernel does
+- * not need to be bothered with it.
+- *
+- * Timers:
+- * t2 - Rate limit timer, min time between routing and hello messages
+- * t3 - Hello timer, send hello messages when it expires
+- *
+- * Callbacks:
+- * up() - Called to initialize device, return value can veto use of
+- * device with DECnet.
+- * down() - Called to turn device off when it goes down
+- * timer3() - Called once for each ifaddr when timer 3 goes off
+- *
+- * sysctl - Hook for sysctl things
+- *
+- */
+-struct dn_dev_parms {
+- int type; /* ARPHRD_xxx */
+- int mode; /* Broadcast, Unicast, Mulitpoint */
+-#define DN_DEV_BCAST 1
+-#define DN_DEV_UCAST 2
+-#define DN_DEV_MPOINT 4
+- int state; /* Initial state */
+- int forwarding; /* 0=EndNode, 1=L1Router, 2=L2Router */
+- unsigned long t2; /* Default value of t2 */
+- unsigned long t3; /* Default value of t3 */
+- int priority; /* Priority to be a router */
+- char *name; /* Name for sysctl */
+- int (*up)(struct net_device *);
+- void (*down)(struct net_device *);
+- void (*timer3)(struct net_device *, struct dn_ifaddr *ifa);
+- void *sysctl;
+-};
+-
+-
+-struct dn_dev {
+- struct dn_ifaddr __rcu *ifa_list;
+- struct net_device *dev;
+- struct dn_dev_parms parms;
+- char use_long;
+- struct timer_list timer;
+- unsigned long t3;
+- struct neigh_parms *neigh_parms;
+- __u8 addr[ETH_ALEN];
+- struct neighbour *router; /* Default router on circuit */
+- struct neighbour *peer; /* Peer on pointopoint links */
+- unsigned long uptime; /* Time device went up in jiffies */
+-};
+-
+-struct dn_short_packet {
+- __u8 msgflg;
+- __le16 dstnode;
+- __le16 srcnode;
+- __u8 forward;
+-} __packed;
+-
+-struct dn_long_packet {
+- __u8 msgflg;
+- __u8 d_area;
+- __u8 d_subarea;
+- __u8 d_id[6];
+- __u8 s_area;
+- __u8 s_subarea;
+- __u8 s_id[6];
+- __u8 nl2;
+- __u8 visit_ct;
+- __u8 s_class;
+- __u8 pt;
+-} __packed;
+-
+-/*------------------------- DRP - Routing messages ---------------------*/
+-
+-struct endnode_hello_message {
+- __u8 msgflg;
+- __u8 tiver[3];
+- __u8 id[6];
+- __u8 iinfo;
+- __le16 blksize;
+- __u8 area;
+- __u8 seed[8];
+- __u8 neighbor[6];
+- __le16 timer;
+- __u8 mpd;
+- __u8 datalen;
+- __u8 data[2];
+-} __packed;
+-
+-struct rtnode_hello_message {
+- __u8 msgflg;
+- __u8 tiver[3];
+- __u8 id[6];
+- __u8 iinfo;
+- __le16 blksize;
+- __u8 priority;
+- __u8 area;
+- __le16 timer;
+- __u8 mpd;
+-} __packed;
+-
+-
+-void dn_dev_init(void);
+-void dn_dev_cleanup(void);
+-
+-int dn_dev_ioctl(unsigned int cmd, void __user *arg);
+-
+-void dn_dev_devices_off(void);
+-void dn_dev_devices_on(void);
+-
+-void dn_dev_init_pkt(struct sk_buff *skb);
+-void dn_dev_veri_pkt(struct sk_buff *skb);
+-void dn_dev_hello(struct sk_buff *skb);
+-
+-void dn_dev_up(struct net_device *);
+-void dn_dev_down(struct net_device *);
+-
+-int dn_dev_set_default(struct net_device *dev, int force);
+-struct net_device *dn_dev_get_default(void);
+-int dn_dev_bind_default(__le16 *addr);
+-
+-int register_dnaddr_notifier(struct notifier_block *nb);
+-int unregister_dnaddr_notifier(struct notifier_block *nb);
+-
+-static inline int dn_dev_islocal(struct net_device *dev, __le16 addr)
+-{
+- struct dn_dev *dn_db;
+- struct dn_ifaddr *ifa;
+- int res = 0;
+-
+- rcu_read_lock();
+- dn_db = rcu_dereference(dev->dn_ptr);
+- if (dn_db == NULL) {
+- printk(KERN_DEBUG "dn_dev_islocal: Called for non DECnet device\n");
+- goto out;
+- }
+-
+- for (ifa = rcu_dereference(dn_db->ifa_list);
+- ifa != NULL;
+- ifa = rcu_dereference(ifa->ifa_next))
+- if ((addr ^ ifa->ifa_local) == 0) {
+- res = 1;
+- break;
+- }
+-out:
+- rcu_read_unlock();
+- return res;
+-}
+-
+-#endif /* _NET_DN_DEV_H */
+diff --git a/include/net/dn_fib.h b/include/net/dn_fib.h
+deleted file mode 100644
+index 6dd2213c5eb21..0000000000000
+--- a/include/net/dn_fib.h
++++ /dev/null
+@@ -1,167 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
+-#ifndef _NET_DN_FIB_H
+-#define _NET_DN_FIB_H
+-
+-#include <linux/netlink.h>
+-#include <linux/refcount.h>
+-
+-extern const struct nla_policy rtm_dn_policy[];
+-
+-struct dn_fib_res {
+- struct fib_rule *r;
+- struct dn_fib_info *fi;
+- unsigned char prefixlen;
+- unsigned char nh_sel;
+- unsigned char type;
+- unsigned char scope;
+-};
+-
+-struct dn_fib_nh {
+- struct net_device *nh_dev;
+- unsigned int nh_flags;
+- unsigned char nh_scope;
+- int nh_weight;
+- int nh_power;
+- int nh_oif;
+- __le16 nh_gw;
+-};
+-
+-struct dn_fib_info {
+- struct dn_fib_info *fib_next;
+- struct dn_fib_info *fib_prev;
+- int fib_treeref;
+- refcount_t fib_clntref;
+- int fib_dead;
+- unsigned int fib_flags;
+- int fib_protocol;
+- __le16 fib_prefsrc;
+- __u32 fib_priority;
+- __u32 fib_metrics[RTAX_MAX];
+- int fib_nhs;
+- int fib_power;
+- struct dn_fib_nh fib_nh[0];
+-#define dn_fib_dev fib_nh[0].nh_dev
+-};
+-
+-
+-#define DN_FIB_RES_RESET(res) ((res).nh_sel = 0)
+-#define DN_FIB_RES_NH(res) ((res).fi->fib_nh[(res).nh_sel])
+-
+-#define DN_FIB_RES_PREFSRC(res) ((res).fi->fib_prefsrc ? : __dn_fib_res_prefsrc(&res))
+-#define DN_FIB_RES_GW(res) (DN_FIB_RES_NH(res).nh_gw)
+-#define DN_FIB_RES_DEV(res) (DN_FIB_RES_NH(res).nh_dev)
+-#define DN_FIB_RES_OIF(res) (DN_FIB_RES_NH(res).nh_oif)
+-
+-typedef struct {
+- __le16 datum;
+-} dn_fib_key_t;
+-
+-typedef struct {
+- __le16 datum;
+-} dn_fib_hash_t;
+-
+-typedef struct {
+- __u16 datum;
+-} dn_fib_idx_t;
+-
+-struct dn_fib_node {
+- struct dn_fib_node *fn_next;
+- struct dn_fib_info *fn_info;
+-#define DN_FIB_INFO(f) ((f)->fn_info)
+- dn_fib_key_t fn_key;
+- u8 fn_type;
+- u8 fn_scope;
+- u8 fn_state;
+-};
+-
+-
+-struct dn_fib_table {
+- struct hlist_node hlist;
+- u32 n;
+-
+- int (*insert)(struct dn_fib_table *t, struct rtmsg *r,
+- struct nlattr *attrs[], struct nlmsghdr *n,
+- struct netlink_skb_parms *req);
+- int (*delete)(struct dn_fib_table *t, struct rtmsg *r,
+- struct nlattr *attrs[], struct nlmsghdr *n,
+- struct netlink_skb_parms *req);
+- int (*lookup)(struct dn_fib_table *t, const struct flowidn *fld,
+- struct dn_fib_res *res);
+- int (*flush)(struct dn_fib_table *t);
+- int (*dump)(struct dn_fib_table *t, struct sk_buff *skb, struct netlink_callback *cb);
+-
+- unsigned char data[0];
+-};
+-
+-#ifdef CONFIG_DECNET_ROUTER
+-/*
+- * dn_fib.c
+- */
+-void dn_fib_init(void);
+-void dn_fib_cleanup(void);
+-
+-int dn_fib_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
+-struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r,
+- struct nlattr *attrs[],
+- const struct nlmsghdr *nlh, int *errp);
+-int dn_fib_semantic_match(int type, struct dn_fib_info *fi,
+- const struct flowidn *fld, struct dn_fib_res *res);
+-void dn_fib_release_info(struct dn_fib_info *fi);
+-void dn_fib_flush(void);
+-void dn_fib_select_multipath(const struct flowidn *fld, struct dn_fib_res *res);
+-
+-/*
+- * dn_tables.c
+- */
+-struct dn_fib_table *dn_fib_get_table(u32 n, int creat);
+-struct dn_fib_table *dn_fib_empty_table(void);
+-void dn_fib_table_init(void);
+-void dn_fib_table_cleanup(void);
+-
+-/*
+- * dn_rules.c
+- */
+-void dn_fib_rules_init(void);
+-void dn_fib_rules_cleanup(void);
+-unsigned int dnet_addr_type(__le16 addr);
+-int dn_fib_lookup(struct flowidn *fld, struct dn_fib_res *res);
+-
+-int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb);
+-
+-void dn_fib_free_info(struct dn_fib_info *fi);
+-
+-static inline void dn_fib_info_put(struct dn_fib_info *fi)
+-{
+- if (refcount_dec_and_test(&fi->fib_clntref))
+- dn_fib_free_info(fi);
+-}
+-
+-static inline void dn_fib_res_put(struct dn_fib_res *res)
+-{
+- if (res->fi)
+- dn_fib_info_put(res->fi);
+- if (res->r)
+- fib_rule_put(res->r);
+-}
+-
+-#else /* Endnode */
+-
+-#define dn_fib_init() do { } while(0)
+-#define dn_fib_cleanup() do { } while(0)
+-
+-#define dn_fib_lookup(fl, res) (-ESRCH)
+-#define dn_fib_info_put(fi) do { } while(0)
+-#define dn_fib_select_multipath(fl, res) do { } while(0)
+-#define dn_fib_rules_policy(saddr,res,flags) (0)
+-#define dn_fib_res_put(res) do { } while(0)
+-
+-#endif /* CONFIG_DECNET_ROUTER */
+-
+-static inline __le16 dnet_make_mask(int n)
+-{
+- if (n)
+- return cpu_to_le16(~((1 << (16 - n)) - 1));
+- return cpu_to_le16(0);
+-}
+-
+-#endif /* _NET_DN_FIB_H */
+diff --git a/include/net/dn_neigh.h b/include/net/dn_neigh.h
+deleted file mode 100644
+index 2e3e7793973a8..0000000000000
+--- a/include/net/dn_neigh.h
++++ /dev/null
+@@ -1,30 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
+-#ifndef _NET_DN_NEIGH_H
+-#define _NET_DN_NEIGH_H
+-
+-/*
+- * The position of the first two fields of
+- * this structure are critical - SJW
+- */
+-struct dn_neigh {
+- struct neighbour n;
+- __le16 addr;
+- unsigned long flags;
+-#define DN_NDFLAG_R1 0x0001 /* Router L1 */
+-#define DN_NDFLAG_R2 0x0002 /* Router L2 */
+-#define DN_NDFLAG_P3 0x0004 /* Phase III Node */
+- unsigned long blksize;
+- __u8 priority;
+-};
+-
+-void dn_neigh_init(void);
+-void dn_neigh_cleanup(void);
+-int dn_neigh_router_hello(struct net *net, struct sock *sk, struct sk_buff *skb);
+-int dn_neigh_endnode_hello(struct net *net, struct sock *sk, struct sk_buff *skb);
+-void dn_neigh_pointopoint_hello(struct sk_buff *skb);
+-int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n);
+-int dn_to_neigh_output(struct net *net, struct sock *sk, struct sk_buff *skb);
+-
+-extern struct neigh_table dn_neigh_table;
+-
+-#endif /* _NET_DN_NEIGH_H */
+diff --git a/include/net/dn_nsp.h b/include/net/dn_nsp.h
+deleted file mode 100644
+index 3a3e33d184562..0000000000000
+--- a/include/net/dn_nsp.h
++++ /dev/null
+@@ -1,204 +0,0 @@
+-#ifndef _NET_DN_NSP_H
+-#define _NET_DN_NSP_H
+-/******************************************************************************
+- (c) 1995-1998 E.M. Serrat emserrat@geocities.com
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License as published by
+- the Free Software Foundation; either version 2 of the License, or
+- any later version.
+-
+- This program is distributed in the hope that it will be useful,
+- but WITHOUT ANY WARRANTY; without even the implied warranty of
+- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- GNU General Public License for more details.
+-*******************************************************************************/
+-/* dn_nsp.c functions prototyping */
+-
+-void dn_nsp_send_data_ack(struct sock *sk);
+-void dn_nsp_send_oth_ack(struct sock *sk);
+-void dn_nsp_delayed_ack(struct sock *sk);
+-void dn_send_conn_ack(struct sock *sk);
+-void dn_send_conn_conf(struct sock *sk, gfp_t gfp);
+-void dn_nsp_send_disc(struct sock *sk, unsigned char type,
+- unsigned short reason, gfp_t gfp);
+-void dn_nsp_return_disc(struct sk_buff *skb, unsigned char type,
+- unsigned short reason);
+-void dn_nsp_send_link(struct sock *sk, unsigned char lsflags, char fcval);
+-void dn_nsp_send_conninit(struct sock *sk, unsigned char flags);
+-
+-void dn_nsp_output(struct sock *sk);
+-int dn_nsp_check_xmit_queue(struct sock *sk, struct sk_buff *skb,
+- struct sk_buff_head *q, unsigned short acknum);
+-void dn_nsp_queue_xmit(struct sock *sk, struct sk_buff *skb, gfp_t gfp,
+- int oob);
+-unsigned long dn_nsp_persist(struct sock *sk);
+-int dn_nsp_xmit_timeout(struct sock *sk);
+-
+-int dn_nsp_rx(struct sk_buff *);
+-int dn_nsp_backlog_rcv(struct sock *sk, struct sk_buff *skb);
+-
+-struct sk_buff *dn_alloc_skb(struct sock *sk, int size, gfp_t pri);
+-struct sk_buff *dn_alloc_send_skb(struct sock *sk, size_t *size, int noblock,
+- long timeo, int *err);
+-
+-#define NSP_REASON_OK 0 /* No error */
+-#define NSP_REASON_NR 1 /* No resources */
+-#define NSP_REASON_UN 2 /* Unrecognised node name */
+-#define NSP_REASON_SD 3 /* Node shutting down */
+-#define NSP_REASON_ID 4 /* Invalid destination end user */
+-#define NSP_REASON_ER 5 /* End user lacks resources */
+-#define NSP_REASON_OB 6 /* Object too busy */
+-#define NSP_REASON_US 7 /* Unspecified error */
+-#define NSP_REASON_TP 8 /* Third-Party abort */
+-#define NSP_REASON_EA 9 /* End user has aborted the link */
+-#define NSP_REASON_IF 10 /* Invalid node name format */
+-#define NSP_REASON_LS 11 /* Local node shutdown */
+-#define NSP_REASON_LL 32 /* Node lacks logical-link resources */
+-#define NSP_REASON_LE 33 /* End user lacks logical-link resources */
+-#define NSP_REASON_UR 34 /* Unacceptable RQSTRID or PASSWORD field */
+-#define NSP_REASON_UA 36 /* Unacceptable ACCOUNT field */
+-#define NSP_REASON_TM 38 /* End user timed out logical link */
+-#define NSP_REASON_NU 39 /* Node unreachable */
+-#define NSP_REASON_NL 41 /* No-link message */
+-#define NSP_REASON_DC 42 /* Disconnect confirm */
+-#define NSP_REASON_IO 43 /* Image data field overflow */
+-
+-#define NSP_DISCINIT 0x38
+-#define NSP_DISCCONF 0x48
+-
+-/*------------------------- NSP - messages ------------------------------*/
+-/* Data Messages */
+-/*---------------*/
+-
+-/* Data Messages (data segment/interrupt/link service) */
+-
+-struct nsp_data_seg_msg {
+- __u8 msgflg;
+- __le16 dstaddr;
+- __le16 srcaddr;
+-} __packed;
+-
+-struct nsp_data_opt_msg {
+- __le16 acknum;
+- __le16 segnum;
+- __le16 lsflgs;
+-} __packed;
+-
+-struct nsp_data_opt_msg1 {
+- __le16 acknum;
+- __le16 segnum;
+-} __packed;
+-
+-
+-/* Acknowledgment Message (data/other data) */
+-struct nsp_data_ack_msg {
+- __u8 msgflg;
+- __le16 dstaddr;
+- __le16 srcaddr;
+- __le16 acknum;
+-} __packed;
+-
+-/* Connect Acknowledgment Message */
+-struct nsp_conn_ack_msg {
+- __u8 msgflg;
+- __le16 dstaddr;
+-} __packed;
+-
+-
+-/* Connect Initiate/Retransmit Initiate/Connect Confirm */
+-struct nsp_conn_init_msg {
+- __u8 msgflg;
+-#define NSP_CI 0x18 /* Connect Initiate */
+-#define NSP_RCI 0x68 /* Retrans. Conn Init */
+- __le16 dstaddr;
+- __le16 srcaddr;
+- __u8 services;
+-#define NSP_FC_NONE 0x00 /* Flow Control None */
+-#define NSP_FC_SRC 0x04 /* Seg Req. Count */
+-#define NSP_FC_SCMC 0x08 /* Sess. Control Mess */
+-#define NSP_FC_MASK 0x0c /* FC type mask */
+- __u8 info;
+- __le16 segsize;
+-} __packed;
+-
+-/* Disconnect Initiate/Disconnect Confirm */
+-struct nsp_disconn_init_msg {
+- __u8 msgflg;
+- __le16 dstaddr;
+- __le16 srcaddr;
+- __le16 reason;
+-} __packed;
+-
+-
+-
+-struct srcobj_fmt {
+- __u8 format;
+- __u8 task;
+- __le16 grpcode;
+- __le16 usrcode;
+- __u8 dlen;
+-} __packed;
+-
+-/*
+- * A collection of functions for manipulating the sequence
+- * numbers used in NSP. Similar in operation to the functions
+- * of the same name in TCP.
+- */
+-static __inline__ int dn_before(__u16 seq1, __u16 seq2)
+-{
+- seq1 &= 0x0fff;
+- seq2 &= 0x0fff;
+-
+- return (int)((seq1 - seq2) & 0x0fff) > 2048;
+-}
+-
+-
+-static __inline__ int dn_after(__u16 seq1, __u16 seq2)
+-{
+- seq1 &= 0x0fff;
+- seq2 &= 0x0fff;
+-
+- return (int)((seq2 - seq1) & 0x0fff) > 2048;
+-}
+-
+-static __inline__ int dn_equal(__u16 seq1, __u16 seq2)
+-{
+- return ((seq1 ^ seq2) & 0x0fff) == 0;
+-}
+-
+-static __inline__ int dn_before_or_equal(__u16 seq1, __u16 seq2)
+-{
+- return (dn_before(seq1, seq2) || dn_equal(seq1, seq2));
+-}
+-
+-static __inline__ void seq_add(__u16 *seq, __u16 off)
+-{
+- (*seq) += off;
+- (*seq) &= 0x0fff;
+-}
+-
+-static __inline__ int seq_next(__u16 seq1, __u16 seq2)
+-{
+- return dn_equal(seq1 + 1, seq2);
+-}
+-
+-/*
+- * Can we delay the ack ?
+- */
+-static __inline__ int sendack(__u16 seq)
+-{
+- return (int)((seq & 0x1000) ? 0 : 1);
+-}
+-
+-/*
+- * Is socket congested ?
+- */
+-static __inline__ int dn_congested(struct sock *sk)
+-{
+- return atomic_read(&sk->sk_rmem_alloc) > (sk->sk_rcvbuf >> 1);
+-}
+-
+-#define DN_MAX_NSP_DATA_HEADER (11)
+-
+-#endif /* _NET_DN_NSP_H */
+diff --git a/include/net/dn_route.h b/include/net/dn_route.h
+deleted file mode 100644
+index 55df9939bca26..0000000000000
+--- a/include/net/dn_route.h
++++ /dev/null
+@@ -1,122 +0,0 @@
+-#ifndef _NET_DN_ROUTE_H
+-#define _NET_DN_ROUTE_H
+-
+-/******************************************************************************
+- (c) 1995-1998 E.M. Serrat emserrat@geocities.com
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License as published by
+- the Free Software Foundation; either version 2 of the License, or
+- any later version.
+-
+- This program is distributed in the hope that it will be useful,
+- but WITHOUT ANY WARRANTY; without even the implied warranty of
+- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- GNU General Public License for more details.
+-*******************************************************************************/
+-
+-struct sk_buff *dn_alloc_skb(struct sock *sk, int size, gfp_t pri);
+-int dn_route_output_sock(struct dst_entry __rcu **pprt, struct flowidn *,
+- struct sock *sk, int flags);
+-int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb);
+-void dn_rt_cache_flush(int delay);
+-int dn_route_rcv(struct sk_buff *skb, struct net_device *dev,
+- struct packet_type *pt, struct net_device *orig_dev);
+-
+-/* Masks for flags field */
+-#define DN_RT_F_PID 0x07 /* Mask for packet type */
+-#define DN_RT_F_PF 0x80 /* Padding Follows */
+-#define DN_RT_F_VER 0x40 /* Version =0 discard packet if ==1 */
+-#define DN_RT_F_IE 0x20 /* Intra Ethernet, Reserved in short pkt */
+-#define DN_RT_F_RTS 0x10 /* Packet is being returned to sender */
+-#define DN_RT_F_RQR 0x08 /* Return packet to sender upon non-delivery */
+-
+-/* Mask for types of routing packets */
+-#define DN_RT_PKT_MSK 0x06
+-/* Types of routing packets */
+-#define DN_RT_PKT_SHORT 0x02 /* Short routing packet */
+-#define DN_RT_PKT_LONG 0x06 /* Long routing packet */
+-
+-/* Mask for control/routing selection */
+-#define DN_RT_PKT_CNTL 0x01 /* Set to 1 if a control packet */
+-/* Types of control packets */
+-#define DN_RT_CNTL_MSK 0x0f /* Mask for control packets */
+-#define DN_RT_PKT_INIT 0x01 /* Initialisation packet */
+-#define DN_RT_PKT_VERI 0x03 /* Verification Message */
+-#define DN_RT_PKT_HELO 0x05 /* Hello and Test Message */
+-#define DN_RT_PKT_L1RT 0x07 /* Level 1 Routing Message */
+-#define DN_RT_PKT_L2RT 0x09 /* Level 2 Routing Message */
+-#define DN_RT_PKT_ERTH 0x0b /* Ethernet Router Hello */
+-#define DN_RT_PKT_EEDH 0x0d /* Ethernet EndNode Hello */
+-
+-/* Values for info field in hello message */
+-#define DN_RT_INFO_TYPE 0x03 /* Type mask */
+-#define DN_RT_INFO_L1RT 0x02 /* L1 Router */
+-#define DN_RT_INFO_L2RT 0x01 /* L2 Router */
+-#define DN_RT_INFO_ENDN 0x03 /* EndNode */
+-#define DN_RT_INFO_VERI 0x04 /* Verification Reqd. */
+-#define DN_RT_INFO_RJCT 0x08 /* Reject Flag, Reserved */
+-#define DN_RT_INFO_VFLD 0x10 /* Verification Failed, Reserved */
+-#define DN_RT_INFO_NOML 0x20 /* No Multicast traffic accepted */
+-#define DN_RT_INFO_BLKR 0x40 /* Blocking Requested */
+-
+-/*
+- * The fl structure is what we used to look up the route.
+- * The rt_saddr & rt_daddr entries are the same as key.saddr & key.daddr
+- * except for local input routes, where the rt_saddr = fl.fld_dst and
+- * rt_daddr = fl.fld_src to allow the route to be used for returning
+- * packets to the originating host.
+- */
+-struct dn_route {
+- struct dst_entry dst;
+-
+- struct neighbour *n;
+-
+- struct flowidn fld;
+-
+- __le16 rt_saddr;
+- __le16 rt_daddr;
+- __le16 rt_gateway;
+- __le16 rt_local_src; /* Source used for forwarding packets */
+- __le16 rt_src_map;
+- __le16 rt_dst_map;
+-
+- unsigned int rt_flags;
+- unsigned int rt_type;
+-};
+-
+-static inline bool dn_is_input_route(struct dn_route *rt)
+-{
+- return rt->fld.flowidn_iif != 0;
+-}
+-
+-static inline bool dn_is_output_route(struct dn_route *rt)
+-{
+- return rt->fld.flowidn_iif == 0;
+-}
+-
+-void dn_route_init(void);
+-void dn_route_cleanup(void);
+-
+-#include <net/sock.h>
+-#include <linux/if_arp.h>
+-
+-static inline void dn_rt_send(struct sk_buff *skb)
+-{
+- dev_queue_xmit(skb);
+-}
+-
+-static inline void dn_rt_finish_output(struct sk_buff *skb, char *dst, char *src)
+-{
+- struct net_device *dev = skb->dev;
+-
+- if ((dev->type != ARPHRD_ETHER) && (dev->type != ARPHRD_LOOPBACK))
+- dst = NULL;
+-
+- if (dev_hard_header(skb, dev, ETH_P_DNA_RT, dst, src, skb->len) >= 0)
+- dn_rt_send(skb);
+- else
+- kfree_skb(skb);
+-}
+-
+-#endif /* _NET_DN_ROUTE_H */
+diff --git a/include/net/neighbour.h b/include/net/neighbour.h
+index e89273f9a0bc6..cb4c3133be568 100644
+--- a/include/net/neighbour.h
++++ b/include/net/neighbour.h
+@@ -250,11 +250,6 @@ static inline void *neighbour_priv(const struct neighbour *n)
+ #define NEIGH_UPDATE_F_ADMIN 0x80000000
+
+
+-static inline bool neigh_key_eq16(const struct neighbour *n, const void *pkey)
+-{
+- return *(const u16 *)n->primary_key == *(const u16 *)pkey;
+-}
+-
+ static inline bool neigh_key_eq32(const struct neighbour *n, const void *pkey)
+ {
+ return *(const u32 *)n->primary_key == *(const u32 *)pkey;
+@@ -304,8 +299,6 @@ void neigh_table_init(int index, struct neigh_table *tbl);
+ int neigh_table_clear(int index, struct neigh_table *tbl);
+ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
+ struct net_device *dev);
+-struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net,
+- const void *pkey);
+ struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
+ struct net_device *dev, bool want_ref);
+ static inline struct neighbour *neigh_create(struct neigh_table *tbl,
+diff --git a/include/uapi/linux/dn.h b/include/uapi/linux/dn.h
+deleted file mode 100644
+index 36ca71bd8bbe2..0000000000000
+--- a/include/uapi/linux/dn.h
++++ /dev/null
+@@ -1,149 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+-#ifndef _LINUX_DN_H
+-#define _LINUX_DN_H
+-
+-#include <linux/ioctl.h>
+-#include <linux/types.h>
+-#include <linux/if_ether.h>
+-
+-/*
+-
+- DECnet Data Structures and Constants
+-
+-*/
+-
+-/*
+- * DNPROTO_NSP can't be the same as SOL_SOCKET,
+- * so increment each by one (compared to ULTRIX)
+- */
+-#define DNPROTO_NSP 2 /* NSP protocol number */
+-#define DNPROTO_ROU 3 /* Routing protocol number */
+-#define DNPROTO_NML 4 /* Net mgt protocol number */
+-#define DNPROTO_EVL 5 /* Evl protocol number (usr) */
+-#define DNPROTO_EVR 6 /* Evl protocol number (evl) */
+-#define DNPROTO_NSPT 7 /* NSP trace protocol number */
+-
+-
+-#define DN_ADDL 2
+-#define DN_MAXADDL 2 /* ULTRIX headers have 20 here, but pathworks has 2 */
+-#define DN_MAXOPTL 16
+-#define DN_MAXOBJL 16
+-#define DN_MAXACCL 40
+-#define DN_MAXALIASL 128
+-#define DN_MAXNODEL 256
+-#define DNBUFSIZE 65023
+-
+-/*
+- * SET/GET Socket options - must match the DSO_ numbers below
+- */
+-#define SO_CONDATA 1
+-#define SO_CONACCESS 2
+-#define SO_PROXYUSR 3
+-#define SO_LINKINFO 7
+-
+-#define DSO_CONDATA 1 /* Set/Get connect data */
+-#define DSO_DISDATA 10 /* Set/Get disconnect data */
+-#define DSO_CONACCESS 2 /* Set/Get connect access data */
+-#define DSO_ACCEPTMODE 4 /* Set/Get accept mode */
+-#define DSO_CONACCEPT 5 /* Accept deferred connection */
+-#define DSO_CONREJECT 6 /* Reject deferred connection */
+-#define DSO_LINKINFO 7 /* Set/Get link information */
+-#define DSO_STREAM 8 /* Set socket type to stream */
+-#define DSO_SEQPACKET 9 /* Set socket type to sequenced packet */
+-#define DSO_MAXWINDOW 11 /* Maximum window size allowed */
+-#define DSO_NODELAY 12 /* Turn off nagle */
+-#define DSO_CORK 13 /* Wait for more data! */
+-#define DSO_SERVICES 14 /* NSP Services field */
+-#define DSO_INFO 15 /* NSP Info field */
+-#define DSO_MAX 15 /* Maximum option number */
+-
+-
+-/* LINK States */
+-#define LL_INACTIVE 0
+-#define LL_CONNECTING 1
+-#define LL_RUNNING 2
+-#define LL_DISCONNECTING 3
+-
+-#define ACC_IMMED 0
+-#define ACC_DEFER 1
+-
+-#define SDF_WILD 1 /* Wild card object */
+-#define SDF_PROXY 2 /* Addr eligible for proxy */
+-#define SDF_UICPROXY 4 /* Use uic-based proxy */
+-
+-/* Structures */
+-
+-
+-struct dn_naddr {
+- __le16 a_len;
+- __u8 a_addr[DN_MAXADDL]; /* Two bytes little endian */
+-};
+-
+-struct sockaddr_dn {
+- __u16 sdn_family;
+- __u8 sdn_flags;
+- __u8 sdn_objnum;
+- __le16 sdn_objnamel;
+- __u8 sdn_objname[DN_MAXOBJL];
+- struct dn_naddr sdn_add;
+-};
+-#define sdn_nodeaddrl sdn_add.a_len /* Node address length */
+-#define sdn_nodeaddr sdn_add.a_addr /* Node address */
+-
+-
+-
+-/*
+- * DECnet set/get DSO_CONDATA, DSO_DISDATA (optional data) structure
+- */
+-struct optdata_dn {
+- __le16 opt_status; /* Extended status return */
+-#define opt_sts opt_status
+- __le16 opt_optl; /* Length of user data */
+- __u8 opt_data[16]; /* User data */
+-};
+-
+-struct accessdata_dn {
+- __u8 acc_accl;
+- __u8 acc_acc[DN_MAXACCL];
+- __u8 acc_passl;
+- __u8 acc_pass[DN_MAXACCL];
+- __u8 acc_userl;
+- __u8 acc_user[DN_MAXACCL];
+-};
+-
+-/*
+- * DECnet logical link information structure
+- */
+-struct linkinfo_dn {
+- __u16 idn_segsize; /* Segment size for link */
+- __u8 idn_linkstate; /* Logical link state */
+-};
+-
+-/*
+- * Ethernet address format (for DECnet)
+- */
+-union etheraddress {
+- __u8 dne_addr[ETH_ALEN]; /* Full ethernet address */
+- struct {
+- __u8 dne_hiord[4]; /* DECnet HIORD prefix */
+- __u8 dne_nodeaddr[2]; /* DECnet node address */
+- } dne_remote;
+-};
+-
+-
+-/*
+- * DECnet physical socket address format
+- */
+-struct dn_addr {
+- __le16 dna_family; /* AF_DECnet */
+- union etheraddress dna_netaddr; /* DECnet ethernet address */
+-};
+-
+-#define DECNET_IOCTL_BASE 0x89 /* PROTOPRIVATE range */
+-
+-#define SIOCSNETADDR _IOW(DECNET_IOCTL_BASE, 0xe0, struct dn_naddr)
+-#define SIOCGNETADDR _IOR(DECNET_IOCTL_BASE, 0xe1, struct dn_naddr)
+-#define OSIOCSNETADDR _IOW(DECNET_IOCTL_BASE, 0xe0, int)
+-#define OSIOCGNETADDR _IOR(DECNET_IOCTL_BASE, 0xe1, int)
+-
+-#endif /* _LINUX_DN_H */
+diff --git a/include/uapi/linux/netfilter_decnet.h b/include/uapi/linux/netfilter_decnet.h
+deleted file mode 100644
+index 9089c38f6abea..0000000000000
+--- a/include/uapi/linux/netfilter_decnet.h
++++ /dev/null
+@@ -1,80 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+-#ifndef __LINUX_DECNET_NETFILTER_H
+-#define __LINUX_DECNET_NETFILTER_H
+-
+-/* DECnet-specific defines for netfilter.
+- * This file (C) Steve Whitehouse 1999 derived from the
+- * ipv4 netfilter header file which is
+- * (C)1998 Rusty Russell -- This code is GPL.
+- */
+-
+-#include <linux/netfilter.h>
+-
+-/* only for userspace compatibility */
+-#ifndef __KERNEL__
+-
+-#include <limits.h> /* for INT_MIN, INT_MAX */
+-
+-/* IP Cache bits. */
+-/* Src IP address. */
+-#define NFC_DN_SRC 0x0001
+-/* Dest IP address. */
+-#define NFC_DN_DST 0x0002
+-/* Input device. */
+-#define NFC_DN_IF_IN 0x0004
+-/* Output device. */
+-#define NFC_DN_IF_OUT 0x0008
+-#endif /* ! __KERNEL__ */
+-
+-/* DECnet Hooks */
+-/* After promisc drops, checksum checks. */
+-#define NF_DN_PRE_ROUTING 0
+-/* If the packet is destined for this box. */
+-#define NF_DN_LOCAL_IN 1
+-/* If the packet is destined for another interface. */
+-#define NF_DN_FORWARD 2
+-/* Packets coming from a local process. */
+-#define NF_DN_LOCAL_OUT 3
+-/* Packets about to hit the wire. */
+-#define NF_DN_POST_ROUTING 4
+-/* Input Hello Packets */
+-#define NF_DN_HELLO 5
+-/* Input Routing Packets */
+-#define NF_DN_ROUTE 6
+-#define NF_DN_NUMHOOKS 7
+-
+-enum nf_dn_hook_priorities {
+- NF_DN_PRI_FIRST = INT_MIN,
+- NF_DN_PRI_CONNTRACK = -200,
+- NF_DN_PRI_MANGLE = -150,
+- NF_DN_PRI_NAT_DST = -100,
+- NF_DN_PRI_FILTER = 0,
+- NF_DN_PRI_NAT_SRC = 100,
+- NF_DN_PRI_DNRTMSG = 200,
+- NF_DN_PRI_LAST = INT_MAX,
+-};
+-
+-struct nf_dn_rtmsg {
+- int nfdn_ifindex;
+-};
+-
+-#define NFDN_RTMSG(r) ((unsigned char *)(r) + NLMSG_ALIGN(sizeof(struct nf_dn_rtmsg)))
+-
+-#ifndef __KERNEL__
+-/* backwards compatibility for userspace */
+-#define DNRMG_L1_GROUP 0x01
+-#define DNRMG_L2_GROUP 0x02
+-#endif
+-
+-enum {
+- DNRNG_NLGRP_NONE,
+-#define DNRNG_NLGRP_NONE DNRNG_NLGRP_NONE
+- DNRNG_NLGRP_L1,
+-#define DNRNG_NLGRP_L1 DNRNG_NLGRP_L1
+- DNRNG_NLGRP_L2,
+-#define DNRNG_NLGRP_L2 DNRNG_NLGRP_L2
+- __DNRNG_NLGRP_MAX
+-};
+-#define DNRNG_NLGRP_MAX (__DNRNG_NLGRP_MAX - 1)
+-
+-#endif /*__LINUX_DECNET_NETFILTER_H*/
+diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
+index 3481cde43a841..c1e0c5df3d53f 100644
+--- a/include/uapi/linux/netlink.h
++++ b/include/uapi/linux/netlink.h
+@@ -20,7 +20,7 @@
+ #define NETLINK_CONNECTOR 11
+ #define NETLINK_NETFILTER 12 /* netfilter subsystem */
+ #define NETLINK_IP6_FW 13
+-#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
++#define NETLINK_DNRTMSG 14 /* DECnet routing messages (obsolete) */
+ #define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
+ #define NETLINK_GENERIC 16
+ /* leave room for NETLINK_DM (DM Events) */
+diff --git a/net/Kconfig b/net/Kconfig
+index 9dba2715919d7..38c873f20d5a5 100644
+--- a/net/Kconfig
++++ b/net/Kconfig
+@@ -195,7 +195,6 @@ config BRIDGE_NETFILTER
+ source "net/netfilter/Kconfig"
+ source "net/ipv4/netfilter/Kconfig"
+ source "net/ipv6/netfilter/Kconfig"
+-source "net/decnet/netfilter/Kconfig"
+ source "net/bridge/netfilter/Kconfig"
+
+ endif
+@@ -210,7 +209,6 @@ source "net/802/Kconfig"
+ source "net/bridge/Kconfig"
+ source "net/dsa/Kconfig"
+ source "net/8021q/Kconfig"
+-source "net/decnet/Kconfig"
+ source "net/llc/Kconfig"
+ source "net/ipx/Kconfig"
+ source "drivers/net/appletalk/Kconfig"
+diff --git a/net/Makefile b/net/Makefile
+index d05dc71996631..1cbe09c68b5c4 100644
+--- a/net/Makefile
++++ b/net/Makefile
+@@ -39,7 +39,6 @@ obj-$(CONFIG_AF_KCM) += kcm/
+ obj-$(CONFIG_STREAM_PARSER) += strparser/
+ obj-$(CONFIG_ATM) += atm/
+ obj-$(CONFIG_L2TP) += l2tp/
+-obj-$(CONFIG_DECNET) += decnet/
+ obj-$(CONFIG_PHONET) += phonet/
+ ifneq ($(CONFIG_VLAN_8021Q),)
+ obj-y += 8021q/
+diff --git a/net/core/dev.c b/net/core/dev.c
+index 04c43c7ecaca3..a0413f65f98d6 100644
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -7958,7 +7958,6 @@ void netdev_run_todo(void)
+ BUG_ON(!list_empty(&dev->ptype_specific));
+ WARN_ON(rcu_access_pointer(dev->ip_ptr));
+ WARN_ON(rcu_access_pointer(dev->ip6_ptr));
+- WARN_ON(dev->dn_ptr);
+
+ if (dev->priv_destructor)
+ dev->priv_destructor(dev);
+diff --git a/net/core/neighbour.c b/net/core/neighbour.c
+index 8e71f392ce212..5e3b0357e9d4d 100644
+--- a/net/core/neighbour.c
++++ b/net/core/neighbour.c
+@@ -474,37 +474,6 @@ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
+ }
+ EXPORT_SYMBOL(neigh_lookup);
+
+-struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net,
+- const void *pkey)
+-{
+- struct neighbour *n;
+- int key_len = tbl->key_len;
+- u32 hash_val;
+- struct neigh_hash_table *nht;
+-
+- NEIGH_CACHE_STAT_INC(tbl, lookups);
+-
+- rcu_read_lock_bh();
+- nht = rcu_dereference_bh(tbl->nht);
+- hash_val = tbl->hash(pkey, NULL, nht->hash_rnd) >> (32 - nht->hash_shift);
+-
+- for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]);
+- n != NULL;
+- n = rcu_dereference_bh(n->next)) {
+- if (!memcmp(n->primary_key, pkey, key_len) &&
+- net_eq(dev_net(n->dev), net)) {
+- if (!refcount_inc_not_zero(&n->refcnt))
+- n = NULL;
+- NEIGH_CACHE_STAT_INC(tbl, hits);
+- break;
+- }
+- }
+-
+- rcu_read_unlock_bh();
+- return n;
+-}
+-EXPORT_SYMBOL(neigh_lookup_nodev);
+-
+ struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
+ struct net_device *dev, bool want_ref)
+ {
+@@ -1656,9 +1625,6 @@ static struct neigh_table *neigh_find_table(int family)
+ case AF_INET6:
+ tbl = neigh_tables[NEIGH_ND_TABLE];
+ break;
+- case AF_DECnet:
+- tbl = neigh_tables[NEIGH_DN_TABLE];
+- break;
+ }
+
+ return tbl;
+diff --git a/net/decnet/Kconfig b/net/decnet/Kconfig
+deleted file mode 100644
+index f3393e154f0f8..0000000000000
+--- a/net/decnet/Kconfig
++++ /dev/null
+@@ -1,43 +0,0 @@
+-#
+-# DECnet configuration
+-#
+-config DECNET
+- tristate "DECnet Support"
+- ---help---
+- The DECnet networking protocol was used in many products made by
+- Digital (now Compaq). It provides reliable stream and sequenced
+- packet communications over which run a variety of services similar
+- to those which run over TCP/IP.
+-
+- To find some tools to use with the kernel layer support, please
+- look at Patrick Caulfield's web site:
+- <http://linux-decnet.sourceforge.net/>.
+-
+- More detailed documentation is available in
+- <file:Documentation/networking/decnet.txt>.
+-
+- Be sure to say Y to "/proc file system support" and "Sysctl support"
+- below when using DECnet, since you will need sysctl support to aid
+- in configuration at run time.
+-
+- The DECnet code is also available as a module ( = code which can be
+- inserted in and removed from the running kernel whenever you want).
+- The module is called decnet.
+-
+-config DECNET_ROUTER
+- bool "DECnet: router support"
+- depends on DECNET
+- select FIB_RULES
+- ---help---
+- Add support for turning your DECnet Endnode into a level 1 or 2
+- router. This is an experimental, but functional option. If you
+- do say Y here, then make sure that you also say Y to "Kernel/User
+- network link driver", "Routing messages" and "Network packet
+- filtering". The first two are required to allow configuration via
+- rtnetlink (you will need Alexey Kuznetsov's iproute2 package
+- from <ftp://ftp.tux.org/pub/net/ip-routing/>). The "Network packet
+- filtering" option will be required for the forthcoming routing daemon
+- to work.
+-
+- See <file:Documentation/networking/decnet.txt> for more information.
+-
+diff --git a/net/decnet/Makefile b/net/decnet/Makefile
+deleted file mode 100644
+index 9e38122d942be..0000000000000
+--- a/net/decnet/Makefile
++++ /dev/null
+@@ -1,11 +0,0 @@
+-# SPDX-License-Identifier: GPL-2.0
+-
+-obj-$(CONFIG_DECNET) += decnet.o
+-
+-decnet-y := af_decnet.o dn_nsp_in.o dn_nsp_out.o \
+- dn_route.o dn_dev.o dn_neigh.o dn_timer.o
+-decnet-$(CONFIG_DECNET_ROUTER) += dn_fib.o dn_rules.o dn_table.o
+-decnet-y += sysctl_net_decnet.o
+-
+-obj-$(CONFIG_NETFILTER) += netfilter/
+-
+diff --git a/net/decnet/README b/net/decnet/README
+deleted file mode 100644
+index 60e7ec88c81fd..0000000000000
+--- a/net/decnet/README
++++ /dev/null
+@@ -1,8 +0,0 @@
+- Linux DECnet Project
+- ======================
+-
+-The documentation for this kernel subsystem is available in the
+-Documentation/networking subdirectory of this distribution and also
+-on line at http://www.chygwyn.com/DECnet/
+-
+-Steve Whitehouse <SteveW@ACM.org>
+diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c
+deleted file mode 100644
+index 9c7b8ff4556a7..0000000000000
+--- a/net/decnet/af_decnet.c
++++ /dev/null
+@@ -1,2423 +0,0 @@
+-
+-/*
+- * DECnet An implementation of the DECnet protocol suite for the LINUX
+- * operating system. DECnet is implemented using the BSD Socket
+- * interface as the means of communication with the user level.
+- *
+- * DECnet Socket Layer Interface
+- *
+- * Authors: Eduardo Marcelo Serrat <emserrat@geocities.com>
+- * Patrick Caulfield <patrick@pandh.demon.co.uk>
+- *
+- * Changes:
+- * Steve Whitehouse: Copied from Eduardo Serrat and Patrick Caulfield's
+- * version of the code. Original copyright preserved
+- * below.
+- * Steve Whitehouse: Some bug fixes, cleaning up some code to make it
+- * compatible with my routing layer.
+- * Steve Whitehouse: Merging changes from Eduardo Serrat and Patrick
+- * Caulfield.
+- * Steve Whitehouse: Further bug fixes, checking module code still works
+- * with new routing layer.
+- * Steve Whitehouse: Additional set/get_sockopt() calls.
+- * Steve Whitehouse: Fixed TIOCINQ ioctl to be same as Eduardo's new
+- * code.
+- * Steve Whitehouse: recvmsg() changed to try and behave in a POSIX like
+- * way. Didn't manage it entirely, but its better.
+- * Steve Whitehouse: ditto for sendmsg().
+- * Steve Whitehouse: A selection of bug fixes to various things.
+- * Steve Whitehouse: Added TIOCOUTQ ioctl.
+- * Steve Whitehouse: Fixes to username2sockaddr & sockaddr2username.
+- * Steve Whitehouse: Fixes to connect() error returns.
+- * Patrick Caulfield: Fixes to delayed acceptance logic.
+- * David S. Miller: New socket locking
+- * Steve Whitehouse: Socket list hashing/locking
+- * Arnaldo C. Melo: use capable, not suser
+- * Steve Whitehouse: Removed unused code. Fix to use sk->allocation
+- * when required.
+- * Patrick Caulfield: /proc/net/decnet now has object name/number
+- * Steve Whitehouse: Fixed local port allocation, hashed sk list
+- * Matthew Wilcox: Fixes for dn_ioctl()
+- * Steve Whitehouse: New connect/accept logic to allow timeouts and
+- * prepare for sendpage etc.
+- */
+-
+-
+-/******************************************************************************
+- (c) 1995-1998 E.M. Serrat emserrat@geocities.com
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License as published by
+- the Free Software Foundation; either version 2 of the License, or
+- any later version.
+-
+- This program is distributed in the hope that it will be useful,
+- but WITHOUT ANY WARRANTY; without even the implied warranty of
+- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- GNU General Public License for more details.
+-
+-HISTORY:
+-
+-Version Kernel Date Author/Comments
+-------- ------ ---- ---------------
+-Version 0.0.1 2.0.30 01-dic-97 Eduardo Marcelo Serrat
+- (emserrat@geocities.com)
+-
+- First Development of DECnet Socket La-
+- yer for Linux. Only supports outgoing
+- connections.
+-
+-Version 0.0.2 2.1.105 20-jun-98 Patrick J. Caulfield
+- (patrick@pandh.demon.co.uk)
+-
+- Port to new kernel development version.
+-
+-Version 0.0.3 2.1.106 25-jun-98 Eduardo Marcelo Serrat
+- (emserrat@geocities.com)
+- _
+- Added support for incoming connections
+- so we can start developing server apps
+- on Linux.
+- -
+- Module Support
+-Version 0.0.4 2.1.109 21-jul-98 Eduardo Marcelo Serrat
+- (emserrat@geocities.com)
+- _
+- Added support for X11R6.4. Now we can
+- use DECnet transport for X on Linux!!!
+- -
+-Version 0.0.5 2.1.110 01-aug-98 Eduardo Marcelo Serrat
+- (emserrat@geocities.com)
+- Removed bugs on flow control
+- Removed bugs on incoming accessdata
+- order
+- -
+-Version 0.0.6 2.1.110 07-aug-98 Eduardo Marcelo Serrat
+- dn_recvmsg fixes
+-
+- Patrick J. Caulfield
+- dn_bind fixes
+-*******************************************************************************/
+-
+-#include <linux/module.h>
+-#include <linux/errno.h>
+-#include <linux/types.h>
+-#include <linux/slab.h>
+-#include <linux/socket.h>
+-#include <linux/in.h>
+-#include <linux/kernel.h>
+-#include <linux/sched/signal.h>
+-#include <linux/timer.h>
+-#include <linux/string.h>
+-#include <linux/sockios.h>
+-#include <linux/net.h>
+-#include <linux/netdevice.h>
+-#include <linux/inet.h>
+-#include <linux/route.h>
+-#include <linux/netfilter.h>
+-#include <linux/seq_file.h>
+-#include <net/sock.h>
+-#include <net/tcp_states.h>
+-#include <net/flow.h>
+-#include <asm/ioctls.h>
+-#include <linux/capability.h>
+-#include <linux/mm.h>
+-#include <linux/interrupt.h>
+-#include <linux/proc_fs.h>
+-#include <linux/stat.h>
+-#include <linux/init.h>
+-#include <linux/poll.h>
+-#include <linux/jiffies.h>
+-#include <net/net_namespace.h>
+-#include <net/neighbour.h>
+-#include <net/dst.h>
+-#include <net/fib_rules.h>
+-#include <net/tcp.h>
+-#include <net/dn.h>
+-#include <net/dn_nsp.h>
+-#include <net/dn_dev.h>
+-#include <net/dn_route.h>
+-#include <net/dn_fib.h>
+-#include <net/dn_neigh.h>
+-
+-struct dn_sock {
+- struct sock sk;
+- struct dn_scp scp;
+-};
+-
+-static void dn_keepalive(struct sock *sk);
+-
+-#define DN_SK_HASH_SHIFT 8
+-#define DN_SK_HASH_SIZE (1 << DN_SK_HASH_SHIFT)
+-#define DN_SK_HASH_MASK (DN_SK_HASH_SIZE - 1)
+-
+-
+-static const struct proto_ops dn_proto_ops;
+-static DEFINE_RWLOCK(dn_hash_lock);
+-static struct hlist_head dn_sk_hash[DN_SK_HASH_SIZE];
+-static struct hlist_head dn_wild_sk;
+-static atomic_long_t decnet_memory_allocated;
+-
+-static int __dn_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen, int flags);
+-static int __dn_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen, int flags);
+-
+-static struct hlist_head *dn_find_list(struct sock *sk)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+-
+- if (scp->addr.sdn_flags & SDF_WILD)
+- return hlist_empty(&dn_wild_sk) ? &dn_wild_sk : NULL;
+-
+- return &dn_sk_hash[le16_to_cpu(scp->addrloc) & DN_SK_HASH_MASK];
+-}
+-
+-/*
+- * Valid ports are those greater than zero and not already in use.
+- */
+-static int check_port(__le16 port)
+-{
+- struct sock *sk;
+-
+- if (port == 0)
+- return -1;
+-
+- sk_for_each(sk, &dn_sk_hash[le16_to_cpu(port) & DN_SK_HASH_MASK]) {
+- struct dn_scp *scp = DN_SK(sk);
+- if (scp->addrloc == port)
+- return -1;
+- }
+- return 0;
+-}
+-
+-static unsigned short port_alloc(struct sock *sk)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+-static unsigned short port = 0x2000;
+- unsigned short i_port = port;
+-
+- while(check_port(cpu_to_le16(++port)) != 0) {
+- if (port == i_port)
+- return 0;
+- }
+-
+- scp->addrloc = cpu_to_le16(port);
+-
+- return 1;
+-}
+-
+-/*
+- * Since this is only ever called from user
+- * level, we don't need a write_lock() version
+- * of this.
+- */
+-static int dn_hash_sock(struct sock *sk)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- struct hlist_head *list;
+- int rv = -EUSERS;
+-
+- BUG_ON(sk_hashed(sk));
+-
+- write_lock_bh(&dn_hash_lock);
+-
+- if (!scp->addrloc && !port_alloc(sk))
+- goto out;
+-
+- rv = -EADDRINUSE;
+- if ((list = dn_find_list(sk)) == NULL)
+- goto out;
+-
+- sk_add_node(sk, list);
+- rv = 0;
+-out:
+- write_unlock_bh(&dn_hash_lock);
+- return rv;
+-}
+-
+-static void dn_unhash_sock(struct sock *sk)
+-{
+- write_lock(&dn_hash_lock);
+- sk_del_node_init(sk);
+- write_unlock(&dn_hash_lock);
+-}
+-
+-static void dn_unhash_sock_bh(struct sock *sk)
+-{
+- write_lock_bh(&dn_hash_lock);
+- sk_del_node_init(sk);
+- write_unlock_bh(&dn_hash_lock);
+-}
+-
+-static struct hlist_head *listen_hash(struct sockaddr_dn *addr)
+-{
+- int i;
+- unsigned int hash = addr->sdn_objnum;
+-
+- if (hash == 0) {
+- hash = addr->sdn_objnamel;
+- for(i = 0; i < le16_to_cpu(addr->sdn_objnamel); i++) {
+- hash ^= addr->sdn_objname[i];
+- hash ^= (hash << 3);
+- }
+- }
+-
+- return &dn_sk_hash[hash & DN_SK_HASH_MASK];
+-}
+-
+-/*
+- * Called to transform a socket from bound (i.e. with a local address)
+- * into a listening socket (doesn't need a local port number) and rehashes
+- * based upon the object name/number.
+- */
+-static void dn_rehash_sock(struct sock *sk)
+-{
+- struct hlist_head *list;
+- struct dn_scp *scp = DN_SK(sk);
+-
+- if (scp->addr.sdn_flags & SDF_WILD)
+- return;
+-
+- write_lock_bh(&dn_hash_lock);
+- sk_del_node_init(sk);
+- DN_SK(sk)->addrloc = 0;
+- list = listen_hash(&DN_SK(sk)->addr);
+- sk_add_node(sk, list);
+- write_unlock_bh(&dn_hash_lock);
+-}
+-
+-int dn_sockaddr2username(struct sockaddr_dn *sdn, unsigned char *buf, unsigned char type)
+-{
+- int len = 2;
+-
+- *buf++ = type;
+-
+- switch (type) {
+- case 0:
+- *buf++ = sdn->sdn_objnum;
+- break;
+- case 1:
+- *buf++ = 0;
+- *buf++ = le16_to_cpu(sdn->sdn_objnamel);
+- memcpy(buf, sdn->sdn_objname, le16_to_cpu(sdn->sdn_objnamel));
+- len = 3 + le16_to_cpu(sdn->sdn_objnamel);
+- break;
+- case 2:
+- memset(buf, 0, 5);
+- buf += 5;
+- *buf++ = le16_to_cpu(sdn->sdn_objnamel);
+- memcpy(buf, sdn->sdn_objname, le16_to_cpu(sdn->sdn_objnamel));
+- len = 7 + le16_to_cpu(sdn->sdn_objnamel);
+- break;
+- }
+-
+- return len;
+-}
+-
+-/*
+- * On reception of usernames, we handle types 1 and 0 for destination
+- * addresses only. Types 2 and 4 are used for source addresses, but the
+- * UIC, GIC are ignored and they are both treated the same way. Type 3
+- * is never used as I've no idea what its purpose might be or what its
+- * format is.
+- */
+-int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *sdn, unsigned char *fmt)
+-{
+- unsigned char type;
+- int size = len;
+- int namel = 12;
+-
+- sdn->sdn_objnum = 0;
+- sdn->sdn_objnamel = cpu_to_le16(0);
+- memset(sdn->sdn_objname, 0, DN_MAXOBJL);
+-
+- if (len < 2)
+- return -1;
+-
+- len -= 2;
+- *fmt = *data++;
+- type = *data++;
+-
+- switch (*fmt) {
+- case 0:
+- sdn->sdn_objnum = type;
+- return 2;
+- case 1:
+- namel = 16;
+- break;
+- case 2:
+- len -= 4;
+- data += 4;
+- break;
+- case 4:
+- len -= 8;
+- data += 8;
+- break;
+- default:
+- return -1;
+- }
+-
+- len -= 1;
+-
+- if (len < 0)
+- return -1;
+-
+- sdn->sdn_objnamel = cpu_to_le16(*data++);
+- len -= le16_to_cpu(sdn->sdn_objnamel);
+-
+- if ((len < 0) || (le16_to_cpu(sdn->sdn_objnamel) > namel))
+- return -1;
+-
+- memcpy(sdn->sdn_objname, data, le16_to_cpu(sdn->sdn_objnamel));
+-
+- return size - len;
+-}
+-
+-struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr)
+-{
+- struct hlist_head *list = listen_hash(addr);
+- struct sock *sk;
+-
+- read_lock(&dn_hash_lock);
+- sk_for_each(sk, list) {
+- struct dn_scp *scp = DN_SK(sk);
+- if (sk->sk_state != TCP_LISTEN)
+- continue;
+- if (scp->addr.sdn_objnum) {
+- if (scp->addr.sdn_objnum != addr->sdn_objnum)
+- continue;
+- } else {
+- if (addr->sdn_objnum)
+- continue;
+- if (scp->addr.sdn_objnamel != addr->sdn_objnamel)
+- continue;
+- if (memcmp(scp->addr.sdn_objname, addr->sdn_objname, le16_to_cpu(addr->sdn_objnamel)) != 0)
+- continue;
+- }
+- sock_hold(sk);
+- read_unlock(&dn_hash_lock);
+- return sk;
+- }
+-
+- sk = sk_head(&dn_wild_sk);
+- if (sk) {
+- if (sk->sk_state == TCP_LISTEN)
+- sock_hold(sk);
+- else
+- sk = NULL;
+- }
+-
+- read_unlock(&dn_hash_lock);
+- return sk;
+-}
+-
+-struct sock *dn_find_by_skb(struct sk_buff *skb)
+-{
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- struct sock *sk;
+- struct dn_scp *scp;
+-
+- read_lock(&dn_hash_lock);
+- sk_for_each(sk, &dn_sk_hash[le16_to_cpu(cb->dst_port) & DN_SK_HASH_MASK]) {
+- scp = DN_SK(sk);
+- if (cb->src != dn_saddr2dn(&scp->peer))
+- continue;
+- if (cb->dst_port != scp->addrloc)
+- continue;
+- if (scp->addrrem && (cb->src_port != scp->addrrem))
+- continue;
+- sock_hold(sk);
+- goto found;
+- }
+- sk = NULL;
+-found:
+- read_unlock(&dn_hash_lock);
+- return sk;
+-}
+-
+-
+-
+-static void dn_destruct(struct sock *sk)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+-
+- skb_queue_purge(&scp->data_xmit_queue);
+- skb_queue_purge(&scp->other_xmit_queue);
+- skb_queue_purge(&scp->other_receive_queue);
+-
+- dst_release(rcu_dereference_check(sk->sk_dst_cache, 1));
+-}
+-
+-static unsigned long dn_memory_pressure;
+-
+-static void dn_enter_memory_pressure(struct sock *sk)
+-{
+- if (!dn_memory_pressure) {
+- dn_memory_pressure = 1;
+- }
+-}
+-
+-static struct proto dn_proto = {
+- .name = "NSP",
+- .owner = THIS_MODULE,
+- .enter_memory_pressure = dn_enter_memory_pressure,
+- .memory_pressure = &dn_memory_pressure,
+- .memory_allocated = &decnet_memory_allocated,
+- .sysctl_mem = sysctl_decnet_mem,
+- .sysctl_wmem = sysctl_decnet_wmem,
+- .sysctl_rmem = sysctl_decnet_rmem,
+- .max_header = DN_MAX_NSP_DATA_HEADER + 64,
+- .obj_size = sizeof(struct dn_sock),
+-};
+-
+-static struct sock *dn_alloc_sock(struct net *net, struct socket *sock, gfp_t gfp, int kern)
+-{
+- struct dn_scp *scp;
+- struct sock *sk = sk_alloc(net, PF_DECnet, gfp, &dn_proto, kern);
+-
+- if (!sk)
+- goto out;
+-
+- if (sock)
+- sock->ops = &dn_proto_ops;
+- sock_init_data(sock, sk);
+-
+- sk->sk_backlog_rcv = dn_nsp_backlog_rcv;
+- sk->sk_destruct = dn_destruct;
+- sk->sk_no_check_tx = 1;
+- sk->sk_family = PF_DECnet;
+- sk->sk_protocol = 0;
+- sk->sk_allocation = gfp;
+- sk->sk_sndbuf = sysctl_decnet_wmem[1];
+- sk->sk_rcvbuf = sysctl_decnet_rmem[1];
+-
+- /* Initialization of DECnet Session Control Port */
+- scp = DN_SK(sk);
+- scp->state = DN_O; /* Open */
+- scp->numdat = 1; /* Next data seg to tx */
+- scp->numoth = 1; /* Next oth data to tx */
+- scp->ackxmt_dat = 0; /* Last data seg ack'ed */
+- scp->ackxmt_oth = 0; /* Last oth data ack'ed */
+- scp->ackrcv_dat = 0; /* Highest data ack recv*/
+- scp->ackrcv_oth = 0; /* Last oth data ack rec*/
+- scp->flowrem_sw = DN_SEND;
+- scp->flowloc_sw = DN_SEND;
+- scp->flowrem_dat = 0;
+- scp->flowrem_oth = 1;
+- scp->flowloc_dat = 0;
+- scp->flowloc_oth = 1;
+- scp->services_rem = 0;
+- scp->services_loc = 1 | NSP_FC_NONE;
+- scp->info_rem = 0;
+- scp->info_loc = 0x03; /* NSP version 4.1 */
+- scp->segsize_rem = 230 - DN_MAX_NSP_DATA_HEADER; /* Default: Updated by remote segsize */
+- scp->nonagle = 0;
+- scp->multi_ireq = 1;
+- scp->accept_mode = ACC_IMMED;
+- scp->addr.sdn_family = AF_DECnet;
+- scp->peer.sdn_family = AF_DECnet;
+- scp->accessdata.acc_accl = 5;
+- memcpy(scp->accessdata.acc_acc, "LINUX", 5);
+-
+- scp->max_window = NSP_MAX_WINDOW;
+- scp->snd_window = NSP_MIN_WINDOW;
+- scp->nsp_srtt = NSP_INITIAL_SRTT;
+- scp->nsp_rttvar = NSP_INITIAL_RTTVAR;
+- scp->nsp_rxtshift = 0;
+-
+- skb_queue_head_init(&scp->data_xmit_queue);
+- skb_queue_head_init(&scp->other_xmit_queue);
+- skb_queue_head_init(&scp->other_receive_queue);
+-
+- scp->persist = 0;
+- scp->persist_fxn = NULL;
+- scp->keepalive = 10 * HZ;
+- scp->keepalive_fxn = dn_keepalive;
+-
+- init_timer(&scp->delack_timer);
+- scp->delack_pending = 0;
+- scp->delack_fxn = dn_nsp_delayed_ack;
+-
+- dn_start_slow_timer(sk);
+-out:
+- return sk;
+-}
+-
+-/*
+- * Keepalive timer.
+- * FIXME: Should respond to SO_KEEPALIVE etc.
+- */
+-static void dn_keepalive(struct sock *sk)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+-
+- /*
+- * By checking the other_data transmit queue is empty
+- * we are double checking that we are not sending too
+- * many of these keepalive frames.
+- */
+- if (skb_queue_empty(&scp->other_xmit_queue))
+- dn_nsp_send_link(sk, DN_NOCHANGE, 0);
+-}
+-
+-
+-/*
+- * Timer for shutdown/destroyed sockets.
+- * When socket is dead & no packets have been sent for a
+- * certain amount of time, they are removed by this
+- * routine. Also takes care of sending out DI & DC
+- * frames at correct times.
+- */
+-int dn_destroy_timer(struct sock *sk)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+-
+- scp->persist = dn_nsp_persist(sk);
+-
+- switch (scp->state) {
+- case DN_DI:
+- dn_nsp_send_disc(sk, NSP_DISCINIT, 0, GFP_ATOMIC);
+- if (scp->nsp_rxtshift >= decnet_di_count)
+- scp->state = DN_CN;
+- return 0;
+-
+- case DN_DR:
+- dn_nsp_send_disc(sk, NSP_DISCINIT, 0, GFP_ATOMIC);
+- if (scp->nsp_rxtshift >= decnet_dr_count)
+- scp->state = DN_DRC;
+- return 0;
+-
+- case DN_DN:
+- if (scp->nsp_rxtshift < decnet_dn_count) {
+- /* printk(KERN_DEBUG "dn_destroy_timer: DN\n"); */
+- dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC,
+- GFP_ATOMIC);
+- return 0;
+- }
+- }
+-
+- scp->persist = (HZ * decnet_time_wait);
+-
+- if (sk->sk_socket)
+- return 0;
+-
+- if (time_after_eq(jiffies, scp->stamp + HZ * decnet_time_wait)) {
+- dn_unhash_sock(sk);
+- sock_put(sk);
+- return 1;
+- }
+-
+- return 0;
+-}
+-
+-static void dn_destroy_sock(struct sock *sk)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+-
+- scp->nsp_rxtshift = 0; /* reset back off */
+-
+- if (sk->sk_socket) {
+- if (sk->sk_socket->state != SS_UNCONNECTED)
+- sk->sk_socket->state = SS_DISCONNECTING;
+- }
+-
+- sk->sk_state = TCP_CLOSE;
+-
+- switch (scp->state) {
+- case DN_DN:
+- dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC,
+- sk->sk_allocation);
+- scp->persist_fxn = dn_destroy_timer;
+- scp->persist = dn_nsp_persist(sk);
+- break;
+- case DN_CR:
+- scp->state = DN_DR;
+- goto disc_reject;
+- case DN_RUN:
+- scp->state = DN_DI;
+- case DN_DI:
+- case DN_DR:
+-disc_reject:
+- dn_nsp_send_disc(sk, NSP_DISCINIT, 0, sk->sk_allocation);
+- case DN_NC:
+- case DN_NR:
+- case DN_RJ:
+- case DN_DIC:
+- case DN_CN:
+- case DN_DRC:
+- case DN_CI:
+- case DN_CD:
+- scp->persist_fxn = dn_destroy_timer;
+- scp->persist = dn_nsp_persist(sk);
+- break;
+- default:
+- printk(KERN_DEBUG "DECnet: dn_destroy_sock passed socket in invalid state\n");
+- case DN_O:
+- dn_stop_slow_timer(sk);
+-
+- dn_unhash_sock_bh(sk);
+- sock_put(sk);
+-
+- break;
+- }
+-}
+-
+-char *dn_addr2asc(__u16 addr, char *buf)
+-{
+- unsigned short node, area;
+-
+- node = addr & 0x03ff;
+- area = addr >> 10;
+- sprintf(buf, "%hd.%hd", area, node);
+-
+- return buf;
+-}
+-
+-
+-
+-static int dn_create(struct net *net, struct socket *sock, int protocol,
+- int kern)
+-{
+- struct sock *sk;
+-
+- if (protocol < 0 || protocol > SK_PROTOCOL_MAX)
+- return -EINVAL;
+-
+- if (!net_eq(net, &init_net))
+- return -EAFNOSUPPORT;
+-
+- switch (sock->type) {
+- case SOCK_SEQPACKET:
+- if (protocol != DNPROTO_NSP)
+- return -EPROTONOSUPPORT;
+- break;
+- case SOCK_STREAM:
+- break;
+- default:
+- return -ESOCKTNOSUPPORT;
+- }
+-
+-
+- if ((sk = dn_alloc_sock(net, sock, GFP_KERNEL, kern)) == NULL)
+- return -ENOBUFS;
+-
+- sk->sk_protocol = protocol;
+-
+- return 0;
+-}
+-
+-
+-static int
+-dn_release(struct socket *sock)
+-{
+- struct sock *sk = sock->sk;
+-
+- if (sk) {
+- sock_orphan(sk);
+- sock_hold(sk);
+- lock_sock(sk);
+- dn_destroy_sock(sk);
+- release_sock(sk);
+- sock_put(sk);
+- }
+-
+- return 0;
+-}
+-
+-static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+-{
+- struct sock *sk = sock->sk;
+- struct dn_scp *scp = DN_SK(sk);
+- struct sockaddr_dn *saddr = (struct sockaddr_dn *)uaddr;
+- struct net_device *dev, *ldev;
+- int rv;
+-
+- if (addr_len != sizeof(struct sockaddr_dn))
+- return -EINVAL;
+-
+- if (saddr->sdn_family != AF_DECnet)
+- return -EINVAL;
+-
+- if (le16_to_cpu(saddr->sdn_nodeaddrl) && (le16_to_cpu(saddr->sdn_nodeaddrl) != 2))
+- return -EINVAL;
+-
+- if (le16_to_cpu(saddr->sdn_objnamel) > DN_MAXOBJL)
+- return -EINVAL;
+-
+- if (saddr->sdn_flags & ~SDF_WILD)
+- return -EINVAL;
+-
+- if (!capable(CAP_NET_BIND_SERVICE) && (saddr->sdn_objnum ||
+- (saddr->sdn_flags & SDF_WILD)))
+- return -EACCES;
+-
+- if (!(saddr->sdn_flags & SDF_WILD)) {
+- if (le16_to_cpu(saddr->sdn_nodeaddrl)) {
+- rcu_read_lock();
+- ldev = NULL;
+- for_each_netdev_rcu(&init_net, dev) {
+- if (!dev->dn_ptr)
+- continue;
+- if (dn_dev_islocal(dev, dn_saddr2dn(saddr))) {
+- ldev = dev;
+- break;
+- }
+- }
+- rcu_read_unlock();
+- if (ldev == NULL)
+- return -EADDRNOTAVAIL;
+- }
+- }
+-
+- rv = -EINVAL;
+- lock_sock(sk);
+- if (sock_flag(sk, SOCK_ZAPPED)) {
+- memcpy(&scp->addr, saddr, addr_len);
+- sock_reset_flag(sk, SOCK_ZAPPED);
+-
+- rv = dn_hash_sock(sk);
+- if (rv)
+- sock_set_flag(sk, SOCK_ZAPPED);
+- }
+- release_sock(sk);
+-
+- return rv;
+-}
+-
+-
+-static int dn_auto_bind(struct socket *sock)
+-{
+- struct sock *sk = sock->sk;
+- struct dn_scp *scp = DN_SK(sk);
+- int rv;
+-
+- sock_reset_flag(sk, SOCK_ZAPPED);
+-
+- scp->addr.sdn_flags = 0;
+- scp->addr.sdn_objnum = 0;
+-
+- /*
+- * This stuff is to keep compatibility with Eduardo's
+- * patch. I hope I can dispense with it shortly...
+- */
+- if ((scp->accessdata.acc_accl != 0) &&
+- (scp->accessdata.acc_accl <= 12)) {
+-
+- scp->addr.sdn_objnamel = cpu_to_le16(scp->accessdata.acc_accl);
+- memcpy(scp->addr.sdn_objname, scp->accessdata.acc_acc, le16_to_cpu(scp->addr.sdn_objnamel));
+-
+- scp->accessdata.acc_accl = 0;
+- memset(scp->accessdata.acc_acc, 0, 40);
+- }
+- /* End of compatibility stuff */
+-
+- scp->addr.sdn_add.a_len = cpu_to_le16(2);
+- rv = dn_dev_bind_default((__le16 *)scp->addr.sdn_add.a_addr);
+- if (rv == 0) {
+- rv = dn_hash_sock(sk);
+- if (rv)
+- sock_set_flag(sk, SOCK_ZAPPED);
+- }
+-
+- return rv;
+-}
+-
+-static int dn_confirm_accept(struct sock *sk, long *timeo, gfp_t allocation)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- DEFINE_WAIT_FUNC(wait, woken_wake_function);
+- int err;
+-
+- if (scp->state != DN_CR)
+- return -EINVAL;
+-
+- scp->state = DN_CC;
+- scp->segsize_loc = dst_metric_advmss(__sk_dst_get(sk));
+- dn_send_conn_conf(sk, allocation);
+-
+- add_wait_queue(sk_sleep(sk), &wait);
+- for(;;) {
+- release_sock(sk);
+- if (scp->state == DN_CC)
+- *timeo = wait_woken(&wait, TASK_INTERRUPTIBLE, *timeo);
+- lock_sock(sk);
+- err = 0;
+- if (scp->state == DN_RUN)
+- break;
+- err = sock_error(sk);
+- if (err)
+- break;
+- err = sock_intr_errno(*timeo);
+- if (signal_pending(current))
+- break;
+- err = -EAGAIN;
+- if (!*timeo)
+- break;
+- }
+- remove_wait_queue(sk_sleep(sk), &wait);
+- if (err == 0) {
+- sk->sk_socket->state = SS_CONNECTED;
+- } else if (scp->state != DN_CC) {
+- sk->sk_socket->state = SS_UNCONNECTED;
+- }
+- return err;
+-}
+-
+-static int dn_wait_run(struct sock *sk, long *timeo)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- DEFINE_WAIT_FUNC(wait, woken_wake_function);
+- int err = 0;
+-
+- if (scp->state == DN_RUN)
+- goto out;
+-
+- if (!*timeo)
+- return -EALREADY;
+-
+- add_wait_queue(sk_sleep(sk), &wait);
+- for(;;) {
+- release_sock(sk);
+- if (scp->state == DN_CI || scp->state == DN_CC)
+- *timeo = wait_woken(&wait, TASK_INTERRUPTIBLE, *timeo);
+- lock_sock(sk);
+- err = 0;
+- if (scp->state == DN_RUN)
+- break;
+- err = sock_error(sk);
+- if (err)
+- break;
+- err = sock_intr_errno(*timeo);
+- if (signal_pending(current))
+- break;
+- err = -ETIMEDOUT;
+- if (!*timeo)
+- break;
+- }
+- remove_wait_queue(sk_sleep(sk), &wait);
+-out:
+- if (err == 0) {
+- sk->sk_socket->state = SS_CONNECTED;
+- } else if (scp->state != DN_CI && scp->state != DN_CC) {
+- sk->sk_socket->state = SS_UNCONNECTED;
+- }
+- return err;
+-}
+-
+-static int __dn_connect(struct sock *sk, struct sockaddr_dn *addr, int addrlen, long *timeo, int flags)
+-{
+- struct socket *sock = sk->sk_socket;
+- struct dn_scp *scp = DN_SK(sk);
+- int err = -EISCONN;
+- struct flowidn fld;
+- struct dst_entry *dst;
+-
+- if (sock->state == SS_CONNECTED)
+- goto out;
+-
+- if (sock->state == SS_CONNECTING) {
+- err = 0;
+- if (scp->state == DN_RUN) {
+- sock->state = SS_CONNECTED;
+- goto out;
+- }
+- err = -ECONNREFUSED;
+- if (scp->state != DN_CI && scp->state != DN_CC) {
+- sock->state = SS_UNCONNECTED;
+- goto out;
+- }
+- return dn_wait_run(sk, timeo);
+- }
+-
+- err = -EINVAL;
+- if (scp->state != DN_O)
+- goto out;
+-
+- if (addr == NULL || addrlen != sizeof(struct sockaddr_dn))
+- goto out;
+- if (addr->sdn_family != AF_DECnet)
+- goto out;
+- if (addr->sdn_flags & SDF_WILD)
+- goto out;
+-
+- if (sock_flag(sk, SOCK_ZAPPED)) {
+- err = dn_auto_bind(sk->sk_socket);
+- if (err)
+- goto out;
+- }
+-
+- memcpy(&scp->peer, addr, sizeof(struct sockaddr_dn));
+-
+- err = -EHOSTUNREACH;
+- memset(&fld, 0, sizeof(fld));
+- fld.flowidn_oif = sk->sk_bound_dev_if;
+- fld.daddr = dn_saddr2dn(&scp->peer);
+- fld.saddr = dn_saddr2dn(&scp->addr);
+- dn_sk_ports_copy(&fld, scp);
+- fld.flowidn_proto = DNPROTO_NSP;
+- if (dn_route_output_sock(&sk->sk_dst_cache, &fld, sk, flags) < 0)
+- goto out;
+- dst = __sk_dst_get(sk);
+- sk->sk_route_caps = dst->dev->features;
+- sock->state = SS_CONNECTING;
+- scp->state = DN_CI;
+- scp->segsize_loc = dst_metric_advmss(dst);
+-
+- dn_nsp_send_conninit(sk, NSP_CI);
+- err = -EINPROGRESS;
+- if (*timeo) {
+- err = dn_wait_run(sk, timeo);
+- }
+-out:
+- return err;
+-}
+-
+-static int dn_connect(struct socket *sock, struct sockaddr *uaddr, int addrlen, int flags)
+-{
+- struct sockaddr_dn *addr = (struct sockaddr_dn *)uaddr;
+- struct sock *sk = sock->sk;
+- int err;
+- long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
+-
+- lock_sock(sk);
+- err = __dn_connect(sk, addr, addrlen, &timeo, 0);
+- release_sock(sk);
+-
+- return err;
+-}
+-
+-static inline int dn_check_state(struct sock *sk, struct sockaddr_dn *addr, int addrlen, long *timeo, int flags)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+-
+- switch (scp->state) {
+- case DN_RUN:
+- return 0;
+- case DN_CR:
+- return dn_confirm_accept(sk, timeo, sk->sk_allocation);
+- case DN_CI:
+- case DN_CC:
+- return dn_wait_run(sk, timeo);
+- case DN_O:
+- return __dn_connect(sk, addr, addrlen, timeo, flags);
+- }
+-
+- return -EINVAL;
+-}
+-
+-
+-static void dn_access_copy(struct sk_buff *skb, struct accessdata_dn *acc)
+-{
+- unsigned char *ptr = skb->data;
+-
+- acc->acc_userl = *ptr++;
+- memcpy(&acc->acc_user, ptr, acc->acc_userl);
+- ptr += acc->acc_userl;
+-
+- acc->acc_passl = *ptr++;
+- memcpy(&acc->acc_pass, ptr, acc->acc_passl);
+- ptr += acc->acc_passl;
+-
+- acc->acc_accl = *ptr++;
+- memcpy(&acc->acc_acc, ptr, acc->acc_accl);
+-
+- skb_pull(skb, acc->acc_accl + acc->acc_passl + acc->acc_userl + 3);
+-
+-}
+-
+-static void dn_user_copy(struct sk_buff *skb, struct optdata_dn *opt)
+-{
+- unsigned char *ptr = skb->data;
+- u16 len = *ptr++; /* yes, it's 8bit on the wire */
+-
+- BUG_ON(len > 16); /* we've checked the contents earlier */
+- opt->opt_optl = cpu_to_le16(len);
+- opt->opt_status = 0;
+- memcpy(opt->opt_data, ptr, len);
+- skb_pull(skb, len + 1);
+-}
+-
+-static struct sk_buff *dn_wait_for_connect(struct sock *sk, long *timeo)
+-{
+- DEFINE_WAIT_FUNC(wait, woken_wake_function);
+- struct sk_buff *skb = NULL;
+- int err = 0;
+-
+- add_wait_queue(sk_sleep(sk), &wait);
+- for(;;) {
+- release_sock(sk);
+- skb = skb_dequeue(&sk->sk_receive_queue);
+- if (skb == NULL) {
+- *timeo = wait_woken(&wait, TASK_INTERRUPTIBLE, *timeo);
+- skb = skb_dequeue(&sk->sk_receive_queue);
+- }
+- lock_sock(sk);
+- if (skb != NULL)
+- break;
+- err = -EINVAL;
+- if (sk->sk_state != TCP_LISTEN)
+- break;
+- err = sock_intr_errno(*timeo);
+- if (signal_pending(current))
+- break;
+- err = -EAGAIN;
+- if (!*timeo)
+- break;
+- }
+- remove_wait_queue(sk_sleep(sk), &wait);
+-
+- return skb == NULL ? ERR_PTR(err) : skb;
+-}
+-
+-static int dn_accept(struct socket *sock, struct socket *newsock, int flags,
+- bool kern)
+-{
+- struct sock *sk = sock->sk, *newsk;
+- struct sk_buff *skb = NULL;
+- struct dn_skb_cb *cb;
+- unsigned char menuver;
+- int err = 0;
+- unsigned char type;
+- long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
+- struct dst_entry *dst;
+-
+- lock_sock(sk);
+-
+- if (sk->sk_state != TCP_LISTEN || DN_SK(sk)->state != DN_O) {
+- release_sock(sk);
+- return -EINVAL;
+- }
+-
+- skb = skb_dequeue(&sk->sk_receive_queue);
+- if (skb == NULL) {
+- skb = dn_wait_for_connect(sk, &timeo);
+- if (IS_ERR(skb)) {
+- release_sock(sk);
+- return PTR_ERR(skb);
+- }
+- }
+-
+- cb = DN_SKB_CB(skb);
+- sk->sk_ack_backlog--;
+- newsk = dn_alloc_sock(sock_net(sk), newsock, sk->sk_allocation, kern);
+- if (newsk == NULL) {
+- release_sock(sk);
+- kfree_skb(skb);
+- return -ENOBUFS;
+- }
+- release_sock(sk);
+-
+- dst = skb_dst(skb);
+- sk_dst_set(newsk, dst);
+- skb_dst_set(skb, NULL);
+-
+- DN_SK(newsk)->state = DN_CR;
+- DN_SK(newsk)->addrrem = cb->src_port;
+- DN_SK(newsk)->services_rem = cb->services;
+- DN_SK(newsk)->info_rem = cb->info;
+- DN_SK(newsk)->segsize_rem = cb->segsize;
+- DN_SK(newsk)->accept_mode = DN_SK(sk)->accept_mode;
+-
+- if (DN_SK(newsk)->segsize_rem < 230)
+- DN_SK(newsk)->segsize_rem = 230;
+-
+- if ((DN_SK(newsk)->services_rem & NSP_FC_MASK) == NSP_FC_NONE)
+- DN_SK(newsk)->max_window = decnet_no_fc_max_cwnd;
+-
+- newsk->sk_state = TCP_LISTEN;
+- memcpy(&(DN_SK(newsk)->addr), &(DN_SK(sk)->addr), sizeof(struct sockaddr_dn));
+-
+- /*
+- * If we are listening on a wild socket, we don't want
+- * the newly created socket on the wrong hash queue.
+- */
+- DN_SK(newsk)->addr.sdn_flags &= ~SDF_WILD;
+-
+- skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &(DN_SK(newsk)->addr), &type));
+- skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &(DN_SK(newsk)->peer), &type));
+- *(__le16 *)(DN_SK(newsk)->peer.sdn_add.a_addr) = cb->src;
+- *(__le16 *)(DN_SK(newsk)->addr.sdn_add.a_addr) = cb->dst;
+-
+- menuver = *skb->data;
+- skb_pull(skb, 1);
+-
+- if (menuver & DN_MENUVER_ACC)
+- dn_access_copy(skb, &(DN_SK(newsk)->accessdata));
+-
+- if (menuver & DN_MENUVER_USR)
+- dn_user_copy(skb, &(DN_SK(newsk)->conndata_in));
+-
+- if (menuver & DN_MENUVER_PRX)
+- DN_SK(newsk)->peer.sdn_flags |= SDF_PROXY;
+-
+- if (menuver & DN_MENUVER_UIC)
+- DN_SK(newsk)->peer.sdn_flags |= SDF_UICPROXY;
+-
+- kfree_skb(skb);
+-
+- memcpy(&(DN_SK(newsk)->conndata_out), &(DN_SK(sk)->conndata_out),
+- sizeof(struct optdata_dn));
+- memcpy(&(DN_SK(newsk)->discdata_out), &(DN_SK(sk)->discdata_out),
+- sizeof(struct optdata_dn));
+-
+- lock_sock(newsk);
+- err = dn_hash_sock(newsk);
+- if (err == 0) {
+- sock_reset_flag(newsk, SOCK_ZAPPED);
+- dn_send_conn_ack(newsk);
+-
+- /*
+- * Here we use sk->sk_allocation since although the conn conf is
+- * for the newsk, the context is the old socket.
+- */
+- if (DN_SK(newsk)->accept_mode == ACC_IMMED)
+- err = dn_confirm_accept(newsk, &timeo,
+- sk->sk_allocation);
+- }
+- release_sock(newsk);
+- return err;
+-}
+-
+-
+-static int dn_getname(struct socket *sock, struct sockaddr *uaddr,int *uaddr_len,int peer)
+-{
+- struct sockaddr_dn *sa = (struct sockaddr_dn *)uaddr;
+- struct sock *sk = sock->sk;
+- struct dn_scp *scp = DN_SK(sk);
+-
+- *uaddr_len = sizeof(struct sockaddr_dn);
+-
+- lock_sock(sk);
+-
+- if (peer) {
+- if ((sock->state != SS_CONNECTED &&
+- sock->state != SS_CONNECTING) &&
+- scp->accept_mode == ACC_IMMED) {
+- release_sock(sk);
+- return -ENOTCONN;
+- }
+-
+- memcpy(sa, &scp->peer, sizeof(struct sockaddr_dn));
+- } else {
+- memcpy(sa, &scp->addr, sizeof(struct sockaddr_dn));
+- }
+-
+- release_sock(sk);
+-
+- return 0;
+-}
+-
+-
+-static unsigned int dn_poll(struct file *file, struct socket *sock, poll_table *wait)
+-{
+- struct sock *sk = sock->sk;
+- struct dn_scp *scp = DN_SK(sk);
+- int mask = datagram_poll(file, sock, wait);
+-
+- if (!skb_queue_empty(&scp->other_receive_queue))
+- mask |= POLLRDBAND;
+-
+- return mask;
+-}
+-
+-static int dn_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+-{
+- struct sock *sk = sock->sk;
+- struct dn_scp *scp = DN_SK(sk);
+- int err = -EOPNOTSUPP;
+- long amount = 0;
+- struct sk_buff *skb;
+- int val;
+-
+- switch(cmd)
+- {
+- case SIOCGIFADDR:
+- case SIOCSIFADDR:
+- return dn_dev_ioctl(cmd, (void __user *)arg);
+-
+- case SIOCATMARK:
+- lock_sock(sk);
+- val = !skb_queue_empty(&scp->other_receive_queue);
+- if (scp->state != DN_RUN)
+- val = -ENOTCONN;
+- release_sock(sk);
+- return val;
+-
+- case TIOCOUTQ:
+- amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
+- if (amount < 0)
+- amount = 0;
+- err = put_user(amount, (int __user *)arg);
+- break;
+-
+- case TIOCINQ:
+- lock_sock(sk);
+- skb = skb_peek(&scp->other_receive_queue);
+- if (skb) {
+- amount = skb->len;
+- } else {
+- skb_queue_walk(&sk->sk_receive_queue, skb)
+- amount += skb->len;
+- }
+- release_sock(sk);
+- err = put_user(amount, (int __user *)arg);
+- break;
+-
+- default:
+- err = -ENOIOCTLCMD;
+- break;
+- }
+-
+- return err;
+-}
+-
+-static int dn_listen(struct socket *sock, int backlog)
+-{
+- struct sock *sk = sock->sk;
+- int err = -EINVAL;
+-
+- lock_sock(sk);
+-
+- if (sock_flag(sk, SOCK_ZAPPED))
+- goto out;
+-
+- if ((DN_SK(sk)->state != DN_O) || (sk->sk_state == TCP_LISTEN))
+- goto out;
+-
+- sk->sk_max_ack_backlog = backlog;
+- sk->sk_ack_backlog = 0;
+- sk->sk_state = TCP_LISTEN;
+- err = 0;
+- dn_rehash_sock(sk);
+-
+-out:
+- release_sock(sk);
+-
+- return err;
+-}
+-
+-
+-static int dn_shutdown(struct socket *sock, int how)
+-{
+- struct sock *sk = sock->sk;
+- struct dn_scp *scp = DN_SK(sk);
+- int err = -ENOTCONN;
+-
+- lock_sock(sk);
+-
+- if (sock->state == SS_UNCONNECTED)
+- goto out;
+-
+- err = 0;
+- if (sock->state == SS_DISCONNECTING)
+- goto out;
+-
+- err = -EINVAL;
+- if (scp->state == DN_O)
+- goto out;
+-
+- if (how != SHUT_RDWR)
+- goto out;
+-
+- sk->sk_shutdown = SHUTDOWN_MASK;
+- dn_destroy_sock(sk);
+- err = 0;
+-
+-out:
+- release_sock(sk);
+-
+- return err;
+-}
+-
+-static int dn_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
+-{
+- struct sock *sk = sock->sk;
+- int err;
+-
+- lock_sock(sk);
+- err = __dn_setsockopt(sock, level, optname, optval, optlen, 0);
+- release_sock(sk);
+-#ifdef CONFIG_NETFILTER
+- /* we need to exclude all possible ENOPROTOOPTs except default case */
+- if (err == -ENOPROTOOPT && optname != DSO_LINKINFO &&
+- optname != DSO_STREAM && optname != DSO_SEQPACKET)
+- err = nf_setsockopt(sk, PF_DECnet, optname, optval, optlen);
+-#endif
+-
+- return err;
+-}
+-
+-static int __dn_setsockopt(struct socket *sock, int level,int optname, char __user *optval, unsigned int optlen, int flags)
+-{
+- struct sock *sk = sock->sk;
+- struct dn_scp *scp = DN_SK(sk);
+- long timeo;
+- union {
+- struct optdata_dn opt;
+- struct accessdata_dn acc;
+- int mode;
+- unsigned long win;
+- int val;
+- unsigned char services;
+- unsigned char info;
+- } u;
+- int err;
+-
+- if (optlen && !optval)
+- return -EINVAL;
+-
+- if (optlen > sizeof(u))
+- return -EINVAL;
+-
+- if (copy_from_user(&u, optval, optlen))
+- return -EFAULT;
+-
+- switch (optname) {
+- case DSO_CONDATA:
+- if (sock->state == SS_CONNECTED)
+- return -EISCONN;
+- if ((scp->state != DN_O) && (scp->state != DN_CR))
+- return -EINVAL;
+-
+- if (optlen != sizeof(struct optdata_dn))
+- return -EINVAL;
+-
+- if (le16_to_cpu(u.opt.opt_optl) > 16)
+- return -EINVAL;
+-
+- memcpy(&scp->conndata_out, &u.opt, optlen);
+- break;
+-
+- case DSO_DISDATA:
+- if (sock->state != SS_CONNECTED &&
+- scp->accept_mode == ACC_IMMED)
+- return -ENOTCONN;
+-
+- if (optlen != sizeof(struct optdata_dn))
+- return -EINVAL;
+-
+- if (le16_to_cpu(u.opt.opt_optl) > 16)
+- return -EINVAL;
+-
+- memcpy(&scp->discdata_out, &u.opt, optlen);
+- break;
+-
+- case DSO_CONACCESS:
+- if (sock->state == SS_CONNECTED)
+- return -EISCONN;
+- if (scp->state != DN_O)
+- return -EINVAL;
+-
+- if (optlen != sizeof(struct accessdata_dn))
+- return -EINVAL;
+-
+- if ((u.acc.acc_accl > DN_MAXACCL) ||
+- (u.acc.acc_passl > DN_MAXACCL) ||
+- (u.acc.acc_userl > DN_MAXACCL))
+- return -EINVAL;
+-
+- memcpy(&scp->accessdata, &u.acc, optlen);
+- break;
+-
+- case DSO_ACCEPTMODE:
+- if (sock->state == SS_CONNECTED)
+- return -EISCONN;
+- if (scp->state != DN_O)
+- return -EINVAL;
+-
+- if (optlen != sizeof(int))
+- return -EINVAL;
+-
+- if ((u.mode != ACC_IMMED) && (u.mode != ACC_DEFER))
+- return -EINVAL;
+-
+- scp->accept_mode = (unsigned char)u.mode;
+- break;
+-
+- case DSO_CONACCEPT:
+- if (scp->state != DN_CR)
+- return -EINVAL;
+- timeo = sock_rcvtimeo(sk, 0);
+- err = dn_confirm_accept(sk, &timeo, sk->sk_allocation);
+- return err;
+-
+- case DSO_CONREJECT:
+- if (scp->state != DN_CR)
+- return -EINVAL;
+-
+- scp->state = DN_DR;
+- sk->sk_shutdown = SHUTDOWN_MASK;
+- dn_nsp_send_disc(sk, 0x38, 0, sk->sk_allocation);
+- break;
+-
+- case DSO_MAXWINDOW:
+- if (optlen != sizeof(unsigned long))
+- return -EINVAL;
+- if (u.win > NSP_MAX_WINDOW)
+- u.win = NSP_MAX_WINDOW;
+- if (u.win == 0)
+- return -EINVAL;
+- scp->max_window = u.win;
+- if (scp->snd_window > u.win)
+- scp->snd_window = u.win;
+- break;
+-
+- case DSO_NODELAY:
+- if (optlen != sizeof(int))
+- return -EINVAL;
+- if (scp->nonagle == TCP_NAGLE_CORK)
+- return -EINVAL;
+- scp->nonagle = (u.val == 0) ? 0 : TCP_NAGLE_OFF;
+- /* if (scp->nonagle == 1) { Push pending frames } */
+- break;
+-
+- case DSO_CORK:
+- if (optlen != sizeof(int))
+- return -EINVAL;
+- if (scp->nonagle == TCP_NAGLE_OFF)
+- return -EINVAL;
+- scp->nonagle = (u.val == 0) ? 0 : TCP_NAGLE_CORK;
+- /* if (scp->nonagle == 0) { Push pending frames } */
+- break;
+-
+- case DSO_SERVICES:
+- if (optlen != sizeof(unsigned char))
+- return -EINVAL;
+- if ((u.services & ~NSP_FC_MASK) != 0x01)
+- return -EINVAL;
+- if ((u.services & NSP_FC_MASK) == NSP_FC_MASK)
+- return -EINVAL;
+- scp->services_loc = u.services;
+- break;
+-
+- case DSO_INFO:
+- if (optlen != sizeof(unsigned char))
+- return -EINVAL;
+- if (u.info & 0xfc)
+- return -EINVAL;
+- scp->info_loc = u.info;
+- break;
+-
+- case DSO_LINKINFO:
+- case DSO_STREAM:
+- case DSO_SEQPACKET:
+- default:
+- return -ENOPROTOOPT;
+- }
+-
+- return 0;
+-}
+-
+-static int dn_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
+-{
+- struct sock *sk = sock->sk;
+- int err;
+-
+- lock_sock(sk);
+- err = __dn_getsockopt(sock, level, optname, optval, optlen, 0);
+- release_sock(sk);
+-#ifdef CONFIG_NETFILTER
+- if (err == -ENOPROTOOPT && optname != DSO_STREAM &&
+- optname != DSO_SEQPACKET && optname != DSO_CONACCEPT &&
+- optname != DSO_CONREJECT) {
+- int len;
+-
+- if (get_user(len, optlen))
+- return -EFAULT;
+-
+- err = nf_getsockopt(sk, PF_DECnet, optname, optval, &len);
+- if (err >= 0)
+- err = put_user(len, optlen);
+- }
+-#endif
+-
+- return err;
+-}
+-
+-static int __dn_getsockopt(struct socket *sock, int level,int optname, char __user *optval,int __user *optlen, int flags)
+-{
+- struct sock *sk = sock->sk;
+- struct dn_scp *scp = DN_SK(sk);
+- struct linkinfo_dn link;
+- unsigned int r_len;
+- void *r_data = NULL;
+- unsigned int val;
+-
+- if(get_user(r_len , optlen))
+- return -EFAULT;
+-
+- switch (optname) {
+- case DSO_CONDATA:
+- if (r_len > sizeof(struct optdata_dn))
+- r_len = sizeof(struct optdata_dn);
+- r_data = &scp->conndata_in;
+- break;
+-
+- case DSO_DISDATA:
+- if (r_len > sizeof(struct optdata_dn))
+- r_len = sizeof(struct optdata_dn);
+- r_data = &scp->discdata_in;
+- break;
+-
+- case DSO_CONACCESS:
+- if (r_len > sizeof(struct accessdata_dn))
+- r_len = sizeof(struct accessdata_dn);
+- r_data = &scp->accessdata;
+- break;
+-
+- case DSO_ACCEPTMODE:
+- if (r_len > sizeof(unsigned char))
+- r_len = sizeof(unsigned char);
+- r_data = &scp->accept_mode;
+- break;
+-
+- case DSO_LINKINFO:
+- if (r_len > sizeof(struct linkinfo_dn))
+- r_len = sizeof(struct linkinfo_dn);
+-
+- memset(&link, 0, sizeof(link));
+-
+- switch (sock->state) {
+- case SS_CONNECTING:
+- link.idn_linkstate = LL_CONNECTING;
+- break;
+- case SS_DISCONNECTING:
+- link.idn_linkstate = LL_DISCONNECTING;
+- break;
+- case SS_CONNECTED:
+- link.idn_linkstate = LL_RUNNING;
+- break;
+- default:
+- link.idn_linkstate = LL_INACTIVE;
+- }
+-
+- link.idn_segsize = scp->segsize_rem;
+- r_data = &link;
+- break;
+-
+- case DSO_MAXWINDOW:
+- if (r_len > sizeof(unsigned long))
+- r_len = sizeof(unsigned long);
+- r_data = &scp->max_window;
+- break;
+-
+- case DSO_NODELAY:
+- if (r_len > sizeof(int))
+- r_len = sizeof(int);
+- val = (scp->nonagle == TCP_NAGLE_OFF);
+- r_data = &val;
+- break;
+-
+- case DSO_CORK:
+- if (r_len > sizeof(int))
+- r_len = sizeof(int);
+- val = (scp->nonagle == TCP_NAGLE_CORK);
+- r_data = &val;
+- break;
+-
+- case DSO_SERVICES:
+- if (r_len > sizeof(unsigned char))
+- r_len = sizeof(unsigned char);
+- r_data = &scp->services_rem;
+- break;
+-
+- case DSO_INFO:
+- if (r_len > sizeof(unsigned char))
+- r_len = sizeof(unsigned char);
+- r_data = &scp->info_rem;
+- break;
+-
+- case DSO_STREAM:
+- case DSO_SEQPACKET:
+- case DSO_CONACCEPT:
+- case DSO_CONREJECT:
+- default:
+- return -ENOPROTOOPT;
+- }
+-
+- if (r_data) {
+- if (copy_to_user(optval, r_data, r_len))
+- return -EFAULT;
+- if (put_user(r_len, optlen))
+- return -EFAULT;
+- }
+-
+- return 0;
+-}
+-
+-
+-static int dn_data_ready(struct sock *sk, struct sk_buff_head *q, int flags, int target)
+-{
+- struct sk_buff *skb;
+- int len = 0;
+-
+- if (flags & MSG_OOB)
+- return !skb_queue_empty(q) ? 1 : 0;
+-
+- skb_queue_walk(q, skb) {
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- len += skb->len;
+-
+- if (cb->nsp_flags & 0x40) {
+- /* SOCK_SEQPACKET reads to EOM */
+- if (sk->sk_type == SOCK_SEQPACKET)
+- return 1;
+- /* so does SOCK_STREAM unless WAITALL is specified */
+- if (!(flags & MSG_WAITALL))
+- return 1;
+- }
+-
+- /* minimum data length for read exceeded */
+- if (len >= target)
+- return 1;
+- }
+-
+- return 0;
+-}
+-
+-
+-static int dn_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
+- int flags)
+-{
+- struct sock *sk = sock->sk;
+- struct dn_scp *scp = DN_SK(sk);
+- struct sk_buff_head *queue = &sk->sk_receive_queue;
+- size_t target = size > 1 ? 1 : 0;
+- size_t copied = 0;
+- int rv = 0;
+- struct sk_buff *skb, *n;
+- struct dn_skb_cb *cb = NULL;
+- unsigned char eor = 0;
+- long timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
+-
+- lock_sock(sk);
+-
+- if (sock_flag(sk, SOCK_ZAPPED)) {
+- rv = -EADDRNOTAVAIL;
+- goto out;
+- }
+-
+- if (sk->sk_shutdown & RCV_SHUTDOWN) {
+- rv = 0;
+- goto out;
+- }
+-
+- rv = dn_check_state(sk, NULL, 0, &timeo, flags);
+- if (rv)
+- goto out;
+-
+- if (flags & ~(MSG_CMSG_COMPAT|MSG_PEEK|MSG_OOB|MSG_WAITALL|MSG_DONTWAIT|MSG_NOSIGNAL)) {
+- rv = -EOPNOTSUPP;
+- goto out;
+- }
+-
+- if (flags & MSG_OOB)
+- queue = &scp->other_receive_queue;
+-
+- if (flags & MSG_WAITALL)
+- target = size;
+-
+-
+- /*
+- * See if there is data ready to read, sleep if there isn't
+- */
+- for(;;) {
+- DEFINE_WAIT_FUNC(wait, woken_wake_function);
+-
+- if (sk->sk_err)
+- goto out;
+-
+- if (!skb_queue_empty(&scp->other_receive_queue)) {
+- if (!(flags & MSG_OOB)) {
+- msg->msg_flags |= MSG_OOB;
+- if (!scp->other_report) {
+- scp->other_report = 1;
+- goto out;
+- }
+- }
+- }
+-
+- if (scp->state != DN_RUN)
+- goto out;
+-
+- if (signal_pending(current)) {
+- rv = sock_intr_errno(timeo);
+- goto out;
+- }
+-
+- if (dn_data_ready(sk, queue, flags, target))
+- break;
+-
+- if (flags & MSG_DONTWAIT) {
+- rv = -EWOULDBLOCK;
+- goto out;
+- }
+-
+- add_wait_queue(sk_sleep(sk), &wait);
+- sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
+- sk_wait_event(sk, &timeo, dn_data_ready(sk, queue, flags, target), &wait);
+- sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
+- remove_wait_queue(sk_sleep(sk), &wait);
+- }
+-
+- skb_queue_walk_safe(queue, skb, n) {
+- unsigned int chunk = skb->len;
+- cb = DN_SKB_CB(skb);
+-
+- if ((chunk + copied) > size)
+- chunk = size - copied;
+-
+- if (memcpy_to_msg(msg, skb->data, chunk)) {
+- rv = -EFAULT;
+- break;
+- }
+- copied += chunk;
+-
+- if (!(flags & MSG_PEEK))
+- skb_pull(skb, chunk);
+-
+- eor = cb->nsp_flags & 0x40;
+-
+- if (skb->len == 0) {
+- skb_unlink(skb, queue);
+- kfree_skb(skb);
+- /*
+- * N.B. Don't refer to skb or cb after this point
+- * in loop.
+- */
+- if ((scp->flowloc_sw == DN_DONTSEND) && !dn_congested(sk)) {
+- scp->flowloc_sw = DN_SEND;
+- dn_nsp_send_link(sk, DN_SEND, 0);
+- }
+- }
+-
+- if (eor) {
+- if (sk->sk_type == SOCK_SEQPACKET)
+- break;
+- if (!(flags & MSG_WAITALL))
+- break;
+- }
+-
+- if (flags & MSG_OOB)
+- break;
+-
+- if (copied >= target)
+- break;
+- }
+-
+- rv = copied;
+-
+-
+- if (eor && (sk->sk_type == SOCK_SEQPACKET))
+- msg->msg_flags |= MSG_EOR;
+-
+-out:
+- if (rv == 0)
+- rv = (flags & MSG_PEEK) ? -sk->sk_err : sock_error(sk);
+-
+- if ((rv >= 0) && msg->msg_name) {
+- __sockaddr_check_size(sizeof(struct sockaddr_dn));
+- memcpy(msg->msg_name, &scp->peer, sizeof(struct sockaddr_dn));
+- msg->msg_namelen = sizeof(struct sockaddr_dn);
+- }
+-
+- release_sock(sk);
+-
+- return rv;
+-}
+-
+-
+-static inline int dn_queue_too_long(struct dn_scp *scp, struct sk_buff_head *queue, int flags)
+-{
+- unsigned char fctype = scp->services_rem & NSP_FC_MASK;
+- if (skb_queue_len(queue) >= scp->snd_window)
+- return 1;
+- if (fctype != NSP_FC_NONE) {
+- if (flags & MSG_OOB) {
+- if (scp->flowrem_oth == 0)
+- return 1;
+- } else {
+- if (scp->flowrem_dat == 0)
+- return 1;
+- }
+- }
+- return 0;
+-}
+-
+-/*
+- * The DECnet spec requires that the "routing layer" accepts packets which
+- * are at least 230 bytes in size. This excludes any headers which the NSP
+- * layer might add, so we always assume that we'll be using the maximal
+- * length header on data packets. The variation in length is due to the
+- * inclusion (or not) of the two 16 bit acknowledgement fields so it doesn't
+- * make much practical difference.
+- */
+-unsigned int dn_mss_from_pmtu(struct net_device *dev, int mtu)
+-{
+- unsigned int mss = 230 - DN_MAX_NSP_DATA_HEADER;
+- if (dev) {
+- struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr);
+- mtu -= LL_RESERVED_SPACE(dev);
+- if (dn_db->use_long)
+- mtu -= 21;
+- else
+- mtu -= 6;
+- mtu -= DN_MAX_NSP_DATA_HEADER;
+- } else {
+- /*
+- * 21 = long header, 16 = guess at MAC header length
+- */
+- mtu -= (21 + DN_MAX_NSP_DATA_HEADER + 16);
+- }
+- if (mtu > mss)
+- mss = mtu;
+- return mss;
+-}
+-
+-static inline unsigned int dn_current_mss(struct sock *sk, int flags)
+-{
+- struct dst_entry *dst = __sk_dst_get(sk);
+- struct dn_scp *scp = DN_SK(sk);
+- int mss_now = min_t(int, scp->segsize_loc, scp->segsize_rem);
+-
+- /* Other data messages are limited to 16 bytes per packet */
+- if (flags & MSG_OOB)
+- return 16;
+-
+- /* This works out the maximum size of segment we can send out */
+- if (dst) {
+- u32 mtu = dst_mtu(dst);
+- mss_now = min_t(int, dn_mss_from_pmtu(dst->dev, mtu), mss_now);
+- }
+-
+- return mss_now;
+-}
+-
+-/*
+- * N.B. We get the timeout wrong here, but then we always did get it
+- * wrong before and this is another step along the road to correcting
+- * it. It ought to get updated each time we pass through the routine,
+- * but in practise it probably doesn't matter too much for now.
+- */
+-static inline struct sk_buff *dn_alloc_send_pskb(struct sock *sk,
+- unsigned long datalen, int noblock,
+- int *errcode)
+-{
+- struct sk_buff *skb = sock_alloc_send_skb(sk, datalen,
+- noblock, errcode);
+- if (skb) {
+- skb->protocol = htons(ETH_P_DNA_RT);
+- skb->pkt_type = PACKET_OUTGOING;
+- }
+- return skb;
+-}
+-
+-static int dn_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+-{
+- struct sock *sk = sock->sk;
+- struct dn_scp *scp = DN_SK(sk);
+- size_t mss;
+- struct sk_buff_head *queue = &scp->data_xmit_queue;
+- int flags = msg->msg_flags;
+- int err = 0;
+- size_t sent = 0;
+- int addr_len = msg->msg_namelen;
+- DECLARE_SOCKADDR(struct sockaddr_dn *, addr, msg->msg_name);
+- struct sk_buff *skb = NULL;
+- struct dn_skb_cb *cb;
+- size_t len;
+- unsigned char fctype;
+- long timeo;
+-
+- if (flags & ~(MSG_TRYHARD|MSG_OOB|MSG_DONTWAIT|MSG_EOR|MSG_NOSIGNAL|MSG_MORE|MSG_CMSG_COMPAT))
+- return -EOPNOTSUPP;
+-
+- if (addr_len && (addr_len != sizeof(struct sockaddr_dn)))
+- return -EINVAL;
+-
+- lock_sock(sk);
+- timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
+- /*
+- * The only difference between stream sockets and sequenced packet
+- * sockets is that the stream sockets always behave as if MSG_EOR
+- * has been set.
+- */
+- if (sock->type == SOCK_STREAM) {
+- if (flags & MSG_EOR) {
+- err = -EINVAL;
+- goto out;
+- }
+- flags |= MSG_EOR;
+- }
+-
+-
+- err = dn_check_state(sk, addr, addr_len, &timeo, flags);
+- if (err)
+- goto out_err;
+-
+- if (sk->sk_shutdown & SEND_SHUTDOWN) {
+- err = -EPIPE;
+- if (!(flags & MSG_NOSIGNAL))
+- send_sig(SIGPIPE, current, 0);
+- goto out_err;
+- }
+-
+- if ((flags & MSG_TRYHARD) && sk->sk_dst_cache)
+- dst_negative_advice(sk);
+-
+- mss = scp->segsize_rem;
+- fctype = scp->services_rem & NSP_FC_MASK;
+-
+- mss = dn_current_mss(sk, flags);
+-
+- if (flags & MSG_OOB) {
+- queue = &scp->other_xmit_queue;
+- if (size > mss) {
+- err = -EMSGSIZE;
+- goto out;
+- }
+- }
+-
+- scp->persist_fxn = dn_nsp_xmit_timeout;
+-
+- while(sent < size) {
+- err = sock_error(sk);
+- if (err)
+- goto out;
+-
+- if (signal_pending(current)) {
+- err = sock_intr_errno(timeo);
+- goto out;
+- }
+-
+- /*
+- * Calculate size that we wish to send.
+- */
+- len = size - sent;
+-
+- if (len > mss)
+- len = mss;
+-
+- /*
+- * Wait for queue size to go down below the window
+- * size.
+- */
+- if (dn_queue_too_long(scp, queue, flags)) {
+- DEFINE_WAIT_FUNC(wait, woken_wake_function);
+-
+- if (flags & MSG_DONTWAIT) {
+- err = -EWOULDBLOCK;
+- goto out;
+- }
+-
+- add_wait_queue(sk_sleep(sk), &wait);
+- sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
+- sk_wait_event(sk, &timeo,
+- !dn_queue_too_long(scp, queue, flags), &wait);
+- sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
+- remove_wait_queue(sk_sleep(sk), &wait);
+- continue;
+- }
+-
+- /*
+- * Get a suitably sized skb.
+- * 64 is a bit of a hack really, but its larger than any
+- * link-layer headers and has served us well as a good
+- * guess as to their real length.
+- */
+- skb = dn_alloc_send_pskb(sk, len + 64 + DN_MAX_NSP_DATA_HEADER,
+- flags & MSG_DONTWAIT, &err);
+-
+- if (err)
+- break;
+-
+- if (!skb)
+- continue;
+-
+- cb = DN_SKB_CB(skb);
+-
+- skb_reserve(skb, 64 + DN_MAX_NSP_DATA_HEADER);
+-
+- if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
+- err = -EFAULT;
+- goto out;
+- }
+-
+- if (flags & MSG_OOB) {
+- cb->nsp_flags = 0x30;
+- if (fctype != NSP_FC_NONE)
+- scp->flowrem_oth--;
+- } else {
+- cb->nsp_flags = 0x00;
+- if (scp->seg_total == 0)
+- cb->nsp_flags |= 0x20;
+-
+- scp->seg_total += len;
+-
+- if (((sent + len) == size) && (flags & MSG_EOR)) {
+- cb->nsp_flags |= 0x40;
+- scp->seg_total = 0;
+- if (fctype == NSP_FC_SCMC)
+- scp->flowrem_dat--;
+- }
+- if (fctype == NSP_FC_SRC)
+- scp->flowrem_dat--;
+- }
+-
+- sent += len;
+- dn_nsp_queue_xmit(sk, skb, sk->sk_allocation, flags & MSG_OOB);
+- skb = NULL;
+-
+- scp->persist = dn_nsp_persist(sk);
+-
+- }
+-out:
+-
+- kfree_skb(skb);
+-
+- release_sock(sk);
+-
+- return sent ? sent : err;
+-
+-out_err:
+- err = sk_stream_error(sk, flags, err);
+- release_sock(sk);
+- return err;
+-}
+-
+-static int dn_device_event(struct notifier_block *this, unsigned long event,
+- void *ptr)
+-{
+- struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+-
+- if (!net_eq(dev_net(dev), &init_net))
+- return NOTIFY_DONE;
+-
+- switch (event) {
+- case NETDEV_UP:
+- dn_dev_up(dev);
+- break;
+- case NETDEV_DOWN:
+- dn_dev_down(dev);
+- break;
+- default:
+- break;
+- }
+-
+- return NOTIFY_DONE;
+-}
+-
+-static struct notifier_block dn_dev_notifier = {
+- .notifier_call = dn_device_event,
+-};
+-
+-static struct packet_type dn_dix_packet_type __read_mostly = {
+- .type = cpu_to_be16(ETH_P_DNA_RT),
+- .func = dn_route_rcv,
+-};
+-
+-#ifdef CONFIG_PROC_FS
+-struct dn_iter_state {
+- int bucket;
+-};
+-
+-static struct sock *dn_socket_get_first(struct seq_file *seq)
+-{
+- struct dn_iter_state *state = seq->private;
+- struct sock *n = NULL;
+-
+- for(state->bucket = 0;
+- state->bucket < DN_SK_HASH_SIZE;
+- ++state->bucket) {
+- n = sk_head(&dn_sk_hash[state->bucket]);
+- if (n)
+- break;
+- }
+-
+- return n;
+-}
+-
+-static struct sock *dn_socket_get_next(struct seq_file *seq,
+- struct sock *n)
+-{
+- struct dn_iter_state *state = seq->private;
+-
+- n = sk_next(n);
+-try_again:
+- if (n)
+- goto out;
+- if (++state->bucket >= DN_SK_HASH_SIZE)
+- goto out;
+- n = sk_head(&dn_sk_hash[state->bucket]);
+- goto try_again;
+-out:
+- return n;
+-}
+-
+-static struct sock *socket_get_idx(struct seq_file *seq, loff_t *pos)
+-{
+- struct sock *sk = dn_socket_get_first(seq);
+-
+- if (sk) {
+- while(*pos && (sk = dn_socket_get_next(seq, sk)))
+- --*pos;
+- }
+- return *pos ? NULL : sk;
+-}
+-
+-static void *dn_socket_get_idx(struct seq_file *seq, loff_t pos)
+-{
+- void *rc;
+- read_lock_bh(&dn_hash_lock);
+- rc = socket_get_idx(seq, &pos);
+- if (!rc) {
+- read_unlock_bh(&dn_hash_lock);
+- }
+- return rc;
+-}
+-
+-static void *dn_socket_seq_start(struct seq_file *seq, loff_t *pos)
+-{
+- return *pos ? dn_socket_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
+-}
+-
+-static void *dn_socket_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+-{
+- void *rc;
+-
+- if (v == SEQ_START_TOKEN) {
+- rc = dn_socket_get_idx(seq, 0);
+- goto out;
+- }
+-
+- rc = dn_socket_get_next(seq, v);
+- if (rc)
+- goto out;
+- read_unlock_bh(&dn_hash_lock);
+-out:
+- ++*pos;
+- return rc;
+-}
+-
+-static void dn_socket_seq_stop(struct seq_file *seq, void *v)
+-{
+- if (v && v != SEQ_START_TOKEN)
+- read_unlock_bh(&dn_hash_lock);
+-}
+-
+-#define IS_NOT_PRINTABLE(x) ((x) < 32 || (x) > 126)
+-
+-static void dn_printable_object(struct sockaddr_dn *dn, unsigned char *buf)
+-{
+- int i;
+-
+- switch (le16_to_cpu(dn->sdn_objnamel)) {
+- case 0:
+- sprintf(buf, "%d", dn->sdn_objnum);
+- break;
+- default:
+- for (i = 0; i < le16_to_cpu(dn->sdn_objnamel); i++) {
+- buf[i] = dn->sdn_objname[i];
+- if (IS_NOT_PRINTABLE(buf[i]))
+- buf[i] = '.';
+- }
+- buf[i] = 0;
+- }
+-}
+-
+-static char *dn_state2asc(unsigned char state)
+-{
+- switch (state) {
+- case DN_O:
+- return "OPEN";
+- case DN_CR:
+- return " CR";
+- case DN_DR:
+- return " DR";
+- case DN_DRC:
+- return " DRC";
+- case DN_CC:
+- return " CC";
+- case DN_CI:
+- return " CI";
+- case DN_NR:
+- return " NR";
+- case DN_NC:
+- return " NC";
+- case DN_CD:
+- return " CD";
+- case DN_RJ:
+- return " RJ";
+- case DN_RUN:
+- return " RUN";
+- case DN_DI:
+- return " DI";
+- case DN_DIC:
+- return " DIC";
+- case DN_DN:
+- return " DN";
+- case DN_CL:
+- return " CL";
+- case DN_CN:
+- return " CN";
+- }
+-
+- return "????";
+-}
+-
+-static inline void dn_socket_format_entry(struct seq_file *seq, struct sock *sk)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- char buf1[DN_ASCBUF_LEN];
+- char buf2[DN_ASCBUF_LEN];
+- char local_object[DN_MAXOBJL+3];
+- char remote_object[DN_MAXOBJL+3];
+-
+- dn_printable_object(&scp->addr, local_object);
+- dn_printable_object(&scp->peer, remote_object);
+-
+- seq_printf(seq,
+- "%6s/%04X %04d:%04d %04d:%04d %01d %-16s "
+- "%6s/%04X %04d:%04d %04d:%04d %01d %-16s %4s %s\n",
+- dn_addr2asc(le16_to_cpu(dn_saddr2dn(&scp->addr)), buf1),
+- scp->addrloc,
+- scp->numdat,
+- scp->numoth,
+- scp->ackxmt_dat,
+- scp->ackxmt_oth,
+- scp->flowloc_sw,
+- local_object,
+- dn_addr2asc(le16_to_cpu(dn_saddr2dn(&scp->peer)), buf2),
+- scp->addrrem,
+- scp->numdat_rcv,
+- scp->numoth_rcv,
+- scp->ackrcv_dat,
+- scp->ackrcv_oth,
+- scp->flowrem_sw,
+- remote_object,
+- dn_state2asc(scp->state),
+- ((scp->accept_mode == ACC_IMMED) ? "IMMED" : "DEFER"));
+-}
+-
+-static int dn_socket_seq_show(struct seq_file *seq, void *v)
+-{
+- if (v == SEQ_START_TOKEN) {
+- seq_puts(seq, "Local Remote\n");
+- } else {
+- dn_socket_format_entry(seq, v);
+- }
+- return 0;
+-}
+-
+-static const struct seq_operations dn_socket_seq_ops = {
+- .start = dn_socket_seq_start,
+- .next = dn_socket_seq_next,
+- .stop = dn_socket_seq_stop,
+- .show = dn_socket_seq_show,
+-};
+-
+-static int dn_socket_seq_open(struct inode *inode, struct file *file)
+-{
+- return seq_open_private(file, &dn_socket_seq_ops,
+- sizeof(struct dn_iter_state));
+-}
+-
+-static const struct file_operations dn_socket_seq_fops = {
+- .owner = THIS_MODULE,
+- .open = dn_socket_seq_open,
+- .read = seq_read,
+- .llseek = seq_lseek,
+- .release = seq_release_private,
+-};
+-#endif
+-
+-static const struct net_proto_family dn_family_ops = {
+- .family = AF_DECnet,
+- .create = dn_create,
+- .owner = THIS_MODULE,
+-};
+-
+-static const struct proto_ops dn_proto_ops = {
+- .family = AF_DECnet,
+- .owner = THIS_MODULE,
+- .release = dn_release,
+- .bind = dn_bind,
+- .connect = dn_connect,
+- .socketpair = sock_no_socketpair,
+- .accept = dn_accept,
+- .getname = dn_getname,
+- .poll = dn_poll,
+- .ioctl = dn_ioctl,
+- .listen = dn_listen,
+- .shutdown = dn_shutdown,
+- .setsockopt = dn_setsockopt,
+- .getsockopt = dn_getsockopt,
+- .sendmsg = dn_sendmsg,
+- .recvmsg = dn_recvmsg,
+- .mmap = sock_no_mmap,
+- .sendpage = sock_no_sendpage,
+-};
+-
+-MODULE_DESCRIPTION("The Linux DECnet Network Protocol");
+-MODULE_AUTHOR("Linux DECnet Project Team");
+-MODULE_LICENSE("GPL");
+-MODULE_ALIAS_NETPROTO(PF_DECnet);
+-
+-static const char banner[] __initconst = KERN_INFO
+-"NET4: DECnet for Linux: V.2.5.68s (C) 1995-2003 Linux DECnet Project Team\n";
+-
+-static int __init decnet_init(void)
+-{
+- int rc;
+-
+- printk(banner);
+-
+- rc = proto_register(&dn_proto, 1);
+- if (rc != 0)
+- goto out;
+-
+- dn_neigh_init();
+- dn_dev_init();
+- dn_route_init();
+- dn_fib_init();
+-
+- sock_register(&dn_family_ops);
+- dev_add_pack(&dn_dix_packet_type);
+- register_netdevice_notifier(&dn_dev_notifier);
+-
+- proc_create("decnet", S_IRUGO, init_net.proc_net, &dn_socket_seq_fops);
+- dn_register_sysctl();
+-out:
+- return rc;
+-
+-}
+-module_init(decnet_init);
+-
+-/*
+- * Prevent DECnet module unloading until its fixed properly.
+- * Requires an audit of the code to check for memory leaks and
+- * initialisation problems etc.
+- */
+-#if 0
+-static void __exit decnet_exit(void)
+-{
+- sock_unregister(AF_DECnet);
+- rtnl_unregister_all(PF_DECnet);
+- dev_remove_pack(&dn_dix_packet_type);
+-
+- dn_unregister_sysctl();
+-
+- unregister_netdevice_notifier(&dn_dev_notifier);
+-
+- dn_route_cleanup();
+- dn_dev_cleanup();
+- dn_neigh_cleanup();
+- dn_fib_cleanup();
+-
+- remove_proc_entry("decnet", init_net.proc_net);
+-
+- proto_unregister(&dn_proto);
+-
+- rcu_barrier_bh(); /* Wait for completion of call_rcu_bh()'s */
+-}
+-module_exit(decnet_exit);
+-#endif
+diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c
+deleted file mode 100644
+index 22876a197ebec..0000000000000
+--- a/net/decnet/dn_dev.c
++++ /dev/null
+@@ -1,1451 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/*
+- * DECnet An implementation of the DECnet protocol suite for the LINUX
+- * operating system. DECnet is implemented using the BSD Socket
+- * interface as the means of communication with the user level.
+- *
+- * DECnet Device Layer
+- *
+- * Authors: Steve Whitehouse <SteveW@ACM.org>
+- * Eduardo Marcelo Serrat <emserrat@geocities.com>
+- *
+- * Changes:
+- * Steve Whitehouse : Devices now see incoming frames so they
+- * can mark on who it came from.
+- * Steve Whitehouse : Fixed bug in creating neighbours. Each neighbour
+- * can now have a device specific setup func.
+- * Steve Whitehouse : Added /proc/sys/net/decnet/conf/<dev>/
+- * Steve Whitehouse : Fixed bug which sometimes killed timer
+- * Steve Whitehouse : Multiple ifaddr support
+- * Steve Whitehouse : SIOCGIFCONF is now a compile time option
+- * Steve Whitehouse : /proc/sys/net/decnet/conf/<sys>/forwarding
+- * Steve Whitehouse : Removed timer1 - it's a user space issue now
+- * Patrick Caulfield : Fixed router hello message format
+- * Steve Whitehouse : Got rid of constant sizes for blksize for
+- * devices. All mtu based now.
+- */
+-
+-#include <linux/capability.h>
+-#include <linux/module.h>
+-#include <linux/moduleparam.h>
+-#include <linux/init.h>
+-#include <linux/net.h>
+-#include <linux/netdevice.h>
+-#include <linux/proc_fs.h>
+-#include <linux/seq_file.h>
+-#include <linux/timer.h>
+-#include <linux/string.h>
+-#include <linux/if_addr.h>
+-#include <linux/if_arp.h>
+-#include <linux/if_ether.h>
+-#include <linux/skbuff.h>
+-#include <linux/sysctl.h>
+-#include <linux/notifier.h>
+-#include <linux/slab.h>
+-#include <linux/jiffies.h>
+-#include <linux/uaccess.h>
+-#include <net/net_namespace.h>
+-#include <net/neighbour.h>
+-#include <net/dst.h>
+-#include <net/flow.h>
+-#include <net/fib_rules.h>
+-#include <net/netlink.h>
+-#include <net/dn.h>
+-#include <net/dn_dev.h>
+-#include <net/dn_route.h>
+-#include <net/dn_neigh.h>
+-#include <net/dn_fib.h>
+-
+-#define DN_IFREQ_SIZE (offsetof(struct ifreq, ifr_ifru) + sizeof(struct sockaddr_dn))
+-
+-static char dn_rt_all_end_mcast[ETH_ALEN] = {0xAB,0x00,0x00,0x04,0x00,0x00};
+-static char dn_rt_all_rt_mcast[ETH_ALEN] = {0xAB,0x00,0x00,0x03,0x00,0x00};
+-static char dn_hiord[ETH_ALEN] = {0xAA,0x00,0x04,0x00,0x00,0x00};
+-static unsigned char dn_eco_version[3] = {0x02,0x00,0x00};
+-
+-extern struct neigh_table dn_neigh_table;
+-
+-/*
+- * decnet_address is kept in network order.
+- */
+-__le16 decnet_address = 0;
+-
+-static DEFINE_SPINLOCK(dndev_lock);
+-static struct net_device *decnet_default_device;
+-static BLOCKING_NOTIFIER_HEAD(dnaddr_chain);
+-
+-static struct dn_dev *dn_dev_create(struct net_device *dev, int *err);
+-static void dn_dev_delete(struct net_device *dev);
+-static void dn_ifaddr_notify(int event, struct dn_ifaddr *ifa);
+-
+-static int dn_eth_up(struct net_device *);
+-static void dn_eth_down(struct net_device *);
+-static void dn_send_brd_hello(struct net_device *dev, struct dn_ifaddr *ifa);
+-static void dn_send_ptp_hello(struct net_device *dev, struct dn_ifaddr *ifa);
+-
+-static struct dn_dev_parms dn_dev_list[] = {
+-{
+- .type = ARPHRD_ETHER, /* Ethernet */
+- .mode = DN_DEV_BCAST,
+- .state = DN_DEV_S_RU,
+- .t2 = 1,
+- .t3 = 10,
+- .name = "ethernet",
+- .up = dn_eth_up,
+- .down = dn_eth_down,
+- .timer3 = dn_send_brd_hello,
+-},
+-{
+- .type = ARPHRD_IPGRE, /* DECnet tunneled over GRE in IP */
+- .mode = DN_DEV_BCAST,
+- .state = DN_DEV_S_RU,
+- .t2 = 1,
+- .t3 = 10,
+- .name = "ipgre",
+- .timer3 = dn_send_brd_hello,
+-},
+-#if 0
+-{
+- .type = ARPHRD_X25, /* Bog standard X.25 */
+- .mode = DN_DEV_UCAST,
+- .state = DN_DEV_S_DS,
+- .t2 = 1,
+- .t3 = 120,
+- .name = "x25",
+- .timer3 = dn_send_ptp_hello,
+-},
+-#endif
+-#if 0
+-{
+- .type = ARPHRD_PPP, /* DECnet over PPP */
+- .mode = DN_DEV_BCAST,
+- .state = DN_DEV_S_RU,
+- .t2 = 1,
+- .t3 = 10,
+- .name = "ppp",
+- .timer3 = dn_send_brd_hello,
+-},
+-#endif
+-{
+- .type = ARPHRD_DDCMP, /* DECnet over DDCMP */
+- .mode = DN_DEV_UCAST,
+- .state = DN_DEV_S_DS,
+- .t2 = 1,
+- .t3 = 120,
+- .name = "ddcmp",
+- .timer3 = dn_send_ptp_hello,
+-},
+-{
+- .type = ARPHRD_LOOPBACK, /* Loopback interface - always last */
+- .mode = DN_DEV_BCAST,
+- .state = DN_DEV_S_RU,
+- .t2 = 1,
+- .t3 = 10,
+- .name = "loopback",
+- .timer3 = dn_send_brd_hello,
+-}
+-};
+-
+-#define DN_DEV_LIST_SIZE ARRAY_SIZE(dn_dev_list)
+-
+-#define DN_DEV_PARMS_OFFSET(x) offsetof(struct dn_dev_parms, x)
+-
+-#ifdef CONFIG_SYSCTL
+-
+-static int min_t2[] = { 1 };
+-static int max_t2[] = { 60 }; /* No max specified, but this seems sensible */
+-static int min_t3[] = { 1 };
+-static int max_t3[] = { 8191 }; /* Must fit in 16 bits when multiplied by BCT3MULT or T3MULT */
+-
+-static int min_priority[1];
+-static int max_priority[] = { 127 }; /* From DECnet spec */
+-
+-static int dn_forwarding_proc(struct ctl_table *, int,
+- void __user *, size_t *, loff_t *);
+-static struct dn_dev_sysctl_table {
+- struct ctl_table_header *sysctl_header;
+- struct ctl_table dn_dev_vars[5];
+-} dn_dev_sysctl = {
+- NULL,
+- {
+- {
+- .procname = "forwarding",
+- .data = (void *)DN_DEV_PARMS_OFFSET(forwarding),
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = dn_forwarding_proc,
+- },
+- {
+- .procname = "priority",
+- .data = (void *)DN_DEV_PARMS_OFFSET(priority),
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = proc_dointvec_minmax,
+- .extra1 = &min_priority,
+- .extra2 = &max_priority
+- },
+- {
+- .procname = "t2",
+- .data = (void *)DN_DEV_PARMS_OFFSET(t2),
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = proc_dointvec_minmax,
+- .extra1 = &min_t2,
+- .extra2 = &max_t2
+- },
+- {
+- .procname = "t3",
+- .data = (void *)DN_DEV_PARMS_OFFSET(t3),
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = proc_dointvec_minmax,
+- .extra1 = &min_t3,
+- .extra2 = &max_t3
+- },
+- { }
+- },
+-};
+-
+-static void dn_dev_sysctl_register(struct net_device *dev, struct dn_dev_parms *parms)
+-{
+- struct dn_dev_sysctl_table *t;
+- int i;
+-
+- char path[sizeof("net/decnet/conf/") + IFNAMSIZ];
+-
+- t = kmemdup(&dn_dev_sysctl, sizeof(*t), GFP_KERNEL);
+- if (t == NULL)
+- return;
+-
+- for(i = 0; i < ARRAY_SIZE(t->dn_dev_vars) - 1; i++) {
+- long offset = (long)t->dn_dev_vars[i].data;
+- t->dn_dev_vars[i].data = ((char *)parms) + offset;
+- }
+-
+- snprintf(path, sizeof(path), "net/decnet/conf/%s",
+- dev? dev->name : parms->name);
+-
+- t->dn_dev_vars[0].extra1 = (void *)dev;
+-
+- t->sysctl_header = register_net_sysctl(&init_net, path, t->dn_dev_vars);
+- if (t->sysctl_header == NULL)
+- kfree(t);
+- else
+- parms->sysctl = t;
+-}
+-
+-static void dn_dev_sysctl_unregister(struct dn_dev_parms *parms)
+-{
+- if (parms->sysctl) {
+- struct dn_dev_sysctl_table *t = parms->sysctl;
+- parms->sysctl = NULL;
+- unregister_net_sysctl_table(t->sysctl_header);
+- kfree(t);
+- }
+-}
+-
+-static int dn_forwarding_proc(struct ctl_table *table, int write,
+- void __user *buffer,
+- size_t *lenp, loff_t *ppos)
+-{
+-#ifdef CONFIG_DECNET_ROUTER
+- struct net_device *dev = table->extra1;
+- struct dn_dev *dn_db;
+- int err;
+- int tmp, old;
+-
+- if (table->extra1 == NULL)
+- return -EINVAL;
+-
+- dn_db = rcu_dereference_raw(dev->dn_ptr);
+- old = dn_db->parms.forwarding;
+-
+- err = proc_dointvec(table, write, buffer, lenp, ppos);
+-
+- if ((err >= 0) && write) {
+- if (dn_db->parms.forwarding < 0)
+- dn_db->parms.forwarding = 0;
+- if (dn_db->parms.forwarding > 2)
+- dn_db->parms.forwarding = 2;
+- /*
+- * What an ugly hack this is... its works, just. It
+- * would be nice if sysctl/proc were just that little
+- * bit more flexible so I don't have to write a special
+- * routine, or suffer hacks like this - SJW
+- */
+- tmp = dn_db->parms.forwarding;
+- dn_db->parms.forwarding = old;
+- if (dn_db->parms.down)
+- dn_db->parms.down(dev);
+- dn_db->parms.forwarding = tmp;
+- if (dn_db->parms.up)
+- dn_db->parms.up(dev);
+- }
+-
+- return err;
+-#else
+- return -EINVAL;
+-#endif
+-}
+-
+-#else /* CONFIG_SYSCTL */
+-static void dn_dev_sysctl_unregister(struct dn_dev_parms *parms)
+-{
+-}
+-static void dn_dev_sysctl_register(struct net_device *dev, struct dn_dev_parms *parms)
+-{
+-}
+-
+-#endif /* CONFIG_SYSCTL */
+-
+-static inline __u16 mtu2blksize(struct net_device *dev)
+-{
+- u32 blksize = dev->mtu;
+- if (blksize > 0xffff)
+- blksize = 0xffff;
+-
+- if (dev->type == ARPHRD_ETHER ||
+- dev->type == ARPHRD_PPP ||
+- dev->type == ARPHRD_IPGRE ||
+- dev->type == ARPHRD_LOOPBACK)
+- blksize -= 2;
+-
+- return (__u16)blksize;
+-}
+-
+-static struct dn_ifaddr *dn_dev_alloc_ifa(void)
+-{
+- struct dn_ifaddr *ifa;
+-
+- ifa = kzalloc(sizeof(*ifa), GFP_KERNEL);
+-
+- return ifa;
+-}
+-
+-static void dn_dev_free_ifa(struct dn_ifaddr *ifa)
+-{
+- kfree_rcu(ifa, rcu);
+-}
+-
+-static void dn_dev_del_ifa(struct dn_dev *dn_db, struct dn_ifaddr __rcu **ifap, int destroy)
+-{
+- struct dn_ifaddr *ifa1 = rtnl_dereference(*ifap);
+- unsigned char mac_addr[6];
+- struct net_device *dev = dn_db->dev;
+-
+- ASSERT_RTNL();
+-
+- *ifap = ifa1->ifa_next;
+-
+- if (dn_db->dev->type == ARPHRD_ETHER) {
+- if (ifa1->ifa_local != dn_eth2dn(dev->dev_addr)) {
+- dn_dn2eth(mac_addr, ifa1->ifa_local);
+- dev_mc_del(dev, mac_addr);
+- }
+- }
+-
+- dn_ifaddr_notify(RTM_DELADDR, ifa1);
+- blocking_notifier_call_chain(&dnaddr_chain, NETDEV_DOWN, ifa1);
+- if (destroy) {
+- dn_dev_free_ifa(ifa1);
+-
+- if (dn_db->ifa_list == NULL)
+- dn_dev_delete(dn_db->dev);
+- }
+-}
+-
+-static int dn_dev_insert_ifa(struct dn_dev *dn_db, struct dn_ifaddr *ifa)
+-{
+- struct net_device *dev = dn_db->dev;
+- struct dn_ifaddr *ifa1;
+- unsigned char mac_addr[6];
+-
+- ASSERT_RTNL();
+-
+- /* Check for duplicates */
+- for (ifa1 = rtnl_dereference(dn_db->ifa_list);
+- ifa1 != NULL;
+- ifa1 = rtnl_dereference(ifa1->ifa_next)) {
+- if (ifa1->ifa_local == ifa->ifa_local)
+- return -EEXIST;
+- }
+-
+- if (dev->type == ARPHRD_ETHER) {
+- if (ifa->ifa_local != dn_eth2dn(dev->dev_addr)) {
+- dn_dn2eth(mac_addr, ifa->ifa_local);
+- dev_mc_add(dev, mac_addr);
+- }
+- }
+-
+- ifa->ifa_next = dn_db->ifa_list;
+- rcu_assign_pointer(dn_db->ifa_list, ifa);
+-
+- dn_ifaddr_notify(RTM_NEWADDR, ifa);
+- blocking_notifier_call_chain(&dnaddr_chain, NETDEV_UP, ifa);
+-
+- return 0;
+-}
+-
+-static int dn_dev_set_ifa(struct net_device *dev, struct dn_ifaddr *ifa)
+-{
+- struct dn_dev *dn_db = rtnl_dereference(dev->dn_ptr);
+- int rv;
+-
+- if (dn_db == NULL) {
+- int err;
+- dn_db = dn_dev_create(dev, &err);
+- if (dn_db == NULL)
+- return err;
+- }
+-
+- ifa->ifa_dev = dn_db;
+-
+- if (dev->flags & IFF_LOOPBACK)
+- ifa->ifa_scope = RT_SCOPE_HOST;
+-
+- rv = dn_dev_insert_ifa(dn_db, ifa);
+- if (rv)
+- dn_dev_free_ifa(ifa);
+- return rv;
+-}
+-
+-
+-int dn_dev_ioctl(unsigned int cmd, void __user *arg)
+-{
+- char buffer[DN_IFREQ_SIZE];
+- struct ifreq *ifr = (struct ifreq *)buffer;
+- struct sockaddr_dn *sdn = (struct sockaddr_dn *)&ifr->ifr_addr;
+- struct dn_dev *dn_db;
+- struct net_device *dev;
+- struct dn_ifaddr *ifa = NULL;
+- struct dn_ifaddr __rcu **ifap = NULL;
+- int ret = 0;
+-
+- if (copy_from_user(ifr, arg, DN_IFREQ_SIZE))
+- return -EFAULT;
+- ifr->ifr_name[IFNAMSIZ-1] = 0;
+-
+- dev_load(&init_net, ifr->ifr_name);
+-
+- switch (cmd) {
+- case SIOCGIFADDR:
+- break;
+- case SIOCSIFADDR:
+- if (!capable(CAP_NET_ADMIN))
+- return -EACCES;
+- if (sdn->sdn_family != AF_DECnet)
+- return -EINVAL;
+- break;
+- default:
+- return -EINVAL;
+- }
+-
+- rtnl_lock();
+-
+- if ((dev = __dev_get_by_name(&init_net, ifr->ifr_name)) == NULL) {
+- ret = -ENODEV;
+- goto done;
+- }
+-
+- if ((dn_db = rtnl_dereference(dev->dn_ptr)) != NULL) {
+- for (ifap = &dn_db->ifa_list;
+- (ifa = rtnl_dereference(*ifap)) != NULL;
+- ifap = &ifa->ifa_next)
+- if (strcmp(ifr->ifr_name, ifa->ifa_label) == 0)
+- break;
+- }
+-
+- if (ifa == NULL && cmd != SIOCSIFADDR) {
+- ret = -EADDRNOTAVAIL;
+- goto done;
+- }
+-
+- switch (cmd) {
+- case SIOCGIFADDR:
+- *((__le16 *)sdn->sdn_nodeaddr) = ifa->ifa_local;
+- goto rarok;
+-
+- case SIOCSIFADDR:
+- if (!ifa) {
+- if ((ifa = dn_dev_alloc_ifa()) == NULL) {
+- ret = -ENOBUFS;
+- break;
+- }
+- memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
+- } else {
+- if (ifa->ifa_local == dn_saddr2dn(sdn))
+- break;
+- dn_dev_del_ifa(dn_db, ifap, 0);
+- }
+-
+- ifa->ifa_local = ifa->ifa_address = dn_saddr2dn(sdn);
+-
+- ret = dn_dev_set_ifa(dev, ifa);
+- }
+-done:
+- rtnl_unlock();
+-
+- return ret;
+-rarok:
+- if (copy_to_user(arg, ifr, DN_IFREQ_SIZE))
+- ret = -EFAULT;
+- goto done;
+-}
+-
+-struct net_device *dn_dev_get_default(void)
+-{
+- struct net_device *dev;
+-
+- spin_lock(&dndev_lock);
+- dev = decnet_default_device;
+- if (dev) {
+- if (dev->dn_ptr)
+- dev_hold(dev);
+- else
+- dev = NULL;
+- }
+- spin_unlock(&dndev_lock);
+-
+- return dev;
+-}
+-
+-int dn_dev_set_default(struct net_device *dev, int force)
+-{
+- struct net_device *old = NULL;
+- int rv = -EBUSY;
+- if (!dev->dn_ptr)
+- return -ENODEV;
+-
+- spin_lock(&dndev_lock);
+- if (force || decnet_default_device == NULL) {
+- old = decnet_default_device;
+- decnet_default_device = dev;
+- rv = 0;
+- }
+- spin_unlock(&dndev_lock);
+-
+- if (old)
+- dev_put(old);
+- return rv;
+-}
+-
+-static void dn_dev_check_default(struct net_device *dev)
+-{
+- spin_lock(&dndev_lock);
+- if (dev == decnet_default_device) {
+- decnet_default_device = NULL;
+- } else {
+- dev = NULL;
+- }
+- spin_unlock(&dndev_lock);
+-
+- if (dev)
+- dev_put(dev);
+-}
+-
+-/*
+- * Called with RTNL
+- */
+-static struct dn_dev *dn_dev_by_index(int ifindex)
+-{
+- struct net_device *dev;
+- struct dn_dev *dn_dev = NULL;
+-
+- dev = __dev_get_by_index(&init_net, ifindex);
+- if (dev)
+- dn_dev = rtnl_dereference(dev->dn_ptr);
+-
+- return dn_dev;
+-}
+-
+-static const struct nla_policy dn_ifa_policy[IFA_MAX+1] = {
+- [IFA_ADDRESS] = { .type = NLA_U16 },
+- [IFA_LOCAL] = { .type = NLA_U16 },
+- [IFA_LABEL] = { .type = NLA_STRING,
+- .len = IFNAMSIZ - 1 },
+- [IFA_FLAGS] = { .type = NLA_U32 },
+-};
+-
+-static int dn_nl_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
+- struct netlink_ext_ack *extack)
+-{
+- struct net *net = sock_net(skb->sk);
+- struct nlattr *tb[IFA_MAX+1];
+- struct dn_dev *dn_db;
+- struct ifaddrmsg *ifm;
+- struct dn_ifaddr *ifa;
+- struct dn_ifaddr __rcu **ifap;
+- int err = -EINVAL;
+-
+- if (!netlink_capable(skb, CAP_NET_ADMIN))
+- return -EPERM;
+-
+- if (!net_eq(net, &init_net))
+- goto errout;
+-
+- err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, dn_ifa_policy,
+- extack);
+- if (err < 0)
+- goto errout;
+-
+- err = -ENODEV;
+- ifm = nlmsg_data(nlh);
+- if ((dn_db = dn_dev_by_index(ifm->ifa_index)) == NULL)
+- goto errout;
+-
+- err = -EADDRNOTAVAIL;
+- for (ifap = &dn_db->ifa_list;
+- (ifa = rtnl_dereference(*ifap)) != NULL;
+- ifap = &ifa->ifa_next) {
+- if (tb[IFA_LOCAL] &&
+- nla_memcmp(tb[IFA_LOCAL], &ifa->ifa_local, 2))
+- continue;
+-
+- if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
+- continue;
+-
+- dn_dev_del_ifa(dn_db, ifap, 1);
+- return 0;
+- }
+-
+-errout:
+- return err;
+-}
+-
+-static int dn_nl_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
+- struct netlink_ext_ack *extack)
+-{
+- struct net *net = sock_net(skb->sk);
+- struct nlattr *tb[IFA_MAX+1];
+- struct net_device *dev;
+- struct dn_dev *dn_db;
+- struct ifaddrmsg *ifm;
+- struct dn_ifaddr *ifa;
+- int err;
+-
+- if (!netlink_capable(skb, CAP_NET_ADMIN))
+- return -EPERM;
+-
+- if (!net_eq(net, &init_net))
+- return -EINVAL;
+-
+- err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, dn_ifa_policy,
+- extack);
+- if (err < 0)
+- return err;
+-
+- if (tb[IFA_LOCAL] == NULL)
+- return -EINVAL;
+-
+- ifm = nlmsg_data(nlh);
+- if ((dev = __dev_get_by_index(&init_net, ifm->ifa_index)) == NULL)
+- return -ENODEV;
+-
+- if ((dn_db = rtnl_dereference(dev->dn_ptr)) == NULL) {
+- dn_db = dn_dev_create(dev, &err);
+- if (!dn_db)
+- return err;
+- }
+-
+- if ((ifa = dn_dev_alloc_ifa()) == NULL)
+- return -ENOBUFS;
+-
+- if (tb[IFA_ADDRESS] == NULL)
+- tb[IFA_ADDRESS] = tb[IFA_LOCAL];
+-
+- ifa->ifa_local = nla_get_le16(tb[IFA_LOCAL]);
+- ifa->ifa_address = nla_get_le16(tb[IFA_ADDRESS]);
+- ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
+- ifm->ifa_flags;
+- ifa->ifa_scope = ifm->ifa_scope;
+- ifa->ifa_dev = dn_db;
+-
+- if (tb[IFA_LABEL])
+- nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
+- else
+- memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
+-
+- err = dn_dev_insert_ifa(dn_db, ifa);
+- if (err)
+- dn_dev_free_ifa(ifa);
+-
+- return err;
+-}
+-
+-static inline size_t dn_ifaddr_nlmsg_size(void)
+-{
+- return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
+- + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
+- + nla_total_size(2) /* IFA_ADDRESS */
+- + nla_total_size(2) /* IFA_LOCAL */
+- + nla_total_size(4); /* IFA_FLAGS */
+-}
+-
+-static int dn_nl_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa,
+- u32 portid, u32 seq, int event, unsigned int flags)
+-{
+- struct ifaddrmsg *ifm;
+- struct nlmsghdr *nlh;
+- u32 ifa_flags = ifa->ifa_flags | IFA_F_PERMANENT;
+-
+- nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags);
+- if (nlh == NULL)
+- return -EMSGSIZE;
+-
+- ifm = nlmsg_data(nlh);
+- ifm->ifa_family = AF_DECnet;
+- ifm->ifa_prefixlen = 16;
+- ifm->ifa_flags = ifa_flags;
+- ifm->ifa_scope = ifa->ifa_scope;
+- ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
+-
+- if ((ifa->ifa_address &&
+- nla_put_le16(skb, IFA_ADDRESS, ifa->ifa_address)) ||
+- (ifa->ifa_local &&
+- nla_put_le16(skb, IFA_LOCAL, ifa->ifa_local)) ||
+- (ifa->ifa_label[0] &&
+- nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
+- nla_put_u32(skb, IFA_FLAGS, ifa_flags))
+- goto nla_put_failure;
+- nlmsg_end(skb, nlh);
+- return 0;
+-
+-nla_put_failure:
+- nlmsg_cancel(skb, nlh);
+- return -EMSGSIZE;
+-}
+-
+-static void dn_ifaddr_notify(int event, struct dn_ifaddr *ifa)
+-{
+- struct sk_buff *skb;
+- int err = -ENOBUFS;
+-
+- skb = alloc_skb(dn_ifaddr_nlmsg_size(), GFP_KERNEL);
+- if (skb == NULL)
+- goto errout;
+-
+- err = dn_nl_fill_ifaddr(skb, ifa, 0, 0, event, 0);
+- if (err < 0) {
+- /* -EMSGSIZE implies BUG in dn_ifaddr_nlmsg_size() */
+- WARN_ON(err == -EMSGSIZE);
+- kfree_skb(skb);
+- goto errout;
+- }
+- rtnl_notify(skb, &init_net, 0, RTNLGRP_DECnet_IFADDR, NULL, GFP_KERNEL);
+- return;
+-errout:
+- if (err < 0)
+- rtnl_set_sk_err(&init_net, RTNLGRP_DECnet_IFADDR, err);
+-}
+-
+-static int dn_nl_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
+-{
+- struct net *net = sock_net(skb->sk);
+- int idx, dn_idx = 0, skip_ndevs, skip_naddr;
+- struct net_device *dev;
+- struct dn_dev *dn_db;
+- struct dn_ifaddr *ifa;
+-
+- if (!net_eq(net, &init_net))
+- return 0;
+-
+- skip_ndevs = cb->args[0];
+- skip_naddr = cb->args[1];
+-
+- idx = 0;
+- rcu_read_lock();
+- for_each_netdev_rcu(&init_net, dev) {
+- if (idx < skip_ndevs)
+- goto cont;
+- else if (idx > skip_ndevs) {
+- /* Only skip over addresses for first dev dumped
+- * in this iteration (idx == skip_ndevs) */
+- skip_naddr = 0;
+- }
+-
+- if ((dn_db = rcu_dereference(dev->dn_ptr)) == NULL)
+- goto cont;
+-
+- for (ifa = rcu_dereference(dn_db->ifa_list), dn_idx = 0; ifa;
+- ifa = rcu_dereference(ifa->ifa_next), dn_idx++) {
+- if (dn_idx < skip_naddr)
+- continue;
+-
+- if (dn_nl_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).portid,
+- cb->nlh->nlmsg_seq, RTM_NEWADDR,
+- NLM_F_MULTI) < 0)
+- goto done;
+- }
+-cont:
+- idx++;
+- }
+-done:
+- rcu_read_unlock();
+- cb->args[0] = idx;
+- cb->args[1] = dn_idx;
+-
+- return skb->len;
+-}
+-
+-static int dn_dev_get_first(struct net_device *dev, __le16 *addr)
+-{
+- struct dn_dev *dn_db;
+- struct dn_ifaddr *ifa;
+- int rv = -ENODEV;
+-
+- rcu_read_lock();
+- dn_db = rcu_dereference(dev->dn_ptr);
+- if (dn_db == NULL)
+- goto out;
+-
+- ifa = rcu_dereference(dn_db->ifa_list);
+- if (ifa != NULL) {
+- *addr = ifa->ifa_local;
+- rv = 0;
+- }
+-out:
+- rcu_read_unlock();
+- return rv;
+-}
+-
+-/*
+- * Find a default address to bind to.
+- *
+- * This is one of those areas where the initial VMS concepts don't really
+- * map onto the Linux concepts, and since we introduced multiple addresses
+- * per interface we have to cope with slightly odd ways of finding out what
+- * "our address" really is. Mostly it's not a problem; for this we just guess
+- * a sensible default. Eventually the routing code will take care of all the
+- * nasties for us I hope.
+- */
+-int dn_dev_bind_default(__le16 *addr)
+-{
+- struct net_device *dev;
+- int rv;
+- dev = dn_dev_get_default();
+-last_chance:
+- if (dev) {
+- rv = dn_dev_get_first(dev, addr);
+- dev_put(dev);
+- if (rv == 0 || dev == init_net.loopback_dev)
+- return rv;
+- }
+- dev = init_net.loopback_dev;
+- dev_hold(dev);
+- goto last_chance;
+-}
+-
+-static void dn_send_endnode_hello(struct net_device *dev, struct dn_ifaddr *ifa)
+-{
+- struct endnode_hello_message *msg;
+- struct sk_buff *skb = NULL;
+- __le16 *pktlen;
+- struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr);
+-
+- if ((skb = dn_alloc_skb(NULL, sizeof(*msg), GFP_ATOMIC)) == NULL)
+- return;
+-
+- skb->dev = dev;
+-
+- msg = skb_put(skb, sizeof(*msg));
+-
+- msg->msgflg = 0x0D;
+- memcpy(msg->tiver, dn_eco_version, 3);
+- dn_dn2eth(msg->id, ifa->ifa_local);
+- msg->iinfo = DN_RT_INFO_ENDN;
+- msg->blksize = cpu_to_le16(mtu2blksize(dev));
+- msg->area = 0x00;
+- memset(msg->seed, 0, 8);
+- memcpy(msg->neighbor, dn_hiord, ETH_ALEN);
+-
+- if (dn_db->router) {
+- struct dn_neigh *dn = (struct dn_neigh *)dn_db->router;
+- dn_dn2eth(msg->neighbor, dn->addr);
+- }
+-
+- msg->timer = cpu_to_le16((unsigned short)dn_db->parms.t3);
+- msg->mpd = 0x00;
+- msg->datalen = 0x02;
+- memset(msg->data, 0xAA, 2);
+-
+- pktlen = skb_push(skb, 2);
+- *pktlen = cpu_to_le16(skb->len - 2);
+-
+- skb_reset_network_header(skb);
+-
+- dn_rt_finish_output(skb, dn_rt_all_rt_mcast, msg->id);
+-}
+-
+-
+-#define DRDELAY (5 * HZ)
+-
+-static int dn_am_i_a_router(struct dn_neigh *dn, struct dn_dev *dn_db, struct dn_ifaddr *ifa)
+-{
+- /* First check time since device went up */
+- if (time_before(jiffies, dn_db->uptime + DRDELAY))
+- return 0;
+-
+- /* If there is no router, then yes... */
+- if (!dn_db->router)
+- return 1;
+-
+- /* otherwise only if we have a higher priority or.. */
+- if (dn->priority < dn_db->parms.priority)
+- return 1;
+-
+- /* if we have equal priority and a higher node number */
+- if (dn->priority != dn_db->parms.priority)
+- return 0;
+-
+- if (le16_to_cpu(dn->addr) < le16_to_cpu(ifa->ifa_local))
+- return 1;
+-
+- return 0;
+-}
+-
+-static void dn_send_router_hello(struct net_device *dev, struct dn_ifaddr *ifa)
+-{
+- int n;
+- struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr);
+- struct dn_neigh *dn = (struct dn_neigh *)dn_db->router;
+- struct sk_buff *skb;
+- size_t size;
+- unsigned char *ptr;
+- unsigned char *i1, *i2;
+- __le16 *pktlen;
+- char *src;
+-
+- if (mtu2blksize(dev) < (26 + 7))
+- return;
+-
+- n = mtu2blksize(dev) - 26;
+- n /= 7;
+-
+- if (n > 32)
+- n = 32;
+-
+- size = 2 + 26 + 7 * n;
+-
+- if ((skb = dn_alloc_skb(NULL, size, GFP_ATOMIC)) == NULL)
+- return;
+-
+- skb->dev = dev;
+- ptr = skb_put(skb, size);
+-
+- *ptr++ = DN_RT_PKT_CNTL | DN_RT_PKT_ERTH;
+- *ptr++ = 2; /* ECO */
+- *ptr++ = 0;
+- *ptr++ = 0;
+- dn_dn2eth(ptr, ifa->ifa_local);
+- src = ptr;
+- ptr += ETH_ALEN;
+- *ptr++ = dn_db->parms.forwarding == 1 ?
+- DN_RT_INFO_L1RT : DN_RT_INFO_L2RT;
+- *((__le16 *)ptr) = cpu_to_le16(mtu2blksize(dev));
+- ptr += 2;
+- *ptr++ = dn_db->parms.priority; /* Priority */
+- *ptr++ = 0; /* Area: Reserved */
+- *((__le16 *)ptr) = cpu_to_le16((unsigned short)dn_db->parms.t3);
+- ptr += 2;
+- *ptr++ = 0; /* MPD: Reserved */
+- i1 = ptr++;
+- memset(ptr, 0, 7); /* Name: Reserved */
+- ptr += 7;
+- i2 = ptr++;
+-
+- n = dn_neigh_elist(dev, ptr, n);
+-
+- *i2 = 7 * n;
+- *i1 = 8 + *i2;
+-
+- skb_trim(skb, (27 + *i2));
+-
+- pktlen = skb_push(skb, 2);
+- *pktlen = cpu_to_le16(skb->len - 2);
+-
+- skb_reset_network_header(skb);
+-
+- if (dn_am_i_a_router(dn, dn_db, ifa)) {
+- struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
+- if (skb2) {
+- dn_rt_finish_output(skb2, dn_rt_all_end_mcast, src);
+- }
+- }
+-
+- dn_rt_finish_output(skb, dn_rt_all_rt_mcast, src);
+-}
+-
+-static void dn_send_brd_hello(struct net_device *dev, struct dn_ifaddr *ifa)
+-{
+- struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr);
+-
+- if (dn_db->parms.forwarding == 0)
+- dn_send_endnode_hello(dev, ifa);
+- else
+- dn_send_router_hello(dev, ifa);
+-}
+-
+-static void dn_send_ptp_hello(struct net_device *dev, struct dn_ifaddr *ifa)
+-{
+- int tdlen = 16;
+- int size = dev->hard_header_len + 2 + 4 + tdlen;
+- struct sk_buff *skb = dn_alloc_skb(NULL, size, GFP_ATOMIC);
+- int i;
+- unsigned char *ptr;
+- char src[ETH_ALEN];
+-
+- if (skb == NULL)
+- return ;
+-
+- skb->dev = dev;
+- skb_push(skb, dev->hard_header_len);
+- ptr = skb_put(skb, 2 + 4 + tdlen);
+-
+- *ptr++ = DN_RT_PKT_HELO;
+- *((__le16 *)ptr) = ifa->ifa_local;
+- ptr += 2;
+- *ptr++ = tdlen;
+-
+- for(i = 0; i < tdlen; i++)
+- *ptr++ = 0252;
+-
+- dn_dn2eth(src, ifa->ifa_local);
+- dn_rt_finish_output(skb, dn_rt_all_rt_mcast, src);
+-}
+-
+-static int dn_eth_up(struct net_device *dev)
+-{
+- struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr);
+-
+- if (dn_db->parms.forwarding == 0)
+- dev_mc_add(dev, dn_rt_all_end_mcast);
+- else
+- dev_mc_add(dev, dn_rt_all_rt_mcast);
+-
+- dn_db->use_long = 1;
+-
+- return 0;
+-}
+-
+-static void dn_eth_down(struct net_device *dev)
+-{
+- struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr);
+-
+- if (dn_db->parms.forwarding == 0)
+- dev_mc_del(dev, dn_rt_all_end_mcast);
+- else
+- dev_mc_del(dev, dn_rt_all_rt_mcast);
+-}
+-
+-static void dn_dev_set_timer(struct net_device *dev);
+-
+-static void dn_dev_timer_func(unsigned long arg)
+-{
+- struct net_device *dev = (struct net_device *)arg;
+- struct dn_dev *dn_db;
+- struct dn_ifaddr *ifa;
+-
+- rcu_read_lock();
+- dn_db = rcu_dereference(dev->dn_ptr);
+- if (dn_db->t3 <= dn_db->parms.t2) {
+- if (dn_db->parms.timer3) {
+- for (ifa = rcu_dereference(dn_db->ifa_list);
+- ifa;
+- ifa = rcu_dereference(ifa->ifa_next)) {
+- if (!(ifa->ifa_flags & IFA_F_SECONDARY))
+- dn_db->parms.timer3(dev, ifa);
+- }
+- }
+- dn_db->t3 = dn_db->parms.t3;
+- } else {
+- dn_db->t3 -= dn_db->parms.t2;
+- }
+- rcu_read_unlock();
+- dn_dev_set_timer(dev);
+-}
+-
+-static void dn_dev_set_timer(struct net_device *dev)
+-{
+- struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr);
+-
+- if (dn_db->parms.t2 > dn_db->parms.t3)
+- dn_db->parms.t2 = dn_db->parms.t3;
+-
+- dn_db->timer.data = (unsigned long)dev;
+- dn_db->timer.function = dn_dev_timer_func;
+- dn_db->timer.expires = jiffies + (dn_db->parms.t2 * HZ);
+-
+- add_timer(&dn_db->timer);
+-}
+-
+-static struct dn_dev *dn_dev_create(struct net_device *dev, int *err)
+-{
+- int i;
+- struct dn_dev_parms *p = dn_dev_list;
+- struct dn_dev *dn_db;
+-
+- for(i = 0; i < DN_DEV_LIST_SIZE; i++, p++) {
+- if (p->type == dev->type)
+- break;
+- }
+-
+- *err = -ENODEV;
+- if (i == DN_DEV_LIST_SIZE)
+- return NULL;
+-
+- *err = -ENOBUFS;
+- if ((dn_db = kzalloc(sizeof(struct dn_dev), GFP_ATOMIC)) == NULL)
+- return NULL;
+-
+- memcpy(&dn_db->parms, p, sizeof(struct dn_dev_parms));
+-
+- rcu_assign_pointer(dev->dn_ptr, dn_db);
+- dn_db->dev = dev;
+- init_timer(&dn_db->timer);
+-
+- dn_db->uptime = jiffies;
+-
+- dn_db->neigh_parms = neigh_parms_alloc(dev, &dn_neigh_table);
+- if (!dn_db->neigh_parms) {
+- RCU_INIT_POINTER(dev->dn_ptr, NULL);
+- kfree(dn_db);
+- return NULL;
+- }
+-
+- if (dn_db->parms.up) {
+- if (dn_db->parms.up(dev) < 0) {
+- neigh_parms_release(&dn_neigh_table, dn_db->neigh_parms);
+- dev->dn_ptr = NULL;
+- kfree(dn_db);
+- return NULL;
+- }
+- }
+-
+- dn_dev_sysctl_register(dev, &dn_db->parms);
+-
+- dn_dev_set_timer(dev);
+-
+- *err = 0;
+- return dn_db;
+-}
+-
+-
+-/*
+- * This processes a device up event. We only start up
+- * the loopback device & ethernet devices with correct
+- * MAC addresses automatically. Others must be started
+- * specifically.
+- *
+- * FIXME: How should we configure the loopback address ? If we could dispense
+- * with using decnet_address here and for autobind, it will be one less thing
+- * for users to worry about setting up.
+- */
+-
+-void dn_dev_up(struct net_device *dev)
+-{
+- struct dn_ifaddr *ifa;
+- __le16 addr = decnet_address;
+- int maybe_default = 0;
+- struct dn_dev *dn_db = rtnl_dereference(dev->dn_ptr);
+-
+- if ((dev->type != ARPHRD_ETHER) && (dev->type != ARPHRD_LOOPBACK))
+- return;
+-
+- /*
+- * Need to ensure that loopback device has a dn_db attached to it
+- * to allow creation of neighbours against it, even though it might
+- * not have a local address of its own. Might as well do the same for
+- * all autoconfigured interfaces.
+- */
+- if (dn_db == NULL) {
+- int err;
+- dn_db = dn_dev_create(dev, &err);
+- if (dn_db == NULL)
+- return;
+- }
+-
+- if (dev->type == ARPHRD_ETHER) {
+- if (memcmp(dev->dev_addr, dn_hiord, 4) != 0)
+- return;
+- addr = dn_eth2dn(dev->dev_addr);
+- maybe_default = 1;
+- }
+-
+- if (addr == 0)
+- return;
+-
+- if ((ifa = dn_dev_alloc_ifa()) == NULL)
+- return;
+-
+- ifa->ifa_local = ifa->ifa_address = addr;
+- ifa->ifa_flags = 0;
+- ifa->ifa_scope = RT_SCOPE_UNIVERSE;
+- strcpy(ifa->ifa_label, dev->name);
+-
+- dn_dev_set_ifa(dev, ifa);
+-
+- /*
+- * Automagically set the default device to the first automatically
+- * configured ethernet card in the system.
+- */
+- if (maybe_default) {
+- dev_hold(dev);
+- if (dn_dev_set_default(dev, 0))
+- dev_put(dev);
+- }
+-}
+-
+-static void dn_dev_delete(struct net_device *dev)
+-{
+- struct dn_dev *dn_db = rtnl_dereference(dev->dn_ptr);
+-
+- if (dn_db == NULL)
+- return;
+-
+- del_timer_sync(&dn_db->timer);
+- dn_dev_sysctl_unregister(&dn_db->parms);
+- dn_dev_check_default(dev);
+- neigh_ifdown(&dn_neigh_table, dev);
+-
+- if (dn_db->parms.down)
+- dn_db->parms.down(dev);
+-
+- dev->dn_ptr = NULL;
+-
+- neigh_parms_release(&dn_neigh_table, dn_db->neigh_parms);
+- neigh_ifdown(&dn_neigh_table, dev);
+-
+- if (dn_db->router)
+- neigh_release(dn_db->router);
+- if (dn_db->peer)
+- neigh_release(dn_db->peer);
+-
+- kfree(dn_db);
+-}
+-
+-void dn_dev_down(struct net_device *dev)
+-{
+- struct dn_dev *dn_db = rtnl_dereference(dev->dn_ptr);
+- struct dn_ifaddr *ifa;
+-
+- if (dn_db == NULL)
+- return;
+-
+- while ((ifa = rtnl_dereference(dn_db->ifa_list)) != NULL) {
+- dn_dev_del_ifa(dn_db, &dn_db->ifa_list, 0);
+- dn_dev_free_ifa(ifa);
+- }
+-
+- dn_dev_delete(dev);
+-}
+-
+-void dn_dev_init_pkt(struct sk_buff *skb)
+-{
+-}
+-
+-void dn_dev_veri_pkt(struct sk_buff *skb)
+-{
+-}
+-
+-void dn_dev_hello(struct sk_buff *skb)
+-{
+-}
+-
+-void dn_dev_devices_off(void)
+-{
+- struct net_device *dev;
+-
+- rtnl_lock();
+- for_each_netdev(&init_net, dev)
+- dn_dev_down(dev);
+- rtnl_unlock();
+-
+-}
+-
+-void dn_dev_devices_on(void)
+-{
+- struct net_device *dev;
+-
+- rtnl_lock();
+- for_each_netdev(&init_net, dev) {
+- if (dev->flags & IFF_UP)
+- dn_dev_up(dev);
+- }
+- rtnl_unlock();
+-}
+-
+-int register_dnaddr_notifier(struct notifier_block *nb)
+-{
+- return blocking_notifier_chain_register(&dnaddr_chain, nb);
+-}
+-
+-int unregister_dnaddr_notifier(struct notifier_block *nb)
+-{
+- return blocking_notifier_chain_unregister(&dnaddr_chain, nb);
+-}
+-
+-#ifdef CONFIG_PROC_FS
+-static inline int is_dn_dev(struct net_device *dev)
+-{
+- return dev->dn_ptr != NULL;
+-}
+-
+-static void *dn_dev_seq_start(struct seq_file *seq, loff_t *pos)
+- __acquires(RCU)
+-{
+- int i;
+- struct net_device *dev;
+-
+- rcu_read_lock();
+-
+- if (*pos == 0)
+- return SEQ_START_TOKEN;
+-
+- i = 1;
+- for_each_netdev_rcu(&init_net, dev) {
+- if (!is_dn_dev(dev))
+- continue;
+-
+- if (i++ == *pos)
+- return dev;
+- }
+-
+- return NULL;
+-}
+-
+-static void *dn_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+-{
+- struct net_device *dev;
+-
+- ++*pos;
+-
+- dev = v;
+- if (v == SEQ_START_TOKEN)
+- dev = net_device_entry(&init_net.dev_base_head);
+-
+- for_each_netdev_continue_rcu(&init_net, dev) {
+- if (!is_dn_dev(dev))
+- continue;
+-
+- return dev;
+- }
+-
+- return NULL;
+-}
+-
+-static void dn_dev_seq_stop(struct seq_file *seq, void *v)
+- __releases(RCU)
+-{
+- rcu_read_unlock();
+-}
+-
+-static char *dn_type2asc(char type)
+-{
+- switch (type) {
+- case DN_DEV_BCAST:
+- return "B";
+- case DN_DEV_UCAST:
+- return "U";
+- case DN_DEV_MPOINT:
+- return "M";
+- }
+-
+- return "?";
+-}
+-
+-static int dn_dev_seq_show(struct seq_file *seq, void *v)
+-{
+- if (v == SEQ_START_TOKEN)
+- seq_puts(seq, "Name Flags T1 Timer1 T3 Timer3 BlkSize Pri State DevType Router Peer\n");
+- else {
+- struct net_device *dev = v;
+- char peer_buf[DN_ASCBUF_LEN];
+- char router_buf[DN_ASCBUF_LEN];
+- struct dn_dev *dn_db = rcu_dereference(dev->dn_ptr);
+-
+- seq_printf(seq, "%-8s %1s %04u %04u %04lu %04lu"
+- " %04hu %03d %02x %-10s %-7s %-7s\n",
+- dev->name ? dev->name : "???",
+- dn_type2asc(dn_db->parms.mode),
+- 0, 0,
+- dn_db->t3, dn_db->parms.t3,
+- mtu2blksize(dev),
+- dn_db->parms.priority,
+- dn_db->parms.state, dn_db->parms.name,
+- dn_db->router ? dn_addr2asc(le16_to_cpu(*(__le16 *)dn_db->router->primary_key), router_buf) : "",
+- dn_db->peer ? dn_addr2asc(le16_to_cpu(*(__le16 *)dn_db->peer->primary_key), peer_buf) : "");
+- }
+- return 0;
+-}
+-
+-static const struct seq_operations dn_dev_seq_ops = {
+- .start = dn_dev_seq_start,
+- .next = dn_dev_seq_next,
+- .stop = dn_dev_seq_stop,
+- .show = dn_dev_seq_show,
+-};
+-
+-static int dn_dev_seq_open(struct inode *inode, struct file *file)
+-{
+- return seq_open(file, &dn_dev_seq_ops);
+-}
+-
+-static const struct file_operations dn_dev_seq_fops = {
+- .owner = THIS_MODULE,
+- .open = dn_dev_seq_open,
+- .read = seq_read,
+- .llseek = seq_lseek,
+- .release = seq_release,
+-};
+-
+-#endif /* CONFIG_PROC_FS */
+-
+-static int addr[2];
+-module_param_array(addr, int, NULL, 0444);
+-MODULE_PARM_DESC(addr, "The DECnet address of this machine: area,node");
+-
+-void __init dn_dev_init(void)
+-{
+- if (addr[0] > 63 || addr[0] < 0) {
+- printk(KERN_ERR "DECnet: Area must be between 0 and 63");
+- return;
+- }
+-
+- if (addr[1] > 1023 || addr[1] < 0) {
+- printk(KERN_ERR "DECnet: Node must be between 0 and 1023");
+- return;
+- }
+-
+- decnet_address = cpu_to_le16((addr[0] << 10) | addr[1]);
+-
+- dn_dev_devices_on();
+-
+- rtnl_register(PF_DECnet, RTM_NEWADDR, dn_nl_newaddr, NULL, 0);
+- rtnl_register(PF_DECnet, RTM_DELADDR, dn_nl_deladdr, NULL, 0);
+- rtnl_register(PF_DECnet, RTM_GETADDR, NULL, dn_nl_dump_ifaddr, 0);
+-
+- proc_create("decnet_dev", S_IRUGO, init_net.proc_net, &dn_dev_seq_fops);
+-
+-#ifdef CONFIG_SYSCTL
+- {
+- int i;
+- for(i = 0; i < DN_DEV_LIST_SIZE; i++)
+- dn_dev_sysctl_register(NULL, &dn_dev_list[i]);
+- }
+-#endif /* CONFIG_SYSCTL */
+-}
+-
+-void __exit dn_dev_cleanup(void)
+-{
+-#ifdef CONFIG_SYSCTL
+- {
+- int i;
+- for(i = 0; i < DN_DEV_LIST_SIZE; i++)
+- dn_dev_sysctl_unregister(&dn_dev_list[i]);
+- }
+-#endif /* CONFIG_SYSCTL */
+-
+- remove_proc_entry("decnet_dev", init_net.proc_net);
+-
+- dn_dev_devices_off();
+-}
+diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c
+deleted file mode 100644
+index b37a1b833c772..0000000000000
+--- a/net/decnet/dn_fib.c
++++ /dev/null
+@@ -1,799 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/*
+- * DECnet An implementation of the DECnet protocol suite for the LINUX
+- * operating system. DECnet is implemented using the BSD Socket
+- * interface as the means of communication with the user level.
+- *
+- * DECnet Routing Forwarding Information Base (Glue/Info List)
+- *
+- * Author: Steve Whitehouse <SteveW@ACM.org>
+- *
+- *
+- * Changes:
+- * Alexey Kuznetsov : SMP locking changes
+- * Steve Whitehouse : Rewrote it... Well to be more correct, I
+- * copied most of it from the ipv4 fib code.
+- * Steve Whitehouse : Updated it in style and fixed a few bugs
+- * which were fixed in the ipv4 code since
+- * this code was copied from it.
+- *
+- */
+-#include <linux/string.h>
+-#include <linux/net.h>
+-#include <linux/socket.h>
+-#include <linux/slab.h>
+-#include <linux/sockios.h>
+-#include <linux/init.h>
+-#include <linux/skbuff.h>
+-#include <linux/netlink.h>
+-#include <linux/rtnetlink.h>
+-#include <linux/proc_fs.h>
+-#include <linux/netdevice.h>
+-#include <linux/timer.h>
+-#include <linux/spinlock.h>
+-#include <linux/atomic.h>
+-#include <linux/uaccess.h>
+-#include <net/neighbour.h>
+-#include <net/dst.h>
+-#include <net/flow.h>
+-#include <net/fib_rules.h>
+-#include <net/dn.h>
+-#include <net/dn_route.h>
+-#include <net/dn_fib.h>
+-#include <net/dn_neigh.h>
+-#include <net/dn_dev.h>
+-#include <net/nexthop.h>
+-
+-#define RT_MIN_TABLE 1
+-
+-#define for_fib_info() { struct dn_fib_info *fi;\
+- for(fi = dn_fib_info_list; fi; fi = fi->fib_next)
+-#define endfor_fib_info() }
+-
+-#define for_nexthops(fi) { int nhsel; const struct dn_fib_nh *nh;\
+- for(nhsel = 0, nh = (fi)->fib_nh; nhsel < (fi)->fib_nhs; nh++, nhsel++)
+-
+-#define change_nexthops(fi) { int nhsel; struct dn_fib_nh *nh;\
+- for(nhsel = 0, nh = (struct dn_fib_nh *)((fi)->fib_nh); nhsel < (fi)->fib_nhs; nh++, nhsel++)
+-
+-#define endfor_nexthops(fi) }
+-
+-static DEFINE_SPINLOCK(dn_fib_multipath_lock);
+-static struct dn_fib_info *dn_fib_info_list;
+-static DEFINE_SPINLOCK(dn_fib_info_lock);
+-
+-static struct
+-{
+- int error;
+- u8 scope;
+-} dn_fib_props[RTN_MAX+1] = {
+- [RTN_UNSPEC] = { .error = 0, .scope = RT_SCOPE_NOWHERE },
+- [RTN_UNICAST] = { .error = 0, .scope = RT_SCOPE_UNIVERSE },
+- [RTN_LOCAL] = { .error = 0, .scope = RT_SCOPE_HOST },
+- [RTN_BROADCAST] = { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE },
+- [RTN_ANYCAST] = { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE },
+- [RTN_MULTICAST] = { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE },
+- [RTN_BLACKHOLE] = { .error = -EINVAL, .scope = RT_SCOPE_UNIVERSE },
+- [RTN_UNREACHABLE] = { .error = -EHOSTUNREACH, .scope = RT_SCOPE_UNIVERSE },
+- [RTN_PROHIBIT] = { .error = -EACCES, .scope = RT_SCOPE_UNIVERSE },
+- [RTN_THROW] = { .error = -EAGAIN, .scope = RT_SCOPE_UNIVERSE },
+- [RTN_NAT] = { .error = 0, .scope = RT_SCOPE_NOWHERE },
+- [RTN_XRESOLVE] = { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE },
+-};
+-
+-static int dn_fib_sync_down(__le16 local, struct net_device *dev, int force);
+-static int dn_fib_sync_up(struct net_device *dev);
+-
+-void dn_fib_free_info(struct dn_fib_info *fi)
+-{
+- if (fi->fib_dead == 0) {
+- printk(KERN_DEBUG "DECnet: BUG! Attempt to free alive dn_fib_info\n");
+- return;
+- }
+-
+- change_nexthops(fi) {
+- if (nh->nh_dev)
+- dev_put(nh->nh_dev);
+- nh->nh_dev = NULL;
+- } endfor_nexthops(fi);
+- kfree(fi);
+-}
+-
+-void dn_fib_release_info(struct dn_fib_info *fi)
+-{
+- spin_lock(&dn_fib_info_lock);
+- if (fi && --fi->fib_treeref == 0) {
+- if (fi->fib_next)
+- fi->fib_next->fib_prev = fi->fib_prev;
+- if (fi->fib_prev)
+- fi->fib_prev->fib_next = fi->fib_next;
+- if (fi == dn_fib_info_list)
+- dn_fib_info_list = fi->fib_next;
+- fi->fib_dead = 1;
+- dn_fib_info_put(fi);
+- }
+- spin_unlock(&dn_fib_info_lock);
+-}
+-
+-static inline int dn_fib_nh_comp(const struct dn_fib_info *fi, const struct dn_fib_info *ofi)
+-{
+- const struct dn_fib_nh *onh = ofi->fib_nh;
+-
+- for_nexthops(fi) {
+- if (nh->nh_oif != onh->nh_oif ||
+- nh->nh_gw != onh->nh_gw ||
+- nh->nh_scope != onh->nh_scope ||
+- nh->nh_weight != onh->nh_weight ||
+- ((nh->nh_flags^onh->nh_flags)&~RTNH_F_DEAD))
+- return -1;
+- onh++;
+- } endfor_nexthops(fi);
+- return 0;
+-}
+-
+-static inline struct dn_fib_info *dn_fib_find_info(const struct dn_fib_info *nfi)
+-{
+- for_fib_info() {
+- if (fi->fib_nhs != nfi->fib_nhs)
+- continue;
+- if (nfi->fib_protocol == fi->fib_protocol &&
+- nfi->fib_prefsrc == fi->fib_prefsrc &&
+- nfi->fib_priority == fi->fib_priority &&
+- memcmp(nfi->fib_metrics, fi->fib_metrics, sizeof(fi->fib_metrics)) == 0 &&
+- ((nfi->fib_flags^fi->fib_flags)&~RTNH_F_DEAD) == 0 &&
+- (nfi->fib_nhs == 0 || dn_fib_nh_comp(fi, nfi) == 0))
+- return fi;
+- } endfor_fib_info();
+- return NULL;
+-}
+-
+-static int dn_fib_count_nhs(const struct nlattr *attr)
+-{
+- struct rtnexthop *nhp = nla_data(attr);
+- int nhs = 0, nhlen = nla_len(attr);
+-
+- while (rtnh_ok(nhp, nhlen)) {
+- nhs++;
+- nhp = rtnh_next(nhp, &nhlen);
+- }
+-
+- /* leftover implies invalid nexthop configuration, discard it */
+- return nhlen > 0 ? 0 : nhs;
+-}
+-
+-static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct nlattr *attr,
+- const struct rtmsg *r)
+-{
+- struct rtnexthop *nhp = nla_data(attr);
+- int nhlen = nla_len(attr);
+-
+- change_nexthops(fi) {
+- int attrlen;
+-
+- if (!rtnh_ok(nhp, nhlen))
+- return -EINVAL;
+-
+- nh->nh_flags = (r->rtm_flags&~0xFF) | nhp->rtnh_flags;
+- nh->nh_oif = nhp->rtnh_ifindex;
+- nh->nh_weight = nhp->rtnh_hops + 1;
+-
+- attrlen = rtnh_attrlen(nhp);
+- if (attrlen > 0) {
+- struct nlattr *gw_attr;
+-
+- gw_attr = nla_find((struct nlattr *) (nhp + 1), attrlen, RTA_GATEWAY);
+- nh->nh_gw = gw_attr ? nla_get_le16(gw_attr) : 0;
+- }
+-
+- nhp = rtnh_next(nhp, &nhlen);
+- } endfor_nexthops(fi);
+-
+- return 0;
+-}
+-
+-
+-static int dn_fib_check_nh(const struct rtmsg *r, struct dn_fib_info *fi, struct dn_fib_nh *nh)
+-{
+- int err;
+-
+- if (nh->nh_gw) {
+- struct flowidn fld;
+- struct dn_fib_res res;
+-
+- if (nh->nh_flags&RTNH_F_ONLINK) {
+- struct net_device *dev;
+-
+- if (r->rtm_scope >= RT_SCOPE_LINK)
+- return -EINVAL;
+- if (dnet_addr_type(nh->nh_gw) != RTN_UNICAST)
+- return -EINVAL;
+- if ((dev = __dev_get_by_index(&init_net, nh->nh_oif)) == NULL)
+- return -ENODEV;
+- if (!(dev->flags&IFF_UP))
+- return -ENETDOWN;
+- nh->nh_dev = dev;
+- dev_hold(dev);
+- nh->nh_scope = RT_SCOPE_LINK;
+- return 0;
+- }
+-
+- memset(&fld, 0, sizeof(fld));
+- fld.daddr = nh->nh_gw;
+- fld.flowidn_oif = nh->nh_oif;
+- fld.flowidn_scope = r->rtm_scope + 1;
+-
+- if (fld.flowidn_scope < RT_SCOPE_LINK)
+- fld.flowidn_scope = RT_SCOPE_LINK;
+-
+- if ((err = dn_fib_lookup(&fld, &res)) != 0)
+- return err;
+-
+- err = -EINVAL;
+- if (res.type != RTN_UNICAST && res.type != RTN_LOCAL)
+- goto out;
+- nh->nh_scope = res.scope;
+- nh->nh_oif = DN_FIB_RES_OIF(res);
+- nh->nh_dev = DN_FIB_RES_DEV(res);
+- if (nh->nh_dev == NULL)
+- goto out;
+- dev_hold(nh->nh_dev);
+- err = -ENETDOWN;
+- if (!(nh->nh_dev->flags & IFF_UP))
+- goto out;
+- err = 0;
+-out:
+- dn_fib_res_put(&res);
+- return err;
+- } else {
+- struct net_device *dev;
+-
+- if (nh->nh_flags&(RTNH_F_PERVASIVE|RTNH_F_ONLINK))
+- return -EINVAL;
+-
+- dev = __dev_get_by_index(&init_net, nh->nh_oif);
+- if (dev == NULL || dev->dn_ptr == NULL)
+- return -ENODEV;
+- if (!(dev->flags&IFF_UP))
+- return -ENETDOWN;
+- nh->nh_dev = dev;
+- dev_hold(nh->nh_dev);
+- nh->nh_scope = RT_SCOPE_HOST;
+- }
+-
+- return 0;
+-}
+-
+-
+-struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct nlattr *attrs[],
+- const struct nlmsghdr *nlh, int *errp)
+-{
+- int err;
+- struct dn_fib_info *fi = NULL;
+- struct dn_fib_info *ofi;
+- int nhs = 1;
+-
+- if (r->rtm_type > RTN_MAX)
+- goto err_inval;
+-
+- if (dn_fib_props[r->rtm_type].scope > r->rtm_scope)
+- goto err_inval;
+-
+- if (attrs[RTA_MULTIPATH] &&
+- (nhs = dn_fib_count_nhs(attrs[RTA_MULTIPATH])) == 0)
+- goto err_inval;
+-
+- fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct dn_fib_nh), GFP_KERNEL);
+- err = -ENOBUFS;
+- if (fi == NULL)
+- goto failure;
+-
+- fi->fib_protocol = r->rtm_protocol;
+- fi->fib_nhs = nhs;
+- fi->fib_flags = r->rtm_flags;
+-
+- if (attrs[RTA_PRIORITY])
+- fi->fib_priority = nla_get_u32(attrs[RTA_PRIORITY]);
+-
+- if (attrs[RTA_METRICS]) {
+- struct nlattr *attr;
+- int rem;
+-
+- nla_for_each_nested(attr, attrs[RTA_METRICS], rem) {
+- int type = nla_type(attr);
+-
+- if (type) {
+- if (type > RTAX_MAX || type == RTAX_CC_ALGO ||
+- nla_len(attr) < 4)
+- goto err_inval;
+-
+- fi->fib_metrics[type-1] = nla_get_u32(attr);
+- }
+- }
+- }
+-
+- if (attrs[RTA_PREFSRC])
+- fi->fib_prefsrc = nla_get_le16(attrs[RTA_PREFSRC]);
+-
+- if (attrs[RTA_MULTIPATH]) {
+- if ((err = dn_fib_get_nhs(fi, attrs[RTA_MULTIPATH], r)) != 0)
+- goto failure;
+-
+- if (attrs[RTA_OIF] &&
+- fi->fib_nh->nh_oif != nla_get_u32(attrs[RTA_OIF]))
+- goto err_inval;
+-
+- if (attrs[RTA_GATEWAY] &&
+- fi->fib_nh->nh_gw != nla_get_le16(attrs[RTA_GATEWAY]))
+- goto err_inval;
+- } else {
+- struct dn_fib_nh *nh = fi->fib_nh;
+-
+- if (attrs[RTA_OIF])
+- nh->nh_oif = nla_get_u32(attrs[RTA_OIF]);
+-
+- if (attrs[RTA_GATEWAY])
+- nh->nh_gw = nla_get_le16(attrs[RTA_GATEWAY]);
+-
+- nh->nh_flags = r->rtm_flags;
+- nh->nh_weight = 1;
+- }
+-
+- if (r->rtm_type == RTN_NAT) {
+- if (!attrs[RTA_GATEWAY] || nhs != 1 || attrs[RTA_OIF])
+- goto err_inval;
+-
+- fi->fib_nh->nh_gw = nla_get_le16(attrs[RTA_GATEWAY]);
+- goto link_it;
+- }
+-
+- if (dn_fib_props[r->rtm_type].error) {
+- if (attrs[RTA_GATEWAY] || attrs[RTA_OIF] || attrs[RTA_MULTIPATH])
+- goto err_inval;
+-
+- goto link_it;
+- }
+-
+- if (r->rtm_scope > RT_SCOPE_HOST)
+- goto err_inval;
+-
+- if (r->rtm_scope == RT_SCOPE_HOST) {
+- struct dn_fib_nh *nh = fi->fib_nh;
+-
+- /* Local address is added */
+- if (nhs != 1 || nh->nh_gw)
+- goto err_inval;
+- nh->nh_scope = RT_SCOPE_NOWHERE;
+- nh->nh_dev = dev_get_by_index(&init_net, fi->fib_nh->nh_oif);
+- err = -ENODEV;
+- if (nh->nh_dev == NULL)
+- goto failure;
+- } else {
+- change_nexthops(fi) {
+- if ((err = dn_fib_check_nh(r, fi, nh)) != 0)
+- goto failure;
+- } endfor_nexthops(fi)
+- }
+-
+- if (fi->fib_prefsrc) {
+- if (r->rtm_type != RTN_LOCAL || !attrs[RTA_DST] ||
+- fi->fib_prefsrc != nla_get_le16(attrs[RTA_DST]))
+- if (dnet_addr_type(fi->fib_prefsrc) != RTN_LOCAL)
+- goto err_inval;
+- }
+-
+-link_it:
+- if ((ofi = dn_fib_find_info(fi)) != NULL) {
+- fi->fib_dead = 1;
+- dn_fib_free_info(fi);
+- ofi->fib_treeref++;
+- return ofi;
+- }
+-
+- fi->fib_treeref++;
+- refcount_set(&fi->fib_clntref, 1);
+- spin_lock(&dn_fib_info_lock);
+- fi->fib_next = dn_fib_info_list;
+- fi->fib_prev = NULL;
+- if (dn_fib_info_list)
+- dn_fib_info_list->fib_prev = fi;
+- dn_fib_info_list = fi;
+- spin_unlock(&dn_fib_info_lock);
+- return fi;
+-
+-err_inval:
+- err = -EINVAL;
+-
+-failure:
+- *errp = err;
+- if (fi) {
+- fi->fib_dead = 1;
+- dn_fib_free_info(fi);
+- }
+-
+- return NULL;
+-}
+-
+-int dn_fib_semantic_match(int type, struct dn_fib_info *fi, const struct flowidn *fld, struct dn_fib_res *res)
+-{
+- int err = dn_fib_props[type].error;
+-
+- if (err == 0) {
+- if (fi->fib_flags & RTNH_F_DEAD)
+- return 1;
+-
+- res->fi = fi;
+-
+- switch (type) {
+- case RTN_NAT:
+- DN_FIB_RES_RESET(*res);
+- refcount_inc(&fi->fib_clntref);
+- return 0;
+- case RTN_UNICAST:
+- case RTN_LOCAL:
+- for_nexthops(fi) {
+- if (nh->nh_flags & RTNH_F_DEAD)
+- continue;
+- if (!fld->flowidn_oif ||
+- fld->flowidn_oif == nh->nh_oif)
+- break;
+- }
+- if (nhsel < fi->fib_nhs) {
+- res->nh_sel = nhsel;
+- refcount_inc(&fi->fib_clntref);
+- return 0;
+- }
+- endfor_nexthops(fi);
+- res->fi = NULL;
+- return 1;
+- default:
+- net_err_ratelimited("DECnet: impossible routing event : dn_fib_semantic_match type=%d\n",
+- type);
+- res->fi = NULL;
+- return -EINVAL;
+- }
+- }
+- return err;
+-}
+-
+-void dn_fib_select_multipath(const struct flowidn *fld, struct dn_fib_res *res)
+-{
+- struct dn_fib_info *fi = res->fi;
+- int w;
+-
+- spin_lock_bh(&dn_fib_multipath_lock);
+- if (fi->fib_power <= 0) {
+- int power = 0;
+- change_nexthops(fi) {
+- if (!(nh->nh_flags&RTNH_F_DEAD)) {
+- power += nh->nh_weight;
+- nh->nh_power = nh->nh_weight;
+- }
+- } endfor_nexthops(fi);
+- fi->fib_power = power;
+- if (power < 0) {
+- spin_unlock_bh(&dn_fib_multipath_lock);
+- res->nh_sel = 0;
+- return;
+- }
+- }
+-
+- w = jiffies % fi->fib_power;
+-
+- change_nexthops(fi) {
+- if (!(nh->nh_flags&RTNH_F_DEAD) && nh->nh_power) {
+- if ((w -= nh->nh_power) <= 0) {
+- nh->nh_power--;
+- fi->fib_power--;
+- res->nh_sel = nhsel;
+- spin_unlock_bh(&dn_fib_multipath_lock);
+- return;
+- }
+- }
+- } endfor_nexthops(fi);
+- res->nh_sel = 0;
+- spin_unlock_bh(&dn_fib_multipath_lock);
+-}
+-
+-static inline u32 rtm_get_table(struct nlattr *attrs[], u8 table)
+-{
+- if (attrs[RTA_TABLE])
+- table = nla_get_u32(attrs[RTA_TABLE]);
+-
+- return table;
+-}
+-
+-static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
+- struct netlink_ext_ack *extack)
+-{
+- struct net *net = sock_net(skb->sk);
+- struct dn_fib_table *tb;
+- struct rtmsg *r = nlmsg_data(nlh);
+- struct nlattr *attrs[RTA_MAX+1];
+- int err;
+-
+- if (!netlink_capable(skb, CAP_NET_ADMIN))
+- return -EPERM;
+-
+- if (!net_eq(net, &init_net))
+- return -EINVAL;
+-
+- err = nlmsg_parse(nlh, sizeof(*r), attrs, RTA_MAX, rtm_dn_policy,
+- extack);
+- if (err < 0)
+- return err;
+-
+- tb = dn_fib_get_table(rtm_get_table(attrs, r->rtm_table), 0);
+- if (!tb)
+- return -ESRCH;
+-
+- return tb->delete(tb, r, attrs, nlh, &NETLINK_CB(skb));
+-}
+-
+-static int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
+- struct netlink_ext_ack *extack)
+-{
+- struct net *net = sock_net(skb->sk);
+- struct dn_fib_table *tb;
+- struct rtmsg *r = nlmsg_data(nlh);
+- struct nlattr *attrs[RTA_MAX+1];
+- int err;
+-
+- if (!netlink_capable(skb, CAP_NET_ADMIN))
+- return -EPERM;
+-
+- if (!net_eq(net, &init_net))
+- return -EINVAL;
+-
+- err = nlmsg_parse(nlh, sizeof(*r), attrs, RTA_MAX, rtm_dn_policy,
+- extack);
+- if (err < 0)
+- return err;
+-
+- tb = dn_fib_get_table(rtm_get_table(attrs, r->rtm_table), 1);
+- if (!tb)
+- return -ENOBUFS;
+-
+- return tb->insert(tb, r, attrs, nlh, &NETLINK_CB(skb));
+-}
+-
+-static void fib_magic(int cmd, int type, __le16 dst, int dst_len, struct dn_ifaddr *ifa)
+-{
+- struct dn_fib_table *tb;
+- struct {
+- struct nlmsghdr nlh;
+- struct rtmsg rtm;
+- } req;
+- struct {
+- struct nlattr hdr;
+- __le16 dst;
+- } dst_attr = {
+- .dst = dst,
+- };
+- struct {
+- struct nlattr hdr;
+- __le16 prefsrc;
+- } prefsrc_attr = {
+- .prefsrc = ifa->ifa_local,
+- };
+- struct {
+- struct nlattr hdr;
+- u32 oif;
+- } oif_attr = {
+- .oif = ifa->ifa_dev->dev->ifindex,
+- };
+- struct nlattr *attrs[RTA_MAX+1] = {
+- [RTA_DST] = (struct nlattr *) &dst_attr,
+- [RTA_PREFSRC] = (struct nlattr * ) &prefsrc_attr,
+- [RTA_OIF] = (struct nlattr *) &oif_attr,
+- };
+-
+- memset(&req.rtm, 0, sizeof(req.rtm));
+-
+- if (type == RTN_UNICAST)
+- tb = dn_fib_get_table(RT_MIN_TABLE, 1);
+- else
+- tb = dn_fib_get_table(RT_TABLE_LOCAL, 1);
+-
+- if (tb == NULL)
+- return;
+-
+- req.nlh.nlmsg_len = sizeof(req);
+- req.nlh.nlmsg_type = cmd;
+- req.nlh.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_APPEND;
+- req.nlh.nlmsg_pid = 0;
+- req.nlh.nlmsg_seq = 0;
+-
+- req.rtm.rtm_dst_len = dst_len;
+- req.rtm.rtm_table = tb->n;
+- req.rtm.rtm_protocol = RTPROT_KERNEL;
+- req.rtm.rtm_scope = (type != RTN_LOCAL ? RT_SCOPE_LINK : RT_SCOPE_HOST);
+- req.rtm.rtm_type = type;
+-
+- if (cmd == RTM_NEWROUTE)
+- tb->insert(tb, &req.rtm, attrs, &req.nlh, NULL);
+- else
+- tb->delete(tb, &req.rtm, attrs, &req.nlh, NULL);
+-}
+-
+-static void dn_fib_add_ifaddr(struct dn_ifaddr *ifa)
+-{
+-
+- fib_magic(RTM_NEWROUTE, RTN_LOCAL, ifa->ifa_local, 16, ifa);
+-
+-#if 0
+- if (!(dev->flags&IFF_UP))
+- return;
+- /* In the future, we will want to add default routes here */
+-
+-#endif
+-}
+-
+-static void dn_fib_del_ifaddr(struct dn_ifaddr *ifa)
+-{
+- int found_it = 0;
+- struct net_device *dev;
+- struct dn_dev *dn_db;
+- struct dn_ifaddr *ifa2;
+-
+- ASSERT_RTNL();
+-
+- /* Scan device list */
+- rcu_read_lock();
+- for_each_netdev_rcu(&init_net, dev) {
+- dn_db = rcu_dereference(dev->dn_ptr);
+- if (dn_db == NULL)
+- continue;
+- for (ifa2 = rcu_dereference(dn_db->ifa_list);
+- ifa2 != NULL;
+- ifa2 = rcu_dereference(ifa2->ifa_next)) {
+- if (ifa2->ifa_local == ifa->ifa_local) {
+- found_it = 1;
+- break;
+- }
+- }
+- }
+- rcu_read_unlock();
+-
+- if (found_it == 0) {
+- fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 16, ifa);
+-
+- if (dnet_addr_type(ifa->ifa_local) != RTN_LOCAL) {
+- if (dn_fib_sync_down(ifa->ifa_local, NULL, 0))
+- dn_fib_flush();
+- }
+- }
+-}
+-
+-static void dn_fib_disable_addr(struct net_device *dev, int force)
+-{
+- if (dn_fib_sync_down(0, dev, force))
+- dn_fib_flush();
+- dn_rt_cache_flush(0);
+- neigh_ifdown(&dn_neigh_table, dev);
+-}
+-
+-static int dn_fib_dnaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
+-{
+- struct dn_ifaddr *ifa = (struct dn_ifaddr *)ptr;
+-
+- switch (event) {
+- case NETDEV_UP:
+- dn_fib_add_ifaddr(ifa);
+- dn_fib_sync_up(ifa->ifa_dev->dev);
+- dn_rt_cache_flush(-1);
+- break;
+- case NETDEV_DOWN:
+- dn_fib_del_ifaddr(ifa);
+- if (ifa->ifa_dev && ifa->ifa_dev->ifa_list == NULL) {
+- dn_fib_disable_addr(ifa->ifa_dev->dev, 1);
+- } else {
+- dn_rt_cache_flush(-1);
+- }
+- break;
+- }
+- return NOTIFY_DONE;
+-}
+-
+-static int dn_fib_sync_down(__le16 local, struct net_device *dev, int force)
+-{
+- int ret = 0;
+- int scope = RT_SCOPE_NOWHERE;
+-
+- if (force)
+- scope = -1;
+-
+- for_fib_info() {
+- /*
+- * This makes no sense for DECnet.... we will almost
+- * certainly have more than one local address the same
+- * over all our interfaces. It needs thinking about
+- * some more.
+- */
+- if (local && fi->fib_prefsrc == local) {
+- fi->fib_flags |= RTNH_F_DEAD;
+- ret++;
+- } else if (dev && fi->fib_nhs) {
+- int dead = 0;
+-
+- change_nexthops(fi) {
+- if (nh->nh_flags&RTNH_F_DEAD)
+- dead++;
+- else if (nh->nh_dev == dev &&
+- nh->nh_scope != scope) {
+- spin_lock_bh(&dn_fib_multipath_lock);
+- nh->nh_flags |= RTNH_F_DEAD;
+- fi->fib_power -= nh->nh_power;
+- nh->nh_power = 0;
+- spin_unlock_bh(&dn_fib_multipath_lock);
+- dead++;
+- }
+- } endfor_nexthops(fi)
+- if (dead == fi->fib_nhs) {
+- fi->fib_flags |= RTNH_F_DEAD;
+- ret++;
+- }
+- }
+- } endfor_fib_info();
+- return ret;
+-}
+-
+-
+-static int dn_fib_sync_up(struct net_device *dev)
+-{
+- int ret = 0;
+-
+- if (!(dev->flags&IFF_UP))
+- return 0;
+-
+- for_fib_info() {
+- int alive = 0;
+-
+- change_nexthops(fi) {
+- if (!(nh->nh_flags&RTNH_F_DEAD)) {
+- alive++;
+- continue;
+- }
+- if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP))
+- continue;
+- if (nh->nh_dev != dev || dev->dn_ptr == NULL)
+- continue;
+- alive++;
+- spin_lock_bh(&dn_fib_multipath_lock);
+- nh->nh_power = 0;
+- nh->nh_flags &= ~RTNH_F_DEAD;
+- spin_unlock_bh(&dn_fib_multipath_lock);
+- } endfor_nexthops(fi);
+-
+- if (alive > 0) {
+- fi->fib_flags &= ~RTNH_F_DEAD;
+- ret++;
+- }
+- } endfor_fib_info();
+- return ret;
+-}
+-
+-static struct notifier_block dn_fib_dnaddr_notifier = {
+- .notifier_call = dn_fib_dnaddr_event,
+-};
+-
+-void __exit dn_fib_cleanup(void)
+-{
+- dn_fib_table_cleanup();
+- dn_fib_rules_cleanup();
+-
+- unregister_dnaddr_notifier(&dn_fib_dnaddr_notifier);
+-}
+-
+-
+-void __init dn_fib_init(void)
+-{
+- dn_fib_table_init();
+- dn_fib_rules_init();
+-
+- register_dnaddr_notifier(&dn_fib_dnaddr_notifier);
+-
+- rtnl_register(PF_DECnet, RTM_NEWROUTE, dn_fib_rtm_newroute, NULL, 0);
+- rtnl_register(PF_DECnet, RTM_DELROUTE, dn_fib_rtm_delroute, NULL, 0);
+-}
+-
+-
+diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c
+deleted file mode 100644
+index 528119a5618e4..0000000000000
+--- a/net/decnet/dn_neigh.c
++++ /dev/null
+@@ -1,620 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/*
+- * DECnet An implementation of the DECnet protocol suite for the LINUX
+- * operating system. DECnet is implemented using the BSD Socket
+- * interface as the means of communication with the user level.
+- *
+- * DECnet Neighbour Functions (Adjacency Database and
+- * On-Ethernet Cache)
+- *
+- * Author: Steve Whitehouse <SteveW@ACM.org>
+- *
+- *
+- * Changes:
+- * Steve Whitehouse : Fixed router listing routine
+- * Steve Whitehouse : Added error_report functions
+- * Steve Whitehouse : Added default router detection
+- * Steve Whitehouse : Hop counts in outgoing messages
+- * Steve Whitehouse : Fixed src/dst in outgoing messages so
+- * forwarding now stands a good chance of
+- * working.
+- * Steve Whitehouse : Fixed neighbour states (for now anyway).
+- * Steve Whitehouse : Made error_report functions dummies. This
+- * is not the right place to return skbs.
+- * Steve Whitehouse : Convert to seq_file
+- *
+- */
+-
+-#include <linux/net.h>
+-#include <linux/module.h>
+-#include <linux/socket.h>
+-#include <linux/if_arp.h>
+-#include <linux/slab.h>
+-#include <linux/if_ether.h>
+-#include <linux/init.h>
+-#include <linux/proc_fs.h>
+-#include <linux/string.h>
+-#include <linux/netfilter_decnet.h>
+-#include <linux/spinlock.h>
+-#include <linux/seq_file.h>
+-#include <linux/rcupdate.h>
+-#include <linux/jhash.h>
+-#include <linux/atomic.h>
+-#include <net/net_namespace.h>
+-#include <net/neighbour.h>
+-#include <net/dst.h>
+-#include <net/flow.h>
+-#include <net/dn.h>
+-#include <net/dn_dev.h>
+-#include <net/dn_neigh.h>
+-#include <net/dn_route.h>
+-
+-static int dn_neigh_construct(struct neighbour *);
+-static void dn_neigh_error_report(struct neighbour *, struct sk_buff *);
+-static int dn_neigh_output(struct neighbour *neigh, struct sk_buff *skb);
+-
+-/*
+- * Operations for adding the link layer header.
+- */
+-static const struct neigh_ops dn_neigh_ops = {
+- .family = AF_DECnet,
+- .error_report = dn_neigh_error_report,
+- .output = dn_neigh_output,
+- .connected_output = dn_neigh_output,
+-};
+-
+-static u32 dn_neigh_hash(const void *pkey,
+- const struct net_device *dev,
+- __u32 *hash_rnd)
+-{
+- return jhash_2words(*(__u16 *)pkey, 0, hash_rnd[0]);
+-}
+-
+-static bool dn_key_eq(const struct neighbour *neigh, const void *pkey)
+-{
+- return neigh_key_eq16(neigh, pkey);
+-}
+-
+-struct neigh_table dn_neigh_table = {
+- .family = PF_DECnet,
+- .entry_size = NEIGH_ENTRY_SIZE(sizeof(struct dn_neigh)),
+- .key_len = sizeof(__le16),
+- .protocol = cpu_to_be16(ETH_P_DNA_RT),
+- .hash = dn_neigh_hash,
+- .key_eq = dn_key_eq,
+- .constructor = dn_neigh_construct,
+- .id = "dn_neigh_cache",
+- .parms ={
+- .tbl = &dn_neigh_table,
+- .reachable_time = 30 * HZ,
+- .data = {
+- [NEIGH_VAR_MCAST_PROBES] = 0,
+- [NEIGH_VAR_UCAST_PROBES] = 0,
+- [NEIGH_VAR_APP_PROBES] = 0,
+- [NEIGH_VAR_RETRANS_TIME] = 1 * HZ,
+- [NEIGH_VAR_BASE_REACHABLE_TIME] = 30 * HZ,
+- [NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ,
+- [NEIGH_VAR_GC_STALETIME] = 60 * HZ,
+- [NEIGH_VAR_QUEUE_LEN_BYTES] = SK_WMEM_MAX,
+- [NEIGH_VAR_PROXY_QLEN] = 0,
+- [NEIGH_VAR_ANYCAST_DELAY] = 0,
+- [NEIGH_VAR_PROXY_DELAY] = 0,
+- [NEIGH_VAR_LOCKTIME] = 1 * HZ,
+- },
+- },
+- .gc_interval = 30 * HZ,
+- .gc_thresh1 = 128,
+- .gc_thresh2 = 512,
+- .gc_thresh3 = 1024,
+-};
+-
+-static int dn_neigh_construct(struct neighbour *neigh)
+-{
+- struct net_device *dev = neigh->dev;
+- struct dn_neigh *dn = container_of(neigh, struct dn_neigh, n);
+- struct dn_dev *dn_db;
+- struct neigh_parms *parms;
+-
+- rcu_read_lock();
+- dn_db = rcu_dereference(dev->dn_ptr);
+- if (dn_db == NULL) {
+- rcu_read_unlock();
+- return -EINVAL;
+- }
+-
+- parms = dn_db->neigh_parms;
+- if (!parms) {
+- rcu_read_unlock();
+- return -EINVAL;
+- }
+-
+- __neigh_parms_put(neigh->parms);
+- neigh->parms = neigh_parms_clone(parms);
+- rcu_read_unlock();
+-
+- neigh->ops = &dn_neigh_ops;
+- neigh->nud_state = NUD_NOARP;
+- neigh->output = neigh->ops->connected_output;
+-
+- if ((dev->type == ARPHRD_IPGRE) || (dev->flags & IFF_POINTOPOINT))
+- memcpy(neigh->ha, dev->broadcast, dev->addr_len);
+- else if ((dev->type == ARPHRD_ETHER) || (dev->type == ARPHRD_LOOPBACK))
+- dn_dn2eth(neigh->ha, dn->addr);
+- else {
+- net_dbg_ratelimited("Trying to create neigh for hw %d\n",
+- dev->type);
+- return -EINVAL;
+- }
+-
+- /*
+- * Make an estimate of the remote block size by assuming that its
+- * two less then the device mtu, which it true for ethernet (and
+- * other things which support long format headers) since there is
+- * an extra length field (of 16 bits) which isn't part of the
+- * ethernet headers and which the DECnet specs won't admit is part
+- * of the DECnet routing headers either.
+- *
+- * If we over estimate here its no big deal, the NSP negotiations
+- * will prevent us from sending packets which are too large for the
+- * remote node to handle. In any case this figure is normally updated
+- * by a hello message in most cases.
+- */
+- dn->blksize = dev->mtu - 2;
+-
+- return 0;
+-}
+-
+-static void dn_neigh_error_report(struct neighbour *neigh, struct sk_buff *skb)
+-{
+- printk(KERN_DEBUG "dn_neigh_error_report: called\n");
+- kfree_skb(skb);
+-}
+-
+-static int dn_neigh_output(struct neighbour *neigh, struct sk_buff *skb)
+-{
+- struct dst_entry *dst = skb_dst(skb);
+- struct dn_route *rt = (struct dn_route *)dst;
+- struct net_device *dev = neigh->dev;
+- char mac_addr[ETH_ALEN];
+- unsigned int seq;
+- int err;
+-
+- dn_dn2eth(mac_addr, rt->rt_local_src);
+- do {
+- seq = read_seqbegin(&neigh->ha_lock);
+- err = dev_hard_header(skb, dev, ntohs(skb->protocol),
+- neigh->ha, mac_addr, skb->len);
+- } while (read_seqretry(&neigh->ha_lock, seq));
+-
+- if (err >= 0)
+- err = dev_queue_xmit(skb);
+- else {
+- kfree_skb(skb);
+- err = -EINVAL;
+- }
+- return err;
+-}
+-
+-static int dn_neigh_output_packet(struct net *net, struct sock *sk, struct sk_buff *skb)
+-{
+- struct dst_entry *dst = skb_dst(skb);
+- struct dn_route *rt = (struct dn_route *)dst;
+- struct neighbour *neigh = rt->n;
+-
+- return neigh->output(neigh, skb);
+-}
+-
+-/*
+- * For talking to broadcast devices: Ethernet & PPP
+- */
+-static int dn_long_output(struct neighbour *neigh, struct sock *sk,
+- struct sk_buff *skb)
+-{
+- struct net_device *dev = neigh->dev;
+- int headroom = dev->hard_header_len + sizeof(struct dn_long_packet) + 3;
+- unsigned char *data;
+- struct dn_long_packet *lp;
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+-
+-
+- if (skb_headroom(skb) < headroom) {
+- struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom);
+- if (skb2 == NULL) {
+- net_crit_ratelimited("dn_long_output: no memory\n");
+- kfree_skb(skb);
+- return -ENOBUFS;
+- }
+- consume_skb(skb);
+- skb = skb2;
+- net_info_ratelimited("dn_long_output: Increasing headroom\n");
+- }
+-
+- data = skb_push(skb, sizeof(struct dn_long_packet) + 3);
+- lp = (struct dn_long_packet *)(data+3);
+-
+- *((__le16 *)data) = cpu_to_le16(skb->len - 2);
+- *(data + 2) = 1 | DN_RT_F_PF; /* Padding */
+-
+- lp->msgflg = DN_RT_PKT_LONG|(cb->rt_flags&(DN_RT_F_IE|DN_RT_F_RQR|DN_RT_F_RTS));
+- lp->d_area = lp->d_subarea = 0;
+- dn_dn2eth(lp->d_id, cb->dst);
+- lp->s_area = lp->s_subarea = 0;
+- dn_dn2eth(lp->s_id, cb->src);
+- lp->nl2 = 0;
+- lp->visit_ct = cb->hops & 0x3f;
+- lp->s_class = 0;
+- lp->pt = 0;
+-
+- skb_reset_network_header(skb);
+-
+- return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING,
+- &init_net, sk, skb, NULL, neigh->dev,
+- dn_neigh_output_packet);
+-}
+-
+-/*
+- * For talking to pointopoint and multidrop devices: DDCMP and X.25
+- */
+-static int dn_short_output(struct neighbour *neigh, struct sock *sk,
+- struct sk_buff *skb)
+-{
+- struct net_device *dev = neigh->dev;
+- int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2;
+- struct dn_short_packet *sp;
+- unsigned char *data;
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+-
+-
+- if (skb_headroom(skb) < headroom) {
+- struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom);
+- if (skb2 == NULL) {
+- net_crit_ratelimited("dn_short_output: no memory\n");
+- kfree_skb(skb);
+- return -ENOBUFS;
+- }
+- consume_skb(skb);
+- skb = skb2;
+- net_info_ratelimited("dn_short_output: Increasing headroom\n");
+- }
+-
+- data = skb_push(skb, sizeof(struct dn_short_packet) + 2);
+- *((__le16 *)data) = cpu_to_le16(skb->len - 2);
+- sp = (struct dn_short_packet *)(data+2);
+-
+- sp->msgflg = DN_RT_PKT_SHORT|(cb->rt_flags&(DN_RT_F_RQR|DN_RT_F_RTS));
+- sp->dstnode = cb->dst;
+- sp->srcnode = cb->src;
+- sp->forward = cb->hops & 0x3f;
+-
+- skb_reset_network_header(skb);
+-
+- return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING,
+- &init_net, sk, skb, NULL, neigh->dev,
+- dn_neigh_output_packet);
+-}
+-
+-/*
+- * For talking to DECnet phase III nodes
+- * Phase 3 output is the same as short output, execpt that
+- * it clears the area bits before transmission.
+- */
+-static int dn_phase3_output(struct neighbour *neigh, struct sock *sk,
+- struct sk_buff *skb)
+-{
+- struct net_device *dev = neigh->dev;
+- int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2;
+- struct dn_short_packet *sp;
+- unsigned char *data;
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+-
+- if (skb_headroom(skb) < headroom) {
+- struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom);
+- if (skb2 == NULL) {
+- net_crit_ratelimited("dn_phase3_output: no memory\n");
+- kfree_skb(skb);
+- return -ENOBUFS;
+- }
+- consume_skb(skb);
+- skb = skb2;
+- net_info_ratelimited("dn_phase3_output: Increasing headroom\n");
+- }
+-
+- data = skb_push(skb, sizeof(struct dn_short_packet) + 2);
+- *((__le16 *)data) = cpu_to_le16(skb->len - 2);
+- sp = (struct dn_short_packet *)(data + 2);
+-
+- sp->msgflg = DN_RT_PKT_SHORT|(cb->rt_flags&(DN_RT_F_RQR|DN_RT_F_RTS));
+- sp->dstnode = cb->dst & cpu_to_le16(0x03ff);
+- sp->srcnode = cb->src & cpu_to_le16(0x03ff);
+- sp->forward = cb->hops & 0x3f;
+-
+- skb_reset_network_header(skb);
+-
+- return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING,
+- &init_net, sk, skb, NULL, neigh->dev,
+- dn_neigh_output_packet);
+-}
+-
+-int dn_to_neigh_output(struct net *net, struct sock *sk, struct sk_buff *skb)
+-{
+- struct dst_entry *dst = skb_dst(skb);
+- struct dn_route *rt = (struct dn_route *) dst;
+- struct neighbour *neigh = rt->n;
+- struct dn_neigh *dn = container_of(neigh, struct dn_neigh, n);
+- struct dn_dev *dn_db;
+- bool use_long;
+-
+- rcu_read_lock();
+- dn_db = rcu_dereference(neigh->dev->dn_ptr);
+- if (dn_db == NULL) {
+- rcu_read_unlock();
+- return -EINVAL;
+- }
+- use_long = dn_db->use_long;
+- rcu_read_unlock();
+-
+- if (dn->flags & DN_NDFLAG_P3)
+- return dn_phase3_output(neigh, sk, skb);
+- if (use_long)
+- return dn_long_output(neigh, sk, skb);
+- else
+- return dn_short_output(neigh, sk, skb);
+-}
+-
+-/*
+- * Unfortunately, the neighbour code uses the device in its hash
+- * function, so we don't get any advantage from it. This function
+- * basically does a neigh_lookup(), but without comparing the device
+- * field. This is required for the On-Ethernet cache
+- */
+-
+-/*
+- * Pointopoint link receives a hello message
+- */
+-void dn_neigh_pointopoint_hello(struct sk_buff *skb)
+-{
+- kfree_skb(skb);
+-}
+-
+-/*
+- * Ethernet router hello message received
+- */
+-int dn_neigh_router_hello(struct net *net, struct sock *sk, struct sk_buff *skb)
+-{
+- struct rtnode_hello_message *msg = (struct rtnode_hello_message *)skb->data;
+-
+- struct neighbour *neigh;
+- struct dn_neigh *dn;
+- struct dn_dev *dn_db;
+- __le16 src;
+-
+- src = dn_eth2dn(msg->id);
+-
+- neigh = __neigh_lookup(&dn_neigh_table, &src, skb->dev, 1);
+-
+- dn = container_of(neigh, struct dn_neigh, n);
+-
+- if (neigh) {
+- write_lock(&neigh->lock);
+-
+- neigh->used = jiffies;
+- dn_db = rcu_dereference(neigh->dev->dn_ptr);
+-
+- if (!(neigh->nud_state & NUD_PERMANENT)) {
+- neigh->updated = jiffies;
+-
+- if (neigh->dev->type == ARPHRD_ETHER)
+- memcpy(neigh->ha, &eth_hdr(skb)->h_source, ETH_ALEN);
+-
+- dn->blksize = le16_to_cpu(msg->blksize);
+- dn->priority = msg->priority;
+-
+- dn->flags &= ~DN_NDFLAG_P3;
+-
+- switch (msg->iinfo & DN_RT_INFO_TYPE) {
+- case DN_RT_INFO_L1RT:
+- dn->flags &=~DN_NDFLAG_R2;
+- dn->flags |= DN_NDFLAG_R1;
+- break;
+- case DN_RT_INFO_L2RT:
+- dn->flags |= DN_NDFLAG_R2;
+- }
+- }
+-
+- /* Only use routers in our area */
+- if ((le16_to_cpu(src)>>10) == (le16_to_cpu((decnet_address))>>10)) {
+- if (!dn_db->router) {
+- dn_db->router = neigh_clone(neigh);
+- } else {
+- if (msg->priority > ((struct dn_neigh *)dn_db->router)->priority)
+- neigh_release(xchg(&dn_db->router, neigh_clone(neigh)));
+- }
+- }
+- write_unlock(&neigh->lock);
+- neigh_release(neigh);
+- }
+-
+- kfree_skb(skb);
+- return 0;
+-}
+-
+-/*
+- * Endnode hello message received
+- */
+-int dn_neigh_endnode_hello(struct net *net, struct sock *sk, struct sk_buff *skb)
+-{
+- struct endnode_hello_message *msg = (struct endnode_hello_message *)skb->data;
+- struct neighbour *neigh;
+- struct dn_neigh *dn;
+- __le16 src;
+-
+- src = dn_eth2dn(msg->id);
+-
+- neigh = __neigh_lookup(&dn_neigh_table, &src, skb->dev, 1);
+-
+- dn = container_of(neigh, struct dn_neigh, n);
+-
+- if (neigh) {
+- write_lock(&neigh->lock);
+-
+- neigh->used = jiffies;
+-
+- if (!(neigh->nud_state & NUD_PERMANENT)) {
+- neigh->updated = jiffies;
+-
+- if (neigh->dev->type == ARPHRD_ETHER)
+- memcpy(neigh->ha, &eth_hdr(skb)->h_source, ETH_ALEN);
+- dn->flags &= ~(DN_NDFLAG_R1 | DN_NDFLAG_R2);
+- dn->blksize = le16_to_cpu(msg->blksize);
+- dn->priority = 0;
+- }
+-
+- write_unlock(&neigh->lock);
+- neigh_release(neigh);
+- }
+-
+- kfree_skb(skb);
+- return 0;
+-}
+-
+-static char *dn_find_slot(char *base, int max, int priority)
+-{
+- int i;
+- unsigned char *min = NULL;
+-
+- base += 6; /* skip first id */
+-
+- for(i = 0; i < max; i++) {
+- if (!min || (*base < *min))
+- min = base;
+- base += 7; /* find next priority */
+- }
+-
+- if (!min)
+- return NULL;
+-
+- return (*min < priority) ? (min - 6) : NULL;
+-}
+-
+-struct elist_cb_state {
+- struct net_device *dev;
+- unsigned char *ptr;
+- unsigned char *rs;
+- int t, n;
+-};
+-
+-static void neigh_elist_cb(struct neighbour *neigh, void *_info)
+-{
+- struct elist_cb_state *s = _info;
+- struct dn_neigh *dn;
+-
+- if (neigh->dev != s->dev)
+- return;
+-
+- dn = container_of(neigh, struct dn_neigh, n);
+- if (!(dn->flags & (DN_NDFLAG_R1|DN_NDFLAG_R2)))
+- return;
+-
+- if (s->t == s->n)
+- s->rs = dn_find_slot(s->ptr, s->n, dn->priority);
+- else
+- s->t++;
+- if (s->rs == NULL)
+- return;
+-
+- dn_dn2eth(s->rs, dn->addr);
+- s->rs += 6;
+- *(s->rs) = neigh->nud_state & NUD_CONNECTED ? 0x80 : 0x0;
+- *(s->rs) |= dn->priority;
+- s->rs++;
+-}
+-
+-int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n)
+-{
+- struct elist_cb_state state;
+-
+- state.dev = dev;
+- state.t = 0;
+- state.n = n;
+- state.ptr = ptr;
+- state.rs = ptr;
+-
+- neigh_for_each(&dn_neigh_table, neigh_elist_cb, &state);
+-
+- return state.t;
+-}
+-
+-
+-#ifdef CONFIG_PROC_FS
+-
+-static inline void dn_neigh_format_entry(struct seq_file *seq,
+- struct neighbour *n)
+-{
+- struct dn_neigh *dn = container_of(n, struct dn_neigh, n);
+- char buf[DN_ASCBUF_LEN];
+-
+- read_lock(&n->lock);
+- seq_printf(seq, "%-7s %s%s%s %02x %02d %07ld %-8s\n",
+- dn_addr2asc(le16_to_cpu(dn->addr), buf),
+- (dn->flags&DN_NDFLAG_R1) ? "1" : "-",
+- (dn->flags&DN_NDFLAG_R2) ? "2" : "-",
+- (dn->flags&DN_NDFLAG_P3) ? "3" : "-",
+- dn->n.nud_state,
+- refcount_read(&dn->n.refcnt),
+- dn->blksize,
+- (dn->n.dev) ? dn->n.dev->name : "?");
+- read_unlock(&n->lock);
+-}
+-
+-static int dn_neigh_seq_show(struct seq_file *seq, void *v)
+-{
+- if (v == SEQ_START_TOKEN) {
+- seq_puts(seq, "Addr Flags State Use Blksize Dev\n");
+- } else {
+- dn_neigh_format_entry(seq, v);
+- }
+-
+- return 0;
+-}
+-
+-static void *dn_neigh_seq_start(struct seq_file *seq, loff_t *pos)
+-{
+- return neigh_seq_start(seq, pos, &dn_neigh_table,
+- NEIGH_SEQ_NEIGH_ONLY);
+-}
+-
+-static const struct seq_operations dn_neigh_seq_ops = {
+- .start = dn_neigh_seq_start,
+- .next = neigh_seq_next,
+- .stop = neigh_seq_stop,
+- .show = dn_neigh_seq_show,
+-};
+-
+-static int dn_neigh_seq_open(struct inode *inode, struct file *file)
+-{
+- return seq_open_net(inode, file, &dn_neigh_seq_ops,
+- sizeof(struct neigh_seq_state));
+-}
+-
+-static const struct file_operations dn_neigh_seq_fops = {
+- .owner = THIS_MODULE,
+- .open = dn_neigh_seq_open,
+- .read = seq_read,
+- .llseek = seq_lseek,
+- .release = seq_release_net,
+-};
+-
+-#endif
+-
+-void __init dn_neigh_init(void)
+-{
+- neigh_table_init(NEIGH_DN_TABLE, &dn_neigh_table);
+- proc_create("decnet_neigh", S_IRUGO, init_net.proc_net,
+- &dn_neigh_seq_fops);
+-}
+-
+-void __exit dn_neigh_cleanup(void)
+-{
+- remove_proc_entry("decnet_neigh", init_net.proc_net);
+- neigh_table_clear(NEIGH_DN_TABLE, &dn_neigh_table);
+-}
+diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c
+deleted file mode 100644
+index 7ac086d5c0c01..0000000000000
+--- a/net/decnet/dn_nsp_in.c
++++ /dev/null
+@@ -1,918 +0,0 @@
+-/*
+- * DECnet An implementation of the DECnet protocol suite for the LINUX
+- * operating system. DECnet is implemented using the BSD Socket
+- * interface as the means of communication with the user level.
+- *
+- * DECnet Network Services Protocol (Input)
+- *
+- * Author: Eduardo Marcelo Serrat <emserrat@geocities.com>
+- *
+- * Changes:
+- *
+- * Steve Whitehouse: Split into dn_nsp_in.c and dn_nsp_out.c from
+- * original dn_nsp.c.
+- * Steve Whitehouse: Updated to work with my new routing architecture.
+- * Steve Whitehouse: Add changes from Eduardo Serrat's patches.
+- * Steve Whitehouse: Put all ack handling code in a common routine.
+- * Steve Whitehouse: Put other common bits into dn_nsp_rx()
+- * Steve Whitehouse: More checks on skb->len to catch bogus packets
+- * Fixed various race conditions and possible nasties.
+- * Steve Whitehouse: Now handles returned conninit frames.
+- * David S. Miller: New socket locking
+- * Steve Whitehouse: Fixed lockup when socket filtering was enabled.
+- * Paul Koning: Fix to push CC sockets into RUN when acks are
+- * received.
+- * Steve Whitehouse:
+- * Patrick Caulfield: Checking conninits for correctness & sending of error
+- * responses.
+- * Steve Whitehouse: Added backlog congestion level return codes.
+- * Patrick Caulfield:
+- * Steve Whitehouse: Added flow control support (outbound)
+- * Steve Whitehouse: Prepare for nonlinear skbs
+- */
+-
+-/******************************************************************************
+- (c) 1995-1998 E.M. Serrat emserrat@geocities.com
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License as published by
+- the Free Software Foundation; either version 2 of the License, or
+- any later version.
+-
+- This program is distributed in the hope that it will be useful,
+- but WITHOUT ANY WARRANTY; without even the implied warranty of
+- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- GNU General Public License for more details.
+-*******************************************************************************/
+-
+-#include <linux/errno.h>
+-#include <linux/types.h>
+-#include <linux/socket.h>
+-#include <linux/in.h>
+-#include <linux/kernel.h>
+-#include <linux/timer.h>
+-#include <linux/string.h>
+-#include <linux/sockios.h>
+-#include <linux/net.h>
+-#include <linux/netdevice.h>
+-#include <linux/inet.h>
+-#include <linux/route.h>
+-#include <linux/slab.h>
+-#include <net/sock.h>
+-#include <net/tcp_states.h>
+-#include <linux/fcntl.h>
+-#include <linux/mm.h>
+-#include <linux/termios.h>
+-#include <linux/interrupt.h>
+-#include <linux/proc_fs.h>
+-#include <linux/stat.h>
+-#include <linux/init.h>
+-#include <linux/poll.h>
+-#include <linux/netfilter_decnet.h>
+-#include <net/neighbour.h>
+-#include <net/dst.h>
+-#include <net/dn.h>
+-#include <net/dn_nsp.h>
+-#include <net/dn_dev.h>
+-#include <net/dn_route.h>
+-
+-extern int decnet_log_martians;
+-
+-static void dn_log_martian(struct sk_buff *skb, const char *msg)
+-{
+- if (decnet_log_martians) {
+- char *devname = skb->dev ? skb->dev->name : "???";
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- net_info_ratelimited("DECnet: Martian packet (%s) dev=%s src=0x%04hx dst=0x%04hx srcport=0x%04hx dstport=0x%04hx\n",
+- msg, devname,
+- le16_to_cpu(cb->src),
+- le16_to_cpu(cb->dst),
+- le16_to_cpu(cb->src_port),
+- le16_to_cpu(cb->dst_port));
+- }
+-}
+-
+-/*
+- * For this function we've flipped the cross-subchannel bit
+- * if the message is an otherdata or linkservice message. Thus
+- * we can use it to work out what to update.
+- */
+-static void dn_ack(struct sock *sk, struct sk_buff *skb, unsigned short ack)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- unsigned short type = ((ack >> 12) & 0x0003);
+- int wakeup = 0;
+-
+- switch (type) {
+- case 0: /* ACK - Data */
+- if (dn_after(ack, scp->ackrcv_dat)) {
+- scp->ackrcv_dat = ack & 0x0fff;
+- wakeup |= dn_nsp_check_xmit_queue(sk, skb,
+- &scp->data_xmit_queue,
+- ack);
+- }
+- break;
+- case 1: /* NAK - Data */
+- break;
+- case 2: /* ACK - OtherData */
+- if (dn_after(ack, scp->ackrcv_oth)) {
+- scp->ackrcv_oth = ack & 0x0fff;
+- wakeup |= dn_nsp_check_xmit_queue(sk, skb,
+- &scp->other_xmit_queue,
+- ack);
+- }
+- break;
+- case 3: /* NAK - OtherData */
+- break;
+- }
+-
+- if (wakeup && !sock_flag(sk, SOCK_DEAD))
+- sk->sk_state_change(sk);
+-}
+-
+-/*
+- * This function is a universal ack processor.
+- */
+-static int dn_process_ack(struct sock *sk, struct sk_buff *skb, int oth)
+-{
+- __le16 *ptr = (__le16 *)skb->data;
+- int len = 0;
+- unsigned short ack;
+-
+- if (skb->len < 2)
+- return len;
+-
+- if ((ack = le16_to_cpu(*ptr)) & 0x8000) {
+- skb_pull(skb, 2);
+- ptr++;
+- len += 2;
+- if ((ack & 0x4000) == 0) {
+- if (oth)
+- ack ^= 0x2000;
+- dn_ack(sk, skb, ack);
+- }
+- }
+-
+- if (skb->len < 2)
+- return len;
+-
+- if ((ack = le16_to_cpu(*ptr)) & 0x8000) {
+- skb_pull(skb, 2);
+- len += 2;
+- if ((ack & 0x4000) == 0) {
+- if (oth)
+- ack ^= 0x2000;
+- dn_ack(sk, skb, ack);
+- }
+- }
+-
+- return len;
+-}
+-
+-
+-/**
+- * dn_check_idf - Check an image data field format is correct.
+- * @pptr: Pointer to pointer to image data
+- * @len: Pointer to length of image data
+- * @max: The maximum allowed length of the data in the image data field
+- * @follow_on: Check that this many bytes exist beyond the end of the image data
+- *
+- * Returns: 0 if ok, -1 on error
+- */
+-static inline int dn_check_idf(unsigned char **pptr, int *len, unsigned char max, unsigned char follow_on)
+-{
+- unsigned char *ptr = *pptr;
+- unsigned char flen = *ptr++;
+-
+- (*len)--;
+- if (flen > max)
+- return -1;
+- if ((flen + follow_on) > *len)
+- return -1;
+-
+- *len -= flen;
+- *pptr = ptr + flen;
+- return 0;
+-}
+-
+-/*
+- * Table of reason codes to pass back to node which sent us a badly
+- * formed message, plus text messages for the log. A zero entry in
+- * the reason field means "don't reply" otherwise a disc init is sent with
+- * the specified reason code.
+- */
+-static struct {
+- unsigned short reason;
+- const char *text;
+-} ci_err_table[] = {
+- { 0, "CI: Truncated message" },
+- { NSP_REASON_ID, "CI: Destination username error" },
+- { NSP_REASON_ID, "CI: Destination username type" },
+- { NSP_REASON_US, "CI: Source username error" },
+- { 0, "CI: Truncated at menuver" },
+- { 0, "CI: Truncated before access or user data" },
+- { NSP_REASON_IO, "CI: Access data format error" },
+- { NSP_REASON_IO, "CI: User data format error" }
+-};
+-
+-/*
+- * This function uses a slightly different lookup method
+- * to find its sockets, since it searches on object name/number
+- * rather than port numbers. Various tests are done to ensure that
+- * the incoming data is in the correct format before it is queued to
+- * a socket.
+- */
+-static struct sock *dn_find_listener(struct sk_buff *skb, unsigned short *reason)
+-{
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- struct nsp_conn_init_msg *msg = (struct nsp_conn_init_msg *)skb->data;
+- struct sockaddr_dn dstaddr;
+- struct sockaddr_dn srcaddr;
+- unsigned char type = 0;
+- int dstlen;
+- int srclen;
+- unsigned char *ptr;
+- int len;
+- int err = 0;
+- unsigned char menuver;
+-
+- memset(&dstaddr, 0, sizeof(struct sockaddr_dn));
+- memset(&srcaddr, 0, sizeof(struct sockaddr_dn));
+-
+- /*
+- * 1. Decode & remove message header
+- */
+- cb->src_port = msg->srcaddr;
+- cb->dst_port = msg->dstaddr;
+- cb->services = msg->services;
+- cb->info = msg->info;
+- cb->segsize = le16_to_cpu(msg->segsize);
+-
+- if (!pskb_may_pull(skb, sizeof(*msg)))
+- goto err_out;
+-
+- skb_pull(skb, sizeof(*msg));
+-
+- len = skb->len;
+- ptr = skb->data;
+-
+- /*
+- * 2. Check destination end username format
+- */
+- dstlen = dn_username2sockaddr(ptr, len, &dstaddr, &type);
+- err++;
+- if (dstlen < 0)
+- goto err_out;
+-
+- err++;
+- if (type > 1)
+- goto err_out;
+-
+- len -= dstlen;
+- ptr += dstlen;
+-
+- /*
+- * 3. Check source end username format
+- */
+- srclen = dn_username2sockaddr(ptr, len, &srcaddr, &type);
+- err++;
+- if (srclen < 0)
+- goto err_out;
+-
+- len -= srclen;
+- ptr += srclen;
+- err++;
+- if (len < 1)
+- goto err_out;
+-
+- menuver = *ptr;
+- ptr++;
+- len--;
+-
+- /*
+- * 4. Check that optional data actually exists if menuver says it does
+- */
+- err++;
+- if ((menuver & (DN_MENUVER_ACC | DN_MENUVER_USR)) && (len < 1))
+- goto err_out;
+-
+- /*
+- * 5. Check optional access data format
+- */
+- err++;
+- if (menuver & DN_MENUVER_ACC) {
+- if (dn_check_idf(&ptr, &len, 39, 1))
+- goto err_out;
+- if (dn_check_idf(&ptr, &len, 39, 1))
+- goto err_out;
+- if (dn_check_idf(&ptr, &len, 39, (menuver & DN_MENUVER_USR) ? 1 : 0))
+- goto err_out;
+- }
+-
+- /*
+- * 6. Check optional user data format
+- */
+- err++;
+- if (menuver & DN_MENUVER_USR) {
+- if (dn_check_idf(&ptr, &len, 16, 0))
+- goto err_out;
+- }
+-
+- /*
+- * 7. Look up socket based on destination end username
+- */
+- return dn_sklist_find_listener(&dstaddr);
+-err_out:
+- dn_log_martian(skb, ci_err_table[err].text);
+- *reason = ci_err_table[err].reason;
+- return NULL;
+-}
+-
+-
+-static void dn_nsp_conn_init(struct sock *sk, struct sk_buff *skb)
+-{
+- if (sk_acceptq_is_full(sk)) {
+- kfree_skb(skb);
+- return;
+- }
+-
+- sk->sk_ack_backlog++;
+- skb_queue_tail(&sk->sk_receive_queue, skb);
+- sk->sk_state_change(sk);
+-}
+-
+-static void dn_nsp_conn_conf(struct sock *sk, struct sk_buff *skb)
+-{
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- struct dn_scp *scp = DN_SK(sk);
+- unsigned char *ptr;
+-
+- if (skb->len < 4)
+- goto out;
+-
+- ptr = skb->data;
+- cb->services = *ptr++;
+- cb->info = *ptr++;
+- cb->segsize = le16_to_cpu(*(__le16 *)ptr);
+-
+- if ((scp->state == DN_CI) || (scp->state == DN_CD)) {
+- scp->persist = 0;
+- scp->addrrem = cb->src_port;
+- sk->sk_state = TCP_ESTABLISHED;
+- scp->state = DN_RUN;
+- scp->services_rem = cb->services;
+- scp->info_rem = cb->info;
+- scp->segsize_rem = cb->segsize;
+-
+- if ((scp->services_rem & NSP_FC_MASK) == NSP_FC_NONE)
+- scp->max_window = decnet_no_fc_max_cwnd;
+-
+- if (skb->len > 0) {
+- u16 dlen = *skb->data;
+- if ((dlen <= 16) && (dlen <= skb->len)) {
+- scp->conndata_in.opt_optl = cpu_to_le16(dlen);
+- skb_copy_from_linear_data_offset(skb, 1,
+- scp->conndata_in.opt_data, dlen);
+- }
+- }
+- dn_nsp_send_link(sk, DN_NOCHANGE, 0);
+- if (!sock_flag(sk, SOCK_DEAD))
+- sk->sk_state_change(sk);
+- }
+-
+-out:
+- kfree_skb(skb);
+-}
+-
+-static void dn_nsp_conn_ack(struct sock *sk, struct sk_buff *skb)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+-
+- if (scp->state == DN_CI) {
+- scp->state = DN_CD;
+- scp->persist = 0;
+- }
+-
+- kfree_skb(skb);
+-}
+-
+-static void dn_nsp_disc_init(struct sock *sk, struct sk_buff *skb)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- unsigned short reason;
+-
+- if (skb->len < 2)
+- goto out;
+-
+- reason = le16_to_cpu(*(__le16 *)skb->data);
+- skb_pull(skb, 2);
+-
+- scp->discdata_in.opt_status = cpu_to_le16(reason);
+- scp->discdata_in.opt_optl = 0;
+- memset(scp->discdata_in.opt_data, 0, 16);
+-
+- if (skb->len > 0) {
+- u16 dlen = *skb->data;
+- if ((dlen <= 16) && (dlen <= skb->len)) {
+- scp->discdata_in.opt_optl = cpu_to_le16(dlen);
+- skb_copy_from_linear_data_offset(skb, 1, scp->discdata_in.opt_data, dlen);
+- }
+- }
+-
+- scp->addrrem = cb->src_port;
+- sk->sk_state = TCP_CLOSE;
+-
+- switch (scp->state) {
+- case DN_CI:
+- case DN_CD:
+- scp->state = DN_RJ;
+- sk->sk_err = ECONNREFUSED;
+- break;
+- case DN_RUN:
+- sk->sk_shutdown |= SHUTDOWN_MASK;
+- scp->state = DN_DN;
+- break;
+- case DN_DI:
+- scp->state = DN_DIC;
+- break;
+- }
+-
+- if (!sock_flag(sk, SOCK_DEAD)) {
+- if (sk->sk_socket->state != SS_UNCONNECTED)
+- sk->sk_socket->state = SS_DISCONNECTING;
+- sk->sk_state_change(sk);
+- }
+-
+- /*
+- * It appears that its possible for remote machines to send disc
+- * init messages with no port identifier if we are in the CI and
+- * possibly also the CD state. Obviously we shouldn't reply with
+- * a message if we don't know what the end point is.
+- */
+- if (scp->addrrem) {
+- dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, GFP_ATOMIC);
+- }
+- scp->persist_fxn = dn_destroy_timer;
+- scp->persist = dn_nsp_persist(sk);
+-
+-out:
+- kfree_skb(skb);
+-}
+-
+-/*
+- * disc_conf messages are also called no_resources or no_link
+- * messages depending upon the "reason" field.
+- */
+-static void dn_nsp_disc_conf(struct sock *sk, struct sk_buff *skb)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- unsigned short reason;
+-
+- if (skb->len != 2)
+- goto out;
+-
+- reason = le16_to_cpu(*(__le16 *)skb->data);
+-
+- sk->sk_state = TCP_CLOSE;
+-
+- switch (scp->state) {
+- case DN_CI:
+- scp->state = DN_NR;
+- break;
+- case DN_DR:
+- if (reason == NSP_REASON_DC)
+- scp->state = DN_DRC;
+- if (reason == NSP_REASON_NL)
+- scp->state = DN_CN;
+- break;
+- case DN_DI:
+- scp->state = DN_DIC;
+- break;
+- case DN_RUN:
+- sk->sk_shutdown |= SHUTDOWN_MASK;
+- case DN_CC:
+- scp->state = DN_CN;
+- }
+-
+- if (!sock_flag(sk, SOCK_DEAD)) {
+- if (sk->sk_socket->state != SS_UNCONNECTED)
+- sk->sk_socket->state = SS_DISCONNECTING;
+- sk->sk_state_change(sk);
+- }
+-
+- scp->persist_fxn = dn_destroy_timer;
+- scp->persist = dn_nsp_persist(sk);
+-
+-out:
+- kfree_skb(skb);
+-}
+-
+-static void dn_nsp_linkservice(struct sock *sk, struct sk_buff *skb)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- unsigned short segnum;
+- unsigned char lsflags;
+- signed char fcval;
+- int wake_up = 0;
+- char *ptr = skb->data;
+- unsigned char fctype = scp->services_rem & NSP_FC_MASK;
+-
+- if (skb->len != 4)
+- goto out;
+-
+- segnum = le16_to_cpu(*(__le16 *)ptr);
+- ptr += 2;
+- lsflags = *(unsigned char *)ptr++;
+- fcval = *ptr;
+-
+- /*
+- * Here we ignore erronous packets which should really
+- * should cause a connection abort. It is not critical
+- * for now though.
+- */
+- if (lsflags & 0xf8)
+- goto out;
+-
+- if (seq_next(scp->numoth_rcv, segnum)) {
+- seq_add(&scp->numoth_rcv, 1);
+- switch(lsflags & 0x04) { /* FCVAL INT */
+- case 0x00: /* Normal Request */
+- switch(lsflags & 0x03) { /* FCVAL MOD */
+- case 0x00: /* Request count */
+- if (fcval < 0) {
+- unsigned char p_fcval = -fcval;
+- if ((scp->flowrem_dat > p_fcval) &&
+- (fctype == NSP_FC_SCMC)) {
+- scp->flowrem_dat -= p_fcval;
+- }
+- } else if (fcval > 0) {
+- scp->flowrem_dat += fcval;
+- wake_up = 1;
+- }
+- break;
+- case 0x01: /* Stop outgoing data */
+- scp->flowrem_sw = DN_DONTSEND;
+- break;
+- case 0x02: /* Ok to start again */
+- scp->flowrem_sw = DN_SEND;
+- dn_nsp_output(sk);
+- wake_up = 1;
+- }
+- break;
+- case 0x04: /* Interrupt Request */
+- if (fcval > 0) {
+- scp->flowrem_oth += fcval;
+- wake_up = 1;
+- }
+- break;
+- }
+- if (wake_up && !sock_flag(sk, SOCK_DEAD))
+- sk->sk_state_change(sk);
+- }
+-
+- dn_nsp_send_oth_ack(sk);
+-
+-out:
+- kfree_skb(skb);
+-}
+-
+-/*
+- * Copy of sock_queue_rcv_skb (from sock.h) without
+- * bh_lock_sock() (its already held when this is called) which
+- * also allows data and other data to be queued to a socket.
+- */
+-static __inline__ int dn_queue_skb(struct sock *sk, struct sk_buff *skb, int sig, struct sk_buff_head *queue)
+-{
+- int err;
+-
+- /* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
+- number of warnings when compiling with -W --ANK
+- */
+- if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >=
+- (unsigned int)sk->sk_rcvbuf) {
+- err = -ENOMEM;
+- goto out;
+- }
+-
+- err = sk_filter(sk, skb);
+- if (err)
+- goto out;
+-
+- skb_set_owner_r(skb, sk);
+- skb_queue_tail(queue, skb);
+-
+- if (!sock_flag(sk, SOCK_DEAD))
+- sk->sk_data_ready(sk);
+-out:
+- return err;
+-}
+-
+-static void dn_nsp_otherdata(struct sock *sk, struct sk_buff *skb)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- unsigned short segnum;
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- int queued = 0;
+-
+- if (skb->len < 2)
+- goto out;
+-
+- cb->segnum = segnum = le16_to_cpu(*(__le16 *)skb->data);
+- skb_pull(skb, 2);
+-
+- if (seq_next(scp->numoth_rcv, segnum)) {
+-
+- if (dn_queue_skb(sk, skb, SIGURG, &scp->other_receive_queue) == 0) {
+- seq_add(&scp->numoth_rcv, 1);
+- scp->other_report = 0;
+- queued = 1;
+- }
+- }
+-
+- dn_nsp_send_oth_ack(sk);
+-out:
+- if (!queued)
+- kfree_skb(skb);
+-}
+-
+-static void dn_nsp_data(struct sock *sk, struct sk_buff *skb)
+-{
+- int queued = 0;
+- unsigned short segnum;
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- struct dn_scp *scp = DN_SK(sk);
+-
+- if (skb->len < 2)
+- goto out;
+-
+- cb->segnum = segnum = le16_to_cpu(*(__le16 *)skb->data);
+- skb_pull(skb, 2);
+-
+- if (seq_next(scp->numdat_rcv, segnum)) {
+- if (dn_queue_skb(sk, skb, SIGIO, &sk->sk_receive_queue) == 0) {
+- seq_add(&scp->numdat_rcv, 1);
+- queued = 1;
+- }
+-
+- if ((scp->flowloc_sw == DN_SEND) && dn_congested(sk)) {
+- scp->flowloc_sw = DN_DONTSEND;
+- dn_nsp_send_link(sk, DN_DONTSEND, 0);
+- }
+- }
+-
+- dn_nsp_send_data_ack(sk);
+-out:
+- if (!queued)
+- kfree_skb(skb);
+-}
+-
+-/*
+- * If one of our conninit messages is returned, this function
+- * deals with it. It puts the socket into the NO_COMMUNICATION
+- * state.
+- */
+-static void dn_returned_conn_init(struct sock *sk, struct sk_buff *skb)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+-
+- if (scp->state == DN_CI) {
+- scp->state = DN_NC;
+- sk->sk_state = TCP_CLOSE;
+- if (!sock_flag(sk, SOCK_DEAD))
+- sk->sk_state_change(sk);
+- }
+-
+- kfree_skb(skb);
+-}
+-
+-static int dn_nsp_no_socket(struct sk_buff *skb, unsigned short reason)
+-{
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- int ret = NET_RX_DROP;
+-
+- /* Must not reply to returned packets */
+- if (cb->rt_flags & DN_RT_F_RTS)
+- goto out;
+-
+- if ((reason != NSP_REASON_OK) && ((cb->nsp_flags & 0x0c) == 0x08)) {
+- switch (cb->nsp_flags & 0x70) {
+- case 0x10:
+- case 0x60: /* (Retransmitted) Connect Init */
+- dn_nsp_return_disc(skb, NSP_DISCINIT, reason);
+- ret = NET_RX_SUCCESS;
+- break;
+- case 0x20: /* Connect Confirm */
+- dn_nsp_return_disc(skb, NSP_DISCCONF, reason);
+- ret = NET_RX_SUCCESS;
+- break;
+- }
+- }
+-
+-out:
+- kfree_skb(skb);
+- return ret;
+-}
+-
+-static int dn_nsp_rx_packet(struct net *net, struct sock *sk2,
+- struct sk_buff *skb)
+-{
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- struct sock *sk = NULL;
+- unsigned char *ptr = (unsigned char *)skb->data;
+- unsigned short reason = NSP_REASON_NL;
+-
+- if (!pskb_may_pull(skb, 2))
+- goto free_out;
+-
+- skb_reset_transport_header(skb);
+- cb->nsp_flags = *ptr++;
+-
+- if (decnet_debug_level & 2)
+- printk(KERN_DEBUG "dn_nsp_rx: Message type 0x%02x\n", (int)cb->nsp_flags);
+-
+- if (cb->nsp_flags & 0x83)
+- goto free_out;
+-
+- /*
+- * Filter out conninits and useless packet types
+- */
+- if ((cb->nsp_flags & 0x0c) == 0x08) {
+- switch (cb->nsp_flags & 0x70) {
+- case 0x00: /* NOP */
+- case 0x70: /* Reserved */
+- case 0x50: /* Reserved, Phase II node init */
+- goto free_out;
+- case 0x10:
+- case 0x60:
+- if (unlikely(cb->rt_flags & DN_RT_F_RTS))
+- goto free_out;
+- sk = dn_find_listener(skb, &reason);
+- goto got_it;
+- }
+- }
+-
+- if (!pskb_may_pull(skb, 3))
+- goto free_out;
+-
+- /*
+- * Grab the destination address.
+- */
+- cb->dst_port = *(__le16 *)ptr;
+- cb->src_port = 0;
+- ptr += 2;
+-
+- /*
+- * If not a connack, grab the source address too.
+- */
+- if (pskb_may_pull(skb, 5)) {
+- cb->src_port = *(__le16 *)ptr;
+- ptr += 2;
+- skb_pull(skb, 5);
+- }
+-
+- /*
+- * Returned packets...
+- * Swap src & dst and look up in the normal way.
+- */
+- if (unlikely(cb->rt_flags & DN_RT_F_RTS)) {
+- __le16 tmp = cb->dst_port;
+- cb->dst_port = cb->src_port;
+- cb->src_port = tmp;
+- tmp = cb->dst;
+- cb->dst = cb->src;
+- cb->src = tmp;
+- }
+-
+- /*
+- * Find the socket to which this skb is destined.
+- */
+- sk = dn_find_by_skb(skb);
+-got_it:
+- if (sk != NULL) {
+- struct dn_scp *scp = DN_SK(sk);
+-
+- /* Reset backoff */
+- scp->nsp_rxtshift = 0;
+-
+- /*
+- * We linearize everything except data segments here.
+- */
+- if (cb->nsp_flags & ~0x60) {
+- if (unlikely(skb_linearize(skb)))
+- goto free_out;
+- }
+-
+- return sk_receive_skb(sk, skb, 0);
+- }
+-
+- return dn_nsp_no_socket(skb, reason);
+-
+-free_out:
+- kfree_skb(skb);
+- return NET_RX_DROP;
+-}
+-
+-int dn_nsp_rx(struct sk_buff *skb)
+-{
+- return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_IN,
+- &init_net, NULL, skb, skb->dev, NULL,
+- dn_nsp_rx_packet);
+-}
+-
+-/*
+- * This is the main receive routine for sockets. It is called
+- * from the above when the socket is not busy, and also from
+- * sock_release() when there is a backlog queued up.
+- */
+-int dn_nsp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+-
+- if (cb->rt_flags & DN_RT_F_RTS) {
+- if (cb->nsp_flags == 0x18 || cb->nsp_flags == 0x68)
+- dn_returned_conn_init(sk, skb);
+- else
+- kfree_skb(skb);
+- return NET_RX_SUCCESS;
+- }
+-
+- /*
+- * Control packet.
+- */
+- if ((cb->nsp_flags & 0x0c) == 0x08) {
+- switch (cb->nsp_flags & 0x70) {
+- case 0x10:
+- case 0x60:
+- dn_nsp_conn_init(sk, skb);
+- break;
+- case 0x20:
+- dn_nsp_conn_conf(sk, skb);
+- break;
+- case 0x30:
+- dn_nsp_disc_init(sk, skb);
+- break;
+- case 0x40:
+- dn_nsp_disc_conf(sk, skb);
+- break;
+- }
+-
+- } else if (cb->nsp_flags == 0x24) {
+- /*
+- * Special for connacks, 'cos they don't have
+- * ack data or ack otherdata info.
+- */
+- dn_nsp_conn_ack(sk, skb);
+- } else {
+- int other = 1;
+-
+- /* both data and ack frames can kick a CC socket into RUN */
+- if ((scp->state == DN_CC) && !sock_flag(sk, SOCK_DEAD)) {
+- scp->state = DN_RUN;
+- sk->sk_state = TCP_ESTABLISHED;
+- sk->sk_state_change(sk);
+- }
+-
+- if ((cb->nsp_flags & 0x1c) == 0)
+- other = 0;
+- if (cb->nsp_flags == 0x04)
+- other = 0;
+-
+- /*
+- * Read out ack data here, this applies equally
+- * to data, other data, link serivce and both
+- * ack data and ack otherdata.
+- */
+- dn_process_ack(sk, skb, other);
+-
+- /*
+- * If we've some sort of data here then call a
+- * suitable routine for dealing with it, otherwise
+- * the packet is an ack and can be discarded.
+- */
+- if ((cb->nsp_flags & 0x0c) == 0) {
+-
+- if (scp->state != DN_RUN)
+- goto free_out;
+-
+- switch (cb->nsp_flags) {
+- case 0x10: /* LS */
+- dn_nsp_linkservice(sk, skb);
+- break;
+- case 0x30: /* OD */
+- dn_nsp_otherdata(sk, skb);
+- break;
+- default:
+- dn_nsp_data(sk, skb);
+- }
+-
+- } else { /* Ack, chuck it out here */
+-free_out:
+- kfree_skb(skb);
+- }
+- }
+-
+- return NET_RX_SUCCESS;
+-}
+-
+diff --git a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c
+deleted file mode 100644
+index 66f035e476eae..0000000000000
+--- a/net/decnet/dn_nsp_out.c
++++ /dev/null
+@@ -1,718 +0,0 @@
+-/*
+- * DECnet An implementation of the DECnet protocol suite for the LINUX
+- * operating system. DECnet is implemented using the BSD Socket
+- * interface as the means of communication with the user level.
+- *
+- * DECnet Network Services Protocol (Output)
+- *
+- * Author: Eduardo Marcelo Serrat <emserrat@geocities.com>
+- *
+- * Changes:
+- *
+- * Steve Whitehouse: Split into dn_nsp_in.c and dn_nsp_out.c from
+- * original dn_nsp.c.
+- * Steve Whitehouse: Updated to work with my new routing architecture.
+- * Steve Whitehouse: Added changes from Eduardo Serrat's patches.
+- * Steve Whitehouse: Now conninits have the "return" bit set.
+- * Steve Whitehouse: Fixes to check alloc'd skbs are non NULL!
+- * Moved output state machine into one function
+- * Steve Whitehouse: New output state machine
+- * Paul Koning: Connect Confirm message fix.
+- * Eduardo Serrat: Fix to stop dn_nsp_do_disc() sending malformed packets.
+- * Steve Whitehouse: dn_nsp_output() and friends needed a spring clean
+- * Steve Whitehouse: Moved dn_nsp_send() in here from route.h
+- */
+-
+-/******************************************************************************
+- (c) 1995-1998 E.M. Serrat emserrat@geocities.com
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License as published by
+- the Free Software Foundation; either version 2 of the License, or
+- any later version.
+-
+- This program is distributed in the hope that it will be useful,
+- but WITHOUT ANY WARRANTY; without even the implied warranty of
+- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- GNU General Public License for more details.
+-*******************************************************************************/
+-
+-#include <linux/errno.h>
+-#include <linux/types.h>
+-#include <linux/socket.h>
+-#include <linux/in.h>
+-#include <linux/kernel.h>
+-#include <linux/timer.h>
+-#include <linux/string.h>
+-#include <linux/sockios.h>
+-#include <linux/net.h>
+-#include <linux/netdevice.h>
+-#include <linux/inet.h>
+-#include <linux/route.h>
+-#include <linux/slab.h>
+-#include <net/sock.h>
+-#include <linux/fcntl.h>
+-#include <linux/mm.h>
+-#include <linux/termios.h>
+-#include <linux/interrupt.h>
+-#include <linux/proc_fs.h>
+-#include <linux/stat.h>
+-#include <linux/init.h>
+-#include <linux/poll.h>
+-#include <linux/if_packet.h>
+-#include <net/neighbour.h>
+-#include <net/dst.h>
+-#include <net/flow.h>
+-#include <net/dn.h>
+-#include <net/dn_nsp.h>
+-#include <net/dn_dev.h>
+-#include <net/dn_route.h>
+-
+-
+-static int nsp_backoff[NSP_MAXRXTSHIFT + 1] = { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
+-
+-static void dn_nsp_send(struct sk_buff *skb)
+-{
+- struct sock *sk = skb->sk;
+- struct dn_scp *scp = DN_SK(sk);
+- struct dst_entry *dst;
+- struct flowidn fld;
+-
+- skb_reset_transport_header(skb);
+- scp->stamp = jiffies;
+-
+- dst = sk_dst_check(sk, 0);
+- if (dst) {
+-try_again:
+- skb_dst_set(skb, dst);
+- dst_output(&init_net, skb->sk, skb);
+- return;
+- }
+-
+- memset(&fld, 0, sizeof(fld));
+- fld.flowidn_oif = sk->sk_bound_dev_if;
+- fld.saddr = dn_saddr2dn(&scp->addr);
+- fld.daddr = dn_saddr2dn(&scp->peer);
+- dn_sk_ports_copy(&fld, scp);
+- fld.flowidn_proto = DNPROTO_NSP;
+- if (dn_route_output_sock(&sk->sk_dst_cache, &fld, sk, 0) == 0) {
+- dst = sk_dst_get(sk);
+- sk->sk_route_caps = dst->dev->features;
+- goto try_again;
+- }
+-
+- sk->sk_err = EHOSTUNREACH;
+- if (!sock_flag(sk, SOCK_DEAD))
+- sk->sk_state_change(sk);
+-}
+-
+-
+-/*
+- * If sk == NULL, then we assume that we are supposed to be making
+- * a routing layer skb. If sk != NULL, then we are supposed to be
+- * creating an skb for the NSP layer.
+- *
+- * The eventual aim is for each socket to have a cached header size
+- * for its outgoing packets, and to set hdr from this when sk != NULL.
+- */
+-struct sk_buff *dn_alloc_skb(struct sock *sk, int size, gfp_t pri)
+-{
+- struct sk_buff *skb;
+- int hdr = 64;
+-
+- if ((skb = alloc_skb(size + hdr, pri)) == NULL)
+- return NULL;
+-
+- skb->protocol = htons(ETH_P_DNA_RT);
+- skb->pkt_type = PACKET_OUTGOING;
+-
+- if (sk)
+- skb_set_owner_w(skb, sk);
+-
+- skb_reserve(skb, hdr);
+-
+- return skb;
+-}
+-
+-/*
+- * Calculate persist timer based upon the smoothed round
+- * trip time and the variance. Backoff according to the
+- * nsp_backoff[] array.
+- */
+-unsigned long dn_nsp_persist(struct sock *sk)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+-
+- unsigned long t = ((scp->nsp_srtt >> 2) + scp->nsp_rttvar) >> 1;
+-
+- t *= nsp_backoff[scp->nsp_rxtshift];
+-
+- if (t < HZ) t = HZ;
+- if (t > (600*HZ)) t = (600*HZ);
+-
+- if (scp->nsp_rxtshift < NSP_MAXRXTSHIFT)
+- scp->nsp_rxtshift++;
+-
+- /* printk(KERN_DEBUG "rxtshift %lu, t=%lu\n", scp->nsp_rxtshift, t); */
+-
+- return t;
+-}
+-
+-/*
+- * This is called each time we get an estimate for the rtt
+- * on the link.
+- */
+-static void dn_nsp_rtt(struct sock *sk, long rtt)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- long srtt = (long)scp->nsp_srtt;
+- long rttvar = (long)scp->nsp_rttvar;
+- long delta;
+-
+- /*
+- * If the jiffies clock flips over in the middle of timestamp
+- * gathering this value might turn out negative, so we make sure
+- * that is it always positive here.
+- */
+- if (rtt < 0)
+- rtt = -rtt;
+- /*
+- * Add new rtt to smoothed average
+- */
+- delta = ((rtt << 3) - srtt);
+- srtt += (delta >> 3);
+- if (srtt >= 1)
+- scp->nsp_srtt = (unsigned long)srtt;
+- else
+- scp->nsp_srtt = 1;
+-
+- /*
+- * Add new rtt varience to smoothed varience
+- */
+- delta >>= 1;
+- rttvar += ((((delta>0)?(delta):(-delta)) - rttvar) >> 2);
+- if (rttvar >= 1)
+- scp->nsp_rttvar = (unsigned long)rttvar;
+- else
+- scp->nsp_rttvar = 1;
+-
+- /* printk(KERN_DEBUG "srtt=%lu rttvar=%lu\n", scp->nsp_srtt, scp->nsp_rttvar); */
+-}
+-
+-/**
+- * dn_nsp_clone_and_send - Send a data packet by cloning it
+- * @skb: The packet to clone and transmit
+- * @gfp: memory allocation flag
+- *
+- * Clone a queued data or other data packet and transmit it.
+- *
+- * Returns: The number of times the packet has been sent previously
+- */
+-static inline unsigned int dn_nsp_clone_and_send(struct sk_buff *skb,
+- gfp_t gfp)
+-{
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- struct sk_buff *skb2;
+- int ret = 0;
+-
+- if ((skb2 = skb_clone(skb, gfp)) != NULL) {
+- ret = cb->xmit_count;
+- cb->xmit_count++;
+- cb->stamp = jiffies;
+- skb2->sk = skb->sk;
+- dn_nsp_send(skb2);
+- }
+-
+- return ret;
+-}
+-
+-/**
+- * dn_nsp_output - Try and send something from socket queues
+- * @sk: The socket whose queues are to be investigated
+- *
+- * Try and send the packet on the end of the data and other data queues.
+- * Other data gets priority over data, and if we retransmit a packet we
+- * reduce the window by dividing it in two.
+- *
+- */
+-void dn_nsp_output(struct sock *sk)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- struct sk_buff *skb;
+- unsigned int reduce_win = 0;
+-
+- /*
+- * First we check for otherdata/linkservice messages
+- */
+- if ((skb = skb_peek(&scp->other_xmit_queue)) != NULL)
+- reduce_win = dn_nsp_clone_and_send(skb, GFP_ATOMIC);
+-
+- /*
+- * If we may not send any data, we don't.
+- * If we are still trying to get some other data down the
+- * channel, we don't try and send any data.
+- */
+- if (reduce_win || (scp->flowrem_sw != DN_SEND))
+- goto recalc_window;
+-
+- if ((skb = skb_peek(&scp->data_xmit_queue)) != NULL)
+- reduce_win = dn_nsp_clone_and_send(skb, GFP_ATOMIC);
+-
+- /*
+- * If we've sent any frame more than once, we cut the
+- * send window size in half. There is always a minimum
+- * window size of one available.
+- */
+-recalc_window:
+- if (reduce_win) {
+- scp->snd_window >>= 1;
+- if (scp->snd_window < NSP_MIN_WINDOW)
+- scp->snd_window = NSP_MIN_WINDOW;
+- }
+-}
+-
+-int dn_nsp_xmit_timeout(struct sock *sk)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+-
+- dn_nsp_output(sk);
+-
+- if (!skb_queue_empty(&scp->data_xmit_queue) ||
+- !skb_queue_empty(&scp->other_xmit_queue))
+- scp->persist = dn_nsp_persist(sk);
+-
+- return 0;
+-}
+-
+-static inline __le16 *dn_mk_common_header(struct dn_scp *scp, struct sk_buff *skb, unsigned char msgflag, int len)
+-{
+- unsigned char *ptr = skb_push(skb, len);
+-
+- BUG_ON(len < 5);
+-
+- *ptr++ = msgflag;
+- *((__le16 *)ptr) = scp->addrrem;
+- ptr += 2;
+- *((__le16 *)ptr) = scp->addrloc;
+- ptr += 2;
+- return (__le16 __force *)ptr;
+-}
+-
+-static __le16 *dn_mk_ack_header(struct sock *sk, struct sk_buff *skb, unsigned char msgflag, int hlen, int other)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- unsigned short acknum = scp->numdat_rcv & 0x0FFF;
+- unsigned short ackcrs = scp->numoth_rcv & 0x0FFF;
+- __le16 *ptr;
+-
+- BUG_ON(hlen < 9);
+-
+- scp->ackxmt_dat = acknum;
+- scp->ackxmt_oth = ackcrs;
+- acknum |= 0x8000;
+- ackcrs |= 0x8000;
+-
+- /* If this is an "other data/ack" message, swap acknum and ackcrs */
+- if (other) {
+- unsigned short tmp = acknum;
+- acknum = ackcrs;
+- ackcrs = tmp;
+- }
+-
+- /* Set "cross subchannel" bit in ackcrs */
+- ackcrs |= 0x2000;
+-
+- ptr = dn_mk_common_header(scp, skb, msgflag, hlen);
+-
+- *ptr++ = cpu_to_le16(acknum);
+- *ptr++ = cpu_to_le16(ackcrs);
+-
+- return ptr;
+-}
+-
+-static __le16 *dn_nsp_mk_data_header(struct sock *sk, struct sk_buff *skb, int oth)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- __le16 *ptr = dn_mk_ack_header(sk, skb, cb->nsp_flags, 11, oth);
+-
+- if (unlikely(oth)) {
+- cb->segnum = scp->numoth;
+- seq_add(&scp->numoth, 1);
+- } else {
+- cb->segnum = scp->numdat;
+- seq_add(&scp->numdat, 1);
+- }
+- *(ptr++) = cpu_to_le16(cb->segnum);
+-
+- return ptr;
+-}
+-
+-void dn_nsp_queue_xmit(struct sock *sk, struct sk_buff *skb,
+- gfp_t gfp, int oth)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- unsigned long t = ((scp->nsp_srtt >> 2) + scp->nsp_rttvar) >> 1;
+-
+- cb->xmit_count = 0;
+- dn_nsp_mk_data_header(sk, skb, oth);
+-
+- /*
+- * Slow start: If we have been idle for more than
+- * one RTT, then reset window to min size.
+- */
+- if ((jiffies - scp->stamp) > t)
+- scp->snd_window = NSP_MIN_WINDOW;
+-
+- if (oth)
+- skb_queue_tail(&scp->other_xmit_queue, skb);
+- else
+- skb_queue_tail(&scp->data_xmit_queue, skb);
+-
+- if (scp->flowrem_sw != DN_SEND)
+- return;
+-
+- dn_nsp_clone_and_send(skb, gfp);
+-}
+-
+-
+-int dn_nsp_check_xmit_queue(struct sock *sk, struct sk_buff *skb, struct sk_buff_head *q, unsigned short acknum)
+-{
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- struct dn_scp *scp = DN_SK(sk);
+- struct sk_buff *skb2, *n, *ack = NULL;
+- int wakeup = 0;
+- int try_retrans = 0;
+- unsigned long reftime = cb->stamp;
+- unsigned long pkttime;
+- unsigned short xmit_count;
+- unsigned short segnum;
+-
+- skb_queue_walk_safe(q, skb2, n) {
+- struct dn_skb_cb *cb2 = DN_SKB_CB(skb2);
+-
+- if (dn_before_or_equal(cb2->segnum, acknum))
+- ack = skb2;
+-
+- /* printk(KERN_DEBUG "ack: %s %04x %04x\n", ack ? "ACK" : "SKIP", (int)cb2->segnum, (int)acknum); */
+-
+- if (ack == NULL)
+- continue;
+-
+- /* printk(KERN_DEBUG "check_xmit_queue: %04x, %d\n", acknum, cb2->xmit_count); */
+-
+- /* Does _last_ packet acked have xmit_count > 1 */
+- try_retrans = 0;
+- /* Remember to wake up the sending process */
+- wakeup = 1;
+- /* Keep various statistics */
+- pkttime = cb2->stamp;
+- xmit_count = cb2->xmit_count;
+- segnum = cb2->segnum;
+- /* Remove and drop ack'ed packet */
+- skb_unlink(ack, q);
+- kfree_skb(ack);
+- ack = NULL;
+-
+- /*
+- * We don't expect to see acknowledgements for packets we
+- * haven't sent yet.
+- */
+- WARN_ON(xmit_count == 0);
+-
+- /*
+- * If the packet has only been sent once, we can use it
+- * to calculate the RTT and also open the window a little
+- * further.
+- */
+- if (xmit_count == 1) {
+- if (dn_equal(segnum, acknum))
+- dn_nsp_rtt(sk, (long)(pkttime - reftime));
+-
+- if (scp->snd_window < scp->max_window)
+- scp->snd_window++;
+- }
+-
+- /*
+- * Packet has been sent more than once. If this is the last
+- * packet to be acknowledged then we want to send the next
+- * packet in the send queue again (assumes the remote host does
+- * go-back-N error control).
+- */
+- if (xmit_count > 1)
+- try_retrans = 1;
+- }
+-
+- if (try_retrans)
+- dn_nsp_output(sk);
+-
+- return wakeup;
+-}
+-
+-void dn_nsp_send_data_ack(struct sock *sk)
+-{
+- struct sk_buff *skb = NULL;
+-
+- if ((skb = dn_alloc_skb(sk, 9, GFP_ATOMIC)) == NULL)
+- return;
+-
+- skb_reserve(skb, 9);
+- dn_mk_ack_header(sk, skb, 0x04, 9, 0);
+- dn_nsp_send(skb);
+-}
+-
+-void dn_nsp_send_oth_ack(struct sock *sk)
+-{
+- struct sk_buff *skb = NULL;
+-
+- if ((skb = dn_alloc_skb(sk, 9, GFP_ATOMIC)) == NULL)
+- return;
+-
+- skb_reserve(skb, 9);
+- dn_mk_ack_header(sk, skb, 0x14, 9, 1);
+- dn_nsp_send(skb);
+-}
+-
+-
+-void dn_send_conn_ack (struct sock *sk)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- struct sk_buff *skb = NULL;
+- struct nsp_conn_ack_msg *msg;
+-
+- if ((skb = dn_alloc_skb(sk, 3, sk->sk_allocation)) == NULL)
+- return;
+-
+- msg = skb_put(skb, 3);
+- msg->msgflg = 0x24;
+- msg->dstaddr = scp->addrrem;
+-
+- dn_nsp_send(skb);
+-}
+-
+-void dn_nsp_delayed_ack(struct sock *sk)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+-
+- if (scp->ackxmt_oth != scp->numoth_rcv)
+- dn_nsp_send_oth_ack(sk);
+-
+- if (scp->ackxmt_dat != scp->numdat_rcv)
+- dn_nsp_send_data_ack(sk);
+-}
+-
+-static int dn_nsp_retrans_conn_conf(struct sock *sk)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+-
+- if (scp->state == DN_CC)
+- dn_send_conn_conf(sk, GFP_ATOMIC);
+-
+- return 0;
+-}
+-
+-void dn_send_conn_conf(struct sock *sk, gfp_t gfp)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- struct sk_buff *skb = NULL;
+- struct nsp_conn_init_msg *msg;
+- __u8 len = (__u8)le16_to_cpu(scp->conndata_out.opt_optl);
+-
+- if ((skb = dn_alloc_skb(sk, 50 + len, gfp)) == NULL)
+- return;
+-
+- msg = skb_put(skb, sizeof(*msg));
+- msg->msgflg = 0x28;
+- msg->dstaddr = scp->addrrem;
+- msg->srcaddr = scp->addrloc;
+- msg->services = scp->services_loc;
+- msg->info = scp->info_loc;
+- msg->segsize = cpu_to_le16(scp->segsize_loc);
+-
+- skb_put_u8(skb, len);
+-
+- if (len > 0)
+- skb_put_data(skb, scp->conndata_out.opt_data, len);
+-
+-
+- dn_nsp_send(skb);
+-
+- scp->persist = dn_nsp_persist(sk);
+- scp->persist_fxn = dn_nsp_retrans_conn_conf;
+-}
+-
+-
+-static __inline__ void dn_nsp_do_disc(struct sock *sk, unsigned char msgflg,
+- unsigned short reason, gfp_t gfp,
+- struct dst_entry *dst,
+- int ddl, unsigned char *dd, __le16 rem, __le16 loc)
+-{
+- struct sk_buff *skb = NULL;
+- int size = 7 + ddl + ((msgflg == NSP_DISCINIT) ? 1 : 0);
+- unsigned char *msg;
+-
+- if ((dst == NULL) || (rem == 0)) {
+- net_dbg_ratelimited("DECnet: dn_nsp_do_disc: BUG! Please report this to SteveW@ACM.org rem=%u dst=%p\n",
+- le16_to_cpu(rem), dst);
+- return;
+- }
+-
+- if ((skb = dn_alloc_skb(sk, size, gfp)) == NULL)
+- return;
+-
+- msg = skb_put(skb, size);
+- *msg++ = msgflg;
+- *(__le16 *)msg = rem;
+- msg += 2;
+- *(__le16 *)msg = loc;
+- msg += 2;
+- *(__le16 *)msg = cpu_to_le16(reason);
+- msg += 2;
+- if (msgflg == NSP_DISCINIT)
+- *msg++ = ddl;
+-
+- if (ddl) {
+- memcpy(msg, dd, ddl);
+- }
+-
+- /*
+- * This doesn't go via the dn_nsp_send() function since we need
+- * to be able to send disc packets out which have no socket
+- * associations.
+- */
+- skb_dst_set(skb, dst_clone(dst));
+- dst_output(&init_net, skb->sk, skb);
+-}
+-
+-
+-void dn_nsp_send_disc(struct sock *sk, unsigned char msgflg,
+- unsigned short reason, gfp_t gfp)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- int ddl = 0;
+-
+- if (msgflg == NSP_DISCINIT)
+- ddl = le16_to_cpu(scp->discdata_out.opt_optl);
+-
+- if (reason == 0)
+- reason = le16_to_cpu(scp->discdata_out.opt_status);
+-
+- dn_nsp_do_disc(sk, msgflg, reason, gfp, __sk_dst_get(sk), ddl,
+- scp->discdata_out.opt_data, scp->addrrem, scp->addrloc);
+-}
+-
+-
+-void dn_nsp_return_disc(struct sk_buff *skb, unsigned char msgflg,
+- unsigned short reason)
+-{
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- int ddl = 0;
+- gfp_t gfp = GFP_ATOMIC;
+-
+- dn_nsp_do_disc(NULL, msgflg, reason, gfp, skb_dst(skb), ddl,
+- NULL, cb->src_port, cb->dst_port);
+-}
+-
+-
+-void dn_nsp_send_link(struct sock *sk, unsigned char lsflags, char fcval)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- struct sk_buff *skb;
+- unsigned char *ptr;
+- gfp_t gfp = GFP_ATOMIC;
+-
+- if ((skb = dn_alloc_skb(sk, DN_MAX_NSP_DATA_HEADER + 2, gfp)) == NULL)
+- return;
+-
+- skb_reserve(skb, DN_MAX_NSP_DATA_HEADER);
+- ptr = skb_put(skb, 2);
+- DN_SKB_CB(skb)->nsp_flags = 0x10;
+- *ptr++ = lsflags;
+- *ptr = fcval;
+-
+- dn_nsp_queue_xmit(sk, skb, gfp, 1);
+-
+- scp->persist = dn_nsp_persist(sk);
+- scp->persist_fxn = dn_nsp_xmit_timeout;
+-}
+-
+-static int dn_nsp_retrans_conninit(struct sock *sk)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+-
+- if (scp->state == DN_CI)
+- dn_nsp_send_conninit(sk, NSP_RCI);
+-
+- return 0;
+-}
+-
+-void dn_nsp_send_conninit(struct sock *sk, unsigned char msgflg)
+-{
+- struct dn_scp *scp = DN_SK(sk);
+- struct nsp_conn_init_msg *msg;
+- unsigned char aux;
+- unsigned char menuver;
+- struct dn_skb_cb *cb;
+- unsigned char type = 1;
+- gfp_t allocation = (msgflg == NSP_CI) ? sk->sk_allocation : GFP_ATOMIC;
+- struct sk_buff *skb = dn_alloc_skb(sk, 200, allocation);
+-
+- if (!skb)
+- return;
+-
+- cb = DN_SKB_CB(skb);
+- msg = skb_put(skb, sizeof(*msg));
+-
+- msg->msgflg = msgflg;
+- msg->dstaddr = 0x0000; /* Remote Node will assign it*/
+-
+- msg->srcaddr = scp->addrloc;
+- msg->services = scp->services_loc; /* Requested flow control */
+- msg->info = scp->info_loc; /* Version Number */
+- msg->segsize = cpu_to_le16(scp->segsize_loc); /* Max segment size */
+-
+- if (scp->peer.sdn_objnum)
+- type = 0;
+-
+- skb_put(skb, dn_sockaddr2username(&scp->peer,
+- skb_tail_pointer(skb), type));
+- skb_put(skb, dn_sockaddr2username(&scp->addr,
+- skb_tail_pointer(skb), 2));
+-
+- menuver = DN_MENUVER_ACC | DN_MENUVER_USR;
+- if (scp->peer.sdn_flags & SDF_PROXY)
+- menuver |= DN_MENUVER_PRX;
+- if (scp->peer.sdn_flags & SDF_UICPROXY)
+- menuver |= DN_MENUVER_UIC;
+-
+- skb_put_u8(skb, menuver); /* Menu Version */
+-
+- aux = scp->accessdata.acc_userl;
+- skb_put_u8(skb, aux);
+- if (aux > 0)
+- skb_put_data(skb, scp->accessdata.acc_user, aux);
+-
+- aux = scp->accessdata.acc_passl;
+- skb_put_u8(skb, aux);
+- if (aux > 0)
+- skb_put_data(skb, scp->accessdata.acc_pass, aux);
+-
+- aux = scp->accessdata.acc_accl;
+- skb_put_u8(skb, aux);
+- if (aux > 0)
+- skb_put_data(skb, scp->accessdata.acc_acc, aux);
+-
+- aux = (__u8)le16_to_cpu(scp->conndata_out.opt_optl);
+- skb_put_u8(skb, aux);
+- if (aux > 0)
+- skb_put_data(skb, scp->conndata_out.opt_data, aux);
+-
+- scp->persist = dn_nsp_persist(sk);
+- scp->persist_fxn = dn_nsp_retrans_conninit;
+-
+- cb->rt_flags = DN_RT_F_RQR;
+-
+- dn_nsp_send(skb);
+-}
+-
+diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c
+deleted file mode 100644
+index ccc189bc36172..0000000000000
+--- a/net/decnet/dn_route.c
++++ /dev/null
+@@ -1,1942 +0,0 @@
+-/*
+- * DECnet An implementation of the DECnet protocol suite for the LINUX
+- * operating system. DECnet is implemented using the BSD Socket
+- * interface as the means of communication with the user level.
+- *
+- * DECnet Routing Functions (Endnode and Router)
+- *
+- * Authors: Steve Whitehouse <SteveW@ACM.org>
+- * Eduardo Marcelo Serrat <emserrat@geocities.com>
+- *
+- * Changes:
+- * Steve Whitehouse : Fixes to allow "intra-ethernet" and
+- * "return-to-sender" bits on outgoing
+- * packets.
+- * Steve Whitehouse : Timeouts for cached routes.
+- * Steve Whitehouse : Use dst cache for input routes too.
+- * Steve Whitehouse : Fixed error values in dn_send_skb.
+- * Steve Whitehouse : Rework routing functions to better fit
+- * DECnet routing design
+- * Alexey Kuznetsov : New SMP locking
+- * Steve Whitehouse : More SMP locking changes & dn_cache_dump()
+- * Steve Whitehouse : Prerouting NF hook, now really is prerouting.
+- * Fixed possible skb leak in rtnetlink funcs.
+- * Steve Whitehouse : Dave Miller's dynamic hash table sizing and
+- * Alexey Kuznetsov's finer grained locking
+- * from ipv4/route.c.
+- * Steve Whitehouse : Routing is now starting to look like a
+- * sensible set of code now, mainly due to
+- * my copying the IPv4 routing code. The
+- * hooks here are modified and will continue
+- * to evolve for a while.
+- * Steve Whitehouse : Real SMP at last :-) Also new netfilter
+- * stuff. Look out raw sockets your days
+- * are numbered!
+- * Steve Whitehouse : Added return-to-sender functions. Added
+- * backlog congestion level return codes.
+- * Steve Whitehouse : Fixed bug where routes were set up with
+- * no ref count on net devices.
+- * Steve Whitehouse : RCU for the route cache
+- * Steve Whitehouse : Preparations for the flow cache
+- * Steve Whitehouse : Prepare for nonlinear skbs
+- */
+-
+-/******************************************************************************
+- (c) 1995-1998 E.M. Serrat emserrat@geocities.com
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License as published by
+- the Free Software Foundation; either version 2 of the License, or
+- any later version.
+-
+- This program is distributed in the hope that it will be useful,
+- but WITHOUT ANY WARRANTY; without even the implied warranty of
+- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- GNU General Public License for more details.
+-*******************************************************************************/
+-
+-#include <linux/errno.h>
+-#include <linux/types.h>
+-#include <linux/socket.h>
+-#include <linux/in.h>
+-#include <linux/kernel.h>
+-#include <linux/sockios.h>
+-#include <linux/net.h>
+-#include <linux/netdevice.h>
+-#include <linux/inet.h>
+-#include <linux/route.h>
+-#include <linux/in_route.h>
+-#include <linux/slab.h>
+-#include <net/sock.h>
+-#include <linux/mm.h>
+-#include <linux/proc_fs.h>
+-#include <linux/seq_file.h>
+-#include <linux/init.h>
+-#include <linux/rtnetlink.h>
+-#include <linux/string.h>
+-#include <linux/netfilter_decnet.h>
+-#include <linux/rcupdate.h>
+-#include <linux/times.h>
+-#include <linux/export.h>
+-#include <asm/errno.h>
+-#include <net/net_namespace.h>
+-#include <net/netlink.h>
+-#include <net/neighbour.h>
+-#include <net/dst.h>
+-#include <net/flow.h>
+-#include <net/fib_rules.h>
+-#include <net/dn.h>
+-#include <net/dn_dev.h>
+-#include <net/dn_nsp.h>
+-#include <net/dn_route.h>
+-#include <net/dn_neigh.h>
+-#include <net/dn_fib.h>
+-
+-struct dn_rt_hash_bucket
+-{
+- struct dn_route __rcu *chain;
+- spinlock_t lock;
+-};
+-
+-extern struct neigh_table dn_neigh_table;
+-
+-
+-static unsigned char dn_hiord_addr[6] = {0xAA,0x00,0x04,0x00,0x00,0x00};
+-
+-static const int dn_rt_min_delay = 2 * HZ;
+-static const int dn_rt_max_delay = 10 * HZ;
+-static const int dn_rt_mtu_expires = 10 * 60 * HZ;
+-
+-static unsigned long dn_rt_deadline;
+-
+-static int dn_dst_gc(struct dst_ops *ops);
+-static struct dst_entry *dn_dst_check(struct dst_entry *, __u32);
+-static unsigned int dn_dst_default_advmss(const struct dst_entry *dst);
+-static unsigned int dn_dst_mtu(const struct dst_entry *dst);
+-static void dn_dst_destroy(struct dst_entry *);
+-static void dn_dst_ifdown(struct dst_entry *, struct net_device *dev, int how);
+-static struct dst_entry *dn_dst_negative_advice(struct dst_entry *);
+-static void dn_dst_link_failure(struct sk_buff *);
+-static void dn_dst_update_pmtu(struct dst_entry *dst, struct sock *sk,
+- struct sk_buff *skb , u32 mtu,
+- bool confirm_neigh);
+-static void dn_dst_redirect(struct dst_entry *dst, struct sock *sk,
+- struct sk_buff *skb);
+-static struct neighbour *dn_dst_neigh_lookup(const struct dst_entry *dst,
+- struct sk_buff *skb,
+- const void *daddr);
+-static int dn_route_input(struct sk_buff *);
+-static void dn_run_flush(unsigned long dummy);
+-
+-static struct dn_rt_hash_bucket *dn_rt_hash_table;
+-static unsigned int dn_rt_hash_mask;
+-
+-static struct timer_list dn_route_timer;
+-static DEFINE_TIMER(dn_rt_flush_timer, dn_run_flush, 0, 0);
+-int decnet_dst_gc_interval = 2;
+-
+-static struct dst_ops dn_dst_ops = {
+- .family = PF_DECnet,
+- .gc_thresh = 128,
+- .gc = dn_dst_gc,
+- .check = dn_dst_check,
+- .default_advmss = dn_dst_default_advmss,
+- .mtu = dn_dst_mtu,
+- .cow_metrics = dst_cow_metrics_generic,
+- .destroy = dn_dst_destroy,
+- .ifdown = dn_dst_ifdown,
+- .negative_advice = dn_dst_negative_advice,
+- .link_failure = dn_dst_link_failure,
+- .update_pmtu = dn_dst_update_pmtu,
+- .redirect = dn_dst_redirect,
+- .neigh_lookup = dn_dst_neigh_lookup,
+-};
+-
+-static void dn_dst_destroy(struct dst_entry *dst)
+-{
+- struct dn_route *rt = (struct dn_route *) dst;
+-
+- if (rt->n)
+- neigh_release(rt->n);
+- dst_destroy_metrics_generic(dst);
+-}
+-
+-static void dn_dst_ifdown(struct dst_entry *dst, struct net_device *dev, int how)
+-{
+- if (how) {
+- struct dn_route *rt = (struct dn_route *) dst;
+- struct neighbour *n = rt->n;
+-
+- if (n && n->dev == dev) {
+- n->dev = dev_net(dev)->loopback_dev;
+- dev_hold(n->dev);
+- dev_put(dev);
+- }
+- }
+-}
+-
+-static __inline__ unsigned int dn_hash(__le16 src, __le16 dst)
+-{
+- __u16 tmp = (__u16 __force)(src ^ dst);
+- tmp ^= (tmp >> 3);
+- tmp ^= (tmp >> 5);
+- tmp ^= (tmp >> 10);
+- return dn_rt_hash_mask & (unsigned int)tmp;
+-}
+-
+-static void dn_dst_check_expire(unsigned long dummy)
+-{
+- int i;
+- struct dn_route *rt;
+- struct dn_route __rcu **rtp;
+- unsigned long now = jiffies;
+- unsigned long expire = 120 * HZ;
+-
+- for (i = 0; i <= dn_rt_hash_mask; i++) {
+- rtp = &dn_rt_hash_table[i].chain;
+-
+- spin_lock(&dn_rt_hash_table[i].lock);
+- while ((rt = rcu_dereference_protected(*rtp,
+- lockdep_is_held(&dn_rt_hash_table[i].lock))) != NULL) {
+- if (atomic_read(&rt->dst.__refcnt) > 1 ||
+- (now - rt->dst.lastuse) < expire) {
+- rtp = &rt->dst.dn_next;
+- continue;
+- }
+- *rtp = rt->dst.dn_next;
+- rt->dst.dn_next = NULL;
+- dst_dev_put(&rt->dst);
+- dst_release(&rt->dst);
+- }
+- spin_unlock(&dn_rt_hash_table[i].lock);
+-
+- if ((jiffies - now) > 0)
+- break;
+- }
+-
+- mod_timer(&dn_route_timer, now + decnet_dst_gc_interval * HZ);
+-}
+-
+-static int dn_dst_gc(struct dst_ops *ops)
+-{
+- struct dn_route *rt;
+- struct dn_route __rcu **rtp;
+- int i;
+- unsigned long now = jiffies;
+- unsigned long expire = 10 * HZ;
+-
+- for (i = 0; i <= dn_rt_hash_mask; i++) {
+-
+- spin_lock_bh(&dn_rt_hash_table[i].lock);
+- rtp = &dn_rt_hash_table[i].chain;
+-
+- while ((rt = rcu_dereference_protected(*rtp,
+- lockdep_is_held(&dn_rt_hash_table[i].lock))) != NULL) {
+- if (atomic_read(&rt->dst.__refcnt) > 1 ||
+- (now - rt->dst.lastuse) < expire) {
+- rtp = &rt->dst.dn_next;
+- continue;
+- }
+- *rtp = rt->dst.dn_next;
+- rt->dst.dn_next = NULL;
+- dst_dev_put(&rt->dst);
+- dst_release(&rt->dst);
+- break;
+- }
+- spin_unlock_bh(&dn_rt_hash_table[i].lock);
+- }
+-
+- return 0;
+-}
+-
+-/*
+- * The decnet standards don't impose a particular minimum mtu, what they
+- * do insist on is that the routing layer accepts a datagram of at least
+- * 230 bytes long. Here we have to subtract the routing header length from
+- * 230 to get the minimum acceptable mtu. If there is no neighbour, then we
+- * assume the worst and use a long header size.
+- *
+- * We update both the mtu and the advertised mss (i.e. the segment size we
+- * advertise to the other end).
+- */
+-static void dn_dst_update_pmtu(struct dst_entry *dst, struct sock *sk,
+- struct sk_buff *skb, u32 mtu,
+- bool confirm_neigh)
+-{
+- struct dn_route *rt = (struct dn_route *) dst;
+- struct neighbour *n = rt->n;
+- u32 min_mtu = 230;
+- struct dn_dev *dn;
+-
+- dn = n ? rcu_dereference_raw(n->dev->dn_ptr) : NULL;
+-
+- if (dn && dn->use_long == 0)
+- min_mtu -= 6;
+- else
+- min_mtu -= 21;
+-
+- if (dst_metric(dst, RTAX_MTU) > mtu && mtu >= min_mtu) {
+- if (!(dst_metric_locked(dst, RTAX_MTU))) {
+- dst_metric_set(dst, RTAX_MTU, mtu);
+- dst_set_expires(dst, dn_rt_mtu_expires);
+- }
+- if (!(dst_metric_locked(dst, RTAX_ADVMSS))) {
+- u32 mss = mtu - DN_MAX_NSP_DATA_HEADER;
+- u32 existing_mss = dst_metric_raw(dst, RTAX_ADVMSS);
+- if (!existing_mss || existing_mss > mss)
+- dst_metric_set(dst, RTAX_ADVMSS, mss);
+- }
+- }
+-}
+-
+-static void dn_dst_redirect(struct dst_entry *dst, struct sock *sk,
+- struct sk_buff *skb)
+-{
+-}
+-
+-/*
+- * When a route has been marked obsolete. (e.g. routing cache flush)
+- */
+-static struct dst_entry *dn_dst_check(struct dst_entry *dst, __u32 cookie)
+-{
+- return NULL;
+-}
+-
+-static struct dst_entry *dn_dst_negative_advice(struct dst_entry *dst)
+-{
+- dst_release(dst);
+- return NULL;
+-}
+-
+-static void dn_dst_link_failure(struct sk_buff *skb)
+-{
+-}
+-
+-static inline int compare_keys(struct flowidn *fl1, struct flowidn *fl2)
+-{
+- return ((fl1->daddr ^ fl2->daddr) |
+- (fl1->saddr ^ fl2->saddr) |
+- (fl1->flowidn_mark ^ fl2->flowidn_mark) |
+- (fl1->flowidn_scope ^ fl2->flowidn_scope) |
+- (fl1->flowidn_oif ^ fl2->flowidn_oif) |
+- (fl1->flowidn_iif ^ fl2->flowidn_iif)) == 0;
+-}
+-
+-static int dn_insert_route(struct dn_route *rt, unsigned int hash, struct dn_route **rp)
+-{
+- struct dn_route *rth;
+- struct dn_route __rcu **rthp;
+- unsigned long now = jiffies;
+-
+- rthp = &dn_rt_hash_table[hash].chain;
+-
+- spin_lock_bh(&dn_rt_hash_table[hash].lock);
+- while ((rth = rcu_dereference_protected(*rthp,
+- lockdep_is_held(&dn_rt_hash_table[hash].lock))) != NULL) {
+- if (compare_keys(&rth->fld, &rt->fld)) {
+- /* Put it first */
+- *rthp = rth->dst.dn_next;
+- rcu_assign_pointer(rth->dst.dn_next,
+- dn_rt_hash_table[hash].chain);
+- rcu_assign_pointer(dn_rt_hash_table[hash].chain, rth);
+-
+- dst_use(&rth->dst, now);
+- spin_unlock_bh(&dn_rt_hash_table[hash].lock);
+-
+- dst_release_immediate(&rt->dst);
+- *rp = rth;
+- return 0;
+- }
+- rthp = &rth->dst.dn_next;
+- }
+-
+- rcu_assign_pointer(rt->dst.dn_next, dn_rt_hash_table[hash].chain);
+- rcu_assign_pointer(dn_rt_hash_table[hash].chain, rt);
+-
+- dst_use(&rt->dst, now);
+- spin_unlock_bh(&dn_rt_hash_table[hash].lock);
+- *rp = rt;
+- return 0;
+-}
+-
+-static void dn_run_flush(unsigned long dummy)
+-{
+- int i;
+- struct dn_route *rt, *next;
+-
+- for (i = 0; i < dn_rt_hash_mask; i++) {
+- spin_lock_bh(&dn_rt_hash_table[i].lock);
+-
+- if ((rt = xchg((struct dn_route **)&dn_rt_hash_table[i].chain, NULL)) == NULL)
+- goto nothing_to_declare;
+-
+- for(; rt; rt = next) {
+- next = rcu_dereference_raw(rt->dst.dn_next);
+- RCU_INIT_POINTER(rt->dst.dn_next, NULL);
+- dst_dev_put(&rt->dst);
+- dst_release(&rt->dst);
+- }
+-
+-nothing_to_declare:
+- spin_unlock_bh(&dn_rt_hash_table[i].lock);
+- }
+-}
+-
+-static DEFINE_SPINLOCK(dn_rt_flush_lock);
+-
+-void dn_rt_cache_flush(int delay)
+-{
+- unsigned long now = jiffies;
+- int user_mode = !in_interrupt();
+-
+- if (delay < 0)
+- delay = dn_rt_min_delay;
+-
+- spin_lock_bh(&dn_rt_flush_lock);
+-
+- if (del_timer(&dn_rt_flush_timer) && delay > 0 && dn_rt_deadline) {
+- long tmo = (long)(dn_rt_deadline - now);
+-
+- if (user_mode && tmo < dn_rt_max_delay - dn_rt_min_delay)
+- tmo = 0;
+-
+- if (delay > tmo)
+- delay = tmo;
+- }
+-
+- if (delay <= 0) {
+- spin_unlock_bh(&dn_rt_flush_lock);
+- dn_run_flush(0);
+- return;
+- }
+-
+- if (dn_rt_deadline == 0)
+- dn_rt_deadline = now + dn_rt_max_delay;
+-
+- dn_rt_flush_timer.expires = now + delay;
+- add_timer(&dn_rt_flush_timer);
+- spin_unlock_bh(&dn_rt_flush_lock);
+-}
+-
+-/**
+- * dn_return_short - Return a short packet to its sender
+- * @skb: The packet to return
+- *
+- */
+-static int dn_return_short(struct sk_buff *skb)
+-{
+- struct dn_skb_cb *cb;
+- unsigned char *ptr;
+- __le16 *src;
+- __le16 *dst;
+-
+- /* Add back headers */
+- skb_push(skb, skb->data - skb_network_header(skb));
+-
+- if ((skb = skb_unshare(skb, GFP_ATOMIC)) == NULL)
+- return NET_RX_DROP;
+-
+- cb = DN_SKB_CB(skb);
+- /* Skip packet length and point to flags */
+- ptr = skb->data + 2;
+- *ptr++ = (cb->rt_flags & ~DN_RT_F_RQR) | DN_RT_F_RTS;
+-
+- dst = (__le16 *)ptr;
+- ptr += 2;
+- src = (__le16 *)ptr;
+- ptr += 2;
+- *ptr = 0; /* Zero hop count */
+-
+- swap(*src, *dst);
+-
+- skb->pkt_type = PACKET_OUTGOING;
+- dn_rt_finish_output(skb, NULL, NULL);
+- return NET_RX_SUCCESS;
+-}
+-
+-/**
+- * dn_return_long - Return a long packet to its sender
+- * @skb: The long format packet to return
+- *
+- */
+-static int dn_return_long(struct sk_buff *skb)
+-{
+- struct dn_skb_cb *cb;
+- unsigned char *ptr;
+- unsigned char *src_addr, *dst_addr;
+- unsigned char tmp[ETH_ALEN];
+-
+- /* Add back all headers */
+- skb_push(skb, skb->data - skb_network_header(skb));
+-
+- if ((skb = skb_unshare(skb, GFP_ATOMIC)) == NULL)
+- return NET_RX_DROP;
+-
+- cb = DN_SKB_CB(skb);
+- /* Ignore packet length and point to flags */
+- ptr = skb->data + 2;
+-
+- /* Skip padding */
+- if (*ptr & DN_RT_F_PF) {
+- char padlen = (*ptr & ~DN_RT_F_PF);
+- ptr += padlen;
+- }
+-
+- *ptr++ = (cb->rt_flags & ~DN_RT_F_RQR) | DN_RT_F_RTS;
+- ptr += 2;
+- dst_addr = ptr;
+- ptr += 8;
+- src_addr = ptr;
+- ptr += 6;
+- *ptr = 0; /* Zero hop count */
+-
+- /* Swap source and destination */
+- memcpy(tmp, src_addr, ETH_ALEN);
+- memcpy(src_addr, dst_addr, ETH_ALEN);
+- memcpy(dst_addr, tmp, ETH_ALEN);
+-
+- skb->pkt_type = PACKET_OUTGOING;
+- dn_rt_finish_output(skb, dst_addr, src_addr);
+- return NET_RX_SUCCESS;
+-}
+-
+-/**
+- * dn_route_rx_packet - Try and find a route for an incoming packet
+- * @skb: The packet to find a route for
+- *
+- * Returns: result of input function if route is found, error code otherwise
+- */
+-static int dn_route_rx_packet(struct net *net, struct sock *sk, struct sk_buff *skb)
+-{
+- struct dn_skb_cb *cb;
+- int err;
+-
+- if ((err = dn_route_input(skb)) == 0)
+- return dst_input(skb);
+-
+- cb = DN_SKB_CB(skb);
+- if (decnet_debug_level & 4) {
+- char *devname = skb->dev ? skb->dev->name : "???";
+-
+- printk(KERN_DEBUG
+- "DECnet: dn_route_rx_packet: rt_flags=0x%02x dev=%s len=%d src=0x%04hx dst=0x%04hx err=%d type=%d\n",
+- (int)cb->rt_flags, devname, skb->len,
+- le16_to_cpu(cb->src), le16_to_cpu(cb->dst),
+- err, skb->pkt_type);
+- }
+-
+- if ((skb->pkt_type == PACKET_HOST) && (cb->rt_flags & DN_RT_F_RQR)) {
+- switch (cb->rt_flags & DN_RT_PKT_MSK) {
+- case DN_RT_PKT_SHORT:
+- return dn_return_short(skb);
+- case DN_RT_PKT_LONG:
+- return dn_return_long(skb);
+- }
+- }
+-
+- kfree_skb(skb);
+- return NET_RX_DROP;
+-}
+-
+-static int dn_route_rx_long(struct sk_buff *skb)
+-{
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- unsigned char *ptr = skb->data;
+-
+- if (!pskb_may_pull(skb, 21)) /* 20 for long header, 1 for shortest nsp */
+- goto drop_it;
+-
+- skb_pull(skb, 20);
+- skb_reset_transport_header(skb);
+-
+- /* Destination info */
+- ptr += 2;
+- cb->dst = dn_eth2dn(ptr);
+- if (memcmp(ptr, dn_hiord_addr, 4) != 0)
+- goto drop_it;
+- ptr += 6;
+-
+-
+- /* Source info */
+- ptr += 2;
+- cb->src = dn_eth2dn(ptr);
+- if (memcmp(ptr, dn_hiord_addr, 4) != 0)
+- goto drop_it;
+- ptr += 6;
+- /* Other junk */
+- ptr++;
+- cb->hops = *ptr++; /* Visit Count */
+-
+- return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING,
+- &init_net, NULL, skb, skb->dev, NULL,
+- dn_route_rx_packet);
+-
+-drop_it:
+- kfree_skb(skb);
+- return NET_RX_DROP;
+-}
+-
+-
+-
+-static int dn_route_rx_short(struct sk_buff *skb)
+-{
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- unsigned char *ptr = skb->data;
+-
+- if (!pskb_may_pull(skb, 6)) /* 5 for short header + 1 for shortest nsp */
+- goto drop_it;
+-
+- skb_pull(skb, 5);
+- skb_reset_transport_header(skb);
+-
+- cb->dst = *(__le16 *)ptr;
+- ptr += 2;
+- cb->src = *(__le16 *)ptr;
+- ptr += 2;
+- cb->hops = *ptr & 0x3f;
+-
+- return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING,
+- &init_net, NULL, skb, skb->dev, NULL,
+- dn_route_rx_packet);
+-
+-drop_it:
+- kfree_skb(skb);
+- return NET_RX_DROP;
+-}
+-
+-static int dn_route_discard(struct net *net, struct sock *sk, struct sk_buff *skb)
+-{
+- /*
+- * I know we drop the packet here, but thats considered success in
+- * this case
+- */
+- kfree_skb(skb);
+- return NET_RX_SUCCESS;
+-}
+-
+-static int dn_route_ptp_hello(struct net *net, struct sock *sk, struct sk_buff *skb)
+-{
+- dn_dev_hello(skb);
+- dn_neigh_pointopoint_hello(skb);
+- return NET_RX_SUCCESS;
+-}
+-
+-int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
+-{
+- struct dn_skb_cb *cb;
+- unsigned char flags = 0;
+- __u16 len = le16_to_cpu(*(__le16 *)skb->data);
+- struct dn_dev *dn = rcu_dereference(dev->dn_ptr);
+- unsigned char padlen = 0;
+-
+- if (!net_eq(dev_net(dev), &init_net))
+- goto dump_it;
+-
+- if (dn == NULL)
+- goto dump_it;
+-
+- if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
+- goto out;
+-
+- if (!pskb_may_pull(skb, 3))
+- goto dump_it;
+-
+- skb_pull(skb, 2);
+-
+- if (len > skb->len)
+- goto dump_it;
+-
+- skb_trim(skb, len);
+-
+- flags = *skb->data;
+-
+- cb = DN_SKB_CB(skb);
+- cb->stamp = jiffies;
+- cb->iif = dev->ifindex;
+-
+- /*
+- * If we have padding, remove it.
+- */
+- if (flags & DN_RT_F_PF) {
+- padlen = flags & ~DN_RT_F_PF;
+- if (!pskb_may_pull(skb, padlen + 1))
+- goto dump_it;
+- skb_pull(skb, padlen);
+- flags = *skb->data;
+- }
+-
+- skb_reset_network_header(skb);
+-
+- /*
+- * Weed out future version DECnet
+- */
+- if (flags & DN_RT_F_VER)
+- goto dump_it;
+-
+- cb->rt_flags = flags;
+-
+- if (decnet_debug_level & 1)
+- printk(KERN_DEBUG
+- "dn_route_rcv: got 0x%02x from %s [%d %d %d]\n",
+- (int)flags, (dev) ? dev->name : "???", len, skb->len,
+- padlen);
+-
+- if (flags & DN_RT_PKT_CNTL) {
+- if (unlikely(skb_linearize(skb)))
+- goto dump_it;
+-
+- switch (flags & DN_RT_CNTL_MSK) {
+- case DN_RT_PKT_INIT:
+- dn_dev_init_pkt(skb);
+- break;
+- case DN_RT_PKT_VERI:
+- dn_dev_veri_pkt(skb);
+- break;
+- }
+-
+- if (dn->parms.state != DN_DEV_S_RU)
+- goto dump_it;
+-
+- switch (flags & DN_RT_CNTL_MSK) {
+- case DN_RT_PKT_HELO:
+- return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
+- &init_net, NULL, skb, skb->dev, NULL,
+- dn_route_ptp_hello);
+-
+- case DN_RT_PKT_L1RT:
+- case DN_RT_PKT_L2RT:
+- return NF_HOOK(NFPROTO_DECNET, NF_DN_ROUTE,
+- &init_net, NULL, skb, skb->dev, NULL,
+- dn_route_discard);
+- case DN_RT_PKT_ERTH:
+- return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
+- &init_net, NULL, skb, skb->dev, NULL,
+- dn_neigh_router_hello);
+-
+- case DN_RT_PKT_EEDH:
+- return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
+- &init_net, NULL, skb, skb->dev, NULL,
+- dn_neigh_endnode_hello);
+- }
+- } else {
+- if (dn->parms.state != DN_DEV_S_RU)
+- goto dump_it;
+-
+- skb_pull(skb, 1); /* Pull flags */
+-
+- switch (flags & DN_RT_PKT_MSK) {
+- case DN_RT_PKT_LONG:
+- return dn_route_rx_long(skb);
+- case DN_RT_PKT_SHORT:
+- return dn_route_rx_short(skb);
+- }
+- }
+-
+-dump_it:
+- kfree_skb(skb);
+-out:
+- return NET_RX_DROP;
+-}
+-
+-static int dn_output(struct net *net, struct sock *sk, struct sk_buff *skb)
+-{
+- struct dst_entry *dst = skb_dst(skb);
+- struct dn_route *rt = (struct dn_route *)dst;
+- struct net_device *dev = dst->dev;
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+-
+- int err = -EINVAL;
+-
+- if (rt->n == NULL)
+- goto error;
+-
+- skb->dev = dev;
+-
+- cb->src = rt->rt_saddr;
+- cb->dst = rt->rt_daddr;
+-
+- /*
+- * Always set the Intra-Ethernet bit on all outgoing packets
+- * originated on this node. Only valid flag from upper layers
+- * is return-to-sender-requested. Set hop count to 0 too.
+- */
+- cb->rt_flags &= ~DN_RT_F_RQR;
+- cb->rt_flags |= DN_RT_F_IE;
+- cb->hops = 0;
+-
+- return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_OUT,
+- &init_net, sk, skb, NULL, dev,
+- dn_to_neigh_output);
+-
+-error:
+- net_dbg_ratelimited("dn_output: This should not happen\n");
+-
+- kfree_skb(skb);
+-
+- return err;
+-}
+-
+-static int dn_forward(struct sk_buff *skb)
+-{
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- struct dst_entry *dst = skb_dst(skb);
+- struct dn_dev *dn_db = rcu_dereference(dst->dev->dn_ptr);
+- struct dn_route *rt;
+- int header_len;
+- struct net_device *dev = skb->dev;
+-
+- if (skb->pkt_type != PACKET_HOST)
+- goto drop;
+-
+- /* Ensure that we have enough space for headers */
+- rt = (struct dn_route *)skb_dst(skb);
+- header_len = dn_db->use_long ? 21 : 6;
+- if (skb_cow(skb, LL_RESERVED_SPACE(rt->dst.dev)+header_len))
+- goto drop;
+-
+- /*
+- * Hop count exceeded.
+- */
+- if (++cb->hops > 30)
+- goto drop;
+-
+- skb->dev = rt->dst.dev;
+-
+- /*
+- * If packet goes out same interface it came in on, then set
+- * the Intra-Ethernet bit. This has no effect for short
+- * packets, so we don't need to test for them here.
+- */
+- cb->rt_flags &= ~DN_RT_F_IE;
+- if (rt->rt_flags & RTCF_DOREDIRECT)
+- cb->rt_flags |= DN_RT_F_IE;
+-
+- return NF_HOOK(NFPROTO_DECNET, NF_DN_FORWARD,
+- &init_net, NULL, skb, dev, skb->dev,
+- dn_to_neigh_output);
+-
+-drop:
+- kfree_skb(skb);
+- return NET_RX_DROP;
+-}
+-
+-/*
+- * Used to catch bugs. This should never normally get
+- * called.
+- */
+-static int dn_rt_bug_out(struct net *net, struct sock *sk, struct sk_buff *skb)
+-{
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+-
+- net_dbg_ratelimited("dn_rt_bug: skb from:%04x to:%04x\n",
+- le16_to_cpu(cb->src), le16_to_cpu(cb->dst));
+-
+- kfree_skb(skb);
+-
+- return NET_RX_DROP;
+-}
+-
+-static int dn_rt_bug(struct sk_buff *skb)
+-{
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+-
+- net_dbg_ratelimited("dn_rt_bug: skb from:%04x to:%04x\n",
+- le16_to_cpu(cb->src), le16_to_cpu(cb->dst));
+-
+- kfree_skb(skb);
+-
+- return NET_RX_DROP;
+-}
+-
+-static unsigned int dn_dst_default_advmss(const struct dst_entry *dst)
+-{
+- return dn_mss_from_pmtu(dst->dev, dst_mtu(dst));
+-}
+-
+-static unsigned int dn_dst_mtu(const struct dst_entry *dst)
+-{
+- unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
+-
+- return mtu ? : dst->dev->mtu;
+-}
+-
+-static struct neighbour *dn_dst_neigh_lookup(const struct dst_entry *dst,
+- struct sk_buff *skb,
+- const void *daddr)
+-{
+- return __neigh_lookup_errno(&dn_neigh_table, daddr, dst->dev);
+-}
+-
+-static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res)
+-{
+- struct dn_fib_info *fi = res->fi;
+- struct net_device *dev = rt->dst.dev;
+- unsigned int mss_metric;
+- struct neighbour *n;
+-
+- if (fi) {
+- if (DN_FIB_RES_GW(*res) &&
+- DN_FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK)
+- rt->rt_gateway = DN_FIB_RES_GW(*res);
+- dst_init_metrics(&rt->dst, fi->fib_metrics, true);
+- }
+- rt->rt_type = res->type;
+-
+- if (dev != NULL && rt->n == NULL) {
+- n = __neigh_lookup_errno(&dn_neigh_table, &rt->rt_gateway, dev);
+- if (IS_ERR(n))
+- return PTR_ERR(n);
+- rt->n = n;
+- }
+-
+- if (dst_metric(&rt->dst, RTAX_MTU) > rt->dst.dev->mtu)
+- dst_metric_set(&rt->dst, RTAX_MTU, rt->dst.dev->mtu);
+- mss_metric = dst_metric_raw(&rt->dst, RTAX_ADVMSS);
+- if (mss_metric) {
+- unsigned int mss = dn_mss_from_pmtu(dev, dst_mtu(&rt->dst));
+- if (mss_metric > mss)
+- dst_metric_set(&rt->dst, RTAX_ADVMSS, mss);
+- }
+- return 0;
+-}
+-
+-static inline int dn_match_addr(__le16 addr1, __le16 addr2)
+-{
+- __u16 tmp = le16_to_cpu(addr1) ^ le16_to_cpu(addr2);
+- int match = 16;
+- while(tmp) {
+- tmp >>= 1;
+- match--;
+- }
+- return match;
+-}
+-
+-static __le16 dnet_select_source(const struct net_device *dev, __le16 daddr, int scope)
+-{
+- __le16 saddr = 0;
+- struct dn_dev *dn_db;
+- struct dn_ifaddr *ifa;
+- int best_match = 0;
+- int ret;
+-
+- rcu_read_lock();
+- dn_db = rcu_dereference(dev->dn_ptr);
+- for (ifa = rcu_dereference(dn_db->ifa_list);
+- ifa != NULL;
+- ifa = rcu_dereference(ifa->ifa_next)) {
+- if (ifa->ifa_scope > scope)
+- continue;
+- if (!daddr) {
+- saddr = ifa->ifa_local;
+- break;
+- }
+- ret = dn_match_addr(daddr, ifa->ifa_local);
+- if (ret > best_match)
+- saddr = ifa->ifa_local;
+- if (best_match == 0)
+- saddr = ifa->ifa_local;
+- }
+- rcu_read_unlock();
+-
+- return saddr;
+-}
+-
+-static inline __le16 __dn_fib_res_prefsrc(struct dn_fib_res *res)
+-{
+- return dnet_select_source(DN_FIB_RES_DEV(*res), DN_FIB_RES_GW(*res), res->scope);
+-}
+-
+-static inline __le16 dn_fib_rules_map_destination(__le16 daddr, struct dn_fib_res *res)
+-{
+- __le16 mask = dnet_make_mask(res->prefixlen);
+- return (daddr&~mask)|res->fi->fib_nh->nh_gw;
+-}
+-
+-static int dn_route_output_slow(struct dst_entry **pprt, const struct flowidn *oldflp, int try_hard)
+-{
+- struct flowidn fld = {
+- .daddr = oldflp->daddr,
+- .saddr = oldflp->saddr,
+- .flowidn_scope = RT_SCOPE_UNIVERSE,
+- .flowidn_mark = oldflp->flowidn_mark,
+- .flowidn_iif = LOOPBACK_IFINDEX,
+- .flowidn_oif = oldflp->flowidn_oif,
+- };
+- struct dn_route *rt = NULL;
+- struct net_device *dev_out = NULL, *dev;
+- struct neighbour *neigh = NULL;
+- unsigned int hash;
+- unsigned int flags = 0;
+- struct dn_fib_res res = { .fi = NULL, .type = RTN_UNICAST };
+- int err;
+- int free_res = 0;
+- __le16 gateway = 0;
+-
+- if (decnet_debug_level & 16)
+- printk(KERN_DEBUG
+- "dn_route_output_slow: dst=%04x src=%04x mark=%d"
+- " iif=%d oif=%d\n", le16_to_cpu(oldflp->daddr),
+- le16_to_cpu(oldflp->saddr),
+- oldflp->flowidn_mark, LOOPBACK_IFINDEX,
+- oldflp->flowidn_oif);
+-
+- /* If we have an output interface, verify its a DECnet device */
+- if (oldflp->flowidn_oif) {
+- dev_out = dev_get_by_index(&init_net, oldflp->flowidn_oif);
+- err = -ENODEV;
+- if (dev_out && dev_out->dn_ptr == NULL) {
+- dev_put(dev_out);
+- dev_out = NULL;
+- }
+- if (dev_out == NULL)
+- goto out;
+- }
+-
+- /* If we have a source address, verify that its a local address */
+- if (oldflp->saddr) {
+- err = -EADDRNOTAVAIL;
+-
+- if (dev_out) {
+- if (dn_dev_islocal(dev_out, oldflp->saddr))
+- goto source_ok;
+- dev_put(dev_out);
+- goto out;
+- }
+- rcu_read_lock();
+- for_each_netdev_rcu(&init_net, dev) {
+- if (!dev->dn_ptr)
+- continue;
+- if (!dn_dev_islocal(dev, oldflp->saddr))
+- continue;
+- if ((dev->flags & IFF_LOOPBACK) &&
+- oldflp->daddr &&
+- !dn_dev_islocal(dev, oldflp->daddr))
+- continue;
+-
+- dev_out = dev;
+- break;
+- }
+- rcu_read_unlock();
+- if (dev_out == NULL)
+- goto out;
+- dev_hold(dev_out);
+-source_ok:
+- ;
+- }
+-
+- /* No destination? Assume its local */
+- if (!fld.daddr) {
+- fld.daddr = fld.saddr;
+-
+- if (dev_out)
+- dev_put(dev_out);
+- err = -EINVAL;
+- dev_out = init_net.loopback_dev;
+- if (!dev_out->dn_ptr)
+- goto out;
+- err = -EADDRNOTAVAIL;
+- dev_hold(dev_out);
+- if (!fld.daddr) {
+- fld.daddr =
+- fld.saddr = dnet_select_source(dev_out, 0,
+- RT_SCOPE_HOST);
+- if (!fld.daddr)
+- goto out;
+- }
+- fld.flowidn_oif = LOOPBACK_IFINDEX;
+- res.type = RTN_LOCAL;
+- goto make_route;
+- }
+-
+- if (decnet_debug_level & 16)
+- printk(KERN_DEBUG
+- "dn_route_output_slow: initial checks complete."
+- " dst=%04x src=%04x oif=%d try_hard=%d\n",
+- le16_to_cpu(fld.daddr), le16_to_cpu(fld.saddr),
+- fld.flowidn_oif, try_hard);
+-
+- /*
+- * N.B. If the kernel is compiled without router support then
+- * dn_fib_lookup() will evaluate to non-zero so this if () block
+- * will always be executed.
+- */
+- err = -ESRCH;
+- if (try_hard || (err = dn_fib_lookup(&fld, &res)) != 0) {
+- struct dn_dev *dn_db;
+- if (err != -ESRCH)
+- goto out;
+- /*
+- * Here the fallback is basically the standard algorithm for
+- * routing in endnodes which is described in the DECnet routing
+- * docs
+- *
+- * If we are not trying hard, look in neighbour cache.
+- * The result is tested to ensure that if a specific output
+- * device/source address was requested, then we honour that
+- * here
+- */
+- if (!try_hard) {
+- neigh = neigh_lookup_nodev(&dn_neigh_table, &init_net, &fld.daddr);
+- if (neigh) {
+- if ((oldflp->flowidn_oif &&
+- (neigh->dev->ifindex != oldflp->flowidn_oif)) ||
+- (oldflp->saddr &&
+- (!dn_dev_islocal(neigh->dev,
+- oldflp->saddr)))) {
+- neigh_release(neigh);
+- neigh = NULL;
+- } else {
+- if (dev_out)
+- dev_put(dev_out);
+- if (dn_dev_islocal(neigh->dev, fld.daddr)) {
+- dev_out = init_net.loopback_dev;
+- res.type = RTN_LOCAL;
+- } else {
+- dev_out = neigh->dev;
+- }
+- dev_hold(dev_out);
+- goto select_source;
+- }
+- }
+- }
+-
+- /* Not there? Perhaps its a local address */
+- if (dev_out == NULL)
+- dev_out = dn_dev_get_default();
+- err = -ENODEV;
+- if (dev_out == NULL)
+- goto out;
+- dn_db = rcu_dereference_raw(dev_out->dn_ptr);
+- if (!dn_db)
+- goto e_inval;
+- /* Possible improvement - check all devices for local addr */
+- if (dn_dev_islocal(dev_out, fld.daddr)) {
+- dev_put(dev_out);
+- dev_out = init_net.loopback_dev;
+- dev_hold(dev_out);
+- res.type = RTN_LOCAL;
+- goto select_source;
+- }
+- /* Not local either.... try sending it to the default router */
+- neigh = neigh_clone(dn_db->router);
+- BUG_ON(neigh && neigh->dev != dev_out);
+-
+- /* Ok then, we assume its directly connected and move on */
+-select_source:
+- if (neigh)
+- gateway = ((struct dn_neigh *)neigh)->addr;
+- if (gateway == 0)
+- gateway = fld.daddr;
+- if (fld.saddr == 0) {
+- fld.saddr = dnet_select_source(dev_out, gateway,
+- res.type == RTN_LOCAL ?
+- RT_SCOPE_HOST :
+- RT_SCOPE_LINK);
+- if (fld.saddr == 0 && res.type != RTN_LOCAL)
+- goto e_addr;
+- }
+- fld.flowidn_oif = dev_out->ifindex;
+- goto make_route;
+- }
+- free_res = 1;
+-
+- if (res.type == RTN_NAT)
+- goto e_inval;
+-
+- if (res.type == RTN_LOCAL) {
+- if (!fld.saddr)
+- fld.saddr = fld.daddr;
+- if (dev_out)
+- dev_put(dev_out);
+- dev_out = init_net.loopback_dev;
+- dev_hold(dev_out);
+- if (!dev_out->dn_ptr)
+- goto e_inval;
+- fld.flowidn_oif = dev_out->ifindex;
+- if (res.fi)
+- dn_fib_info_put(res.fi);
+- res.fi = NULL;
+- goto make_route;
+- }
+-
+- if (res.fi->fib_nhs > 1 && fld.flowidn_oif == 0)
+- dn_fib_select_multipath(&fld, &res);
+-
+- /*
+- * We could add some logic to deal with default routes here and
+- * get rid of some of the special casing above.
+- */
+-
+- if (!fld.saddr)
+- fld.saddr = DN_FIB_RES_PREFSRC(res);
+-
+- if (dev_out)
+- dev_put(dev_out);
+- dev_out = DN_FIB_RES_DEV(res);
+- dev_hold(dev_out);
+- fld.flowidn_oif = dev_out->ifindex;
+- gateway = DN_FIB_RES_GW(res);
+-
+-make_route:
+- if (dev_out->flags & IFF_LOOPBACK)
+- flags |= RTCF_LOCAL;
+-
+- rt = dst_alloc(&dn_dst_ops, dev_out, 0, DST_OBSOLETE_NONE, DST_HOST);
+- if (rt == NULL)
+- goto e_nobufs;
+-
+- memset(&rt->fld, 0, sizeof(rt->fld));
+- rt->fld.saddr = oldflp->saddr;
+- rt->fld.daddr = oldflp->daddr;
+- rt->fld.flowidn_oif = oldflp->flowidn_oif;
+- rt->fld.flowidn_iif = 0;
+- rt->fld.flowidn_mark = oldflp->flowidn_mark;
+-
+- rt->rt_saddr = fld.saddr;
+- rt->rt_daddr = fld.daddr;
+- rt->rt_gateway = gateway ? gateway : fld.daddr;
+- rt->rt_local_src = fld.saddr;
+-
+- rt->rt_dst_map = fld.daddr;
+- rt->rt_src_map = fld.saddr;
+-
+- rt->n = neigh;
+- neigh = NULL;
+-
+- rt->dst.lastuse = jiffies;
+- rt->dst.output = dn_output;
+- rt->dst.input = dn_rt_bug;
+- rt->rt_flags = flags;
+- if (flags & RTCF_LOCAL)
+- rt->dst.input = dn_nsp_rx;
+-
+- err = dn_rt_set_next_hop(rt, &res);
+- if (err)
+- goto e_neighbour;
+-
+- hash = dn_hash(rt->fld.saddr, rt->fld.daddr);
+- /* dn_insert_route() increments dst->__refcnt */
+- dn_insert_route(rt, hash, (struct dn_route **)pprt);
+-
+-done:
+- if (neigh)
+- neigh_release(neigh);
+- if (free_res)
+- dn_fib_res_put(&res);
+- if (dev_out)
+- dev_put(dev_out);
+-out:
+- return err;
+-
+-e_addr:
+- err = -EADDRNOTAVAIL;
+- goto done;
+-e_inval:
+- err = -EINVAL;
+- goto done;
+-e_nobufs:
+- err = -ENOBUFS;
+- goto done;
+-e_neighbour:
+- dst_release_immediate(&rt->dst);
+- goto e_nobufs;
+-}
+-
+-
+-/*
+- * N.B. The flags may be moved into the flowi at some future stage.
+- */
+-static int __dn_route_output_key(struct dst_entry **pprt, const struct flowidn *flp, int flags)
+-{
+- unsigned int hash = dn_hash(flp->saddr, flp->daddr);
+- struct dn_route *rt = NULL;
+-
+- if (!(flags & MSG_TRYHARD)) {
+- rcu_read_lock_bh();
+- for (rt = rcu_dereference_bh(dn_rt_hash_table[hash].chain); rt;
+- rt = rcu_dereference_bh(rt->dst.dn_next)) {
+- if ((flp->daddr == rt->fld.daddr) &&
+- (flp->saddr == rt->fld.saddr) &&
+- (flp->flowidn_mark == rt->fld.flowidn_mark) &&
+- dn_is_output_route(rt) &&
+- (rt->fld.flowidn_oif == flp->flowidn_oif)) {
+- dst_use(&rt->dst, jiffies);
+- rcu_read_unlock_bh();
+- *pprt = &rt->dst;
+- return 0;
+- }
+- }
+- rcu_read_unlock_bh();
+- }
+-
+- return dn_route_output_slow(pprt, flp, flags);
+-}
+-
+-static int dn_route_output_key(struct dst_entry **pprt, struct flowidn *flp, int flags)
+-{
+- int err;
+-
+- err = __dn_route_output_key(pprt, flp, flags);
+- if (err == 0 && flp->flowidn_proto) {
+- *pprt = xfrm_lookup(&init_net, *pprt,
+- flowidn_to_flowi(flp), NULL, 0);
+- if (IS_ERR(*pprt)) {
+- err = PTR_ERR(*pprt);
+- *pprt = NULL;
+- }
+- }
+- return err;
+-}
+-
+-int dn_route_output_sock(struct dst_entry __rcu **pprt, struct flowidn *fl, struct sock *sk, int flags)
+-{
+- int err;
+-
+- err = __dn_route_output_key(pprt, fl, flags & MSG_TRYHARD);
+- if (err == 0 && fl->flowidn_proto) {
+- *pprt = xfrm_lookup(&init_net, *pprt,
+- flowidn_to_flowi(fl), sk, 0);
+- if (IS_ERR(*pprt)) {
+- err = PTR_ERR(*pprt);
+- *pprt = NULL;
+- }
+- }
+- return err;
+-}
+-
+-static int dn_route_input_slow(struct sk_buff *skb)
+-{
+- struct dn_route *rt = NULL;
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- struct net_device *in_dev = skb->dev;
+- struct net_device *out_dev = NULL;
+- struct dn_dev *dn_db;
+- struct neighbour *neigh = NULL;
+- unsigned int hash;
+- int flags = 0;
+- __le16 gateway = 0;
+- __le16 local_src = 0;
+- struct flowidn fld = {
+- .daddr = cb->dst,
+- .saddr = cb->src,
+- .flowidn_scope = RT_SCOPE_UNIVERSE,
+- .flowidn_mark = skb->mark,
+- .flowidn_iif = skb->dev->ifindex,
+- };
+- struct dn_fib_res res = { .fi = NULL, .type = RTN_UNREACHABLE };
+- int err = -EINVAL;
+- int free_res = 0;
+-
+- dev_hold(in_dev);
+-
+- if ((dn_db = rcu_dereference(in_dev->dn_ptr)) == NULL)
+- goto out;
+-
+- /* Zero source addresses are not allowed */
+- if (fld.saddr == 0)
+- goto out;
+-
+- /*
+- * In this case we've just received a packet from a source
+- * outside ourselves pretending to come from us. We don't
+- * allow it any further to prevent routing loops, spoofing and
+- * other nasties. Loopback packets already have the dst attached
+- * so this only affects packets which have originated elsewhere.
+- */
+- err = -ENOTUNIQ;
+- if (dn_dev_islocal(in_dev, cb->src))
+- goto out;
+-
+- err = dn_fib_lookup(&fld, &res);
+- if (err) {
+- if (err != -ESRCH)
+- goto out;
+- /*
+- * Is the destination us ?
+- */
+- if (!dn_dev_islocal(in_dev, cb->dst))
+- goto e_inval;
+-
+- res.type = RTN_LOCAL;
+- } else {
+- __le16 src_map = fld.saddr;
+- free_res = 1;
+-
+- out_dev = DN_FIB_RES_DEV(res);
+- if (out_dev == NULL) {
+- net_crit_ratelimited("Bug in dn_route_input_slow() No output device\n");
+- goto e_inval;
+- }
+- dev_hold(out_dev);
+-
+- if (res.r)
+- src_map = fld.saddr; /* no NAT support for now */
+-
+- gateway = DN_FIB_RES_GW(res);
+- if (res.type == RTN_NAT) {
+- fld.daddr = dn_fib_rules_map_destination(fld.daddr, &res);
+- dn_fib_res_put(&res);
+- free_res = 0;
+- if (dn_fib_lookup(&fld, &res))
+- goto e_inval;
+- free_res = 1;
+- if (res.type != RTN_UNICAST)
+- goto e_inval;
+- flags |= RTCF_DNAT;
+- gateway = fld.daddr;
+- }
+- fld.saddr = src_map;
+- }
+-
+- switch(res.type) {
+- case RTN_UNICAST:
+- /*
+- * Forwarding check here, we only check for forwarding
+- * being turned off, if you want to only forward intra
+- * area, its up to you to set the routing tables up
+- * correctly.
+- */
+- if (dn_db->parms.forwarding == 0)
+- goto e_inval;
+-
+- if (res.fi->fib_nhs > 1 && fld.flowidn_oif == 0)
+- dn_fib_select_multipath(&fld, &res);
+-
+- /*
+- * Check for out_dev == in_dev. We use the RTCF_DOREDIRECT
+- * flag as a hint to set the intra-ethernet bit when
+- * forwarding. If we've got NAT in operation, we don't do
+- * this optimisation.
+- */
+- if (out_dev == in_dev && !(flags & RTCF_NAT))
+- flags |= RTCF_DOREDIRECT;
+-
+- local_src = DN_FIB_RES_PREFSRC(res);
+-
+- case RTN_BLACKHOLE:
+- case RTN_UNREACHABLE:
+- break;
+- case RTN_LOCAL:
+- flags |= RTCF_LOCAL;
+- fld.saddr = cb->dst;
+- fld.daddr = cb->src;
+-
+- /* Routing tables gave us a gateway */
+- if (gateway)
+- goto make_route;
+-
+- /* Packet was intra-ethernet, so we know its on-link */
+- if (cb->rt_flags & DN_RT_F_IE) {
+- gateway = cb->src;
+- goto make_route;
+- }
+-
+- /* Use the default router if there is one */
+- neigh = neigh_clone(dn_db->router);
+- if (neigh) {
+- gateway = ((struct dn_neigh *)neigh)->addr;
+- goto make_route;
+- }
+-
+- /* Close eyes and pray */
+- gateway = cb->src;
+- goto make_route;
+- default:
+- goto e_inval;
+- }
+-
+-make_route:
+- rt = dst_alloc(&dn_dst_ops, out_dev, 1, DST_OBSOLETE_NONE, DST_HOST);
+- if (rt == NULL)
+- goto e_nobufs;
+-
+- memset(&rt->fld, 0, sizeof(rt->fld));
+- rt->rt_saddr = fld.saddr;
+- rt->rt_daddr = fld.daddr;
+- rt->rt_gateway = fld.daddr;
+- if (gateway)
+- rt->rt_gateway = gateway;
+- rt->rt_local_src = local_src ? local_src : rt->rt_saddr;
+-
+- rt->rt_dst_map = fld.daddr;
+- rt->rt_src_map = fld.saddr;
+-
+- rt->fld.saddr = cb->src;
+- rt->fld.daddr = cb->dst;
+- rt->fld.flowidn_oif = 0;
+- rt->fld.flowidn_iif = in_dev->ifindex;
+- rt->fld.flowidn_mark = fld.flowidn_mark;
+-
+- rt->n = neigh;
+- rt->dst.lastuse = jiffies;
+- rt->dst.output = dn_rt_bug_out;
+- switch (res.type) {
+- case RTN_UNICAST:
+- rt->dst.input = dn_forward;
+- break;
+- case RTN_LOCAL:
+- rt->dst.output = dn_output;
+- rt->dst.input = dn_nsp_rx;
+- rt->dst.dev = in_dev;
+- flags |= RTCF_LOCAL;
+- break;
+- default:
+- case RTN_UNREACHABLE:
+- case RTN_BLACKHOLE:
+- rt->dst.input = dst_discard;
+- }
+- rt->rt_flags = flags;
+-
+- err = dn_rt_set_next_hop(rt, &res);
+- if (err)
+- goto e_neighbour;
+-
+- hash = dn_hash(rt->fld.saddr, rt->fld.daddr);
+- /* dn_insert_route() increments dst->__refcnt */
+- dn_insert_route(rt, hash, &rt);
+- skb_dst_set(skb, &rt->dst);
+-
+-done:
+- if (neigh)
+- neigh_release(neigh);
+- if (free_res)
+- dn_fib_res_put(&res);
+- dev_put(in_dev);
+- if (out_dev)
+- dev_put(out_dev);
+-out:
+- return err;
+-
+-e_inval:
+- err = -EINVAL;
+- goto done;
+-
+-e_nobufs:
+- err = -ENOBUFS;
+- goto done;
+-
+-e_neighbour:
+- dst_release_immediate(&rt->dst);
+- goto done;
+-}
+-
+-static int dn_route_input(struct sk_buff *skb)
+-{
+- struct dn_route *rt;
+- struct dn_skb_cb *cb = DN_SKB_CB(skb);
+- unsigned int hash = dn_hash(cb->src, cb->dst);
+-
+- if (skb_dst(skb))
+- return 0;
+-
+- rcu_read_lock();
+- for(rt = rcu_dereference(dn_rt_hash_table[hash].chain); rt != NULL;
+- rt = rcu_dereference(rt->dst.dn_next)) {
+- if ((rt->fld.saddr == cb->src) &&
+- (rt->fld.daddr == cb->dst) &&
+- (rt->fld.flowidn_oif == 0) &&
+- (rt->fld.flowidn_mark == skb->mark) &&
+- (rt->fld.flowidn_iif == cb->iif)) {
+- dst_use(&rt->dst, jiffies);
+- rcu_read_unlock();
+- skb_dst_set(skb, (struct dst_entry *)rt);
+- return 0;
+- }
+- }
+- rcu_read_unlock();
+-
+- return dn_route_input_slow(skb);
+-}
+-
+-static int dn_rt_fill_info(struct sk_buff *skb, u32 portid, u32 seq,
+- int event, int nowait, unsigned int flags)
+-{
+- struct dn_route *rt = (struct dn_route *)skb_dst(skb);
+- struct rtmsg *r;
+- struct nlmsghdr *nlh;
+- long expires;
+-
+- nlh = nlmsg_put(skb, portid, seq, event, sizeof(*r), flags);
+- if (!nlh)
+- return -EMSGSIZE;
+-
+- r = nlmsg_data(nlh);
+- r->rtm_family = AF_DECnet;
+- r->rtm_dst_len = 16;
+- r->rtm_src_len = 0;
+- r->rtm_tos = 0;
+- r->rtm_table = RT_TABLE_MAIN;
+- r->rtm_type = rt->rt_type;
+- r->rtm_flags = (rt->rt_flags & ~0xFFFF) | RTM_F_CLONED;
+- r->rtm_scope = RT_SCOPE_UNIVERSE;
+- r->rtm_protocol = RTPROT_UNSPEC;
+-
+- if (rt->rt_flags & RTCF_NOTIFY)
+- r->rtm_flags |= RTM_F_NOTIFY;
+-
+- if (nla_put_u32(skb, RTA_TABLE, RT_TABLE_MAIN) < 0 ||
+- nla_put_le16(skb, RTA_DST, rt->rt_daddr) < 0)
+- goto errout;
+-
+- if (rt->fld.saddr) {
+- r->rtm_src_len = 16;
+- if (nla_put_le16(skb, RTA_SRC, rt->fld.saddr) < 0)
+- goto errout;
+- }
+- if (rt->dst.dev &&
+- nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex) < 0)
+- goto errout;
+-
+- /*
+- * Note to self - change this if input routes reverse direction when
+- * they deal only with inputs and not with replies like they do
+- * currently.
+- */
+- if (nla_put_le16(skb, RTA_PREFSRC, rt->rt_local_src) < 0)
+- goto errout;
+-
+- if (rt->rt_daddr != rt->rt_gateway &&
+- nla_put_le16(skb, RTA_GATEWAY, rt->rt_gateway) < 0)
+- goto errout;
+-
+- if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
+- goto errout;
+-
+- expires = rt->dst.expires ? rt->dst.expires - jiffies : 0;
+- if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires,
+- rt->dst.error) < 0)
+- goto errout;
+-
+- if (dn_is_input_route(rt) &&
+- nla_put_u32(skb, RTA_IIF, rt->fld.flowidn_iif) < 0)
+- goto errout;
+-
+- nlmsg_end(skb, nlh);
+- return 0;
+-
+-errout:
+- nlmsg_cancel(skb, nlh);
+- return -EMSGSIZE;
+-}
+-
+-const struct nla_policy rtm_dn_policy[RTA_MAX + 1] = {
+- [RTA_DST] = { .type = NLA_U16 },
+- [RTA_SRC] = { .type = NLA_U16 },
+- [RTA_IIF] = { .type = NLA_U32 },
+- [RTA_OIF] = { .type = NLA_U32 },
+- [RTA_GATEWAY] = { .type = NLA_U16 },
+- [RTA_PRIORITY] = { .type = NLA_U32 },
+- [RTA_PREFSRC] = { .type = NLA_U16 },
+- [RTA_METRICS] = { .type = NLA_NESTED },
+- [RTA_MULTIPATH] = { .type = NLA_NESTED },
+- [RTA_TABLE] = { .type = NLA_U32 },
+- [RTA_MARK] = { .type = NLA_U32 },
+-};
+-
+-/*
+- * This is called by both endnodes and routers now.
+- */
+-static int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
+- struct netlink_ext_ack *extack)
+-{
+- struct net *net = sock_net(in_skb->sk);
+- struct rtmsg *rtm = nlmsg_data(nlh);
+- struct dn_route *rt = NULL;
+- struct dn_skb_cb *cb;
+- int err;
+- struct sk_buff *skb;
+- struct flowidn fld;
+- struct nlattr *tb[RTA_MAX+1];
+-
+- if (!net_eq(net, &init_net))
+- return -EINVAL;
+-
+- err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_dn_policy,
+- extack);
+- if (err < 0)
+- return err;
+-
+- memset(&fld, 0, sizeof(fld));
+- fld.flowidn_proto = DNPROTO_NSP;
+-
+- skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+- if (skb == NULL)
+- return -ENOBUFS;
+- skb_reset_mac_header(skb);
+- cb = DN_SKB_CB(skb);
+-
+- if (tb[RTA_SRC])
+- fld.saddr = nla_get_le16(tb[RTA_SRC]);
+-
+- if (tb[RTA_DST])
+- fld.daddr = nla_get_le16(tb[RTA_DST]);
+-
+- if (tb[RTA_IIF])
+- fld.flowidn_iif = nla_get_u32(tb[RTA_IIF]);
+-
+- if (fld.flowidn_iif) {
+- struct net_device *dev;
+- dev = __dev_get_by_index(&init_net, fld.flowidn_iif);
+- if (!dev || !dev->dn_ptr) {
+- kfree_skb(skb);
+- return -ENODEV;
+- }
+- skb->protocol = htons(ETH_P_DNA_RT);
+- skb->dev = dev;
+- cb->src = fld.saddr;
+- cb->dst = fld.daddr;
+- local_bh_disable();
+- err = dn_route_input(skb);
+- local_bh_enable();
+- memset(cb, 0, sizeof(struct dn_skb_cb));
+- rt = (struct dn_route *)skb_dst(skb);
+- if (!err && -rt->dst.error)
+- err = rt->dst.error;
+- } else {
+- if (tb[RTA_OIF])
+- fld.flowidn_oif = nla_get_u32(tb[RTA_OIF]);
+-
+- err = dn_route_output_key((struct dst_entry **)&rt, &fld, 0);
+- }
+-
+- skb->dev = NULL;
+- if (err)
+- goto out_free;
+- skb_dst_set(skb, &rt->dst);
+- if (rtm->rtm_flags & RTM_F_NOTIFY)
+- rt->rt_flags |= RTCF_NOTIFY;
+-
+- err = dn_rt_fill_info(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, RTM_NEWROUTE, 0, 0);
+- if (err < 0) {
+- err = -EMSGSIZE;
+- goto out_free;
+- }
+-
+- return rtnl_unicast(skb, &init_net, NETLINK_CB(in_skb).portid);
+-
+-out_free:
+- kfree_skb(skb);
+- return err;
+-}
+-
+-/*
+- * For routers, this is called from dn_fib_dump, but for endnodes its
+- * called directly from the rtnetlink dispatch table.
+- */
+-int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb)
+-{
+- struct net *net = sock_net(skb->sk);
+- struct dn_route *rt;
+- int h, s_h;
+- int idx, s_idx;
+- struct rtmsg *rtm;
+-
+- if (!net_eq(net, &init_net))
+- return 0;
+-
+- if (nlmsg_len(cb->nlh) < sizeof(struct rtmsg))
+- return -EINVAL;
+-
+- rtm = nlmsg_data(cb->nlh);
+- if (!(rtm->rtm_flags & RTM_F_CLONED))
+- return 0;
+-
+- s_h = cb->args[0];
+- s_idx = idx = cb->args[1];
+- for(h = 0; h <= dn_rt_hash_mask; h++) {
+- if (h < s_h)
+- continue;
+- if (h > s_h)
+- s_idx = 0;
+- rcu_read_lock_bh();
+- for(rt = rcu_dereference_bh(dn_rt_hash_table[h].chain), idx = 0;
+- rt;
+- rt = rcu_dereference_bh(rt->dst.dn_next), idx++) {
+- if (idx < s_idx)
+- continue;
+- skb_dst_set(skb, dst_clone(&rt->dst));
+- if (dn_rt_fill_info(skb, NETLINK_CB(cb->skb).portid,
+- cb->nlh->nlmsg_seq, RTM_NEWROUTE,
+- 1, NLM_F_MULTI) < 0) {
+- skb_dst_drop(skb);
+- rcu_read_unlock_bh();
+- goto done;
+- }
+- skb_dst_drop(skb);
+- }
+- rcu_read_unlock_bh();
+- }
+-
+-done:
+- cb->args[0] = h;
+- cb->args[1] = idx;
+- return skb->len;
+-}
+-
+-#ifdef CONFIG_PROC_FS
+-struct dn_rt_cache_iter_state {
+- int bucket;
+-};
+-
+-static struct dn_route *dn_rt_cache_get_first(struct seq_file *seq)
+-{
+- struct dn_route *rt = NULL;
+- struct dn_rt_cache_iter_state *s = seq->private;
+-
+- for(s->bucket = dn_rt_hash_mask; s->bucket >= 0; --s->bucket) {
+- rcu_read_lock_bh();
+- rt = rcu_dereference_bh(dn_rt_hash_table[s->bucket].chain);
+- if (rt)
+- break;
+- rcu_read_unlock_bh();
+- }
+- return rt;
+-}
+-
+-static struct dn_route *dn_rt_cache_get_next(struct seq_file *seq, struct dn_route *rt)
+-{
+- struct dn_rt_cache_iter_state *s = seq->private;
+-
+- rt = rcu_dereference_bh(rt->dst.dn_next);
+- while (!rt) {
+- rcu_read_unlock_bh();
+- if (--s->bucket < 0)
+- break;
+- rcu_read_lock_bh();
+- rt = rcu_dereference_bh(dn_rt_hash_table[s->bucket].chain);
+- }
+- return rt;
+-}
+-
+-static void *dn_rt_cache_seq_start(struct seq_file *seq, loff_t *pos)
+-{
+- struct dn_route *rt = dn_rt_cache_get_first(seq);
+-
+- if (rt) {
+- while(*pos && (rt = dn_rt_cache_get_next(seq, rt)))
+- --*pos;
+- }
+- return *pos ? NULL : rt;
+-}
+-
+-static void *dn_rt_cache_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+-{
+- struct dn_route *rt = dn_rt_cache_get_next(seq, v);
+- ++*pos;
+- return rt;
+-}
+-
+-static void dn_rt_cache_seq_stop(struct seq_file *seq, void *v)
+-{
+- if (v)
+- rcu_read_unlock_bh();
+-}
+-
+-static int dn_rt_cache_seq_show(struct seq_file *seq, void *v)
+-{
+- struct dn_route *rt = v;
+- char buf1[DN_ASCBUF_LEN], buf2[DN_ASCBUF_LEN];
+-
+- seq_printf(seq, "%-8s %-7s %-7s %04d %04d %04d\n",
+- rt->dst.dev ? rt->dst.dev->name : "*",
+- dn_addr2asc(le16_to_cpu(rt->rt_daddr), buf1),
+- dn_addr2asc(le16_to_cpu(rt->rt_saddr), buf2),
+- atomic_read(&rt->dst.__refcnt),
+- rt->dst.__use, 0);
+- return 0;
+-}
+-
+-static const struct seq_operations dn_rt_cache_seq_ops = {
+- .start = dn_rt_cache_seq_start,
+- .next = dn_rt_cache_seq_next,
+- .stop = dn_rt_cache_seq_stop,
+- .show = dn_rt_cache_seq_show,
+-};
+-
+-static int dn_rt_cache_seq_open(struct inode *inode, struct file *file)
+-{
+- return seq_open_private(file, &dn_rt_cache_seq_ops,
+- sizeof(struct dn_rt_cache_iter_state));
+-}
+-
+-static const struct file_operations dn_rt_cache_seq_fops = {
+- .owner = THIS_MODULE,
+- .open = dn_rt_cache_seq_open,
+- .read = seq_read,
+- .llseek = seq_lseek,
+- .release = seq_release_private,
+-};
+-
+-#endif /* CONFIG_PROC_FS */
+-
+-void __init dn_route_init(void)
+-{
+- int i, goal, order;
+-
+- dn_dst_ops.kmem_cachep =
+- kmem_cache_create("dn_dst_cache", sizeof(struct dn_route), 0,
+- SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
+- dst_entries_init(&dn_dst_ops);
+- setup_timer(&dn_route_timer, dn_dst_check_expire, 0);
+- dn_route_timer.expires = jiffies + decnet_dst_gc_interval * HZ;
+- add_timer(&dn_route_timer);
+-
+- goal = totalram_pages >> (26 - PAGE_SHIFT);
+-
+- for(order = 0; (1UL << order) < goal; order++)
+- /* NOTHING */;
+-
+- /*
+- * Only want 1024 entries max, since the table is very, very unlikely
+- * to be larger than that.
+- */
+- while(order && ((((1UL << order) * PAGE_SIZE) /
+- sizeof(struct dn_rt_hash_bucket)) >= 2048))
+- order--;
+-
+- do {
+- dn_rt_hash_mask = (1UL << order) * PAGE_SIZE /
+- sizeof(struct dn_rt_hash_bucket);
+- while(dn_rt_hash_mask & (dn_rt_hash_mask - 1))
+- dn_rt_hash_mask--;
+- dn_rt_hash_table = (struct dn_rt_hash_bucket *)
+- __get_free_pages(GFP_ATOMIC, order);
+- } while (dn_rt_hash_table == NULL && --order > 0);
+-
+- if (!dn_rt_hash_table)
+- panic("Failed to allocate DECnet route cache hash table\n");
+-
+- printk(KERN_INFO
+- "DECnet: Routing cache hash table of %u buckets, %ldKbytes\n",
+- dn_rt_hash_mask,
+- (long)(dn_rt_hash_mask*sizeof(struct dn_rt_hash_bucket))/1024);
+-
+- dn_rt_hash_mask--;
+- for(i = 0; i <= dn_rt_hash_mask; i++) {
+- spin_lock_init(&dn_rt_hash_table[i].lock);
+- dn_rt_hash_table[i].chain = NULL;
+- }
+-
+- dn_dst_ops.gc_thresh = (dn_rt_hash_mask + 1);
+-
+- proc_create("decnet_cache", S_IRUGO, init_net.proc_net,
+- &dn_rt_cache_seq_fops);
+-
+-#ifdef CONFIG_DECNET_ROUTER
+- rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute,
+- dn_fib_dump, 0);
+-#else
+- rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute,
+- dn_cache_dump, 0);
+-#endif
+-}
+-
+-void __exit dn_route_cleanup(void)
+-{
+- del_timer(&dn_route_timer);
+- dn_run_flush(0);
+-
+- remove_proc_entry("decnet_cache", init_net.proc_net);
+- dst_entries_destroy(&dn_dst_ops);
+-}
+-
+diff --git a/net/decnet/dn_rules.c b/net/decnet/dn_rules.c
+deleted file mode 100644
+index c795c3f509c94..0000000000000
+--- a/net/decnet/dn_rules.c
++++ /dev/null
+@@ -1,257 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-
+-/*
+- * DECnet An implementation of the DECnet protocol suite for the LINUX
+- * operating system. DECnet is implemented using the BSD Socket
+- * interface as the means of communication with the user level.
+- *
+- * DECnet Routing Forwarding Information Base (Rules)
+- *
+- * Author: Steve Whitehouse <SteveW@ACM.org>
+- * Mostly copied from Alexey Kuznetsov's ipv4/fib_rules.c
+- *
+- *
+- * Changes:
+- * Steve Whitehouse <steve@chygwyn.com>
+- * Updated for Thomas Graf's generic rules
+- *
+- */
+-#include <linux/net.h>
+-#include <linux/init.h>
+-#include <linux/netlink.h>
+-#include <linux/rtnetlink.h>
+-#include <linux/netdevice.h>
+-#include <linux/spinlock.h>
+-#include <linux/list.h>
+-#include <linux/rcupdate.h>
+-#include <linux/export.h>
+-#include <net/neighbour.h>
+-#include <net/dst.h>
+-#include <net/flow.h>
+-#include <net/fib_rules.h>
+-#include <net/dn.h>
+-#include <net/dn_fib.h>
+-#include <net/dn_neigh.h>
+-#include <net/dn_dev.h>
+-#include <net/dn_route.h>
+-
+-static struct fib_rules_ops *dn_fib_rules_ops;
+-
+-struct dn_fib_rule
+-{
+- struct fib_rule common;
+- unsigned char dst_len;
+- unsigned char src_len;
+- __le16 src;
+- __le16 srcmask;
+- __le16 dst;
+- __le16 dstmask;
+- __le16 srcmap;
+- u8 flags;
+-};
+-
+-
+-int dn_fib_lookup(struct flowidn *flp, struct dn_fib_res *res)
+-{
+- struct fib_lookup_arg arg = {
+- .result = res,
+- };
+- int err;
+-
+- err = fib_rules_lookup(dn_fib_rules_ops,
+- flowidn_to_flowi(flp), 0, &arg);
+- res->r = arg.rule;
+-
+- return err;
+-}
+-
+-static int dn_fib_rule_action(struct fib_rule *rule, struct flowi *flp,
+- int flags, struct fib_lookup_arg *arg)
+-{
+- struct flowidn *fld = &flp->u.dn;
+- int err = -EAGAIN;
+- struct dn_fib_table *tbl;
+-
+- switch(rule->action) {
+- case FR_ACT_TO_TBL:
+- break;
+-
+- case FR_ACT_UNREACHABLE:
+- err = -ENETUNREACH;
+- goto errout;
+-
+- case FR_ACT_PROHIBIT:
+- err = -EACCES;
+- goto errout;
+-
+- case FR_ACT_BLACKHOLE:
+- default:
+- err = -EINVAL;
+- goto errout;
+- }
+-
+- tbl = dn_fib_get_table(rule->table, 0);
+- if (tbl == NULL)
+- goto errout;
+-
+- err = tbl->lookup(tbl, fld, (struct dn_fib_res *)arg->result);
+- if (err > 0)
+- err = -EAGAIN;
+-errout:
+- return err;
+-}
+-
+-static const struct nla_policy dn_fib_rule_policy[FRA_MAX+1] = {
+- FRA_GENERIC_POLICY,
+-};
+-
+-static int dn_fib_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
+-{
+- struct dn_fib_rule *r = (struct dn_fib_rule *)rule;
+- struct flowidn *fld = &fl->u.dn;
+- __le16 daddr = fld->daddr;
+- __le16 saddr = fld->saddr;
+-
+- if (((saddr ^ r->src) & r->srcmask) ||
+- ((daddr ^ r->dst) & r->dstmask))
+- return 0;
+-
+- return 1;
+-}
+-
+-static int dn_fib_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
+- struct fib_rule_hdr *frh,
+- struct nlattr **tb)
+-{
+- int err = -EINVAL;
+- struct dn_fib_rule *r = (struct dn_fib_rule *)rule;
+-
+- if (frh->tos)
+- goto errout;
+-
+- if (rule->table == RT_TABLE_UNSPEC) {
+- if (rule->action == FR_ACT_TO_TBL) {
+- struct dn_fib_table *table;
+-
+- table = dn_fib_empty_table();
+- if (table == NULL) {
+- err = -ENOBUFS;
+- goto errout;
+- }
+-
+- rule->table = table->n;
+- }
+- }
+-
+- if (frh->src_len)
+- r->src = nla_get_le16(tb[FRA_SRC]);
+-
+- if (frh->dst_len)
+- r->dst = nla_get_le16(tb[FRA_DST]);
+-
+- r->src_len = frh->src_len;
+- r->srcmask = dnet_make_mask(r->src_len);
+- r->dst_len = frh->dst_len;
+- r->dstmask = dnet_make_mask(r->dst_len);
+- err = 0;
+-errout:
+- return err;
+-}
+-
+-static int dn_fib_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
+- struct nlattr **tb)
+-{
+- struct dn_fib_rule *r = (struct dn_fib_rule *)rule;
+-
+- if (frh->src_len && (r->src_len != frh->src_len))
+- return 0;
+-
+- if (frh->dst_len && (r->dst_len != frh->dst_len))
+- return 0;
+-
+- if (frh->src_len && (r->src != nla_get_le16(tb[FRA_SRC])))
+- return 0;
+-
+- if (frh->dst_len && (r->dst != nla_get_le16(tb[FRA_DST])))
+- return 0;
+-
+- return 1;
+-}
+-
+-unsigned int dnet_addr_type(__le16 addr)
+-{
+- struct flowidn fld = { .daddr = addr };
+- struct dn_fib_res res;
+- unsigned int ret = RTN_UNICAST;
+- struct dn_fib_table *tb = dn_fib_get_table(RT_TABLE_LOCAL, 0);
+-
+- res.r = NULL;
+-
+- if (tb) {
+- if (!tb->lookup(tb, &fld, &res)) {
+- ret = res.type;
+- dn_fib_res_put(&res);
+- }
+- }
+- return ret;
+-}
+-
+-static int dn_fib_rule_fill(struct fib_rule *rule, struct sk_buff *skb,
+- struct fib_rule_hdr *frh)
+-{
+- struct dn_fib_rule *r = (struct dn_fib_rule *)rule;
+-
+- frh->dst_len = r->dst_len;
+- frh->src_len = r->src_len;
+- frh->tos = 0;
+-
+- if ((r->dst_len &&
+- nla_put_le16(skb, FRA_DST, r->dst)) ||
+- (r->src_len &&
+- nla_put_le16(skb, FRA_SRC, r->src)))
+- goto nla_put_failure;
+- return 0;
+-
+-nla_put_failure:
+- return -ENOBUFS;
+-}
+-
+-static void dn_fib_rule_flush_cache(struct fib_rules_ops *ops)
+-{
+- dn_rt_cache_flush(-1);
+-}
+-
+-static const struct fib_rules_ops __net_initconst dn_fib_rules_ops_template = {
+- .family = AF_DECnet,
+- .rule_size = sizeof(struct dn_fib_rule),
+- .addr_size = sizeof(u16),
+- .action = dn_fib_rule_action,
+- .match = dn_fib_rule_match,
+- .configure = dn_fib_rule_configure,
+- .compare = dn_fib_rule_compare,
+- .fill = dn_fib_rule_fill,
+- .flush_cache = dn_fib_rule_flush_cache,
+- .nlgroup = RTNLGRP_DECnet_RULE,
+- .policy = dn_fib_rule_policy,
+- .owner = THIS_MODULE,
+- .fro_net = &init_net,
+-};
+-
+-void __init dn_fib_rules_init(void)
+-{
+- dn_fib_rules_ops =
+- fib_rules_register(&dn_fib_rules_ops_template, &init_net);
+- BUG_ON(IS_ERR(dn_fib_rules_ops));
+- BUG_ON(fib_default_rule_add(dn_fib_rules_ops, 0x7fff,
+- RT_TABLE_MAIN, 0));
+-}
+-
+-void __exit dn_fib_rules_cleanup(void)
+-{
+- rtnl_lock();
+- fib_rules_unregister(dn_fib_rules_ops);
+- rtnl_unlock();
+- rcu_barrier();
+-}
+-
+-
+diff --git a/net/decnet/dn_table.c b/net/decnet/dn_table.c
+deleted file mode 100644
+index 08667f68e6011..0000000000000
+--- a/net/decnet/dn_table.c
++++ /dev/null
+@@ -1,927 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/*
+- * DECnet An implementation of the DECnet protocol suite for the LINUX
+- * operating system. DECnet is implemented using the BSD Socket
+- * interface as the means of communication with the user level.
+- *
+- * DECnet Routing Forwarding Information Base (Routing Tables)
+- *
+- * Author: Steve Whitehouse <SteveW@ACM.org>
+- * Mostly copied from the IPv4 routing code
+- *
+- *
+- * Changes:
+- *
+- */
+-#include <linux/string.h>
+-#include <linux/net.h>
+-#include <linux/socket.h>
+-#include <linux/slab.h>
+-#include <linux/sockios.h>
+-#include <linux/init.h>
+-#include <linux/skbuff.h>
+-#include <linux/rtnetlink.h>
+-#include <linux/proc_fs.h>
+-#include <linux/netdevice.h>
+-#include <linux/timer.h>
+-#include <linux/spinlock.h>
+-#include <linux/atomic.h>
+-#include <linux/uaccess.h>
+-#include <linux/route.h> /* RTF_xxx */
+-#include <net/neighbour.h>
+-#include <net/netlink.h>
+-#include <net/tcp.h>
+-#include <net/dst.h>
+-#include <net/flow.h>
+-#include <net/fib_rules.h>
+-#include <net/dn.h>
+-#include <net/dn_route.h>
+-#include <net/dn_fib.h>
+-#include <net/dn_neigh.h>
+-#include <net/dn_dev.h>
+-
+-struct dn_zone
+-{
+- struct dn_zone *dz_next;
+- struct dn_fib_node **dz_hash;
+- int dz_nent;
+- int dz_divisor;
+- u32 dz_hashmask;
+-#define DZ_HASHMASK(dz) ((dz)->dz_hashmask)
+- int dz_order;
+- __le16 dz_mask;
+-#define DZ_MASK(dz) ((dz)->dz_mask)
+-};
+-
+-struct dn_hash
+-{
+- struct dn_zone *dh_zones[17];
+- struct dn_zone *dh_zone_list;
+-};
+-
+-#define dz_key_0(key) ((key).datum = 0)
+-
+-#define for_nexthops(fi) { int nhsel; const struct dn_fib_nh *nh;\
+- for(nhsel = 0, nh = (fi)->fib_nh; nhsel < (fi)->fib_nhs; nh++, nhsel++)
+-
+-#define endfor_nexthops(fi) }
+-
+-#define DN_MAX_DIVISOR 1024
+-#define DN_S_ZOMBIE 1
+-#define DN_S_ACCESSED 2
+-
+-#define DN_FIB_SCAN(f, fp) \
+-for( ; ((f) = *(fp)) != NULL; (fp) = &(f)->fn_next)
+-
+-#define DN_FIB_SCAN_KEY(f, fp, key) \
+-for( ; ((f) = *(fp)) != NULL && dn_key_eq((f)->fn_key, (key)); (fp) = &(f)->fn_next)
+-
+-#define RT_TABLE_MIN 1
+-#define DN_FIB_TABLE_HASHSZ 256
+-static struct hlist_head dn_fib_table_hash[DN_FIB_TABLE_HASHSZ];
+-static DEFINE_RWLOCK(dn_fib_tables_lock);
+-
+-static struct kmem_cache *dn_hash_kmem __read_mostly;
+-static int dn_fib_hash_zombies;
+-
+-static inline dn_fib_idx_t dn_hash(dn_fib_key_t key, struct dn_zone *dz)
+-{
+- u16 h = le16_to_cpu(key.datum)>>(16 - dz->dz_order);
+- h ^= (h >> 10);
+- h ^= (h >> 6);
+- h &= DZ_HASHMASK(dz);
+- return *(dn_fib_idx_t *)&h;
+-}
+-
+-static inline dn_fib_key_t dz_key(__le16 dst, struct dn_zone *dz)
+-{
+- dn_fib_key_t k;
+- k.datum = dst & DZ_MASK(dz);
+- return k;
+-}
+-
+-static inline struct dn_fib_node **dn_chain_p(dn_fib_key_t key, struct dn_zone *dz)
+-{
+- return &dz->dz_hash[dn_hash(key, dz).datum];
+-}
+-
+-static inline struct dn_fib_node *dz_chain(dn_fib_key_t key, struct dn_zone *dz)
+-{
+- return dz->dz_hash[dn_hash(key, dz).datum];
+-}
+-
+-static inline int dn_key_eq(dn_fib_key_t a, dn_fib_key_t b)
+-{
+- return a.datum == b.datum;
+-}
+-
+-static inline int dn_key_leq(dn_fib_key_t a, dn_fib_key_t b)
+-{
+- return a.datum <= b.datum;
+-}
+-
+-static inline void dn_rebuild_zone(struct dn_zone *dz,
+- struct dn_fib_node **old_ht,
+- int old_divisor)
+-{
+- struct dn_fib_node *f, **fp, *next;
+- int i;
+-
+- for(i = 0; i < old_divisor; i++) {
+- for(f = old_ht[i]; f; f = next) {
+- next = f->fn_next;
+- for(fp = dn_chain_p(f->fn_key, dz);
+- *fp && dn_key_leq((*fp)->fn_key, f->fn_key);
+- fp = &(*fp)->fn_next)
+- /* NOTHING */;
+- f->fn_next = *fp;
+- *fp = f;
+- }
+- }
+-}
+-
+-static void dn_rehash_zone(struct dn_zone *dz)
+-{
+- struct dn_fib_node **ht, **old_ht;
+- int old_divisor, new_divisor;
+- u32 new_hashmask;
+-
+- old_divisor = dz->dz_divisor;
+-
+- switch (old_divisor) {
+- case 16:
+- new_divisor = 256;
+- new_hashmask = 0xFF;
+- break;
+- default:
+- printk(KERN_DEBUG "DECnet: dn_rehash_zone: BUG! %d\n",
+- old_divisor);
+- case 256:
+- new_divisor = 1024;
+- new_hashmask = 0x3FF;
+- break;
+- }
+-
+- ht = kcalloc(new_divisor, sizeof(struct dn_fib_node*), GFP_KERNEL);
+- if (ht == NULL)
+- return;
+-
+- write_lock_bh(&dn_fib_tables_lock);
+- old_ht = dz->dz_hash;
+- dz->dz_hash = ht;
+- dz->dz_hashmask = new_hashmask;
+- dz->dz_divisor = new_divisor;
+- dn_rebuild_zone(dz, old_ht, old_divisor);
+- write_unlock_bh(&dn_fib_tables_lock);
+- kfree(old_ht);
+-}
+-
+-static void dn_free_node(struct dn_fib_node *f)
+-{
+- dn_fib_release_info(DN_FIB_INFO(f));
+- kmem_cache_free(dn_hash_kmem, f);
+-}
+-
+-
+-static struct dn_zone *dn_new_zone(struct dn_hash *table, int z)
+-{
+- int i;
+- struct dn_zone *dz = kzalloc(sizeof(struct dn_zone), GFP_KERNEL);
+- if (!dz)
+- return NULL;
+-
+- if (z) {
+- dz->dz_divisor = 16;
+- dz->dz_hashmask = 0x0F;
+- } else {
+- dz->dz_divisor = 1;
+- dz->dz_hashmask = 0;
+- }
+-
+- dz->dz_hash = kcalloc(dz->dz_divisor, sizeof(struct dn_fib_node *), GFP_KERNEL);
+- if (!dz->dz_hash) {
+- kfree(dz);
+- return NULL;
+- }
+-
+- dz->dz_order = z;
+- dz->dz_mask = dnet_make_mask(z);
+-
+- for(i = z + 1; i <= 16; i++)
+- if (table->dh_zones[i])
+- break;
+-
+- write_lock_bh(&dn_fib_tables_lock);
+- if (i>16) {
+- dz->dz_next = table->dh_zone_list;
+- table->dh_zone_list = dz;
+- } else {
+- dz->dz_next = table->dh_zones[i]->dz_next;
+- table->dh_zones[i]->dz_next = dz;
+- }
+- table->dh_zones[z] = dz;
+- write_unlock_bh(&dn_fib_tables_lock);
+- return dz;
+-}
+-
+-
+-static int dn_fib_nh_match(struct rtmsg *r, struct nlmsghdr *nlh, struct nlattr *attrs[], struct dn_fib_info *fi)
+-{
+- struct rtnexthop *nhp;
+- int nhlen;
+-
+- if (attrs[RTA_PRIORITY] &&
+- nla_get_u32(attrs[RTA_PRIORITY]) != fi->fib_priority)
+- return 1;
+-
+- if (attrs[RTA_OIF] || attrs[RTA_GATEWAY]) {
+- if ((!attrs[RTA_OIF] || nla_get_u32(attrs[RTA_OIF]) == fi->fib_nh->nh_oif) &&
+- (!attrs[RTA_GATEWAY] || nla_get_le16(attrs[RTA_GATEWAY]) != fi->fib_nh->nh_gw))
+- return 0;
+- return 1;
+- }
+-
+- if (!attrs[RTA_MULTIPATH])
+- return 0;
+-
+- nhp = nla_data(attrs[RTA_MULTIPATH]);
+- nhlen = nla_len(attrs[RTA_MULTIPATH]);
+-
+- for_nexthops(fi) {
+- int attrlen = nhlen - sizeof(struct rtnexthop);
+- __le16 gw;
+-
+- if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0)
+- return -EINVAL;
+- if (nhp->rtnh_ifindex && nhp->rtnh_ifindex != nh->nh_oif)
+- return 1;
+- if (attrlen) {
+- struct nlattr *gw_attr;
+-
+- gw_attr = nla_find((struct nlattr *) (nhp + 1), attrlen, RTA_GATEWAY);
+- gw = gw_attr ? nla_get_le16(gw_attr) : 0;
+-
+- if (gw && gw != nh->nh_gw)
+- return 1;
+- }
+- nhp = RTNH_NEXT(nhp);
+- } endfor_nexthops(fi);
+-
+- return 0;
+-}
+-
+-static inline size_t dn_fib_nlmsg_size(struct dn_fib_info *fi)
+-{
+- size_t payload = NLMSG_ALIGN(sizeof(struct rtmsg))
+- + nla_total_size(4) /* RTA_TABLE */
+- + nla_total_size(2) /* RTA_DST */
+- + nla_total_size(4) /* RTA_PRIORITY */
+- + nla_total_size(TCP_CA_NAME_MAX); /* RTAX_CC_ALGO */
+-
+- /* space for nested metrics */
+- payload += nla_total_size((RTAX_MAX * nla_total_size(4)));
+-
+- if (fi->fib_nhs) {
+- /* Also handles the special case fib_nhs == 1 */
+-
+- /* each nexthop is packed in an attribute */
+- size_t nhsize = nla_total_size(sizeof(struct rtnexthop));
+-
+- /* may contain a gateway attribute */
+- nhsize += nla_total_size(4);
+-
+- /* all nexthops are packed in a nested attribute */
+- payload += nla_total_size(fi->fib_nhs * nhsize);
+- }
+-
+- return payload;
+-}
+-
+-static int dn_fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event,
+- u32 tb_id, u8 type, u8 scope, void *dst, int dst_len,
+- struct dn_fib_info *fi, unsigned int flags)
+-{
+- struct rtmsg *rtm;
+- struct nlmsghdr *nlh;
+-
+- nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), flags);
+- if (!nlh)
+- return -EMSGSIZE;
+-
+- rtm = nlmsg_data(nlh);
+- rtm->rtm_family = AF_DECnet;
+- rtm->rtm_dst_len = dst_len;
+- rtm->rtm_src_len = 0;
+- rtm->rtm_tos = 0;
+- rtm->rtm_table = tb_id;
+- rtm->rtm_flags = fi->fib_flags;
+- rtm->rtm_scope = scope;
+- rtm->rtm_type = type;
+- rtm->rtm_protocol = fi->fib_protocol;
+-
+- if (nla_put_u32(skb, RTA_TABLE, tb_id) < 0)
+- goto errout;
+-
+- if (rtm->rtm_dst_len &&
+- nla_put(skb, RTA_DST, 2, dst) < 0)
+- goto errout;
+-
+- if (fi->fib_priority &&
+- nla_put_u32(skb, RTA_PRIORITY, fi->fib_priority) < 0)
+- goto errout;
+-
+- if (rtnetlink_put_metrics(skb, fi->fib_metrics) < 0)
+- goto errout;
+-
+- if (fi->fib_nhs == 1) {
+- if (fi->fib_nh->nh_gw &&
+- nla_put_le16(skb, RTA_GATEWAY, fi->fib_nh->nh_gw) < 0)
+- goto errout;
+-
+- if (fi->fib_nh->nh_oif &&
+- nla_put_u32(skb, RTA_OIF, fi->fib_nh->nh_oif) < 0)
+- goto errout;
+- }
+-
+- if (fi->fib_nhs > 1) {
+- struct rtnexthop *nhp;
+- struct nlattr *mp_head;
+-
+- if (!(mp_head = nla_nest_start(skb, RTA_MULTIPATH)))
+- goto errout;
+-
+- for_nexthops(fi) {
+- if (!(nhp = nla_reserve_nohdr(skb, sizeof(*nhp))))
+- goto errout;
+-
+- nhp->rtnh_flags = nh->nh_flags & 0xFF;
+- nhp->rtnh_hops = nh->nh_weight - 1;
+- nhp->rtnh_ifindex = nh->nh_oif;
+-
+- if (nh->nh_gw &&
+- nla_put_le16(skb, RTA_GATEWAY, nh->nh_gw) < 0)
+- goto errout;
+-
+- nhp->rtnh_len = skb_tail_pointer(skb) - (unsigned char *)nhp;
+- } endfor_nexthops(fi);
+-
+- nla_nest_end(skb, mp_head);
+- }
+-
+- nlmsg_end(skb, nlh);
+- return 0;
+-
+-errout:
+- nlmsg_cancel(skb, nlh);
+- return -EMSGSIZE;
+-}
+-
+-
+-static void dn_rtmsg_fib(int event, struct dn_fib_node *f, int z, u32 tb_id,
+- struct nlmsghdr *nlh, struct netlink_skb_parms *req)
+-{
+- struct sk_buff *skb;
+- u32 portid = req ? req->portid : 0;
+- int err = -ENOBUFS;
+-
+- skb = nlmsg_new(dn_fib_nlmsg_size(DN_FIB_INFO(f)), GFP_KERNEL);
+- if (skb == NULL)
+- goto errout;
+-
+- err = dn_fib_dump_info(skb, portid, nlh->nlmsg_seq, event, tb_id,
+- f->fn_type, f->fn_scope, &f->fn_key, z,
+- DN_FIB_INFO(f), 0);
+- if (err < 0) {
+- /* -EMSGSIZE implies BUG in dn_fib_nlmsg_size() */
+- WARN_ON(err == -EMSGSIZE);
+- kfree_skb(skb);
+- goto errout;
+- }
+- rtnl_notify(skb, &init_net, portid, RTNLGRP_DECnet_ROUTE, nlh, GFP_KERNEL);
+- return;
+-errout:
+- if (err < 0)
+- rtnl_set_sk_err(&init_net, RTNLGRP_DECnet_ROUTE, err);
+-}
+-
+-static __inline__ int dn_hash_dump_bucket(struct sk_buff *skb,
+- struct netlink_callback *cb,
+- struct dn_fib_table *tb,
+- struct dn_zone *dz,
+- struct dn_fib_node *f)
+-{
+- int i, s_i;
+-
+- s_i = cb->args[4];
+- for(i = 0; f; i++, f = f->fn_next) {
+- if (i < s_i)
+- continue;
+- if (f->fn_state & DN_S_ZOMBIE)
+- continue;
+- if (dn_fib_dump_info(skb, NETLINK_CB(cb->skb).portid,
+- cb->nlh->nlmsg_seq,
+- RTM_NEWROUTE,
+- tb->n,
+- (f->fn_state & DN_S_ZOMBIE) ? 0 : f->fn_type,
+- f->fn_scope, &f->fn_key, dz->dz_order,
+- f->fn_info, NLM_F_MULTI) < 0) {
+- cb->args[4] = i;
+- return -1;
+- }
+- }
+- cb->args[4] = i;
+- return skb->len;
+-}
+-
+-static __inline__ int dn_hash_dump_zone(struct sk_buff *skb,
+- struct netlink_callback *cb,
+- struct dn_fib_table *tb,
+- struct dn_zone *dz)
+-{
+- int h, s_h;
+-
+- s_h = cb->args[3];
+- for(h = 0; h < dz->dz_divisor; h++) {
+- if (h < s_h)
+- continue;
+- if (h > s_h)
+- memset(&cb->args[4], 0, sizeof(cb->args) - 4*sizeof(cb->args[0]));
+- if (dz->dz_hash == NULL || dz->dz_hash[h] == NULL)
+- continue;
+- if (dn_hash_dump_bucket(skb, cb, tb, dz, dz->dz_hash[h]) < 0) {
+- cb->args[3] = h;
+- return -1;
+- }
+- }
+- cb->args[3] = h;
+- return skb->len;
+-}
+-
+-static int dn_fib_table_dump(struct dn_fib_table *tb, struct sk_buff *skb,
+- struct netlink_callback *cb)
+-{
+- int m, s_m;
+- struct dn_zone *dz;
+- struct dn_hash *table = (struct dn_hash *)tb->data;
+-
+- s_m = cb->args[2];
+- read_lock(&dn_fib_tables_lock);
+- for(dz = table->dh_zone_list, m = 0; dz; dz = dz->dz_next, m++) {
+- if (m < s_m)
+- continue;
+- if (m > s_m)
+- memset(&cb->args[3], 0, sizeof(cb->args) - 3*sizeof(cb->args[0]));
+-
+- if (dn_hash_dump_zone(skb, cb, tb, dz) < 0) {
+- cb->args[2] = m;
+- read_unlock(&dn_fib_tables_lock);
+- return -1;
+- }
+- }
+- read_unlock(&dn_fib_tables_lock);
+- cb->args[2] = m;
+-
+- return skb->len;
+-}
+-
+-int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb)
+-{
+- struct net *net = sock_net(skb->sk);
+- unsigned int h, s_h;
+- unsigned int e = 0, s_e;
+- struct dn_fib_table *tb;
+- int dumped = 0;
+-
+- if (!net_eq(net, &init_net))
+- return 0;
+-
+- if (nlmsg_len(cb->nlh) >= sizeof(struct rtmsg) &&
+- ((struct rtmsg *)nlmsg_data(cb->nlh))->rtm_flags&RTM_F_CLONED)
+- return dn_cache_dump(skb, cb);
+-
+- s_h = cb->args[0];
+- s_e = cb->args[1];
+-
+- for (h = s_h; h < DN_FIB_TABLE_HASHSZ; h++, s_h = 0) {
+- e = 0;
+- hlist_for_each_entry(tb, &dn_fib_table_hash[h], hlist) {
+- if (e < s_e)
+- goto next;
+- if (dumped)
+- memset(&cb->args[2], 0, sizeof(cb->args) -
+- 2 * sizeof(cb->args[0]));
+- if (tb->dump(tb, skb, cb) < 0)
+- goto out;
+- dumped = 1;
+-next:
+- e++;
+- }
+- }
+-out:
+- cb->args[1] = e;
+- cb->args[0] = h;
+-
+- return skb->len;
+-}
+-
+-static int dn_fib_table_insert(struct dn_fib_table *tb, struct rtmsg *r, struct nlattr *attrs[],
+- struct nlmsghdr *n, struct netlink_skb_parms *req)
+-{
+- struct dn_hash *table = (struct dn_hash *)tb->data;
+- struct dn_fib_node *new_f, *f, **fp, **del_fp;
+- struct dn_zone *dz;
+- struct dn_fib_info *fi;
+- int z = r->rtm_dst_len;
+- int type = r->rtm_type;
+- dn_fib_key_t key;
+- int err;
+-
+- if (z > 16)
+- return -EINVAL;
+-
+- dz = table->dh_zones[z];
+- if (!dz && !(dz = dn_new_zone(table, z)))
+- return -ENOBUFS;
+-
+- dz_key_0(key);
+- if (attrs[RTA_DST]) {
+- __le16 dst = nla_get_le16(attrs[RTA_DST]);
+- if (dst & ~DZ_MASK(dz))
+- return -EINVAL;
+- key = dz_key(dst, dz);
+- }
+-
+- if ((fi = dn_fib_create_info(r, attrs, n, &err)) == NULL)
+- return err;
+-
+- if (dz->dz_nent > (dz->dz_divisor << 2) &&
+- dz->dz_divisor > DN_MAX_DIVISOR &&
+- (z==16 || (1<<z) > dz->dz_divisor))
+- dn_rehash_zone(dz);
+-
+- fp = dn_chain_p(key, dz);
+-
+- DN_FIB_SCAN(f, fp) {
+- if (dn_key_leq(key, f->fn_key))
+- break;
+- }
+-
+- del_fp = NULL;
+-
+- if (f && (f->fn_state & DN_S_ZOMBIE) &&
+- dn_key_eq(f->fn_key, key)) {
+- del_fp = fp;
+- fp = &f->fn_next;
+- f = *fp;
+- goto create;
+- }
+-
+- DN_FIB_SCAN_KEY(f, fp, key) {
+- if (fi->fib_priority <= DN_FIB_INFO(f)->fib_priority)
+- break;
+- }
+-
+- if (f && dn_key_eq(f->fn_key, key) &&
+- fi->fib_priority == DN_FIB_INFO(f)->fib_priority) {
+- struct dn_fib_node **ins_fp;
+-
+- err = -EEXIST;
+- if (n->nlmsg_flags & NLM_F_EXCL)
+- goto out;
+-
+- if (n->nlmsg_flags & NLM_F_REPLACE) {
+- del_fp = fp;
+- fp = &f->fn_next;
+- f = *fp;
+- goto replace;
+- }
+-
+- ins_fp = fp;
+- err = -EEXIST;
+-
+- DN_FIB_SCAN_KEY(f, fp, key) {
+- if (fi->fib_priority != DN_FIB_INFO(f)->fib_priority)
+- break;
+- if (f->fn_type == type &&
+- f->fn_scope == r->rtm_scope &&
+- DN_FIB_INFO(f) == fi)
+- goto out;
+- }
+-
+- if (!(n->nlmsg_flags & NLM_F_APPEND)) {
+- fp = ins_fp;
+- f = *fp;
+- }
+- }
+-
+-create:
+- err = -ENOENT;
+- if (!(n->nlmsg_flags & NLM_F_CREATE))
+- goto out;
+-
+-replace:
+- err = -ENOBUFS;
+- new_f = kmem_cache_zalloc(dn_hash_kmem, GFP_KERNEL);
+- if (new_f == NULL)
+- goto out;
+-
+- new_f->fn_key = key;
+- new_f->fn_type = type;
+- new_f->fn_scope = r->rtm_scope;
+- DN_FIB_INFO(new_f) = fi;
+-
+- new_f->fn_next = f;
+- write_lock_bh(&dn_fib_tables_lock);
+- *fp = new_f;
+- write_unlock_bh(&dn_fib_tables_lock);
+- dz->dz_nent++;
+-
+- if (del_fp) {
+- f = *del_fp;
+- write_lock_bh(&dn_fib_tables_lock);
+- *del_fp = f->fn_next;
+- write_unlock_bh(&dn_fib_tables_lock);
+-
+- if (!(f->fn_state & DN_S_ZOMBIE))
+- dn_rtmsg_fib(RTM_DELROUTE, f, z, tb->n, n, req);
+- if (f->fn_state & DN_S_ACCESSED)
+- dn_rt_cache_flush(-1);
+- dn_free_node(f);
+- dz->dz_nent--;
+- } else {
+- dn_rt_cache_flush(-1);
+- }
+-
+- dn_rtmsg_fib(RTM_NEWROUTE, new_f, z, tb->n, n, req);
+-
+- return 0;
+-out:
+- dn_fib_release_info(fi);
+- return err;
+-}
+-
+-
+-static int dn_fib_table_delete(struct dn_fib_table *tb, struct rtmsg *r, struct nlattr *attrs[],
+- struct nlmsghdr *n, struct netlink_skb_parms *req)
+-{
+- struct dn_hash *table = (struct dn_hash*)tb->data;
+- struct dn_fib_node **fp, **del_fp, *f;
+- int z = r->rtm_dst_len;
+- struct dn_zone *dz;
+- dn_fib_key_t key;
+- int matched;
+-
+-
+- if (z > 16)
+- return -EINVAL;
+-
+- if ((dz = table->dh_zones[z]) == NULL)
+- return -ESRCH;
+-
+- dz_key_0(key);
+- if (attrs[RTA_DST]) {
+- __le16 dst = nla_get_le16(attrs[RTA_DST]);
+- if (dst & ~DZ_MASK(dz))
+- return -EINVAL;
+- key = dz_key(dst, dz);
+- }
+-
+- fp = dn_chain_p(key, dz);
+-
+- DN_FIB_SCAN(f, fp) {
+- if (dn_key_eq(f->fn_key, key))
+- break;
+- if (dn_key_leq(key, f->fn_key))
+- return -ESRCH;
+- }
+-
+- matched = 0;
+- del_fp = NULL;
+- DN_FIB_SCAN_KEY(f, fp, key) {
+- struct dn_fib_info *fi = DN_FIB_INFO(f);
+-
+- if (f->fn_state & DN_S_ZOMBIE)
+- return -ESRCH;
+-
+- matched++;
+-
+- if (del_fp == NULL &&
+- (!r->rtm_type || f->fn_type == r->rtm_type) &&
+- (r->rtm_scope == RT_SCOPE_NOWHERE || f->fn_scope == r->rtm_scope) &&
+- (!r->rtm_protocol ||
+- fi->fib_protocol == r->rtm_protocol) &&
+- dn_fib_nh_match(r, n, attrs, fi) == 0)
+- del_fp = fp;
+- }
+-
+- if (del_fp) {
+- f = *del_fp;
+- dn_rtmsg_fib(RTM_DELROUTE, f, z, tb->n, n, req);
+-
+- if (matched != 1) {
+- write_lock_bh(&dn_fib_tables_lock);
+- *del_fp = f->fn_next;
+- write_unlock_bh(&dn_fib_tables_lock);
+-
+- if (f->fn_state & DN_S_ACCESSED)
+- dn_rt_cache_flush(-1);
+- dn_free_node(f);
+- dz->dz_nent--;
+- } else {
+- f->fn_state |= DN_S_ZOMBIE;
+- if (f->fn_state & DN_S_ACCESSED) {
+- f->fn_state &= ~DN_S_ACCESSED;
+- dn_rt_cache_flush(-1);
+- }
+- if (++dn_fib_hash_zombies > 128)
+- dn_fib_flush();
+- }
+-
+- return 0;
+- }
+-
+- return -ESRCH;
+-}
+-
+-static inline int dn_flush_list(struct dn_fib_node **fp, int z, struct dn_hash *table)
+-{
+- int found = 0;
+- struct dn_fib_node *f;
+-
+- while((f = *fp) != NULL) {
+- struct dn_fib_info *fi = DN_FIB_INFO(f);
+-
+- if (fi && ((f->fn_state & DN_S_ZOMBIE) || (fi->fib_flags & RTNH_F_DEAD))) {
+- write_lock_bh(&dn_fib_tables_lock);
+- *fp = f->fn_next;
+- write_unlock_bh(&dn_fib_tables_lock);
+-
+- dn_free_node(f);
+- found++;
+- continue;
+- }
+- fp = &f->fn_next;
+- }
+-
+- return found;
+-}
+-
+-static int dn_fib_table_flush(struct dn_fib_table *tb)
+-{
+- struct dn_hash *table = (struct dn_hash *)tb->data;
+- struct dn_zone *dz;
+- int found = 0;
+-
+- dn_fib_hash_zombies = 0;
+- for(dz = table->dh_zone_list; dz; dz = dz->dz_next) {
+- int i;
+- int tmp = 0;
+- for(i = dz->dz_divisor-1; i >= 0; i--)
+- tmp += dn_flush_list(&dz->dz_hash[i], dz->dz_order, table);
+- dz->dz_nent -= tmp;
+- found += tmp;
+- }
+-
+- return found;
+-}
+-
+-static int dn_fib_table_lookup(struct dn_fib_table *tb, const struct flowidn *flp, struct dn_fib_res *res)
+-{
+- int err;
+- struct dn_zone *dz;
+- struct dn_hash *t = (struct dn_hash *)tb->data;
+-
+- read_lock(&dn_fib_tables_lock);
+- for(dz = t->dh_zone_list; dz; dz = dz->dz_next) {
+- struct dn_fib_node *f;
+- dn_fib_key_t k = dz_key(flp->daddr, dz);
+-
+- for(f = dz_chain(k, dz); f; f = f->fn_next) {
+- if (!dn_key_eq(k, f->fn_key)) {
+- if (dn_key_leq(k, f->fn_key))
+- break;
+- else
+- continue;
+- }
+-
+- f->fn_state |= DN_S_ACCESSED;
+-
+- if (f->fn_state&DN_S_ZOMBIE)
+- continue;
+-
+- if (f->fn_scope < flp->flowidn_scope)
+- continue;
+-
+- err = dn_fib_semantic_match(f->fn_type, DN_FIB_INFO(f), flp, res);
+-
+- if (err == 0) {
+- res->type = f->fn_type;
+- res->scope = f->fn_scope;
+- res->prefixlen = dz->dz_order;
+- goto out;
+- }
+- if (err < 0)
+- goto out;
+- }
+- }
+- err = 1;
+-out:
+- read_unlock(&dn_fib_tables_lock);
+- return err;
+-}
+-
+-
+-struct dn_fib_table *dn_fib_get_table(u32 n, int create)
+-{
+- struct dn_fib_table *t;
+- unsigned int h;
+-
+- if (n < RT_TABLE_MIN)
+- return NULL;
+-
+- if (n > RT_TABLE_MAX)
+- return NULL;
+-
+- h = n & (DN_FIB_TABLE_HASHSZ - 1);
+- rcu_read_lock();
+- hlist_for_each_entry_rcu(t, &dn_fib_table_hash[h], hlist) {
+- if (t->n == n) {
+- rcu_read_unlock();
+- return t;
+- }
+- }
+- rcu_read_unlock();
+-
+- if (!create)
+- return NULL;
+-
+- if (in_interrupt()) {
+- net_dbg_ratelimited("DECnet: BUG! Attempt to create routing table from interrupt\n");
+- return NULL;
+- }
+-
+- t = kzalloc(sizeof(struct dn_fib_table) + sizeof(struct dn_hash),
+- GFP_KERNEL);
+- if (t == NULL)
+- return NULL;
+-
+- t->n = n;
+- t->insert = dn_fib_table_insert;
+- t->delete = dn_fib_table_delete;
+- t->lookup = dn_fib_table_lookup;
+- t->flush = dn_fib_table_flush;
+- t->dump = dn_fib_table_dump;
+- hlist_add_head_rcu(&t->hlist, &dn_fib_table_hash[h]);
+-
+- return t;
+-}
+-
+-struct dn_fib_table *dn_fib_empty_table(void)
+-{
+- u32 id;
+-
+- for(id = RT_TABLE_MIN; id <= RT_TABLE_MAX; id++)
+- if (dn_fib_get_table(id, 0) == NULL)
+- return dn_fib_get_table(id, 1);
+- return NULL;
+-}
+-
+-void dn_fib_flush(void)
+-{
+- int flushed = 0;
+- struct dn_fib_table *tb;
+- unsigned int h;
+-
+- for (h = 0; h < DN_FIB_TABLE_HASHSZ; h++) {
+- hlist_for_each_entry(tb, &dn_fib_table_hash[h], hlist)
+- flushed += tb->flush(tb);
+- }
+-
+- if (flushed)
+- dn_rt_cache_flush(-1);
+-}
+-
+-void __init dn_fib_table_init(void)
+-{
+- dn_hash_kmem = kmem_cache_create("dn_fib_info_cache",
+- sizeof(struct dn_fib_info),
+- 0, SLAB_HWCACHE_ALIGN,
+- NULL);
+-}
+-
+-void __exit dn_fib_table_cleanup(void)
+-{
+- struct dn_fib_table *t;
+- struct hlist_node *next;
+- unsigned int h;
+-
+- write_lock(&dn_fib_tables_lock);
+- for (h = 0; h < DN_FIB_TABLE_HASHSZ; h++) {
+- hlist_for_each_entry_safe(t, next, &dn_fib_table_hash[h],
+- hlist) {
+- hlist_del(&t->hlist);
+- kfree(t);
+- }
+- }
+- write_unlock(&dn_fib_tables_lock);
+-}
+diff --git a/net/decnet/dn_timer.c b/net/decnet/dn_timer.c
+deleted file mode 100644
+index f430daed24a0d..0000000000000
+--- a/net/decnet/dn_timer.c
++++ /dev/null
+@@ -1,104 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/*
+- * DECnet An implementation of the DECnet protocol suite for the LINUX
+- * operating system. DECnet is implemented using the BSD Socket
+- * interface as the means of communication with the user level.
+- *
+- * DECnet Socket Timer Functions
+- *
+- * Author: Steve Whitehouse <SteveW@ACM.org>
+- *
+- *
+- * Changes:
+- * Steve Whitehouse : Made keepalive timer part of the same
+- * timer idea.
+- * Steve Whitehouse : Added checks for sk->sock_readers
+- * David S. Miller : New socket locking
+- * Steve Whitehouse : Timer grabs socket ref.
+- */
+-#include <linux/net.h>
+-#include <linux/socket.h>
+-#include <linux/skbuff.h>
+-#include <linux/netdevice.h>
+-#include <linux/timer.h>
+-#include <linux/spinlock.h>
+-#include <net/sock.h>
+-#include <linux/atomic.h>
+-#include <linux/jiffies.h>
+-#include <net/flow.h>
+-#include <net/dn.h>
+-
+-/*
+- * Slow timer is for everything else (n * 500mS)
+- */
+-
+-#define SLOW_INTERVAL (HZ/2)
+-
+-static void dn_slow_timer(unsigned long arg);
+-
+-void dn_start_slow_timer(struct sock *sk)
+-{
+- setup_timer(&sk->sk_timer, dn_slow_timer, (unsigned long)sk);
+- sk_reset_timer(sk, &sk->sk_timer, jiffies + SLOW_INTERVAL);
+-}
+-
+-void dn_stop_slow_timer(struct sock *sk)
+-{
+- sk_stop_timer(sk, &sk->sk_timer);
+-}
+-
+-static void dn_slow_timer(unsigned long arg)
+-{
+- struct sock *sk = (struct sock *)arg;
+- struct dn_scp *scp = DN_SK(sk);
+-
+- bh_lock_sock(sk);
+-
+- if (sock_owned_by_user(sk)) {
+- sk_reset_timer(sk, &sk->sk_timer, jiffies + HZ / 10);
+- goto out;
+- }
+-
+- /*
+- * The persist timer is the standard slow timer used for retransmits
+- * in both connection establishment and disconnection as well as
+- * in the RUN state. The different states are catered for by changing
+- * the function pointer in the socket. Setting the timer to a value
+- * of zero turns it off. We allow the persist_fxn to turn the
+- * timer off in a permant way by returning non-zero, so that
+- * timer based routines may remove sockets. This is why we have a
+- * sock_hold()/sock_put() around the timer to prevent the socket
+- * going away in the middle.
+- */
+- if (scp->persist && scp->persist_fxn) {
+- if (scp->persist <= SLOW_INTERVAL) {
+- scp->persist = 0;
+-
+- if (scp->persist_fxn(sk))
+- goto out;
+- } else {
+- scp->persist -= SLOW_INTERVAL;
+- }
+- }
+-
+- /*
+- * Check for keepalive timeout. After the other timer 'cos if
+- * the previous timer caused a retransmit, we don't need to
+- * do this. scp->stamp is the last time that we sent a packet.
+- * The keepalive function sends a link service packet to the
+- * other end. If it remains unacknowledged, the standard
+- * socket timers will eventually shut the socket down. Each
+- * time we do this, scp->stamp will be updated, thus
+- * we won't try and send another until scp->keepalive has passed
+- * since the last successful transmission.
+- */
+- if (scp->keepalive && scp->keepalive_fxn && (scp->state == DN_RUN)) {
+- if (time_after_eq(jiffies, scp->stamp + scp->keepalive))
+- scp->keepalive_fxn(sk);
+- }
+-
+- sk_reset_timer(sk, &sk->sk_timer, jiffies + SLOW_INTERVAL);
+-out:
+- bh_unlock_sock(sk);
+- sock_put(sk);
+-}
+diff --git a/net/decnet/netfilter/Kconfig b/net/decnet/netfilter/Kconfig
+deleted file mode 100644
+index 8d7c109d5109d..0000000000000
+--- a/net/decnet/netfilter/Kconfig
++++ /dev/null
+@@ -1,16 +0,0 @@
+-#
+-# DECnet netfilter configuration
+-#
+-
+-menu "DECnet: Netfilter Configuration"
+- depends on DECNET && NETFILTER
+- depends on NETFILTER_ADVANCED
+-
+-config DECNET_NF_GRABULATOR
+- tristate "Routing message grabulator (for userland routing daemon)"
+- help
+- Enable this module if you want to use the userland DECnet routing
+- daemon. You will also need to enable routing support for DECnet
+- unless you just want to monitor routing messages from other nodes.
+-
+-endmenu
+diff --git a/net/decnet/netfilter/Makefile b/net/decnet/netfilter/Makefile
+deleted file mode 100644
+index 255c1ae9daeb0..0000000000000
+--- a/net/decnet/netfilter/Makefile
++++ /dev/null
+@@ -1,6 +0,0 @@
+-#
+-# Makefile for DECnet netfilter modules
+-#
+-
+-obj-$(CONFIG_DECNET_NF_GRABULATOR) += dn_rtmsg.o
+-
+diff --git a/net/decnet/netfilter/dn_rtmsg.c b/net/decnet/netfilter/dn_rtmsg.c
+deleted file mode 100644
+index ab395e55cd789..0000000000000
+--- a/net/decnet/netfilter/dn_rtmsg.c
++++ /dev/null
+@@ -1,161 +0,0 @@
+-/*
+- * DECnet An implementation of the DECnet protocol suite for the LINUX
+- * operating system. DECnet is implemented using the BSD Socket
+- * interface as the means of communication with the user level.
+- *
+- * DECnet Routing Message Grabulator
+- *
+- * (C) 2000 ChyGwyn Limited - http://www.chygwyn.com/
+- * This code may be copied under the GPL v.2 or at your option
+- * any later version.
+- *
+- * Author: Steven Whitehouse <steve@chygwyn.com>
+- *
+- */
+-#include <linux/module.h>
+-#include <linux/skbuff.h>
+-#include <linux/slab.h>
+-#include <linux/init.h>
+-#include <linux/netdevice.h>
+-#include <linux/netfilter.h>
+-#include <linux/spinlock.h>
+-#include <net/netlink.h>
+-#include <linux/netfilter_decnet.h>
+-
+-#include <net/sock.h>
+-#include <net/flow.h>
+-#include <net/dn.h>
+-#include <net/dn_route.h>
+-
+-static struct sock *dnrmg = NULL;
+-
+-
+-static struct sk_buff *dnrmg_build_message(struct sk_buff *rt_skb, int *errp)
+-{
+- struct sk_buff *skb = NULL;
+- size_t size;
+- sk_buff_data_t old_tail;
+- struct nlmsghdr *nlh;
+- unsigned char *ptr;
+- struct nf_dn_rtmsg *rtm;
+-
+- size = NLMSG_ALIGN(rt_skb->len) +
+- NLMSG_ALIGN(sizeof(struct nf_dn_rtmsg));
+- skb = nlmsg_new(size, GFP_ATOMIC);
+- if (!skb) {
+- *errp = -ENOMEM;
+- return NULL;
+- }
+- old_tail = skb->tail;
+- nlh = nlmsg_put(skb, 0, 0, 0, size, 0);
+- if (!nlh) {
+- kfree_skb(skb);
+- *errp = -ENOMEM;
+- return NULL;
+- }
+- rtm = (struct nf_dn_rtmsg *)nlmsg_data(nlh);
+- rtm->nfdn_ifindex = rt_skb->dev->ifindex;
+- ptr = NFDN_RTMSG(rtm);
+- skb_copy_from_linear_data(rt_skb, ptr, rt_skb->len);
+- nlh->nlmsg_len = skb->tail - old_tail;
+- return skb;
+-}
+-
+-static void dnrmg_send_peer(struct sk_buff *skb)
+-{
+- struct sk_buff *skb2;
+- int status = 0;
+- int group = 0;
+- unsigned char flags = *skb->data;
+-
+- switch (flags & DN_RT_CNTL_MSK) {
+- case DN_RT_PKT_L1RT:
+- group = DNRNG_NLGRP_L1;
+- break;
+- case DN_RT_PKT_L2RT:
+- group = DNRNG_NLGRP_L2;
+- break;
+- default:
+- return;
+- }
+-
+- skb2 = dnrmg_build_message(skb, &status);
+- if (skb2 == NULL)
+- return;
+- NETLINK_CB(skb2).dst_group = group;
+- netlink_broadcast(dnrmg, skb2, 0, group, GFP_ATOMIC);
+-}
+-
+-
+-static unsigned int dnrmg_hook(void *priv,
+- struct sk_buff *skb,
+- const struct nf_hook_state *state)
+-{
+- dnrmg_send_peer(skb);
+- return NF_ACCEPT;
+-}
+-
+-
+-#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err), NULL); return; } while (0)
+-
+-static inline void dnrmg_receive_user_skb(struct sk_buff *skb)
+-{
+- struct nlmsghdr *nlh = nlmsg_hdr(skb);
+-
+- if (skb->len < sizeof(*nlh) ||
+- nlh->nlmsg_len < sizeof(*nlh) ||
+- skb->len < nlh->nlmsg_len)
+- return;
+-
+- if (!netlink_capable(skb, CAP_NET_ADMIN))
+- RCV_SKB_FAIL(-EPERM);
+-
+- /* Eventually we might send routing messages too */
+-
+- RCV_SKB_FAIL(-EINVAL);
+-}
+-
+-static const struct nf_hook_ops dnrmg_ops = {
+- .hook = dnrmg_hook,
+- .pf = NFPROTO_DECNET,
+- .hooknum = NF_DN_ROUTE,
+- .priority = NF_DN_PRI_DNRTMSG,
+-};
+-
+-static int __init dn_rtmsg_init(void)
+-{
+- int rv = 0;
+- struct netlink_kernel_cfg cfg = {
+- .groups = DNRNG_NLGRP_MAX,
+- .input = dnrmg_receive_user_skb,
+- };
+-
+- dnrmg = netlink_kernel_create(&init_net, NETLINK_DNRTMSG, &cfg);
+- if (dnrmg == NULL) {
+- printk(KERN_ERR "dn_rtmsg: Cannot create netlink socket");
+- return -ENOMEM;
+- }
+-
+- rv = nf_register_net_hook(&init_net, &dnrmg_ops);
+- if (rv) {
+- netlink_kernel_release(dnrmg);
+- }
+-
+- return rv;
+-}
+-
+-static void __exit dn_rtmsg_fini(void)
+-{
+- nf_unregister_net_hook(&init_net, &dnrmg_ops);
+- netlink_kernel_release(dnrmg);
+-}
+-
+-
+-MODULE_DESCRIPTION("DECnet Routing Message Grabulator");
+-MODULE_AUTHOR("Steven Whitehouse <steve@chygwyn.com>");
+-MODULE_LICENSE("GPL");
+-MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_DNRTMSG);
+-
+-module_init(dn_rtmsg_init);
+-module_exit(dn_rtmsg_fini);
+-
+diff --git a/net/decnet/sysctl_net_decnet.c b/net/decnet/sysctl_net_decnet.c
+deleted file mode 100644
+index 55bf64a22b593..0000000000000
+--- a/net/decnet/sysctl_net_decnet.c
++++ /dev/null
+@@ -1,373 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/*
+- * DECnet An implementation of the DECnet protocol suite for the LINUX
+- * operating system. DECnet is implemented using the BSD Socket
+- * interface as the means of communication with the user level.
+- *
+- * DECnet sysctl support functions
+- *
+- * Author: Steve Whitehouse <SteveW@ACM.org>
+- *
+- *
+- * Changes:
+- * Steve Whitehouse - C99 changes and default device handling
+- * Steve Whitehouse - Memory buffer settings, like the tcp ones
+- *
+- */
+-#include <linux/mm.h>
+-#include <linux/sysctl.h>
+-#include <linux/fs.h>
+-#include <linux/netdevice.h>
+-#include <linux/string.h>
+-#include <net/neighbour.h>
+-#include <net/dst.h>
+-#include <net/flow.h>
+-
+-#include <linux/uaccess.h>
+-
+-#include <net/dn.h>
+-#include <net/dn_dev.h>
+-#include <net/dn_route.h>
+-
+-
+-int decnet_debug_level;
+-int decnet_time_wait = 30;
+-int decnet_dn_count = 1;
+-int decnet_di_count = 3;
+-int decnet_dr_count = 3;
+-int decnet_log_martians = 1;
+-int decnet_no_fc_max_cwnd = NSP_MIN_WINDOW;
+-
+-/* Reasonable defaults, I hope, based on tcp's defaults */
+-long sysctl_decnet_mem[3] = { 768 << 3, 1024 << 3, 1536 << 3 };
+-int sysctl_decnet_wmem[3] = { 4 * 1024, 16 * 1024, 128 * 1024 };
+-int sysctl_decnet_rmem[3] = { 4 * 1024, 87380, 87380 * 2 };
+-
+-#ifdef CONFIG_SYSCTL
+-extern int decnet_dst_gc_interval;
+-static int min_decnet_time_wait[] = { 5 };
+-static int max_decnet_time_wait[] = { 600 };
+-static int min_state_count[] = { 1 };
+-static int max_state_count[] = { NSP_MAXRXTSHIFT };
+-static int min_decnet_dst_gc_interval[] = { 1 };
+-static int max_decnet_dst_gc_interval[] = { 60 };
+-static int min_decnet_no_fc_max_cwnd[] = { NSP_MIN_WINDOW };
+-static int max_decnet_no_fc_max_cwnd[] = { NSP_MAX_WINDOW };
+-static char node_name[7] = "???";
+-
+-static struct ctl_table_header *dn_table_header = NULL;
+-
+-/*
+- * ctype.h :-)
+- */
+-#define ISNUM(x) (((x) >= '0') && ((x) <= '9'))
+-#define ISLOWER(x) (((x) >= 'a') && ((x) <= 'z'))
+-#define ISUPPER(x) (((x) >= 'A') && ((x) <= 'Z'))
+-#define ISALPHA(x) (ISLOWER(x) || ISUPPER(x))
+-#define INVALID_END_CHAR(x) (ISNUM(x) || ISALPHA(x))
+-
+-static void strip_it(char *str)
+-{
+- for(;;) {
+- switch (*str) {
+- case ' ':
+- case '\n':
+- case '\r':
+- case ':':
+- *str = 0;
+- /* Fallthrough */
+- case 0:
+- return;
+- }
+- str++;
+- }
+-}
+-
+-/*
+- * Simple routine to parse an ascii DECnet address
+- * into a network order address.
+- */
+-static int parse_addr(__le16 *addr, char *str)
+-{
+- __u16 area, node;
+-
+- while(*str && !ISNUM(*str)) str++;
+-
+- if (*str == 0)
+- return -1;
+-
+- area = (*str++ - '0');
+- if (ISNUM(*str)) {
+- area *= 10;
+- area += (*str++ - '0');
+- }
+-
+- if (*str++ != '.')
+- return -1;
+-
+- if (!ISNUM(*str))
+- return -1;
+-
+- node = *str++ - '0';
+- if (ISNUM(*str)) {
+- node *= 10;
+- node += (*str++ - '0');
+- }
+- if (ISNUM(*str)) {
+- node *= 10;
+- node += (*str++ - '0');
+- }
+- if (ISNUM(*str)) {
+- node *= 10;
+- node += (*str++ - '0');
+- }
+-
+- if ((node > 1023) || (area > 63))
+- return -1;
+-
+- if (INVALID_END_CHAR(*str))
+- return -1;
+-
+- *addr = cpu_to_le16((area << 10) | node);
+-
+- return 0;
+-}
+-
+-static int dn_node_address_handler(struct ctl_table *table, int write,
+- void __user *buffer,
+- size_t *lenp, loff_t *ppos)
+-{
+- char addr[DN_ASCBUF_LEN];
+- size_t len;
+- __le16 dnaddr;
+-
+- if (!*lenp || (*ppos && !write)) {
+- *lenp = 0;
+- return 0;
+- }
+-
+- if (write) {
+- len = (*lenp < DN_ASCBUF_LEN) ? *lenp : (DN_ASCBUF_LEN-1);
+-
+- if (copy_from_user(addr, buffer, len))
+- return -EFAULT;
+-
+- addr[len] = 0;
+- strip_it(addr);
+-
+- if (parse_addr(&dnaddr, addr))
+- return -EINVAL;
+-
+- dn_dev_devices_off();
+-
+- decnet_address = dnaddr;
+-
+- dn_dev_devices_on();
+-
+- *ppos += len;
+-
+- return 0;
+- }
+-
+- dn_addr2asc(le16_to_cpu(decnet_address), addr);
+- len = strlen(addr);
+- addr[len++] = '\n';
+-
+- if (len > *lenp) len = *lenp;
+-
+- if (copy_to_user(buffer, addr, len))
+- return -EFAULT;
+-
+- *lenp = len;
+- *ppos += len;
+-
+- return 0;
+-}
+-
+-static int dn_def_dev_handler(struct ctl_table *table, int write,
+- void __user *buffer,
+- size_t *lenp, loff_t *ppos)
+-{
+- size_t len;
+- struct net_device *dev;
+- char devname[17];
+-
+- if (!*lenp || (*ppos && !write)) {
+- *lenp = 0;
+- return 0;
+- }
+-
+- if (write) {
+- if (*lenp > 16)
+- return -E2BIG;
+-
+- if (copy_from_user(devname, buffer, *lenp))
+- return -EFAULT;
+-
+- devname[*lenp] = 0;
+- strip_it(devname);
+-
+- dev = dev_get_by_name(&init_net, devname);
+- if (dev == NULL)
+- return -ENODEV;
+-
+- if (dev->dn_ptr == NULL) {
+- dev_put(dev);
+- return -ENODEV;
+- }
+-
+- if (dn_dev_set_default(dev, 1)) {
+- dev_put(dev);
+- return -ENODEV;
+- }
+- *ppos += *lenp;
+-
+- return 0;
+- }
+-
+- dev = dn_dev_get_default();
+- if (dev == NULL) {
+- *lenp = 0;
+- return 0;
+- }
+-
+- strcpy(devname, dev->name);
+- dev_put(dev);
+- len = strlen(devname);
+- devname[len++] = '\n';
+-
+- if (len > *lenp) len = *lenp;
+-
+- if (copy_to_user(buffer, devname, len))
+- return -EFAULT;
+-
+- *lenp = len;
+- *ppos += len;
+-
+- return 0;
+-}
+-
+-static struct ctl_table dn_table[] = {
+- {
+- .procname = "node_address",
+- .maxlen = 7,
+- .mode = 0644,
+- .proc_handler = dn_node_address_handler,
+- },
+- {
+- .procname = "node_name",
+- .data = node_name,
+- .maxlen = 7,
+- .mode = 0644,
+- .proc_handler = proc_dostring,
+- },
+- {
+- .procname = "default_device",
+- .maxlen = 16,
+- .mode = 0644,
+- .proc_handler = dn_def_dev_handler,
+- },
+- {
+- .procname = "time_wait",
+- .data = &decnet_time_wait,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = proc_dointvec_minmax,
+- .extra1 = &min_decnet_time_wait,
+- .extra2 = &max_decnet_time_wait
+- },
+- {
+- .procname = "dn_count",
+- .data = &decnet_dn_count,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = proc_dointvec_minmax,
+- .extra1 = &min_state_count,
+- .extra2 = &max_state_count
+- },
+- {
+- .procname = "di_count",
+- .data = &decnet_di_count,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = proc_dointvec_minmax,
+- .extra1 = &min_state_count,
+- .extra2 = &max_state_count
+- },
+- {
+- .procname = "dr_count",
+- .data = &decnet_dr_count,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = proc_dointvec_minmax,
+- .extra1 = &min_state_count,
+- .extra2 = &max_state_count
+- },
+- {
+- .procname = "dst_gc_interval",
+- .data = &decnet_dst_gc_interval,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = proc_dointvec_minmax,
+- .extra1 = &min_decnet_dst_gc_interval,
+- .extra2 = &max_decnet_dst_gc_interval
+- },
+- {
+- .procname = "no_fc_max_cwnd",
+- .data = &decnet_no_fc_max_cwnd,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = proc_dointvec_minmax,
+- .extra1 = &min_decnet_no_fc_max_cwnd,
+- .extra2 = &max_decnet_no_fc_max_cwnd
+- },
+- {
+- .procname = "decnet_mem",
+- .data = &sysctl_decnet_mem,
+- .maxlen = sizeof(sysctl_decnet_mem),
+- .mode = 0644,
+- .proc_handler = proc_doulongvec_minmax
+- },
+- {
+- .procname = "decnet_rmem",
+- .data = &sysctl_decnet_rmem,
+- .maxlen = sizeof(sysctl_decnet_rmem),
+- .mode = 0644,
+- .proc_handler = proc_dointvec,
+- },
+- {
+- .procname = "decnet_wmem",
+- .data = &sysctl_decnet_wmem,
+- .maxlen = sizeof(sysctl_decnet_wmem),
+- .mode = 0644,
+- .proc_handler = proc_dointvec,
+- },
+- {
+- .procname = "debug",
+- .data = &decnet_debug_level,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = proc_dointvec,
+- },
+- { }
+-};
+-
+-void dn_register_sysctl(void)
+-{
+- dn_table_header = register_net_sysctl(&init_net, "net/decnet", dn_table);
+-}
+-
+-void dn_unregister_sysctl(void)
+-{
+- unregister_net_sysctl_table(dn_table_header);
+-}
+-
+-#else /* CONFIG_SYSCTL */
+-void dn_unregister_sysctl(void)
+-{
+-}
+-void dn_register_sysctl(void)
+-{
+-}
+-
+-#endif
+diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c
+index d5cdba8213a44..49e11bbf390cf 100644
+--- a/net/ipv6/ping.c
++++ b/net/ipv6/ping.c
+@@ -101,7 +101,8 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
+ addr_type = ipv6_addr_type(daddr);
+ if ((__ipv6_addr_needs_scope_id(addr_type) && !oif) ||
+ (addr_type & IPV6_ADDR_MAPPED) ||
+- (oif && sk->sk_bound_dev_if && oif != sk->sk_bound_dev_if))
++ (oif && sk->sk_bound_dev_if && oif != sk->sk_bound_dev_if &&
++ l3mdev_master_ifindex_by_index(sock_net(sk), oif) != sk->sk_bound_dev_if))
+ return -EINVAL;
+
+ /* TODO: use ip6_datagram_send_ctl to get options from cmsg */
+diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
+index 2cee032af46d2..96ae1c4ad9a91 100644
+--- a/net/netfilter/nfnetlink.c
++++ b/net/netfilter/nfnetlink.c
+@@ -411,7 +411,8 @@ ack:
+ * processed, this avoids that the same error is
+ * reported several times when replaying the batch.
+ */
+- if (nfnl_err_add(&err_list, nlh, err, &extack) < 0) {
++ if (err == -ENOMEM ||
++ nfnl_err_add(&err_list, nlh, err, &extack) < 0) {
+ /* We failed to enqueue an error, reset the
+ * list of errors and send OOM to userspace
+ * pointing to the batch header.
+diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
+index 03434e7295eb6..80ab383d54950 100644
+--- a/net/sctp/sm_statefuns.c
++++ b/net/sctp/sm_statefuns.c
+@@ -4312,7 +4312,7 @@ enum sctp_disposition sctp_sf_eat_auth(struct net *net,
+ SCTP_AUTH_NEWKEY, GFP_ATOMIC);
+
+ if (!ev)
+- return -ENOMEM;
++ return SCTP_DISPOSITION_NOMEM;
+
+ sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
+ SCTP_ULPEVENT(ev));
+diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
+index c8001471da6c3..506cbd1b22235 100644
+--- a/net/tipc/bearer.c
++++ b/net/tipc/bearer.c
+@@ -1072,7 +1072,7 @@ int tipc_nl_media_get(struct sk_buff *skb, struct genl_info *info)
+ struct tipc_nl_msg msg;
+ struct tipc_media *media;
+ struct sk_buff *rep;
+- struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1];
++ struct nlattr *attrs[TIPC_NLA_MEDIA_MAX + 1];
+
+ if (!info->attrs[TIPC_NLA_MEDIA])
+ return -EINVAL;
+@@ -1120,7 +1120,7 @@ int tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info)
+ int err;
+ char *name;
+ struct tipc_media *m;
+- struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1];
++ struct nlattr *attrs[TIPC_NLA_MEDIA_MAX + 1];
+
+ if (!info->attrs[TIPC_NLA_MEDIA])
+ return -EINVAL;
+diff --git a/tools/testing/selftests/ptp/testptp.c b/tools/testing/selftests/ptp/testptp.c
+index 5d2eae16f7ee5..f8b5e183e0063 100644
+--- a/tools/testing/selftests/ptp/testptp.c
++++ b/tools/testing/selftests/ptp/testptp.c
+@@ -504,11 +504,11 @@ int main(int argc, char *argv[])
+ interval = t2 - t1;
+ offset = (t2 + t1) / 2 - tp;
+
+- printf("system time: %lld.%u\n",
++ printf("system time: %lld.%09u\n",
+ (pct+2*i)->sec, (pct+2*i)->nsec);
+- printf("phc time: %lld.%u\n",
++ printf("phc time: %lld.%09u\n",
+ (pct+2*i+1)->sec, (pct+2*i+1)->nsec);
+- printf("system time: %lld.%u\n",
++ printf("system time: %lld.%09u\n",
+ (pct+2*i+2)->sec, (pct+2*i+2)->nsec);
+ printf("system/phc clock time offset is %" PRId64 " ns\n"
+ "system clock time delay is %" PRId64 " ns\n",