summaryrefslogtreecommitdiffstats
path: root/net/decnet
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-02-16 01:07:24 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-02-16 01:07:24 +0000
commit95db6b748fc86297827fbd9c9ef174d491c9ad89 (patch)
tree27a92a942821cde1edda9a1b088718d436b3efe4 /net/decnet
parent45b27b0a0652331d104c953a5b192d843fff88f8 (diff)
Merge with Linux 2.3.40.
Diffstat (limited to 'net/decnet')
-rw-r--r--net/decnet/Config.in3
-rw-r--r--net/decnet/Makefile2
-rw-r--r--net/decnet/TODO38
-rw-r--r--net/decnet/af_decnet.c89
-rw-r--r--net/decnet/dn_dev.c315
-rw-r--r--net/decnet/dn_fib.c1027
-rw-r--r--net/decnet/dn_neigh.c7
-rw-r--r--net/decnet/dn_nsp_in.c35
-rw-r--r--net/decnet/dn_nsp_out.c102
-rw-r--r--net/decnet/dn_route.c289
-rw-r--r--net/decnet/dn_rules.c372
-rw-r--r--net/decnet/dn_table.c907
-rw-r--r--net/decnet/sysctl_net_decnet.c124
13 files changed, 2233 insertions, 1077 deletions
diff --git a/net/decnet/Config.in b/net/decnet/Config.in
index 323ae86f7..b11cdfa22 100644
--- a/net/decnet/Config.in
+++ b/net/decnet/Config.in
@@ -4,5 +4,8 @@
bool ' DECnet: SIOCGIFCONF support' CONFIG_DECNET_SIOCGIFCONF
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool ' DECnet: router support (EXPERIMENTAL)' CONFIG_DECNET_ROUTER
+ if [ "$CONFIG_DECNET_ROUTER" = "y" ]; then
+ bool ' DECnet: use FWMARK value as routing key' CONFIG_DECNET_ROUTE_FWMARK
+ fi
fi
bool ' DECnet: raw socket support' CONFIG_DECNET_RAW
diff --git a/net/decnet/Makefile b/net/decnet/Makefile
index 8a44ccac8..1eeef6b40 100644
--- a/net/decnet/Makefile
+++ b/net/decnet/Makefile
@@ -5,7 +5,7 @@ O_OBJS := af_decnet.o dn_nsp_in.o dn_nsp_out.o dn_route.o dn_dev.o dn_neigh.o
M_OBJS := $(O_TARGET)
ifeq ($(CONFIG_DECNET_ROUTER),y)
-O_OBJS += dn_fib.o
+O_OBJS += dn_fib.o dn_rules.o dn_table.o
endif
ifeq ($(CONFIG_DECNET_RAW),y)
diff --git a/net/decnet/TODO b/net/decnet/TODO
index fe8184e03..c2e8cf47b 100644
--- a/net/decnet/TODO
+++ b/net/decnet/TODO
@@ -4,8 +4,6 @@ Steve's quick list of things that need finishing off:
o Proper timeouts on each neighbour (in routing mode) rather than
just the 60 second On-Ethernet cache value.
- o Routing stuff in dn_fib.c
-
o Misc. get/set_sockopt() functions [done for the time being, more later]
o Support for X.25 linklayer
@@ -16,9 +14,11 @@ Steve's quick list of things that need finishing off:
o PPP support (rfc1762)
- o sendmsg() in the raw socket layer
+ o sendmsg() in the raw socket layer (yes, its for sending routing messages)
- o Better filtering of traffic in raw sockets
+ o Better filtering of traffic in raw sockets. Aside from receiving routing
+ messages, there really doesn't seem to be a lot else that raw sockets
+ could be useful for... suggestions on a postcard please :-)
o Fix /proc for raw sockets
@@ -33,19 +33,31 @@ Steve's quick list of things that need finishing off:
o check MSG_TRUNC, MSG_CTRUNC are set where they should be.
- o Work out if I really need support for rtnetlink "link" messages and if
- so how they should be handled.
-
- o More rtnetlink "route" message support & testing of this code
-
- o Routing ioctl() support
-
o Start to hack together user level software and add more DECnet support
in ifconfig for example.
- o Fix conninit_rx to check out each CI before queuing it
+ o Fix conninit_rx to check out each CI before queuing it. Support code is
+ now in place, so this should be easy.
- o Work out which errors we can return from conninit_rx, and how to do it
+ o Work out which errors we can return from conninit_rx. Support code is
+ now in place, so this should be easy.
o Check out receiving of errors in the light of what conninit_rx can return
+ o Test adding/deleting of routes
+
+ o Test route lookup
+
+ o Test /proc/net/decnet_route route listing works correctly (maybe I'll
+ change the format of this file... atm its very similar to the IPv4 route
+ file)
+
+ o Find all the commonality between DECnet and IPv4 routing code and extract
+ it into a small library of routines. [probably a project for 2.5.xx]
+
+ o Test ip_gre tunneling works... it did the last time I tested it and it
+ will have to if I'm to test routing properly.
+
+ o Hello messages should be generated for each primary address on each
+ interface.
+
diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c
index c4a9c44dd..a5237a712 100644
--- a/net/decnet/af_decnet.c
+++ b/net/decnet/af_decnet.c
@@ -135,7 +135,6 @@ static void dn_keepalive(struct sock *sk);
*/
dn_address decnet_address = 0;
unsigned char decnet_ether_address[ETH_ALEN] = { 0xAA, 0x00, 0x04, 0x00, 0x00, 0x00 };
-int decnet_node_type = DN_RT_INFO_ENDN;
static struct proto_ops dn_proto_ops;
rwlock_t dn_hash_lock = RW_LOCK_UNLOCKED;
@@ -484,10 +483,9 @@ static void dn_keepalive(struct sock *sk)
* 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. This is called by both
- * socket level and interrupt driven code.
+ * frames at correct times.
*/
-static int dn_destroy_timer(struct sock *sk)
+int dn_destroy_timer(struct sock *sk)
{
struct dn_scp *scp = &sk->protinfo.dn;
@@ -495,13 +493,13 @@ static int dn_destroy_timer(struct sock *sk)
switch(scp->state) {
case DN_DI:
- dn_send_disc(sk, NSP_DISCINIT, 0);
+ 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_send_disc(sk, NSP_DISCINIT, 0);
+ dn_nsp_send_disc(sk, NSP_DISCINIT, 0, GFP_ATOMIC);
if (scp->nsp_rxtshift >= decnet_dr_count)
scp->state = DN_DRC;
return 0;
@@ -509,7 +507,7 @@ static int dn_destroy_timer(struct sock *sk)
case DN_DN:
if (scp->nsp_rxtshift < decnet_dn_count) {
/* printk(KERN_DEBUG "dn_destroy_timer: DN\n"); */
- dn_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC);
+ dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, GFP_ATOMIC);
return 0;
}
}
@@ -529,7 +527,7 @@ static int dn_destroy_timer(struct sock *sk)
return 0;
}
-void dn_destroy_sock(struct sock *sk)
+static void dn_destroy_sock(struct sock *sk)
{
struct dn_scp *scp = &sk->protinfo.dn;
@@ -548,11 +546,10 @@ void dn_destroy_sock(struct sock *sk)
switch(scp->state) {
case DN_DN:
- dn_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC);
+ dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, GFP_KERNEL);
scp->persist_fxn = dn_destroy_timer;
scp->persist = dn_nsp_persist(sk);
break;
- case DN_CD:
case DN_CR:
scp->state = DN_DR;
goto disc_reject;
@@ -561,7 +558,7 @@ void dn_destroy_sock(struct sock *sk)
case DN_DI:
case DN_DR:
disc_reject:
- dn_send_disc(sk, NSP_DISCINIT, 0);
+ dn_nsp_send_disc(sk, NSP_DISCINIT, 0, GFP_KERNEL);
case DN_NC:
case DN_NR:
case DN_RJ:
@@ -569,6 +566,7 @@ disc_reject:
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;
@@ -1041,7 +1039,7 @@ static int dn_accept(struct socket *sock, struct socket *newsock, int flags)
if (newsk->protinfo.dn.accept_mode == ACC_IMMED) {
newsk->protinfo.dn.state = DN_CC;
- dn_send_conn_conf(newsk);
+ dn_send_conn_conf(newsk, GFP_KERNEL);
err = dn_wait_accept(newsock, flags);
}
@@ -1392,7 +1390,7 @@ static int __dn_setsockopt(struct socket *sock, int level,int optname, char *opt
return -EINVAL;
scp->state = DN_CC;
- dn_send_conn_conf(sk);
+ dn_send_conn_conf(sk, GFP_KERNEL);
err = dn_wait_accept(sock, sock->file->f_flags);
return err;
@@ -1403,7 +1401,7 @@ static int __dn_setsockopt(struct socket *sock, int level,int optname, char *opt
scp->state = DN_DR;
sk->shutdown = SHUTDOWN_MASK;
- dn_send_disc(sk, 0x38, 0);
+ dn_nsp_send_disc(sk, 0x38, 0, GFP_KERNEL);
break;
#ifdef CONFIG_DECNET_FW
@@ -1426,7 +1424,7 @@ static int __dn_setsockopt(struct socket *sock, int level,int optname, char *opt
if (copy_from_user(&tmp_fw, optval, optlen))
return -EFAULT;
err = dn_fw_ctl(optname, &tmp_fw, optlen);
- return -err; /* -0 is 0 after all */
+ return err;
#endif
default:
case DSO_LINKINFO:
@@ -1540,7 +1538,7 @@ static int dn_wait_run(struct sock *sk, int flags)
case DN_CR:
scp->state = DN_CC;
- dn_send_conn_conf(sk);
+ dn_send_conn_conf(sk, GFP_KERNEL);
return dn_wait_accept(sk->socket, (flags & MSG_DONTWAIT) ? O_NONBLOCK : 0);
case DN_CI:
case DN_CC:
@@ -2072,6 +2070,8 @@ void dn_unregister_sysctl(void);
void __init decnet_proto_init(struct net_proto *pro)
{
+ printk(KERN_INFO "NET4: DECnet for Linux: V.2.3.38s (C) 1995-1999 Linux DECnet Project Team\n");
+
sock_register(&dn_family_ops);
dev_add_pack(&dn_dix_packet_type);
register_netdevice_notifier(&dn_dev_notifier);
@@ -2084,10 +2084,6 @@ void __init decnet_proto_init(struct net_proto *pro)
dn_neigh_init();
dn_route_init();
-#ifdef CONFIG_DECNET_FW
- dn_fw_init();
-#endif /* CONFIG_DECNET_FW */
-
#ifdef CONFIG_DECNET_ROUTER
dn_fib_init();
#endif /* CONFIG_DECNET_ROUTER */
@@ -2095,7 +2091,6 @@ void __init decnet_proto_init(struct net_proto *pro)
#ifdef CONFIG_SYSCTL
dn_register_sysctl();
#endif /* CONFIG_SYSCTL */
- printk(KERN_INFO "NET4: DECnet for Linux: V.2.3.15s (C) 1995-1999 Linux DECnet Project Team\n");
}
@@ -2104,27 +2099,11 @@ static int __init decnet_setup(char *str)
{
unsigned short area = simple_strtoul(str, &str, 0);
unsigned short node = simple_strtoul(*str > 0 ? ++str : str, &str, 0);
- unsigned short type = simple_strtoul(*str > 0 ? ++str : str, &str, 0);
+ /* unsigned short type = simple_strtoul(*str > 0 ? ++str : str, &str, 0); */
decnet_address = dn_htons(area << 10 | node);
dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
- switch(type) {
- default:
- printk(KERN_INFO "Invalid DECnet node type, switching to EndNode\n");
- case 0:
- decnet_node_type = DN_RT_INFO_ENDN;
- break;
-#ifdef CONFIG_DECNET_ROUTER
- case 1:
- decnet_node_type = DN_RT_INFO_L1RT;
- break;
- case 2:
- decnet_node_type = DN_RT_INFO_L2RT;
- break;
-#endif /* CONFIG_DECNET_ROUTER */
- }
-
return 0;
}
@@ -2137,18 +2116,11 @@ MODULE_DESCRIPTION("The Linux DECnet Network Protocol");
MODULE_AUTHOR("Linux DECnet Project Team");
static int addr[2] = {0, 0};
-#ifdef CONFIG_DECNET_ROUTER
-static int type = 0;
-#endif
MODULE_PARM(addr, "2i");
MODULE_PARM_DESC(addr, "The DECnet address of this machine: area,node");
-#ifdef CONFIG_DECNET_ROUTER
-MODULE_PARM(type, "i");
-MODULE_PARM_DESC(type, "The type of this DECnet node: 0=EndNode, 1,2=Router");
-#endif
-int init_module(void)
+int __init init_module(void)
{
if (addr[0] > 63 || addr[0] < 0) {
printk(KERN_ERR "DECnet: Area must be between 0 and 63");
@@ -2163,31 +2135,12 @@ int init_module(void)
decnet_address = dn_htons((addr[0] << 10) | addr[1]);
dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
-#ifdef CONFIG_DECNET_ROUTER
- switch(type) {
- case 0:
- decnet_node_type = DN_RT_INFO_ENDN;
- break;
- case 1:
- decnet_node_type = DN_RT_INFO_L1RT;
- break;
- case 2:
- decnet_node_type = DN_RT_INFO_L2RT;
- break;
- default:
- printk(KERN_ERR "DECnet: Node type must be between 0 and 2 inclusive\n");
- return 1;
- }
-#else
- decnet_node_type = DN_RT_INFO_ENDN;
-#endif
-
decnet_proto_init(NULL);
return 0;
}
-void cleanup_module(void)
+void __exit cleanup_module(void)
{
#ifdef CONFIG_SYSCTL
dn_unregister_sysctl();
@@ -2199,10 +2152,6 @@ void cleanup_module(void)
dn_neigh_cleanup();
dn_dev_cleanup();
-#ifdef CONFIG_DECNET_FW
- /* dn_fw_cleanup(); */
-#endif /* CONFIG_DECNET_FW */
-
#ifdef CONFIG_DECNET_ROUTER
dn_fib_cleanup();
#endif /* CONFIG_DECNET_ROUTER */
diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c
index f024b8cf9..80bf05b5f 100644
--- a/net/decnet/dn_dev.c
+++ b/net/decnet/dn_dev.c
@@ -17,6 +17,8 @@
* 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 - its a user space issue now
*/
#include <linux/config.h>
@@ -59,16 +61,17 @@ static void rtmsg_ifa(int event, struct dn_ifaddr *ifa);
static int dn_eth_up(struct net_device *);
static void dn_send_brd_hello(struct net_device *dev);
+#if 0
static void dn_send_ptp_hello(struct net_device *dev);
+#endif
static struct dn_dev_parms dn_dev_list[] = {
{
ARPHRD_ETHER, /* Ethernet */
DN_DEV_BCAST,
DN_DEV_S_RU,
- 1,
+ 0,
1498,
- 10,
1,
10,
0,
@@ -76,7 +79,6 @@ static struct dn_dev_parms dn_dev_list[] = {
NET_DECNET_CONF_ETHER,
dn_eth_up,
NULL,
- NULL,
dn_send_brd_hello,
NULL
},
@@ -84,9 +86,8 @@ static struct dn_dev_parms dn_dev_list[] = {
ARPHRD_IPGRE, /* DECnet tunneled over GRE in IP */
DN_DEV_BCAST,
DN_DEV_S_RU,
- 2,
+ 0,
1400,
- 10,
1,
10,
0,
@@ -94,7 +95,6 @@ static struct dn_dev_parms dn_dev_list[] = {
NET_DECNET_CONF_GRE,
NULL,
NULL,
- NULL,
dn_send_brd_hello,
NULL
},
@@ -103,9 +103,8 @@ static struct dn_dev_parms dn_dev_list[] = {
ARPHRD_X25, /* Bog standard X.25 */
DN_DEV_UCAST,
DN_DEV_S_DS,
- 5,
+ 0,
230,
- 10 * 60,
1,
120,
0,
@@ -113,7 +112,6 @@ static struct dn_dev_parms dn_dev_list[] = {
NET_DECNET_CONF_X25,
NULL,
NULL,
- NULL,
dn_send_ptp_hello,
NULL
},
@@ -123,9 +121,8 @@ static struct dn_dev_parms dn_dev_list[] = {
ARPHRD_PPP, /* DECnet over PPP */
DN_DEV_BCAST,
DN_DEV_S_RU,
- 5,
+ 0,
230,
- 10,
1,
10,
0,
@@ -133,7 +130,6 @@ static struct dn_dev_parms dn_dev_list[] = {
NET_DECNET_CONF_PPP,
NULL,
NULL,
- NULL,
dn_send_brd_hello,
NULL
},
@@ -143,9 +139,8 @@ static struct dn_dev_parms dn_dev_list[] = {
ARPHRD_DDCMP, /* DECnet over DDCMP */
DN_DEV_UCAST,
DN_DEV_S_DS,
- 5,
+ 0,
230,
- 10 * 60,
1,
120,
0,
@@ -153,7 +148,6 @@ static struct dn_dev_parms dn_dev_list[] = {
NET_DECNET_CONF_DDCMP,
NULL,
NULL,
- NULL,
dn_send_ptp_hello,
NULL
},
@@ -164,7 +158,6 @@ static struct dn_dev_parms dn_dev_list[] = {
DN_DEV_S_RU,
0,
1498,
- 10,
1,
10,
0,
@@ -172,7 +165,6 @@ static struct dn_dev_parms dn_dev_list[] = {
NET_DECNET_CONF_LOOPBACK,
NULL,
NULL,
- NULL,
dn_send_brd_hello,
NULL
}
@@ -188,22 +180,20 @@ 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 */
-#ifdef CONFIG_DECNET_ROUTER
-static int min_t1[] = { 1 };
-static int max_t1[] = { 8191 }; /* No max specified, so made it the same as t3 */
-static int min_cost[] = { 0 };
-static int max_cost[] = { 25 }; /* From DECnet spec */
+
static int min_priority[] = { 0 };
static int max_priority[] = { 127 }; /* From DECnet spec */
-#endif /* CONFIG_DECNET_ROUTER */
+
+static int dn_forwarding_proc(ctl_table *, int, struct file *,
+ void *, size_t *);
+static int dn_forwarding_sysctl(ctl_table *table, int *name, int nlen,
+ void *oldval, size_t *oldlenp,
+ void *newval, size_t newlen,
+ void **context);
static struct dn_dev_sysctl_table {
struct ctl_table_header *sysctl_header;
-#ifdef CONFIG_DECNET_ROUTER
- ctl_table dn_dev_vars[6];
-#else
- ctl_table dn_dev_vars[3];
-#endif
+ ctl_table dn_dev_vars[5];
ctl_table dn_dev_dev[2];
ctl_table dn_dev_conf_dir[2];
ctl_table dn_dev_proto_dir[2];
@@ -211,20 +201,16 @@ static struct dn_dev_sysctl_table {
} dn_dev_sysctl = {
NULL,
{
-#ifdef CONFIG_DECNET_ROUTER
- {NET_DECNET_CONF_DEV_COST, "cost", (void *)DN_DEV_PARMS_OFFSET(cost),
+ {NET_DECNET_CONF_DEV_FORWARDING, "forwarding",
+ (void *)DN_DEV_PARMS_OFFSET(forwarding),
sizeof(int), 0644, NULL,
- proc_dointvec_minmax, sysctl_intvec,
- NULL, &min_cost, &max_cost},
- {NET_DECNET_CONF_DEV_PRIORITY, "priority", (void *)DN_DEV_PARMS_OFFSET(priority),
+ dn_forwarding_proc, dn_forwarding_sysctl,
+ NULL, NULL, NULL},
+ {NET_DECNET_CONF_DEV_PRIORITY, "priority",
+ (void *)DN_DEV_PARMS_OFFSET(priority),
sizeof(int), 0644, NULL,
proc_dointvec_minmax, sysctl_intvec,
NULL, &min_priority, &max_priority},
- {NET_DECNET_CONF_DEV_T1, "t1", (void *)DN_DEV_PARMS_OFFSET(t1),
- sizeof(int), 0644, NULL,
- proc_dointvec_minmax, sysctl_intvec,
- NULL, &min_t1, &max_t1},
-#endif
{NET_DECNET_CONF_DEV_T2, "t2", (void *)DN_DEV_PARMS_OFFSET(t2),
sizeof(int), 0644, NULL,
proc_dointvec_minmax, sysctl_intvec,
@@ -274,6 +260,7 @@ static void dn_dev_sysctl_register(struct net_device *dev, struct dn_dev_parms *
t->dn_dev_proto_dir[0].de = NULL;
t->dn_dev_root_dir[0].child = t->dn_dev_proto_dir;
t->dn_dev_root_dir[0].de = NULL;
+ t->dn_dev_vars[0].extra1 = (void *)dev;
t->sysctl_header = register_sysctl_table(t->dn_dev_root_dir, 0);
if (t->sysctl_header == NULL)
@@ -291,6 +278,90 @@ static void dn_dev_sysctl_unregister(struct dn_dev_parms *parms)
kfree(t);
}
}
+
+
+static int dn_forwarding_proc(ctl_table *table, int write,
+ struct file *filep,
+ void *buffer, size_t *lenp)
+{
+#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 = dev->dn_ptr;
+ old = dn_db->parms.forwarding;
+
+ err = proc_dointvec(table, write, filep, buffer, lenp);
+
+ 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
+}
+
+static int dn_forwarding_sysctl(ctl_table *table, int *name, int nlen,
+ void *oldval, size_t *oldlenp,
+ void *newval, size_t newlen,
+ void **context)
+{
+#ifdef CONFIG_DECNET_ROUTER
+ struct net_device *dev = table->extra1;
+ struct dn_dev *dn_db;
+ int value;
+
+ if (table->extra1 == NULL)
+ return -EINVAL;
+
+ dn_db = dev->dn_ptr;
+
+ if (newval && newlen) {
+ if (newlen != sizeof(int))
+ return -EINVAL;
+
+ get_user(value, (int *)newval);
+ if (value < 0)
+ return -EINVAL;
+ if (value > 2)
+ return -EINVAL;
+
+ if (dn_db->parms.down)
+ dn_db->parms.down(dev);
+ dn_db->parms.forwarding = value;
+ if (dn_db->parms.up)
+ dn_db->parms.up(dev);
+ }
+
+ return 0;
+#else
+ return -EINVAL;
+#endif
+}
+
#else /* CONFIG_SYSCTL */
static void dn_dev_sysctl_unregister(struct dn_dev_parms *parms)
{
@@ -372,19 +443,6 @@ static int dn_dev_set_ifa(struct net_device *dev, struct dn_ifaddr *ifa)
return dn_dev_insert_ifa(dn_db, ifa);
}
-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(ifindex);
- if (dev) {
- dn_dev = dev->dn_ptr;
- dev_put(dev);
- }
-
- return dn_dev;
-}
-
int dn_dev_ioctl(unsigned int cmd, void *arg)
{
@@ -471,6 +529,18 @@ rarok:
}
#ifdef CONFIG_RTNETLINK
+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(ifindex);
+ if (dev) {
+ dn_dev = dev->dn_ptr;
+ dev_put(dev);
+ }
+
+ return dn_dev;
+}
static int dn_dev_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
@@ -577,39 +647,6 @@ static void rtmsg_ifa(int event, struct dn_ifaddr *ifa)
netlink_broadcast(rtnl, skb, 0, RTMGRP_DECnet_IFADDR, GFP_KERNEL);
}
-static int dn_dev_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
- int type, u32 pid, u32 seq)
-{
- struct ifinfomsg *r;
- struct nlmsghdr *nlh;
- unsigned char *b = skb->tail;
- struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
- unsigned char priority = dn_db->parms.priority;
- unsigned short cost = dn_db->parms.cost;
-
- nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*r));
- if (pid) nlh->nlmsg_flags |= NLM_F_MULTI;
- r = NLMSG_DATA(nlh);
-
- r->ifi_family = AF_DECnet;
- r->ifi_type = dev->type;
- r->ifi_index = dev->ifindex;
- r->ifi_flags = dev->flags;
- r->ifi_change = ~0U;
-
- RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name);
- RTA_PUT(skb, IFLA_COST, sizeof(unsigned short), &cost);
- RTA_PUT(skb, IFLA_PRIORITY, sizeof(unsigned char), &priority);
-
- nlh->nlmsg_len = skb->tail - b;
- return skb->len;
-
-nlmsg_failure:
-rtattr_failure:
- skb_trim(skb, b - skb->data);
- return -1;
-}
-
static int dn_dev_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
{
int idx, dn_idx;
@@ -648,45 +685,6 @@ done:
return skb->len;
}
-static int dn_dev_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
-{
- int idx;
- int s_idx = cb->args[0];
- struct net_device *dev;
-
- read_lock(&dev_base_lock);
- for(dev=dev_base, idx=0; dev; dev = dev->next) {
- if (!dev->dn_ptr)
- continue;
- idx++;
- if (idx < s_idx)
- continue;
- if (dn_dev_fill_ifinfo(skb, dev, RTM_NEWLINK, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq) <= 0)
- break;
- }
- read_unlock(&dev_base_lock);
- cb->args[0] = idx;
-
- return skb->len;
-}
-
-static void dn_dev_ifinfo(int type, struct net_device *dev)
-{
- struct sk_buff *skb;
- int size = NLMSG_GOODSIZE;
-
- skb = alloc_skb(size, GFP_KERNEL);
- if (!skb)
- return;
-
- if (dn_dev_fill_ifinfo(skb, dev, type, 0, 0) < 0) {
- kfree_skb(skb);
- return;
- }
-
- NETLINK_CB(skb).dst_groups = RTMGRP_LINK;
- netlink_broadcast(rtnl, skb, 0, NETLINK_CB(skb).dst_groups, GFP_KERNEL);
-}
#endif /* CONFIG_RTNETLINK */
static void dn_send_endnode_hello(struct net_device *dev)
@@ -793,7 +791,8 @@ static void dn_send_router_hello(struct net_device *dev)
*ptr++ = 0;
memcpy(ptr, decnet_ether_address, ETH_ALEN);
ptr += ETH_ALEN;
- *ptr++ = (unsigned char)decnet_node_type;
+ *ptr++ = dn_db->parms.forwarding == 1 ?
+ DN_RT_INFO_L1RT : DN_RT_INFO_L2RT;
*((unsigned short *)ptr) = dn_htons(dn_db->parms.blksize);
ptr += 2;
*ptr++ = 0; /* Priority */
@@ -829,7 +828,9 @@ static void dn_send_router_hello(struct net_device *dev)
static void dn_send_brd_hello(struct net_device *dev)
{
- if (decnet_node_type == DN_RT_INFO_ENDN)
+ struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
+
+ if (dn_db->parms.forwarding == 0)
dn_send_endnode_hello(dev);
else
dn_send_router_hello(dev);
@@ -841,14 +842,16 @@ static void dn_send_brd_hello(struct net_device *dev)
}
#endif
+#if 0
static void dn_send_ptp_hello(struct net_device *dev)
{
int tdlen = 16;
int size = dev->hard_header_len + 2 + 4 + tdlen;
struct sk_buff *skb = dn_alloc_skb(NULL, size, GFP_ATOMIC);
- /* struct dn_dev *dn_db = dev->dn_ptr; */
- unsigned char *ptr;
+ struct dn_dev *dn_db = dev->dn_ptr;
int i;
+ unsigned char *ptr;
+ struct dn_neigh *dn = (struct dn_neigh *)dn_db->router;
if (skb == NULL)
return ;
@@ -865,27 +868,24 @@ static void dn_send_ptp_hello(struct net_device *dev)
for(i = 0; i < tdlen; i++)
*ptr++ = 0252;
-#if 0
- if (dn_db->router) {
- struct dn_neigh *dn = (struct dn_neigh *)dn_db->router;
- if (memcmp(dn->addr, decnet_ether_address, ETH_ALEN) == 0) {
- struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (dn_am_i_a_router(dn, dn_db)) {
+ struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
+ if (skb2) {
dn_rt_finish_output(skb2, dn_rt_all_end_mcast);
}
}
-#endif
dn_rt_finish_output(skb, dn_rt_all_rt_mcast);
}
+#endif
static int dn_eth_up(struct net_device *dev)
{
struct dn_dev *dn_db = dev->dn_ptr;
- if (decnet_node_type == DN_RT_INFO_ENDN)
+ if (dn_db->parms.forwarding == 0)
dev_mc_add(dev, dn_rt_all_end_mcast, ETH_ALEN, 0);
-
- if (decnet_node_type == DN_RT_INFO_L1RT || decnet_node_type == DN_RT_INFO_L2RT)
+ else
dev_mc_add(dev, dn_rt_all_rt_mcast, ETH_ALEN, 0);
dev_mc_upload(dev);
@@ -902,18 +902,6 @@ static void dn_dev_timer_func(unsigned long arg)
struct net_device *dev = (struct net_device *)arg;
struct dn_dev *dn_db = dev->dn_ptr;
-#ifdef CONFIG_DECNET_ROUTER
- if (decnet_node_type == DN_RT_INFO_L1RT || decnet_node_type == DN_RT_INFO_L2RT) {
- if (dn_db->t1 <= dn_db->parms.t2) {
- if (dn_db->parms.timer1)
- dn_db->parms.timer1(dev);
- dn_db->t1 = dn_db->parms.t1;
- } else {
- dn_db->t1 -= dn_db->parms.t2;
- }
- }
-#endif /* CONFIG_DECNET_ROUTER */
-
if (dn_db->t3 <= dn_db->parms.t2) {
if (dn_db->parms.timer3)
dn_db->parms.timer3(dev);
@@ -929,11 +917,6 @@ static void dn_dev_set_timer(struct net_device *dev)
{
struct dn_dev *dn_db = dev->dn_ptr;
-#ifdef CONFIG_DECNET_ROUTER
- if (dn_db->parms.t2 > dn_db->parms.t1)
- dn_db->parms.t2 = dn_db->parms.t1;
-#endif
-
if (dn_db->parms.t2 > dn_db->parms.t3)
dn_db->parms.t2 = dn_db->parms.t3;
@@ -987,10 +970,6 @@ struct dn_dev *dn_dev_create(struct net_device *dev, int *err)
dn_dev_set_timer(dev);
-#ifdef CONFIG_RTNETLINK
- dn_dev_ifinfo(RTM_NEWLINK, dev);
-#endif
-
*err = 0;
return dn_db;
}
@@ -1034,10 +1013,6 @@ static void dn_dev_delete(struct net_device *dev)
del_timer(&dn_db->timer);
synchronize_bh();
-#ifdef CONFIG_RTNETLINK
- dn_dev_ifinfo(RTM_DELLINK, dev);
-#endif
-
dn_dev_sysctl_unregister(&dn_db->parms);
neigh_ifdown(&dn_neigh_table, dev);
@@ -1190,10 +1165,10 @@ static int decnet_dev_get_info(char *buffer, char **start, off_t offset, int len
if ((dn_db = (struct dn_dev *)dev->dn_ptr) == NULL)
continue;
- len += sprintf(buffer + len, "%-8s %1s %04lu %04lu %04lu %04lu %04hu %03d %02x %-10s %-7s %-7s\n",
+ len += sprintf(buffer + len, "%-8s %1s %04u %04u %04lu %04lu %04hu %03d %02x %-10s %-7s %-7s\n",
dev->name ? dev->name : "???",
dn_type2asc(dn_db->parms.mode),
- dn_db->t1, dn_db->parms.t1,
+ 0, 0,
dn_db->t3, dn_db->parms.t3,
dn_db->parms.blksize,
dn_db->parms.priority,
@@ -1229,13 +1204,14 @@ static struct rtnetlink_link dnet_rtnetlink_table[RTM_MAX-RTM_BASE+1] =
{
{ NULL, NULL, },
{ NULL, NULL, },
- { NULL, dn_dev_dump_ifinfo, },
+ { NULL, NULL, },
{ NULL, NULL, },
{ dn_dev_rtm_newaddr, NULL, },
{ dn_dev_rtm_deladdr, NULL, },
{ NULL, dn_dev_dump_ifaddr, },
{ NULL, NULL, },
+
#ifdef CONFIG_DECNET_ROUTER
{ dn_fib_rtm_newroute, NULL, },
{ dn_fib_rtm_delroute, NULL, },
@@ -1252,10 +1228,17 @@ static struct rtnetlink_link dnet_rtnetlink_table[RTM_MAX-RTM_BASE+1] =
{ NULL, NULL, },
{ NULL, NULL, },
+#ifdef CONFIG_DECNET_ROUTER
+ { dn_fib_rtm_newrule, NULL, },
+ { dn_fib_rtm_delrule, NULL, },
+ { NULL, dn_fib_dump_rules, },
+ { NULL, NULL, }
+#else
{ NULL, NULL, },
{ NULL, NULL, },
{ NULL, NULL, },
{ NULL, NULL, }
+#endif
};
#endif /* CONFIG_RTNETLINK */
@@ -1284,8 +1267,7 @@ void __init dn_dev_init(void)
#endif /* CONFIG_SYSCTL */
}
-#if defined(CONFIG_DECNET_MODULE)
-void dn_dev_cleanup(void)
+void __exit dn_dev_cleanup(void)
{
#ifdef CONFIG_RTNETLINK
rtnetlink_links[PF_DECnet] = NULL;
@@ -1309,4 +1291,3 @@ void dn_dev_cleanup(void)
dn_dev_devices_off();
}
-#endif /* CONFIG_DECNET_MODULE */
diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c
index 358e9a184..aff2dc05d 100644
--- a/net/decnet/dn_fib.c
+++ b/net/decnet/dn_fib.c
@@ -3,13 +3,15 @@
* operating system. DECnet is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
- * DECnet Routing Forwarding Information Base
+ * 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.
*
*/
#include <linux/config.h>
@@ -30,451 +32,376 @@
#include <net/neighbour.h>
#include <net/dst.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>
-/*
- * N.B. Some of the functions here should really be inlines, but
- * I'll sort out that when its all working properly, for now the
- * stack frames will be useful for debugging.
- */
-#define DN_NUM_TABLES 255
-#define DN_MIN_TABLE 1
-#define DN_L1_TABLE 1
-#define DN_L2_TABLE 2
-
-#ifdef CONFIG_RTNETLINK
-static int dn_fib_table_dump(struct dn_fib_table *t, struct sk_buff *skb, struct netlink_callback *cb);
-static void dn_rtmsg_fib(int event, int table, struct dn_fib_action *fa, struct nlmsghdr *nlh, struct netlink_skb_parms *req);
-extern int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb);
-#endif /* CONFIG_RTNETLINK */
-
-static void dn_fib_del_tree(struct dn_fib_table *t);
-static struct dn_fib_table *dn_fib_tables[DN_NUM_TABLES + 1];
-static int dn_fib_allocs = 0;
-static int dn_fib_actions = 0;
+#define for_fib_info() { struct dn_fib_info *fi;\
+ for(fi = dn_fib_info_list; fi; fi = fi->fib_next)
+#define endfor_fib_info() }
-static struct dn_fib_node *dn_fib_alloc(void)
-{
- struct dn_fib_node *fn;
+#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++)
- fn = kmalloc(sizeof(struct dn_fib_node), GFP_KERNEL);
+#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++)
- if (fn) {
- memset(fn, 0, sizeof(struct dn_fib_node));
- dn_fib_allocs++;
- }
+#define endfor_nexthops(fi) }
- return fn;
-}
+#ifdef CONFIG_RTNETLINK
+extern int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb);
+#endif /* CONFIG_RTNETLINK */
-static __inline__ void dn_fib_free(struct dn_fib_node *fn)
-{
- kfree_s(fn, sizeof(struct dn_fib_node));
- dn_fib_allocs--;
-}
+static struct dn_fib_info *dn_fib_info_list = NULL;
+static rwlock_t dn_fib_info_lock = RW_LOCK_UNLOCKED;
+int dn_fib_info_cnt;
+
+static struct
+{
+ int error;
+ u8 scope;
+} dn_fib_props[RTA_MAX+1] = {
+ { 0, RT_SCOPE_NOWHERE }, /* RTN_UNSPEC */
+ { 0, RT_SCOPE_UNIVERSE }, /* RTN_UNICAST */
+ { 0, RT_SCOPE_HOST }, /* RTN_LOCAL */
+ { -EINVAL, RT_SCOPE_NOWHERE }, /* RTN_BROADCAST */
+ { -EINVAL, RT_SCOPE_NOWHERE }, /* RTN_ANYCAST */
+ { -EINVAL, RT_SCOPE_NOWHERE }, /* RTN_MULTICAST */
+ { -EINVAL, RT_SCOPE_UNIVERSE }, /* RTN_BLACKHOLE */
+ { -EHOSTUNREACH, RT_SCOPE_UNIVERSE }, /* RTN_UNREACHABLE */
+ { -EACCES, RT_SCOPE_UNIVERSE }, /* RTN_PROHIBIT */
+ { -EAGAIN, RT_SCOPE_UNIVERSE }, /* RTN_THROW */
+ { -EINVAL, RT_SCOPE_NOWHERE }, /* RTN_NAT */
+ { -EINVAL, RT_SCOPE_NOWHERE } /* RTN_XRESOLVE */
+};
-static struct dn_fib_action *dn_fib_new_action(void)
+void dn_fib_free_info(struct dn_fib_info *fi)
{
- struct dn_fib_action *fa;
-
- fa = kmalloc(sizeof(struct dn_fib_action), GFP_KERNEL);
-
- if (fa) {
- memset(fa, 0, sizeof(struct dn_fib_action));
- dn_fib_actions++;
+ if (fi->fib_dead == 0) {
+ printk(KERN_DEBUG "DECnet: BUG! Attempt to free alive dn_fib_info\n");
+ return;
}
- return fa;
-}
-
-static __inline__ void dn_fib_del_action(struct dn_fib_action *fa)
-{
- if ((fa->fa_type == RTN_UNICAST) && fa->fa_neigh)
- neigh_release(fa->fa_neigh);
-
- kfree_s(fa, sizeof(struct dn_fib_action));
- dn_fib_actions--;
-}
-
-static struct dn_fib_node *dn_fib_follow(struct dn_fib_node *fn, dn_address key)
-{
- while(fn->fn_action == NULL)
- fn = DN_FIB_NEXT(fn, key);
-
- return fn;
-}
-
-
-static struct dn_fib_node *dn_fib_follow1(struct dn_fib_node *fn, dn_address key)
-{
- while((fn->fn_action == NULL) && (((key ^ fn->fn_key) >> fn->fn_shift) == 0))
- fn = DN_FIB_NEXT(fn, key);
-
- return fn;
+ change_nexthops(fi) {
+ if (nh->nh_dev)
+ dev_put(nh->nh_dev);
+ nh->nh_dev = NULL;
+ } endfor_nexthops(fi);
+ dn_fib_info_cnt--;
+ kfree(fi);
+}
+
+void dn_fib_release_info(struct dn_fib_info *fi)
+{
+ write_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);
+ }
+ write_unlock(&dn_fib_info_lock);
}
-
-static int dn_fib_table_insert1(struct dn_fib_table *t, struct dn_fib_node *leaf)
+static __inline__ int dn_fib_nh_comp(const struct dn_fib_info *fi, const struct dn_fib_info *ofi)
{
- struct dn_fib_node *fn, *fn1, *fn2;
- int shift = -1;
- dn_address match;
- dn_address cmpmask = 1;
-
- if (!t->root) {
- t->root = leaf;
- t->count++;
- return 0;
- }
-
- fn1 = dn_fib_follow1(t->root, leaf->fn_key);
- fn2 = fn1->fn_up;
-
- if (fn1->fn_key == leaf->fn_key)
- return -EEXIST;
-
- if ((fn = dn_fib_alloc()) == NULL)
- return -ENOBUFS;
-
- fn->fn_key = leaf->fn_key;
- match = fn1->fn_key ^ fn->fn_key;
-
- while(match) {
- match >>= 1;
- shift++;
- }
- cmpmask <<= shift;
-
- fn->fn_cmpmask = cmpmask;
- fn->fn_shift = shift;
-
- if (fn2) {
- DN_FIB_NEXT(fn2, fn->fn_key) = fn;
- } else {
- t->root = fn;
- }
-
- t->count++;
- fn->fn_up = fn2;
- DN_FIB_NEXT(fn, fn1->fn_key) = fn1;
- DN_FIB_NEXT(fn, leaf->fn_key) = leaf;
+ 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__ int dn_maskcmp(dn_address m1, dn_address m2)
+static __inline__ struct dn_fib_info *dn_fib_find_info(const struct dn_fib_info *nfi)
{
- int cmp = 0;
-
- while(m1 || m2) {
- if (m1 & 0x8000)
- cmp++;
- if (m2 & 0x8000)
- cmp--;
- m1 <<= 1;
- m2 <<= 1;
+ 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 &&
+ ((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;
+}
+
+u16 dn_fib_get_attr16(struct rtattr *attr, int attrlen, int type)
+{
+ while(RTA_OK(attr,attrlen)) {
+ if (attr->rta_type == type)
+ return *(u16*)RTA_DATA(attr);
+ attr = RTA_NEXT(attr, attrlen);
}
- return cmp;
+ return 0;
}
-
-static int dn_fib_table_insert(struct dn_fib_table *t, struct dn_fib_action *fa)
+static int dn_fib_count_nhs(struct rtattr *rta)
{
- struct dn_fib_node *fn;
- struct dn_fib_action **fap;
- int err;
- int cmp;
-
- if (t->root && ((fn = dn_fib_follow(t->root, fa->fa_key)) != NULL) &&
- (fn->fn_key == fa->fa_key))
- goto add_action;
-
- if ((fn = dn_fib_alloc()) == NULL)
- return -ENOBUFS;
-
- fn->fn_key = fa->fa_key;
- fn->fn_action = fa;
-
- if ((err = dn_fib_table_insert1(t, fn)) < 0)
- dn_fib_free(fn);
+ int nhs = 0;
+ struct rtnexthop *nhp = RTA_DATA(rta);
+ int nhlen = RTA_PAYLOAD(rta);
-#ifdef CONFIG_RTNETLINK
- if (!err)
- dn_rtmsg_fib(RTM_NEWROUTE, t->n, fa, NULL, NULL);
-#endif /* CONFIG_RTNETLINK */
-
- return err;
-
-add_action:
- fap = &fn->fn_action;
-
- for(; *fap; fap = &((*fap)->fa_next)) {
- if ((cmp = dn_maskcmp((*fap)->fa_mask, fa->fa_mask)) > 0)
- break;
- if (cmp < 0)
- continue;
- if ((*fap)->fa_cost > fa->fa_cost)
- break;
+ while(nhlen >= (int)sizeof(struct rtnexthop)) {
+ if ((nhlen -= nhp->rtnh_len) < 0)
+ return 0;
+ nhs++;
+ nhp = RTNH_NEXT(nhp);
}
- fa->fa_next = *fap;
- *fap = fa;
-
-#ifdef CONFIG_RTNETLINK
- dn_rtmsg_fib(RTM_NEWROUTE, t->n, fa, NULL, NULL);
-#endif /* CONFIG_RTNETLINK */
-
- return 0;
+ return nhs;
}
-static int dn_fib_table_delete1(struct dn_fib_table *t, struct dn_fib_node *fn)
+static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct rtattr *rta, const struct rtmsg *r)
{
- struct dn_fib_node *fn1 = fn->fn_up;
- struct dn_fib_node *fn2;
- struct dn_fib_node *fn3;
-
- if (fn == t->root) {
- t->root = NULL;
- t->count--;
- return 0;
- }
-
- if (fn1 == NULL)
- return -EINVAL;
+ struct rtnexthop *nhp = RTA_DATA(rta);
+ int nhlen = RTA_PAYLOAD(rta);
- fn2 = fn1->fn_up;
- fn3 = DN_FIB_NEXT(fn1, ~fn->fn_key);
+ change_nexthops(fi) {
+ int attrlen = nhlen - sizeof(struct rtnexthop);
+ if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0)
+ return -EINVAL;
- if (fn2)
- DN_FIB_NEXT(fn2, fn1->fn_key) = fn3;
- else
- t->root = fn3;
+ nh->nh_flags = (r->rtm_flags&~0xFF) | nhp->rtnh_flags;
+ nh->nh_oif = nhp->rtnh_ifindex;
+ nh->nh_weight = nhp->rtnh_hops + 1;
- fn3->fn_up = fn2;
+ if (attrlen) {
+ nh->nh_gw = dn_fib_get_attr16(RTNH_DATA(nhp), attrlen, RTA_GATEWAY);
+ }
+ nhp = RTNH_NEXT(nhp);
+ } endfor_nexthops(fi);
- dn_fib_free(fn1);
- t->count--;
return 0;
}
-static int dn_fib_table_delete(struct dn_fib_table *t, struct dn_fib_action *fa)
+
+static int dn_fib_check_nh(const struct rtmsg *r, struct dn_fib_info *fi, struct dn_fib_nh *nh)
{
- struct dn_fib_res res;
- struct dn_fib_node *fn;
- struct dn_fib_action **fap, *old;
int err;
- res.res_type = 0;
- res.res_addr = fa->fa_key;
- res.res_mask = fa->fa_mask;
- res.res_ifindex = fa->fa_ifindex;
- res.res_proto = fa->fa_proto;
- res.res_cost = fa->fa_cost;
-
- if ((err = t->lookup(t, &res)) < 0)
- return err;
-
- fn = res.res_fn;
- fap = &fn->fn_action;
- while((*fap) != res.res_fa)
- fap = &((*fap)->fa_next);
- old = *fap;
- *fap = (*fap)->fa_next;
+ if (nh->nh_gw) {
+ struct dn_fib_key key;
+ struct dn_fib_res res;
- if (fn->fn_action == NULL)
- dn_fib_table_delete1(t, fn);
+ if (nh->nh_flags&RTNH_F_ONLINK) {
+ struct net_device *dev;
- if (t->root == NULL)
- dn_fib_del_tree(t);
-
-#ifdef CONFIG_RTNETLINK
- dn_rtmsg_fib(RTM_DELROUTE, t->n, old, NULL, NULL);
-#endif /* CONFIG_RTNETLINK */
-
- dn_fib_del_action(old);
+ if (r->rtm_scope >= RT_SCOPE_LINK)
+ return -EINVAL;
+ if ((dev = __dev_get_by_index(nh->nh_oif)) == NULL)
+ return -ENODEV;
+ if (!(dev->flags&IFF_UP))
+ return -ENETDOWN;
+ nh->nh_dev = dev;
+ atomic_inc(&dev->refcnt);
+ nh->nh_scope = RT_SCOPE_LINK;
+ return 0;
+ }
- return 0;
-}
+ memset(&key, 0, sizeof(key));
+ key.dst = nh->nh_gw;
+ key.oif = nh->nh_oif;
+ key.scope = r->rtm_scope + 1;
-static int dn_fib_search(struct dn_fib_node *fn, struct dn_fib_res *res)
-{
- struct dn_fib_action *fa = fn->fn_action;
+ if (key.scope < RT_SCOPE_LINK)
+ key.scope = RT_SCOPE_LINK;
- for(; fa; fa = fa->fa_next) {
- if ((fa->fa_key ^ res->res_addr) & fa->fa_mask)
- continue;
- if (res->res_ifindex && (res->res_ifindex != fa->fa_ifindex))
- continue;
- if (res->res_mask && (res->res_mask != fa->fa_mask))
- continue;
- if (res->res_proto && (res->res_proto != fa->fa_proto))
- continue;
- if (res->res_cost && (res->res_cost != fa->fa_cost))
- continue;
+ if ((err = dn_fib_lookup(&key, &res)) != 0)
+ return err;
- res->res_fn = fn;
- res->res_fa = fa;
- return 1;
+ 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)
+ atomic_inc(&nh->nh_dev->refcnt);
+ dn_fib_res_put(&res);
+ } else {
+ struct net_device *dev;
+
+ if (nh->nh_flags&(RTNH_F_PERVASIVE|RTNH_F_ONLINK))
+ return -EINVAL;
+
+ dev = __dev_get_by_index(nh->nh_oif);
+ if (dev == NULL || dev->dn_ptr == NULL)
+ return -ENODEV;
+ if (!(dev->flags&IFF_UP))
+ return -ENETDOWN;
+ nh->nh_dev = dev;
+ atomic_inc(&nh->nh_dev->refcnt);
+ nh->nh_scope = RT_SCOPE_HOST;
}
return 0;
}
-static int dn_fib_recurse(struct dn_fib_node *fn, struct dn_fib_res *res)
-{
- struct dn_fib_node *fn1;
- int err = -ENOENT;
-
- fn1 = dn_fib_follow(fn, res->res_addr);
-
- if (dn_fib_search(fn1, res))
- return 0;
- while((fn1 = fn1->fn_up) != fn)
- if ((err = dn_fib_recurse(DN_FIB_NEXT(fn1, ~res->res_addr), res)) == 0)
- break;
-
- return err;
-}
-
-static int dn_fib_table_lookup(struct dn_fib_table *t, struct dn_fib_res *res)
+struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta *rta, const struct nlmsghdr *nlh, int *errp)
{
- struct dn_fib_node *fn = t->root;
- int err = -ENOENT;
-
- if (t->root == NULL)
- return err;
-
- fn = dn_fib_follow(t->root, res->res_addr);
-
- if (dn_fib_search(fn, res))
- return 0;
-
- while((fn = fn->fn_up) != NULL)
- if ((err = dn_fib_recurse(DN_FIB_NEXT(fn, ~res->res_addr), res)) == 0)
- break;
+ int err;
+ struct dn_fib_info *fi = NULL;
+ struct dn_fib_info *ofi;
+ int nhs = 1;
- return err;
-}
+ if (dn_fib_props[r->rtm_type].scope > r->rtm_scope)
+ goto err_inval;
-static int dn_fib_table_walk_recurse(struct dn_fib_walker_t *fwt, struct dn_fib_node *fn)
-{
- struct dn_fib_table *t = fwt->table;
+ if (rta->rta_mp) {
+ nhs = dn_fib_count_nhs(rta->rta_mp);
+ if (nhs == 0)
+ goto err_inval;
+ }
- if (fn->fn_action) {
- fwt->fxn(fwt, fn);
+ fi = kmalloc(sizeof(*fi)+nhs*sizeof(struct dn_fib_nh), GFP_KERNEL);
+ err = -ENOBUFS;
+ if (fi == NULL)
+ goto failure;
+ memset(fi, 0, sizeof(*fi)+nhs*sizeof(struct dn_fib_nh));
+
+ fi->fib_protocol = r->rtm_protocol;
+ fi->fib_nhs = nhs;
+ fi->fib_flags = r->rtm_flags;
+ if (rta->rta_priority)
+ fi->fib_priority = *rta->rta_priority;
+ if (rta->rta_prefsrc)
+ memcpy(&fi->fib_prefsrc, rta->rta_prefsrc, 2);
+
+ if (rta->rta_mp) {
+ if ((err = dn_fib_get_nhs(fi, rta->rta_mp, r)) != 0)
+ goto failure;
+ if (rta->rta_oif && fi->fib_nh->nh_oif != *rta->rta_oif)
+ goto err_inval;
+ if (rta->rta_gw && memcmp(&fi->fib_nh->nh_gw, rta->rta_gw, 2))
+ goto err_inval;
} else {
- dn_fib_table_walk_recurse(fwt, t->root->fn_children[0]);
- dn_fib_table_walk_recurse(fwt, t->root->fn_children[1]);
+ struct dn_fib_nh *nh = fi->fib_nh;
+ if (rta->rta_oif)
+ nh->nh_oif = *rta->rta_oif;
+ if (rta->rta_gw)
+ memcpy(&nh->nh_gw, rta->rta_gw, 2);
+ nh->nh_flags = r->rtm_flags;
+ nh->nh_weight = 1;
}
- return 0;
-}
-
-static int dn_fib_table_walk(struct dn_fib_walker_t *fwt)
-{
- struct dn_fib_table *t = fwt->table;
-
- if (t->root != NULL) {
- if (t->root->fn_action) {
- fwt->fxn(fwt, t->root);
- } else {
- dn_fib_table_walk_recurse(fwt, t->root->fn_children[0]);
- dn_fib_table_walk_recurse(fwt, t->root->fn_children[1]);
- }
+ if (dn_fib_props[r->rtm_type].error) {
+ if (rta->rta_gw || rta->rta_oif || rta->rta_mp)
+ goto err_inval;
+ goto link_it;
}
- return 0;
-}
-
-static struct dn_fib_table *dn_fib_get_tree(int n, int create)
-{
- struct dn_fib_table *t;
-
- if (n < DN_MIN_TABLE)
- return NULL;
-
- if (n > DN_NUM_TABLES)
- return NULL;
+ if (r->rtm_scope > RT_SCOPE_HOST)
+ goto err_inval;
- if (dn_fib_tables[n])
- return dn_fib_tables[n];
+ if (r->rtm_scope == RT_SCOPE_HOST) {
+ struct dn_fib_nh *nh = fi->fib_nh;
- if (!create)
- return NULL;
-
- if ((t = kmalloc(sizeof(struct dn_fib_table), GFP_KERNEL)) == NULL)
- return NULL;
-
- dn_fib_tables[n] = t;
- memset(t, 0, sizeof(struct dn_fib_table));
+ /* 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(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)
+ }
- t->n = n;
- t->insert = dn_fib_table_insert;
- t->delete = dn_fib_table_delete;
- t->lookup = dn_fib_table_lookup;
- t->walk = dn_fib_table_walk;
-#ifdef CONFIG_RTNETLINK
- t->dump = dn_fib_table_dump;
+#if I_GET_AROUND_TO_FIXING_PREFSRC
+ if (fi->fib_prefsrc) {
+ if (r->rtm_type != RTN_LOCAL || rta->rta_dst == NULL ||
+ memcmp(&fi->fib_prefsrc, rta->rta_dst, 2))
+ if (dn_addr_type(fi->fib_prefsrc) != RTN_LOCAL)
+ goto err_inval;
+ }
#endif
- return t;
-}
-
-static void dn_fib_del_tree(struct dn_fib_table *t)
-{
- dn_fib_tables[t->n] = NULL;
+link_it:
+ if ((ofi = dn_fib_find_info(fi)) != NULL) {
+ fi->fib_dead = 1;
+ dn_fib_free_info(fi);
+ ofi->fib_treeref++;
+ return ofi;
+ }
- if (t) {
- kfree_s(t, sizeof(struct dn_fib_table));
+ fi->fib_treeref++;
+ atomic_inc(&fi->fib_clntref);
+ write_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;
+ dn_fib_info_cnt++;
+ write_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_resolve(struct dn_fib_res *res)
+void dn_fib_select_multipath(const struct dn_fib_key *key, struct dn_fib_res *res)
{
- int table = DN_L1_TABLE;
- int count = 0;
- struct dn_fib_action *fa;
- int err;
-
- if ((res->res_addr ^ dn_ntohs(decnet_address)) & 0xfc00)
- table = DN_L2_TABLE;
-
- for(;;) {
- struct dn_fib_table *t = dn_fib_get_tree(table, 0);
-
- if (t == NULL)
- return -ENOENT;
-
- if ((err = t->lookup(t, res)) < 0)
- return err;
+ struct dn_fib_info *fi = res->fi;
+ int w;
- if ((fa = res->res_fa) == NULL)
- return -ENOENT;
+ 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 (fa->fa_type != RTN_THROW)
- break;
+ w = jiffies % fi->fib_power;
- table = fa->fa_table;
+ 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;
+ return;
+ }
+ }
+ } endfor_nexthops(fi);
- if (count++ > DN_NUM_TABLES)
- return -ENOENT;
- }
+ printk(KERN_DEBUG "DECnet: BUG! dn_fib_select_multipath\n");
+}
- switch(fa->fa_type) {
- case RTN_PROHIBIT:
- case RTN_UNREACHABLE:
- return -fa->fa_error;
- }
- return 0;
-}
/*
* Punt to user via netlink for example, but for now
@@ -489,72 +416,19 @@ int dn_fib_rt_message(struct sk_buff *skb)
#ifdef CONFIG_RTNETLINK
-static int dn_fib_convert_rtm(struct dn_fib_action *fa,
- struct rtmsg *r, struct rtattr **rta,
- struct nlmsghdr *n,
- struct netlink_skb_parms *req)
-{
- dn_address dst, gw, mask = 0xffff;
- int ifindex = 0;
- struct neighbour *neigh;
- struct net_device *dev;
- unsigned char addr[ETH_ALEN];
-
- if (r->rtm_family != AF_DECnet)
- return -EINVAL;
-
- if (rta[RTA_DST-1])
- memcpy(&dst, RTA_DATA(rta[RTA_DST-1]), 2);
-
- if (rta[RTA_OIF-1])
- memcpy(&ifindex, RTA_DATA(rta[RTA_OIF-1]), sizeof(int));
-
- if (rta[RTA_GATEWAY-1])
- memcpy(&gw, RTA_DATA(rta[RTA_GATEWAY-1]), 2);
-
- fa->fa_key = dn_ntohs(dst);
- fa->fa_mask = dn_ntohs(mask);
- fa->fa_ifindex = ifindex;
- fa->fa_proto = r->rtm_protocol;
- fa->fa_type = r->rtm_type;
-
- switch(fa->fa_type) {
- case RTN_UNICAST:
- if ((dev = __dev_get_by_index(ifindex)) == NULL)
- return -ENODEV;
- dn_dn2eth(addr, dn_ntohs(gw));
- if ((neigh = __neigh_lookup(&dn_neigh_table, &addr, dev, 1)) == NULL)
- return -EHOSTUNREACH;
- fa->fa_neigh = neigh;
- break;
- case RTN_THROW:
- fa->fa_table = 0;
- break;
- case RTN_PROHIBIT:
- fa->fa_error = EPERM;
- break;
- case RTN_UNREACHABLE:
- fa->fa_error = EHOSTUNREACH;
- break;
- case RTN_BLACKHOLE:
- fa->fa_error = EINVAL;
- break;
- }
-
- return 0;
-}
static int dn_fib_check_attr(struct rtmsg *r, struct rtattr **rta)
{
- switch(r->rtm_type) {
- case RTN_UNICAST:
- case RTN_BLACKHOLE:
- case RTN_PROHIBIT:
- case RTN_UNREACHABLE:
- case RTN_THROW:
- break;
- default:
- return -1;
+ int i;
+
+ for(i = 1; i <= RTA_MAX; i++) {
+ struct rtattr *attr = rta[i-1];
+ if (attr) {
+ if (RTA_PAYLOAD(attr) < 4 && RTA_PAYLOAD(attr) != 2)
+ return -EINVAL;
+ if (i != RTA_MULTIPATH && i != RTA_METRICS)
+ rta[i-1] = (struct rtattr *)RTA_DATA(attr);
+ }
}
return 0;
@@ -562,114 +436,36 @@ static int dn_fib_check_attr(struct rtmsg *r, struct rtattr **rta)
int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
- struct dn_fib_table *t;
+ struct dn_fib_table *tb;
struct rtattr **rta = arg;
struct rtmsg *r = NLMSG_DATA(nlh);
- struct dn_fib_action *fa;
- int err;
if (dn_fib_check_attr(r, rta))
return -EINVAL;
- if ((fa = dn_fib_new_action()) == NULL)
- return -ENOBUFS;
+ tb = dn_fib_get_table(r->rtm_table, 0);
+ if (tb)
+ return tb->delete(tb, r, (struct dn_kern_rta *)rta, nlh, &NETLINK_CB(skb));
- t = dn_fib_get_tree(r->rtm_table, 0);
- if (t) {
- if ((err = dn_fib_convert_rtm(fa, r, rta, nlh, &NETLINK_CB(skb))) < 0) {
- dn_fib_del_action(fa);
- return err;
- }
- err = t->delete(t, fa);
- dn_fib_del_action(fa);
- return err;
- }
return -ESRCH;
}
int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
- struct dn_fib_table *t;
+ struct dn_fib_table *tb;
struct rtattr **rta = arg;
struct rtmsg *r = NLMSG_DATA(nlh);
- struct dn_fib_action *fa;
- int err;
if (dn_fib_check_attr(r, rta))
return -EINVAL;
- if ((fa = dn_fib_new_action()) == NULL)
- return -ENOBUFS;
+ tb = dn_fib_get_table(r->rtm_table, 1);
+ if (tb)
+ return tb->insert(tb, r, (struct dn_kern_rta *)rta, nlh, &NETLINK_CB(skb));
- t = dn_fib_get_tree(r->rtm_table, 1);
- if (t) {
- if ((err = dn_fib_convert_rtm(fa, r, rta, nlh, &NETLINK_CB(skb))) < 0) {
- dn_fib_del_action(fa);
- return err;
- }
- return t->insert(t, fa);
- }
return -ENOBUFS;
}
-int dn_fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
- int table, struct dn_fib_action *fa)
-{
- struct rtmsg *rtm;
- struct nlmsghdr *nlh;
- unsigned char *b = skb->tail;
-
- nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*rtm));
- rtm = NLMSG_DATA(nlh);
- rtm->rtm_family = AF_DECnet;
- rtm->rtm_dst_len = 16;
- rtm->rtm_src_len = 16;
- rtm->rtm_tos = 0;
- rtm->rtm_table = table;
- rtm->rtm_type = fa->fa_type;
- rtm->rtm_flags = 0;
- rtm->rtm_protocol = fa->fa_proto;
- RTA_PUT(skb, RTA_DST, 2, &fa->fa_key);
- if (fa->fa_ifindex)
- RTA_PUT(skb, RTA_OIF, sizeof(int), &fa->fa_ifindex);
-
- nlh->nlmsg_len = skb->tail - b;
- return skb->len;
-
-nlmsg_failure:
-rtattr_failure:
- skb_trim(skb, b - skb->data);
- return -1;
-}
-
-static void dn_rtmsg_fib(int event, int table, struct dn_fib_action *fa,
- struct nlmsghdr *nlh, struct netlink_skb_parms *req)
-{
- struct sk_buff *skb;
- u32 pid = req ? req->pid : 0;
- int size = NLMSG_SPACE(sizeof(struct rtmsg) + 256);
-
- skb = alloc_skb(size, GFP_KERNEL);
- if (!skb)
- return;
-
- if (dn_fib_dump_info(skb, pid, nlh->nlmsg_seq, event, table, fa) < 0) {
- kfree_skb(skb);
- return;
- }
- NETLINK_CB(skb).dst_groups = RTMGRP_DECnet_ROUTE;
- if (nlh->nlmsg_flags & NLM_F_ECHO)
- atomic_inc(&skb->users);
- netlink_broadcast(rtnl, skb, pid, RTMGRP_DECnet_ROUTE, GFP_KERNEL);
- if (nlh->nlmsg_flags & NLM_F_ECHO)
- netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT);
-}
-
-static int dn_fib_table_dump(struct dn_fib_table *t, struct sk_buff *skb, struct netlink_callback *cb)
-{
-
- return skb->len;
-}
int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb)
{
@@ -690,7 +486,7 @@ int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb)
continue;
if (t > s_t)
memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(int));
- tb = dn_fib_get_tree(t, 0);
+ tb = dn_fib_get_table(t, 0);
if (tb == NULL)
continue;
if (tb->dump(tb, skb, cb) < 0)
@@ -703,6 +499,96 @@ int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb)
}
#endif /* CONFIG_RTNETLINK */
+int dn_fib_sync_down(dn_address 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) {
+ nh->nh_flags |= RTNH_F_DEAD;
+ fi->fib_power -= nh->nh_power;
+ nh->nh_power = 0;
+ dead++;
+ }
+ } endfor_nexthops(fi)
+ if (dead == fi->fib_nhs) {
+ fi->fib_flags |= RTNH_F_DEAD;
+ ret++;
+ }
+ }
+ } endfor_fib_info();
+ return ret;
+}
+
+
+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++;
+ nh->nh_power = 0;
+ nh->nh_flags &= ~RTNH_F_DEAD;
+ } endfor_nexthops(fi);
+
+ if (alive == fi->fib_nhs) {
+ fi->fib_flags &= ~RTNH_F_DEAD;
+ ret++;
+ }
+ } endfor_fib_info();
+ return ret;
+}
+
+void dn_fib_flush(void)
+{
+ int flushed = 0;
+ struct dn_fib_table *tb;
+ int id;
+
+ for(id = DN_NUM_TABLES; id > 0; id--) {
+ if ((tb = dn_fib_get_table(id, 0)) == NULL)
+ continue;
+ flushed += tb->flush(tb);
+ }
+
+ if (flushed)
+ dn_rt_cache_flush(-1);
+}
+
int dn_fib_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
@@ -720,104 +606,65 @@ int dn_fib_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
#ifdef CONFIG_PROC_FS
-struct dn_fib_procfs {
- int len;
- off_t pos;
- off_t begin;
- off_t offset;
- int length;
- char *buffer;
-};
-
-static int dn_proc_action_list(struct dn_fib_walker_t *fwt, struct dn_fib_node *fn)
-{
- struct dn_fib_procfs *pinfo = (struct dn_fib_procfs *)fwt->arg;
- struct dn_fib_action *fa;
- char ab[DN_ASCBUF_LEN];
-
- if (pinfo->pos > pinfo->offset + pinfo->length)
- return 0;
-
- for(fa = fn->fn_action; fa; fa = fa->fa_next) {
-
- pinfo->len += sprintf(pinfo->buffer + pinfo->len,
- "%s/%04hx %02x %02x\n",
- dn_addr2asc(fa->fa_key, ab),
- fa->fa_mask,
- fa->fa_type,
- fa->fa_proto);
-
- pinfo->pos = pinfo->begin + pinfo->len;
- if (pinfo->pos < pinfo->offset) {
- pinfo->len = 0;
- pinfo->begin = pinfo->pos;
- }
- if (pinfo->pos > pinfo->offset + pinfo->length)
- break;
- }
-
- return 0;
-}
-
static int decnet_rt_get_info(char *buffer, char **start, off_t offset, int length)
{
- struct dn_fib_procfs pinfo;
- int i;
- struct dn_fib_table *t;
- struct dn_fib_walker_t fwt;
-
- pinfo.pos = 0;
- pinfo.len = 0;
- pinfo.begin = 0;
- pinfo.offset = offset;
- pinfo.length = length;
- pinfo.buffer = buffer;
-
- fwt.arg = &pinfo;
- fwt.fxn = dn_proc_action_list;
-
- start_bh_atomic();
- for(i = 0; i < DN_NUM_TABLES; i++) {
- if ((t = dn_fib_get_tree(i, 0)) == NULL)
- continue;
+ int first = offset / 128;
+ char *ptr = buffer;
+ int count = (length + 127) / 128;
+ int len;
+ int i;
+ struct dn_fib_table *tb;
- fwt.table = t;
- t->walk(&fwt);
+ *start = buffer + (offset % 128);
+
+ if (--first < 0) {
+ sprintf(buffer, "%-127s\n", "Iface\tDest\tGW \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT");
+ --count;
+ ptr += 128;
+ first = 0;
+ }
- if (pinfo.pos > pinfo.offset + pinfo.length)
- break;
- }
- end_bh_atomic();
- *start = pinfo.buffer + (pinfo.offset - pinfo.begin);
- pinfo.len -= (pinfo.offset - pinfo.begin);
+ for(i = DN_MIN_TABLE; (i <= DN_NUM_TABLES) && (count > 0); i++) {
+ if ((tb = dn_fib_get_table(i, 0)) != NULL) {
+ int n = tb->get_info(tb, ptr, first, count);
+ count -= n;
+ ptr += n * 128;
+ }
+ }
- if (pinfo.len > pinfo.length)
- pinfo.len = pinfo.length;
+ len = ptr - *start;
+ if (len >= length)
+ return length;
+ if (len >= 0)
+ return len;
- return pinfo.len;
+ return 0;
}
#endif /* CONFIG_PROC_FS */
-#ifdef CONFIG_DECNET_MODULE
-void dn_fib_cleanup(void)
+
+void __exit dn_fib_cleanup(void)
{
#ifdef CONFIG_PROC_FS
- proc_net_create("decnet_route",0,decnet_rt_get_info);
-#endif /* CONFIG_PROC_FS */
+ proc_net_remove("decnet_route");
+#endif
+
+ dn_fib_table_cleanup();
+ dn_fib_rules_cleanup();
}
-#endif /* CONFIG_DECNET_MODULE */
void __init dn_fib_init(void)
{
- memset(dn_fib_tables, 0, DN_NUM_TABLES * sizeof(struct dn_fib_table *));
#ifdef CONFIG_PROC_FS
- proc_net_remove("decnet_route");
+ proc_net_create("decnet_route", 0, decnet_rt_get_info);
#endif
+ dn_fib_table_init();
+ dn_fib_rules_init();
}
diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c
index bddf4b049..eb50c8c54 100644
--- a/net/decnet/dn_neigh.c
+++ b/net/decnet/dn_neigh.c
@@ -554,6 +554,7 @@ int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n)
struct dn_neigh *dn;
struct neigh_table *tbl = &dn_neigh_table;
unsigned char *rs = ptr;
+ struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
read_lock_bh(&tbl->lock);
@@ -564,7 +565,7 @@ int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n)
dn = (struct dn_neigh *)neigh;
if (!(dn->flags & (DN_NDFLAG_R1|DN_NDFLAG_R2)))
continue;
- if (decnet_node_type == DN_RT_INFO_L1RT && (dn->flags & DN_NDFLAG_R2))
+ if (dn_db->parms.forwarding == 1 && (dn->flags & DN_NDFLAG_R2))
continue;
if (t == n)
rs = dn_find_slot(ptr, n, dn->priority);
@@ -654,12 +655,10 @@ void __init dn_neigh_init(void)
#endif /* CONFIG_PROC_FS */
}
-#ifdef CONFIG_DECNET_MODULE
-void dn_neigh_cleanup(void)
+void __exit dn_neigh_cleanup(void)
{
#ifdef CONFIG_PROC_FS
proc_net_remove("decnet_neigh");
#endif /* CONFIG_PROC_FS */
neigh_table_clear(&dn_neigh_table);
}
-#endif /* CONFIG_DECNET_MODULE */
diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c
index a997d253e..5603c1d1f 100644
--- a/net/decnet/dn_nsp_in.c
+++ b/net/decnet/dn_nsp_in.c
@@ -260,8 +260,6 @@ static void dn_nsp_disc_init(struct sock *sk, struct sk_buff *skb)
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
unsigned short reason;
- /* printk(KERN_DEBUG "DECnet: discinit %d\n", skb->len); */
-
if (skb->len < 2)
goto out;
@@ -283,8 +281,6 @@ static void dn_nsp_disc_init(struct sock *sk, struct sk_buff *skb)
scp->addrrem = cb->src_port;
sk->state = TCP_CLOSE;
- /* printk(KERN_DEBUG "DECnet: discinit\n"); */
-
switch(scp->state) {
case DN_CI:
case DN_CD:
@@ -299,10 +295,15 @@ static void dn_nsp_disc_init(struct sock *sk, struct sk_buff *skb)
break;
}
- if (!sk->dead)
+ if (!sk->dead) {
+ if (sk->socket->state != SS_UNCONNECTED)
+ sk->socket->state = SS_DISCONNECTING;
sk->state_change(sk);
+ }
- dn_destroy_sock(sk);
+ 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);
@@ -343,10 +344,14 @@ static void dn_nsp_disc_conf(struct sock *sk, struct sk_buff *skb)
scp->state = DN_CN;
}
- if (!sk->dead)
+ if (!sk->dead) {
+ if (sk->socket->state != SS_UNCONNECTED)
+ sk->socket->state = SS_DISCONNECTING;
sk->state_change(sk);
+ }
- dn_destroy_sock(sk);
+ scp->persist_fxn = dn_destroy_timer;
+ scp->persist = dn_nsp_persist(sk);
out:
kfree_skb(skb);
@@ -519,6 +524,18 @@ static void dn_returned_conn_init(struct sock *sk, struct sk_buff *skb)
kfree_skb(skb);
}
+static void dn_nsp_no_socket(struct sk_buff *skb)
+{
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+ switch(cb->nsp_flags) {
+ case 0x28: /* Connect Confirm */
+ dn_nsp_return_disc(skb, NSP_DISCCONF, NSP_REASON_NL);
+ }
+
+ kfree_skb(skb);
+}
+
static int dn_nsp_rx_packet(struct sk_buff *skb)
{
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
@@ -615,6 +632,8 @@ got_it:
return ret;
}
+
+ dn_nsp_no_socket(skb);
return 1;
free_out:
diff --git a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c
index 2630c5c07..3b487617d 100644
--- a/net/decnet/dn_nsp_out.c
+++ b/net/decnet/dn_nsp_out.c
@@ -18,6 +18,7 @@
* 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.
*/
/******************************************************************************
@@ -463,14 +464,24 @@ void dn_nsp_delayed_ack(struct sock *sk)
dn_nsp_send_data_ack(sk);
}
-void dn_send_conn_conf (struct sock *sk)
+static int dn_nsp_retrans_conn_conf(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ if (scp->state == DN_CC)
+ dn_send_conn_conf(sk, GFP_ATOMIC);
+
+ return 0;
+}
+
+void dn_send_conn_conf(struct sock *sk, int gfp)
{
struct dn_scp *scp = &sk->protinfo.dn;
struct sk_buff *skb = NULL;
struct nsp_conn_init_msg *msg;
- unsigned short int aux;
+ unsigned char len = scp->conndata_out.opt_optl;
- if ((skb = dn_alloc_skb(sk, 50 + scp->conndata_out.opt_optl, GFP_KERNEL)) == NULL)
+ if ((skb = dn_alloc_skb(sk, 50 + scp->conndata_out.opt_optl, gfp)) == NULL)
return;
msg = (struct nsp_conn_init_msg *)skb_put(skb, sizeof(*msg));
@@ -481,46 +492,89 @@ void dn_send_conn_conf (struct sock *sk)
msg->info = 0x03;
msg->segsize = dn_htons(0x05B3);
- if (scp->conndata_out.opt_optl > 0) {
- aux = scp->conndata_out.opt_optl;
- *skb_put(skb,1) = aux;
- memcpy(skb_put(skb, aux), scp->conndata_out.opt_data, aux);
- }
+ *skb_put(skb,1) = len;
+
+ if (len > 0)
+ memcpy(skb_put(skb, len), scp->conndata_out.opt_data, len);
- dn_nsp_send(skb);
+ dn_nsp_send(skb);
+
+ scp->persist = dn_nsp_persist(sk);
+ scp->persist_fxn = dn_nsp_retrans_conn_conf;
}
-void dn_send_disc (struct sock *sk, unsigned char msgflg, unsigned short reason)
+
+static __inline__ void dn_nsp_do_disc(struct sock *sk, unsigned char msgflg,
+ unsigned short reason, int gfp, struct dst_entry *dst,
+ int ddl, unsigned char *dd, __u16 rem, __u16 loc)
{
- struct dn_scp *scp = &sk->protinfo.dn;
struct sk_buff *skb = NULL;
- int ddl = (msgflg == NSP_DISCINIT || msgflg == 0x38) ? (1 + scp->discdata_out.opt_optl) : 0;
- int size = 7 + ddl;
+ int size = 7 + (ddl ? (ddl + 1) : 0);
unsigned char *msg;
- if ((skb = dn_alloc_skb(sk, size, GFP_ATOMIC)) == NULL)
+ if ((dst == NULL) || (rem == 0)) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "DECnet: dn_nsp_do_disc: BUG! Please report this to SteveW@ACM.org rem=%u dst=%p\n", (unsigned)rem, dst);
+ return;
+ }
+
+ if ((skb = dn_alloc_skb(sk, size, gfp)) == NULL)
return;
- if (reason == 0) reason = scp->discdata_out.opt_status;
-
msg = skb_put(skb, size);
*msg++ = msgflg;
- *(__u16 *)msg = scp->addrrem;
+ *(__u16 *)msg = rem;
msg += 2;
- *(__u16 *)msg = scp->addrloc;
+ *(__u16 *)msg = loc;
msg += 2;
*(__u16 *)msg = dn_htons(reason);
msg += 2;
if (ddl) {
- *msg++ = scp->discdata_out.opt_optl;
- memcpy(msg, scp->discdata_out.opt_data, scp->discdata_out.opt_optl);
+ *msg++ = ddl;
+ memcpy(msg, dd, ddl);
}
- dn_nsp_send(skb);
+ /*
+ * This doesn't go via the dn_nsp_send() fucntion since we need
+ * to be able to send disc packets out which have no socket
+ * associations.
+ */
+ skb->dst = dst_clone(dst);
+ skb->dst->output(skb);
+}
+
+
+void dn_nsp_send_disc(struct sock *sk, unsigned char msgflg,
+ unsigned short reason, int gfp)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ int ddl = 0;
+
+ if (msgflg == NSP_DISCINIT)
+ ddl = scp->discdata_out.opt_optl;
+
+ if (reason == 0)
+ reason = scp->discdata_out.opt_status;
+
+ dn_nsp_do_disc(sk, msgflg, reason, gfp, sk->dst_cache, 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 = (struct dn_skb_cb *)skb->cb;
+ int ddl = 0;
+ int gfp = GFP_ATOMIC;
+
+ dn_nsp_do_disc(NULL, msgflg, reason, gfp, skb->dst, ddl,
+ NULL, cb->src_port, cb->dst_port);
+}
+
+
void dn_nsp_send_lnk(struct sock *sk, unsigned short flgs)
{
struct dn_scp *scp = &sk->protinfo.dn;
@@ -543,8 +597,6 @@ void dn_nsp_send_lnk(struct sock *sk, unsigned short flgs)
msg1->segnum = dn_htons(cb->segnum = (scp->numoth++ & 0x0FFF));
msg1->lsflgs = flgs;
- /* printk(KERN_DEBUG "dn_nsp_send_lnk: %02x\n", flgs); */
-
dn_nsp_queue_xmit(sk, skb, 1);
scp->persist = dn_nsp_persist(sk);
@@ -556,10 +608,8 @@ static int dn_nsp_retrans_conninit(struct sock *sk)
{
struct dn_scp *scp = &sk->protinfo.dn;
- if (scp->state == DN_CI) {
+ if (scp->state == DN_CI)
dn_nsp_send_conninit(sk, NSP_RCI);
- scp->persist = dn_nsp_persist(sk);
- }
return 0;
}
diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c
index c4f834157..6f842d465 100644
--- a/net/decnet/dn_route.c
+++ b/net/decnet/dn_route.c
@@ -25,6 +25,11 @@
* 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.
*/
/******************************************************************************
@@ -82,17 +87,23 @@ extern struct neigh_table dn_neigh_table;
static unsigned char dn_hiord_addr[6] = {0xAA,0x00,0x04,0x00,0x00,0x00};
+int dn_rt_min_delay = 2*HZ;
+int dn_rt_max_delay = 10*HZ;
+static unsigned long dn_rt_deadline = 0;
+
static int dn_dst_gc(void);
static struct dst_entry *dn_dst_check(struct dst_entry *, __u32);
static struct dst_entry *dn_dst_reroute(struct dst_entry *, struct sk_buff *skb);
static struct dst_entry *dn_dst_negative_advice(struct dst_entry *);
static void dn_dst_link_failure(struct sk_buff *);
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 dn_rt_hash_mask;
static struct timer_list dn_route_timer = { NULL, NULL, 0, 0L, NULL };
+static struct timer_list dn_rt_flush_timer = { NULL, NULL, 0, 0L, dn_run_flush };
int decnet_dst_gc_interval = 2;
static struct dst_ops dn_dst_ops = {
@@ -109,12 +120,12 @@ static struct dst_ops dn_dst_ops = {
ATOMIC_INIT(0)
};
-static __inline__ unsigned dn_hash(unsigned short dest)
+static __inline__ unsigned dn_hash(unsigned short src, unsigned short dst)
{
- unsigned short tmp = dest;
- tmp ^= (dest >> 3);
- tmp ^= (dest >> 5);
- tmp ^= (dest >> 10);
+ unsigned short tmp = src ^ dst;
+ tmp ^= (tmp >> 3);
+ tmp ^= (tmp >> 5);
+ tmp ^= (tmp >> 10);
return dn_rt_hash_mask & (unsigned)tmp;
}
@@ -155,8 +166,10 @@ static int dn_dst_gc(void)
unsigned long expire = 10 * HZ;
for(i = 0; i <= dn_rt_hash_mask; i++) {
+
write_lock_bh(&dn_rt_hash_table[i].lock);
rtp = &dn_rt_hash_table[i].chain;
+
for(; (rt=*rtp); rtp = &rt->u.rt_next) {
if (atomic_read(&rt->u.dst.__refcnt) ||
(now - rt->u.dst.lastuse) < expire)
@@ -199,9 +212,8 @@ static void dn_dst_link_failure(struct sk_buff *skb)
return;
}
-static void dn_insert_route(struct dn_route *rt)
+static void dn_insert_route(struct dn_route *rt, unsigned hash)
{
- unsigned hash = dn_hash(rt->rt_daddr);
unsigned long now = jiffies;
write_lock_bh(&dn_rt_hash_table[hash].lock);
@@ -237,6 +249,43 @@ nothing_to_declare:
}
}
+static spinlock_t dn_rt_flush_lock = SPIN_LOCK_UNLOCKED;
+
+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);
+}
+
+
static int dn_route_rx_packet(struct sk_buff *skb)
{
int err;
@@ -531,33 +580,49 @@ static int dn_route_output_slow(struct dst_entry **pprt, dn_address dst, dn_addr
struct net_device *dev = decnet_default_device;
struct neighbour *neigh = NULL;
struct dn_dev *dn_db;
-
-
+ unsigned hash;
#ifdef CONFIG_DECNET_ROUTER
- if (decnet_node_type != DN_RT_INFO_ENDN) {
- struct dn_fib_res res;
- int err = 0;
-
- res.res_addr = dst;
- res.res_mask = 0;
- res.res_ifindex = 0;
- res.res_proto = RTN_UNSPEC;
- res.res_cost = 0;
-
- if ((err = dn_fib_resolve(&res)) == 0) {
-
- if (!res.res_fa || (res.res_fa->fa_type != RTN_UNICAST))
- return -EPROTO;
-
- if ((neigh = neigh_clone(res.res_fa->fa_neigh)) != NULL)
- goto got_route;
+ struct dn_fib_key key;
+ struct dn_fib_res res;
+ int err;
- return -ENOBUFS;
+ key.src = src;
+ key.dst = dst;
+ key.iif = 0;
+ key.oif = 0;
+ key.fwmark = 0;
+ key.scope = RT_SCOPE_UNIVERSE;
+
+ if ((err = dn_fib_lookup(&key, &res)) == 0) {
+ switch(res.type) {
+ case RTN_UNICAST:
+ /*
+ * This method of handling multipath
+ * routes is a hack and will change.
+ * It works for now though.
+ */
+ if (res.fi->fib_nhs)
+ dn_fib_select_multipath(&key, &res);
+ neigh = __neigh_lookup(&dn_neigh_table, &DN_FIB_RES_GW(res), DN_FIB_RES_DEV(res), 1);
+ err = -ENOBUFS;
+ if (!neigh)
+ break;
+ err = 0;
+ break;
+ case RTN_UNREACHABLE:
+ err = -EHOSTUNREACH;
+ break;
+ default:
+ err = -EINVAL;
}
-
- if (err != -ENOENT)
+ dn_fib_res_put(&res);
+ if (err < 0)
return err;
+ goto got_route;
}
+
+ if (err != -ESRCH)
+ return err;
#endif
/* Look in On-Ethernet cache first */
@@ -594,13 +659,16 @@ got_route:
dn_db = (struct dn_dev *)neigh->dev->dn_ptr;
- rt->rt_saddr = src;
- rt->rt_daddr = dst;
- rt->rt_oif = neigh->dev->ifindex;
- rt->rt_iif = 0;
+ rt->key.saddr = src;
+ rt->rt_saddr = src;
+ rt->key.daddr = dst;
+ rt->rt_daddr = dst;
+ rt->key.oif = neigh ? neigh->dev->ifindex : -1;
+ rt->key.iif = 0;
+ rt->key.fwmark = 0;
rt->u.dst.neighbour = neigh;
- rt->u.dst.dev = neigh->dev;
+ rt->u.dst.dev = neigh ? neigh->dev : NULL;
rt->u.dst.lastuse = jiffies;
rt->u.dst.output = dn_output;
rt->u.dst.input = dn_rt_bug;
@@ -608,7 +676,8 @@ got_route:
if (dn_dev_islocal(neigh->dev, rt->rt_daddr))
rt->u.dst.input = dn_nsp_rx;
- dn_insert_route(rt);
+ hash = dn_hash(rt->key.saddr, rt->key.daddr);
+ dn_insert_route(rt, hash);
*pprt = &rt->u.dst;
return 0;
@@ -616,16 +685,16 @@ got_route:
int dn_route_output(struct dst_entry **pprt, dn_address dst, dn_address src, int flags)
{
- unsigned hash = dn_hash(dst);
+ unsigned hash = dn_hash(src, dst);
struct dn_route *rt = NULL;
if (!(flags & MSG_TRYHARD)) {
read_lock_bh(&dn_rt_hash_table[hash].lock);
for(rt = dn_rt_hash_table[hash].chain; rt; rt = rt->u.rt_next) {
- if ((dst == rt->rt_daddr) &&
- (src == rt->rt_saddr) &&
- (rt->rt_iif == 0) &&
- (rt->rt_oif != 0)) {
+ if ((dst == rt->key.daddr) &&
+ (src == rt->key.saddr) &&
+ (rt->key.iif == 0) &&
+ (rt->key.oif != 0)) {
rt->u.dst.lastuse = jiffies;
dst_hold(&rt->u.dst);
rt->u.dst.__use++;
@@ -649,6 +718,15 @@ static int dn_route_input_slow(struct sk_buff *skb)
struct neighbour *neigh = NULL;
int (*dnrt_input)(struct sk_buff *skb);
int (*dnrt_output)(struct sk_buff *skb);
+ u32 fwmark = 0;
+ unsigned hash;
+ dn_address saddr = cb->src;
+ dn_address daddr = cb->dst;
+#ifdef CONFIG_DECNET_ROUTER
+ struct dn_fib_key key;
+ struct dn_fib_res res;
+ int err;
+#endif
if (dev == NULL)
return -EINVAL;
@@ -683,6 +761,8 @@ static int dn_route_input_slow(struct sk_buff *skb)
*/
dnrt_input = dn_nsp_rx;
dnrt_output = dn_output;
+ saddr = cb->dst;
+ daddr = cb->src;
if ((neigh = neigh_lookup(&dn_neigh_table, &cb->src, dev)) != NULL)
goto add_entry;
@@ -705,9 +785,49 @@ non_local_input:
* Destination is another node... find next hop in
* routing table here.
*/
- if (decnet_node_type == DN_RT_INFO_ENDN)
+
+ key.src = cb->src;
+ key.dst = cb->dst;
+ key.iif = dev->ifindex;
+ key.oif = 0;
+ key.scope = RT_SCOPE_UNIVERSE;
+
+#ifdef CONFIG_DECNET_ROUTE_FWMASK
+ if (skb->nfreason == NF_REASON_FOR_ROUTING)
+ key.fwmark = skb->fwmark;
+ else
+ key.fwmark = 0;
+#else
+ key.fwmark = 0;
+#endif
+
+ if ((err = dn_fib_lookup(&key, &res)) == 0) {
+ switch(res.type) {
+ case RTN_UNICAST:
+ if (res.fi->fib_nhs)
+ dn_fib_select_multipath(&key, &res);
+ neigh = __neigh_lookup(&dn_neigh_table, &DN_FIB_RES_GW(res), DN_FIB_RES_DEV(res), 1);
+ err = -ENOBUFS;
+ if (!neigh)
+ break;
+ err = 0;
+ dnrt_input = dn_forward;
+ fwmark = key.fwmark;
+ break;
+ case RTN_UNREACHABLE:
+ dnrt_input = dn_blackhole;
+ fwmark = key.fwmark;
+ break;
+ default:
+ err = -EINVAL;
+ }
+ dn_fib_res_put(&res);
+ if (err < 0)
+ return err;
goto add_entry;
+ }
+ return err;
#endif /* CONFIG_DECNET_ROUTER */
@@ -718,18 +838,22 @@ add_entry:
return -EINVAL;
}
- rt->rt_saddr = cb->dst;
- rt->rt_daddr = cb->src;
- rt->rt_oif = 0;
- rt->rt_iif = dev->ifindex;
+ rt->key.saddr = cb->src;
+ rt->rt_saddr = saddr;
+ rt->key.daddr = cb->dst;
+ rt->rt_daddr = daddr;
+ rt->key.oif = 0;
+ rt->key.iif = dev->ifindex;
+ rt->key.fwmark = fwmark;
rt->u.dst.neighbour = neigh;
- rt->u.dst.dev = dev;
+ rt->u.dst.dev = neigh ? neigh->dev : NULL;
rt->u.dst.lastuse = jiffies;
rt->u.dst.output = dnrt_output;
rt->u.dst.input = dnrt_input;
- dn_insert_route(rt);
+ hash = dn_hash(rt->key.saddr, rt->key.daddr);
+ dn_insert_route(rt, hash);
skb->dst = (struct dst_entry *)rt;
return 0;
@@ -739,17 +863,22 @@ int dn_route_input(struct sk_buff *skb)
{
struct dn_route *rt;
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
- unsigned hash = dn_hash(cb->src);
+ unsigned hash = dn_hash(cb->src, cb->dst);
if (skb->dst)
return 0;
read_lock(&dn_rt_hash_table[hash].lock);
for(rt = dn_rt_hash_table[hash].chain; rt != NULL; rt = rt->u.rt_next) {
- if ((rt->rt_saddr == cb->dst) &&
- (rt->rt_daddr == cb->src) &&
- (rt->rt_oif == 0) &&
- (rt->rt_iif == cb->iif)) {
+ if ((rt->key.saddr == cb->src) &&
+ (rt->key.daddr == cb->dst) &&
+ (rt->key.oif == 0) &&
+#ifdef CONFIG_DECNET_ROUTE_FWMASK
+ (rt->key.fwmark == (skb->nfreason ==
+ NF_REASON_FOR_ROUTING
+ ? skb->nfmark : 0)) &&
+#endif
+ (rt->key.iif == cb->iif)) {
rt->u.dst.lastuse = jiffies;
dst_hold(&rt->u.dst);
rt->u.dst.__use++;
@@ -953,6 +1082,7 @@ static int decnet_cache_get_info(char *buffer, char **start, off_t offset, int l
);
+
pos = begin + len;
if (pos < offset) {
@@ -998,48 +1128,51 @@ void __init dn_route_init(void)
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);
+ /*
+ * 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");
+ panic("Failed to allocate DECnet route cache hash table\n");
- printk(KERN_INFO "DECnet: Routing cache hash table of %u buckets, %dKbytes\n", dn_rt_hash_mask, (dn_rt_hash_mask*sizeof(struct dn_rt_hash_bucket))/1024);
+ printk(KERN_INFO
+ "DECnet: Routing cache hash table of %u buckets, %dKbytes\n",
+ dn_rt_hash_mask,
+ (dn_rt_hash_mask*sizeof(struct dn_rt_hash_bucket))/1024);
dn_rt_hash_mask--;
- for(i = 0; i <= dn_rt_hash_mask; i++) {
- dn_rt_hash_table[i].lock = RW_LOCK_UNLOCKED;
- dn_rt_hash_table[i].chain = NULL;
- }
+ for(i = 0; i <= dn_rt_hash_mask; i++) {
+ dn_rt_hash_table[i].lock = RW_LOCK_UNLOCKED;
+ dn_rt_hash_table[i].chain = NULL;
+ }
- dn_dst_ops.gc_thresh = (dn_rt_hash_mask + 1);
+ dn_dst_ops.gc_thresh = (dn_rt_hash_mask + 1);
#ifdef CONFIG_PROC_FS
proc_net_create("decnet_cache",0,decnet_cache_get_info);
#endif /* CONFIG_PROC_FS */
}
-#ifdef CONFIG_DECNET_MODULE
-void dn_route_cleanup(void)
+void __exit dn_route_cleanup(void)
{
del_timer(&dn_route_timer);
dn_run_flush(0);
+
#ifdef CONFIG_PROC_FS
proc_net_remove("decnet_cache");
#endif /* CONFIG_PROC_FS */
}
-#endif /* CONFIG_DECNET_MODULE */
+
diff --git a/net/decnet/dn_rules.c b/net/decnet/dn_rules.c
new file mode 100644
index 000000000..133591f0b
--- /dev/null
+++ b/net/decnet/dn_rules.c
@@ -0,0 +1,372 @@
+
+/*
+ * 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:
+ *
+ */
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/net.h>
+#include <linux/socket.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 <asm/atomic.h>
+#include <asm/uaccess.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/dn.h>
+#include <net/dn_fib.h>
+#include <net/dn_neigh.h>
+#include <net/dn_dev.h>
+
+struct dn_fib_rule
+{
+ struct dn_fib_rule *r_next;
+ atomic_t r_clntref;
+ u32 r_preference;
+ unsigned char r_table;
+ unsigned char r_action;
+ unsigned char r_dst_len;
+ unsigned char r_src_len;
+ dn_address r_src;
+ dn_address r_srcmask;
+ dn_address r_dst;
+ dn_address r_dstmask;
+ u8 r_flags;
+#ifdef CONFIG_DECNET_ROUTE_FWMARK
+ u32 r_fwmark;
+#endif
+ int r_ifindex;
+ char r_ifname[IFNAMSIZ];
+ int r_dead;
+};
+
+static struct dn_fib_rule default_rule = { NULL, ATOMIC_INIT(2), 0x7fff, DN_DEFAULT_TABLE, RTN_UNICAST };
+
+static struct dn_fib_rule *dn_fib_rules = &default_rule;
+static rwlock_t dn_fib_rules_lock = RW_LOCK_UNLOCKED;
+
+
+int dn_fib_rtm_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct rtattr **rta = arg;
+ struct rtmsg *rtm = NLMSG_DATA(nlh);
+ struct dn_fib_rule *r, **rp;
+ int err = -ESRCH;
+
+ for(rp=&dn_fib_rules; (r=*rp) != NULL; rp = &r->r_next) {
+ if ((!rta[RTA_SRC-1] || memcmp(RTA_DATA(rta[RTA_SRC-1]), &r->r_src, 2) == 0) &&
+ rtm->rtm_src_len == r->r_src_len &&
+ rtm->rtm_dst_len == r->r_dst_len &&
+ (!rta[RTA_DST-1] || memcmp(RTA_DATA(rta[RTA_DST-1]), &r->r_dst, 2) == 0) &&
+#ifdef CONFIG_DECNET_ROUTE_FWMARK
+ (!rta[RTA_PROTOINFO-1] || memcmp(RTA_DATA(rta[RTA_PROTOINFO-1]), &r->r_fwmark, 4) == 0) &&
+#endif
+ (!rtm->rtm_type || rtm->rtm_type == r->r_action) &&
+ (!rta[RTA_PRIORITY-1] || memcmp(RTA_DATA(rta[RTA_PRIORITY-1]), &r->r_preference, 4) == 0) &&
+ (!rta[RTA_IIF-1] || strcmp(RTA_DATA(rta[RTA_IIF-1]), r->r_ifname) == 0) &&
+ (!rtm->rtm_table || (r && rtm->rtm_table == r->r_table))) {
+
+ err = -EPERM;
+ if (r == &default_rule)
+ break;
+
+ write_lock_bh(&dn_fib_rules_lock);
+ *rp = r->r_next;
+ r->r_dead = 1;
+ write_unlock_bh(&dn_fib_rules_lock);
+ dn_fib_rule_put(r);
+ err = 0;
+ break;
+ }
+ }
+
+ return err;
+}
+
+void dn_fib_rule_put(struct dn_fib_rule *r)
+{
+ if (atomic_dec_and_test(&r->r_clntref)) {
+ if (r->r_dead)
+ kfree(r);
+ else
+ printk(KERN_DEBUG "Attempt to free alive dn_fib_rule\n");
+ }
+}
+
+
+int dn_fib_rtm_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct rtattr **rta = arg;
+ struct rtmsg *rtm = NLMSG_DATA(nlh);
+ struct dn_fib_rule *r, *new_r, **rp;
+ unsigned char table_id;
+
+ if (rtm->rtm_src_len > 16 || rtm->rtm_dst_len > 16)
+ return -EINVAL;
+
+ if (rta[RTA_IIF-1] && RTA_PAYLOAD(rta[RTA_IIF-1]) > IFNAMSIZ)
+ return -EINVAL;
+
+ if (rtm->rtm_type == RTN_NAT)
+ return -EINVAL;
+
+ table_id = rtm->rtm_table;
+ if (table_id == RT_TABLE_UNSPEC) {
+ struct dn_fib_table *tb;
+ if (rtm->rtm_type == RTN_UNICAST) {
+ if ((tb = dn_fib_empty_table()) == NULL)
+ return -ENOBUFS;
+ table_id = tb->n;
+ }
+ }
+
+ new_r = kmalloc(sizeof(*new_r), GFP_KERNEL);
+ if (!new_r)
+ return -ENOMEM;
+ memset(new_r, 0, sizeof(*new_r));
+ if (rta[RTA_SRC-1])
+ memcpy(&new_r->r_src, RTA_DATA(rta[RTA_SRC-1]), 2);
+ if (rta[RTA_DST-1])
+ memcpy(&new_r->r_dst, RTA_DATA(rta[RTA_DST-1]), 2);
+ new_r->r_src_len = rtm->rtm_src_len;
+ new_r->r_dst_len = rtm->rtm_dst_len;
+ new_r->r_srcmask = dnet_make_mask(rtm->rtm_src_len);
+ new_r->r_dstmask = dnet_make_mask(rtm->rtm_dst_len);
+#ifdef CONFIG_DECNET_ROUTE_FWMARK
+ if (rta[RTA_PROTOINFO-1])
+ memcpy(&new_r->r_fwmark, RTA_DATA(rta[RTA_PROTOINFO-1]), 4);
+#endif
+ new_r->r_action = rtm->rtm_type;
+ new_r->r_flags = rtm->rtm_flags;
+ if (rta[RTA_PRIORITY-1])
+ memcpy(&new_r->r_preference, RTA_DATA(rta[RTA_PRIORITY-1]), 4);
+ new_r->r_table = table_id;
+ if (rta[RTA_IIF-1]) {
+ struct net_device *dev;
+ memcpy(new_r->r_ifname, RTA_DATA(rta[RTA_IIF-1]), IFNAMSIZ);
+ new_r->r_ifname[IFNAMSIZ-1] = 0;
+ new_r->r_ifindex = -1;
+ dev = __dev_get_by_name(new_r->r_ifname);
+ if (dev)
+ new_r->r_ifindex = dev->ifindex;
+ }
+
+ rp = &dn_fib_rules;
+ if (!new_r->r_preference) {
+ r = dn_fib_rules;
+ if (r && (r = r->r_next) != NULL) {
+ rp = &dn_fib_rules->r_next;
+ if (r->r_preference)
+ new_r->r_preference = r->r_preference - 1;
+ }
+ }
+
+ while((r=*rp) != NULL) {
+ if (r->r_preference > new_r->r_preference)
+ break;
+ rp = &r->r_next;
+ }
+
+ new_r->r_next = r;
+ atomic_inc(&new_r->r_clntref);
+ write_lock_bh(&dn_fib_rules_lock);
+ *rp = new_r;
+ write_unlock_bh(&dn_fib_rules_lock);
+ return 0;
+}
+
+
+int dn_fib_lookup(struct dn_fib_key *key, struct dn_fib_res *res)
+{
+ struct dn_fib_rule *r, *policy;
+ struct dn_fib_table *tb;
+ dn_address saddr = key->src;
+ dn_address daddr = key->dst;
+ int err;
+
+ read_lock(&dn_fib_rules_lock);
+ for(r = dn_fib_rules; r; r = r->r_next) {
+ if (((saddr^r->r_src) & r->r_srcmask) ||
+ ((daddr^r->r_dst) & r->r_dstmask) ||
+#ifdef CONFIG_DECNET_ROUTE_FWMARK
+ (r->r_fwmark && r->r_fwmark != key->fwmark) ||
+#endif
+ (r->r_ifindex && r->r_ifindex != key->iif))
+ continue;
+
+ switch(r->r_action) {
+ case RTN_UNICAST:
+ policy = r;
+ break;
+ case RTN_UNREACHABLE:
+ read_unlock(&dn_fib_rules_lock);
+ return -ENETUNREACH;
+ default:
+ case RTN_BLACKHOLE:
+ read_unlock(&dn_fib_rules_lock);
+ return -EINVAL;
+ case RTN_PROHIBIT:
+ read_unlock(&dn_fib_rules_lock);
+ return -EACCES;
+ }
+
+ if ((tb = dn_fib_get_table(r->r_table, 0)) == NULL)
+ continue;
+ err = tb->lookup(tb, key, res);
+ if (err == 0) {
+ res->r = policy;
+ if (policy)
+ atomic_inc(&policy->r_clntref);
+ read_unlock(&dn_fib_rules_lock);
+ return 0;
+ }
+ if (err < 0 && err != -EAGAIN) {
+ read_unlock(&dn_fib_rules_lock);
+ return err;
+ }
+ }
+
+ read_unlock(&dn_fib_rules_lock);
+ return -ESRCH;
+}
+
+static void dn_fib_rules_detach(struct net_device *dev)
+{
+ struct dn_fib_rule *r;
+
+ for(r = dn_fib_rules; r; r = r->r_next) {
+ if (r->r_ifindex == dev->ifindex) {
+ write_lock_bh(&dn_fib_rules_lock);
+ r->r_ifindex = -1;
+ write_unlock_bh(&dn_fib_rules_lock);
+ }
+ }
+}
+
+static void dn_fib_rules_attach(struct net_device *dev)
+{
+ struct dn_fib_rule *r;
+
+ for(r = dn_fib_rules; r; r = r->r_next) {
+ if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0) {
+ write_lock_bh(&dn_fib_rules_lock);
+ r->r_ifindex = dev->ifindex;
+ write_unlock_bh(&dn_fib_rules_lock);
+ }
+ }
+}
+
+static int dn_fib_rules_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ struct net_device *dev = ptr;
+
+ switch(event) {
+ case NETDEV_UNREGISTER:
+ dn_fib_rules_detach(dev);
+ dn_fib_sync_down(0, dev, 1);
+ case NETDEV_REGISTER:
+ dn_fib_rules_attach(dev);
+ dn_fib_sync_up(dev);
+ }
+
+ return NOTIFY_DONE;
+}
+
+
+static struct notifier_block dn_fib_rules_notifier = {
+ dn_fib_rules_event,
+ NULL,
+ 0
+};
+
+#ifdef CONFIG_RTNETLINK
+
+static int dn_fib_fill_rule(struct sk_buff *skb, struct dn_fib_rule *r, struct netlink_callback *cb)
+{
+ struct rtmsg *rtm;
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+
+
+ nlh = NLMSG_PUT(skb, NETLINK_CREDS(cb->skb)->pid, cb->nlh->nlmsg_seq, RTM_NEWRULE, sizeof(*rtm));
+ rtm = NLMSG_DATA(nlh);
+ rtm->rtm_family = AF_DECnet;
+ rtm->rtm_dst_len = r->r_dst_len;
+ rtm->rtm_src_len = r->r_src_len;
+ rtm->rtm_tos = 0;
+#ifdef CONFIG_DECNET_ROUTE_FWMARK
+ if (r->r_fwmark)
+ RTA_PUT(skb, RTA_PROTOINFO, 4, &r->r_fwmark);
+#endif
+ rtm->rtm_table = r->r_table;
+ rtm->rtm_protocol = 0;
+ rtm->rtm_scope = 0;
+ rtm->rtm_type = r->r_action;
+ rtm->rtm_flags = r->r_flags;
+
+ if (r->r_dst_len)
+ RTA_PUT(skb, RTA_DST, 2, &r->r_dst);
+ if (r->r_src_len)
+ RTA_PUT(skb, RTA_SRC, 2, &r->r_src);
+ if (r->r_ifname[0])
+ RTA_PUT(skb, RTA_IIF, IFNAMSIZ, &r->r_ifname);
+ if (r->r_preference)
+ RTA_PUT(skb, RTA_PRIORITY, 4, &r->r_preference);
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+ skb_put(skb, b - skb->tail);
+ return -1;
+}
+
+int dn_fib_dump_rules(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int idx;
+ int s_idx = cb->args[0];
+ struct dn_fib_rule *r;
+
+ read_lock(&dn_fib_rules_lock);
+ for(r = dn_fib_rules, idx = 0; r; r = r->r_next, idx++) {
+ if (idx < s_idx)
+ continue;
+ if (dn_fib_fill_rule(skb, r, cb) < 0)
+ break;
+ }
+ read_unlock(&dn_fib_rules_lock);
+ cb->args[0] = idx;
+
+ return skb->len;
+}
+
+#endif /* CONFIG_RTNETLINK */
+
+void __init dn_fib_rules_init(void)
+{
+ register_netdevice_notifier(&dn_fib_rules_notifier);
+}
+
+void __exit dn_fib_rules_cleanup(void)
+{
+ unregister_netdevice_notifier(&dn_fib_rules_notifier);
+}
+
+
diff --git a/net/decnet/dn_table.c b/net/decnet/dn_table.c
new file mode 100644
index 000000000..48c319c8a
--- /dev/null
+++ b/net/decnet/dn_table.c
@@ -0,0 +1,907 @@
+/*
+ * 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/config.h>
+#include <linux/string.h>
+#include <linux/net.h>
+#include <linux/socket.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 <asm/atomic.h>
+#include <asm/uaccess.h>
+#include <linux/route.h> /* RTF_xxx */
+#include <net/neighbour.h>
+#include <net/dst.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;
+ u32 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 dz_prefix(key,dz) ((key).datum)
+
+#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)
+
+
+static rwlock_t dn_fib_tables_lock = RW_LOCK_UNLOCKED;
+static struct dn_fib_table *dn_fib_tables[DN_NUM_TABLES + 1];
+
+static kmem_cache_t *dn_hash_kmem;
+static int dn_fib_hash_zombies = 0;
+
+static __inline__ dn_fib_idx_t dn_hash(dn_fib_key_t key, struct dn_zone *dz)
+{
+ u32 h = ntohs(key.datum)>>(16 - dz->dz_order);
+ h ^= (h >> 10);
+ h ^= (h >> 6);
+ h ^= (h >> 3);
+ h &= DZ_HASHMASK(dz);
+ return *(dn_fib_idx_t *)&h;
+}
+
+static __inline__ dn_fib_key_t dz_key(u16 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)
+{
+ int i;
+ struct dn_fib_node *f, **fp, *next;
+
+ for(i = 0; i < old_divisor; i++) {
+ for(f = old_ht[i]; f; f = f->fn_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 = kmalloc(new_divisor*sizeof(struct dn_fib_node*), GFP_KERNEL);
+
+ if (ht == NULL)
+ return;
+
+ memset(ht, 0, new_divisor*sizeof(struct dn_fib_node *));
+ 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 = kmalloc(sizeof(struct dn_zone), GFP_KERNEL);
+ if (!dz)
+ return NULL;
+
+ memset(dz, 0, sizeof(struct dn_zone));
+ if (z) {
+ dz->dz_divisor = 16;
+ dz->dz_hashmask = 0x0F;
+ } else {
+ dz->dz_divisor = 1;
+ dz->dz_hashmask = 0;
+ }
+
+ dz->dz_hash = kmalloc(dz->dz_divisor*sizeof(struct dn_fib_node *), GFP_KERNEL);
+
+ if (!dz->dz_hash) {
+ kfree(dz);
+ return NULL;
+ }
+
+ memset(dz->dz_hash, 0, dz->dz_divisor*sizeof(struct dn_fib_node*));
+ 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 dn_kern_rta *rta, struct dn_fib_info *fi)
+{
+ struct rtnexthop *nhp;
+ int nhlen;
+
+ if (rta->rta_priority && *rta->rta_priority != fi->fib_priority)
+ return 1;
+
+ if (rta->rta_oif || rta->rta_gw) {
+ if ((!rta->rta_oif || *rta->rta_oif == fi->fib_nh->nh_oif) &&
+ (!rta->rta_gw || memcmp(rta->rta_gw, &fi->fib_nh->nh_gw, 2) == 0))
+ return 0;
+ return 1;
+ }
+
+ if (rta->rta_mp == NULL)
+ return 0;
+
+ nhp = RTA_DATA(rta->rta_mp);
+ nhlen = RTA_PAYLOAD(rta->rta_mp);
+
+ for_nexthops(fi) {
+ int attrlen = nhlen - sizeof(struct rtnexthop);
+ dn_address 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) {
+ gw = dn_fib_get_attr16(RTNH_DATA(nhp), attrlen, RTA_GATEWAY);
+
+ if (gw && gw != nh->nh_gw)
+ return 1;
+ }
+ nhp = RTNH_NEXT(nhp);
+ } endfor_nexthops(fi);
+
+ return 0;
+}
+
+#ifdef CONFIG_RTNETLINK
+static int dn_fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
+ u8 tb_id, u8 type, u8 scope, void *dst, int dst_len,
+ struct dn_fib_info *fi)
+{
+ struct rtmsg *rtm;
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+
+ nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*rtm));
+ 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;
+ if (rtm->rtm_dst_len)
+ RTA_PUT(skb, RTA_DST, 2, dst);
+ rtm->rtm_protocol = fi->fib_protocol;
+ if (fi->fib_priority)
+ RTA_PUT(skb, RTA_PRIORITY, 4, &fi->fib_priority);
+ if (fi->fib_nhs == 1) {
+ if (fi->fib_nh->nh_gw)
+ RTA_PUT(skb, RTA_GATEWAY, 2, &fi->fib_nh->nh_gw);
+ if (fi->fib_nh->nh_oif)
+ RTA_PUT(skb, RTA_OIF, sizeof(int), &fi->fib_nh->nh_oif);
+ }
+ if (fi->fib_nhs > 1) {
+ struct rtnexthop *nhp;
+ struct rtattr *mp_head;
+ if (skb_tailroom(skb) <= RTA_SPACE(0))
+ goto rtattr_failure;
+ mp_head = (struct rtattr *)skb_put(skb, RTA_SPACE(0));
+
+ for_nexthops(fi) {
+ if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
+ goto rtattr_failure;
+ nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
+ nhp->rtnh_flags = nh->nh_flags & 0xFF;
+ nhp->rtnh_hops = nh->nh_weight - 1;
+ nhp->rtnh_ifindex = nh->nh_oif;
+ if (nh->nh_gw)
+ RTA_PUT(skb, RTA_GATEWAY, 2, &nh->nh_gw);
+ nhp->rtnh_len = skb->tail - (unsigned char *)nhp;
+ } endfor_nexthops(fi);
+ mp_head->rta_type = RTA_MULTIPATH;
+ mp_head->rta_len = skb->tail - (u8*)mp_head;
+ }
+
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+
+nlmsg_failure:
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+
+static void dn_rtmsg_fib(int event, struct dn_fib_node *f, int z, int tb_id,
+ struct nlmsghdr *nlh, struct netlink_skb_parms *req)
+{
+ struct sk_buff *skb;
+ u32 pid = req ? req->pid : 0;
+ int size = NLMSG_SPACE(sizeof(struct rtmsg) + 256);
+
+ skb = alloc_skb(size, GFP_KERNEL);
+ if (!skb)
+ return;
+
+ if (dn_fib_dump_info(skb, pid, nlh->nlmsg_seq, event, tb_id,
+ f->fn_type, f->fn_scope, &f->fn_key, z,
+ DN_FIB_INFO(f)) < 0) {
+ kfree_skb(skb);
+ return;
+ }
+ NETLINK_CB(skb).dst_groups = RTMGRP_DECnet_ROUTE;
+ if (nlh->nlmsg_flags & NLM_F_ECHO)
+ atomic_inc(&skb->users);
+ netlink_broadcast(rtnl, skb, pid, RTMGRP_DECnet_ROUTE, GFP_KERNEL);
+ if (nlh->nlmsg_flags & NLM_F_ECHO)
+ netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT);
+}
+
+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[3];
+ 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).pid,
+ 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) < 0) {
+ cb->args[3] = i;
+ return -1;
+ }
+ }
+ cb->args[3] = 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[2];
+ for(h = 0; h < dz->dz_divisor; h++) {
+ if (h < s_h)
+ continue;
+ if (h > s_h)
+ memset(&cb->args[3], 0, sizeof(cb->args) - 3*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[2] = h;
+ return -1;
+ }
+ }
+ cb->args[2] = 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[1];
+ 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[2], 0, sizeof(cb->args) - 2*sizeof(cb->args[0]));
+
+ if (dn_hash_dump_zone(skb, cb, tb, dz) < 0) {
+ cb->args[1] = m;
+ read_unlock(&dn_fib_tables_lock);
+ return -1;
+ }
+ }
+ read_unlock(&dn_fib_tables_lock);
+ cb->args[1] = m;
+
+ return skb->len;
+}
+
+#else /* no CONFIG_RTNETLINK */
+
+#define dn_rt_msg_fib(event,f,z,tb_id,nlh,req)
+
+#endif /* CONFIG_RTNETLINK */
+
+static int dn_fib_table_insert(struct dn_fib_table *tb, struct rtmsg *r, struct dn_kern_rta *rta, 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 (rta->rta_dst) {
+ dn_address dst;
+ memcpy(&dst, rta->rta_dst, 2);
+ if (dst & ~DZ_MASK(dz))
+ return -EINVAL;
+ key = dz_key(dst, dz);
+ }
+
+ if ((fi = dn_fib_create_info(r, rta, 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_alloc(dn_hash_kmem, SLAB_KERNEL);
+ if (new_f == NULL)
+ goto out;
+
+ memset(new_f, 0, sizeof(struct dn_fib_node));
+
+ 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 dn_kern_rta *rta, 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 (rta->rta_dst) {
+ dn_address dst;
+ memcpy(&dst, rta->rta_dst, 2);
+ 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, rta, 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 dn_fib_key *
+key, 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(key->dst, dz);
+
+ for(f = dz_chain(k, dz); f; f = f->fn_next) {
+ 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 < key->scope)
+ continue;
+
+ err = dn_fib_semantic_match(f->fn_type, DN_FIB_INFO(f), key, 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;
+}
+
+#ifdef CONFIG_PROC_FS
+
+static unsigned dn_fib_flag_trans(int type, int dead, u16 mask, struct dn_fib_info *fi)
+{
+ static unsigned type2flags[RTN_MAX+1] = {
+ 0, 0, 0, 0, 0, 0, 0, RTF_REJECT, RTF_REJECT, 0, 0, 0
+ };
+ unsigned flags = type2flags[type];
+
+ if (fi && fi->fib_nh->nh_gw)
+ flags |= RTF_GATEWAY;
+ if (mask == 0xFFFF)
+ flags |= RTF_HOST;
+ if (dead)
+ flags |= RTF_UP;
+ return flags;
+}
+
+static void dn_fib_node_get_info(int type, int dead, struct dn_fib_info *fi, u16 prefix, u16 mask, char *buffer)
+{
+ int len;
+ unsigned flags = dn_fib_flag_trans(type, dead, mask, fi);
+
+ if (fi) {
+ len = sprintf(buffer, "%s\t%04x\t%04x\t%04x\t%d\t%u\t%d\t%04x\t%d\t%u\t%u",
+ fi->fib_dev ? fi->fib_dev->name : "*", prefix,
+ fi->fib_nh->nh_gw, flags, 0, 0, fi->fib_priority,
+ mask, 0, 0, 0);
+ } else {
+ len = sprintf(buffer, "*\t%04x\t%04x\t%04x\t%d\t%u\t%d\t%04x\t%d\t%u\t%u",
+ prefix, 0,
+ flags, 0, 0, 0,
+ mask, 0, 0, 0);
+ }
+ memset(buffer+len, ' ', 127-len);
+ buffer[127] = '\n';
+}
+
+static int dn_fib_table_get_info(struct dn_fib_table *tb, char *buffer, int first, int count)
+{
+ struct dn_hash *table = (struct dn_hash *)tb->data;
+ struct dn_zone *dz;
+ int pos = 0;
+ int n = 0;
+
+ read_lock(&dn_fib_tables_lock);
+ for(dz = table->dh_zone_list; dz; dz = dz->dz_next) {
+ int i;
+ struct dn_fib_node *f;
+ int maxslot = dz->dz_divisor;
+ struct dn_fib_node **fp = dz->dz_hash;
+
+ if (dz->dz_nent == 0)
+ continue;
+
+ if (pos + dz->dz_nent < first) {
+ pos += dz->dz_nent;
+ continue;
+ }
+
+ for(i = 0; i < maxslot; i++, fp++) {
+ for(f = *fp; f ; f = f->fn_next) {
+ if (++pos <= first)
+ continue;
+ dn_fib_node_get_info(f->fn_type,
+ f->fn_state & DN_S_ZOMBIE,
+ DN_FIB_INFO(f),
+ dz_prefix(f->fn_key, dz),
+ DZ_MASK(dz), buffer);
+ buffer += 128;
+ if (++n >= count)
+ goto out;
+ }
+ }
+ }
+out:
+ read_unlock(&dn_fib_tables_lock);
+ return n;
+}
+#endif /* CONFIG_PROC_FS */
+
+struct dn_fib_table *dn_fib_get_table(int n, int create)
+{
+ struct dn_fib_table *t;
+
+ if (n < DN_MIN_TABLE)
+ return NULL;
+
+ if (n > DN_NUM_TABLES)
+ return NULL;
+
+ if (dn_fib_tables[n])
+ return dn_fib_tables[n];
+
+ if (!create)
+ return NULL;
+
+ if (in_interrupt() && net_ratelimit()) {
+ printk(KERN_DEBUG "DECnet: BUG! Attempt to create routing table
+from interrupt\n");
+ return NULL;
+ }
+ if ((t = kmalloc(sizeof(struct dn_fib_table), GFP_KERNEL)) == NULL)
+ return NULL;
+
+ memset(t, 0, sizeof(struct dn_fib_table));
+
+ 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;
+#ifdef CONFIG_PROC_FS
+ t->get_info = dn_fib_table_get_info;
+#endif
+#ifdef CONFIG_RTNETLINK
+ t->dump = dn_fib_table_dump;
+#endif
+ dn_fib_tables[n] = t;
+
+ return t;
+}
+
+static void dn_fib_del_tree(int n)
+{
+ struct dn_fib_table *t;
+
+ write_lock(&dn_fib_tables_lock);
+ t = dn_fib_tables[n];
+ dn_fib_tables[n] = NULL;
+ write_unlock(&dn_fib_tables_lock);
+
+ if (t) {
+ kfree_s(t, sizeof(struct dn_fib_table));
+ }
+}
+
+struct dn_fib_table *dn_fib_empty_table(void)
+{
+ int id;
+
+ for(id = DN_MIN_TABLE; id <= DN_NUM_TABLES; id++)
+ if (dn_fib_tables[id] == NULL)
+ return dn_fib_get_table(id, 1);
+ return NULL;
+}
+
+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, NULL);
+}
+
+void __exit dn_fib_table_cleanup(void)
+{
+ return;
+}
+
diff --git a/net/decnet/sysctl_net_decnet.c b/net/decnet/sysctl_net_decnet.c
index 5dcb0ef49..f80e5afc1 100644
--- a/net/decnet/sysctl_net_decnet.c
+++ b/net/decnet/sysctl_net_decnet.c
@@ -104,123 +104,6 @@ static int parse_addr(dn_address *addr, char *str)
return 0;
}
-static char *node2str(int n)
-{
- switch(n) {
- case DN_RT_INFO_ENDN:
- return "EndNode\n";
- case DN_RT_INFO_L1RT:
- return "Level 1 Router\n";
- case DN_RT_INFO_L2RT:
- return "Level 2 Router\n";
- }
-
- return "Unknown\n";
-}
-
-static int dn_node_type_strategy(ctl_table *table, int *name, int nlen,
- void *oldval, size_t *oldlenp,
- void *newval, size_t newlen,
- void **context)
-{
- int len;
- int type;
-
- if (oldval && oldlenp) {
- if (get_user(len, oldlenp))
- return -EFAULT;
- if (len) {
- if (len != sizeof(int))
- return -EINVAL;
- if (put_user(decnet_node_type, (int *)oldval))
- return -EFAULT;
- }
- }
-
- if (newval && newlen) {
- if (newlen != sizeof(int))
- return -EINVAL;
-
- if (get_user(type, (int *)newval))
- return -EFAULT;
-
- switch(type) {
- case DN_RT_INFO_ENDN: /* EndNode */
-#ifdef CONFIG_DECNET_ROUTER
- case DN_RT_INFO_L1RT: /* Level 1 Router */
- case DN_RT_INFO_L2RT: /* Level 2 Router */
-#endif
- break;
- default:
- return -EINVAL;
- }
-
- if (decnet_node_type != type) {
- dn_dev_devices_off();
- decnet_node_type = type;
- dn_dev_devices_on();
- }
- }
- return 0;
-}
-
-static int dn_node_type_handler(ctl_table *table, int write,
- struct file * filp,
- void *buffer, size_t *lenp)
-{
- char *s = node2str(decnet_node_type);
- int len = strlen(s);
-
- if (!*lenp || (filp->f_pos && !write)) {
- *lenp = 0;
- return 0;
- }
-
- if (write) {
- char c = *(char *)buffer;
- int type = 0;
-
- switch(c) {
- case 'e':
- case 'E':
- case '0':
- type = DN_RT_INFO_ENDN;
- break;
-#ifdef CONFIG_DECNET_ROUTER
- case 'r':
- case '1':
- type = DN_RT_INFO_L1RT;
- break;
- case 'R':
- case '2':
- type = DN_RT_INFO_L2RT;
- break;
-#endif /* CONFIG_DECNET_ROUTER */
- default:
- return -EINVAL;
- }
-
- if (decnet_node_type != type) {
- dn_dev_devices_off();
- decnet_node_type = type;
- dn_dev_devices_on();
- }
-
- filp->f_pos += 1;
-
- return 0;
- }
-
- if (len > *lenp) len = *lenp;
-
- if (copy_to_user(buffer, s, len))
- return -EFAULT;
-
- *lenp = len;
- filp->f_pos += len;
-
- return 0;
-}
static int dn_node_address_strategy(ctl_table *table, int *name, int nlen,
void *oldval, size_t *oldlenp,
@@ -374,6 +257,7 @@ static int dn_def_dev_handler(ctl_table *table, int write,
}
if (write) {
+ int i;
if (*lenp > 16)
return -E2BIG;
@@ -382,6 +266,9 @@ static int dn_def_dev_handler(ctl_table *table, int write,
return -EFAULT;
devname[*lenp] = 0;
+ for(i = 0; i < (*lenp); i++)
+ if (devname[i] == '\n')
+ devname[i] = 0;
if ((dev = __dev_get_by_name(devname)) == NULL)
return -ENODEV;
@@ -416,9 +303,6 @@ static int dn_def_dev_handler(ctl_table *table, int write,
}
static ctl_table dn_table[] = {
- {NET_DECNET_NODE_TYPE, "node_type", NULL, 1, 0644, NULL,
- dn_node_type_handler, dn_node_type_strategy, NULL,
- NULL, NULL},
{NET_DECNET_NODE_ADDRESS, "node_address", NULL, 7, 0644, NULL,
dn_node_address_handler, dn_node_address_strategy, NULL,
NULL, NULL},