diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-16 01:07:24 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-16 01:07:24 +0000 |
commit | 95db6b748fc86297827fbd9c9ef174d491c9ad89 (patch) | |
tree | 27a92a942821cde1edda9a1b088718d436b3efe4 /net/decnet | |
parent | 45b27b0a0652331d104c953a5b192d843fff88f8 (diff) |
Merge with Linux 2.3.40.
Diffstat (limited to 'net/decnet')
-rw-r--r-- | net/decnet/Config.in | 3 | ||||
-rw-r--r-- | net/decnet/Makefile | 2 | ||||
-rw-r--r-- | net/decnet/TODO | 38 | ||||
-rw-r--r-- | net/decnet/af_decnet.c | 89 | ||||
-rw-r--r-- | net/decnet/dn_dev.c | 315 | ||||
-rw-r--r-- | net/decnet/dn_fib.c | 1027 | ||||
-rw-r--r-- | net/decnet/dn_neigh.c | 7 | ||||
-rw-r--r-- | net/decnet/dn_nsp_in.c | 35 | ||||
-rw-r--r-- | net/decnet/dn_nsp_out.c | 102 | ||||
-rw-r--r-- | net/decnet/dn_route.c | 289 | ||||
-rw-r--r-- | net/decnet/dn_rules.c | 372 | ||||
-rw-r--r-- | net/decnet/dn_table.c | 907 | ||||
-rw-r--r-- | net/decnet/sysctl_net_decnet.c | 124 |
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}, |