diff options
Diffstat (limited to 'net')
| -rw-r--r-- | net/decnet/TODO | 7 | ||||
| -rw-r--r-- | net/decnet/af_decnet.c | 136 | ||||
| -rw-r--r-- | net/decnet/dn_dev.c | 304 | ||||
| -rw-r--r-- | net/decnet/dn_neigh.c | 5 | ||||
| -rw-r--r-- | net/decnet/dn_route.c | 29 | ||||
| -rw-r--r-- | net/decnet/sysctl_net_decnet.c | 190 |
6 files changed, 491 insertions, 180 deletions
diff --git a/net/decnet/TODO b/net/decnet/TODO index c8ea8178dbfe..8c5484ff5be1 100644 --- a/net/decnet/TODO +++ b/net/decnet/TODO @@ -35,14 +35,11 @@ Steve's quick list of things that need finishing off: 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] + it into a small library of routines. [probably a project for 2.7.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. - o Add the routing message grabbing netfilter module [written, tested, awaiting merge] @@ -55,3 +52,5 @@ Steve's quick list of things that need finishing off: o DECnet sendpages() function + o AIO for DECnet + diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index a8e81d8972ac..6489c5bd6446 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -100,6 +100,7 @@ Version 0.0.6 2.1.110 07-aug-98 Eduardo Marcelo Serrat #include <linux/module.h> #include <linux/errno.h> #include <linux/types.h> +#include <linux/slab.h> #include <linux/socket.h> #include <linux/in.h> #include <linux/kernel.h> @@ -131,21 +132,20 @@ Version 0.0.6 2.1.110 07-aug-98 Eduardo Marcelo Serrat #include <net/dn_fib.h> #include <net/dn_neigh.h> -static void dn_keepalive(struct sock *sk); +struct dn_sock { + struct sock sk; + struct dn_scp scp; +}; -/* - * decnet_address is kept in network order, decnet_ether_address is kept - * as a string of bytes. - */ -dn_address decnet_address = 0; -unsigned char decnet_ether_address[ETH_ALEN] = { 0xAA, 0x00, 0x04, 0x00, 0x00, 0x00 }; +static void dn_keepalive(struct sock *sk); #define DN_SK_HASH_SHIFT 8 #define DN_SK_HASH_SIZE (1 << DN_SK_HASH_SHIFT) #define DN_SK_HASH_MASK (DN_SK_HASH_SIZE - 1) +static kmem_cache_t *dn_sk_cachep; static struct proto_ops dn_proto_ops; -rwlock_t dn_hash_lock = RW_LOCK_UNLOCKED; +static rwlock_t dn_hash_lock = RW_LOCK_UNLOCKED; static struct sock *dn_sk_hash[DN_SK_HASH_SIZE]; static struct sock *dn_wild_sk; @@ -473,17 +473,16 @@ struct sock *dn_alloc_sock(struct socket *sock, int gfp) struct sock *sk; struct dn_scp *scp; - if ((sk = sk_alloc(PF_DECnet, gfp, 1, NULL)) == NULL) + if ((sk = sk_alloc(PF_DECnet, gfp, sizeof(struct dn_sock), dn_sk_cachep)) == NULL) goto no_sock; - scp = kmalloc(sizeof(*scp), gfp); - if (!scp) - goto free_sock; + + scp = (struct dn_scp *)(sk + 1); + DN_SK(sk) = scp; if (sock) { sock->ops = &dn_proto_ops; } sock_init_data(sock,sk); - DN_SK(sk) = scp; sk->backlog_rcv = dn_nsp_backlog_rcv; sk->destruct = dn_destruct; @@ -544,8 +543,7 @@ struct sock *dn_alloc_sock(struct socket *sock, int gfp) MOD_INC_USE_COUNT; return sk; -free_sock: - sk_free(sk); + no_sock: return NULL; } @@ -771,9 +769,6 @@ static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) struct net_device *dev; int rv; - if (sk->zapped == 0) - return -EINVAL; - if (addr_len != sizeof(struct sockaddr_dn)) return -EINVAL; @@ -783,19 +778,30 @@ static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if (dn_ntohs(saddr->sdn_nodeaddrl) && (dn_ntohs(saddr->sdn_nodeaddrl) != 2)) return -EINVAL; - if (saddr->sdn_objnum && !capable(CAP_NET_BIND_SERVICE)) - return -EPERM; - if (dn_ntohs(saddr->sdn_objnamel) > DN_MAXOBJL) return -EINVAL; if (saddr->sdn_flags & ~SDF_WILD) return -EINVAL; - if (saddr->sdn_flags & SDF_WILD) { - if (!capable(CAP_NET_BIND_SERVICE)) - return -EPERM; - } else { +#if 1 + if (!capable(CAP_NET_BIND_SERVICE) && saddr->sdn_objnum || + (saddr->sdn_flags & SDF_WILD)) + return -EACCES; +#else + /* + * Maybe put the default actions in the default security ops for + * dn_prot_sock ? Would be nice if the capable call would go there + * too. + */ + if (security_ops->dn_prot_sock(saddr) && + !capable(CAP_NET_BIND_SERVICE) || + saddr->sdn_objnum || (saddr->sdn_flags & SDF_WILD)) + return -EACCES; +#endif + + + if (!(saddr->sdn_flags & SDF_WILD)) { if (dn_ntohs(saddr->sdn_nodeaddrl)) { read_lock(&dev_base_lock); for(dev = dev_base; dev; dev = dev->next) { @@ -810,12 +816,18 @@ static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) } } + rv = -EINVAL; + lock_sock(sk); + if (sk->zapped != 0) { + memcpy(&scp->addr, saddr, addr_len); + sk->zapped = 0; - memcpy(&scp->addr, saddr, addr_len); - sk->zapped = 0; - - if ((rv = dn_hash_sock(sk)) != 0) - sk->zapped = 1; + rv = dn_hash_sock(sk); + if (rv) { + sk->zapped = 1; + } + } + release_sock(sk); return rv; } @@ -825,6 +837,7 @@ static int dn_auto_bind(struct socket *sock) { struct sock *sk = sock->sk; struct dn_scp *scp = DN_SK(sk); + int rv; sk->zapped = 0; @@ -844,13 +857,18 @@ static int dn_auto_bind(struct socket *sock) scp->accessdata.acc_accl = 0; memset(scp->accessdata.acc_acc, 0, 40); } + /* End of compatibility stuff */ scp->addr.sdn_add.a_len = dn_htons(2); - *(dn_address *)scp->addr.sdn_add.a_addr = decnet_address; - - dn_hash_sock(sk); + rv = dn_dev_bind_default((dn_address *)scp->addr.sdn_add.a_addr); + if (rv == 0) { + rv = dn_hash_sock(sk); + if (rv) { + sk->zapped = 1; + } + } - return 0; + return rv; } @@ -1209,6 +1227,7 @@ static int dn_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return dn_fib_ioctl(sock, cmd, arg); #endif /* CONFIG_DECNET_ROUTER */ +#if 0 case OSIOCSNETADDR: if (!capable(CAP_NET_ADMIN)) { err = -EPERM; @@ -1218,7 +1237,6 @@ static int dn_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) dn_dev_devices_off(); decnet_address = (unsigned short)arg; - dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address)); dn_dev_devices_on(); err = 0; @@ -1227,6 +1245,7 @@ static int dn_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) case OSIOCGNETADDR: err = put_user(decnet_address, (unsigned short *)arg); break; +#endif case SIOCGIFCONF: case SIOCGIFFLAGS: case SIOCGIFBRDADDR: @@ -2227,38 +2246,24 @@ void dn_unregister_sysctl(void); #endif -#ifdef MODULE MODULE_DESCRIPTION("The Linux DECnet Network Protocol"); MODULE_AUTHOR("Linux DECnet Project Team"); MODULE_LICENSE("GPL"); -static int addr[2] = {0, 0}; - -MODULE_PARM(addr, "2i"); -MODULE_PARM_DESC(addr, "The DECnet address of this machine: area,node"); -#endif -static char banner[] __initdata = KERN_INFO "NET4: DECnet for Linux: V.2.4.20-pre1s (C) 1995-2002 Linux DECnet Project Team\n"; +static char banner[] __initdata = KERN_INFO "NET4: DECnet for Linux: V.2.5.40s (C) 1995-2002 Linux DECnet Project Team\n"; static int __init decnet_init(void) { -#ifdef MODULE - if (addr[0] > 63 || addr[0] < 0) { - printk(KERN_ERR "DECnet: Area must be between 0 and 63"); - return 1; - } - - if (addr[1] > 1023 || addr[1] < 0) { - printk(KERN_ERR "DECnet: Node must be between 0 and 1023"); - return 1; - } - - decnet_address = dn_htons((addr[0] << 10) | addr[1]); - dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address)); -#endif - printk(banner); + dn_sk_cachep = kmem_cache_create("decnet_socket_cache", + sizeof(struct dn_sock), + 0, SLAB_HWCACHE_ALIGN, + NULL, NULL); + if (!dn_sk_cachep) + return -ENOMEM; + sock_register(&dn_family_ops); dev_add_pack(&dn_dix_packet_type); register_netdevice_notifier(&dn_dev_notifier); @@ -2288,21 +2293,6 @@ static int __init decnet_init(void) } -#ifndef MODULE -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); - - decnet_address = dn_htons(area << 10 | node); - dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address)); - - return 1; -} - -__setup("decnet=", decnet_setup); -#endif - static void __exit decnet_exit(void) { sock_unregister(AF_DECnet); @@ -2323,6 +2313,8 @@ static void __exit decnet_exit(void) #endif /* CONFIG_DECNET_ROUTER */ proc_net_remove("decnet"); + + kmem_cache_destroy(dn_sk_cachep); } module_init(decnet_init); diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index 989bcb931458..cfeddada7873 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -23,6 +23,8 @@ */ #include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> #include <linux/net.h> #include <linux/netdevice.h> #include <linux/proc_fs.h> @@ -52,16 +54,23 @@ static unsigned char dn_eco_version[3] = {0x02,0x00,0x00}; extern struct neigh_table dn_neigh_table; -struct net_device *decnet_default_device; +/* + * decnet_address is kept in network order. + */ +dn_address decnet_address = 0; + +static rwlock_t dndev_lock = RW_LOCK_UNLOCKED; +static struct net_device *decnet_default_device; static struct dn_dev *dn_dev_create(struct net_device *dev, int *err); static void dn_dev_delete(struct net_device *dev); 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); +static void dn_eth_down(struct net_device *); +static void dn_send_brd_hello(struct net_device *dev, struct dn_ifaddr *ifa); #if 0 -static void dn_send_ptp_hello(struct net_device *dev); +static void dn_send_ptp_hello(struct net_device *dev, struct dn_ifaddr *ifa); #endif static struct dn_dev_parms dn_dev_list[] = { @@ -75,6 +84,7 @@ static struct dn_dev_parms dn_dev_list[] = { .name = "ethernet", .ctl_name = NET_DECNET_CONF_ETHER, .up = dn_eth_up, + .down = dn_eth_down, .timer3 = dn_send_brd_hello, }, { @@ -249,6 +259,51 @@ static void dn_dev_sysctl_unregister(struct dn_dev_parms *parms) } } +struct net_device *dn_dev_get_default(void) +{ + struct net_device *dev; + read_lock(&dndev_lock); + dev = decnet_default_device; + if (dev) { + if (dev->dn_ptr) + dev_hold(dev); + else + dev = NULL; + } + read_unlock(&dndev_lock); + return dev; +} + +int dn_dev_set_default(struct net_device *dev, int force) +{ + struct net_device *old = NULL; + int rv = -EBUSY; + if (!dev->dn_ptr) + return -ENODEV; + write_lock(&dndev_lock); + if (force || decnet_default_device == NULL) { + old = decnet_default_device; + decnet_default_device = dev; + rv = 0; + } + write_unlock(&dndev_lock); + if (old) + dev_put(dev); + return rv; +} + +static void dn_dev_check_default(struct net_device *dev) +{ + write_lock(&dndev_lock); + if (dev == decnet_default_device) { + decnet_default_device = NULL; + } else { + dev = NULL; + } + write_unlock(&dndev_lock); + if (dev) + dev_put(dev); +} static int dn_forwarding_proc(ctl_table *table, int write, struct file *filep, @@ -364,9 +419,20 @@ static __inline__ void dn_dev_free_ifa(struct dn_ifaddr *ifa) static void dn_dev_del_ifa(struct dn_dev *dn_db, struct dn_ifaddr **ifap, int destroy) { struct dn_ifaddr *ifa1 = *ifap; + unsigned char mac_addr[6]; + struct net_device *dev = dn_db->dev; + + ASSERT_RTNL(); *ifap = ifa1->ifa_next; + if (dn_db->dev->type == ARPHRD_ETHER) { + if (ifa1->ifa_local != dn_htons(dn_eth2dn(dev->dev_addr))) { + dn_dn2eth(mac_addr, ifa1->ifa_local); + dev_mc_delete(dev, mac_addr, ETH_ALEN, 0); + } + } + rtmsg_ifa(RTM_DELADDR, ifa1); if (destroy) { @@ -379,9 +445,25 @@ static void dn_dev_del_ifa(struct dn_dev *dn_db, struct dn_ifaddr **ifap, int de static int dn_dev_insert_ifa(struct dn_dev *dn_db, struct dn_ifaddr *ifa) { - /* - * FIXME: Duplicate check here. - */ + struct net_device *dev = dn_db->dev; + struct dn_ifaddr *ifa1; + unsigned char mac_addr[6]; + + ASSERT_RTNL(); + + /* Check for duplicates */ + for(ifa1 = dn_db->ifa_list; ifa1; ifa1 = ifa1->ifa_next) { + if (ifa1->ifa_local == ifa->ifa_local) + return -EEXIST; + } + + if (dev->type == ARPHRD_ETHER) { + if (ifa->ifa_local != dn_htons(dn_eth2dn(dev->dev_addr))) { + dn_dn2eth(mac_addr, ifa->ifa_local); + dev_mc_add(dev, mac_addr, ETH_ALEN, 0); + dev_mc_upload(dev); + } + } ifa->ifa_next = dn_db->ifa_list; dn_db->ifa_list = ifa; @@ -394,6 +476,7 @@ static int dn_dev_insert_ifa(struct dn_dev *dn_db, struct dn_ifaddr *ifa) static int dn_dev_set_ifa(struct net_device *dev, struct dn_ifaddr *ifa) { struct dn_dev *dn_db = dev->dn_ptr; + int rv; if (dn_db == NULL) { int err; @@ -407,7 +490,10 @@ static int dn_dev_set_ifa(struct net_device *dev, struct dn_ifaddr *ifa) if (dev->flags & IFF_LOOPBACK) ifa->ifa_scope = RT_SCOPE_HOST; - return dn_dev_insert_ifa(dn_db, ifa); + rv = dn_dev_insert_ifa(dn_db, ifa); + if (rv) + dn_dev_free_ifa(ifa); + return rv; } @@ -538,6 +624,7 @@ static int dn_dev_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *a struct dn_dev *dn_db; struct ifaddrmsg *ifm = NLMSG_DATA(nlh); struct dn_ifaddr *ifa; + int rv; if (rta[IFA_LOCAL-1] == NULL) return -EINVAL; @@ -564,7 +651,10 @@ static int dn_dev_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *a else memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); - return dn_dev_insert_ifa(dn_db, ifa); + rv = dn_dev_insert_ifa(dn_db, ifa); + if (rv) + dn_dev_free_ifa(ifa); + return rv; } static int dn_dev_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa, @@ -651,7 +741,52 @@ done: return skb->len; } -static void dn_send_endnode_hello(struct net_device *dev) +static int dn_dev_get_first(struct net_device *dev, dn_address *addr) +{ + struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr; + struct dn_ifaddr *ifa; + int rv = -ENODEV; + if (dn_db == NULL) + goto out; + ifa = dn_db->ifa_list; + if (ifa != NULL) { + *addr = ifa->ifa_local; + rv = 0; + } +out: + return rv; +} + +/* + * Find a default address to bind to. + * + * This is one of those areas where the initial VMS concepts don't really + * map onto the Linux concepts, and since we introduced multiple addresses + * per interface we have to cope with slightly odd ways of finding out what + * "our address" really is. Mostly its not a problem; for this we just guess + * a sensible default. Eventually the routing code will take care of all the + * nasties for us I hope. + */ +int dn_dev_bind_default(dn_address *addr) +{ + struct net_device *dev; + int rv; + dev = dn_dev_get_default(); +last_chance: + if (dev) { + read_lock(&dev_base_lock); + rv = dn_dev_get_first(dev, addr); + read_unlock(&dev_base_lock); + dev_put(dev); + if (rv == 0 || dev == &loopback_dev) + return rv; + } + dev = &loopback_dev; + dev_hold(dev); + goto last_chance; +} + +static void dn_send_endnode_hello(struct net_device *dev, struct dn_ifaddr *ifa) { struct endnode_hello_message *msg; struct sk_buff *skb = NULL; @@ -667,7 +802,7 @@ static void dn_send_endnode_hello(struct net_device *dev) msg->msgflg = 0x0D; memcpy(msg->tiver, dn_eco_version, 3); - memcpy(msg->id, decnet_ether_address, 6); + dn_dn2eth(msg->id, ifa->ifa_local); msg->iinfo = DN_RT_INFO_ENDN; msg->blksize = dn_htons(dn_db->parms.blksize); msg->area = 0x00; @@ -689,7 +824,7 @@ static void dn_send_endnode_hello(struct net_device *dev) skb->nh.raw = skb->data; - dn_rt_finish_output(skb, dn_rt_all_rt_mcast); + dn_rt_finish_output(skb, dn_rt_all_rt_mcast, msg->id); } @@ -697,7 +832,7 @@ static void dn_send_endnode_hello(struct net_device *dev) #define DRDELAY (5 * HZ) -static int dn_am_i_a_router(struct dn_neigh *dn, struct dn_dev *dn_db) +static int dn_am_i_a_router(struct dn_neigh *dn, struct dn_dev *dn_db, struct dn_ifaddr *ifa) { /* First check time since device went up */ if ((jiffies - dn_db->uptime) < DRDELAY) @@ -715,13 +850,13 @@ static int dn_am_i_a_router(struct dn_neigh *dn, struct dn_dev *dn_db) if (dn->priority != dn_db->parms.priority) return 0; - if (dn_ntohs(dn->addr) < dn_ntohs(decnet_address)) + if (dn_ntohs(dn->addr) < dn_ntohs(ifa->ifa_local)) return 1; return 0; } -static void dn_send_router_hello(struct net_device *dev) +static void dn_send_router_hello(struct net_device *dev, struct dn_ifaddr *ifa) { int n; struct dn_dev *dn_db = dev->dn_ptr; @@ -731,6 +866,7 @@ static void dn_send_router_hello(struct net_device *dev) unsigned char *ptr; unsigned char *i1, *i2; unsigned short *pktlen; + char *src; if (dn_db->parms.blksize < (26 + 7)) return; @@ -753,7 +889,8 @@ static void dn_send_router_hello(struct net_device *dev) *ptr++ = 2; /* ECO */ *ptr++ = 0; *ptr++ = 0; - memcpy(ptr, decnet_ether_address, ETH_ALEN); + dn_dn2eth(ptr, ifa->ifa_local); + src = ptr; ptr += ETH_ALEN; *ptr++ = dn_db->parms.forwarding == 1 ? DN_RT_INFO_L1RT : DN_RT_INFO_L2RT; @@ -781,34 +918,34 @@ static void dn_send_router_hello(struct net_device *dev) skb->nh.raw = skb->data; - if (dn_am_i_a_router(dn, dn_db)) { + if (dn_am_i_a_router(dn, dn_db, ifa)) { struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC); if (skb2) { - dn_rt_finish_output(skb2, dn_rt_all_end_mcast); + dn_rt_finish_output(skb2, dn_rt_all_end_mcast, src); } } - dn_rt_finish_output(skb, dn_rt_all_rt_mcast); + dn_rt_finish_output(skb, dn_rt_all_rt_mcast, src); } -static void dn_send_brd_hello(struct net_device *dev) +static void dn_send_brd_hello(struct net_device *dev, struct dn_ifaddr *ifa) { struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr; if (dn_db->parms.forwarding == 0) - dn_send_endnode_hello(dev); + dn_send_endnode_hello(dev, ifa); else - dn_send_router_hello(dev); + dn_send_router_hello(dev, ifa); } #else -static void dn_send_brd_hello(struct net_device *dev) +static void dn_send_brd_hello(struct net_device *dev, struct dn_ifaddr *ifa) { - dn_send_endnode_hello(dev); + dn_send_endnode_hello(dev, ifa); } #endif #if 0 -static void dn_send_ptp_hello(struct net_device *dev) +static void dn_send_ptp_hello(struct net_device *dev, struct dn_ifaddr *ifa) { int tdlen = 16; int size = dev->hard_header_len + 2 + 4 + tdlen; @@ -817,6 +954,7 @@ static void dn_send_ptp_hello(struct net_device *dev) int i; unsigned char *ptr; struct dn_neigh *dn = (struct dn_neigh *)dn_db->router; + char src[ETH_ALEN]; if (skb == NULL) return ; @@ -826,21 +964,15 @@ static void dn_send_ptp_hello(struct net_device *dev) ptr = skb_put(skb, 2 + 4 + tdlen); *ptr++ = DN_RT_PKT_HELO; - *((dn_address *)ptr) = decnet_address; + *((dn_address *)ptr) = ifa->ifa_local; ptr += 2; *ptr++ = tdlen; for(i = 0; i < tdlen; i++) *ptr++ = 0252; - 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); - } - } - - dn_rt_finish_output(skb, dn_rt_all_rt_mcast); + dn_dn2eth(src, ifa->ifa_local); + dn_rt_finish_output(skb, dn_rt_all_rt_mcast, src); } #endif @@ -860,16 +992,31 @@ static int dn_eth_up(struct net_device *dev) return 0; } +static void dn_eth_down(struct net_device *dev) +{ + struct dn_dev *dn_db = dev->dn_ptr; + + if (dn_db->parms.forwarding == 0) + dev_mc_delete(dev, dn_rt_all_end_mcast, ETH_ALEN, 0); + else + dev_mc_delete(dev, dn_rt_all_rt_mcast, ETH_ALEN, 0); +} + static void dn_dev_set_timer(struct net_device *dev); static void dn_dev_timer_func(unsigned long arg) { struct net_device *dev = (struct net_device *)arg; struct dn_dev *dn_db = dev->dn_ptr; + struct dn_ifaddr *ifa; if (dn_db->t3 <= dn_db->parms.t2) { - if (dn_db->parms.timer3) - dn_db->parms.timer3(dev); + if (dn_db->parms.timer3) { + for(ifa = dn_db->ifa_list; ifa; ifa = ifa->ifa_next) { + if (!(ifa->ifa_flags & IFA_F_SECONDARY)) + dn_db->parms.timer3(dev, ifa); + } + } dn_db->t3 = dn_db->parms.t3; } else { dn_db->t3 -= dn_db->parms.t2; @@ -917,8 +1064,6 @@ struct dn_dev *dn_dev_create(struct net_device *dev, int *err) dn_db->dev = dev; init_timer(&dn_db->timer); - memcpy(dn_db->addr, decnet_ether_address, ETH_ALEN); /* To go... */ - dn_db->uptime = jiffies; if (dn_db->parms.up) { if (dn_db->parms.up(dev) < 0) { @@ -929,7 +1074,6 @@ struct dn_dev *dn_dev_create(struct net_device *dev, int *err) } dn_db->neigh_parms = neigh_parms_alloc(dev, &dn_neigh_table); - /* dn_db->neigh_parms->neigh_setup = dn_db->parms.neigh_setup; */ dn_dev_sysctl_register(dev, &dn_db->parms); @@ -945,27 +1089,64 @@ struct dn_dev *dn_dev_create(struct net_device *dev, int *err) * the loopback device & ethernet devices with correct * MAC addreses automatically. Others must be started * specifically. + * + * FIXME: How should we configure the loopback address ? If we could dispense + * with using decnet_address here and for autobind, it will be one less thing + * for users to worry about setting up. */ + void dn_dev_up(struct net_device *dev) { struct dn_ifaddr *ifa; + dn_address addr = decnet_address; + int maybe_default = 0; + struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr; if ((dev->type != ARPHRD_ETHER) && (dev->type != ARPHRD_LOOPBACK)) return; - if (dev->type == ARPHRD_ETHER) - if (memcmp(dev->dev_addr, decnet_ether_address, ETH_ALEN) != 0) + /* + * Need to ensure that loopback device has a dn_db attached to it + * to allow creation of neighbours against it, even though it might + * not have a local address of its own. Might as well do the same for + * all autoconfigured interfaces. + */ + if (dn_db == NULL) { + int err; + dn_db = dn_dev_create(dev, &err); + if (dn_db == NULL) + return; + } + + if (dev->type == ARPHRD_ETHER) { + if (memcmp(dev->dev_addr, dn_hiord, 4) != 0) return; + addr = dn_htons(dn_eth2dn(dev->dev_addr)); + maybe_default = 1; + } + + if (addr == 0) + return; if ((ifa = dn_dev_alloc_ifa()) == NULL) return; - ifa->ifa_local = decnet_address; + ifa->ifa_local = addr; ifa->ifa_flags = 0; ifa->ifa_scope = RT_SCOPE_UNIVERSE; strcpy(ifa->ifa_label, dev->name); dn_dev_set_ifa(dev, ifa); + + /* + * Automagically set the default device to the first automatically + * configured ethernet card in the system. + */ + if (maybe_default) { + dev_hold(dev); + if (dn_dev_set_default(dev, 0)) + dev_put(dev); + } } static void dn_dev_delete(struct net_device *dev) @@ -976,14 +1157,10 @@ static void dn_dev_delete(struct net_device *dev) return; del_timer_sync(&dn_db->timer); - dn_dev_sysctl_unregister(&dn_db->parms); - + dn_dev_check_default(dev); neigh_ifdown(&dn_neigh_table, dev); - if (dev == decnet_default_device) - decnet_default_device = NULL; - if (dn_db->parms.down) dn_db->parms.down(dev); @@ -1204,8 +1381,28 @@ static struct rtnetlink_link dnet_rtnetlink_table[RTM_MAX-RTM_BASE+1] = #endif }; +#ifdef MODULE +static int addr[2] = {0, 0}; + +MODULE_PARM(addr, "2i"); +MODULE_PARM_DESC(addr, "The DECnet address of this machine: area,node"); +#endif + void __init dn_dev_init(void) { +#ifdef MODULE + if (addr[0] > 63 || addr[0] < 0) { + printk(KERN_ERR "DECnet: Area must be between 0 and 63"); + return 1; + } + + if (addr[1] > 1023 || addr[1] < 0) { + printk(KERN_ERR "DECnet: Node must be between 0 and 1023"); + return 1; + } + + decnet_address = dn_htons((addr[0] << 10) | addr[1]); +#endif dn_dev_devices_on(); #ifdef CONFIG_DECNET_SIOCGIFCONF @@ -1247,3 +1444,18 @@ void __exit dn_dev_cleanup(void) dn_dev_devices_off(); } + +#ifndef MODULE +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); + + decnet_address = dn_htons(area << 10 | node); + + return 1; +} + +__setup("decnet=", decnet_setup); +#endif + diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index 2857bf2fc94c..161cdf8a5f3f 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -181,10 +181,13 @@ static void dn_short_error_report(struct neighbour *neigh, struct sk_buff *skb) static int dn_neigh_output_packet(struct sk_buff *skb) { struct dst_entry *dst = skb->dst; + struct dn_route *rt = (struct dn_route *)dst; struct neighbour *neigh = dst->neighbour; struct net_device *dev = neigh->dev; + char mac_addr[ETH_ALEN]; - if (!dev->hard_header || dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len) >= 0) + dn_dn2eth(mac_addr, rt->rt_saddr); + if (!dev->hard_header || dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, mac_addr, skb->len) >= 0) return neigh->ops->queue_xmit(skb); if (net_ratelimit()) diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index a02f53734033..de86208c480c 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -334,7 +334,7 @@ static int dn_return_short(struct sk_buff *skb) *dst = tmp; skb->pkt_type = PACKET_OUTGOING; - dn_rt_finish_output(skb, NULL); + dn_rt_finish_output(skb, NULL, NULL); return NET_RX_SUCCESS; } @@ -380,7 +380,7 @@ static int dn_return_long(struct sk_buff *skb) memcpy(dst_addr, tmp, ETH_ALEN); skb->pkt_type = PACKET_OUTGOING; - dn_rt_finish_output(skb, tmp); + dn_rt_finish_output(skb, dst_addr, src_addr); return NET_RX_SUCCESS; } @@ -641,7 +641,9 @@ static int dn_forward(struct sk_buff *skb) struct dn_skb_cb *cb = DN_SKB_CB(skb); struct dst_entry *dst = skb->dst; struct neighbour *neigh; +#ifdef CONFIG_NETFILTER struct net_device *dev = skb->dev; +#endif int err = -EINVAL; if ((neigh = dst->neighbour) == NULL) @@ -711,10 +713,11 @@ static int dn_rt_bug(struct sk_buff *skb) static int dn_route_output_slow(struct dst_entry **pprt, dn_address dst, dn_address src, int flags) { struct dn_route *rt = NULL; - struct net_device *dev = decnet_default_device; + struct net_device *dev = NULL; struct neighbour *neigh = NULL; struct dn_dev *dn_db; unsigned hash; + #ifdef CONFIG_DECNET_ROUTER struct dn_fib_key key; struct dn_fib_res res; @@ -765,13 +768,25 @@ static int dn_route_output_slow(struct dst_entry **pprt, dn_address dst, dn_addr goto got_route; } + dev = dn_dev_get_default(); if (dev == NULL) return -EINVAL; dn_db = dev->dn_ptr; - if (dn_db == NULL) + /* Check to see if its one of our own local addresses */ + if (dn_dev_islocal(dev, dst)) { + struct net_device *lo = &loopback_dev; + if (lo->dn_ptr) { + neigh = __neigh_lookup(&dn_neigh_table, &dst, lo, 1); + if (neigh) + goto got_route; + } + if (net_ratelimit()) + printk("dn_route_output_slow: Dest is local interface address, but loopback device is not up\n"); + dev_put(dev); return -EINVAL; + } /* Try default router */ if ((neigh = neigh_clone(dn_db->router)) != NULL) @@ -781,10 +796,12 @@ static int dn_route_output_slow(struct dst_entry **pprt, dn_address dst, dn_addr if ((neigh = __neigh_lookup(&dn_neigh_table, &dst, dev, 1)) != NULL) goto got_route; - + dev_put(dev); return -EINVAL; got_route: + if (dev) + dev_put(dev); if ((rt = dst_alloc(&dn_dst_ops)) == NULL) { neigh_release(neigh); @@ -809,7 +826,7 @@ got_route: rt->u.dst.output = dn_output; rt->u.dst.input = dn_rt_bug; - if (dn_dev_islocal(neigh->dev, rt->rt_daddr)) + if (neigh->dev->flags & IFF_LOOPBACK) rt->u.dst.input = dn_nsp_rx; hash = dn_hash(rt->key.saddr, rt->key.daddr); diff --git a/net/decnet/sysctl_net_decnet.c b/net/decnet/sysctl_net_decnet.c index e62c9b7fcd0b..384c9d90b27d 100644 --- a/net/decnet/sysctl_net_decnet.c +++ b/net/decnet/sysctl_net_decnet.c @@ -9,6 +9,7 @@ * * * Changes: + * Steve Whitehouse - C99 changes and default device handling * */ #include <linux/config.h> @@ -152,7 +153,6 @@ static int dn_node_address_strategy(ctl_table *table, int *name, int nlen, dn_dev_devices_off(); decnet_address = addr; - dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address)); dn_dev_devices_on(); } @@ -187,7 +187,6 @@ static int dn_node_address_handler(ctl_table *table, int write, dn_dev_devices_off(); decnet_address = dnaddr; - dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address)); dn_dev_devices_on(); @@ -218,9 +217,10 @@ static int dn_def_dev_strategy(ctl_table *table, int *name, int nlen, void **context) { size_t len; - struct net_device *dev = decnet_default_device; + struct net_device *dev; char devname[17]; size_t namel; + int rv = 0; devname[0] = 0; @@ -228,8 +228,11 @@ static int dn_def_dev_strategy(ctl_table *table, int *name, int nlen, if (get_user(len, oldlenp)) return -EFAULT; if (len) { - if (dev) + dev = dn_dev_get_default(); + if (dev) { strcpy(devname, dev->name); + dev_put(dev); + } namel = strlen(devname) + 1; if (len > namel) len = namel; @@ -251,16 +254,19 @@ static int dn_def_dev_strategy(ctl_table *table, int *name, int nlen, devname[newlen] = 0; - if ((dev = __dev_get_by_name(devname)) == NULL) + dev = dev_get_by_name(devname); + if (dev == NULL) return -ENODEV; - if (dev->dn_ptr == NULL) - return -ENODEV; - - decnet_default_device = dev; + rv = -ENODEV; + if (dev->dn_ptr != NULL) { + rv = dn_dev_set_default(dev, 1); + if (rv) + dev_put(dev); + } } - return 0; + return rv; } @@ -269,7 +275,7 @@ static int dn_def_dev_handler(ctl_table *table, int write, void *buffer, size_t *lenp) { size_t len; - struct net_device *dev = decnet_default_device; + struct net_device *dev; char devname[17]; if (!*lenp || (filp->f_pos && !write)) { @@ -287,24 +293,32 @@ static int dn_def_dev_handler(ctl_table *table, int write, devname[*lenp] = 0; strip_it(devname); - if ((dev = __dev_get_by_name(devname)) == NULL) + dev = dev_get_by_name(devname); + if (dev == NULL) return -ENODEV; - if (dev->dn_ptr == NULL) + if (dev->dn_ptr == NULL) { + dev_put(dev); return -ENODEV; + } - decnet_default_device = dev; + if (dn_dev_set_default(dev, 1)) { + dev_put(dev); + return -ENODEV; + } filp->f_pos += *lenp; return 0; } + dev = dn_dev_get_default(); if (dev == NULL) { *lenp = 0; return 0; } strcpy(devname, dev->name); + dev_put(dev); len = strlen(devname); devname[len++] = '\n'; @@ -320,51 +334,125 @@ static int dn_def_dev_handler(ctl_table *table, int write, } static ctl_table dn_table[] = { - {NET_DECNET_NODE_ADDRESS, "node_address", NULL, 7, 0644, NULL, - dn_node_address_handler, dn_node_address_strategy, NULL, - NULL, NULL}, - {NET_DECNET_NODE_NAME, "node_name", node_name, 7, 0644, NULL, - &proc_dostring, &sysctl_string, NULL, NULL, NULL}, - {NET_DECNET_DEFAULT_DEVICE, "default_device", NULL, 16, 0644, NULL, - dn_def_dev_handler, dn_def_dev_strategy, NULL, NULL, NULL}, - {NET_DECNET_TIME_WAIT, "time_wait", &decnet_time_wait, - sizeof(int), 0644, - NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL, - &min_decnet_time_wait, &max_decnet_time_wait}, - {NET_DECNET_DN_COUNT, "dn_count", &decnet_dn_count, - sizeof(int), 0644, - NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL, - &min_state_count, &max_state_count}, - {NET_DECNET_DI_COUNT, "di_count", &decnet_di_count, - sizeof(int), 0644, - NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL, - &min_state_count, &max_state_count}, - {NET_DECNET_DR_COUNT, "dr_count", &decnet_dr_count, - sizeof(int), 0644, - NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL, - &min_state_count, &max_state_count}, - {NET_DECNET_DST_GC_INTERVAL, "dst_gc_interval", &decnet_dst_gc_interval, - sizeof(int), 0644, - NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL, - &min_decnet_dst_gc_interval, &max_decnet_dst_gc_interval}, - {NET_DECNET_NO_FC_MAX_CWND, "no_fc_max_cwnd", &decnet_no_fc_max_cwnd, - sizeof(int), 0644, - NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL, - &min_decnet_no_fc_max_cwnd, &max_decnet_no_fc_max_cwnd}, - {NET_DECNET_DEBUG_LEVEL, "debug", &decnet_debug_level, - sizeof(int), 0644, - NULL, &proc_dointvec, &sysctl_intvec, NULL, - NULL, NULL}, + { + .ctl_name = NET_DECNET_NODE_ADDRESS, + .procname = "node_address", + .maxlen = 7, + .mode = 0644, + .proc_handler = dn_node_address_handler, + .strategy = dn_node_address_strategy, + }, + { + .ctl_name = NET_DECNET_NODE_NAME, + .procname = "node_name", + .data = node_name, + .maxlen = 7, + .mode = 0644, + .proc_handler = &proc_dostring, + .strategy = &sysctl_string, + }, + { + .ctl_name = NET_DECNET_DEFAULT_DEVICE, + .procname = "default_device", + .maxlen = 16, + .mode = 0644, + .proc_handler = dn_def_dev_handler, + .strategy = dn_def_dev_strategy, + }, + { + .ctl_name = NET_DECNET_TIME_WAIT, + .procname = "time_wait", + .data = &decnet_time_wait, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &min_decnet_time_wait, + .extra2 = &max_decnet_time_wait + }, + { + .ctl_name = NET_DECNET_DN_COUNT, + .procname = "dn_count", + .data = &decnet_dn_count, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &min_state_count, + .extra2 = &max_state_count + }, + { + .ctl_name = NET_DECNET_DI_COUNT, + .procname = "di_count", + .data = &decnet_di_count, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &min_state_count, + .extra2 = &max_state_count + }, + { + .ctl_name = NET_DECNET_DR_COUNT, + .procname = "dr_count", + .data = &decnet_dr_count, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &min_state_count, + .extra2 = &max_state_count + }, + { + .ctl_name = NET_DECNET_DST_GC_INTERVAL, + .procname = "dst_gc_interval", + .data = &decnet_dst_gc_interval, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &min_decnet_dst_gc_interval, + .extra2 = &max_decnet_dst_gc_interval + }, + { + .ctl_name = NET_DECNET_NO_FC_MAX_CWND, + .procname = "no_fc_max_cwnd", + .data = &decnet_no_fc_max_cwnd, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &min_decnet_no_fc_max_cwnd, + .extra2 = &max_decnet_no_fc_max_cwnd + }, + { + .ctl_name = NET_DECNET_DEBUG_LEVEL, + .procname = "debug", + .data = &decnet_debug_level, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + .strategy = &sysctl_intvec, + }, {0} }; static ctl_table dn_dir_table[] = { - {NET_DECNET, "decnet", NULL, 0, 0555, dn_table}, + { + .ctl_name = NET_DECNET, + .procname = "decnet", + .mode = 0555, + .child = dn_table}, {0} }; static ctl_table dn_root_table[] = { - {CTL_NET, "net", NULL, 0, 0555, dn_dir_table}, + { + .ctl_name = CTL_NET, + .procname = "net", + .mode = 0555, + .child = dn_dir_table + }, {0} }; |
