/*
* DECnet An implementation of the DECnet protocol suite for the LINUX
* operating system. DECnet is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
* DECnet Device Layer
*
* Authors: Steve Whitehouse <SteveW@ACM.org>
* Eduardo Marcelo Serrat <emserrat@geocities.com>
*
* Changes:
* Steve Whitehouse : Devices now see incoming frames so they
* can mark on who it came from.
* Steve Whitehouse : Fixed bug in creating neighbours. Each neighbour
* can now have a device specific setup func.
* Steve Whitehouse : Added /proc/sys/net/decnet/conf/<dev>/
* Steve Whitehouse : Fixed bug which sometimes killed timer
* Steve Whitehouse : Multiple ifaddr support
* Steve Whitehouse : SIOCGIFCONF is now a compile time option
* Steve Whitehouse : /proc/sys/net/decnet/conf/<sys>/forwarding
* Steve Whitehouse : Removed timer1 - it's a user space issue now
* Patrick Caulfield : Fixed router hello message format
* Steve Whitehouse : Got rid of constant sizes for blksize for
* devices. All mtu based now.
*/
#include <linux/capability.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/if_addr.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/skbuff.h>
#include <linux/sysctl.h>
#include <linux/notifier.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <net/neighbour.h>
#include <net/dst.h>
#include <net/flow.h>
#include <net/fib_rules.h>
#include <net/netlink.h>
#include <net/dn.h>
#include <net/dn_dev.h>
#include <net/dn_route.h>
#include <net/dn_neigh.h>
#include <net/dn_fib.h>
#define DN_IFREQ_SIZE (sizeof(struct ifreq) - sizeof(struct sockaddr) + sizeof(struct sockaddr_dn))
static char dn_rt_all_end_mcast[ETH_ALEN] = {0xAB,0x00,0x00,0x04,0x00,0x00};
static char dn_rt_all_rt_mcast[ETH_ALEN] = {0xAB,0x00,0x00,0x03,0x00,0x00};
static char dn_hiord[ETH_ALEN] = {0xAA,0x00,0x04,0x00,0x00,0x00};
static unsigned char dn_eco_version[3] = {0x02,0x00,0x00};
extern struct neigh_table dn_neigh_table;
/*
* decnet_address is kept in network order.
*/
__le16 decnet_address = 0;
static DEFINE_RWLOCK(dndev_lock);
static struct net_device *decnet_default_device;
static BLOCKING_NOTIFIER_HEAD(dnaddr_chain);
static struct dn_dev *dn_dev_create(struct net_device *dev, int *err);
static void dn_dev_delete(struct net_device *dev);
static void dn_ifaddr_notify(int event, struct dn_ifaddr *ifa);
static int dn_eth_up(struct net_device *);
static void dn_eth_down(struct net_device *);
static void dn_send_brd_hello(struct net_device *dev, struct dn_ifaddr *ifa);
static void dn_send_ptp_hello(struct net_device *dev, struct dn_ifaddr *ifa);
static struct dn_dev_parms dn_dev_list[] = {
{
.type = ARPHRD_ETHER, /* Ethernet */
.mode = DN_DEV_BCAST,
.state = DN_DEV_S_RU,
.t2 = 1,
.t3 = 10,
.name = "ethernet",
.ctl_name = NET_DECNET_CONF_ETHER,
.up = dn_eth_up,
.down = dn_eth_down,
.timer3 = dn_send_brd_hello,
},
{
.type = ARPHRD_IPGRE, /* DECnet tunneled over GRE in IP */
.mode = DN_DEV_BCAST,
.state = DN_DEV_S_RU,
.t2 = 1,
.t3 = 10,
.name = "ipgre",
.ctl_name = NET_DECNET_CONF_GRE,
.timer3 = dn_send_brd_hello,
},
#if 0
{
.type = ARPHRD_X25, /* Bog standard X.25 */
.mode = DN_DEV_UCAST,
.state = DN_DEV_S_DS,
.t2 = 1,
.t3 = 120,
.name = "x25",
.ctl_name = NET_DECNET_CONF_X25,
.timer3 = dn_send_ptp_hello,
},
#endif
#if 0
{
.type = ARPHRD_PPP, /* DECnet over PPP */
.mode = DN_DEV_BCAST,
.state = DN_DEV_S_RU,
.t2 = 1,
.t3 = 10,
.name = "ppp",
.ctl_name = NET_DECNET_CONF_PPP,
.timer3 = dn_send_brd_hello,
},
#endif
{
.type = ARPHRD_DDCMP, /* DECnet over DDCMP */
.mode = DN_DEV_UCAST,
.state = DN_DEV_S_DS,
.t2 = 1,
.t3 = 120,
.name = "ddcmp",
.ctl_name = NET_DECNET_CONF_DDCMP,
.timer3 = dn_send_ptp_hello,
},
{
.type = ARPHRD_LOOPBACK, /* Loopback interface - always last */
.mode = DN_DEV_BCAST,
.state = DN_DEV_S_RU,
.t2 = 1,
.t3 = 10,
.name = "loopback",
.ctl_name = NET_DECNET_CONF_LOOPBACK,
.timer3 = dn_send_brd_hello,
}
};
#define DN_DEV_LIST_SIZE (sizeof(dn_dev_list)/sizeof(struct dn_dev_parms))
#define DN_DEV_PARMS_OFFSET(x) ((int) ((char *) &((struct dn_dev_parms *)0)->x))
#ifdef CONFIG_SYSCTL
static int min_t2[] = { 1 };
static int max_t2[] = { 60 }; /* No max specified, but this seems sensible */
static int min_t3[] = { 1 };
static int max_t3[] = { 8191 }; /* Must fit in 16 bits when multiplied by BCT3MULT or T3MULT */
static int min_priority[1];
static int max_priority[] = { 127 }; /* From DECnet spec */
static int dn_forwarding_proc(ctl_table *, int, struct file *,
void __user *, size_t *, loff_t *);
static int dn_forwarding_sysctl(ctl_table *table, int __user *name, int nlen,
void __user *oldval, size_t __user *oldlenp,
void __user *newval, size_t newlen);
static struct dn_dev_sysctl_table {
struct ctl_table_header *sysctl_header;
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];
ctl_table dn_dev_root_dir[2];
} dn_dev_sysctl = {
NULL,
{
{
.ctl_name = NET_DECNET_CONF_DEV_FORWARDING,
.procname = "forwarding",
.data = (void *)DN_DEV_PARMS_OFFSET(forwarding),
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = dn_forwarding_proc,
.strategy = dn_forwarding_sysctl,
},
{
.ctl_name = NET_DECNET_CONF_DEV_PRIORITY,
.procname = "priority",
.data = (void *)DN_DEV_PARMS_OFFSET(priority),
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.strategy = sysctl_intvec,
.extra1 = &min_priority,
.extra2 = &max_priority
},
{
.ctl_name = NET_DECNET_CONF_DEV_T2,
.procname = "t2",
.data = (void *)DN_DEV_PARMS_OFFSET(t2),
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.strategy = sysctl_intvec,
.extra1 = &min_t2,
.extra2 = &max_t2
},
{
.ctl_name = NET_DECNET_CONF_DEV_T3,
.procname = "t3",
.data = (void *)DN_DEV_PARMS_OFFSET(t3),
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.strategy = sysctl_intvec,
.extra1 = &min_t3,
.extra2 = &max_t3
},
{0}
},
{{
.ctl_name = 0,
.procname = "",
.mode = 0555,
.child = dn_dev_sysctl.dn_dev_vars
}, {0}},
{{
.ctl_name = NET_DECNET_CONF,
.procname = "conf",
.mode = 0555,
.child = dn_dev_sysctl.dn_dev_dev
}, {0}},
{{
.ctl_name = NET_DECNET,
.procname = "decnet",
.mode = 0555,
.child = dn_dev_sysctl.dn_dev_conf_dir
}, {0}},
{{
.ctl_name = CTL_NET,
.procname = "net",
.mode = 0555,
.child = dn_dev_sysctl.dn_dev_proto_dir
}, {0}}
};
static void dn_dev_sysctl_register(struct net_device *dev, struct dn_dev_parms *parms)
{
struct dn_dev_sysctl_table *t;
int i;
t = kmemdup(&dn_dev_sysctl, sizeof(*t), GFP_KERNEL);
if (t == NULL)
return;
for(i = 0; i < ARRAY_SIZE(t->dn_dev_vars) - 1; i++) {
long offset = (long)t->dn_dev_vars[i].data;
t->dn_dev_vars[i].data = ((char *)parms) + offset;
t->dn_dev_vars[i].de = NULL;
}
if (dev) {
t->dn_dev_dev[0].procname = dev->name;
t->dn_dev_dev[0].ctl_name = dev->ifindex;
} else {
t->dn_dev_dev[0].procname = parms->name;
t->dn_dev_dev[0].ctl_name = parms->ctl_name;
}
t->dn_dev_dev[0].child = t->
|