For testing purposes, forcing specific paths in a network where all nodes are within reach, can be useful. This patch allows the use to enter addresses from which direct OGMs should be ignored.
An address is added by echoing it into /sys/class/net/bat0/filter_add_addr and removed again by echoing the same address into /sys/class/net/bat0/filter_del_addr. The list of currently blocked direct OGM sources is available in /sys/kernel/debug/batman-adv/bat0/filter_table.
The filtering is performed by checking the value of member "orig_node->filter", where the orig_node struct is found using the Ethernet source of the OGM message in question. If "orig_node->filter is true (1), the OGM is simply dropped.
Signed-off-by: Martin Hundebøll martin@hundeboll.net --- Makefile | 2 + Makefile.kbuild | 1 + bat_debugfs.c | 15 ++++++ bat_sysfs.c | 43 ++++++++++++++++++ filter.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++ filter.h | 53 ++++++++++++++++++++++ gen-compat-autoconf.sh | 1 + originator.c | 2 + routing.c | 5 ++ types.h | 1 + 10 files changed, 239 insertions(+), 0 deletions(-) create mode 100644 filter.c create mode 100644 filter.h
diff --git a/Makefile b/Makefile index 08f8c39..42895f6 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,8 @@ export CONFIG_BATMAN_ADV_DEBUG=n # B.A.T.M.A.N. bridge loop avoidance: export CONFIG_BATMAN_ADV_BLA=y +# B.A.T.M.A.N. OGM packet filtering: +export CONFIG_BATMAN_ADV_FILTER=y
PWD:=$(shell pwd) KERNELPATH ?= /lib/modules/$(shell uname -r)/build diff --git a/Makefile.kbuild b/Makefile.kbuild index 6d5c194..8bf515c 100644 --- a/Makefile.kbuild +++ b/Makefile.kbuild @@ -24,6 +24,7 @@ batman-adv-y += bat_iv_ogm.o batman-adv-y += bat_sysfs.o batman-adv-y += bitarray.o batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o +batman-adv-$(CONFIG_BATMAN_ADV_FILTER) += filter.o batman-adv-y += gateway_client.o batman-adv-y += gateway_common.o batman-adv-y += hard-interface.o diff --git a/bat_debugfs.c b/bat_debugfs.c index 916380c..3f8ef4f 100644 --- a/bat_debugfs.c +++ b/bat_debugfs.c @@ -33,6 +33,7 @@ #include "vis.h" #include "icmp_socket.h" #include "bridge_loop_avoidance.h" +#include "filter.h"
static struct dentry *bat_debugfs;
@@ -253,6 +254,14 @@ static int bla_claim_table_open(struct inode *inode, struct file *file) } #endif
+#ifdef CONFIG_BATMAN_ADV_FILTER +static int filter_show_table_open(struct inode *inode, struct file *file) +{ + struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, filter_show_table_seq_print_text, net_dev); +} +#endif + static int transtable_local_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; @@ -289,6 +298,9 @@ static BAT_DEBUGINFO(transtable_global, S_IRUGO, transtable_global_open); #ifdef CONFIG_BATMAN_ADV_BLA static BAT_DEBUGINFO(bla_claim_table, S_IRUGO, bla_claim_table_open); #endif +#ifdef CONFIG_BATMAN_ADV_FILTER +static BAT_DEBUGINFO(filter_table, S_IRUGO, filter_show_table_open); +#endif static BAT_DEBUGINFO(transtable_local, S_IRUGO, transtable_local_open); static BAT_DEBUGINFO(vis_data, S_IRUGO, vis_data_open);
@@ -299,6 +311,9 @@ static struct bat_debuginfo *mesh_debuginfos[] = { #ifdef CONFIG_BATMAN_ADV_BLA &bat_debuginfo_bla_claim_table, #endif +#ifdef CONFIG_BATMAN_ADV_FILTER + &bat_debuginfo_filter_table, +#endif &bat_debuginfo_transtable_local, &bat_debuginfo_vis_data, NULL, diff --git a/bat_sysfs.c b/bat_sysfs.c index 3adb183..0fc5e3e 100644 --- a/bat_sysfs.c +++ b/bat_sysfs.c @@ -27,6 +27,7 @@ #include "gateway_common.h" #include "gateway_client.h" #include "vis.h" +#include "filter.h"
static struct net_device *kobj_to_netdev(struct kobject *obj) { @@ -384,11 +385,49 @@ static ssize_t store_gw_bwidth(struct kobject *kobj, struct attribute *attr, return gw_bandwidth_set(net_dev, buff, count); }
+static ssize_t add_filter_addr(struct kobject *kobj, struct attribute *attr, + char *buff, size_t count) +{ + struct net_device *net_dev = kobj_to_netdev(kobj); + struct bat_priv *bat_priv = netdev_priv(net_dev); + char mac[ETH_ALEN]; + int res; + + if ((res = filter_parse_addr_str(buff, count, mac)) < 0) { + bat_info(net_dev, "Address has invalid format: %s\n", buff); + return res; + } + + filter_addr_add(bat_priv, mac); + + return count; +} + +static ssize_t del_filter_addr(struct kobject *kobj, struct attribute *attr, + char *buff, size_t count) +{ + struct net_device *net_dev = kobj_to_netdev(kobj); + struct bat_priv *bat_priv = netdev_priv(net_dev); + char mac[ETH_ALEN]; + int res; + + if ((res = filter_parse_addr_str(buff, count, mac)) < 0) + return res; + + filter_addr_del(bat_priv, mac); + + return count; +} + BAT_ATTR_BOOL(aggregated_ogms, S_IRUGO | S_IWUSR, NULL); BAT_ATTR_BOOL(bonding, S_IRUGO | S_IWUSR, NULL); #ifdef CONFIG_BATMAN_ADV_BLA BAT_ATTR_BOOL(bridge_loop_avoidance, S_IRUGO | S_IWUSR, NULL); #endif +#ifdef CONFIG_BATMAN_ADV_FILTER +static BAT_ATTR(filter_add_addr, S_IRUGO | S_IWUSR, NULL, add_filter_addr); +static BAT_ATTR(filter_del_addr, S_IRUGO | S_IWUSR, NULL, del_filter_addr); +#endif BAT_ATTR_BOOL(fragmentation, S_IRUGO | S_IWUSR, update_min_mtu); BAT_ATTR_BOOL(ap_isolation, S_IRUGO | S_IWUSR, NULL); static BAT_ATTR(vis_mode, S_IRUGO | S_IWUSR, show_vis_mode, store_vis_mode); @@ -410,6 +449,10 @@ static struct bat_attribute *mesh_attrs[] = { #ifdef CONFIG_BATMAN_ADV_BLA &bat_attr_bridge_loop_avoidance, #endif +#ifdef CONFIG_BATMAN_ADV_FILTER + &bat_attr_filter_add_addr, + &bat_attr_filter_del_addr, +#endif &bat_attr_fragmentation, &bat_attr_ap_isolation, &bat_attr_vis_mode, diff --git a/filter.c b/filter.c new file mode 100644 index 0000000..844e10e --- /dev/null +++ b/filter.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: + * + * Martin Hundebøll martin@hundeboll.net + * + * 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 "filter.h" +#include "originator.h" + +bool filter_addr_drop(struct bat_priv *bat_priv, const uint8_t *addr) +{ + struct orig_node *orig_node = orig_hash_find(bat_priv, addr); + + if (!orig_node) + return false; + + if (atomic_read(&orig_node->filter)) + return true; + + orig_node_free_ref(orig_node); + return false; +} + +void filter_addr_add(struct bat_priv *bat_priv, const uint8_t *addr) +{ + struct orig_node *orig_node = orig_hash_find(bat_priv, addr); + + if (!orig_node) + return; + + atomic_set(&orig_node->filter, FILTER_TRUE); + orig_node_free_ref(orig_node); +} + +void filter_addr_del(struct bat_priv *bat_priv, const uint8_t *addr) +{ + struct orig_node *orig_node = orig_hash_find(bat_priv, addr); + + if (!orig_node) + return; + + atomic_set(&orig_node->filter, FILTER_FALSE); + orig_node_free_ref(orig_node); +} + +int filter_parse_addr_str(char *buff, size_t count, char *addr) +{ + int i; + + if (buff[count - 1] == '\n') + buff[count - 1] = '\0'; + + /* Validate if string has correct length */ + if (count - 1 != PRETTY_MAC_ADDR_LEN) + return -EINVAL; + + /* Validate if string has valid format */ + for (i = 1; i <= ETH_ALEN - 1; i++) + if (buff[i*3-1] != ':') + return -EINVAL; + + /* Convert string to bytes */ + for (i = 0; i < ETH_ALEN; i++) { + unsigned long l; + + l = simple_strtoul(&buff[i*3], (char **)NULL, 16); + addr[i] = (char)l; + } + + return count; +} + +int filter_show_table_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 hashtable_t *hash = bat_priv->orig_hash; + + struct hlist_node *node; + struct hlist_head *head; + struct orig_node *orig_node; + int i; + + if (!hash) + return 0; + + /* Traverse list of originators */ + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + /* For each orig_node in this bin */ + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) + if (atomic_read(&orig_node->filter)) + seq_printf(seq, "%pM\n", orig_node->orig); + rcu_read_unlock(); + } + + return 0; +} diff --git a/filter.h b/filter.h new file mode 100644 index 0000000..3b6cdf3 --- /dev/null +++ b/filter.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: + * + * Martin Hundebøll martin@hundeboll.net + * + * 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 + * + */ + +#ifndef _NET_BATMAN_ADV_NETWORK_FILTER_H +#define _NET_BATMAN_ADV_NETWORK_FILTER_H + +#define FILTER_TRUE 1 +#define FILTER_FALSE 0 + +#define PRETTY_MAC_ADDR_LEN 17 + +#ifdef CONFIG_BATMAN_ADV_FILTER + +int filter_parse_addr_str(char *buff, size_t count, char *addr); +int filter_show_table_seq_print_text(struct seq_file *seq, void *offset); +void filter_addr_add(struct bat_priv *bat_priv, const uint8_t *addr); +void filter_addr_del(struct bat_priv *bat_priv, const uint8_t *addr); +bool filter_addr_drop(struct bat_priv *bat_priv, const uint8_t *addr); + +#else /* ifdef CONFIG_BATMAN_ADV_FILTER */ + +#define filter_show_table_seq_print_text(...) (0) +#define filter_parse_addr_str(...) (0) +#define filter_addr_add(...) {} +#define filter_addr_del(...) {} + +static inline bool filter_addr_drop(struct bat_priv *bat_priv, + const uint8_t *addr) +{ + return false +} + +#endif /* ifdef CONFIG_BATMAN_ADV_FILTER */ + +#endif /* _NET_BATMAN_ADV_NETWORK_FILTER_H */ diff --git a/gen-compat-autoconf.sh b/gen-compat-autoconf.sh index 7cf621b..f2e7eae 100755 --- a/gen-compat-autoconf.sh +++ b/gen-compat-autoconf.sh @@ -38,6 +38,7 @@ gen_config() { # write config variables gen_config 'CONFIG_BATMAN_ADV_DEBUG' ${CONFIG_BATMAN_ADV_DEBUG:="n"} >> "${TMP}" gen_config 'CONFIG_BATMAN_ADV_BLA' ${CONFIG_BATMAN_ADV_BLA:="y"} >> "${TMP}" +gen_config 'CONFIG_BATMAN_ADV_FILTER' ${CONFIG_BATMAN_ADV_FILTER:="y"} >> "${TMP}"
# only regenerate compat-autoconf.h when config was changed diff "${TMP}" "${TARGET}" > /dev/null 2>&1 || cp "${TMP}" "${TARGET}" diff --git a/originator.c b/originator.c index 2db4b53..a949856 100644 --- a/originator.c +++ b/originator.c @@ -29,6 +29,7 @@ #include "unicast.h" #include "soft-interface.h" #include "bridge_loop_avoidance.h" +#include "filter.h"
static void purge_orig(struct work_struct *work);
@@ -236,6 +237,7 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, const uint8_t *addr) - msecs_to_jiffies(RESET_PROTECTION_MS);
atomic_set(&orig_node->bond_candidates, 0); + atomic_set(&orig_node->filter, FILTER_FALSE);
size = bat_priv->num_ifaces * sizeof(unsigned long) * NUM_WORDS;
diff --git a/routing.c b/routing.c index 7b7fcbe..81c57b7 100644 --- a/routing.c +++ b/routing.c @@ -30,6 +30,7 @@ #include "vis.h" #include "unicast.h" #include "bridge_loop_avoidance.h" +#include "filter.h"
static int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if); @@ -259,6 +260,10 @@ int recv_bat_ogm_packet(struct sk_buff *skb, struct hard_iface *hard_iface)
ethhdr = (struct ethhdr *)skb_mac_header(skb);
+ /* Packet is filtered by user */ + if (filter_addr_drop(bat_priv, ethhdr->h_source)) + return NET_RX_DROP; + /* packet with broadcast indication but unicast recipient */ if (!is_broadcast_ether_addr(ethhdr->h_dest)) return NET_RX_DROP; diff --git a/types.h b/types.h index 7f7f610..29e632d 100644 --- a/types.h +++ b/types.h @@ -108,6 +108,7 @@ struct orig_node { spinlock_t tt_list_lock; /* protects tt_list */ atomic_t bond_candidates; struct list_head bond_list; + atomic_t filter; /* Boolean */ };
struct gw_node {
Hello Martin,
On Wed, Feb 08, 2012 at 08:05:58PM +0100, Martin Hundebøll wrote:
static int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if); @@ -259,6 +260,10 @@ int recv_bat_ogm_packet(struct sk_buff *skb, struct hard_iface *hard_iface)
ethhdr = (struct ethhdr *)skb_mac_header(skb);
- /* Packet is filtered by user */
- if (filter_addr_drop(bat_priv, ethhdr->h_source))
return NET_RX_DROP;
- /* packet with broadcast indication but unicast recipient */ if (!is_broadcast_ether_addr(ethhdr->h_dest)) return NET_RX_DROP;
nice infrastructure :-) But dropping the OGM at this point, doesn;t seem to be a good choice imho. Actually the first OGM will pass through (because there is no orig_node struct to check for filter), then all the other OGMs will be dropped up to the orig purge timeout. At that point the orig_node structure will be deleted and so we will have another OGM passing through and then a period of silence.
I hope I am not wrong :-)
What about moving the filter operation so that at least the last_valid field is updated? Another option could be to take a different approach..like letting all the OGM pass through but skipping the orig_node when checking for a possible next-hop...
Cheers,
On Thursday, February 09, 2012 03:05:58 Martin Hundebøll wrote:
For testing purposes, forcing specific paths in a network where all nodes are within reach, can be useful. This patch allows the use to enter addresses from which direct OGMs should be ignored.
An address is added by echoing it into /sys/class/net/bat0/filter_add_addr and removed again by echoing the same address into /sys/class/net/bat0/filter_del_addr. The list of currently blocked direct OGM sources is available in /sys/kernel/debug/batman-adv/bat0/filter_table.
In addition to what Antonio has already pointed out:
diff --git a/bat_sysfs.c b/bat_sysfs.c index 3adb183..0fc5e3e 100644 --- a/bat_sysfs.c +++ b/bat_sysfs.c @@ -27,6 +27,7 @@ #include "gateway_common.h" #include "gateway_client.h" #include "vis.h" +#include "filter.h"
static struct net_device *kobj_to_netdev(struct kobject *obj) { @@ -384,11 +385,49 @@ static ssize_t store_gw_bwidth(struct kobject *kobj, struct attribute *attr, return gw_bandwidth_set(net_dev, buff, count); }
It might be better to move filter_add/filter_del into debugfs. There we have less "regulation rules" and more freedom when it comes to the syntax and what we add. It is for debugging purposes anyways.
Do you plan to add more "filters" in the future ? Otherwise I'd rather call it "block_orig/unblock_orig". Seems more obvious than the generic "filter".
- if ((res = filter_parse_addr_str(buff, count, mac)) < 0) {
bat_info(net_dev, "Address has invalid format: %s\n", buff);
return res;
Is there no existing kernel function for the mac address parsing ?
+void filter_addr_add(struct bat_priv *bat_priv, const uint8_t *addr) +{
- struct orig_node *orig_node = orig_hash_find(bat_priv, addr);
- if (!orig_node)
return;
- atomic_set(&orig_node->filter, FILTER_TRUE);
- orig_node_free_ref(orig_node);
+}
This will only allow blocking an originator if it is already there. What if I configure my node before the mesh is up ? For example via scripts ?
Cheers, Marek
For testing purposes, forcing specific paths in a network where all nodes are within reach, can be useful. This patch allows the user to enter addresses from which direct OGMs should be ignored or not dropped.
The blocking of OGM is controlled through a file in debugfs: /sys/kernel/debug/batman-adv/bat0/block_ogm.
The list of currently handled addresses is exported through that file. An addresses is added by echoing it, together with an action, into the file, e.g.: echo de:ad:be:ef:ca:fe drop > block_ogm echo de:ad:be:ef:ca:fe allow > block_ogm echo de:ad:be:ef:ca:fe del > block_ogm
The drop action leads to dropping all OGMs received directly from this address. The allow action leads to dropping all OGMs *not* from this (or other allowed) addresses. The del action deletes the address from the blocking list.
Addresses and actions are stored in a doubly linked list. When new addresses are added, the list of originators is searched, and an possible existing originator is updated. When new originators are created, the list of addresses is searched and "orig_node->block_action" is set accordingly.
The filtering is performed by checking the value of member "orig_node->block_action" together with a count of addresses with the "allow" action. The orig_node struct is found using the Ethernet source of the OGM message in question. If "orig_node->block_action" is equal 2. If the count of addresses with the "allow" action is non-zero, and "orig_node->block_action" is not 3, the OGM is dropped.
v2: This is almost a complete rewrite of the previous patch to allow adding addresses of unknown originators and the allow-only feature.
Signed-off-by: Martin Hundebøll martin@hundeboll.net --- Makefile | 2 + Makefile.kbuild | 1 + bat_debugfs.c | 3 + block_ogm.c | 394 ++++++++++++++++++++++++++++++++++++++++++++++++ block_ogm.h | 59 +++++++ compat.c | 8 + compat.h | 1 + gen-compat-autoconf.sh | 1 + main.c | 2 + originator.c | 2 + routing.c | 5 + soft-interface.c | 1 + types.h | 12 ++ 13 files changed, 491 insertions(+), 0 deletions(-) create mode 100644 block_ogm.c create mode 100644 block_ogm.h
diff --git a/Makefile b/Makefile index 08f8c39..b60a47f 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,8 @@ export CONFIG_BATMAN_ADV_DEBUG=n # B.A.T.M.A.N. bridge loop avoidance: export CONFIG_BATMAN_ADV_BLA=y +# B.A.T.M.A.N. OGM packet filtering: +export CONFIG_BATMAN_ADV_BLOCK_OGM=y
PWD:=$(shell pwd) KERNELPATH ?= /lib/modules/$(shell uname -r)/build diff --git a/Makefile.kbuild b/Makefile.kbuild index 6d5c194..8262811 100644 --- a/Makefile.kbuild +++ b/Makefile.kbuild @@ -24,6 +24,7 @@ batman-adv-y += bat_iv_ogm.o batman-adv-y += bat_sysfs.o batman-adv-y += bitarray.o batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o +batman-adv-$(CONFIG_BATMAN_ADV_BLOCK_OGM) += block_ogm.o batman-adv-y += gateway_client.o batman-adv-y += gateway_common.o batman-adv-y += hard-interface.o diff --git a/bat_debugfs.c b/bat_debugfs.c index 916380c..863f59b 100644 --- a/bat_debugfs.c +++ b/bat_debugfs.c @@ -33,6 +33,7 @@ #include "vis.h" #include "icmp_socket.h" #include "bridge_loop_avoidance.h" +#include "block_ogm.h"
static struct dentry *bat_debugfs;
@@ -350,6 +351,7 @@ int debugfs_add_meshif(struct net_device *dev)
bat_socket_setup(bat_priv); debug_log_setup(bat_priv); + block_file_setup(bat_priv);
for (bat_debug = mesh_debuginfos; *bat_debug; ++bat_debug) { file = debugfs_create_file(((*bat_debug)->attr).name, @@ -380,6 +382,7 @@ void debugfs_del_meshif(struct net_device *dev) struct bat_priv *bat_priv = netdev_priv(dev);
debug_log_cleanup(bat_priv); + block_file_cleanup(bat_priv);
if (bat_debugfs) { debugfs_remove_recursive(bat_priv->debug_dir); diff --git a/block_ogm.c b/block_ogm.c new file mode 100644 index 0000000..3df385e --- /dev/null +++ b/block_ogm.c @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: + * + * Martin Hundebøll martin@hundeboll.net + * + * 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 <linux/debugfs.h> +#include "main.h" +#include "block_ogm.h" +#include "originator.h" + +/* Called when receiving OGM packets to check if it should be dropped. + * Returns true if the packets should be dropped. False if not. */ +bool block_ogm(struct bat_priv *bat_priv, const uint8_t *addr) +{ + struct orig_node *orig_node = orig_hash_find(bat_priv, addr); + uint8_t action; + + if (!orig_node) + return false; + + action = atomic_read(&orig_node->block_action); + + /* Check if address is blocked */ + if (action == BLOCK_ACTION_DROP) + return true; + + /* Check if address is not allowed */ + if (atomic_read(&bat_priv->block_ogm_allow_cnt) && + action != BLOCK_ACTION_ALLOW) + return true; + + return false; +} + +/* Decrement reference count of the block entry and free it, if it becomes + * zero. */ +static void block_entry_free_ref(struct block_entry *block_entry) +{ + if (atomic_dec_and_test(&block_entry->refcount)) + kfree_rcu(block_entry, rcu); +} + +/* Search the list of block-entries for an address. Returns the found entry + * and NULL if none is found. */ +static struct block_entry *block_find_entry(struct bat_priv *bat_priv, + const uint8_t *addr) +{ + struct block_entry *block_entry_tmp, *block_entry = NULL; + + /* Search for existing entry */ + rcu_read_lock(); + list_for_each_entry_rcu(block_entry_tmp, &bat_priv->block_list, list) { + if (compare_eth(block_entry_tmp->addr, addr)) { + /* Found a match */ + block_entry = block_entry_tmp; + atomic_inc_not_zero(&block_entry->refcount); + break; + } + } + rcu_read_unlock(); + + return block_entry; +} + +/* Searches the list of block-entries and creates one, if none is found. */ +static struct block_entry *block_get_entry(struct bat_priv *bat_priv, + const uint8_t *addr) +{ + struct block_entry *block_entry = block_find_entry(bat_priv, addr); + + if (block_entry) + return block_entry; + + /* Create and insert entry if needed */ + block_entry = kmalloc(sizeof(*block_entry), GFP_ATOMIC); + if (!block_entry) + return NULL; + + memcpy(block_entry->addr, addr, ETH_ALEN); + atomic_set(&block_entry->refcount, 2); + block_entry->action = BLOCK_ACTION_NONE; + + spin_lock_bh(&bat_priv->block_lock); + list_add(&block_entry->list, &bat_priv->block_list); + spin_unlock_bh(&bat_priv->block_lock); + + return block_entry; +} + +/* Deletes an entry from the list */ +static void block_del_addr(struct bat_priv *bat_priv, + struct block_entry *block_entry) +{ + if (!block_entry) + return; + + spin_lock_bh(&bat_priv->block_lock); + list_del(&block_entry->list); + spin_unlock_bh(&bat_priv->block_lock); + + block_entry_free_ref(block_entry); +} + +/* When new entries are added to the list, existing originators are + * searched and if one exists, its block_action member is set accordingly. */ +static void block_set_orig_action(struct bat_priv *bat_priv, + struct block_entry *block_entry, + uint8_t action) +{ + struct orig_node *orig_node = orig_hash_find(bat_priv, block_entry->addr); + + if (!orig_node) + return; + + atomic_set(&orig_node->block_action, action); + orig_node_free_ref(orig_node); +} + +/* Set the action of an entry and update the allow counter in bat_priv + * if necessary. Also, update existing originators. */ +static void block_add_action(struct bat_priv *bat_priv, + struct block_entry *block_entry, + uint8_t action) +{ + uint8_t old_action; + + if (!block_entry) + return; + + old_action = block_entry->action; + + /* Check if the allow counter should be adjusted. */ + if (action == old_action) + return; + else if (old_action == BLOCK_ACTION_ALLOW) + atomic_dec_not_zero(&bat_priv->block_ogm_allow_cnt); + else if (action == BLOCK_ACTION_ALLOW) + atomic_inc(&bat_priv->block_ogm_allow_cnt); + + block_entry->action = action; + block_set_orig_action(bat_priv, block_entry, action); +} + +/* Adjust allow counter if needed and set action of originator, if one + * exists. */ +static void block_del_action(struct bat_priv *bat_priv, + struct block_entry *block_entry) +{ + if (!block_entry) + return; + + if (block_entry->action == BLOCK_ACTION_ALLOW) + atomic_dec_not_zero(&bat_priv->block_ogm_allow_cnt); + + block_set_orig_action(bat_priv, block_entry, BLOCK_ACTION_NONE); +} + +/* Called upon creation of an originator. Checks if the address of the new + * originator is listed in the list of block entries and sets the action of + * the originator accordingly. */ +void block_check_orig_entry(struct bat_priv *bat_priv, + struct orig_node *orig_node) +{ + struct block_entry *block_entry; + + block_entry = block_find_entry(bat_priv, orig_node->orig); + + if (block_entry) { + atomic_set(&orig_node->block_action, block_entry->action); + block_entry_free_ref(block_entry); + } else { + atomic_set(&orig_node->block_action, BLOCK_ACTION_NONE); + } +} + +/* Convert a human-readable MAC address into byte values. */ +static int block_parse_pretty_mac(const char *str, char *addr) +{ + int i; + + if (str[MAC_ADDR_LEN] != ' ') + return -EINVAL; + + /* Validate if string has valid format */ + for (i = 1; i <= ETH_ALEN - 1; i++) { + if (str[i*3-1] != ':') + return -EINVAL; + } + + /* Convert string to bytes */ + for (i = 0; i < ETH_ALEN; i++) { + unsigned long l; + + l = simple_strtoul(&str[i*3], (char **)NULL, 16); + addr[i] = (char)l; + } + + return 0; +} + +/* Convert the human readable action to a decimal value. */ +static int block_parse_action(const char *str, int *action) +{ + if (strncmp(str, BLOCK_ACTION_DEL_NAME, + strlen(BLOCK_ACTION_DEL_NAME)) == 0) + *action = BLOCK_ACTION_DEL; + else if (strncmp(str, BLOCK_ACTION_DROP_NAME, + strlen(BLOCK_ACTION_DROP_NAME)) == 0) + *action = BLOCK_ACTION_DROP; + else if (strncmp(str, BLOCK_ACTION_ALLOW_NAME, + strlen(BLOCK_ACTION_ALLOW_NAME)) == 0) + *action = BLOCK_ACTION_ALLOW; + else + return -EINVAL; + + return 0; +} + +/* Called by debugfs when our file is opened. */ +static int block_file_open(struct inode *inode, struct file *file) +{ + nonseekable_open(inode, file); + file->private_data = inode->i_private; + inc_module_count(); + return 0; +} + +/* Called by debugfs when our file is closed. */ +static int block_file_release(struct inode *inode, struct file *file) +{ + dec_module_count(); + return 0; +} + +/* Called by debugfs when data is requested from our file. The function + * copies the address and action from each entry in the block list into + * the user buffer. */ +static ssize_t block_file_read(struct file *file, char __user *buff, + size_t count, loff_t *ppos) +{ + struct bat_priv *bat_priv = file->private_data; + struct block_entry *block_entry; + char mac[MAC_ADDR_LEN + BLOCK_ACTION_MAX_LEN + 2]; + ssize_t c, i = 0; + + /* If private_data is cleared, our work is done. */ + if (!bat_priv) + return 0; + + if (!access_ok(VERIFY_WRITE, buff, count)) + return -EFAULT; + + /* For each block entry. */ + rcu_read_lock(); + list_for_each_entry_rcu(block_entry, &bat_priv->block_list, + list) { + /* Copy address and action into temp buffer. */ + c = scnprintf(mac, MAC_ADDR_LEN + BLOCK_ACTION_MAX_LEN + 2, + "%pM %hhu\n", + block_entry->addr, + block_entry->action); + /* Copy formatted string to user space. */ + c = c > count ? count : c; + copy_to_user(buff, mac, c); + + /* Update counters before next copy. */ + buff += c; + i += c; + } + rcu_read_unlock(); + + /* Clear private_data to skip further reads */ + file->private_data = NULL; + + return i; +} + +/* Called by debugfs when data is written to our file. Copies the written data + * into a local buffer and reads out the MAC address and action from this and + * calls the according functions. */ +static ssize_t block_file_write(struct file *file, const char __user *buff, + size_t count, loff_t *ppos) +{ + struct bat_priv *bat_priv = file->private_data; + struct block_entry *block_entry = NULL; + char *str; + char addr[MAC_ADDR_LEN]; + int error = 0; + int action = 0; + + /* Make sure buffer is long enough to hold at least a MAC address. */ + if (!buff || count < MAC_ADDR_LEN + 1) + return -EINVAL; + + if (!access_ok(VERIFY_READ, buff, count)) + return -EFAULT; + + /* Copy data from user space buffer. */ + str = kmalloc(count, GFP_ATOMIC); + if (!str) + return -ENOMEM; + copy_from_user(str, buff, count); + str[count - 1] = '\0'; + + /* Read out MAC address. */ + if ((error = block_parse_pretty_mac(str, addr))) + goto out; + + /* Read out action. */ + if ((error = block_parse_action(str + MAC_ADDR_LEN + 1, &action)) < 0) + goto out; + + switch (action) { + /* Find and delete requested address. */ + case BLOCK_ACTION_DEL: + block_entry = block_find_entry(bat_priv, addr); + block_del_action(bat_priv, block_entry); + block_del_addr(bat_priv, block_entry); + break; + + /* Create or update requested address. */ + case BLOCK_ACTION_DROP: + case BLOCK_ACTION_ALLOW: + block_entry = block_get_entry(bat_priv, addr); + block_add_action(bat_priv, block_entry, action); + break; + } + + if (block_entry) + block_entry_free_ref(block_entry); + +out: + kfree(str); + return count; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = block_file_open, + .release = block_file_release, + .read = block_file_read, + .write = block_file_write, + .llseek = no_llseek, +}; + +/* Register/create our file in debugfs and initialize bat_priv members. */ +int block_file_setup(struct bat_priv *bat_priv) +{ + struct dentry *d; + + if (!bat_priv->debug_dir) + goto err; + + d = debugfs_create_file("block_ogm", S_IFREG | S_IWUSR | S_IRUSR, + bat_priv->debug_dir, bat_priv, &fops); + if (d) + goto err; + + return 0; + +err: + return 1; +} + +/* Remove all entries from the block list. */ +int block_file_cleanup(struct bat_priv *bat_priv) +{ + struct block_entry *block_entry, *block_entry_tmp; + + spin_lock_bh(&bat_priv->block_lock); + list_for_each_entry_safe(block_entry, block_entry_tmp, + &bat_priv->block_list, list) { + list_del(&block_entry->list); + kfree(block_entry); + } + spin_unlock_bh(&bat_priv->block_lock); + return 0; +} diff --git a/block_ogm.h b/block_ogm.h new file mode 100644 index 0000000..ff9cd75 --- /dev/null +++ b/block_ogm.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: + * + * Martin Hundebøll martin@hundeboll.net + * + * 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 + * + */ + +#ifndef _NET_BATMAN_ADV_NETWORK_BLOCK_OGM_H +#define _NET_BATMAN_ADV_NETWORK_BLOCK_OGM_H + +enum { + BLOCK_ACTION_NONE, + BLOCK_ACTION_DEL, + BLOCK_ACTION_DROP, + BLOCK_ACTION_ALLOW, +}; + +#define BLOCK_ACTION_DEL_NAME "del" +#define BLOCK_ACTION_DROP_NAME "drop" +#define BLOCK_ACTION_ALLOW_NAME "allow" + +#define MAC_ADDR_LEN 17 +#define BLOCK_ACTION_MAX_LEN 10 + +#ifdef CONFIG_BATMAN_ADV_BLOCK_OGM + +int block_file_setup(struct bat_priv *bat_priv); +int block_file_cleanup(struct bat_priv *bat_priv); +bool block_ogm(struct bat_priv *bat_priv, const uint8_t *addr); +void block_check_orig_entry(struct bat_priv *bat_priv, struct orig_node *orig_node); + +#else /* ifdef CONFIG_BATMAN_ADV_BLOCK_OGM */ + +#define block_file_setup(...) (0) +#define block_file_cleanup(...) (0) + +static inline bool block_addr_drop(struct bat_priv *bat_priv, + const uint8_t *addr) +{ + return false +} + +#endif /* ifdef CONFIG_BATMAN_ADV_BLOCK_OGM */ + +#endif /* _NET_BATMAN_ADV_NETWORK_BLOCK_OGM_H */ diff --git a/compat.c b/compat.c index 73a2d8b..8a0b16b 100644 --- a/compat.c +++ b/compat.c @@ -63,4 +63,12 @@ void free_rcu_backbone_gw(struct rcu_head *rcu) kfree(backbone_gw); }
+void free_rcu_block_entry(struct rcu_head *rcu) +{ + struct block_entry *block_entry; + + block_entry = container_of(rcu, struct block_entry, rcu); + kfree(block_entry); +} + #endif /* < KERNEL_VERSION(3, 0, 0) */ diff --git a/compat.h b/compat.h index e90409f..ccb787d 100644 --- a/compat.h +++ b/compat.h @@ -124,6 +124,7 @@ void free_rcu_gw_node(struct rcu_head *rcu); void free_rcu_neigh_node(struct rcu_head *rcu); void free_rcu_tt_local_entry(struct rcu_head *rcu); void free_rcu_backbone_gw(struct rcu_head *rcu); +void free_rcu_block_entry(struct rcu_head *rcu);
#endif /* < KERNEL_VERSION(3, 0, 0) */
diff --git a/gen-compat-autoconf.sh b/gen-compat-autoconf.sh index 7cf621b..b4ffe84 100755 --- a/gen-compat-autoconf.sh +++ b/gen-compat-autoconf.sh @@ -38,6 +38,7 @@ gen_config() { # write config variables gen_config 'CONFIG_BATMAN_ADV_DEBUG' ${CONFIG_BATMAN_ADV_DEBUG:="n"} >> "${TMP}" gen_config 'CONFIG_BATMAN_ADV_BLA' ${CONFIG_BATMAN_ADV_BLA:="y"} >> "${TMP}" +gen_config 'CONFIG_BATMAN_ADV_BLOCK_OGM' ${CONFIG_BATMAN_ADV_FILTER:="y"} >> "${TMP}"
# only regenerate compat-autoconf.h when config was changed diff "${TMP}" "${TARGET}" > /dev/null 2>&1 || cp "${TMP}" "${TARGET}" diff --git a/main.c b/main.c index 7750bca..8808ed6 100644 --- a/main.c +++ b/main.c @@ -97,6 +97,7 @@ int mesh_init(struct net_device *soft_iface) spin_lock_init(&bat_priv->gw_list_lock); spin_lock_init(&bat_priv->vis_hash_lock); spin_lock_init(&bat_priv->vis_list_lock); + spin_lock_init(&bat_priv->block_lock);
INIT_HLIST_HEAD(&bat_priv->forw_bat_list); INIT_HLIST_HEAD(&bat_priv->forw_bcast_list); @@ -104,6 +105,7 @@ int mesh_init(struct net_device *soft_iface) INIT_LIST_HEAD(&bat_priv->tt_changes_list); INIT_LIST_HEAD(&bat_priv->tt_req_list); INIT_LIST_HEAD(&bat_priv->tt_roam_list); + INIT_LIST_HEAD(&bat_priv->block_list);
if (originator_init(bat_priv) < 1) goto err; diff --git a/originator.c b/originator.c index 2db4b53..992738b 100644 --- a/originator.c +++ b/originator.c @@ -29,6 +29,7 @@ #include "unicast.h" #include "soft-interface.h" #include "bridge_loop_avoidance.h" +#include "block_ogm.h"
static void purge_orig(struct work_struct *work);
@@ -236,6 +237,7 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, const uint8_t *addr) - msecs_to_jiffies(RESET_PROTECTION_MS);
atomic_set(&orig_node->bond_candidates, 0); + block_check_orig_entry(bat_priv, orig_node);
size = bat_priv->num_ifaces * sizeof(unsigned long) * NUM_WORDS;
diff --git a/routing.c b/routing.c index 7b7fcbe..125f35c 100644 --- a/routing.c +++ b/routing.c @@ -30,6 +30,7 @@ #include "vis.h" #include "unicast.h" #include "bridge_loop_avoidance.h" +#include "block_ogm.h"
static int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if); @@ -259,6 +260,10 @@ int recv_bat_ogm_packet(struct sk_buff *skb, struct hard_iface *hard_iface)
ethhdr = (struct ethhdr *)skb_mac_header(skb);
+ /* Packet is blocked by user */ + if (block_ogm(bat_priv, ethhdr->h_source)) + return NET_RX_DROP; + /* packet with broadcast indication but unicast recipient */ if (!is_broadcast_ether_addr(ethhdr->h_dest)) return NET_RX_DROP; diff --git a/soft-interface.c b/soft-interface.c index 8de8779..357f555 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -389,6 +389,7 @@ struct net_device *softif_create(const char *name) atomic_set(&bat_priv->tt_local_changes, 0); atomic_set(&bat_priv->tt_ogm_append_cnt, 0); atomic_set(&bat_priv->bla_num_requests, 0); + atomic_set(&bat_priv->block_ogm_allow_cnt, 0);
bat_priv->tt_buff = NULL; bat_priv->tt_buff_len = 0; diff --git a/types.h b/types.h index 7f7f610..54c71b7 100644 --- a/types.h +++ b/types.h @@ -108,6 +108,7 @@ struct orig_node { spinlock_t tt_list_lock; /* protects tt_list */ atomic_t bond_candidates; struct list_head bond_list; + atomic_t block_action; /* Boolean */ };
struct gw_node { @@ -217,6 +218,9 @@ struct bat_priv { struct hard_iface __rcu *primary_if; /* rcu protected pointer */ struct vis_info *my_vis_info; struct bat_algo_ops *bat_algo_ops; + struct list_head block_list; + spinlock_t block_lock; + atomic_t block_ogm_allow_cnt; /* uint */ };
struct socket_client { @@ -385,4 +389,12 @@ struct bat_algo_ops { struct sk_buff *skb); };
+struct block_entry { + struct list_head list; + uint8_t addr[ETH_ALEN]; + uint8_t action; + struct rcu_head rcu; + atomic_t refcount; /* uint */ +}; + #endif /* _NET_BATMAN_ADV_TYPES_H_ */
On 2012-02-10 15:53, Martin Hundebøll wrote:
For testing purposes, forcing specific paths in a network where all nodes are within reach, can be useful. This patch allows the user to enter addresses from which direct OGMs should be ignored or not dropped.
The code/patch is also available in the git repositorie: http://git.open-mesh.org/?p=hundeboll/batman-adv.git;h=refs/heads/filter
Hey Martin,
On Fri, Feb 10, 2012 at 03:53:11PM +0100, Martin Hundebøll wrote:
13 files changed, 491 insertions(+), 0 deletions(-)
please excuse my superficial review, but do we really need to bloat batman-adv by 491 more lines to drop these OGMs? I'm afraid the filter will evolve more and more, and eventually the research/debugging code in batman-adv is bigger than the routing code. ;)
(sorry for ranting ;] )
Have you considered using and/or extending the former ebtables patch from Linus[1]? We removed [2] it after some discussions [3], but maybe its worth fixing these problems if its just about dropping some neighbors packets. The patch has some problems, but it was really short and I guess we wouldn't mind accepting the performance overhead by ebtables for research/debugging purposes. Plus, we could make this a compile-feature too.
Cheers, Simon
[1] http://ftp.uni-kl.de/pub/linux/kernel/people/gregkh/staging/2.6/2.6.35/0120-... [2] https://lists.open-mesh.org/pipermail/b.a.t.m.a.n/2010-September/003305.html [3] http://lkml.indiana.edu/hypermail/linux/kernel/1008.1/02232.html
Hi Simon,
On 2012-02-15 20:23, Simon Wunderlich wrote:
Hey Martin,
On Fri, Feb 10, 2012 at 03:53:11PM +0100, Martin Hundebøll wrote:
13 files changed, 491 insertions(+), 0 deletions(-)
please excuse my superficial review, but do we really need to bloat batman-adv by 491 more lines to drop these OGMs? I'm afraid the filter will evolve more and more, and eventually the research/debugging code in batman-adv is bigger than the routing code. ;)
Actually, I agree with you. The first patch was nice and (too) simple, but quickly grew :) My initial goal with the filtering was not to get it included in batman-adv, but just to make it available to others, who might need it. I use it in my work on the university and will continue to keep it updated on master.
(sorry for ranting ;] )
We like to rant and bash :)
Have you considered using and/or extending the former ebtables patch from Linus[1]? We removed [2] it after some discussions [3], but maybe its worth fixing these problems if its just about dropping some neighbors packets. The patch has some problems, but it was really short and I guess we wouldn't mind accepting the performance overhead by ebtables for research/debugging purposes. Plus, we could make this a compile-feature too.
With my approach, we only check OGMs, which is relatively cheap, compared to checking every incoming packet, which is why I didn't chose the netfilter path. At this point I am satisfied with the dropping of OGM's, so I don't think I will spend time looking into netfilter.
b.a.t.m.a.n@lists.open-mesh.org