The following commit has been merged in the linux branch: commit 4743c625ed5350308009f7dfa3eb60cd3e8bd143 Merge: 4b8c81acae918d7b3d2e3a969bf72e137e3b7bc3 f4ba126cfb6b1ee9d45dcdf8641cc08dd40d086c Author: Sven Eckelmann sven.eckelmann@gmx.de Date: Tue Jul 6 20:26:04 2010 +0200
Merge branch 'maint' into linux
Conflicts: drivers/staging/batman-adv/compat.h
diff --combined drivers/staging/batman-adv/aggregation.c index 3a5c349,9862d16..9862d16 --- a/drivers/staging/batman-adv/aggregation.c +++ b/drivers/staging/batman-adv/aggregation.c @@@ -106,11 -106,14 +106,14 @@@ static void new_aggregated_packet(unsig { struct forw_packet *forw_packet_aggr; unsigned long flags; + /* FIXME: each batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device);
/* own packet should always be scheduled */ if (!own_packet) { if (!atomic_dec_not_zero(&batman_queue_left)) { - bat_dbg(DBG_BATMAN, "batman packet queue full\n"); + bat_dbg(DBG_BATMAN, bat_priv, + "batman packet queue full\n"); return; } } diff --combined drivers/staging/batman-adv/bat_debugfs.c index fafca9f,0000000..8e8eef0 mode 100644,000000..100644 --- a/drivers/staging/batman-adv/bat_debugfs.c +++ b/drivers/staging/batman-adv/bat_debugfs.c @@@ -1,150 -1,0 +1,341 @@@ +/* + * Copyright (C) 2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + ++#include "main.h" ++ +#include <linux/debugfs.h> + - #include "main.h" +#include "bat_debugfs.h" +#include "translation-table.h" +#include "originator.h" +#include "hard-interface.h" +#include "vis.h" +#include "icmp_socket.h" + +static struct dentry *bat_debugfs; + ++#ifdef CONFIG_BATMAN_ADV_DEBUG ++#define LOG_BUFF_MASK (log_buff_len-1) ++#define LOG_BUFF(idx) (debug_log->log_buff[(idx) & LOG_BUFF_MASK]) ++ ++static int log_buff_len = LOG_BUF_LEN; ++ ++static void emit_log_char(struct debug_log *debug_log, char c) ++{ ++ LOG_BUFF(debug_log->log_end) = c; ++ debug_log->log_end++; ++ ++ if (debug_log->log_end - debug_log->log_start > log_buff_len) ++ debug_log->log_start = debug_log->log_end - log_buff_len; ++} ++ ++static int fdebug_log(struct debug_log *debug_log, char *fmt, ...) ++{ ++ int printed_len; ++ va_list args; ++ static char debug_log_buf[256]; ++ char *p; ++ unsigned long flags; ++ ++ if (!debug_log) ++ return 0; ++ ++ spin_lock_irqsave(&debug_log->lock, flags); ++ va_start(args, fmt); ++ printed_len = vscnprintf(debug_log_buf, sizeof(debug_log_buf), ++ fmt, args); ++ va_end(args); ++ ++ for (p = debug_log_buf; *p != 0; p++) ++ emit_log_char(debug_log, *p); ++ ++ spin_unlock_irqrestore(&debug_log->lock, flags); ++ ++ wake_up(&debug_log->queue_wait); ++ ++ return 0; ++} ++ ++int debug_log(struct bat_priv *bat_priv, char *fmt, ...) ++{ ++ va_list args; ++ char tmp_log_buf[256]; ++ ++ va_start(args, fmt); ++ vscnprintf(tmp_log_buf, sizeof(tmp_log_buf), fmt, args); ++ fdebug_log(bat_priv->debug_log, "[%10u] %s", ++ (jiffies / HZ), tmp_log_buf); ++ va_end(args); ++ ++ return 0; ++} ++ ++static int log_open(struct inode *inode, struct file *file) ++{ ++ file->private_data = inode->i_private; ++ inc_module_count(); ++ return 0; ++} ++ ++static int log_release(struct inode *inode, struct file *file) ++{ ++ dec_module_count(); ++ return 0; ++} ++ ++static ssize_t log_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct bat_priv *bat_priv = (struct bat_priv *)file->private_data; ++ struct debug_log *debug_log = bat_priv->debug_log; ++ int error, i = 0; ++ char c; ++ unsigned long flags; ++ ++ if ((file->f_flags & O_NONBLOCK) && ++ !(debug_log->log_end - debug_log->log_start)) ++ return -EAGAIN; ++ ++ if ((!buf) || (count < 0)) ++ return -EINVAL; ++ ++ if (count == 0) ++ return 0; ++ ++ if (!access_ok(VERIFY_WRITE, buf, count)) ++ return -EFAULT; ++ ++ error = wait_event_interruptible(debug_log->queue_wait, ++ (debug_log->log_start - debug_log->log_end)); ++ ++ if (error) ++ return error; ++ ++ spin_lock_irqsave(&debug_log->lock, flags); ++ ++ while ((!error) && (i < count) && ++ (debug_log->log_start != debug_log->log_end)) { ++ c = LOG_BUFF(debug_log->log_start); ++ ++ debug_log->log_start++; ++ ++ spin_unlock_irqrestore(&debug_log->lock, flags); ++ ++ error = __put_user(c, buf); ++ ++ spin_lock_irqsave(&debug_log->lock, flags); ++ ++ buf++; ++ i++; ++ ++ } ++ ++ spin_unlock_irqrestore(&debug_log->lock, flags); ++ ++ if (!error) ++ return i; ++ ++ return error; ++} ++ ++static unsigned int log_poll(struct file *file, poll_table *wait) ++{ ++ struct bat_priv *bat_priv = (struct bat_priv *)file->private_data; ++ struct debug_log *debug_log = bat_priv->debug_log; ++ ++ poll_wait(file, &debug_log->queue_wait, wait); ++ ++ if (debug_log->log_end - debug_log->log_start) ++ return POLLIN | POLLRDNORM; ++ ++ return 0; ++} ++ ++static const struct file_operations log_fops = { ++ .open = log_open, ++ .release = log_release, ++ .read = log_read, ++ .poll = log_poll, ++}; ++ ++static int debug_log_setup(struct bat_priv *bat_priv) ++{ ++ struct dentry *d; ++ ++ if (!bat_priv->debug_dir) ++ goto err; ++ ++ bat_priv->debug_log = kzalloc(sizeof(struct debug_log), GFP_ATOMIC); ++ if (!bat_priv->debug_log) ++ goto err; ++ ++ spin_lock_init(&bat_priv->debug_log->lock); ++ init_waitqueue_head(&bat_priv->debug_log->queue_wait); ++ ++ d = debugfs_create_file("log", S_IFREG | S_IRUSR, ++ bat_priv->debug_dir, bat_priv, &log_fops); ++ if (d) ++ goto err; ++ ++ return 0; ++ ++err: ++ return 1; ++} ++ ++static void debug_log_cleanup(struct bat_priv *bat_priv) ++{ ++ kfree(bat_priv->debug_log); ++ bat_priv->debug_log = NULL; ++} ++#else /* CONFIG_BATMAN_ADV_DEBUG */ ++static int debug_log_setup(struct bat_priv *bat_priv) ++{ ++ bat_priv->debug_log = NULL; ++ return 0; ++} ++ ++static void debug_log_cleanup(struct bat_priv *bat_priv) ++{ ++ return; ++} ++#endif ++ +static int originators_open(struct inode *inode, struct file *file) +{ + struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, orig_seq_print_text, net_dev); +} + +static int transtable_global_open(struct inode *inode, struct file *file) +{ + struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, hna_global_seq_print_text, net_dev); +} + +static int transtable_local_open(struct inode *inode, struct file *file) +{ + struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, hna_local_seq_print_text, net_dev); +} + +static int vis_data_open(struct inode *inode, struct file *file) +{ + struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, vis_seq_print_text, net_dev); +} + +struct bat_debuginfo { + struct attribute attr; + const struct file_operations fops; +}; + +#define BAT_DEBUGINFO(_name, _mode, _open) \ +struct bat_debuginfo bat_debuginfo_##_name = { \ + .attr = { .name = __stringify(_name), \ + .mode = _mode, }, \ + .fops = { .owner = THIS_MODULE, \ + .open = _open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ + } \ +}; + +static BAT_DEBUGINFO(originators, S_IRUGO, originators_open); +static BAT_DEBUGINFO(transtable_global, S_IRUGO, transtable_global_open); +static BAT_DEBUGINFO(transtable_local, S_IRUGO, transtable_local_open); +static BAT_DEBUGINFO(vis_data, S_IRUGO, vis_data_open); + +static struct bat_debuginfo *mesh_debuginfos[] = { + &bat_debuginfo_originators, + &bat_debuginfo_transtable_global, + &bat_debuginfo_transtable_local, + &bat_debuginfo_vis_data, + NULL, +}; + +void debugfs_init(void) +{ + bat_debugfs = debugfs_create_dir(DEBUGFS_BAT_SUBDIR, NULL); + if (bat_debugfs == ERR_PTR(-ENODEV)) + bat_debugfs = NULL; +} + +void debugfs_destroy(void) +{ + if (bat_debugfs) { + debugfs_remove_recursive(bat_debugfs); + bat_debugfs = NULL; + } +} + +int debugfs_add_meshif(struct net_device *dev) +{ + struct bat_priv *bat_priv = netdev_priv(dev); + struct bat_debuginfo **bat_debug; + struct dentry *file; + + if (!bat_debugfs) + goto out; + + bat_priv->debug_dir = debugfs_create_dir(dev->name, bat_debugfs); + if (!bat_priv->debug_dir) + goto out; + + bat_socket_setup(bat_priv); ++ debug_log_setup(bat_priv); + + for (bat_debug = mesh_debuginfos; *bat_debug; ++bat_debug) { + file = debugfs_create_file(((*bat_debug)->attr).name, + S_IFREG | ((*bat_debug)->attr).mode, + bat_priv->debug_dir, + dev, &(*bat_debug)->fops); + if (!file) { - printk(KERN_ERR "batman-adv:Can't add debugfs file: " - "%s/%s\n", dev->name, ((*bat_debug)->attr).name); ++ bat_err(dev, "Can't add debugfs file: %s/%s\n", ++ dev->name, ((*bat_debug)->attr).name); + goto rem_attr; + } + } + + return 0; +rem_attr: + debugfs_remove_recursive(bat_priv->debug_dir); + bat_priv->debug_dir = NULL; +out: +#ifdef CONFIG_DEBUG_FS + return -ENOMEM; +#else + return 0; +#endif /* CONFIG_DEBUG_FS */ +} + +void debugfs_del_meshif(struct net_device *dev) +{ + struct bat_priv *bat_priv = netdev_priv(dev); + ++ debug_log_cleanup(bat_priv); ++ + if (bat_debugfs) { + debugfs_remove_recursive(bat_priv->debug_dir); + bat_priv->debug_dir = NULL; + } +} diff --combined drivers/staging/batman-adv/bat_sysfs.c index 4e9c71d,0000000..05ca15a mode 100644,000000..100644 --- a/drivers/staging/batman-adv/bat_sysfs.c +++ b/drivers/staging/batman-adv/bat_sysfs.c @@@ -1,433 -1,0 +1,488 @@@ +/* + * Copyright (C) 2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "bat_sysfs.h" +#include "translation-table.h" +#include "originator.h" +#include "hard-interface.h" +#include "vis.h" + +#define to_dev(obj) container_of(obj, struct device, kobj) + +#define BAT_ATTR(_name, _mode, _show, _store) \ +struct bat_attribute bat_attr_##_name = { \ + .attr = {.name = __stringify(_name), \ + .mode = _mode }, \ + .show = _show, \ + .store = _store, \ +}; + +static ssize_t show_aggr_ogms(struct kobject *kobj, struct attribute *attr, + char *buff) +{ + struct device *dev = to_dev(kobj->parent); + struct bat_priv *bat_priv = netdev_priv(to_net_dev(dev)); + int aggr_status = atomic_read(&bat_priv->aggregation_enabled); + + return sprintf(buff, "%s\n", + aggr_status == 0 ? "disabled" : "enabled"); +} + +static ssize_t store_aggr_ogms(struct kobject *kobj, struct attribute *attr, + char *buff, size_t count) +{ + struct device *dev = to_dev(kobj->parent); + struct net_device *net_dev = to_net_dev(dev); + struct bat_priv *bat_priv = netdev_priv(net_dev); + int aggr_tmp = -1; + + if (((count == 2) && (buff[0] == '1')) || + (strncmp(buff, "enable", 6) == 0)) + aggr_tmp = 1; + + if (((count == 2) && (buff[0] == '0')) || + (strncmp(buff, "disable", 7) == 0)) + aggr_tmp = 0; + + if (aggr_tmp < 0) { + if (buff[count - 1] == '\n') + buff[count - 1] = '\0'; + - printk(KERN_INFO "batman-adv:Invalid parameter for 'aggregate OGM' setting on mesh %s received: %s\n", - net_dev->name, buff); ++ bat_info(net_dev, ++ "Invalid parameter for 'aggregate OGM' setting" ++ "received: %s\n", buff); + return -EINVAL; + } + + if (atomic_read(&bat_priv->aggregation_enabled) == aggr_tmp) + return count; + - printk(KERN_INFO "batman-adv:Changing aggregation from: %s to: %s on mesh: %s\n", - atomic_read(&bat_priv->aggregation_enabled) == 1 ? - "enabled" : "disabled", aggr_tmp == 1 ? "enabled" : "disabled", - net_dev->name); ++ bat_info(net_dev, "Changing aggregation from: %s to: %s\n", ++ atomic_read(&bat_priv->aggregation_enabled) == 1 ? ++ "enabled" : "disabled", aggr_tmp == 1 ? "enabled" : ++ "disabled"); + + atomic_set(&bat_priv->aggregation_enabled, (unsigned)aggr_tmp); + return count; +} + +static ssize_t show_bond(struct kobject *kobj, struct attribute *attr, + char *buff) +{ + struct device *dev = to_dev(kobj->parent); + struct bat_priv *bat_priv = netdev_priv(to_net_dev(dev)); + int bond_status = atomic_read(&bat_priv->bonding_enabled); + + return sprintf(buff, "%s\n", + bond_status == 0 ? "disabled" : "enabled"); +} + +static ssize_t store_bond(struct kobject *kobj, struct attribute *attr, + char *buff, size_t count) +{ + struct device *dev = to_dev(kobj->parent); + struct net_device *net_dev = to_net_dev(dev); + struct bat_priv *bat_priv = netdev_priv(net_dev); + int bonding_enabled_tmp = -1; + + if (((count == 2) && (buff[0] == '1')) || + (strncmp(buff, "enable", 6) == 0)) + bonding_enabled_tmp = 1; + + if (((count == 2) && (buff[0] == '0')) || + (strncmp(buff, "disable", 7) == 0)) + bonding_enabled_tmp = 0; + + if (bonding_enabled_tmp < 0) { + if (buff[count - 1] == '\n') + buff[count - 1] = '\0'; + - printk(KERN_ERR "batman-adv:Invalid parameter for 'bonding' setting on mesh %s received: %s\n", - net_dev->name, buff); ++ bat_err(net_dev, ++ "Invalid parameter for 'bonding' setting received: " ++ "%s\n", buff); + return -EINVAL; + } + + if (atomic_read(&bat_priv->bonding_enabled) == bonding_enabled_tmp) + return count; + - printk(KERN_INFO "batman-adv:Changing bonding from: %s to: %s on mesh: %s\n", - atomic_read(&bat_priv->bonding_enabled) == 1 ? - "enabled" : "disabled", - bonding_enabled_tmp == 1 ? "enabled" : "disabled", - net_dev->name); ++ bat_info(net_dev, "Changing bonding from: %s to: %s\n", ++ atomic_read(&bat_priv->bonding_enabled) == 1 ? ++ "enabled" : "disabled", ++ bonding_enabled_tmp == 1 ? "enabled" : "disabled"); + + atomic_set(&bat_priv->bonding_enabled, (unsigned)bonding_enabled_tmp); + return count; +} + +static ssize_t show_vis_mode(struct kobject *kobj, struct attribute *attr, + char *buff) +{ + struct device *dev = to_dev(kobj->parent); + struct bat_priv *bat_priv = netdev_priv(to_net_dev(dev)); + int vis_mode = atomic_read(&bat_priv->vis_mode); + + return sprintf(buff, "%s\n", + vis_mode == VIS_TYPE_CLIENT_UPDATE ? + "client" : "server"); +} + +static ssize_t store_vis_mode(struct kobject *kobj, struct attribute *attr, + char *buff, size_t count) +{ + struct device *dev = to_dev(kobj->parent); + struct net_device *net_dev = to_net_dev(dev); + struct bat_priv *bat_priv = netdev_priv(net_dev); + unsigned long val; + int ret, vis_mode_tmp = -1; + + ret = strict_strtoul(buff, 10, &val); + + if (((count == 2) && (!ret) && (val == VIS_TYPE_CLIENT_UPDATE)) || + (strncmp(buff, "client", 6) == 0) || + (strncmp(buff, "off", 3) == 0)) + vis_mode_tmp = VIS_TYPE_CLIENT_UPDATE; + + if (((count == 2) && (!ret) && (val == VIS_TYPE_SERVER_SYNC)) || + (strncmp(buff, "server", 6) == 0)) + vis_mode_tmp = VIS_TYPE_SERVER_SYNC; + + if (vis_mode_tmp < 0) { + if (buff[count - 1] == '\n') + buff[count - 1] = '\0'; + - printk(KERN_INFO "batman-adv:Invalid parameter for 'vis mode' setting on mesh %s received: %s\n", - net_dev->name, buff); ++ bat_info(net_dev, ++ "Invalid parameter for 'vis mode' setting received: " ++ "%s\n", buff); + return -EINVAL; + } + + if (atomic_read(&bat_priv->vis_mode) == vis_mode_tmp) + return count; + - printk(KERN_INFO "batman-adv:Changing vis mode from: %s to: %s on mesh: %s\n", - atomic_read(&bat_priv->vis_mode) == VIS_TYPE_CLIENT_UPDATE ? - "client" : "server", vis_mode_tmp == VIS_TYPE_CLIENT_UPDATE ? - "client" : "server", net_dev->name); ++ bat_info(net_dev, "Changing vis mode from: %s to: %s\n", ++ atomic_read(&bat_priv->vis_mode) == VIS_TYPE_CLIENT_UPDATE ? ++ "client" : "server", vis_mode_tmp == VIS_TYPE_CLIENT_UPDATE ? ++ "client" : "server"); + + atomic_set(&bat_priv->vis_mode, (unsigned)vis_mode_tmp); + return count; +} + +static ssize_t show_orig_interval(struct kobject *kobj, struct attribute *attr, + char *buff) +{ + struct device *dev = to_dev(kobj->parent); + struct bat_priv *bat_priv = netdev_priv(to_net_dev(dev)); + + return sprintf(buff, "%i\n", + atomic_read(&bat_priv->orig_interval)); +} + +static ssize_t store_orig_interval(struct kobject *kobj, struct attribute *attr, + char *buff, size_t count) +{ + struct device *dev = to_dev(kobj->parent); + struct net_device *net_dev = to_net_dev(dev); + struct bat_priv *bat_priv = netdev_priv(net_dev); + unsigned long orig_interval_tmp; + int ret; + + ret = strict_strtoul(buff, 10, &orig_interval_tmp); + if (ret) { - printk(KERN_INFO "batman-adv:Invalid parameter for 'orig_interval' setting on mesh %s received: %s\n", - net_dev->name, buff); ++ bat_info(net_dev, "Invalid parameter for 'orig_interval' " ++ "setting received: %s\n", buff); + return -EINVAL; + } + + if (orig_interval_tmp < JITTER * 2) { - printk(KERN_INFO "batman-adv:New originator interval too small: %li (min: %i)\n", - orig_interval_tmp, JITTER * 2); ++ bat_info(net_dev, "New originator interval too small: %li " ++ "(min: %i)\n", orig_interval_tmp, JITTER * 2); + return -EINVAL; + } + + if (atomic_read(&bat_priv->orig_interval) == orig_interval_tmp) + return count; + - printk(KERN_INFO "batman-adv:Changing originator interval from: %i to: %li on mesh: %s\n", - atomic_read(&bat_priv->orig_interval), - orig_interval_tmp, net_dev->name); ++ bat_info(net_dev, "Changing originator interval from: %i to: %li\n", ++ atomic_read(&bat_priv->orig_interval), ++ orig_interval_tmp); + + atomic_set(&bat_priv->orig_interval, orig_interval_tmp); + return count; +} + ++#ifdef CONFIG_BATMAN_ADV_DEBUG ++static ssize_t show_log_level(struct kobject *kobj, struct attribute *attr, ++ char *buff) ++{ ++ struct device *dev = to_dev(kobj->parent); ++ struct bat_priv *bat_priv = netdev_priv(to_net_dev(dev)); ++ int log_level = atomic_read(&bat_priv->log_level); ++ ++ return sprintf(buff, "%d\n", log_level); ++} ++ ++static ssize_t store_log_level(struct kobject *kobj, struct attribute *attr, ++ char *buff, size_t count) ++{ ++ struct device *dev = to_dev(kobj->parent); ++ struct net_device *net_dev = to_net_dev(dev); ++ struct bat_priv *bat_priv = netdev_priv(net_dev); ++ unsigned long log_level_tmp; ++ int ret; ++ ++ ret = strict_strtoul(buff, 10, &log_level_tmp); ++ if (ret) { ++ bat_info(net_dev, "Invalid parameter for 'log_level' " ++ "setting received: %s\n", buff); ++ return -EINVAL; ++ } ++ ++ if (log_level_tmp > 3) { ++ bat_info(net_dev, "New log level too big: %li " ++ "(max: %i)\n", log_level_tmp, 3); ++ return -EINVAL; ++ } ++ ++ if (atomic_read(&bat_priv->log_level) == log_level_tmp) ++ return count; ++ ++ bat_info(net_dev, "Changing log level from: %i to: %li\n", ++ atomic_read(&bat_priv->log_level), ++ log_level_tmp); ++ ++ atomic_set(&bat_priv->log_level, (unsigned)log_level_tmp); ++ return count; ++} ++#endif ++ +static BAT_ATTR(aggregated_ogms, S_IRUGO | S_IWUSR, + show_aggr_ogms, store_aggr_ogms); +static BAT_ATTR(bonding, S_IRUGO | S_IWUSR, show_bond, store_bond); +static BAT_ATTR(vis_mode, S_IRUGO | S_IWUSR, show_vis_mode, store_vis_mode); +static BAT_ATTR(orig_interval, S_IRUGO | S_IWUSR, + show_orig_interval, store_orig_interval); ++#ifdef CONFIG_BATMAN_ADV_DEBUG ++static BAT_ATTR(log_level, S_IRUGO | S_IWUSR, show_log_level, store_log_level); ++#endif + +static struct bat_attribute *mesh_attrs[] = { + &bat_attr_aggregated_ogms, + &bat_attr_bonding, + &bat_attr_vis_mode, + &bat_attr_orig_interval, ++#ifdef CONFIG_BATMAN_ADV_DEBUG ++ &bat_attr_log_level, ++#endif + NULL, +}; + +int sysfs_add_meshif(struct net_device *dev) +{ + struct kobject *batif_kobject = &dev->dev.kobj; + struct bat_priv *bat_priv = netdev_priv(dev); + struct bat_attribute **bat_attr; + int err; + + /* FIXME: should be done in the general mesh setup + routine as soon as we have it */ + atomic_set(&bat_priv->aggregation_enabled, 1); + atomic_set(&bat_priv->bonding_enabled, 0); + atomic_set(&bat_priv->vis_mode, VIS_TYPE_CLIENT_UPDATE); + atomic_set(&bat_priv->orig_interval, 1000); ++ atomic_set(&bat_priv->log_level, 0); ++ + bat_priv->primary_if = NULL; + bat_priv->num_ifaces = 0; + + bat_priv->mesh_obj = kobject_create_and_add(SYSFS_IF_MESH_SUBDIR, + batif_kobject); + if (!bat_priv->mesh_obj) { - printk(KERN_ERR "batman-adv:Can't add sysfs directory: %s/%s\n", - dev->name, SYSFS_IF_MESH_SUBDIR); ++ bat_err(dev, "Can't add sysfs directory: %s/%s\n", dev->name, ++ SYSFS_IF_MESH_SUBDIR); + goto out; + } + + for (bat_attr = mesh_attrs; *bat_attr; ++bat_attr) { + err = sysfs_create_file(bat_priv->mesh_obj, + &((*bat_attr)->attr)); + if (err) { - printk(KERN_ERR "batman-adv:Can't add sysfs file: %s/%s/%s\n", - dev->name, SYSFS_IF_MESH_SUBDIR, - ((*bat_attr)->attr).name); ++ bat_err(dev, "Can't add sysfs file: %s/%s/%s\n", ++ dev->name, SYSFS_IF_MESH_SUBDIR, ++ ((*bat_attr)->attr).name); + goto rem_attr; + } + } + + return 0; + +rem_attr: + for (bat_attr = mesh_attrs; *bat_attr; ++bat_attr) + sysfs_remove_file(bat_priv->mesh_obj, &((*bat_attr)->attr)); + + kobject_put(bat_priv->mesh_obj); + bat_priv->mesh_obj = NULL; +out: + return -ENOMEM; +} + +void sysfs_del_meshif(struct net_device *dev) +{ + struct bat_priv *bat_priv = netdev_priv(dev); + struct bat_attribute **bat_attr; + + for (bat_attr = mesh_attrs; *bat_attr; ++bat_attr) + sysfs_remove_file(bat_priv->mesh_obj, &((*bat_attr)->attr)); + + kobject_put(bat_priv->mesh_obj); + bat_priv->mesh_obj = NULL; +} + +static ssize_t show_mesh_iface(struct kobject *kobj, struct attribute *attr, + char *buff) +{ + struct device *dev = to_dev(kobj->parent); + struct net_device *net_dev = to_net_dev(dev); + struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); + + if (!batman_if) + return 0; + + return sprintf(buff, "%s\n", + batman_if->if_status == IF_NOT_IN_USE ? + "none" : "bat0"); +} + +static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr, + char *buff, size_t count) +{ + struct device *dev = to_dev(kobj->parent); + struct net_device *net_dev = to_net_dev(dev); + struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); + int status_tmp = -1; + + if (!batman_if) + return count; + + if (strncmp(buff, "none", 4) == 0) + status_tmp = IF_NOT_IN_USE; + + if (strncmp(buff, "bat0", 4) == 0) + status_tmp = IF_I_WANT_YOU; + + if (status_tmp < 0) { + if (buff[count - 1] == '\n') + buff[count - 1] = '\0'; + - printk(KERN_ERR "batman-adv:Invalid parameter for 'mesh_iface' setting received: %s\n", - buff); ++ pr_err("Invalid parameter for 'mesh_iface' setting received: " ++ "%s\n", buff); + return -EINVAL; + } + + if ((batman_if->if_status == status_tmp) || + ((status_tmp == IF_I_WANT_YOU) && + (batman_if->if_status != IF_NOT_IN_USE))) + return count; + + if (status_tmp == IF_I_WANT_YOU) + status_tmp = hardif_enable_interface(batman_if); + else + hardif_disable_interface(batman_if); + + return (status_tmp < 0 ? status_tmp : count); +} + +static ssize_t show_iface_status(struct kobject *kobj, struct attribute *attr, + char *buff) +{ + struct device *dev = to_dev(kobj->parent); + struct net_device *net_dev = to_net_dev(dev); + struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); + + if (!batman_if) + return 0; + + switch (batman_if->if_status) { + case IF_TO_BE_REMOVED: + return sprintf(buff, "disabling\n"); + case IF_INACTIVE: + return sprintf(buff, "inactive\n"); + case IF_ACTIVE: + return sprintf(buff, "active\n"); + case IF_TO_BE_ACTIVATED: + return sprintf(buff, "enabling\n"); + case IF_NOT_IN_USE: + default: + return sprintf(buff, "not in use\n"); + } +} + +static BAT_ATTR(mesh_iface, S_IRUGO | S_IWUSR, + show_mesh_iface, store_mesh_iface); +static BAT_ATTR(iface_status, S_IRUGO, show_iface_status, NULL); + +static struct bat_attribute *batman_attrs[] = { + &bat_attr_mesh_iface, + &bat_attr_iface_status, + NULL, +}; + +int sysfs_add_hardif(struct kobject **hardif_obj, struct net_device *dev) +{ + struct kobject *hardif_kobject = &dev->dev.kobj; + struct bat_attribute **bat_attr; + int err; + + *hardif_obj = kobject_create_and_add(SYSFS_IF_BAT_SUBDIR, + hardif_kobject); + + if (!*hardif_obj) { - printk(KERN_ERR "batman-adv:Can't add sysfs directory: %s/%s\n", - dev->name, SYSFS_IF_BAT_SUBDIR); ++ bat_err(dev, "Can't add sysfs directory: %s/%s\n", dev->name, ++ SYSFS_IF_BAT_SUBDIR); + goto out; + } + + for (bat_attr = batman_attrs; *bat_attr; ++bat_attr) { + err = sysfs_create_file(*hardif_obj, &((*bat_attr)->attr)); + if (err) { - printk(KERN_ERR "batman-adv:Can't add sysfs file: %s/%s/%s\n", - dev->name, SYSFS_IF_BAT_SUBDIR, - ((*bat_attr)->attr).name); ++ bat_err(dev, "Can't add sysfs file: %s/%s/%s\n", ++ dev->name, SYSFS_IF_BAT_SUBDIR, ++ ((*bat_attr)->attr).name); + goto rem_attr; + } + } + + return 0; + +rem_attr: + for (bat_attr = batman_attrs; *bat_attr; ++bat_attr) + sysfs_remove_file(*hardif_obj, &((*bat_attr)->attr)); +out: + return -ENOMEM; +} + +void sysfs_del_hardif(struct kobject **hardif_obj) +{ + kobject_put(*hardif_obj); + *hardif_obj = NULL; +} diff --combined drivers/staging/batman-adv/bitarray.c index c10fe03,dd4193c..dd4193c --- a/drivers/staging/batman-adv/bitarray.c +++ b/drivers/staging/batman-adv/bitarray.c @@@ -128,6 -128,9 +128,9 @@@ static void bit_reset_window(TYPE_OF_WO char bit_get_packet(TYPE_OF_WORD *seq_bits, int32_t seq_num_diff, int8_t set_mark) { + /* FIXME: each orig_node->batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + /* sequence number is slightly older. We already got a sequence number * higher than this one, so we just mark it. */
@@@ -152,7 -155,7 +155,7 @@@
if ((seq_num_diff >= TQ_LOCAL_WINDOW_SIZE) || (seq_num_diff < EXPECTED_SEQNO_RANGE)) { - bat_dbg(DBG_BATMAN, + bat_dbg(DBG_BATMAN, bat_priv, "We missed a lot of packets (%i) !\n", seq_num_diff - 1); bit_reset_window(seq_bits); @@@ -169,7 -172,7 +172,7 @@@ if ((seq_num_diff <= -TQ_LOCAL_WINDOW_SIZE) || (seq_num_diff >= EXPECTED_SEQNO_RANGE)) {
- bat_dbg(DBG_BATMAN, + bat_dbg(DBG_BATMAN, bat_priv, "Other host probably restarted!\n");
bit_reset_window(seq_bits); diff --combined drivers/staging/batman-adv/hard-interface.c index ba7ddfc,0000000..5cc369c mode 100644,000000..100644 --- a/drivers/staging/batman-adv/hard-interface.c +++ b/drivers/staging/batman-adv/hard-interface.c @@@ -1,548 -1,0 +1,545 @@@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "hard-interface.h" +#include "soft-interface.h" +#include "send.h" +#include "translation-table.h" +#include "routing.h" +#include "bat_sysfs.h" +#include "originator.h" +#include "hash.h" + +#include <linux/if_arp.h> +#include <linux/netfilter_bridge.h> + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +struct batman_if *get_batman_if_by_netdev(struct net_device *net_dev) +{ + struct batman_if *batman_if; + + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) { + if (batman_if->net_dev == net_dev) + goto out; + } + + batman_if = NULL; + +out: + rcu_read_unlock(); + return batman_if; +} + +static int is_valid_iface(struct net_device *net_dev) +{ + if (net_dev->flags & IFF_LOOPBACK) + return 0; + + if (net_dev->type != ARPHRD_ETHER) + return 0; + + if (net_dev->addr_len != ETH_ALEN) + return 0; + + /* no batman over batman */ +#ifdef HAVE_NET_DEVICE_OPS + if (net_dev->netdev_ops->ndo_start_xmit == interface_tx) + return 0; +#else + if (net_dev->hard_start_xmit == interface_tx) + return 0; +#endif + + /* Device is being bridged */ + /* if (net_dev->priv_flags & IFF_BRIDGE_PORT) + return 0; */ + + return 1; +} + +static struct batman_if *get_active_batman_if(void) +{ + struct batman_if *batman_if; + + /* TODO: should check interfaces belonging to bat_priv */ + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) { + if (batman_if->if_status == IF_ACTIVE) + goto out; + } + + batman_if = NULL; + +out: + rcu_read_unlock(); + return batman_if; +} + +static void set_primary_if(struct bat_priv *bat_priv, + struct batman_if *batman_if) +{ + struct batman_packet *batman_packet; + + bat_priv->primary_if = batman_if; + + if (!bat_priv->primary_if) + return; + + set_main_if_addr(batman_if->net_dev->dev_addr); + + batman_packet = (struct batman_packet *)(batman_if->packet_buff); + batman_packet->flags = PRIMARIES_FIRST_HOP; + batman_packet->ttl = TTL; + + /*** + * hacky trick to make sure that we send the HNA information via + * our new primary interface + */ + atomic_set(&hna_local_changed, 1); +} + +static bool hardif_is_iface_up(struct batman_if *batman_if) +{ + if (batman_if->net_dev->flags & IFF_UP) + return true; + + return false; +} + +static void update_mac_addresses(struct batman_if *batman_if) +{ + addr_to_string(batman_if->addr_str, batman_if->net_dev->dev_addr); + + memcpy(((struct batman_packet *)(batman_if->packet_buff))->orig, + batman_if->net_dev->dev_addr, ETH_ALEN); + memcpy(((struct batman_packet *)(batman_if->packet_buff))->prev_sender, + batman_if->net_dev->dev_addr, ETH_ALEN); +} + +static void check_known_mac_addr(uint8_t *addr) +{ + struct batman_if *batman_if; + + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) { + if ((batman_if->if_status != IF_ACTIVE) && + (batman_if->if_status != IF_TO_BE_ACTIVATED)) + continue; + + if (!compare_orig(batman_if->net_dev->dev_addr, addr)) + continue; + - printk(KERN_WARNING "batman-adv:" - "The newly added mac address (%pM) already exists on: %s\n", - addr, batman_if->dev); - printk(KERN_WARNING "batman-adv:" - "It is strongly recommended to keep mac addresses unique" - "to avoid problems!\n"); ++ pr_warning("The newly added mac address (%pM) already exists " ++ "on: %s\n", addr, batman_if->dev); ++ pr_warning("It is strongly recommended to keep mac addresses " ++ "unique to avoid problems!\n"); + } + rcu_read_unlock(); +} + +int hardif_min_mtu(void) +{ + struct batman_if *batman_if; + /* allow big frames if all devices are capable to do so + * (have MTU > 1500 + BAT_HEADER_LEN) */ + int min_mtu = ETH_DATA_LEN; + + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) { + if ((batman_if->if_status == IF_ACTIVE) || + (batman_if->if_status == IF_TO_BE_ACTIVATED)) + min_mtu = MIN(batman_if->net_dev->mtu - BAT_HEADER_LEN, + min_mtu); + } + rcu_read_unlock(); + + return min_mtu; +} + +/* adjusts the MTU if a new interface with a smaller MTU appeared. */ +void update_min_mtu(void) +{ + int min_mtu; + + min_mtu = hardif_min_mtu(); + if (soft_device->mtu != min_mtu) + soft_device->mtu = min_mtu; +} + - static void hardif_activate_interface(struct bat_priv *bat_priv, ++static void hardif_activate_interface(struct net_device *net_dev, ++ struct bat_priv *bat_priv, + struct batman_if *batman_if) +{ + if (batman_if->if_status != IF_INACTIVE) + return; + + dev_hold(batman_if->net_dev); + + update_mac_addresses(batman_if); + batman_if->if_status = IF_TO_BE_ACTIVATED; + + /** + * the first active interface becomes our primary interface or + * the next active interface after the old primay interface was removed + */ + if (!bat_priv->primary_if) + set_primary_if(bat_priv, batman_if); + - printk(KERN_INFO "batman-adv:Interface activated: %s\n", - batman_if->dev); ++ bat_info(net_dev, "Interface activated: %s\n", batman_if->dev); + + if (atomic_read(&module_state) == MODULE_INACTIVE) + activate_module(); + + update_min_mtu(); + return; +} + - static void hardif_deactivate_interface(struct batman_if *batman_if) ++static void hardif_deactivate_interface(struct net_device *net_dev, ++ struct batman_if *batman_if) +{ + if ((batman_if->if_status != IF_ACTIVE) && + (batman_if->if_status != IF_TO_BE_ACTIVATED)) + return; + + dev_put(batman_if->net_dev); + + batman_if->if_status = IF_INACTIVE; + - printk(KERN_INFO "batman-adv:Interface deactivated: %s\n", - batman_if->dev); ++ bat_info(net_dev, "Interface deactivated: %s\n", batman_if->dev); + + update_min_mtu(); +} + +int hardif_enable_interface(struct batman_if *batman_if) +{ + /* FIXME: each batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + struct batman_packet *batman_packet; + + if (batman_if->if_status != IF_NOT_IN_USE) + goto out; + + batman_if->packet_len = BAT_PACKET_LEN; + batman_if->packet_buff = kmalloc(batman_if->packet_len, GFP_ATOMIC); + + if (!batman_if->packet_buff) { - printk(KERN_ERR "batman-adv:" - "Can't add interface packet (%s): out of memory\n", - batman_if->dev); ++ bat_err(soft_device, "Can't add interface packet (%s): " ++ "out of memory\n", batman_if->dev); + goto err; + } + + batman_packet = (struct batman_packet *)(batman_if->packet_buff); + batman_packet->packet_type = BAT_PACKET; + batman_packet->version = COMPAT_VERSION; + batman_packet->flags = 0; + batman_packet->ttl = 2; + batman_packet->tq = TQ_MAX_VALUE; + batman_packet->num_hna = 0; + + batman_if->if_num = bat_priv->num_ifaces; + bat_priv->num_ifaces++; + batman_if->if_status = IF_INACTIVE; + orig_hash_add_if(batman_if, bat_priv->num_ifaces); + + atomic_set(&batman_if->seqno, 1); - printk(KERN_INFO "batman-adv:Adding interface: %s\n", batman_if->dev); ++ bat_info(soft_device, "Adding interface: %s\n", batman_if->dev); + + if (hardif_is_iface_up(batman_if)) - hardif_activate_interface(bat_priv, batman_if); ++ hardif_activate_interface(soft_device, bat_priv, batman_if); + else - printk(KERN_ERR "batman-adv:" - "Not using interface %s " - "(retrying later): interface not active\n", - batman_if->dev); ++ bat_err(soft_device, "Not using interface %s " ++ "(retrying later): interface not active\n", ++ batman_if->dev); + + /* begin scheduling originator messages on that interface */ + schedule_own_packet(batman_if); + +out: + return 0; + +err: + return -ENOMEM; +} + +void hardif_disable_interface(struct batman_if *batman_if) +{ + /* FIXME: each batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + + if (batman_if->if_status == IF_ACTIVE) - hardif_deactivate_interface(batman_if); ++ hardif_deactivate_interface(soft_device, batman_if); + + if (batman_if->if_status != IF_INACTIVE) + return; + - printk(KERN_INFO "batman-adv:Removing interface: %s\n", batman_if->dev); ++ bat_info(soft_device, "Removing interface: %s\n", batman_if->dev); + bat_priv->num_ifaces--; + orig_hash_del_if(batman_if, bat_priv->num_ifaces); + + if (batman_if == bat_priv->primary_if) + set_primary_if(bat_priv, get_active_batman_if()); + + kfree(batman_if->packet_buff); + batman_if->packet_buff = NULL; + batman_if->if_status = IF_NOT_IN_USE; + + if ((atomic_read(&module_state) == MODULE_ACTIVE) && + (bat_priv->num_ifaces == 0)) + deactivate_module(); +} + +static struct batman_if *hardif_add_interface(struct net_device *net_dev) +{ + struct batman_if *batman_if; + int ret; + + ret = is_valid_iface(net_dev); + if (ret != 1) + goto out; + + batman_if = kmalloc(sizeof(struct batman_if), GFP_ATOMIC); + if (!batman_if) { - printk(KERN_ERR "batman-adv:" - "Can't add interface (%s): out of memory\n", ++ pr_err("Can't add interface (%s): out of memory\n", + net_dev->name); + goto out; + } + + batman_if->dev = kstrdup(net_dev->name, GFP_ATOMIC); + if (!batman_if->dev) + goto free_if; + + ret = sysfs_add_hardif(&batman_if->hardif_obj, net_dev); + if (ret) + goto free_dev; + + batman_if->if_num = -1; + batman_if->net_dev = net_dev; + batman_if->if_status = IF_NOT_IN_USE; + INIT_LIST_HEAD(&batman_if->list); + + check_known_mac_addr(batman_if->net_dev->dev_addr); + list_add_tail_rcu(&batman_if->list, &if_list); + return batman_if; + +free_dev: + kfree(batman_if->dev); +free_if: + kfree(batman_if); +out: + return NULL; +} + +static void hardif_free_interface(struct rcu_head *rcu) +{ + struct batman_if *batman_if = container_of(rcu, struct batman_if, rcu); + + /* delete all references to this batman_if */ + purge_orig(NULL); + purge_outstanding_packets(batman_if); + + kfree(batman_if->dev); + kfree(batman_if); +} + +static void hardif_remove_interface(struct batman_if *batman_if) +{ + /* first deactivate interface */ + if (batman_if->if_status != IF_NOT_IN_USE) + hardif_disable_interface(batman_if); + + if (batman_if->if_status != IF_NOT_IN_USE) + return; + + batman_if->if_status = IF_TO_BE_REMOVED; + list_del_rcu(&batman_if->list); + sysfs_del_hardif(&batman_if->hardif_obj); + call_rcu(&batman_if->rcu, hardif_free_interface); +} + +void hardif_remove_interfaces(void) +{ + struct batman_if *batman_if, *batman_if_tmp; + + list_for_each_entry_safe(batman_if, batman_if_tmp, &if_list, list) + hardif_remove_interface(batman_if); +} + +static int hard_if_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *net_dev = (struct net_device *)ptr; + struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); + /* FIXME: each batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + + if (!batman_if) + batman_if = hardif_add_interface(net_dev); + + if (!batman_if) + goto out; + + switch (event) { + case NETDEV_REGISTER: + break; + case NETDEV_UP: - hardif_activate_interface(bat_priv, batman_if); ++ hardif_activate_interface(soft_device, bat_priv, batman_if); + break; + case NETDEV_GOING_DOWN: + case NETDEV_DOWN: - hardif_deactivate_interface(batman_if); ++ hardif_deactivate_interface(soft_device, batman_if); + break; + case NETDEV_UNREGISTER: + hardif_remove_interface(batman_if); + break; + case NETDEV_CHANGENAME: + break; + case NETDEV_CHANGEADDR: + check_known_mac_addr(batman_if->net_dev->dev_addr); + update_mac_addresses(batman_if); + if (batman_if == bat_priv->primary_if) + set_primary_if(bat_priv, batman_if); + break; + default: + break; + }; + +out: + return NOTIFY_DONE; +} + +static int batman_skb_recv_finish(struct sk_buff *skb) +{ + return NF_ACCEPT; +} + +/* receive a packet with the batman ethertype coming on a hard + * interface */ +int batman_skb_recv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *ptype, struct net_device *orig_dev) +{ ++ /* FIXME: each orig_node->batman_if will be attached to a softif */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); + struct batman_packet *batman_packet; + struct batman_if *batman_if; + struct net_device_stats *stats; + int ret; + + skb = skb_share_check(skb, GFP_ATOMIC); + + /* skb was released by skb_share_check() */ + if (!skb) + goto err_out; + + if (atomic_read(&module_state) != MODULE_ACTIVE) + goto err_free; + + /* if netfilter/ebtables wants to block incoming batman + * packets then give them a chance to do so here */ + ret = NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, dev, NULL, + batman_skb_recv_finish); + if (ret != 1) + goto err_out; + + /* packet should hold at least type and version */ + if (unlikely(skb_headlen(skb) < 2)) + goto err_free; + + /* expect a valid ethernet header here. */ + if (unlikely(skb->mac_len != sizeof(struct ethhdr) + || !skb_mac_header(skb))) + goto err_free; + + batman_if = get_batman_if_by_netdev(skb->dev); + if (!batman_if) + goto err_free; + + /* discard frames on not active interfaces */ + if (batman_if->if_status != IF_ACTIVE) + goto err_free; + + stats = (struct net_device_stats *)dev_get_stats(skb->dev); + if (stats) { + stats->rx_packets++; + stats->rx_bytes += skb->len; + } + + batman_packet = (struct batman_packet *)skb->data; + + if (batman_packet->version != COMPAT_VERSION) { - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: incompatible batman version (%i)\n", + batman_packet->version); + goto err_free; + } + + /* all receive handlers return whether they received or reused + * the supplied skb. if not, we have to free the skb. */ + + switch (batman_packet->packet_type) { + /* batman originator packet */ + case BAT_PACKET: + ret = recv_bat_packet(skb, batman_if); + break; + + /* batman icmp packet */ + case BAT_ICMP: + ret = recv_icmp_packet(skb); + break; + + /* unicast packet */ + case BAT_UNICAST: + ret = recv_unicast_packet(skb, batman_if); + break; + + /* broadcast packet */ + case BAT_BCAST: + ret = recv_bcast_packet(skb); + break; + + /* vis packet */ + case BAT_VIS: + ret = recv_vis_packet(skb); + break; + default: + ret = NET_RX_DROP; + } + + if (ret == NET_RX_DROP) + kfree_skb(skb); + + /* return NET_RX_SUCCESS in any case as we + * most probably dropped the packet for + * routing-logical reasons. */ + + return NET_RX_SUCCESS; + +err_free: + kfree_skb(skb); +err_out: + return NET_RX_DROP; +} + +struct notifier_block hard_if_notifier = { + .notifier_call = hard_if_event, +}; diff --combined drivers/staging/batman-adv/icmp_socket.c index 08a5f7b,0000000..532f246 mode 100644,000000..100644 --- a/drivers/staging/batman-adv/icmp_socket.c +++ b/drivers/staging/batman-adv/icmp_socket.c @@@ -1,337 -1,0 +1,338 @@@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + ++#include "main.h" +#include <linux/debugfs.h> +#include <linux/slab.h> - #include "main.h" +#include "icmp_socket.h" +#include "send.h" +#include "types.h" +#include "hash.h" +#include "hard-interface.h" + + +static struct socket_client *socket_client_hash[256]; + +static void bat_socket_add_packet(struct socket_client *socket_client, + struct icmp_packet_rr *icmp_packet, + size_t icmp_len); + +void bat_socket_init(void) +{ + memset(socket_client_hash, 0, sizeof(socket_client_hash)); +} + +static int bat_socket_open(struct inode *inode, struct file *file) +{ + unsigned int i; + struct socket_client *socket_client; + + socket_client = kmalloc(sizeof(struct socket_client), GFP_KERNEL); + + if (!socket_client) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(socket_client_hash); i++) { + if (!socket_client_hash[i]) { + socket_client_hash[i] = socket_client; + break; + } + } + + if (i == ARRAY_SIZE(socket_client_hash)) { - printk(KERN_ERR "batman-adv:" - "Error - can't add another packet client: " ++ pr_err("Error - can't add another packet client: " + "maximum number of clients reached\n"); + kfree(socket_client); + return -EXFULL; + } + + INIT_LIST_HEAD(&socket_client->queue_list); + socket_client->queue_len = 0; + socket_client->index = i; + spin_lock_init(&socket_client->lock); + init_waitqueue_head(&socket_client->queue_wait); + + file->private_data = socket_client; + + inc_module_count(); + return 0; +} + +static int bat_socket_release(struct inode *inode, struct file *file) +{ + struct socket_client *socket_client = + (struct socket_client *)file->private_data; + struct socket_packet *socket_packet; + struct list_head *list_pos, *list_pos_tmp; + unsigned long flags; + + spin_lock_irqsave(&socket_client->lock, flags); + + /* for all packets in the queue ... */ + list_for_each_safe(list_pos, list_pos_tmp, &socket_client->queue_list) { + socket_packet = list_entry(list_pos, + struct socket_packet, list); + + list_del(list_pos); + kfree(socket_packet); + } + + socket_client_hash[socket_client->index] = NULL; + spin_unlock_irqrestore(&socket_client->lock, flags); + + kfree(socket_client); + dec_module_count(); + + return 0; +} + +static ssize_t bat_socket_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct socket_client *socket_client = + (struct socket_client *)file->private_data; + struct socket_packet *socket_packet; + size_t packet_len; + int error; + unsigned long flags; + + if ((file->f_flags & O_NONBLOCK) && (socket_client->queue_len == 0)) + return -EAGAIN; + + if ((!buf) || (count < sizeof(struct icmp_packet))) + return -EINVAL; + + if (!access_ok(VERIFY_WRITE, buf, count)) + return -EFAULT; + + error = wait_event_interruptible(socket_client->queue_wait, + socket_client->queue_len); + + if (error) + return error; + + spin_lock_irqsave(&socket_client->lock, flags); + + socket_packet = list_first_entry(&socket_client->queue_list, + struct socket_packet, list); + list_del(&socket_packet->list); + socket_client->queue_len--; + + spin_unlock_irqrestore(&socket_client->lock, flags); + + error = __copy_to_user(buf, &socket_packet->icmp_packet, + socket_packet->icmp_len); + + packet_len = socket_packet->icmp_len; + kfree(socket_packet); + + if (error) + return -EFAULT; + + return packet_len; +} + +static ssize_t bat_socket_write(struct file *file, const char __user *buff, + size_t len, loff_t *off) +{ ++ /* FIXME: each orig_node->batman_if will be attached to a softif */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); + struct socket_client *socket_client = + (struct socket_client *)file->private_data; + struct icmp_packet_rr icmp_packet; + struct orig_node *orig_node; + struct batman_if *batman_if; + size_t packet_len = sizeof(struct icmp_packet); + uint8_t dstaddr[ETH_ALEN]; + unsigned long flags; + + if (len < sizeof(struct icmp_packet)) { - bat_dbg(DBG_BATMAN, "batman-adv:" ++ bat_dbg(DBG_BATMAN, bat_priv, + "Error - can't send packet from char device: " + "invalid packet size\n"); + return -EINVAL; + } + + if (len >= sizeof(struct icmp_packet_rr)) + packet_len = sizeof(struct icmp_packet_rr); + + if (!access_ok(VERIFY_READ, buff, packet_len)) + return -EFAULT; + + if (__copy_from_user(&icmp_packet, buff, packet_len)) + return -EFAULT; + + if (icmp_packet.packet_type != BAT_ICMP) { - bat_dbg(DBG_BATMAN, "batman-adv:" ++ bat_dbg(DBG_BATMAN, bat_priv, + "Error - can't send packet from char device: " + "got bogus packet type (expected: BAT_ICMP)\n"); + return -EINVAL; + } + + if (icmp_packet.msg_type != ECHO_REQUEST) { - bat_dbg(DBG_BATMAN, "batman-adv:" ++ bat_dbg(DBG_BATMAN, bat_priv, + "Error - can't send packet from char device: " + "got bogus message type (expected: ECHO_REQUEST)\n"); + return -EINVAL; + } + + icmp_packet.uid = socket_client->index; + + if (icmp_packet.version != COMPAT_VERSION) { + icmp_packet.msg_type = PARAMETER_PROBLEM; + icmp_packet.ttl = COMPAT_VERSION; + bat_socket_add_packet(socket_client, &icmp_packet, packet_len); + goto out; + } + + if (atomic_read(&module_state) != MODULE_ACTIVE) + goto dst_unreach; + + spin_lock_irqsave(&orig_hash_lock, flags); + orig_node = ((struct orig_node *)hash_find(orig_hash, icmp_packet.dst)); + + if (!orig_node) + goto unlock; + + if (!orig_node->router) + goto unlock; + + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + + spin_unlock_irqrestore(&orig_hash_lock, flags); + + if (!batman_if) + goto dst_unreach; + + if (batman_if->if_status != IF_ACTIVE) + goto dst_unreach; + + memcpy(icmp_packet.orig, batman_if->net_dev->dev_addr, ETH_ALEN); + + if (packet_len == sizeof(struct icmp_packet_rr)) + memcpy(icmp_packet.rr, batman_if->net_dev->dev_addr, ETH_ALEN); + + send_raw_packet((unsigned char *)&icmp_packet, + packet_len, batman_if, dstaddr); + + goto out; + +unlock: + spin_unlock_irqrestore(&orig_hash_lock, flags); +dst_unreach: + icmp_packet.msg_type = DESTINATION_UNREACHABLE; + bat_socket_add_packet(socket_client, &icmp_packet, packet_len); +out: + return len; +} + +static unsigned int bat_socket_poll(struct file *file, poll_table *wait) +{ + struct socket_client *socket_client = + (struct socket_client *)file->private_data; + + poll_wait(file, &socket_client->queue_wait, wait); + + if (socket_client->queue_len > 0) + return POLLIN | POLLRDNORM; + + return 0; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = bat_socket_open, + .release = bat_socket_release, + .read = bat_socket_read, + .write = bat_socket_write, + .poll = bat_socket_poll, +}; + +int bat_socket_setup(struct bat_priv *bat_priv) +{ + struct dentry *d; + + if (!bat_priv->debug_dir) + goto err; + + d = debugfs_create_file(ICMP_SOCKET, S_IFREG | S_IWUSR | S_IRUSR, + bat_priv->debug_dir, NULL, &fops); + if (d) + goto err; + + return 0; + +err: + return 1; +} + +static void bat_socket_add_packet(struct socket_client *socket_client, + struct icmp_packet_rr *icmp_packet, + size_t icmp_len) +{ + struct socket_packet *socket_packet; + unsigned long flags; + + socket_packet = kmalloc(sizeof(struct socket_packet), GFP_ATOMIC); + + if (!socket_packet) + return; + + INIT_LIST_HEAD(&socket_packet->list); + memcpy(&socket_packet->icmp_packet, icmp_packet, icmp_len); + socket_packet->icmp_len = icmp_len; + + spin_lock_irqsave(&socket_client->lock, flags); + + /* while waiting for the lock the socket_client could have been + * deleted */ + if (!socket_client_hash[icmp_packet->uid]) { + spin_unlock_irqrestore(&socket_client->lock, flags); + kfree(socket_packet); + return; + } + + list_add_tail(&socket_packet->list, &socket_client->queue_list); + socket_client->queue_len++; + + if (socket_client->queue_len > 100) { + socket_packet = list_first_entry(&socket_client->queue_list, + struct socket_packet, list); + + list_del(&socket_packet->list); + kfree(socket_packet); + socket_client->queue_len--; + } + + spin_unlock_irqrestore(&socket_client->lock, flags); + + wake_up(&socket_client->queue_wait); +} + +void bat_socket_receive_packet(struct icmp_packet_rr *icmp_packet, + size_t icmp_len) +{ + struct socket_client *hash = socket_client_hash[icmp_packet->uid]; + + if (hash) + bat_socket_add_packet(hash, icmp_packet, icmp_len); +} diff --combined drivers/staging/batman-adv/main.c index d7e2960,0000000..a4d238b mode 100644,000000..100644 --- a/drivers/staging/batman-adv/main.c +++ b/drivers/staging/batman-adv/main.c @@@ -1,299 -1,0 +1,285 @@@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "bat_sysfs.h" +#include "bat_debugfs.h" +#include "routing.h" +#include "send.h" +#include "originator.h" +#include "soft-interface.h" +#include "icmp_socket.h" +#include "translation-table.h" +#include "hard-interface.h" +#include "types.h" +#include "vis.h" +#include "hash.h" + +struct list_head if_list; +struct hlist_head forw_bat_list; +struct hlist_head forw_bcast_list; +struct hashtable_t *orig_hash; + +DEFINE_SPINLOCK(orig_hash_lock); +DEFINE_SPINLOCK(forw_bat_list_lock); +DEFINE_SPINLOCK(forw_bcast_list_lock); + +atomic_t bcast_queue_left; +atomic_t batman_queue_left; + +int16_t num_hna; + +struct net_device *soft_device; + +unsigned char broadcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +atomic_t module_state; + +static struct packet_type batman_adv_packet_type __read_mostly = { + .type = __constant_htons(ETH_P_BATMAN), + .func = batman_skb_recv, +}; + +struct workqueue_struct *bat_event_workqueue; + - #ifdef CONFIG_BATMAN_ADV_DEBUG - int debug; - - module_param(debug, int, 0644); - - int bat_debug_type(int type) - { - return debug & type; - } - #endif - +int init_module(void) +{ + int retval; + + INIT_LIST_HEAD(&if_list); + INIT_HLIST_HEAD(&forw_bat_list); + INIT_HLIST_HEAD(&forw_bcast_list); + + atomic_set(&module_state, MODULE_INACTIVE); + + atomic_set(&bcast_queue_left, BCAST_QUEUE_LEN); + atomic_set(&batman_queue_left, BATMAN_QUEUE_LEN); + + /* the name should not be longer than 10 chars - see + * http://lwn.net/Articles/23634/ */ + bat_event_workqueue = create_singlethread_workqueue("bat_events"); + + if (!bat_event_workqueue) + return -ENOMEM; + + bat_socket_init(); + debugfs_init(); + + /* initialize layer 2 interface */ + soft_device = alloc_netdev(sizeof(struct bat_priv) , "bat%d", + interface_setup); + + if (!soft_device) { - printk(KERN_ERR "batman-adv:" - "Unable to allocate the batman interface\n"); ++ pr_err("Unable to allocate the batman interface\n"); + goto end; + } + + retval = register_netdev(soft_device); + + if (retval < 0) { - printk(KERN_ERR "batman-adv:" - "Unable to register the batman interface: %i\n", retval); ++ pr_err("Unable to register the batman interface: %i\n", retval); + goto free_soft_device; + } + + retval = sysfs_add_meshif(soft_device); + + if (retval < 0) + goto unreg_soft_device; + + retval = debugfs_add_meshif(soft_device); + + if (retval < 0) + goto unreg_sysfs; + + register_netdevice_notifier(&hard_if_notifier); + dev_add_pack(&batman_adv_packet_type); + - printk(KERN_INFO "batman-adv:" - "B.A.T.M.A.N. advanced %s%s (compatibility version %i) loaded\n", - SOURCE_VERSION, REVISION_VERSION_STR, COMPAT_VERSION); ++ pr_info("B.A.T.M.A.N. advanced %s%s (compatibility version %i) " ++ "loaded\n", SOURCE_VERSION, REVISION_VERSION_STR, ++ COMPAT_VERSION); + + return 0; + +unreg_sysfs: + sysfs_del_meshif(soft_device); +unreg_soft_device: + unregister_netdev(soft_device); + soft_device = NULL; + return -ENOMEM; + +free_soft_device: + free_netdev(soft_device); + soft_device = NULL; +end: + return -ENOMEM; +} + +void cleanup_module(void) +{ + deactivate_module(); + + debugfs_destroy(); + unregister_netdevice_notifier(&hard_if_notifier); + hardif_remove_interfaces(); + + if (soft_device) { + debugfs_del_meshif(soft_device); + sysfs_del_meshif(soft_device); + unregister_netdev(soft_device); + soft_device = NULL; + } + + dev_remove_pack(&batman_adv_packet_type); + + destroy_workqueue(bat_event_workqueue); + bat_event_workqueue = NULL; +} + +/* activates the module, starts timer ... */ +void activate_module(void) +{ + if (originator_init() < 1) + goto err; + + if (hna_local_init() < 1) + goto err; + + if (hna_global_init() < 1) + goto err; + + hna_local_add(soft_device->dev_addr); + + if (vis_init() < 1) + goto err; + + update_min_mtu(); + atomic_set(&module_state, MODULE_ACTIVE); + goto end; + +err: - printk(KERN_ERR "batman-adv:" - "Unable to allocate memory for mesh information structures: " ++ pr_err("Unable to allocate memory for mesh information structures: " + "out of mem ?\n"); + deactivate_module(); +end: + return; +} + +/* shuts down the whole module.*/ +void deactivate_module(void) +{ + atomic_set(&module_state, MODULE_DEACTIVATING); + + purge_outstanding_packets(NULL); + flush_workqueue(bat_event_workqueue); + + vis_quit(); + + /* TODO: unregister BATMAN pack */ + + originator_free(); + + hna_local_free(); + hna_global_free(); + + synchronize_net(); + + synchronize_rcu(); + atomic_set(&module_state, MODULE_INACTIVE); +} + +void inc_module_count(void) +{ + try_module_get(THIS_MODULE); +} + +void dec_module_count(void) +{ + module_put(THIS_MODULE); +} + +int addr_to_string(char *buff, uint8_t *addr) +{ + return sprintf(buff, "%pM", addr); +} + +/* returns 1 if they are the same originator */ + +int compare_orig(void *data1, void *data2) +{ + return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); +} + +/* hashfunction to choose an entry in a hash table of given size */ +/* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ +int choose_orig(void *data, int32_t size) +{ + unsigned char *key = data; + uint32_t hash = 0; + size_t i; + + for (i = 0; i < 6; i++) { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash % size; +} + +int is_my_mac(uint8_t *addr) +{ + struct batman_if *batman_if; + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) { + if ((batman_if->net_dev) && + (compare_orig(batman_if->net_dev->dev_addr, addr))) { + rcu_read_unlock(); + return 1; + } + } + rcu_read_unlock(); + return 0; + +} + +int is_bcast(uint8_t *addr) +{ + return (addr[0] == (uint8_t)0xff) && (addr[1] == (uint8_t)0xff); +} + +int is_mcast(uint8_t *addr) +{ + return *addr & 0x01; +} + +MODULE_LICENSE("GPL"); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_SUPPORTED_DEVICE(DRIVER_DEVICE); +#ifdef REVISION_VERSION +MODULE_VERSION(SOURCE_VERSION "-" REVISION_VERSION); +#else +MODULE_VERSION(SOURCE_VERSION); +#endif diff --combined drivers/staging/batman-adv/main.h index e308673,8513261..8513261 --- a/drivers/staging/batman-adv/main.h +++ b/drivers/staging/batman-adv/main.h @@@ -86,25 -86,16 +86,16 @@@ /* * Debug Messages */ + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt /* Append 'batman-adv: ' before + * kernel messages */
#define DBG_BATMAN 1 /* all messages related to routing / flooding / * broadcasting / etc */ #define DBG_ROUTES 2 /* route or hna added / changed / deleted */ + #define DBG_ALL 3
- #ifdef CONFIG_BATMAN_ADV_DEBUG - extern int debug; + #define LOG_BUF_LEN 8192 /* has to be a power of 2 */
- extern int bat_debug_type(int type); - #define bat_dbg(type, fmt, arg...) do { \ - if (bat_debug_type(type)) \ - printk(KERN_DEBUG "batman-adv:" fmt, ## arg); \ - } \ - while (0) - #else /* !CONFIG_BATMAN_ADV_DEBUG */ - #define bat_dbg(type, fmt, arg...) do { \ - } \ - while (0) - #endif
/* * Vis @@@ -166,4 -157,43 +157,43 @@@ int is_my_mac(uint8_t *addr) int is_bcast(uint8_t *addr); int is_mcast(uint8_t *addr);
+ #ifdef CONFIG_BATMAN_ADV_DEBUG + extern int debug_log(struct bat_priv *bat_priv, char *fmt, ...); + + #define bat_dbg(type, bat_priv, fmt, arg...) \ + do { \ + if (atomic_read(&bat_priv->log_level) & type) \ + debug_log(bat_priv, fmt, ## arg); \ + } \ + while (0) + #else /* !CONFIG_BATMAN_ADV_DEBUG */ + static inline void bat_dbg(char type __attribute__((unused)), + struct bat_priv *bat_priv __attribute__((unused)), + char *fmt __attribute__((unused)), ...) + { + } + #endif + + #define bat_warning(net_dev, fmt, arg...) \ + do { \ + struct net_device *_netdev = (net_dev); \ + struct bat_priv *_batpriv = netdev_priv(_netdev); \ + bat_dbg(DBG_ALL, _batpriv, fmt, ## arg); \ + pr_warning("%s: " fmt, _netdev->name, ## arg); \ + } while (0) + #define bat_info(net_dev, fmt, arg...) \ + do { \ + struct net_device *_netdev = (net_dev); \ + struct bat_priv *_batpriv = netdev_priv(_netdev); \ + bat_dbg(DBG_ALL, _batpriv, fmt, ## arg); \ + pr_info("%s: " fmt, _netdev->name, ## arg); \ + } while (0) + #define bat_err(net_dev, fmt, arg...) \ + do { \ + struct net_device *_netdev = (net_dev); \ + struct bat_priv *_batpriv = netdev_priv(_netdev); \ + bat_dbg(DBG_ALL, _batpriv, fmt, ## arg); \ + pr_err("%s: " fmt, _netdev->name, ## arg); \ + } while (0) + #endif /* _NET_BATMAN_ADV_MAIN_H_ */ diff --combined drivers/staging/batman-adv/originator.c index 26bbf2d,0000000..28bb627 mode 100644,000000..100644 --- a/drivers/staging/batman-adv/originator.c +++ b/drivers/staging/batman-adv/originator.c @@@ -1,499 -1,0 +1,509 @@@ +/* + * Copyright (C) 2009-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +/* increase the reference counter for this originator */ + +#include "main.h" +#include "originator.h" +#include "hash.h" +#include "translation-table.h" +#include "routing.h" +#include "hard-interface.h" + +static DECLARE_DELAYED_WORK(purge_orig_wq, purge_orig); + +static void start_purge_timer(void) +{ + queue_delayed_work(bat_event_workqueue, &purge_orig_wq, 1 * HZ); +} + +int originator_init(void) +{ + unsigned long flags; + if (orig_hash) + return 1; + + spin_lock_irqsave(&orig_hash_lock, flags); + orig_hash = hash_new(128, compare_orig, choose_orig); + + if (!orig_hash) + goto err; + + spin_unlock_irqrestore(&orig_hash_lock, flags); + start_purge_timer(); + return 1; + +err: + spin_unlock_irqrestore(&orig_hash_lock, flags); + return 0; +} + +struct neigh_node * +create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, + uint8_t *neigh, struct batman_if *if_incoming) +{ ++ /* FIXME: each orig_node->batman_if will be attached to a softif */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); + struct neigh_node *neigh_node; + - bat_dbg(DBG_BATMAN, "Creating new last-hop neighbor of originator\n"); ++ bat_dbg(DBG_BATMAN, bat_priv, ++ "Creating new last-hop neighbor of originator\n"); + + neigh_node = kzalloc(sizeof(struct neigh_node), GFP_ATOMIC); + if (!neigh_node) + return NULL; + + INIT_LIST_HEAD(&neigh_node->list); + + memcpy(neigh_node->addr, neigh, ETH_ALEN); + neigh_node->orig_node = orig_neigh_node; + neigh_node->if_incoming = if_incoming; + + list_add_tail(&neigh_node->list, &orig_node->neigh_list); + return neigh_node; +} + +static void free_orig_node(void *data) +{ + struct list_head *list_pos, *list_pos_tmp; + struct neigh_node *neigh_node; + struct orig_node *orig_node = (struct orig_node *)data; + + /* for all neighbors towards this originator ... */ + list_for_each_safe(list_pos, list_pos_tmp, &orig_node->neigh_list) { + neigh_node = list_entry(list_pos, struct neigh_node, list); + + list_del(list_pos); + kfree(neigh_node); + } + + hna_global_del_orig(orig_node, "originator timed out"); + + kfree(orig_node->bcast_own); + kfree(orig_node->bcast_own_sum); + kfree(orig_node); +} + +void originator_free(void) +{ + unsigned long flags; + + if (!orig_hash) + return; + + cancel_delayed_work_sync(&purge_orig_wq); + + spin_lock_irqsave(&orig_hash_lock, flags); + hash_delete(orig_hash, free_orig_node); + orig_hash = NULL; + spin_unlock_irqrestore(&orig_hash_lock, flags); +} + +/* this function finds or creates an originator entry for the given + * address if it does not exits */ +struct orig_node *get_orig_node(uint8_t *addr) +{ + /* FIXME: each batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + struct orig_node *orig_node; + struct hashtable_t *swaphash; + int size; + + orig_node = ((struct orig_node *)hash_find(orig_hash, addr)); + + if (orig_node != NULL) + return orig_node; + - bat_dbg(DBG_BATMAN, "Creating new originator: %pM\n", addr); ++ bat_dbg(DBG_BATMAN, bat_priv, ++ "Creating new originator: %pM\n", addr); + + orig_node = kzalloc(sizeof(struct orig_node), GFP_ATOMIC); + if (!orig_node) + return NULL; + + INIT_LIST_HEAD(&orig_node->neigh_list); + + memcpy(orig_node->orig, addr, ETH_ALEN); + orig_node->router = NULL; + orig_node->hna_buff = NULL; + orig_node->bcast_seqno_reset = jiffies - 1 + - msecs_to_jiffies(RESET_PROTECTION_MS); + orig_node->batman_seqno_reset = jiffies - 1 + - msecs_to_jiffies(RESET_PROTECTION_MS); + + size = bat_priv->num_ifaces * sizeof(TYPE_OF_WORD) * NUM_WORDS; + + orig_node->bcast_own = kzalloc(size, GFP_ATOMIC); + if (!orig_node->bcast_own) + goto free_orig_node; + + size = bat_priv->num_ifaces * sizeof(uint8_t); + orig_node->bcast_own_sum = kzalloc(size, GFP_ATOMIC); + if (!orig_node->bcast_own_sum) + goto free_bcast_own; + + if (hash_add(orig_hash, orig_node) < 0) + goto free_bcast_own_sum; + + if (orig_hash->elements * 4 > orig_hash->size) { + swaphash = hash_resize(orig_hash, orig_hash->size * 2); + + if (swaphash == NULL) - printk(KERN_ERR - "batman-adv:Couldn't resize orig hash table\n"); ++ bat_err(soft_device, ++ "Couldn't resize orig hash table\n"); + else + orig_hash = swaphash; + } + + return orig_node; +free_bcast_own_sum: + kfree(orig_node->bcast_own_sum); +free_bcast_own: + kfree(orig_node->bcast_own); +free_orig_node: + kfree(orig_node); + return NULL; +} + +static bool purge_orig_neighbors(struct orig_node *orig_node, + struct neigh_node **best_neigh_node) +{ ++ /* FIXME: each orig_node->batman_if will be attached to a softif */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); + struct list_head *list_pos, *list_pos_tmp; + struct neigh_node *neigh_node; + bool neigh_purged = false; + + *best_neigh_node = NULL; + + /* for all neighbors towards this originator ... */ + list_for_each_safe(list_pos, list_pos_tmp, &orig_node->neigh_list) { + neigh_node = list_entry(list_pos, struct neigh_node, list); + + if ((time_after(jiffies, + neigh_node->last_valid + PURGE_TIMEOUT * HZ)) || + (neigh_node->if_incoming->if_status == + IF_TO_BE_REMOVED)) { + + if (neigh_node->if_incoming->if_status == + IF_TO_BE_REMOVED) - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "neighbor purge: originator %pM, " + "neighbor: %pM, iface: %s\n", + orig_node->orig, neigh_node->addr, + neigh_node->if_incoming->dev); + else - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "neighbor timeout: originator %pM, " + "neighbor: %pM, last_valid: %lu\n", + orig_node->orig, neigh_node->addr, + (neigh_node->last_valid / HZ)); + + neigh_purged = true; + list_del(list_pos); + kfree(neigh_node); + } else { + if ((*best_neigh_node == NULL) || + (neigh_node->tq_avg > (*best_neigh_node)->tq_avg)) + *best_neigh_node = neigh_node; + } + } + return neigh_purged; +} + +static bool purge_orig_node(struct orig_node *orig_node) +{ + /* FIXME: each batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + struct neigh_node *best_neigh_node; + + if (time_after(jiffies, + orig_node->last_valid + 2 * PURGE_TIMEOUT * HZ)) { + - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "Originator timeout: originator %pM, last_valid %lu\n", + orig_node->orig, (orig_node->last_valid / HZ)); + return true; + } else { + if (purge_orig_neighbors(orig_node, &best_neigh_node)) { + update_routes(orig_node, best_neigh_node, + orig_node->hna_buff, + orig_node->hna_buff_len); + /* update bonding candidates, we could have lost + * some candidates. */ + update_bonding_candidates(bat_priv, orig_node); + } + } + + return false; +} + +void purge_orig(struct work_struct *work) +{ + HASHIT(hashit); + struct orig_node *orig_node; + unsigned long flags; + + spin_lock_irqsave(&orig_hash_lock, flags); + + /* for all origins... */ + while (hash_iterate(orig_hash, &hashit)) { + orig_node = hashit.bucket->data; + if (purge_orig_node(orig_node)) { + hash_remove_bucket(orig_hash, &hashit); + free_orig_node(orig_node); + } + } + + spin_unlock_irqrestore(&orig_hash_lock, flags); + + /* if work == NULL we were not called by the timer + * and thus do not need to re-arm the timer */ + if (work) + start_purge_timer(); +} + +int orig_seq_print_text(struct seq_file *seq, void *offset) +{ + HASHIT(hashit); + struct net_device *net_dev = (struct net_device *)seq->private; + struct bat_priv *bat_priv = netdev_priv(net_dev); + struct orig_node *orig_node; + struct neigh_node *neigh_node; + int batman_count = 0; ++ int last_seen_secs; ++ int last_seen_msecs; + unsigned long flags; + char orig_str[ETH_STR_LEN], router_str[ETH_STR_LEN]; + + if ((!bat_priv->primary_if) || + (bat_priv->primary_if->if_status != IF_ACTIVE)) { + if (!bat_priv->primary_if) + return seq_printf(seq, "BATMAN mesh %s disabled - " + "please specify interfaces to enable it\n", + net_dev->name); + + return seq_printf(seq, "BATMAN mesh %s " + "disabled - primary interface not active\n", + net_dev->name); + } + + rcu_read_lock(); - seq_printf(seq, " %-14s (%s/%i) %17s [%10s]: %20s " - "... [B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%s (%s)]\n", - "Originator", "#", TQ_MAX_VALUE, "Nexthop", "outgoingIF", - "Potential nexthops", SOURCE_VERSION, REVISION_VERSION_STR, ++ seq_printf(seq, "[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%s (%s)]\n", ++ SOURCE_VERSION, REVISION_VERSION_STR, + bat_priv->primary_if->dev, bat_priv->primary_if->addr_str, + net_dev->name); ++ seq_printf(seq, " %-15s %s (%s/%i) %17s [%10s]: %20s ...\n", ++ "Originator", "last-seen", "#", TQ_MAX_VALUE, "Nexthop", ++ "outgoingIF", "Potential nexthops"); + rcu_read_unlock(); + + spin_lock_irqsave(&orig_hash_lock, flags); + + while (hash_iterate(orig_hash, &hashit)) { + + orig_node = hashit.bucket->data; + + if (!orig_node->router) + continue; + + if (orig_node->router->tq_avg == 0) + continue; + + addr_to_string(orig_str, orig_node->orig); + addr_to_string(router_str, orig_node->router->addr); - - seq_printf(seq, "%-17s (%3i) %17s [%10s]:", - orig_str, orig_node->router->tq_avg, router_str, ++ last_seen_secs = jiffies_to_msecs(jiffies - ++ orig_node->last_valid) / 1000; ++ last_seen_msecs = jiffies_to_msecs(jiffies - ++ orig_node->last_valid) % 1000; ++ ++ seq_printf(seq, "%-17s %4i.%03is (%3i) %17s [%10s]:", ++ orig_str, last_seen_secs, last_seen_msecs, ++ orig_node->router->tq_avg, router_str, + orig_node->router->if_incoming->dev); + + list_for_each_entry(neigh_node, &orig_node->neigh_list, list) { + addr_to_string(orig_str, neigh_node->addr); + seq_printf(seq, " %17s (%3i)", orig_str, + neigh_node->tq_avg); + } + + seq_printf(seq, "\n"); + batman_count++; + } + + spin_unlock_irqrestore(&orig_hash_lock, flags); + + if ((batman_count == 0)) + seq_printf(seq, "No batman nodes in range ...\n"); + + return 0; +} + +static int orig_node_add_if(struct orig_node *orig_node, int max_if_num) +{ + void *data_ptr; + + data_ptr = kmalloc(max_if_num * sizeof(TYPE_OF_WORD) * NUM_WORDS, + GFP_ATOMIC); + if (!data_ptr) { - printk(KERN_ERR - "batman-adv:Can't resize orig: out of memory\n"); ++ pr_err("Can't resize orig: out of memory\n"); + return -1; + } + + memcpy(data_ptr, orig_node->bcast_own, + (max_if_num - 1) * sizeof(TYPE_OF_WORD) * NUM_WORDS); + kfree(orig_node->bcast_own); + orig_node->bcast_own = data_ptr; + + data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC); + if (!data_ptr) { - printk(KERN_ERR - "batman-adv:Can't resize orig: out of memory\n"); ++ pr_err("Can't resize orig: out of memory\n"); + return -1; + } + + memcpy(data_ptr, orig_node->bcast_own_sum, + (max_if_num - 1) * sizeof(uint8_t)); + kfree(orig_node->bcast_own_sum); + orig_node->bcast_own_sum = data_ptr; + + return 0; +} + +int orig_hash_add_if(struct batman_if *batman_if, int max_if_num) +{ + struct orig_node *orig_node; + HASHIT(hashit); + + /* resize all orig nodes because orig_node->bcast_own(_sum) depend on + * if_num */ + spin_lock(&orig_hash_lock); + + while (hash_iterate(orig_hash, &hashit)) { + orig_node = hashit.bucket->data; + + if (orig_node_add_if(orig_node, max_if_num) == -1) + goto err; + } + + spin_unlock(&orig_hash_lock); + return 0; + +err: + spin_unlock(&orig_hash_lock); + return -ENOMEM; +} + +static int orig_node_del_if(struct orig_node *orig_node, + int max_if_num, int del_if_num) +{ + void *data_ptr = NULL; + int chunk_size; + + /* last interface was removed */ + if (max_if_num == 0) + goto free_bcast_own; + + chunk_size = sizeof(TYPE_OF_WORD) * NUM_WORDS; + data_ptr = kmalloc(max_if_num * chunk_size, GFP_ATOMIC); + if (!data_ptr) { - printk(KERN_ERR - "batman-adv:Can't resize orig: out of memory\n"); ++ pr_err("Can't resize orig: out of memory\n"); + return -1; + } + + /* copy first part */ + memcpy(data_ptr, orig_node->bcast_own, del_if_num * chunk_size); + + /* copy second part */ + memcpy(data_ptr, + orig_node->bcast_own + ((del_if_num + 1) * chunk_size), + (max_if_num - del_if_num) * chunk_size); + +free_bcast_own: + kfree(orig_node->bcast_own); + orig_node->bcast_own = data_ptr; + + if (max_if_num == 0) + goto free_own_sum; + + data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC); + if (!data_ptr) { - printk(KERN_ERR - "batman-adv:Can't resize orig: out of memory\n"); ++ pr_err("Can't resize orig: out of memory\n"); + return -1; + } + + memcpy(data_ptr, orig_node->bcast_own_sum, + del_if_num * sizeof(uint8_t)); + + memcpy(data_ptr, + orig_node->bcast_own_sum + ((del_if_num + 1) * sizeof(uint8_t)), + (max_if_num - del_if_num) * sizeof(uint8_t)); + +free_own_sum: + kfree(orig_node->bcast_own_sum); + orig_node->bcast_own_sum = data_ptr; + + return 0; +} + +int orig_hash_del_if(struct batman_if *batman_if, int max_if_num) +{ + struct batman_if *batman_if_tmp; + struct orig_node *orig_node; + HASHIT(hashit); + int ret; + + /* resize all orig nodes because orig_node->bcast_own(_sum) depend on + * if_num */ + spin_lock(&orig_hash_lock); + + while (hash_iterate(orig_hash, &hashit)) { + orig_node = hashit.bucket->data; + + ret = orig_node_del_if(orig_node, max_if_num, + batman_if->if_num); + + if (ret == -1) + goto err; + } + + /* renumber remaining batman interfaces _inside_ of orig_hash_lock */ + rcu_read_lock(); + list_for_each_entry_rcu(batman_if_tmp, &if_list, list) { + if (batman_if_tmp->if_status == IF_NOT_IN_USE) + continue; + + if (batman_if == batman_if_tmp) + continue; + + if (batman_if_tmp->if_num > batman_if->if_num) + batman_if_tmp->if_num--; + } + rcu_read_unlock(); + + batman_if->if_num = -1; + spin_unlock(&orig_hash_lock); + return 0; + +err: + spin_unlock(&orig_hash_lock); + return -ENOMEM; +} diff --combined drivers/staging/batman-adv/routing.c index 127cfbf,0000000..066cc91 mode 100644,000000..100644 --- a/drivers/staging/batman-adv/routing.c +++ b/drivers/staging/batman-adv/routing.c @@@ -1,1291 -1,0 +1,1305 @@@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "routing.h" +#include "send.h" +#include "hash.h" +#include "soft-interface.h" +#include "hard-interface.h" +#include "icmp_socket.h" +#include "translation-table.h" +#include "originator.h" +#include "types.h" +#include "ring_buffer.h" +#include "vis.h" +#include "aggregation.h" + +static DECLARE_WAIT_QUEUE_HEAD(thread_wait); + +void slide_own_bcast_window(struct batman_if *batman_if) +{ + HASHIT(hashit); + struct orig_node *orig_node; + TYPE_OF_WORD *word; + unsigned long flags; + + spin_lock_irqsave(&orig_hash_lock, flags); + + while (hash_iterate(orig_hash, &hashit)) { + orig_node = hashit.bucket->data; + word = &(orig_node->bcast_own[batman_if->if_num * NUM_WORDS]); + + bit_get_packet(word, 1, 0); + orig_node->bcast_own_sum[batman_if->if_num] = + bit_packet_count(word); + } + + spin_unlock_irqrestore(&orig_hash_lock, flags); +} + +static void update_HNA(struct orig_node *orig_node, + unsigned char *hna_buff, int hna_buff_len) +{ + if ((hna_buff_len != orig_node->hna_buff_len) || + ((hna_buff_len > 0) && + (orig_node->hna_buff_len > 0) && + (memcmp(orig_node->hna_buff, hna_buff, hna_buff_len) != 0))) { + + if (orig_node->hna_buff_len > 0) + hna_global_del_orig(orig_node, + "originator changed hna"); + + if ((hna_buff_len > 0) && (hna_buff != NULL)) + hna_global_add_orig(orig_node, hna_buff, hna_buff_len); + } +} + +static void update_route(struct orig_node *orig_node, + struct neigh_node *neigh_node, + unsigned char *hna_buff, int hna_buff_len) +{ ++ /* FIXME: each orig_node->batman_if will be attached to a softif */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); ++ + /* route deleted */ + if ((orig_node->router != NULL) && (neigh_node == NULL)) { + - bat_dbg(DBG_ROUTES, "Deleting route towards: %pM\n", ++ bat_dbg(DBG_ROUTES, bat_priv, "Deleting route towards: %pM\n", + orig_node->orig); + hna_global_del_orig(orig_node, "originator timed out"); + + /* route added */ + } else if ((orig_node->router == NULL) && (neigh_node != NULL)) { + - bat_dbg(DBG_ROUTES, ++ bat_dbg(DBG_ROUTES, bat_priv, + "Adding route towards: %pM (via %pM)\n", + orig_node->orig, neigh_node->addr); + hna_global_add_orig(orig_node, hna_buff, hna_buff_len); + + /* route changed */ + } else { - bat_dbg(DBG_ROUTES, ++ bat_dbg(DBG_ROUTES, bat_priv, + "Changing route towards: %pM " + "(now via %pM - was via %pM)\n", + orig_node->orig, neigh_node->addr, + orig_node->router->addr); + } + + orig_node->router = neigh_node; +} + + +void update_routes(struct orig_node *orig_node, + struct neigh_node *neigh_node, + unsigned char *hna_buff, int hna_buff_len) +{ + + if (orig_node == NULL) + return; + + if (orig_node->router != neigh_node) + update_route(orig_node, neigh_node, hna_buff, hna_buff_len); + /* may be just HNA changed */ + else + update_HNA(orig_node, hna_buff, hna_buff_len); +} + +static int is_bidirectional_neigh(struct orig_node *orig_node, + struct orig_node *orig_neigh_node, + struct batman_packet *batman_packet, + struct batman_if *if_incoming) +{ ++ /* FIXME: each orig_node->batman_if will be attached to a softif */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); + struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; + unsigned char total_count; + + if (orig_node == orig_neigh_node) { + list_for_each_entry(tmp_neigh_node, + &orig_node->neigh_list, + list) { + + if (compare_orig(tmp_neigh_node->addr, + orig_neigh_node->orig) && + (tmp_neigh_node->if_incoming == if_incoming)) + neigh_node = tmp_neigh_node; + } + + if (!neigh_node) + neigh_node = create_neighbor(orig_node, + orig_neigh_node, + orig_neigh_node->orig, + if_incoming); + /* create_neighbor failed, return 0 */ + if (!neigh_node) + return 0; + + neigh_node->last_valid = jiffies; + } else { + /* find packet count of corresponding one hop neighbor */ + list_for_each_entry(tmp_neigh_node, + &orig_neigh_node->neigh_list, list) { + + if (compare_orig(tmp_neigh_node->addr, + orig_neigh_node->orig) && + (tmp_neigh_node->if_incoming == if_incoming)) + neigh_node = tmp_neigh_node; + } + + if (!neigh_node) + neigh_node = create_neighbor(orig_neigh_node, + orig_neigh_node, + orig_neigh_node->orig, + if_incoming); + /* create_neighbor failed, return 0 */ + if (!neigh_node) + return 0; + } + + orig_node->last_valid = jiffies; + + /* pay attention to not get a value bigger than 100 % */ + total_count = (orig_neigh_node->bcast_own_sum[if_incoming->if_num] > + neigh_node->real_packet_count ? + neigh_node->real_packet_count : + orig_neigh_node->bcast_own_sum[if_incoming->if_num]); + + /* if we have too few packets (too less data) we set tq_own to zero */ + /* if we receive too few packets it is not considered bidirectional */ + if ((total_count < TQ_LOCAL_BIDRECT_SEND_MINIMUM) || + (neigh_node->real_packet_count < TQ_LOCAL_BIDRECT_RECV_MINIMUM)) + orig_neigh_node->tq_own = 0; + else + /* neigh_node->real_packet_count is never zero as we + * only purge old information when getting new + * information */ + orig_neigh_node->tq_own = (TQ_MAX_VALUE * total_count) / + neigh_node->real_packet_count; + + /* + * 1 - ((1-x) ** 3), normalized to TQ_MAX_VALUE this does + * affect the nearly-symmetric links only a little, but + * punishes asymmetric links more. This will give a value + * between 0 and TQ_MAX_VALUE + */ + orig_neigh_node->tq_asym_penalty = + TQ_MAX_VALUE - + (TQ_MAX_VALUE * + (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) * + (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) * + (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count)) / + (TQ_LOCAL_WINDOW_SIZE * + TQ_LOCAL_WINDOW_SIZE * + TQ_LOCAL_WINDOW_SIZE); + + batman_packet->tq = ((batman_packet->tq * + orig_neigh_node->tq_own * + orig_neigh_node->tq_asym_penalty) / + (TQ_MAX_VALUE * TQ_MAX_VALUE)); + - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "bidirectional: " + "orig = %-15pM neigh = %-15pM => own_bcast = %2i, " + "real recv = %2i, local tq: %3i, asym_penalty: %3i, " + "total tq: %3i\n", + orig_node->orig, orig_neigh_node->orig, total_count, + neigh_node->real_packet_count, orig_neigh_node->tq_own, + orig_neigh_node->tq_asym_penalty, batman_packet->tq); + + /* if link has the minimum required transmission quality + * consider it bidirectional */ + if (batman_packet->tq >= TQ_TOTAL_BIDRECT_LIMIT) + return 1; + + return 0; +} + +static void update_orig(struct orig_node *orig_node, struct ethhdr *ethhdr, + struct batman_packet *batman_packet, + struct batman_if *if_incoming, + unsigned char *hna_buff, int hna_buff_len, + char is_duplicate) +{ ++ /* FIXME: get bat_priv */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); + struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; + int tmp_hna_buff_len; + - bat_dbg(DBG_BATMAN, "update_originator(): " ++ bat_dbg(DBG_BATMAN, bat_priv, "update_originator(): " + "Searching and updating originator entry of received packet\n"); + + list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { + if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && + (tmp_neigh_node->if_incoming == if_incoming)) { + neigh_node = tmp_neigh_node; + continue; + } + + if (is_duplicate) + continue; + + ring_buffer_set(tmp_neigh_node->tq_recv, + &tmp_neigh_node->tq_index, 0); + tmp_neigh_node->tq_avg = + ring_buffer_avg(tmp_neigh_node->tq_recv); + } + + if (!neigh_node) { + struct orig_node *orig_tmp; + + orig_tmp = get_orig_node(ethhdr->h_source); + if (!orig_tmp) + return; + + neigh_node = create_neighbor(orig_node, + orig_tmp, + ethhdr->h_source, if_incoming); + if (!neigh_node) + return; + } else - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "Updating existing last-hop neighbor of originator\n"); + + orig_node->flags = batman_packet->flags; + neigh_node->last_valid = jiffies; + + ring_buffer_set(neigh_node->tq_recv, + &neigh_node->tq_index, + batman_packet->tq); + neigh_node->tq_avg = ring_buffer_avg(neigh_node->tq_recv); + + if (!is_duplicate) { + orig_node->last_ttl = batman_packet->ttl; + neigh_node->last_ttl = batman_packet->ttl; + } + + tmp_hna_buff_len = (hna_buff_len > batman_packet->num_hna * ETH_ALEN ? + batman_packet->num_hna * ETH_ALEN : hna_buff_len); + + /* if this neighbor already is our next hop there is nothing + * to change */ + if (orig_node->router == neigh_node) + goto update_hna; + + /* if this neighbor does not offer a better TQ we won't consider it */ + if ((orig_node->router) && + (orig_node->router->tq_avg > neigh_node->tq_avg)) + goto update_hna; + + /* if the TQ is the same and the link not more symetric we + * won't consider it either */ + if ((orig_node->router) && + ((neigh_node->tq_avg == orig_node->router->tq_avg) && + (orig_node->router->orig_node->bcast_own_sum[if_incoming->if_num] + >= neigh_node->orig_node->bcast_own_sum[if_incoming->if_num]))) + goto update_hna; + + update_routes(orig_node, neigh_node, hna_buff, tmp_hna_buff_len); + return; + +update_hna: + update_routes(orig_node, orig_node->router, hna_buff, tmp_hna_buff_len); +} + +/* checks whether the host restarted and is in the protection time. + * returns: + * 0 if the packet is to be accepted + * 1 if the packet is to be ignored. + */ +static int window_protected(int32_t seq_num_diff, + unsigned long *last_reset) +{ ++ /* FIXME: each orig_node->batman_if will be attached to a softif */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); ++ + if ((seq_num_diff <= -TQ_LOCAL_WINDOW_SIZE) + || (seq_num_diff >= EXPECTED_SEQNO_RANGE)) { + if (time_after(jiffies, *last_reset + + msecs_to_jiffies(RESET_PROTECTION_MS))) { + + *last_reset = jiffies; - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "old packet received, start protection\n"); + + return 0; + } else + return 1; + } + return 0; +} + +/* processes a batman packet for all interfaces, adjusts the sequence number and + * finds out whether it is a duplicate. + * returns: + * 1 the packet is a duplicate + * 0 the packet has not yet been received + * -1 the packet is old and has been received while the seqno window + * was protected. Caller should drop it. + */ +static char count_real_packets(struct ethhdr *ethhdr, + struct batman_packet *batman_packet, + struct batman_if *if_incoming) +{ ++ /* FIXME: each orig_node->batman_if will be attached to a softif */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); + struct orig_node *orig_node; + struct neigh_node *tmp_neigh_node; + char is_duplicate = 0; + int32_t seq_diff; + int need_update = 0; + int set_mark; + + orig_node = get_orig_node(batman_packet->orig); + if (orig_node == NULL) + return 0; + + seq_diff = batman_packet->seqno - orig_node->last_real_seqno; + + /* signalize caller that the packet is to be dropped. */ + if (window_protected(seq_diff, &orig_node->batman_seqno_reset)) + return -1; + + list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { + + is_duplicate |= get_bit_status(tmp_neigh_node->real_bits, + orig_node->last_real_seqno, + batman_packet->seqno); + + if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && + (tmp_neigh_node->if_incoming == if_incoming)) + set_mark = 1; + else + set_mark = 0; + + /* if the window moved, set the update flag. */ + need_update |= bit_get_packet(tmp_neigh_node->real_bits, + seq_diff, set_mark); + + tmp_neigh_node->real_packet_count = + bit_packet_count(tmp_neigh_node->real_bits); + } + + if (need_update) { - bat_dbg(DBG_BATMAN, "updating last_seqno: old %d, new %d\n", ++ bat_dbg(DBG_BATMAN, bat_priv, ++ "updating last_seqno: old %d, new %d\n", + orig_node->last_real_seqno, batman_packet->seqno); + orig_node->last_real_seqno = batman_packet->seqno; + } + + return is_duplicate; +} + +/* copy primary address for bonding */ +static void mark_bonding_address(struct bat_priv *bat_priv, + struct orig_node *orig_node, + struct orig_node *orig_neigh_node, + struct batman_packet *batman_packet) + +{ + if (batman_packet->flags & PRIMARIES_FIRST_HOP) + memcpy(orig_neigh_node->primary_addr, + orig_node->orig, ETH_ALEN); + + return; +} + +/* mark possible bond.candidates in the neighbor list */ +void update_bonding_candidates(struct bat_priv *bat_priv, + struct orig_node *orig_node) +{ + int candidates; + int interference_candidate; + int best_tq; + struct neigh_node *tmp_neigh_node, *tmp_neigh_node2; + struct neigh_node *first_candidate, *last_candidate; + + /* update the candidates for this originator */ + if (!orig_node->router) { + orig_node->bond.candidates = 0; + return; + } + + best_tq = orig_node->router->tq_avg; + + /* update bond.candidates */ + + candidates = 0; + + /* mark other nodes which also received "PRIMARIES FIRST HOP" packets + * as "bonding partner" */ + + /* first, zero the list */ + list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { + tmp_neigh_node->next_bond_candidate = NULL; + } + + first_candidate = NULL; + last_candidate = NULL; + list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { + + /* only consider if it has the same primary address ... */ + if (memcmp(orig_node->orig, + tmp_neigh_node->orig_node->primary_addr, + ETH_ALEN) != 0) + continue; + + /* ... and is good enough to be considered */ + if (tmp_neigh_node->tq_avg < best_tq - BONDING_TQ_THRESHOLD) + continue; + + /* check if we have another candidate with the same + * mac address or interface. If we do, we won't + * select this candidate because of possible interference. */ + + interference_candidate = 0; + list_for_each_entry(tmp_neigh_node2, + &orig_node->neigh_list, list) { + + if (tmp_neigh_node2 == tmp_neigh_node) + continue; + + /* we only care if the other candidate is even + * considered as candidate. */ + if (tmp_neigh_node2->next_bond_candidate == NULL) + continue; + + + if ((tmp_neigh_node->if_incoming == + tmp_neigh_node2->if_incoming) + || (memcmp(tmp_neigh_node->addr, + tmp_neigh_node2->addr, ETH_ALEN) == 0)) { + + interference_candidate = 1; + break; + } + } + /* don't care further if it is an interference candidate */ + if (interference_candidate) + continue; + + if (first_candidate == NULL) { + first_candidate = tmp_neigh_node; + tmp_neigh_node->next_bond_candidate = first_candidate; + } else + tmp_neigh_node->next_bond_candidate = last_candidate; + + last_candidate = tmp_neigh_node; + + candidates++; + } + + if (candidates > 0) { + first_candidate->next_bond_candidate = last_candidate; + orig_node->bond.selected = first_candidate; + } + + orig_node->bond.candidates = candidates; +} + +void receive_bat_packet(struct ethhdr *ethhdr, + struct batman_packet *batman_packet, + unsigned char *hna_buff, int hna_buff_len, + struct batman_if *if_incoming) +{ + /* FIXME: each orig_node->batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + struct batman_if *batman_if; + struct orig_node *orig_neigh_node, *orig_node; + char has_directlink_flag; + char is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0; + char is_broadcast = 0, is_bidirectional, is_single_hop_neigh; + char is_duplicate; + uint32_t if_incoming_seqno; + + /* Silently drop when the batman packet is actually not a + * correct packet. + * + * This might happen if a packet is padded (e.g. Ethernet has a + * minimum frame length of 64 byte) and the aggregation interprets + * it as an additional length. + * + * TODO: A more sane solution would be to have a bit in the + * batman_packet to detect whether the packet is the last + * packet in an aggregation. Here we expect that the padding + * is always zero (or not 0x01) + */ + if (batman_packet->packet_type != BAT_PACKET) + return; + + /* could be changed by schedule_own_packet() */ + if_incoming_seqno = atomic_read(&if_incoming->seqno); + + has_directlink_flag = (batman_packet->flags & DIRECTLINK ? 1 : 0); + + is_single_hop_neigh = (compare_orig(ethhdr->h_source, + batman_packet->orig) ? 1 : 0); + - bat_dbg(DBG_BATMAN, "Received BATMAN packet via NB: %pM, IF: %s [%s] " ++ bat_dbg(DBG_BATMAN, bat_priv, ++ "Received BATMAN packet via NB: %pM, IF: %s [%s] " + "(from OG: %pM, via prev OG: %pM, seqno %d, tq %d, " + "TTL %d, V %d, IDF %d)\n", + ethhdr->h_source, if_incoming->dev, if_incoming->addr_str, + batman_packet->orig, batman_packet->prev_sender, + batman_packet->seqno, batman_packet->tq, batman_packet->ttl, + batman_packet->version, has_directlink_flag); + + list_for_each_entry_rcu(batman_if, &if_list, list) { + if (batman_if->if_status != IF_ACTIVE) + continue; + + if (compare_orig(ethhdr->h_source, + batman_if->net_dev->dev_addr)) + is_my_addr = 1; + + if (compare_orig(batman_packet->orig, + batman_if->net_dev->dev_addr)) + is_my_orig = 1; + + if (compare_orig(batman_packet->prev_sender, + batman_if->net_dev->dev_addr)) + is_my_oldorig = 1; + + if (compare_orig(ethhdr->h_source, broadcast_addr)) + is_broadcast = 1; + } + + if (batman_packet->version != COMPAT_VERSION) { - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: incompatible batman version (%i)\n", + batman_packet->version); + return; + } + + if (is_my_addr) { - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: received my own broadcast (sender: %pM" + ")\n", + ethhdr->h_source); + return; + } + + if (is_broadcast) { - bat_dbg(DBG_BATMAN, "Drop packet: " ++ bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: " + "ignoring all packets with broadcast source addr (sender: %pM" + ")\n", ethhdr->h_source); + return; + } + + if (is_my_orig) { + TYPE_OF_WORD *word; + int offset; + + orig_neigh_node = get_orig_node(ethhdr->h_source); + + if (!orig_neigh_node) + return; + + /* neighbor has to indicate direct link and it has to + * come via the corresponding interface */ + /* if received seqno equals last send seqno save new + * seqno for bidirectional check */ + if (has_directlink_flag && + compare_orig(if_incoming->net_dev->dev_addr, + batman_packet->orig) && + (batman_packet->seqno - if_incoming_seqno + 2 == 0)) { + offset = if_incoming->if_num * NUM_WORDS; + word = &(orig_neigh_node->bcast_own[offset]); + bit_mark(word, 0); + orig_neigh_node->bcast_own_sum[if_incoming->if_num] = + bit_packet_count(word); + } + - bat_dbg(DBG_BATMAN, "Drop packet: " ++ bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: " + "originator packet from myself (via neighbor)\n"); + return; + } + + if (is_my_oldorig) { - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: ignoring all rebroadcast echos (sender: " + "%pM)\n", ethhdr->h_source); + return; + } + + orig_node = get_orig_node(batman_packet->orig); + if (orig_node == NULL) + return; + + is_duplicate = count_real_packets(ethhdr, batman_packet, if_incoming); + + if (is_duplicate == -1) { - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: packet within seqno protection time " + "(sender: %pM)\n", ethhdr->h_source); + return; + } + + if (batman_packet->tq == 0) { - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: originator packet with tq equal 0\n"); + return; + } + + /* avoid temporary routing loops */ + if ((orig_node->router) && + (orig_node->router->orig_node->router) && + (compare_orig(orig_node->router->addr, + batman_packet->prev_sender)) && + !(compare_orig(batman_packet->orig, batman_packet->prev_sender)) && + (compare_orig(orig_node->router->addr, + orig_node->router->orig_node->router->addr))) { - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: ignoring all rebroadcast packets that " + "may make me loop (sender: %pM)\n", ethhdr->h_source); + return; + } + + /* if sender is a direct neighbor the sender mac equals + * originator mac */ + orig_neigh_node = (is_single_hop_neigh ? + orig_node : get_orig_node(ethhdr->h_source)); + if (orig_neigh_node == NULL) + return; + + /* drop packet if sender is not a direct neighbor and if we + * don't route towards it */ + if (!is_single_hop_neigh && + (orig_neigh_node->router == NULL)) { - bat_dbg(DBG_BATMAN, "Drop packet: OGM via unknown neighbor!\n"); ++ bat_dbg(DBG_BATMAN, bat_priv, ++ "Drop packet: OGM via unknown neighbor!\n"); + return; + } + + is_bidirectional = is_bidirectional_neigh(orig_node, orig_neigh_node, + batman_packet, if_incoming); + + /* update ranking if it is not a duplicate or has the same + * seqno and similar ttl as the non-duplicate */ + if (is_bidirectional && + (!is_duplicate || + ((orig_node->last_real_seqno == batman_packet->seqno) && + (orig_node->last_ttl - 3 <= batman_packet->ttl)))) + update_orig(orig_node, ethhdr, batman_packet, + if_incoming, hna_buff, hna_buff_len, is_duplicate); + + mark_bonding_address(bat_priv, orig_node, + orig_neigh_node, batman_packet); + update_bonding_candidates(bat_priv, orig_node); + + /* is single hop (direct) neighbor */ + if (is_single_hop_neigh) { + + /* mark direct link on incoming interface */ + schedule_forward_packet(orig_node, ethhdr, batman_packet, + 1, hna_buff_len, if_incoming); + - bat_dbg(DBG_BATMAN, "Forwarding packet: " ++ bat_dbg(DBG_BATMAN, bat_priv, "Forwarding packet: " + "rebroadcast neighbor packet with direct link flag\n"); + return; + } + + /* multihop originator */ + if (!is_bidirectional) { - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: not received via bidirectional link\n"); + return; + } + + if (is_duplicate) { - bat_dbg(DBG_BATMAN, "Drop packet: duplicate packet received\n"); ++ bat_dbg(DBG_BATMAN, bat_priv, ++ "Drop packet: duplicate packet received\n"); + return; + } + - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "Forwarding packet: rebroadcast originator packet\n"); + schedule_forward_packet(orig_node, ethhdr, batman_packet, + 0, hna_buff_len, if_incoming); +} + +int recv_bat_packet(struct sk_buff *skb, + struct batman_if *batman_if) +{ + struct ethhdr *ethhdr; + unsigned long flags; + struct sk_buff *skb_old; + + /* drop packet if it has not necessary minimum size */ + if (skb_headlen(skb) < sizeof(struct batman_packet)) + return NET_RX_DROP; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* packet with broadcast indication but unicast recipient */ + if (!is_bcast(ethhdr->h_dest)) + return NET_RX_DROP; + + /* packet with broadcast sender address */ + if (is_bcast(ethhdr->h_source)) + return NET_RX_DROP; + + /* TODO: we use headlen instead of "length", because + * only this data is paged in. */ + + /* create a copy of the skb, if needed, to modify it. */ + if (!skb_clone_writable(skb, skb_headlen(skb))) { + skb_old = skb; + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + kfree_skb(skb_old); + } + + spin_lock_irqsave(&orig_hash_lock, flags); + receive_aggr_bat_packet(ethhdr, + skb->data, + skb_headlen(skb), + batman_if); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + kfree_skb(skb); + return NET_RX_SUCCESS; +} + +static int recv_my_icmp_packet(struct sk_buff *skb, size_t icmp_len) +{ + struct orig_node *orig_node; + struct icmp_packet_rr *icmp_packet; + struct ethhdr *ethhdr; + struct sk_buff *skb_old; + struct batman_if *batman_if; + int ret; + unsigned long flags; + uint8_t dstaddr[ETH_ALEN]; + + icmp_packet = (struct icmp_packet_rr *)skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* add data to device queue */ + if (icmp_packet->msg_type != ECHO_REQUEST) { + bat_socket_receive_packet(icmp_packet, icmp_len); + return NET_RX_DROP; + } + + /* answer echo request (ping) */ + /* get routing information */ + spin_lock_irqsave(&orig_hash_lock, flags); + orig_node = ((struct orig_node *)hash_find(orig_hash, + icmp_packet->orig)); + ret = NET_RX_DROP; + + if ((orig_node != NULL) && + (orig_node->router != NULL)) { + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + /* create a copy of the skb, if needed, to modify it. */ + skb_old = NULL; + if (!skb_clone_writable(skb, icmp_len)) { + skb_old = skb; + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + icmp_packet = (struct icmp_packet_rr *)skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + kfree_skb(skb_old); + } + + memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); + memcpy(icmp_packet->orig, ethhdr->h_dest, ETH_ALEN); + icmp_packet->msg_type = ECHO_REPLY; + icmp_packet->ttl = TTL; + + send_skb_packet(skb, batman_if, dstaddr); + ret = NET_RX_SUCCESS; + + } else + spin_unlock_irqrestore(&orig_hash_lock, flags); + + return ret; +} + +static int recv_icmp_ttl_exceeded(struct sk_buff *skb, size_t icmp_len) +{ + struct orig_node *orig_node; + struct icmp_packet *icmp_packet; + struct ethhdr *ethhdr; + struct sk_buff *skb_old; + struct batman_if *batman_if; + int ret; + unsigned long flags; + uint8_t dstaddr[ETH_ALEN]; + + icmp_packet = (struct icmp_packet *)skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* send TTL exceeded if packet is an echo request (traceroute) */ + if (icmp_packet->msg_type != ECHO_REQUEST) { - printk(KERN_WARNING "batman-adv:" - "Warning - can't forward icmp packet from %pM to %pM: " - "ttl exceeded\n", - icmp_packet->orig, icmp_packet->dst); ++ pr_warning("Warning - can't forward icmp packet from %pM to " ++ "%pM: ttl exceeded\n", icmp_packet->orig, ++ icmp_packet->dst); + return NET_RX_DROP; + } + + /* get routing information */ + spin_lock_irqsave(&orig_hash_lock, flags); + orig_node = ((struct orig_node *) + hash_find(orig_hash, icmp_packet->orig)); + ret = NET_RX_DROP; + + if ((orig_node != NULL) && + (orig_node->router != NULL)) { + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + /* create a copy of the skb, if needed, to modify it. */ + if (!skb_clone_writable(skb, icmp_len)) { + skb_old = skb; + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + icmp_packet = (struct icmp_packet *) skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + kfree_skb(skb_old); + } + + memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); + memcpy(icmp_packet->orig, ethhdr->h_dest, ETH_ALEN); + icmp_packet->msg_type = TTL_EXCEEDED; + icmp_packet->ttl = TTL; + + send_skb_packet(skb, batman_if, dstaddr); + ret = NET_RX_SUCCESS; + + } else + spin_unlock_irqrestore(&orig_hash_lock, flags); + + return ret; +} + + +int recv_icmp_packet(struct sk_buff *skb) +{ + struct icmp_packet_rr *icmp_packet; + struct ethhdr *ethhdr; + struct orig_node *orig_node; + struct sk_buff *skb_old; + struct batman_if *batman_if; + int hdr_size = sizeof(struct icmp_packet); + int ret; + unsigned long flags; + uint8_t dstaddr[ETH_ALEN]; + + /** + * we truncate all incoming icmp packets if they don't match our size + */ + if (skb_headlen(skb) >= sizeof(struct icmp_packet_rr)) + hdr_size = sizeof(struct icmp_packet_rr); + + /* drop packet if it has not necessary minimum size */ + if (skb_headlen(skb) < hdr_size) + return NET_RX_DROP; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* packet with unicast indication but broadcast recipient */ + if (is_bcast(ethhdr->h_dest)) + return NET_RX_DROP; + + /* packet with broadcast sender address */ + if (is_bcast(ethhdr->h_source)) + return NET_RX_DROP; + + /* not for me */ + if (!is_my_mac(ethhdr->h_dest)) + return NET_RX_DROP; + + icmp_packet = (struct icmp_packet_rr *)skb->data; + + /* add record route information if not full */ + if ((hdr_size == sizeof(struct icmp_packet_rr)) && + (icmp_packet->rr_cur < BAT_RR_LEN)) { + memcpy(&(icmp_packet->rr[icmp_packet->rr_cur]), + ethhdr->h_dest, ETH_ALEN); + icmp_packet->rr_cur++; + } + + /* packet for me */ + if (is_my_mac(icmp_packet->dst)) + return recv_my_icmp_packet(skb, hdr_size); + + /* TTL exceeded */ + if (icmp_packet->ttl < 2) + return recv_icmp_ttl_exceeded(skb, hdr_size); + + ret = NET_RX_DROP; + + /* get routing information */ + spin_lock_irqsave(&orig_hash_lock, flags); + orig_node = ((struct orig_node *) + hash_find(orig_hash, icmp_packet->dst)); + + if ((orig_node != NULL) && + (orig_node->router != NULL)) { + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + /* create a copy of the skb, if needed, to modify it. */ + if (!skb_clone_writable(skb, hdr_size)) { + skb_old = skb; + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + icmp_packet = (struct icmp_packet_rr *)skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + kfree_skb(skb_old); + } + + /* decrement ttl */ + icmp_packet->ttl--; + + /* route it */ + send_skb_packet(skb, batman_if, dstaddr); + ret = NET_RX_SUCCESS; + + } else + spin_unlock_irqrestore(&orig_hash_lock, flags); + + return ret; +} + +/* find a suitable router for this originator, and use + * bonding if possible. */ +struct neigh_node *find_router(struct orig_node *orig_node, + struct batman_if *recv_if) +{ + /* FIXME: each orig_node->batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + struct orig_node *primary_orig_node; + struct orig_node *router_orig; + struct neigh_node *router, *first_candidate, *best_router; + static uint8_t zero_mac[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; + int bonding_enabled; + + if (!orig_node) + return NULL; + + if (!orig_node->router) + return NULL; + + /* without bonding, the first node should + * always choose the default router. */ + + bonding_enabled = atomic_read(&bat_priv->bonding_enabled); + if (!bonding_enabled && (recv_if == NULL)) + return orig_node->router; + + router_orig = orig_node->router->orig_node; + + /* if we have something in the primary_addr, we can search + * for a potential bonding candidate. */ + if (memcmp(router_orig->primary_addr, zero_mac, ETH_ALEN) == 0) + return orig_node->router; + + /* find the orig_node which has the primary interface. might + * even be the same as our router_orig in many cases */ + + if (memcmp(router_orig->primary_addr, + router_orig->orig, ETH_ALEN) == 0) { + primary_orig_node = router_orig; + } else { + primary_orig_node = hash_find(orig_hash, + router_orig->primary_addr); + if (!primary_orig_node) + return orig_node->router; + } + + /* with less than 2 candidates, we can't do any + * bonding and prefer the original router. */ + + if (primary_orig_node->bond.candidates < 2) + return orig_node->router; + + + /* all nodes between should choose a candidate which + * is is not on the interface where the packet came + * in. */ + first_candidate = primary_orig_node->bond.selected; + router = first_candidate; + + if (bonding_enabled) { + /* in the bonding case, send the packets in a round + * robin fashion over the remaining interfaces. */ + do { + /* recv_if == NULL on the first node. */ + if (router->if_incoming != recv_if) + break; + + router = router->next_bond_candidate; + } while (router != first_candidate); + + primary_orig_node->bond.selected = router->next_bond_candidate; + + } else { + /* if bonding is disabled, use the best of the + * remaining candidates which are not using + * this interface. */ + best_router = first_candidate; + + do { + /* recv_if == NULL on the first node. */ + if ((router->if_incoming != recv_if) && + (router->tq_avg > best_router->tq_avg)) + best_router = router; + + router = router->next_bond_candidate; + } while (router != first_candidate); + + router = best_router; + } + + return router; +} + +int recv_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if) +{ + struct unicast_packet *unicast_packet; + struct orig_node *orig_node; + struct neigh_node *router; + struct ethhdr *ethhdr; + struct batman_if *batman_if; + struct sk_buff *skb_old; + uint8_t dstaddr[ETH_ALEN]; + int hdr_size = sizeof(struct unicast_packet); + unsigned long flags; + + /* drop packet if it has not necessary minimum size */ + if (skb_headlen(skb) < hdr_size) + return NET_RX_DROP; + + ethhdr = (struct ethhdr *) skb_mac_header(skb); + + /* packet with unicast indication but broadcast recipient */ + if (is_bcast(ethhdr->h_dest)) + return NET_RX_DROP; + + /* packet with broadcast sender address */ + if (is_bcast(ethhdr->h_source)) + return NET_RX_DROP; + + /* not for me */ + if (!is_my_mac(ethhdr->h_dest)) + return NET_RX_DROP; + + unicast_packet = (struct unicast_packet *) skb->data; + + /* packet for me */ + if (is_my_mac(unicast_packet->dest)) { + interface_rx(skb, hdr_size); + return NET_RX_SUCCESS; + } + + /* TTL exceeded */ + if (unicast_packet->ttl < 2) { - printk(KERN_WARNING "batman-adv:Warning - " - "can't forward unicast packet from %pM to %pM: " - "ttl exceeded\n", - ethhdr->h_source, unicast_packet->dest); ++ pr_warning("Warning - can't forward unicast packet from %pM to " ++ "%pM: ttl exceeded\n", ethhdr->h_source, ++ unicast_packet->dest); + return NET_RX_DROP; + } + + /* get routing information */ + spin_lock_irqsave(&orig_hash_lock, flags); + orig_node = ((struct orig_node *) + hash_find(orig_hash, unicast_packet->dest)); + + router = find_router(orig_node, recv_if); + + if (!router) { + spin_unlock_irqrestore(&orig_hash_lock, flags); + return NET_RX_DROP; + } + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + + batman_if = router->if_incoming; + memcpy(dstaddr, router->addr, ETH_ALEN); + + spin_unlock_irqrestore(&orig_hash_lock, flags); + + /* create a copy of the skb, if needed, to modify it. */ + if (!skb_clone_writable(skb, sizeof(struct unicast_packet))) { + skb_old = skb; + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + unicast_packet = (struct unicast_packet *) skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + kfree_skb(skb_old); + } + + /* decrement ttl */ + unicast_packet->ttl--; + + /* route it */ + send_skb_packet(skb, batman_if, dstaddr); + + return NET_RX_SUCCESS; +} + +int recv_bcast_packet(struct sk_buff *skb) +{ + struct orig_node *orig_node; + struct bcast_packet *bcast_packet; + struct ethhdr *ethhdr; + int hdr_size = sizeof(struct bcast_packet); + int32_t seq_diff; + unsigned long flags; + + /* drop packet if it has not necessary minimum size */ + if (skb_headlen(skb) < hdr_size) + return NET_RX_DROP; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* packet with broadcast indication but unicast recipient */ + if (!is_bcast(ethhdr->h_dest)) + return NET_RX_DROP; + + /* packet with broadcast sender address */ + if (is_bcast(ethhdr->h_source)) + return NET_RX_DROP; + + /* ignore broadcasts sent by myself */ + if (is_my_mac(ethhdr->h_source)) + return NET_RX_DROP; + + bcast_packet = (struct bcast_packet *)skb->data; + + /* ignore broadcasts originated by myself */ + if (is_my_mac(bcast_packet->orig)) + return NET_RX_DROP; + + if (bcast_packet->ttl < 2) + return NET_RX_DROP; + + spin_lock_irqsave(&orig_hash_lock, flags); + orig_node = ((struct orig_node *) + hash_find(orig_hash, bcast_packet->orig)); + + if (orig_node == NULL) { + spin_unlock_irqrestore(&orig_hash_lock, flags); + return NET_RX_DROP; + } + + /* check whether the packet is a duplicate */ + if (get_bit_status(orig_node->bcast_bits, + orig_node->last_bcast_seqno, + ntohl(bcast_packet->seqno))) { + spin_unlock_irqrestore(&orig_hash_lock, flags); + return NET_RX_DROP; + } + + seq_diff = ntohl(bcast_packet->seqno) - orig_node->last_bcast_seqno; + + /* check whether the packet is old and the host just restarted. */ + if (window_protected(seq_diff, &orig_node->bcast_seqno_reset)) { + spin_unlock_irqrestore(&orig_hash_lock, flags); + return NET_RX_DROP; + } + + /* mark broadcast in flood history, update window position + * if required. */ + if (bit_get_packet(orig_node->bcast_bits, seq_diff, 1)) + orig_node->last_bcast_seqno = ntohl(bcast_packet->seqno); + + spin_unlock_irqrestore(&orig_hash_lock, flags); + /* rebroadcast packet */ + add_bcast_packet_to_list(skb); + + /* broadcast for me */ + interface_rx(skb, hdr_size); + + return NET_RX_SUCCESS; +} + +int recv_vis_packet(struct sk_buff *skb) +{ + struct vis_packet *vis_packet; + struct ethhdr *ethhdr; + struct bat_priv *bat_priv; + int hdr_size = sizeof(struct vis_packet); + + if (skb_headlen(skb) < hdr_size) + return NET_RX_DROP; + + vis_packet = (struct vis_packet *) skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* not for me */ + if (!is_my_mac(ethhdr->h_dest)) + return NET_RX_DROP; + + /* ignore own packets */ + if (is_my_mac(vis_packet->vis_orig)) + return NET_RX_DROP; + + if (is_my_mac(vis_packet->sender_orig)) + return NET_RX_DROP; + + /* FIXME: each batman_if will be attached to a softif */ + bat_priv = netdev_priv(soft_device); + + switch (vis_packet->vis_type) { + case VIS_TYPE_SERVER_SYNC: + /* TODO: handle fragmented skbs properly */ + receive_server_sync_packet(bat_priv, vis_packet, + skb_headlen(skb)); + break; + + case VIS_TYPE_CLIENT_UPDATE: + /* TODO: handle fragmented skbs properly */ + receive_client_update_packet(bat_priv, vis_packet, + skb_headlen(skb)); + break; + + default: /* ignore unknown packet */ + break; + } + + /* We take a copy of the data in the packet, so we should + always free the skbuf. */ + return NET_RX_DROP; +} diff --combined drivers/staging/batman-adv/send.c index d4b92ef,0000000..055edee mode 100644,000000..100644 --- a/drivers/staging/batman-adv/send.c +++ b/drivers/staging/batman-adv/send.c @@@ -1,576 -1,0 +1,585 @@@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "send.h" +#include "routing.h" +#include "translation-table.h" +#include "soft-interface.h" +#include "hard-interface.h" +#include "types.h" +#include "vis.h" +#include "aggregation.h" + +#include <linux/netfilter_bridge.h> + +static void send_outstanding_bcast_packet(struct work_struct *work); + +/* apply hop penalty for a normal link */ +static uint8_t hop_penalty(const uint8_t tq) +{ + return (tq * (TQ_MAX_VALUE - TQ_HOP_PENALTY)) / (TQ_MAX_VALUE); +} + +/* when do we schedule our own packet to be sent */ +static unsigned long own_send_time(struct bat_priv *bat_priv) +{ + return jiffies + msecs_to_jiffies( + atomic_read(&bat_priv->orig_interval) - + JITTER + (random32() % 2*JITTER)); +} + +/* when do we schedule a forwarded packet to be sent */ +static unsigned long forward_send_time(struct bat_priv *bat_priv) +{ + return jiffies + msecs_to_jiffies(random32() % (JITTER/2)); +} + +/* send out an already prepared packet to the given address via the + * specified batman interface */ +int send_skb_packet(struct sk_buff *skb, + struct batman_if *batman_if, + uint8_t *dst_addr) +{ + struct ethhdr *ethhdr; + + if (batman_if->if_status != IF_ACTIVE) + goto send_skb_err; + + if (unlikely(!batman_if->net_dev)) + goto send_skb_err; + + if (!(batman_if->net_dev->flags & IFF_UP)) { - printk(KERN_WARNING - "batman-adv:Interface %s " - "is not up - can't send packet via that interface!\n", - batman_if->dev); ++ pr_warning("Interface %s is not up - can't send packet via " ++ "that interface!\n", batman_if->dev); + goto send_skb_err; + } + + /* push to the ethernet header. */ + if (my_skb_push(skb, sizeof(struct ethhdr)) < 0) + goto send_skb_err; + + skb_reset_mac_header(skb); + + ethhdr = (struct ethhdr *) skb_mac_header(skb); + memcpy(ethhdr->h_source, batman_if->net_dev->dev_addr, ETH_ALEN); + memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN); + ethhdr->h_proto = __constant_htons(ETH_P_BATMAN); + + skb_set_network_header(skb, ETH_HLEN); + skb->priority = TC_PRIO_CONTROL; + skb->protocol = __constant_htons(ETH_P_BATMAN); + + skb->dev = batman_if->net_dev; + + /* dev_queue_xmit() returns a negative result on error. However on + * congestion and traffic shaping, it drops and returns NET_XMIT_DROP + * (which is > 0). This will not be treated as an error. + * Also, if netfilter/ebtables wants to block outgoing batman + * packets then giving them a chance to do so here */ + + return NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, + dev_queue_xmit); +send_skb_err: + kfree_skb(skb); + return NET_XMIT_DROP; +} + +/* sends a raw packet. */ +void send_raw_packet(unsigned char *pack_buff, int pack_buff_len, + struct batman_if *batman_if, uint8_t *dst_addr) +{ + struct sk_buff *skb; + char *data; + + skb = dev_alloc_skb(pack_buff_len + sizeof(struct ethhdr)); + if (!skb) + return; + data = skb_put(skb, pack_buff_len + sizeof(struct ethhdr)); + memcpy(data + sizeof(struct ethhdr), pack_buff, pack_buff_len); + /* pull back to the batman "network header" */ + skb_pull(skb, sizeof(struct ethhdr)); + send_skb_packet(skb, batman_if, dst_addr); +} + +/* Send a packet to a given interface */ +static void send_packet_to_if(struct forw_packet *forw_packet, + struct batman_if *batman_if) +{ ++ /* FIXME: each batman_if will be attached to a softif */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); + char *fwd_str; + uint8_t packet_num; + int16_t buff_pos; + struct batman_packet *batman_packet; + + if (batman_if->if_status != IF_ACTIVE) + return; + + packet_num = 0; + buff_pos = 0; + batman_packet = (struct batman_packet *) + (forw_packet->packet_buff); + + /* adjust all flags and log packets */ + while (aggregated_packet(buff_pos, + forw_packet->packet_len, + batman_packet->num_hna)) { + + /* we might have aggregated direct link packets with an + * ordinary base packet */ + if ((forw_packet->direct_link_flags & (1 << packet_num)) && + (forw_packet->if_incoming == batman_if)) + batman_packet->flags |= DIRECTLINK; + else + batman_packet->flags &= ~DIRECTLINK; + + fwd_str = (packet_num > 0 ? "Forwarding" : (forw_packet->own ? + "Sending own" : + "Forwarding")); - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "%s %spacket (originator %pM, seqno %d, TQ %d, TTL %d," + " IDF %s) on interface %s [%s]\n", + fwd_str, (packet_num > 0 ? "aggregated " : ""), + batman_packet->orig, ntohl(batman_packet->seqno), + batman_packet->tq, batman_packet->ttl, + (batman_packet->flags & DIRECTLINK ? + "on" : "off"), + batman_if->dev, batman_if->addr_str); + + buff_pos += sizeof(struct batman_packet) + + (batman_packet->num_hna * ETH_ALEN); + packet_num++; + batman_packet = (struct batman_packet *) + (forw_packet->packet_buff + buff_pos); + } + + send_raw_packet(forw_packet->packet_buff, + forw_packet->packet_len, + batman_if, broadcast_addr); +} + +/* send a batman packet */ +static void send_packet(struct forw_packet *forw_packet) +{ ++ /* FIXME: each batman_if will be attached to a softif */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); + struct batman_if *batman_if; + struct batman_packet *batman_packet = + (struct batman_packet *)(forw_packet->packet_buff); + unsigned char directlink = (batman_packet->flags & DIRECTLINK ? 1 : 0); + + if (!forw_packet->if_incoming) { - printk(KERN_ERR "batman-adv: Error - can't forward packet: " - "incoming iface not specified\n"); ++ pr_err("Error - can't forward packet: incoming iface not " ++ "specified\n"); + return; + } + + if (forw_packet->if_incoming->if_status != IF_ACTIVE) + return; + + /* multihomed peer assumed */ + /* non-primary OGMs are only broadcasted on their interface */ + if ((directlink && (batman_packet->ttl == 1)) || + (forw_packet->own && (forw_packet->if_incoming->if_num > 0))) { + + /* FIXME: what about aggregated packets ? */ - bat_dbg(DBG_BATMAN, ++ bat_dbg(DBG_BATMAN, bat_priv, + "%s packet (originator %pM, seqno %d, TTL %d) " + "on interface %s [%s]\n", + (forw_packet->own ? "Sending own" : "Forwarding"), + batman_packet->orig, ntohl(batman_packet->seqno), + batman_packet->ttl, forw_packet->if_incoming->dev, + forw_packet->if_incoming->addr_str); + + send_raw_packet(forw_packet->packet_buff, + forw_packet->packet_len, + forw_packet->if_incoming, + broadcast_addr); + return; + } + + /* broadcast on every interface */ + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) + send_packet_to_if(forw_packet, batman_if); + rcu_read_unlock(); +} + +static void rebuild_batman_packet(struct batman_if *batman_if) +{ + int new_len; + unsigned char *new_buff; + struct batman_packet *batman_packet; + + new_len = sizeof(struct batman_packet) + (num_hna * ETH_ALEN); + new_buff = kmalloc(new_len, GFP_ATOMIC); + + /* keep old buffer if kmalloc should fail */ + if (new_buff) { + memcpy(new_buff, batman_if->packet_buff, + sizeof(struct batman_packet)); + batman_packet = (struct batman_packet *)new_buff; + + batman_packet->num_hna = hna_local_fill_buffer( + new_buff + sizeof(struct batman_packet), + new_len - sizeof(struct batman_packet)); + + kfree(batman_if->packet_buff); + batman_if->packet_buff = new_buff; + batman_if->packet_len = new_len; + } +} + +void schedule_own_packet(struct batman_if *batman_if) +{ + /* FIXME: each batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + unsigned long send_time; + struct batman_packet *batman_packet; + int vis_server; + + if ((batman_if->if_status == IF_NOT_IN_USE) || + (batman_if->if_status == IF_TO_BE_REMOVED)) + return; + + vis_server = atomic_read(&bat_priv->vis_mode); + + /** + * the interface gets activated here to avoid race conditions between + * the moment of activating the interface in + * hardif_activate_interface() where the originator mac is set and + * outdated packets (especially uninitialized mac addresses) in the + * packet queue + */ + if (batman_if->if_status == IF_TO_BE_ACTIVATED) + batman_if->if_status = IF_ACTIVE; + + /* if local hna has changed and interface is a primary interface */ + if ((atomic_read(&hna_local_changed)) && + (batman_if == bat_priv->primary_if)) + rebuild_batman_packet(batman_if); + + /** + * NOTE: packet_buff might just have been re-allocated in + * rebuild_batman_packet() + */ + batman_packet = (struct batman_packet *)batman_if->packet_buff; + + /* change sequence number to network order */ + batman_packet->seqno = + htonl((uint32_t)atomic_read(&batman_if->seqno)); + + if (vis_server == VIS_TYPE_SERVER_SYNC) + batman_packet->flags |= VIS_SERVER; + else + batman_packet->flags &= ~VIS_SERVER; + + atomic_inc(&batman_if->seqno); + + slide_own_bcast_window(batman_if); + send_time = own_send_time(bat_priv); + add_bat_packet_to_list(bat_priv, + batman_if->packet_buff, + batman_if->packet_len, + batman_if, 1, send_time); +} + +void schedule_forward_packet(struct orig_node *orig_node, + struct ethhdr *ethhdr, + struct batman_packet *batman_packet, + uint8_t directlink, int hna_buff_len, + struct batman_if *if_incoming) +{ + /* FIXME: each batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + unsigned char in_tq, in_ttl, tq_avg = 0; + unsigned long send_time; + + if (batman_packet->ttl <= 1) { - bat_dbg(DBG_BATMAN, "ttl exceeded\n"); ++ bat_dbg(DBG_BATMAN, bat_priv, "ttl exceeded\n"); + return; + } + + in_tq = batman_packet->tq; + in_ttl = batman_packet->ttl; + + batman_packet->ttl--; + memcpy(batman_packet->prev_sender, ethhdr->h_source, ETH_ALEN); + + /* rebroadcast tq of our best ranking neighbor to ensure the rebroadcast + * of our best tq value */ + if ((orig_node->router) && (orig_node->router->tq_avg != 0)) { + + /* rebroadcast ogm of best ranking neighbor as is */ + if (!compare_orig(orig_node->router->addr, ethhdr->h_source)) { + batman_packet->tq = orig_node->router->tq_avg; + + if (orig_node->router->last_ttl) + batman_packet->ttl = orig_node->router->last_ttl + - 1; + } + + tq_avg = orig_node->router->tq_avg; + } + + /* apply hop penalty */ + batman_packet->tq = hop_penalty(batman_packet->tq); + - bat_dbg(DBG_BATMAN, "Forwarding packet: tq_orig: %i, tq_avg: %i, " ++ bat_dbg(DBG_BATMAN, bat_priv, ++ "Forwarding packet: tq_orig: %i, tq_avg: %i, " + "tq_forw: %i, ttl_orig: %i, ttl_forw: %i\n", + in_tq, tq_avg, batman_packet->tq, in_ttl - 1, + batman_packet->ttl); + + batman_packet->seqno = htonl(batman_packet->seqno); + + /* switch of primaries first hop flag when forwarding */ + batman_packet->flags &= ~PRIMARIES_FIRST_HOP; + if (directlink) + batman_packet->flags |= DIRECTLINK; + else + batman_packet->flags &= ~DIRECTLINK; + + send_time = forward_send_time(bat_priv); + add_bat_packet_to_list(bat_priv, + (unsigned char *)batman_packet, + sizeof(struct batman_packet) + hna_buff_len, + if_incoming, 0, send_time); +} + +static void forw_packet_free(struct forw_packet *forw_packet) +{ + if (forw_packet->skb) + kfree_skb(forw_packet->skb); + kfree(forw_packet->packet_buff); + kfree(forw_packet); +} + +static void _add_bcast_packet_to_list(struct forw_packet *forw_packet, + unsigned long send_time) +{ + unsigned long flags; + INIT_HLIST_NODE(&forw_packet->list); + + /* add new packet to packet list */ + spin_lock_irqsave(&forw_bcast_list_lock, flags); + hlist_add_head(&forw_packet->list, &forw_bcast_list); + spin_unlock_irqrestore(&forw_bcast_list_lock, flags); + + /* start timer for this packet */ + INIT_DELAYED_WORK(&forw_packet->delayed_work, + send_outstanding_bcast_packet); + queue_delayed_work(bat_event_workqueue, &forw_packet->delayed_work, + send_time); +} + +#define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0) +/* add a broadcast packet to the queue and setup timers. broadcast packets + * are sent multiple times to increase probability for beeing received. + * + * This function returns NETDEV_TX_OK on success and NETDEV_TX_BUSY on + * errors. + * + * The skb is not consumed, so the caller should make sure that the + * skb is freed. */ +int add_bcast_packet_to_list(struct sk_buff *skb) +{ + struct forw_packet *forw_packet; + struct bcast_packet *bcast_packet; ++ /* FIXME: each batman_if will be attached to a softif */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); + + if (!atomic_dec_not_zero(&bcast_queue_left)) { - bat_dbg(DBG_BATMAN, "bcast packet queue full\n"); ++ bat_dbg(DBG_BATMAN, bat_priv, "bcast packet queue full\n"); + goto out; + } + + forw_packet = kmalloc(sizeof(struct forw_packet), GFP_ATOMIC); + + if (!forw_packet) + goto out_and_inc; + + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + goto packet_free; + + /* as we have a copy now, it is safe to decrease the TTL */ + bcast_packet = (struct bcast_packet *)skb->data; + bcast_packet->ttl--; + + skb_reset_mac_header(skb); + + forw_packet->skb = skb; + forw_packet->packet_buff = NULL; + + /* how often did we send the bcast packet ? */ + forw_packet->num_packets = 0; + + _add_bcast_packet_to_list(forw_packet, 1); + return NETDEV_TX_OK; + +packet_free: + kfree(forw_packet); +out_and_inc: + atomic_inc(&bcast_queue_left); +out: + return NETDEV_TX_BUSY; +} + +static void send_outstanding_bcast_packet(struct work_struct *work) +{ + struct batman_if *batman_if; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct forw_packet *forw_packet = + container_of(delayed_work, struct forw_packet, delayed_work); + unsigned long flags; + struct sk_buff *skb1; + + spin_lock_irqsave(&forw_bcast_list_lock, flags); + hlist_del(&forw_packet->list); + spin_unlock_irqrestore(&forw_bcast_list_lock, flags); + + if (atomic_read(&module_state) == MODULE_DEACTIVATING) + goto out; + + /* rebroadcast packet */ + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) { + /* send a copy of the saved skb */ + skb1 = skb_copy(forw_packet->skb, GFP_ATOMIC); + if (skb1) + send_skb_packet(skb1, + batman_if, broadcast_addr); + } + rcu_read_unlock(); + + forw_packet->num_packets++; + + /* if we still have some more bcasts to send */ + if (forw_packet->num_packets < 3) { + _add_bcast_packet_to_list(forw_packet, ((5 * HZ) / 1000)); + return; + } + +out: + forw_packet_free(forw_packet); + atomic_inc(&bcast_queue_left); +} + +void send_outstanding_bat_packet(struct work_struct *work) +{ + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct forw_packet *forw_packet = + container_of(delayed_work, struct forw_packet, delayed_work); + unsigned long flags; + + spin_lock_irqsave(&forw_bat_list_lock, flags); + hlist_del(&forw_packet->list); + spin_unlock_irqrestore(&forw_bat_list_lock, flags); + + if (atomic_read(&module_state) == MODULE_DEACTIVATING) + goto out; + + send_packet(forw_packet); + + /** + * we have to have at least one packet in the queue + * to determine the queues wake up time unless we are + * shutting down + */ + if (forw_packet->own) + schedule_own_packet(forw_packet->if_incoming); + +out: + /* don't count own packet */ + if (!forw_packet->own) + atomic_inc(&batman_queue_left); + + forw_packet_free(forw_packet); +} + +void purge_outstanding_packets(struct batman_if *batman_if) +{ ++ /* FIXME: each batman_if will be attached to a softif */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); + struct forw_packet *forw_packet; + struct hlist_node *tmp_node, *safe_tmp_node; + unsigned long flags; + + if (batman_if) - bat_dbg(DBG_BATMAN, "purge_outstanding_packets(): %s\n", ++ bat_dbg(DBG_BATMAN, bat_priv, ++ "purge_outstanding_packets(): %s\n", + batman_if->dev); + else - bat_dbg(DBG_BATMAN, "purge_outstanding_packets()\n"); ++ bat_dbg(DBG_BATMAN, bat_priv, ++ "purge_outstanding_packets()\n"); + + /* free bcast list */ + spin_lock_irqsave(&forw_bcast_list_lock, flags); + hlist_for_each_entry_safe(forw_packet, tmp_node, safe_tmp_node, + &forw_bcast_list, list) { + + /** + * if purge_outstanding_packets() was called with an argmument + * we delete only packets belonging to the given interface + */ + if ((batman_if) && + (forw_packet->if_incoming != batman_if)) + continue; + + spin_unlock_irqrestore(&forw_bcast_list_lock, flags); + + /** + * send_outstanding_bcast_packet() will lock the list to + * delete the item from the list + */ + cancel_delayed_work_sync(&forw_packet->delayed_work); + spin_lock_irqsave(&forw_bcast_list_lock, flags); + } + spin_unlock_irqrestore(&forw_bcast_list_lock, flags); + + /* free batman packet list */ + spin_lock_irqsave(&forw_bat_list_lock, flags); + hlist_for_each_entry_safe(forw_packet, tmp_node, safe_tmp_node, + &forw_bat_list, list) { + + /** + * if purge_outstanding_packets() was called with an argmument + * we delete only packets belonging to the given interface + */ + if ((batman_if) && + (forw_packet->if_incoming != batman_if)) + continue; + + spin_unlock_irqrestore(&forw_bat_list_lock, flags); + + /** + * send_outstanding_bat_packet() will lock the list to + * delete the item from the list + */ + cancel_delayed_work_sync(&forw_packet->delayed_work); + spin_lock_irqsave(&forw_bat_list_lock, flags); + } + spin_unlock_irqrestore(&forw_bat_list_lock, flags); +} diff --combined drivers/staging/batman-adv/translation-table.c index 9e3c845,0000000..b233377 mode 100644,000000..100644 --- a/drivers/staging/batman-adv/translation-table.c +++ b/drivers/staging/batman-adv/translation-table.c @@@ -1,498 -1,0 +1,505 @@@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "translation-table.h" +#include "soft-interface.h" +#include "types.h" +#include "hash.h" + +struct hashtable_t *hna_local_hash; +static struct hashtable_t *hna_global_hash; +atomic_t hna_local_changed; + +DEFINE_SPINLOCK(hna_local_hash_lock); +static DEFINE_SPINLOCK(hna_global_hash_lock); + +static void hna_local_purge(struct work_struct *work); +static DECLARE_DELAYED_WORK(hna_local_purge_wq, hna_local_purge); +static void _hna_global_del_orig(struct hna_global_entry *hna_global_entry, + char *message); + +static void hna_local_start_timer(void) +{ + queue_delayed_work(bat_event_workqueue, &hna_local_purge_wq, 10 * HZ); +} + +int hna_local_init(void) +{ + if (hna_local_hash) + return 1; + + hna_local_hash = hash_new(128, compare_orig, choose_orig); + + if (!hna_local_hash) + return 0; + + atomic_set(&hna_local_changed, 0); + hna_local_start_timer(); + + return 1; +} + +void hna_local_add(uint8_t *addr) +{ ++ /* FIXME: each orig_node->batman_if will be attached to a softif */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); + struct hna_local_entry *hna_local_entry; + struct hna_global_entry *hna_global_entry; + struct hashtable_t *swaphash; + unsigned long flags; + + spin_lock_irqsave(&hna_local_hash_lock, flags); + hna_local_entry = + ((struct hna_local_entry *)hash_find(hna_local_hash, addr)); + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + + if (hna_local_entry != NULL) { + hna_local_entry->last_seen = jiffies; + return; + } + + /* only announce as many hosts as possible in the batman-packet and + space in batman_packet->num_hna That also should give a limit to + MAC-flooding. */ + if ((num_hna + 1 > (ETH_DATA_LEN - BAT_PACKET_LEN) / ETH_ALEN) || + (num_hna + 1 > 255)) { - bat_dbg(DBG_ROUTES, ++ bat_dbg(DBG_ROUTES, bat_priv, + "Can't add new local hna entry (%pM): " + "number of local hna entries exceeds packet size\n", + addr); + return; + } + - bat_dbg(DBG_ROUTES, "Creating new local hna entry: %pM\n", - addr); ++ bat_dbg(DBG_ROUTES, bat_priv, ++ "Creating new local hna entry: %pM\n", addr); + + hna_local_entry = kmalloc(sizeof(struct hna_local_entry), GFP_ATOMIC); + if (!hna_local_entry) + return; + + memcpy(hna_local_entry->addr, addr, ETH_ALEN); + hna_local_entry->last_seen = jiffies; + + /* the batman interface mac address should never be purged */ + if (compare_orig(addr, soft_device->dev_addr)) + hna_local_entry->never_purge = 1; + else + hna_local_entry->never_purge = 0; + + spin_lock_irqsave(&hna_local_hash_lock, flags); + + hash_add(hna_local_hash, hna_local_entry); + num_hna++; + atomic_set(&hna_local_changed, 1); + + if (hna_local_hash->elements * 4 > hna_local_hash->size) { + swaphash = hash_resize(hna_local_hash, + hna_local_hash->size * 2); + + if (swaphash == NULL) - printk(KERN_ERR "batman-adv:" - "Couldn't resize local hna hash table\n"); ++ pr_err("Couldn't resize local hna hash table\n"); + else + hna_local_hash = swaphash; + } + + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + + /* remove address from global hash if present */ + spin_lock_irqsave(&hna_global_hash_lock, flags); + + hna_global_entry = + ((struct hna_global_entry *)hash_find(hna_global_hash, addr)); + + if (hna_global_entry != NULL) + _hna_global_del_orig(hna_global_entry, "local hna received"); + + spin_unlock_irqrestore(&hna_global_hash_lock, flags); +} + +int hna_local_fill_buffer(unsigned char *buff, int buff_len) +{ + struct hna_local_entry *hna_local_entry; + HASHIT(hashit); + int i = 0; + unsigned long flags; + + spin_lock_irqsave(&hna_local_hash_lock, flags); + + while (hash_iterate(hna_local_hash, &hashit)) { + + if (buff_len < (i + 1) * ETH_ALEN) + break; + + hna_local_entry = hashit.bucket->data; + memcpy(buff + (i * ETH_ALEN), hna_local_entry->addr, ETH_ALEN); + + i++; + } + + /* if we did not get all new local hnas see you next time ;-) */ + if (i == num_hna) + atomic_set(&hna_local_changed, 0); + + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + + return i; +} + +int hna_local_seq_print_text(struct seq_file *seq, void *offset) +{ + struct net_device *net_dev = (struct net_device *)seq->private; + struct bat_priv *bat_priv = netdev_priv(net_dev); + struct hna_local_entry *hna_local_entry; + HASHIT(hashit); + HASHIT(hashit_count); + unsigned long flags; + size_t buf_size, pos; + char *buff; + + if (!bat_priv->primary_if) { + return seq_printf(seq, "BATMAN mesh %s disabled - " + "please specify interfaces to enable it\n", + net_dev->name); + } + + seq_printf(seq, "Locally retrieved addresses (from %s) " + "announced via HNA:\n", + net_dev->name); + + spin_lock_irqsave(&hna_local_hash_lock, flags); + + buf_size = 1; + /* Estimate length for: " * xx:xx:xx:xx:xx:xx\n" */ + while (hash_iterate(hna_local_hash, &hashit_count)) + buf_size += 21; + + buff = kmalloc(buf_size, GFP_ATOMIC); + if (!buff) { + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + return -ENOMEM; + } + buff[0] = '\0'; + pos = 0; + + while (hash_iterate(hna_local_hash, &hashit)) { + hna_local_entry = hashit.bucket->data; + + pos += snprintf(buff + pos, 22, " * %pM\n", + hna_local_entry->addr); + } + + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + + seq_printf(seq, "%s", buff); + kfree(buff); + return 0; +} + +static void _hna_local_del(void *data) +{ + kfree(data); + num_hna--; + atomic_set(&hna_local_changed, 1); +} + +static void hna_local_del(struct hna_local_entry *hna_local_entry, + char *message) +{ - bat_dbg(DBG_ROUTES, "Deleting local hna entry (%pM): %s\n", ++ /* FIXME: each orig_node->batman_if will be attached to a softif */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); ++ bat_dbg(DBG_ROUTES, bat_priv, "Deleting local hna entry (%pM): %s\n", + hna_local_entry->addr, message); + + hash_remove(hna_local_hash, hna_local_entry->addr); + _hna_local_del(hna_local_entry); +} + +void hna_local_remove(uint8_t *addr, char *message) +{ + struct hna_local_entry *hna_local_entry; + unsigned long flags; + + spin_lock_irqsave(&hna_local_hash_lock, flags); + + hna_local_entry = (struct hna_local_entry *) + hash_find(hna_local_hash, addr); + if (hna_local_entry) + hna_local_del(hna_local_entry, message); + + spin_unlock_irqrestore(&hna_local_hash_lock, flags); +} + +static void hna_local_purge(struct work_struct *work) +{ + struct hna_local_entry *hna_local_entry; + HASHIT(hashit); + unsigned long flags; + unsigned long timeout; + + spin_lock_irqsave(&hna_local_hash_lock, flags); + + while (hash_iterate(hna_local_hash, &hashit)) { + hna_local_entry = hashit.bucket->data; + + timeout = hna_local_entry->last_seen + LOCAL_HNA_TIMEOUT * HZ; + if ((!hna_local_entry->never_purge) && + time_after(jiffies, timeout)) + hna_local_del(hna_local_entry, "address timed out"); + } + + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + hna_local_start_timer(); +} + +void hna_local_free(void) +{ + if (!hna_local_hash) + return; + + cancel_delayed_work_sync(&hna_local_purge_wq); + hash_delete(hna_local_hash, _hna_local_del); + hna_local_hash = NULL; +} + +int hna_global_init(void) +{ + if (hna_global_hash) + return 1; + + hna_global_hash = hash_new(128, compare_orig, choose_orig); + + if (!hna_global_hash) + return 0; + + return 1; +} + +void hna_global_add_orig(struct orig_node *orig_node, + unsigned char *hna_buff, int hna_buff_len) +{ ++ /* FIXME: each orig_node->batman_if will be attached to a softif */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); + struct hna_global_entry *hna_global_entry; + struct hna_local_entry *hna_local_entry; + struct hashtable_t *swaphash; + int hna_buff_count = 0; + unsigned long flags; + unsigned char *hna_ptr; + + while ((hna_buff_count + 1) * ETH_ALEN <= hna_buff_len) { + spin_lock_irqsave(&hna_global_hash_lock, flags); + + hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN); + hna_global_entry = (struct hna_global_entry *) + hash_find(hna_global_hash, hna_ptr); + + if (hna_global_entry == NULL) { + spin_unlock_irqrestore(&hna_global_hash_lock, flags); + + hna_global_entry = + kmalloc(sizeof(struct hna_global_entry), + GFP_ATOMIC); + + if (!hna_global_entry) + break; + + memcpy(hna_global_entry->addr, hna_ptr, ETH_ALEN); + - bat_dbg(DBG_ROUTES, ++ bat_dbg(DBG_ROUTES, bat_priv, + "Creating new global hna entry: " + "%pM (via %pM)\n", + hna_global_entry->addr, orig_node->orig); + + spin_lock_irqsave(&hna_global_hash_lock, flags); + hash_add(hna_global_hash, hna_global_entry); + + } + + hna_global_entry->orig_node = orig_node; + spin_unlock_irqrestore(&hna_global_hash_lock, flags); + + /* remove address from local hash if present */ + spin_lock_irqsave(&hna_local_hash_lock, flags); + + hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN); + hna_local_entry = (struct hna_local_entry *) + hash_find(hna_local_hash, hna_ptr); + + if (hna_local_entry != NULL) + hna_local_del(hna_local_entry, "global hna received"); + + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + + hna_buff_count++; + } + + /* initialize, and overwrite if malloc succeeds */ + orig_node->hna_buff = NULL; + orig_node->hna_buff_len = 0; + + if (hna_buff_len > 0) { + orig_node->hna_buff = kmalloc(hna_buff_len, GFP_ATOMIC); + if (orig_node->hna_buff) { + memcpy(orig_node->hna_buff, hna_buff, hna_buff_len); + orig_node->hna_buff_len = hna_buff_len; + } + } + + spin_lock_irqsave(&hna_global_hash_lock, flags); + + if (hna_global_hash->elements * 4 > hna_global_hash->size) { + swaphash = hash_resize(hna_global_hash, + hna_global_hash->size * 2); + + if (swaphash == NULL) - printk(KERN_ERR "batman-adv:" - "Couldn't resize global hna hash table\n"); ++ pr_err("Couldn't resize global hna hash table\n"); + else + hna_global_hash = swaphash; + } + + spin_unlock_irqrestore(&hna_global_hash_lock, flags); +} + +int hna_global_seq_print_text(struct seq_file *seq, void *offset) +{ + struct net_device *net_dev = (struct net_device *)seq->private; + struct bat_priv *bat_priv = netdev_priv(net_dev); + struct hna_global_entry *hna_global_entry; + HASHIT(hashit); + HASHIT(hashit_count); + unsigned long flags; + size_t buf_size, pos; + char *buff; + + if (!bat_priv->primary_if) { + return seq_printf(seq, "BATMAN mesh %s disabled - " + "please specify interfaces to enable it\n", + net_dev->name); + } + + seq_printf(seq, "Globally announced HNAs received via the mesh %s\n", + net_dev->name); + + spin_lock_irqsave(&hna_global_hash_lock, flags); + + buf_size = 1; + /* Estimate length for: " * xx:xx:xx:xx:xx:xx via xx:xx:xx:xx:xx:xx\n"*/ + while (hash_iterate(hna_global_hash, &hashit_count)) + buf_size += 43; + + buff = kmalloc(buf_size, GFP_ATOMIC); + if (!buff) { + spin_unlock_irqrestore(&hna_global_hash_lock, flags); + return -ENOMEM; + } + buff[0] = '\0'; + pos = 0; + + while (hash_iterate(hna_global_hash, &hashit)) { + hna_global_entry = hashit.bucket->data; + + pos += snprintf(buff + pos, 44, + " * %pM via %pM\n", hna_global_entry->addr, + hna_global_entry->orig_node->orig); + } + + spin_unlock_irqrestore(&hna_global_hash_lock, flags); + + seq_printf(seq, "%s", buff); + kfree(buff); + return 0; +} + +static void _hna_global_del_orig(struct hna_global_entry *hna_global_entry, + char *message) +{ - bat_dbg(DBG_ROUTES, "Deleting global hna entry %pM (via %pM): %s\n", ++ /* FIXME: each orig_node->batman_if will be attached to a softif */ ++ struct bat_priv *bat_priv = netdev_priv(soft_device); ++ bat_dbg(DBG_ROUTES, bat_priv, ++ "Deleting global hna entry %pM (via %pM): %s\n", + hna_global_entry->addr, hna_global_entry->orig_node->orig, + message); + + hash_remove(hna_global_hash, hna_global_entry->addr); + kfree(hna_global_entry); +} + +void hna_global_del_orig(struct orig_node *orig_node, char *message) +{ + struct hna_global_entry *hna_global_entry; + int hna_buff_count = 0; + unsigned long flags; + unsigned char *hna_ptr; + + if (orig_node->hna_buff_len == 0) + return; + + spin_lock_irqsave(&hna_global_hash_lock, flags); + + while ((hna_buff_count + 1) * ETH_ALEN <= orig_node->hna_buff_len) { + hna_ptr = orig_node->hna_buff + (hna_buff_count * ETH_ALEN); + hna_global_entry = (struct hna_global_entry *) + hash_find(hna_global_hash, hna_ptr); + + if ((hna_global_entry != NULL) && + (hna_global_entry->orig_node == orig_node)) + _hna_global_del_orig(hna_global_entry, message); + + hna_buff_count++; + } + + spin_unlock_irqrestore(&hna_global_hash_lock, flags); + + orig_node->hna_buff_len = 0; + kfree(orig_node->hna_buff); + orig_node->hna_buff = NULL; +} + +static void hna_global_del(void *data) +{ + kfree(data); +} + +void hna_global_free(void) +{ + if (!hna_global_hash) + return; + + hash_delete(hna_global_hash, hna_global_del); + hna_global_hash = NULL; +} + +struct orig_node *transtable_search(uint8_t *addr) +{ + struct hna_global_entry *hna_global_entry; + unsigned long flags; + + spin_lock_irqsave(&hna_global_hash_lock, flags); + hna_global_entry = (struct hna_global_entry *) + hash_find(hna_global_hash, addr); + spin_unlock_irqrestore(&hna_global_hash_lock, flags); + + if (hna_global_entry == NULL) + return NULL; + + return hna_global_entry->orig_node; +} diff --combined drivers/staging/batman-adv/types.h index 82602d0,21d0717..21d0717 --- a/drivers/staging/batman-adv/types.h +++ b/drivers/staging/batman-adv/types.h @@@ -21,8 -21,6 +21,6 @@@
- - #ifndef _NET_BATMAN_ADV_TYPES_H_ #define _NET_BATMAN_ADV_TYPES_H_
@@@ -114,7 -112,9 +112,9 @@@ struct bat_priv atomic_t bonding_enabled; atomic_t vis_mode; atomic_t orig_interval; + atomic_t log_level; char num_ifaces; + struct debug_log *debug_log; struct batman_if *primary_if; struct kobject *mesh_obj; struct dentry *debug_dir; @@@ -172,4 -172,12 +172,12 @@@ struct if_list_entry struct hlist_node list; };
+ struct debug_log { + char log_buff[LOG_BUF_LEN]; + unsigned long log_start; + unsigned long log_end; + spinlock_t lock; + wait_queue_head_t queue_wait; + }; + #endif /* _NET_BATMAN_ADV_TYPES_H_ */ diff --combined drivers/staging/batman-adv/vis.c index ddee0f2,0000000..4b6a504 mode 100644,000000..100644 --- a/drivers/staging/batman-adv/vis.c +++ b/drivers/staging/batman-adv/vis.c @@@ -1,818 -1,0 +1,817 @@@ +/* + * Copyright (C) 2008-2010 B.A.T.M.A.N. contributors: + * + * Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "send.h" +#include "translation-table.h" +#include "vis.h" +#include "soft-interface.h" +#include "hard-interface.h" +#include "hash.h" + +/* Returns the smallest signed integer in two's complement with the sizeof x */ +#define smallest_signed_int(x) (1u << (7u + 8u * (sizeof(x) - 1u))) + +/* Checks if a sequence number x is a predecessor/successor of y. + they handle overflows/underflows and can correctly check for a + predecessor/successor unless the variable sequence number has grown by + more then 2**(bitwidth(x)-1)-1. + This means that for a uint8_t with the maximum value 255, it would think: + * when adding nothing - it is neither a predecessor nor a successor + * before adding more than 127 to the starting value - it is a predecessor, + * when adding 128 - it is neither a predecessor nor a successor, + * after adding more than 127 to the starting value - it is a successor */ +#define seq_before(x, y) ({typeof(x) _dummy = (x - y); \ + _dummy > smallest_signed_int(_dummy); }) +#define seq_after(x, y) seq_before(y, x) + +static struct hashtable_t *vis_hash; +static DEFINE_SPINLOCK(vis_hash_lock); +static DEFINE_SPINLOCK(recv_list_lock); +static struct vis_info *my_vis_info; +static struct list_head send_list; /* always locked with vis_hash_lock */ + +static void start_vis_timer(void); + +/* free the info */ +static void free_info(struct kref *ref) +{ + struct vis_info *info = container_of(ref, struct vis_info, refcount); + struct recvlist_node *entry, *tmp; + unsigned long flags; + + list_del_init(&info->send_list); + spin_lock_irqsave(&recv_list_lock, flags); + list_for_each_entry_safe(entry, tmp, &info->recv_list, list) { + list_del(&entry->list); + kfree(entry); + } + spin_unlock_irqrestore(&recv_list_lock, flags); + kfree(info); +} + +/* Compare two vis packets, used by the hashing algorithm */ +static int vis_info_cmp(void *data1, void *data2) +{ + struct vis_info *d1, *d2; + d1 = data1; + d2 = data2; + return compare_orig(d1->packet.vis_orig, d2->packet.vis_orig); +} + +/* hash function to choose an entry in a hash table of given size */ +/* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ +static int vis_info_choose(void *data, int size) +{ + struct vis_info *vis_info = data; + unsigned char *key; + uint32_t hash = 0; + size_t i; + + key = vis_info->packet.vis_orig; + for (i = 0; i < ETH_ALEN; i++) { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash % size; +} + +/* insert interface to the list of interfaces of one originator, if it + * does not already exist in the list */ +static void vis_data_insert_interface(const uint8_t *interface, + struct hlist_head *if_list, + bool primary) +{ + struct if_list_entry *entry; + struct hlist_node *pos; + + hlist_for_each_entry(entry, pos, if_list, list) { + if (compare_orig(entry->addr, (void *)interface)) + return; + } + + /* its a new address, add it to the list */ + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) + return; + memcpy(entry->addr, interface, ETH_ALEN); + entry->primary = primary; + hlist_add_head(&entry->list, if_list); +} + +static ssize_t vis_data_read_prim_sec(char *buff, struct hlist_head *if_list) +{ + struct if_list_entry *entry; + struct hlist_node *pos; + char tmp_addr_str[ETH_STR_LEN]; + size_t len = 0; + + hlist_for_each_entry(entry, pos, if_list, list) { + if (entry->primary) + len += sprintf(buff + len, "PRIMARY, "); + else { + addr_to_string(tmp_addr_str, entry->addr); + len += sprintf(buff + len, "SEC %s, ", tmp_addr_str); + } + } + + return len; +} + +static size_t vis_data_count_prim_sec(struct hlist_head *if_list) +{ + struct if_list_entry *entry; + struct hlist_node *pos; + size_t count = 0; + + hlist_for_each_entry(entry, pos, if_list, list) { + if (entry->primary) + count += 9; + else + count += 23; + } + + return count; +} + +/* read an entry */ +static ssize_t vis_data_read_entry(char *buff, struct vis_info_entry *entry, + uint8_t *src, bool primary) +{ + char to[18]; + + /* maximal length: max(4+17+2, 3+17+1+3+2) == 26 */ + addr_to_string(to, entry->dest); + if (primary && entry->quality == 0) + return sprintf(buff, "HNA %s, ", to); + else if (compare_orig(entry->src, src)) + return sprintf(buff, "TQ %s %d, ", to, entry->quality); + + return 0; +} + +int vis_seq_print_text(struct seq_file *seq, void *offset) +{ + HASHIT(hashit); + HASHIT(hashit_count); + struct vis_info *info; + struct vis_info_entry *entries; + struct net_device *net_dev = (struct net_device *)seq->private; + struct bat_priv *bat_priv = netdev_priv(net_dev); + HLIST_HEAD(vis_if_list); + struct if_list_entry *entry; + struct hlist_node *pos, *n; + int i; + char tmp_addr_str[ETH_STR_LEN]; + unsigned long flags; + int vis_server = atomic_read(&bat_priv->vis_mode); + size_t buff_pos, buf_size; + char *buff; + + if ((!bat_priv->primary_if) || + (vis_server == VIS_TYPE_CLIENT_UPDATE)) + return 0; + + buf_size = 1; + /* Estimate length */ + spin_lock_irqsave(&vis_hash_lock, flags); + while (hash_iterate(vis_hash, &hashit_count)) { + info = hashit_count.bucket->data; + entries = (struct vis_info_entry *) + ((char *)info + sizeof(struct vis_info)); + + for (i = 0; i < info->packet.entries; i++) { + if (entries[i].quality == 0) + continue; + vis_data_insert_interface(entries[i].src, &vis_if_list, + compare_orig(entries[i].src, + info->packet.vis_orig)); + } + + hlist_for_each_entry(entry, pos, &vis_if_list, list) { + buf_size += 18 + 26 * info->packet.entries; + + /* add primary/secondary records */ + if (compare_orig(entry->addr, info->packet.vis_orig)) + buf_size += + vis_data_count_prim_sec(&vis_if_list); + + buf_size += 1; + } + + hlist_for_each_entry_safe(entry, pos, n, &vis_if_list, list) { + hlist_del(&entry->list); + kfree(entry); + } + } + + buff = kmalloc(buf_size, GFP_ATOMIC); + if (!buff) { + spin_unlock_irqrestore(&vis_hash_lock, flags); + return -ENOMEM; + } + buff[0] = '\0'; + buff_pos = 0; + + while (hash_iterate(vis_hash, &hashit)) { + info = hashit.bucket->data; + entries = (struct vis_info_entry *) + ((char *)info + sizeof(struct vis_info)); + + for (i = 0; i < info->packet.entries; i++) { + if (entries[i].quality == 0) + continue; + vis_data_insert_interface(entries[i].src, &vis_if_list, + compare_orig(entries[i].src, + info->packet.vis_orig)); + } + + hlist_for_each_entry(entry, pos, &vis_if_list, list) { + addr_to_string(tmp_addr_str, entry->addr); + buff_pos += sprintf(buff + buff_pos, "%s,", + tmp_addr_str); + + for (i = 0; i < info->packet.entries; i++) + buff_pos += vis_data_read_entry(buff + buff_pos, + &entries[i], + entry->addr, + entry->primary); + + /* add primary/secondary records */ + if (compare_orig(entry->addr, info->packet.vis_orig)) + buff_pos += + vis_data_read_prim_sec(buff + buff_pos, + &vis_if_list); + + buff_pos += sprintf(buff + buff_pos, "\n"); + } + + hlist_for_each_entry_safe(entry, pos, n, &vis_if_list, list) { + hlist_del(&entry->list); + kfree(entry); + } + } + + spin_unlock_irqrestore(&vis_hash_lock, flags); + + seq_printf(seq, "%s", buff); + kfree(buff); + + return 0; +} + +/* add the info packet to the send list, if it was not + * already linked in. */ +static void send_list_add(struct vis_info *info) +{ + if (list_empty(&info->send_list)) { + kref_get(&info->refcount); + list_add_tail(&info->send_list, &send_list); + } +} + +/* delete the info packet from the send list, if it was + * linked in. */ +static void send_list_del(struct vis_info *info) +{ + if (!list_empty(&info->send_list)) { + list_del_init(&info->send_list); + kref_put(&info->refcount, free_info); + } +} + +/* tries to add one entry to the receive list. */ +static void recv_list_add(struct list_head *recv_list, char *mac) +{ + struct recvlist_node *entry; + unsigned long flags; + + entry = kmalloc(sizeof(struct recvlist_node), GFP_ATOMIC); + if (!entry) + return; + + memcpy(entry->mac, mac, ETH_ALEN); + spin_lock_irqsave(&recv_list_lock, flags); + list_add_tail(&entry->list, recv_list); + spin_unlock_irqrestore(&recv_list_lock, flags); +} + +/* returns 1 if this mac is in the recv_list */ +static int recv_list_is_in(struct list_head *recv_list, char *mac) +{ + struct recvlist_node *entry; + unsigned long flags; + + spin_lock_irqsave(&recv_list_lock, flags); + list_for_each_entry(entry, recv_list, list) { + if (memcmp(entry->mac, mac, ETH_ALEN) == 0) { + spin_unlock_irqrestore(&recv_list_lock, flags); + return 1; + } + } + spin_unlock_irqrestore(&recv_list_lock, flags); + return 0; +} + +/* try to add the packet to the vis_hash. return NULL if invalid (e.g. too old, + * broken.. ). vis hash must be locked outside. is_new is set when the packet + * is newer than old entries in the hash. */ +static struct vis_info *add_packet(struct vis_packet *vis_packet, + int vis_info_len, int *is_new, + int make_broadcast) +{ + struct vis_info *info, *old_info; + struct vis_info search_elem; + + *is_new = 0; + /* sanity check */ + if (vis_hash == NULL) + return NULL; + + /* see if the packet is already in vis_hash */ + memcpy(search_elem.packet.vis_orig, vis_packet->vis_orig, ETH_ALEN); + old_info = hash_find(vis_hash, &search_elem); + + if (old_info != NULL) { + if (!seq_after(ntohl(vis_packet->seqno), + ntohl(old_info->packet.seqno))) { + if (old_info->packet.seqno == vis_packet->seqno) { + recv_list_add(&old_info->recv_list, + vis_packet->sender_orig); + return old_info; + } else { + /* newer packet is already in hash. */ + return NULL; + } + } + /* remove old entry */ + hash_remove(vis_hash, old_info); + send_list_del(old_info); + kref_put(&old_info->refcount, free_info); + } + + info = kmalloc(sizeof(struct vis_info) + vis_info_len, GFP_ATOMIC); + if (info == NULL) + return NULL; + + kref_init(&info->refcount); + INIT_LIST_HEAD(&info->send_list); + INIT_LIST_HEAD(&info->recv_list); + info->first_seen = jiffies; + memcpy(&info->packet, vis_packet, + sizeof(struct vis_packet) + vis_info_len); + + /* initialize and add new packet. */ + *is_new = 1; + + /* Make it a broadcast packet, if required */ + if (make_broadcast) + memcpy(info->packet.target_orig, broadcast_addr, ETH_ALEN); + + /* repair if entries is longer than packet. */ + if (info->packet.entries * sizeof(struct vis_info_entry) > vis_info_len) + info->packet.entries = vis_info_len / + sizeof(struct vis_info_entry); + + recv_list_add(&info->recv_list, info->packet.sender_orig); + + /* try to add it */ + if (hash_add(vis_hash, info) < 0) { + /* did not work (for some reason) */ + kref_put(&old_info->refcount, free_info); + info = NULL; + } + + return info; +} + +/* handle the server sync packet, forward if needed. */ +void receive_server_sync_packet(struct bat_priv *bat_priv, + struct vis_packet *vis_packet, + int vis_info_len) +{ + struct vis_info *info; + int is_new, make_broadcast; + unsigned long flags; + int vis_server = atomic_read(&bat_priv->vis_mode); + + make_broadcast = (vis_server == VIS_TYPE_SERVER_SYNC); + + spin_lock_irqsave(&vis_hash_lock, flags); + info = add_packet(vis_packet, vis_info_len, &is_new, make_broadcast); + if (info == NULL) + goto end; + + /* only if we are server ourselves and packet is newer than the one in + * hash.*/ + if (vis_server == VIS_TYPE_SERVER_SYNC && is_new) + send_list_add(info); +end: + spin_unlock_irqrestore(&vis_hash_lock, flags); +} + +/* handle an incoming client update packet and schedule forward if needed. */ +void receive_client_update_packet(struct bat_priv *bat_priv, + struct vis_packet *vis_packet, + int vis_info_len) +{ + struct vis_info *info; + int is_new; + unsigned long flags; + int vis_server = atomic_read(&bat_priv->vis_mode); + int are_target = 0; + + /* clients shall not broadcast. */ + if (is_bcast(vis_packet->target_orig)) + return; + + /* Are we the target for this VIS packet? */ + if (vis_server == VIS_TYPE_SERVER_SYNC && + is_my_mac(vis_packet->target_orig)) + are_target = 1; + + spin_lock_irqsave(&vis_hash_lock, flags); + info = add_packet(vis_packet, vis_info_len, &is_new, are_target); + if (info == NULL) + goto end; + /* note that outdated packets will be dropped at this point. */ + + + /* send only if we're the target server or ... */ + if (are_target && is_new) { + info->packet.vis_type = VIS_TYPE_SERVER_SYNC; /* upgrade! */ + send_list_add(info); + + /* ... we're not the recipient (and thus need to forward). */ + } else if (!is_my_mac(info->packet.target_orig)) { + send_list_add(info); + } +end: + spin_unlock_irqrestore(&vis_hash_lock, flags); +} + +/* Walk the originators and find the VIS server with the best tq. Set the packet + * address to its address and return the best_tq. + * + * Must be called with the originator hash locked */ +static int find_best_vis_server(struct vis_info *info) +{ + HASHIT(hashit); + struct orig_node *orig_node; + int best_tq = -1; + + while (hash_iterate(orig_hash, &hashit)) { + orig_node = hashit.bucket->data; + if ((orig_node != NULL) && + (orig_node->router != NULL) && + (orig_node->flags & VIS_SERVER) && + (orig_node->router->tq_avg > best_tq)) { + best_tq = orig_node->router->tq_avg; + memcpy(info->packet.target_orig, orig_node->orig, + ETH_ALEN); + } + } + return best_tq; +} + +/* Return true if the vis packet is full. */ +static bool vis_packet_full(struct vis_info *info) +{ + if (info->packet.entries + 1 > + (1000 - sizeof(struct vis_info)) / sizeof(struct vis_info_entry)) + return true; + return false; +} + +/* generates a packet of own vis data, + * returns 0 on success, -1 if no packet could be generated */ +static int generate_vis_packet(struct bat_priv *bat_priv) +{ + HASHIT(hashit_local); + HASHIT(hashit_global); + struct orig_node *orig_node; + struct vis_info *info = (struct vis_info *)my_vis_info; + struct vis_info_entry *entry, *entry_array; + struct hna_local_entry *hna_local_entry; + int best_tq = -1; + unsigned long flags; + + info->first_seen = jiffies; + info->packet.vis_type = atomic_read(&bat_priv->vis_mode); + + spin_lock_irqsave(&orig_hash_lock, flags); + memcpy(info->packet.target_orig, broadcast_addr, ETH_ALEN); + info->packet.ttl = TTL; + info->packet.seqno = htonl(ntohl(info->packet.seqno) + 1); + info->packet.entries = 0; + + if (info->packet.vis_type == VIS_TYPE_CLIENT_UPDATE) { + best_tq = find_best_vis_server(info); + if (best_tq < 0) { + spin_unlock_irqrestore(&orig_hash_lock, flags); + return -1; + } + } + + entry_array = (struct vis_info_entry *) + ((char *)info + sizeof(struct vis_info)); + + while (hash_iterate(orig_hash, &hashit_global)) { + orig_node = hashit_global.bucket->data; + if (orig_node->router != NULL + && compare_orig(orig_node->router->addr, + orig_node->orig) + && (orig_node->router->if_incoming->if_status == + IF_ACTIVE) + && orig_node->router->tq_avg > 0) { + + /* fill one entry into buffer. */ + entry = &entry_array[info->packet.entries]; + memcpy(entry->src, + orig_node->router->if_incoming->net_dev->dev_addr, + ETH_ALEN); + memcpy(entry->dest, orig_node->orig, ETH_ALEN); + entry->quality = orig_node->router->tq_avg; + info->packet.entries++; + + if (vis_packet_full(info)) { + spin_unlock_irqrestore(&orig_hash_lock, flags); + return 0; + } + } + } + + spin_unlock_irqrestore(&orig_hash_lock, flags); + + spin_lock_irqsave(&hna_local_hash_lock, flags); + while (hash_iterate(hna_local_hash, &hashit_local)) { + hna_local_entry = hashit_local.bucket->data; + entry = &entry_array[info->packet.entries]; + memset(entry->src, 0, ETH_ALEN); + memcpy(entry->dest, hna_local_entry->addr, ETH_ALEN); + entry->quality = 0; /* 0 means HNA */ + info->packet.entries++; + + if (vis_packet_full(info)) { + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + return 0; + } + } + spin_unlock_irqrestore(&hna_local_hash_lock, flags); + return 0; +} + +/* free old vis packets. Must be called with this vis_hash_lock + * held */ +static void purge_vis_packets(void) +{ + HASHIT(hashit); + struct vis_info *info; + + while (hash_iterate(vis_hash, &hashit)) { + info = hashit.bucket->data; + if (info == my_vis_info) /* never purge own data. */ + continue; + if (time_after(jiffies, + info->first_seen + VIS_TIMEOUT * HZ)) { + hash_remove_bucket(vis_hash, &hashit); + send_list_del(info); + kref_put(&info->refcount, free_info); + } + } +} + +static void broadcast_vis_packet(struct vis_info *info, int packet_length) +{ + HASHIT(hashit); + struct orig_node *orig_node; + unsigned long flags; + struct batman_if *batman_if; + uint8_t dstaddr[ETH_ALEN]; + + spin_lock_irqsave(&orig_hash_lock, flags); + + /* send to all routers in range. */ + while (hash_iterate(orig_hash, &hashit)) { + orig_node = hashit.bucket->data; + + /* if it's a vis server and reachable, send it. */ + if ((!orig_node) || (!orig_node->router)) + continue; + if (!(orig_node->flags & VIS_SERVER)) + continue; + /* don't send it if we already received the packet from + * this node. */ + if (recv_list_is_in(&info->recv_list, orig_node->orig)) + continue; + + memcpy(info->packet.target_orig, orig_node->orig, ETH_ALEN); + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + send_raw_packet((unsigned char *)&info->packet, + packet_length, batman_if, dstaddr); + + spin_lock_irqsave(&orig_hash_lock, flags); + + } + spin_unlock_irqrestore(&orig_hash_lock, flags); + memcpy(info->packet.target_orig, broadcast_addr, ETH_ALEN); +} + +static void unicast_vis_packet(struct vis_info *info, int packet_length) +{ + struct orig_node *orig_node; + unsigned long flags; + struct batman_if *batman_if; + uint8_t dstaddr[ETH_ALEN]; + + spin_lock_irqsave(&orig_hash_lock, flags); + orig_node = ((struct orig_node *) + hash_find(orig_hash, info->packet.target_orig)); + + if ((!orig_node) || (!orig_node->router)) + goto out; + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + send_raw_packet((unsigned char *)&info->packet, + packet_length, batman_if, dstaddr); + return; + +out: + spin_unlock_irqrestore(&orig_hash_lock, flags); +} + +/* only send one vis packet. called from send_vis_packets() */ +static void send_vis_packet(struct vis_info *info) +{ + int packet_length; + + if (info->packet.ttl < 2) { - printk(KERN_WARNING "batman-adv: Error - can't send vis packet: ttl exceeded\n"); ++ pr_warning("Error - can't send vis packet: ttl exceeded\n"); + return; + } + + memcpy(info->packet.sender_orig, main_if_addr, ETH_ALEN); + info->packet.ttl--; + + packet_length = sizeof(struct vis_packet) + + info->packet.entries * sizeof(struct vis_info_entry); + + if (is_bcast(info->packet.target_orig)) + broadcast_vis_packet(info, packet_length); + else + unicast_vis_packet(info, packet_length); + info->packet.ttl++; /* restore TTL */ +} + +/* called from timer; send (and maybe generate) vis packet. */ +static void send_vis_packets(struct work_struct *work) +{ + struct vis_info *info, *temp; + unsigned long flags; + /* FIXME: each batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + + spin_lock_irqsave(&vis_hash_lock, flags); + + purge_vis_packets(); + + if (generate_vis_packet(bat_priv) == 0) { + /* schedule if generation was successful */ + send_list_add(my_vis_info); + } + + list_for_each_entry_safe(info, temp, &send_list, send_list) { + + kref_get(&info->refcount); + spin_unlock_irqrestore(&vis_hash_lock, flags); + + send_vis_packet(info); + + spin_lock_irqsave(&vis_hash_lock, flags); + send_list_del(info); + kref_put(&info->refcount, free_info); + } + spin_unlock_irqrestore(&vis_hash_lock, flags); + start_vis_timer(); +} +static DECLARE_DELAYED_WORK(vis_timer_wq, send_vis_packets); + +/* init the vis server. this may only be called when if_list is already + * initialized (e.g. bat0 is initialized, interfaces have been added) */ +int vis_init(void) +{ + unsigned long flags; + if (vis_hash) + return 1; + + spin_lock_irqsave(&vis_hash_lock, flags); + + vis_hash = hash_new(256, vis_info_cmp, vis_info_choose); + if (!vis_hash) { - printk(KERN_ERR "batman-adv:Can't initialize vis_hash\n"); ++ pr_err("Can't initialize vis_hash\n"); + goto err; + } + + my_vis_info = kmalloc(1000, GFP_ATOMIC); + if (!my_vis_info) { - printk(KERN_ERR "batman-adv:Can't initialize vis packet\n"); ++ pr_err("Can't initialize vis packet\n"); + goto err; + } + + /* prefill the vis info */ + my_vis_info->first_seen = jiffies - msecs_to_jiffies(VIS_INTERVAL); + INIT_LIST_HEAD(&my_vis_info->recv_list); + INIT_LIST_HEAD(&my_vis_info->send_list); + kref_init(&my_vis_info->refcount); + my_vis_info->packet.version = COMPAT_VERSION; + my_vis_info->packet.packet_type = BAT_VIS; + my_vis_info->packet.ttl = TTL; + my_vis_info->packet.seqno = 0; + my_vis_info->packet.entries = 0; + + INIT_LIST_HEAD(&send_list); + + memcpy(my_vis_info->packet.vis_orig, main_if_addr, ETH_ALEN); + memcpy(my_vis_info->packet.sender_orig, main_if_addr, ETH_ALEN); + + if (hash_add(vis_hash, my_vis_info) < 0) { - printk(KERN_ERR - "batman-adv:Can't add own vis packet into hash\n"); ++ pr_err("Can't add own vis packet into hash\n"); + /* not in hash, need to remove it manually. */ + kref_put(&my_vis_info->refcount, free_info); + goto err; + } + + spin_unlock_irqrestore(&vis_hash_lock, flags); + start_vis_timer(); + return 1; + +err: + spin_unlock_irqrestore(&vis_hash_lock, flags); + vis_quit(); + return 0; +} + +/* Decrease the reference count on a hash item info */ +static void free_info_ref(void *data) +{ + struct vis_info *info = data; + + send_list_del(info); + kref_put(&info->refcount, free_info); +} + +/* shutdown vis-server */ +void vis_quit(void) +{ + unsigned long flags; + if (!vis_hash) + return; + + cancel_delayed_work_sync(&vis_timer_wq); + + spin_lock_irqsave(&vis_hash_lock, flags); + /* properly remove, kill timers ... */ + hash_delete(vis_hash, free_info_ref); + vis_hash = NULL; + my_vis_info = NULL; + spin_unlock_irqrestore(&vis_hash_lock, flags); +} + +/* schedule packets for (re)transmission */ +static void start_vis_timer(void) +{ + queue_delayed_work(bat_event_workqueue, &vis_timer_wq, + (VIS_INTERVAL * HZ) / 1000); +}