Hello people,
**** This is the second version of this patchset. Some bugs/typ0s have been corrected. Each patch contains in its header the change made from the previous version. Moreover a 7th patch has been included which add a compile-time option to enable/disable DAT. ****
as most of you may already know, last summer I've been working on the B.A.T.M.A.N.-Adv GSoC project named "DAT: Distributed ARP Table". For who wants to get deeper into the details of the project there are two links: - The GSoC proposal [1] - The DAT wikipage on open-mesh.org [2], with status and ideas description
Just to recap: DAT is a distributes hash table meant to store ARP entries for fast lookup. In a normal scenario, whenever a node wants to communicate with another one, it first needs to issue a broadcast ARP request in order to retrieve its PHY/MAC address. In a sparse network a broadcast message could be lost several times before reaching the real destination so creating high latencies. With DAT, every ARP entries (a pair [IP addr, MAC addr]) is stored on a "computed" set of nodes, therefore in case of ARP request theses nodes can directly be contacted (in unicast) and the needed information can be quickly fetched.
Cheers, Antonio
[1] http://www.google-melange.com/gsoc/project/google/gsoc2011/ordex/4001 [2] http://www.open-mesh.org/wiki/batman-adv/GSOC2011_DAT
A new function named prepare_unicast_packet() has been implemented so that it can do all the needed operations to set up a skb for unicast sending. It is general enough to be used in every context. Helpful for later developments
Signed-off-by: Antonio Quartulli ordex@autistici.org ---
* label "prepare_packet:" restored to "find_router:" in unicast.c * restored wrongly removed blank line
unicast.c | 43 ++++++++++++++++++++++++++++++------------- unicast.h | 2 ++ 2 files changed, 32 insertions(+), 13 deletions(-)
diff --git a/unicast.c b/unicast.c index 07d1c1d..cbadc21 100644 --- a/unicast.c +++ b/unicast.c @@ -283,6 +283,33 @@ out: return ret; }
+struct sk_buff *prepare_unicast_packet(struct sk_buff *skb, + struct orig_node *orig_node) +{ + struct unicast_packet *unicast_packet; + + if (my_skb_head_push(skb, sizeof(*unicast_packet)) < 0) + goto out; + + unicast_packet = (struct unicast_packet *)skb->data; + + unicast_packet->version = COMPAT_VERSION; + /* batman packet type: unicast */ + unicast_packet->packet_type = BAT_UNICAST; + /* set unicast ttl */ + unicast_packet->ttl = TTL; + /* copy the destination for faster routing */ + memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN); + /* set the destination tt version number */ + unicast_packet->ttvn = + (uint8_t)atomic_read(&orig_node->last_ttvn); + + return skb; +out: + kfree(skb); + return NULL; +} + int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv) { struct ethhdr *ethhdr = (struct ethhdr *)skb->data; @@ -315,22 +342,12 @@ find_router: if (!neigh_node) goto out;
- if (my_skb_head_push(skb, sizeof(*unicast_packet)) < 0) + skb = prepare_unicast_packet(skb, orig_node); + if (!skb) goto out;
unicast_packet = (struct unicast_packet *)skb->data;
- unicast_packet->version = COMPAT_VERSION; - /* batman packet type: unicast */ - unicast_packet->packet_type = BAT_UNICAST; - /* set unicast ttl */ - unicast_packet->ttl = TTL; - /* copy the destination for faster routing */ - memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN); - /* set the destination tt version number */ - unicast_packet->ttvn = - (uint8_t)atomic_read(&orig_node->last_ttvn); - if (atomic_read(&bat_priv->fragmentation) && data_len + sizeof(*unicast_packet) > neigh_node->if_incoming->net_dev->mtu) { @@ -350,7 +367,7 @@ out: neigh_node_free_ref(neigh_node); if (orig_node) orig_node_free_ref(orig_node); - if (ret == 1) + if (ret == 1 && skb) kfree_skb(skb); return ret; } diff --git a/unicast.h b/unicast.h index 8fd5535..f53a735 100644 --- a/unicast.h +++ b/unicast.h @@ -33,6 +33,8 @@ void frag_list_free(struct list_head *head); int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv); int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv, struct hard_iface *hard_iface, const uint8_t dstaddr[]); +struct sk_buff *prepare_unicast_packet(struct sk_buff *skb, + struct orig_node *orig_node);
static inline int frag_can_reassemble(const struct sk_buff *skb, int mtu) {
A new log level has been added to concentrate messages regarding DAT: ARP snooping, requests, response and DHT related messages. The new log level is named DBG_ARP
Signed-off-by: Antonio Quartulli ordex@autistici.org ---
- nothing changed
bat_sysfs.c | 2 +- main.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/bat_sysfs.c b/bat_sysfs.c index c25492f..ec2e437 100644 --- a/bat_sysfs.c +++ b/bat_sysfs.c @@ -390,7 +390,7 @@ BAT_ATTR_UINT(gw_sel_class, S_IRUGO | S_IWUSR, 1, TQ_MAX_VALUE, static BAT_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, show_gw_bwidth, store_gw_bwidth); #ifdef CONFIG_BATMAN_ADV_DEBUG -BAT_ATTR_UINT(log_level, S_IRUGO | S_IWUSR, 0, 7, NULL); +BAT_ATTR_UINT(log_level, S_IRUGO | S_IWUSR, 0, 15, NULL); #endif
static struct bat_attribute *mesh_attrs[] = { diff --git a/main.h b/main.h index 53e5d9f..f3978c0 100644 --- a/main.h +++ b/main.h @@ -120,7 +120,8 @@ enum dbg_level { DBG_BATMAN = 1 << 0, DBG_ROUTES = 1 << 1, /* route added / changed / deleted */ DBG_TT = 1 << 2, /* translation table operations */ - DBG_ALL = 7 + DBG_ARP = 1 << 3, /* snooped arp messages / dht operations */ + DBG_ALL = 15 };
Add all the relevant functions in order to manage a Distributed Hash Table over the B.A.T.M.A.N.-adv network. It will later be used to store several ARP entries and implement DAT (Distributed ARP Table)
Signed-off-by: Antonio Quartulli ordex@autistici.org ---
* removed wrongly refcount increment in dht_send_data() * removed useless brackets
Makefile.kbuild | 1 + distributed-arp-table.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++ distributed-arp-table.h | 46 ++++++++++++ hard-interface.c | 2 + main.h | 7 ++ originator.c | 1 + soft-interface.c | 2 + types.h | 7 ++ 8 files changed, 247 insertions(+), 0 deletions(-) create mode 100644 distributed-arp-table.c create mode 100644 distributed-arp-table.h
diff --git a/Makefile.kbuild b/Makefile.kbuild index bd7e93c..e8861cb 100644 --- a/Makefile.kbuild +++ b/Makefile.kbuild @@ -36,6 +36,7 @@ batman-adv-y += bat_debugfs.o batman-adv-y += bat_iv_ogm.o batman-adv-y += bat_sysfs.o batman-adv-y += bitarray.o +batman-adv-y += distributed-arp-table.o batman-adv-y += gateway_client.o batman-adv-y += gateway_common.o batman-adv-y += hard-interface.o diff --git a/distributed-arp-table.c b/distributed-arp-table.c new file mode 100644 index 0000000..5bc3004 --- /dev/null +++ b/distributed-arp-table.c @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2011 B.A.T.M.A.N. contributors: + * + * Antonio Quartulli + * + * 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/if_ether.h> +#include <linux/if_arp.h> + +#include "main.h" +#include "distributed-arp-table.h" +#include "hard-interface.h" +#include "originator.h" +#include "send.h" +#include "types.h" +#include "unicast.h" + +/* Given a key, selects the candidates which the DHT message has to be sent to. + * An originator O is selected if and only if its DHT_ID value is one of three + * closest values (but not greater) then the hash value of the key. + * ip_dst is the key. + * + * return an array of size DHT_CANDIDATES_NUM */ +static struct dht_candidate *dht_select_candidates(struct bat_priv *bat_priv, + uint32_t ip_dst) +{ + struct hashtable_t *hash = bat_priv->orig_hash; + struct hlist_node *node; + struct hlist_head *head; + struct orig_node *orig_node, *max_orig_node = NULL; + int select, i, j; + uint16_t last_max = USHRT_MAX, max, tmp_max, ip_key; + struct dht_candidate *res; + bool chosen_me = false; + + if (!hash) + return NULL; + + res = kmalloc(DHT_CANDIDATES_NUM * sizeof(*res), GFP_ATOMIC); + if (!res) + return NULL; + + ip_key = hash_ipv4(&ip_dst, USHRT_MAX); + + bat_dbg(DBG_ARP, bat_priv, "DHT_SEL_CAND key: %pI4 %u\n", &ip_dst, + ip_key); + + for (select = 0; select < DHT_CANDIDATES_NUM; select++) { + max = 0; + max_orig_node = NULL; + if (!chosen_me) { + /* if true, wrap around the key space */ + if (bat_priv->dht_hash > ip_key) + max = USHRT_MAX - bat_priv->dht_hash + + ip_key; + else + max = ip_key - bat_priv->dht_hash; + max = bat_priv->dht_hash; + res[select].type = DHT_CANDIDATE_ME; + } else + res[select].type = DHT_CANDIDATE_NULL; + /* for all origins... */ + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, node, head, + hash_entry) { + if (orig_node->dht_hash > ip_key) + tmp_max = USHRT_MAX - + orig_node->dht_hash + ip_key; + else + tmp_max = ip_key - orig_node->dht_hash; + + /* this is closest! */ + if (tmp_max <= max) + continue; + + /* this has already been selected */ + if (tmp_max > last_max) + continue; + + /* In case of hash collision we can select two + * nodes with the same hash, but we have ensure + * they are different */ + if (tmp_max == last_max) { + for (j = 0; j < select; j++) + if (res[j].orig_node == + orig_node) + break; + if (j < select) + continue; + } + + if (!atomic_inc_not_zero(&orig_node->refcount)) + continue; + + max = tmp_max; + if (max_orig_node) + orig_node_free_ref(max_orig_node); + max_orig_node = orig_node; + } + rcu_read_unlock(); + } + last_max = max; + if (max_orig_node) { + res[select].type = DHT_CANDIDATE_ORIG; + res[select].orig_node = max_orig_node; + bat_dbg(DBG_ARP, bat_priv, "DHT_SEL_CAND %d: %pM %u\n", + select, max_orig_node->orig, max); + } + if (res[select].type == DHT_CANDIDATE_ME) { + chosen_me = true; + bat_dbg(DBG_ARP, bat_priv, "DHT_SEL_CAND %d: ME %u\n", + select, bat_priv->dht_hash); + } + + max_orig_node = NULL; + } + + return res; +} + +/* + * Sends the skb payload passed as argument to the candidates selected for + * 'ip'. The skb is copied by means of pskb_copy() and is sent as unicast packet + * to each of the selected candidate. */ +static bool dht_send_data(struct bat_priv *bat_priv, struct sk_buff *skb, + uint32_t ip) +{ + int i; + bool ret = false; + struct neigh_node *neigh_node = NULL; + struct sk_buff *tmp_skb; + struct dht_candidate *cand = dht_select_candidates(bat_priv, ip); + + if (!cand) + goto out; + + bat_dbg(DBG_ARP, bat_priv, "DHT_SEND for %pI4\n", &ip); + + for (i = 0; i < DHT_CANDIDATES_NUM; i++) { + if (cand[i].type == DHT_CANDIDATE_ME || + cand[i].type == DHT_CANDIDATE_NULL) + continue; + + neigh_node = orig_node_get_router(cand[i].orig_node); + if (!neigh_node) + goto free_orig; + + tmp_skb = pskb_copy(skb, GFP_ATOMIC); + tmp_skb = prepare_unicast_packet(tmp_skb, cand[i].orig_node); + if (tmp_skb) + send_skb_packet(tmp_skb, neigh_node->if_incoming, + neigh_node->addr); + /* set ret to true only if we send at least one request */ + ret = true; + neigh_node_free_ref(neigh_node); +free_orig: + orig_node_free_ref(cand[i].orig_node); + } + +out: + kfree(cand); + return ret; +} diff --git a/distributed-arp-table.h b/distributed-arp-table.h new file mode 100644 index 0000000..ee1a8b3 --- /dev/null +++ b/distributed-arp-table.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2011 B.A.T.M.A.N. contributors: + * + * Antonio Quartulli + * + * 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_ARP_H_ +#define _NET_BATMAN_ADV_ARP_H_ + +/* hash function to choose an entry in a hash table of given size */ +/* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ +static inline uint32_t hash_ipv4(const void *data, uint32_t size) +{ + const unsigned char *key = data; + uint32_t hash = 0; + size_t i; + + for (i = 0; i < 4; i++) { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash % size; +} + +#endif /* _NET_BATMAN_ADV_ARP_H_ */ diff --git a/hard-interface.c b/hard-interface.c index 7704df4..3508934 100644 --- a/hard-interface.c +++ b/hard-interface.c @@ -117,6 +117,8 @@ static void primary_if_update_addr(struct bat_priv *bat_priv) if (!primary_if) goto out;
+ bat_priv->dht_hash = choose_orig(bat_priv, USHRT_MAX); + vis_packet = (struct vis_packet *) bat_priv->my_vis_info->skb_packet->data; memcpy(vis_packet->vis_orig, primary_if->net_dev->dev_addr, ETH_ALEN); diff --git a/main.h b/main.h index f3978c0..2ca7b08 100644 --- a/main.h +++ b/main.h @@ -64,6 +64,9 @@
#define NULL_IFINDEX 0 /* dummy ifindex used to avoid iface checks */
+/* numbers of originator to contact for any STORE/GET DHT operation */ +#define DHT_CANDIDATES_NUM 3 + #define NUM_WORDS (TQ_LOCAL_WINDOW_SIZE / WORD_BIT_SIZE)
#define LOG_BUF_LEN 8192 /* has to be a power of 2 */ @@ -106,6 +109,10 @@ enum uev_type {
#define GW_THRESHOLD 50
+#define DHT_CANDIDATE_NULL 0 +#define DHT_CANDIDATE_ME 1 +#define DHT_CANDIDATE_ORIG 2 + /* * Debug Messages */ diff --git a/originator.c b/originator.c index 0bc2045..9673b44 100644 --- a/originator.c +++ b/originator.c @@ -222,6 +222,7 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, const uint8_t *addr) orig_node->tt_poss_change = false; orig_node->bat_priv = bat_priv; memcpy(orig_node->orig, addr, ETH_ALEN); + orig_node->dht_hash = choose_orig(addr, USHRT_MAX); orig_node->router = NULL; orig_node->tt_crc = 0; atomic_set(&orig_node->last_ttvn, 0); diff --git a/soft-interface.c b/soft-interface.c index 45297c8..56a6e10 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -855,6 +855,8 @@ struct net_device *softif_create(const char *name) bat_priv->primary_if = NULL; bat_priv->num_ifaces = 0;
+ bat_priv->dht_hash = 0; + ret = sysfs_add_meshif(soft_iface); if (ret < 0) goto unreg_soft_iface; diff --git a/types.h b/types.h index ab8d0fe..837bc42 100644 --- a/types.h +++ b/types.h @@ -67,6 +67,7 @@ struct hard_iface { struct orig_node { uint8_t orig[ETH_ALEN]; uint8_t primary_addr[ETH_ALEN]; + uint16_t dht_hash; struct neigh_node __rcu *router; /* rcu protected pointer */ unsigned long *bcast_own; uint8_t *bcast_own_sum; @@ -204,6 +205,7 @@ struct bat_priv { struct gw_node __rcu *curr_gw; /* rcu protected pointer */ atomic_t gw_reselect; struct hard_iface __rcu *primary_if; /* rcu protected pointer */ + uint16_t dht_hash; struct vis_info *my_vis_info; };
@@ -343,4 +345,9 @@ struct softif_neigh { struct rcu_head rcu; };
+struct dht_candidate { + int type; + struct orig_node *orig_node; +}; + #endif /* _NET_BATMAN_ADV_TYPES_H_ */
ARP messages are now parsed to make it possible to trigger special actions depending on their types (snooping).
Signed-off-by: Antonio Quartulli ordex@autistici.org ---
* now this patch is checkpatch --strict clean * corrected use of arp_hdr_len() * added print of ARP type as string (uses scnprintf())
distributed-arp-table.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++ distributed-arp-table.h | 8 +++++++ 2 files changed, 59 insertions(+), 0 deletions(-)
diff --git a/distributed-arp-table.c b/distributed-arp-table.c index 5bc3004..5fad513 100644 --- a/distributed-arp-table.c +++ b/distributed-arp-table.c @@ -179,3 +179,54 @@ out: kfree(cand); return ret; } + +/* Returns arphdr->ar_op if the skb contains a valid ARP packet, otherwise + * returns 0 */ +uint16_t arp_get_type(struct bat_priv *bat_priv, struct sk_buff *skb) +{ + struct arphdr *arphdr; + struct ethhdr *ethhdr; + uint16_t type = 0; + char buf[30], *type_str[] = { "REQUEST", "REPLY", "RREQUEST", "RREPLY", + "InREQUEST", "InREPLY", "NAK" }; + + if (unlikely(!pskb_may_pull(skb, ETH_HLEN))) + goto out; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + if (ethhdr->h_proto != htons(ETH_P_ARP)) + goto out; + + if (unlikely(!pskb_may_pull(skb, ETH_HLEN + arp_hdr_len(skb->dev)))) + goto out; + + arphdr = (struct arphdr *)(skb->data + sizeof(struct ethhdr)); + + /* Check whether the ARP packet carries a valid + * IP information */ + if (arphdr->ar_hrd != htons(ARPHRD_ETHER)) + goto out; + + if (arphdr->ar_pro != htons(ETH_P_IP)) + goto out; + + if (arphdr->ar_hln != ETH_ALEN) + goto out; + + if (arphdr->ar_pln != 4) + goto out; + + type = ntohs(arphdr->ar_op); + + if (type >= 1 && type <= 10) + scnprintf(buf, 30, "%s", type_str[type - 1]); + else + scnprintf(buf, 30, "UNKNOWN (%hu)", type); + + bat_dbg(DBG_ARP, bat_priv, "ARP message of type %s recognised " + "[%pM-%pI4 %pM-%pI4]\n", buf, ARP_HW_SRC(skb), &ARP_IP_SRC(skb), + ARP_HW_DST(skb), &ARP_IP_DST(skb)); +out: + return type; +} diff --git a/distributed-arp-table.h b/distributed-arp-table.h index ee1a8b3..439af22 100644 --- a/distributed-arp-table.h +++ b/distributed-arp-table.h @@ -22,6 +22,14 @@ #ifndef _NET_BATMAN_ADV_ARP_H_ #define _NET_BATMAN_ADV_ARP_H_
+#define ARP_HW_SRC(skb) ((uint8_t *)(skb->data) + sizeof(struct ethhdr) + \ + sizeof(struct arphdr)) +#define ARP_IP_SRC(skb) (*(uint32_t *)(ARP_HW_SRC(skb) + ETH_ALEN)) +#define ARP_HW_DST(skb) (ARP_HW_SRC(skb) + ETH_ALEN + 4) +#define ARP_IP_DST(skb) (*(uint32_t *)(ARP_HW_SRC(skb) + ETH_ALEN * 2 + 4)) + +uint16_t arp_get_type(struct bat_priv *bat_priv, struct sk_buff *skb); + /* hash function to choose an entry in a hash table of given size */ /* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ static inline uint32_t hash_ipv4(const void *data, uint32_t size)
In case of an ARP message going in or out the soft_iface, it is intercepted and a special action is performed. In particular the DHT helper functions previously implemented are used to store all the ARP entries belonging to the network in order to provide a fast and unicast lookup instead of the classic broadcast flooding mechanism. Each node stores the entries it is responsible for (following the DHT rules) in its soft_iface ARP table. This makes it possible to reuse the kernel data structures and functions for ARP management.
Signed-off-by: Antonio Quartulli ordex@autistici.org ---
* re-commented debug option in Makefile.kbuild * send the forged ARP reply to the soft_iface (with netrx_if()), not to the mesh * check arp_create() return value
distributed-arp-table.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++ distributed-arp-table.h | 6 ++ main.h | 2 + routing.c | 1 + send.c | 18 ++++ soft-interface.c | 23 +++++- 6 files changed, 252 insertions(+), 1 deletions(-)
diff --git a/distributed-arp-table.c b/distributed-arp-table.c index 5fad513..ea79a3e 100644 --- a/distributed-arp-table.c +++ b/distributed-arp-table.c @@ -21,6 +21,8 @@
#include <linux/if_ether.h> #include <linux/if_arp.h> +/* needed to use arp_tbl */ +#include <net/arp.h>
#include "main.h" #include "distributed-arp-table.h" @@ -180,6 +182,31 @@ out: return ret; }
+/* Update the neighbour entry corresponding to the IP passed as parameter with + * the hw address hw. If the neighbour entry doesn't exists, then it will be + * created */ +static void arp_neigh_update(struct bat_priv *bat_priv, uint32_t ip, + uint8_t *hw) +{ + struct neighbour *n = NULL; + struct hard_iface *primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + n = __neigh_lookup(&arp_tbl, &ip, primary_if->soft_iface, 1); + if (!n) + goto out; + + bat_dbg(DBG_ARP, bat_priv, "Updating neighbour: %pI4 - %pM\n", &ip, hw); + + neigh_update(n, hw, NUD_CONNECTED, NEIGH_UPDATE_F_OVERRIDE); +out: + if (n && !IS_ERR(n)) + neigh_release(n); + if (primary_if) + hardif_free_ref(primary_if); +} + /* Returns arphdr->ar_op if the skb contains a valid ARP packet, otherwise * returns 0 */ uint16_t arp_get_type(struct bat_priv *bat_priv, struct sk_buff *skb) @@ -230,3 +257,179 @@ uint16_t arp_get_type(struct bat_priv *bat_priv, struct sk_buff *skb) out: return type; } + +/* return true if the message has been sent to the dht candidates, false + * otherwise. In case of true the message has to be enqueued to permit the + * fallback */ +bool arp_snoop_outgoing_request(struct bat_priv *bat_priv, struct sk_buff *skb) +{ + uint16_t type = 0; + uint32_t ip_dst, ip_src; + uint8_t *hw_src; + bool ret = false; + struct neighbour *n = NULL; + struct hard_iface *primary_if = NULL; + struct sk_buff *skb_new; + + type = arp_get_type(bat_priv, skb); + /* If we get an ARP_REQUEST we have to send the unicast message to the + * selected DHT candidates */ + if (type != ARPOP_REQUEST) + goto out; + + bat_dbg(DBG_ARP, bat_priv, "Snooped outgoing ARP request\n"); + + ip_src = ARP_IP_SRC(skb); + hw_src = ARP_HW_SRC(skb); + ip_dst = ARP_IP_DST(skb); + + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + arp_neigh_update(bat_priv, ip_src, hw_src); + + n = neigh_lookup(&arp_tbl, &ip_dst, primary_if->soft_iface); + /* check if it is a valid neigh entry */ + if (n && (n->nud_state & NUD_CONNECTED)) { + skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src, + primary_if->soft_iface, ip_dst, hw_src, + n->ha, hw_src); + if (!skb_new) + goto out; + + skb_reset_mac_header(skb_new); + skb_new->protocol = eth_type_trans(skb_new, + primary_if->soft_iface); + bat_priv->stats.rx_packets++; + bat_priv->stats.rx_bytes += skb->len + sizeof(struct ethhdr); + primary_if->soft_iface->last_rx = jiffies; + + netif_rx(skb_new); + bat_dbg(DBG_ARP, bat_priv, "ARP request replied locally\n"); + } else + /* Send the request on the DHT */ + ret = dht_send_data(bat_priv, skb, ip_dst); +out: + if (n) + neigh_release(n); + if (primary_if) + hardif_free_ref(primary_if); + return ret; +} + +/* This function is meant to be invoked for an ARP request which is coming into + * the bat0 interfaces from the mesh network. It will check for the needed data + * into the local table. If found, an ARP reply is sent immediately, otherwise + * the caller has to deliver the ARP request to the upper layer */ +bool arp_snoop_incoming_request(struct bat_priv *bat_priv, struct sk_buff *skb) +{ + uint16_t type; + uint32_t ip_src, ip_dst; + uint8_t *hw_src; + struct hard_iface *primary_if = NULL; + struct sk_buff *skb_new; + struct neighbour *n = NULL; + bool ret = false; + + type = arp_get_type(bat_priv, skb); + if (type != ARPOP_REQUEST) + goto out; + + hw_src = ARP_HW_SRC(skb); + ip_src = ARP_IP_SRC(skb); + ip_dst = ARP_IP_DST(skb); + + bat_dbg(DBG_ARP, bat_priv, "Snooped incoming ARP request\n"); + + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + arp_neigh_update(bat_priv, ip_src, hw_src); + + n = neigh_lookup(&arp_tbl, &ip_dst, primary_if->soft_iface); + /* check if it is a valid neigh entry */ + if (!n || !(n->nud_state & NUD_CONNECTED)) + goto out; + + skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src, + primary_if->soft_iface, ip_dst, hw_src, n->ha, + hw_src); + + unicast_send_skb(skb_new, bat_priv); + + ret = true; +out: + if (n) + neigh_release(n); + if (primary_if) + hardif_free_ref(primary_if); + if (ret) + kfree_skb(skb); + return ret; +} + +/* This function is meant to be invoked on an ARP reply packet going into the + * soft interface. The related neighbour entry has to be updated and the DHT has + * to be populated as well */ +bool arp_snoop_outgoing_reply(struct bat_priv *bat_priv, struct sk_buff *skb) +{ + uint16_t type; + uint32_t ip_src, ip_dst; + uint8_t *hw_src, *hw_dst; + bool ret = false; + + type = arp_get_type(bat_priv, skb); + if (type != ARPOP_REPLY) + goto out; + + bat_dbg(DBG_ARP, bat_priv, "Snooped outgoing ARP reply\n"); + + hw_src = ARP_HW_SRC(skb); + ip_src = ARP_IP_SRC(skb); + hw_dst = ARP_HW_DST(skb); + ip_dst = ARP_IP_DST(skb); + + + arp_neigh_update(bat_priv, ip_src, hw_src); + arp_neigh_update(bat_priv, ip_dst, hw_dst); + + /* Send the ARP reply to the candidates for both the IP addresses we + * fetched from the ARP reply */ + dht_send_data(bat_priv, skb, ip_src); + dht_send_data(bat_priv, skb, ip_dst); + ret = true; +out: + return ret; +} + +/* This function has to be invoked on an ARP reply coming into the soft + * interface from the mesh network. The local table has to be updated */ +bool arp_snoop_incoming_reply(struct bat_priv *bat_priv, struct sk_buff *skb) +{ + uint16_t type; + uint32_t ip_src, ip_dst; + uint8_t *hw_src, *hw_dst; + bool ret = false; + + type = arp_get_type(bat_priv, skb); + if (type != ARPOP_REPLY) + goto out; + + bat_dbg(DBG_ARP, bat_priv, "Snooped incoming ARP reply\n"); + + hw_src = ARP_HW_SRC(skb); + ip_src = ARP_IP_SRC(skb); + hw_dst = ARP_HW_DST(skb); + ip_dst = ARP_IP_DST(skb); + + /* Update our internal cache with both the IP addresses we fetched from + * the ARP reply */ + arp_neigh_update(bat_priv, ip_src, hw_src); + arp_neigh_update(bat_priv, ip_dst, hw_dst); + + ret = true; +out: + return ret; +} diff --git a/distributed-arp-table.h b/distributed-arp-table.h index 439af22..d3fb8b1 100644 --- a/distributed-arp-table.h +++ b/distributed-arp-table.h @@ -29,6 +29,12 @@ #define ARP_IP_DST(skb) (*(uint32_t *)(ARP_HW_SRC(skb) + ETH_ALEN * 2 + 4))
uint16_t arp_get_type(struct bat_priv *bat_priv, struct sk_buff *skb); +bool arp_snoop_outgoing_request(struct bat_priv *bat_priv, + struct sk_buff *skb); +bool arp_snoop_incoming_request(struct bat_priv *bat_priv, + struct sk_buff *skb); +bool arp_snoop_outgoing_reply(struct bat_priv *bat_priv, struct sk_buff *skb); +bool arp_snoop_incoming_reply(struct bat_priv *bat_priv, struct sk_buff *skb);
/* hash function to choose an entry in a hash table of given size */ /* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ diff --git a/main.h b/main.h index 2ca7b08..13f63d8 100644 --- a/main.h +++ b/main.h @@ -64,6 +64,8 @@
#define NULL_IFINDEX 0 /* dummy ifindex used to avoid iface checks */
+/* msecs after which an ARP_REQUEST is sent in broadcast as fallback */ +#define ARP_REQ_DELAY 250 /* numbers of originator to contact for any STORE/GET DHT operation */ #define DHT_CANDIDATES_NUM 3
diff --git a/routing.c b/routing.c index ef24a72..ed76406 100644 --- a/routing.c +++ b/routing.c @@ -20,6 +20,7 @@ */
#include "main.h" +#include "distributed-arp-table.h" #include "routing.h" #include "send.h" #include "soft-interface.h" diff --git a/send.c b/send.c index 8a684eb..eaa67f6 100644 --- a/send.c +++ b/send.c @@ -20,6 +20,7 @@ */
#include "main.h" +#include "distributed-arp-table.h" #include "send.h" #include "routing.h" #include "translation-table.h" @@ -30,6 +31,8 @@ #include "originator.h" #include "bat_ogm.h"
+#include <net/arp.h> + static void send_outstanding_bcast_packet(struct work_struct *work);
/* send out an already prepared packet to the given address via the @@ -267,6 +270,7 @@ static void send_outstanding_bcast_packet(struct work_struct *work) struct sk_buff *skb1; struct net_device *soft_iface = forw_packet->if_incoming->soft_iface; struct bat_priv *bat_priv = netdev_priv(soft_iface); + struct neighbour *n;
spin_lock_bh(&bat_priv->forw_bcast_list_lock); hlist_del(&forw_packet->list); @@ -275,6 +279,20 @@ static void send_outstanding_bcast_packet(struct work_struct *work) if (atomic_read(&bat_priv->mesh_state) == MESH_DEACTIVATING) goto out;
+ /* If this packet is an ARP_REQUEST and we already have the information + * that it is going to ask, we can drop the packet */ + if (!forw_packet->num_packets && + (arp_get_type(bat_priv, forw_packet->skb) == + ARPOP_REQUEST)) { + n = neigh_lookup(&arp_tbl, &ARP_IP_DST(forw_packet->skb), + soft_iface); + /* check if we already know this neigh */ + if (n && (n->nud_state & NUD_CONNECTED)) + goto out; + + bat_dbg(DBG_ARP, bat_priv, "ARP request: fallback\n"); + } + /* rebroadcast packet */ rcu_read_lock(); list_for_each_entry_rcu(hard_iface, &hardif_list, list) { diff --git a/soft-interface.c b/soft-interface.c index 56a6e10..91b0d40 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -22,6 +22,7 @@ #include "main.h" #include "soft-interface.h" #include "hard-interface.h" +#include "distributed-arp-table.h" #include "routing.h" #include "send.h" #include "bat_debugfs.h" @@ -564,9 +565,12 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) struct vlan_ethhdr *vhdr; struct softif_neigh *curr_softif_neigh = NULL; unsigned int header_len = 0; + struct orig_node *orig_node = NULL; + struct sk_buff *arp_skb = NULL; int data_len = skb->len, ret; short vid = -1; bool do_bcast = false; + unsigned long brd_delay = 1;
if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE) goto dropped; @@ -628,6 +632,9 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) if (!primary_if) goto dropped;
+ if (arp_snoop_outgoing_request(bat_priv, skb)) + brd_delay = msecs_to_jiffies(ARP_REQ_DELAY); + if (my_skb_head_push(skb, sizeof(*bcast_packet)) < 0) goto dropped;
@@ -647,7 +654,7 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) bcast_packet->seqno = htonl(atomic_inc_return(&bat_priv->bcast_seqno));
- add_bcast_packet_to_list(bat_priv, skb, 1); + add_bcast_packet_to_list(bat_priv, skb, brd_delay);
/* a copy is stored in the bcast list, therefore removing * the original skb. */ @@ -661,7 +668,12 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) goto dropped; }
+ /* Increase the refcount to avoid to make the kernel consume + * the skb */ + arp_skb = skb_clone(skb, GFP_ATOMIC); ret = unicast_send_skb(skb, bat_priv); + + arp_snoop_outgoing_reply(bat_priv, arp_skb); if (ret != 0) goto dropped_freed; } @@ -679,6 +691,10 @@ end: softif_neigh_free_ref(curr_softif_neigh); if (primary_if) hardif_free_ref(primary_if); + if (orig_node) + orig_node_free_ref(orig_node); + if (arp_skb) + kfree_skb(arp_skb); return NETDEV_TX_OK; }
@@ -716,6 +732,11 @@ void interface_rx(struct net_device *soft_iface, goto dropped; }
+ if (arp_snoop_incoming_request(bat_priv, skb)) + goto out; + + arp_snoop_incoming_reply(bat_priv, skb); + /** * if we have a another chosen mesh exit node in range * it will transport the packets to the non-mesh network
The default timeout value for ARP entries belonging to any soft_iface ARP table has been incremented by a factor 4. This is necessary because the DHT will store several network entries in the soft_iface ARP table.
Signed-off-by: Antonio Quartulli ordex@autistici.org ---
- nothing changed
main.h | 4 ++++ soft-interface.c | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 0 deletions(-)
diff --git a/main.h b/main.h index 13f63d8..8c534b4 100644 --- a/main.h +++ b/main.h @@ -69,6 +69,10 @@ /* numbers of originator to contact for any STORE/GET DHT operation */ #define DHT_CANDIDATES_NUM 3
+/* Factor which default ARP timeout values of the soft_iface table are + * multiplied by */ +#define ARP_TIMEOUT_FACTOR 4 + #define NUM_WORDS (TQ_LOCAL_WINDOW_SIZE / WORD_BIT_SIZE)
#define LOG_BUF_LEN 8192 /* has to be a power of 2 */ diff --git a/soft-interface.c b/soft-interface.c index 91b0d40..a8f8a60 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -36,6 +36,7 @@ #include <linux/ethtool.h> #include <linux/etherdevice.h> #include <linux/if_vlan.h> +#include <linux/inetdevice.h> #include "unicast.h"
@@ -832,6 +833,7 @@ static void interface_setup(struct net_device *dev) struct net_device *softif_create(const char *name) { struct net_device *soft_iface; + struct in_device *in_dev; struct bat_priv *bat_priv; int ret;
@@ -847,6 +849,21 @@ struct net_device *softif_create(const char *name) goto free_soft_iface; }
+ in_dev = in_dev_get(soft_iface); + if (!in_dev) { + pr_err("Unable to set ARP parameters for the batman interface " + "'%s'\n", name); + goto free_soft_iface; + } + + /* Introduce a delay in the ARP state-machine transactions. Entries + * will be kept in the ARP table for the default time multiplied by 4 */ + in_dev->arp_parms->base_reachable_time *= ARP_TIMEOUT_FACTOR; + in_dev->arp_parms->gc_staletime *= ARP_TIMEOUT_FACTOR; + in_dev->arp_parms->reachable_time *= ARP_TIMEOUT_FACTOR; + + in_dev_put(in_dev); + bat_priv = netdev_priv(soft_iface);
atomic_set(&bat_priv->aggregated_ogms, 1);
Signed-off-by: Antonio Quartulli ordex@autistici.org --- Makefile.kbuild | 8 +++++++- README | 4 ++++ distributed-arp-table.h | 12 ++++++++++++ 3 files changed, 23 insertions(+), 1 deletions(-)
diff --git a/Makefile.kbuild b/Makefile.kbuild index e8861cb..f09004f 100644 --- a/Makefile.kbuild +++ b/Makefile.kbuild @@ -18,6 +18,8 @@ # 02110-1301, USA #
+# uncomment the following line to enable the Distributed ARP Table feature +# CONFIG_BATMAN_ADV_DAT=y
# openwrt integration @@ -27,6 +29,10 @@ endif
# ccflags-y += -DCONFIG_BATMAN_ADV_DEBUG
+ifeq ($(CONFIG_BATMAN_ADV_DAT), y) +ccflags-y += -DCONFIG_BATMAN_ADV_DAT +endif + ifneq ($(REVISION),) ccflags-y += -DSOURCE_VERSION="$(REVISION)" endif @@ -36,7 +42,7 @@ batman-adv-y += bat_debugfs.o batman-adv-y += bat_iv_ogm.o batman-adv-y += bat_sysfs.o batman-adv-y += bitarray.o -batman-adv-y += distributed-arp-table.o +batman-adv-$(CONFIG_BATMAN_ADV_DAT) += distributed-arp-table.o batman-adv-y += gateway_client.o batman-adv-y += gateway_common.o batman-adv-y += hard-interface.o diff --git a/README b/README index 5ba56b5..1a27f9f 100644 --- a/README +++ b/README @@ -26,6 +26,10 @@ it. If you work on a backport, feel free to contact us. :-) COMPILE -------
+Before compiling you want to have a look at the Makefile.kbuild +file to enable/disable wanted features. Actually there are: +- CONFIG_BATMAN_ADV_DAT enables the Distributed ARP Table + To compile against your currently installed kernel, just type:
# make diff --git a/distributed-arp-table.h b/distributed-arp-table.h index d3fb8b1..496b7f3 100644 --- a/distributed-arp-table.h +++ b/distributed-arp-table.h @@ -28,6 +28,8 @@ #define ARP_HW_DST(skb) (ARP_HW_SRC(skb) + ETH_ALEN + 4) #define ARP_IP_DST(skb) (*(uint32_t *)(ARP_HW_SRC(skb) + ETH_ALEN * 2 + 4))
+#ifdef CONFIG_BATMAN_ADV_BAT + uint16_t arp_get_type(struct bat_priv *bat_priv, struct sk_buff *skb); bool arp_snoop_outgoing_request(struct bat_priv *bat_priv, struct sk_buff *skb); @@ -36,6 +38,16 @@ bool arp_snoop_incoming_request(struct bat_priv *bat_priv, bool arp_snoop_outgoing_reply(struct bat_priv *bat_priv, struct sk_buff *skb); bool arp_snoop_incoming_reply(struct bat_priv *bat_priv, struct sk_buff *skb);
+#else + +#define arp_get_type(...) (0) +#define arp_snoop_outgoing_request(...) (0) +#define arp_snoop_incoming_request(...) (0) +#define arp_snoop_outgoing_reply(...) +#define arp_snoop_incoming_reply(...) + +#endif + /* hash function to choose an entry in a hash table of given size */ /* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ static inline uint32_t hash_ipv4(const void *data, uint32_t size)
On Tue, Nov 08, 2011 at 12:34:22PM +0100, Antonio Quartulli wrote:
Signed-off-by: Antonio Quartulli ordex@autistici.org
Makefile.kbuild | 8 +++++++- README | 4 ++++ distributed-arp-table.h | 12 ++++++++++++ 3 files changed, 23 insertions(+), 1 deletions(-)
diff --git a/Makefile.kbuild b/Makefile.kbuild index e8861cb..f09004f 100644 --- a/Makefile.kbuild +++ b/Makefile.kbuild @@ -18,6 +18,8 @@ # 02110-1301, USA #
+# uncomment the following line to enable the Distributed ARP Table feature +# CONFIG_BATMAN_ADV_DAT=y
# openwrt integration @@ -27,6 +29,10 @@ endif
# ccflags-y += -DCONFIG_BATMAN_ADV_DEBUG
+ifeq ($(CONFIG_BATMAN_ADV_DAT), y) +ccflags-y += -DCONFIG_BATMAN_ADV_DAT +endif
ifneq ($(REVISION),) ccflags-y += -DSOURCE_VERSION="$(REVISION)" endif @@ -36,7 +42,7 @@ batman-adv-y += bat_debugfs.o batman-adv-y += bat_iv_ogm.o batman-adv-y += bat_sysfs.o batman-adv-y += bitarray.o -batman-adv-y += distributed-arp-table.o +batman-adv-$(CONFIG_BATMAN_ADV_DAT) += distributed-arp-table.o batman-adv-y += gateway_client.o batman-adv-y += gateway_common.o batman-adv-y += hard-interface.o diff --git a/README b/README index 5ba56b5..1a27f9f 100644 --- a/README +++ b/README @@ -26,6 +26,10 @@ it. If you work on a backport, feel free to contact us. :-) COMPILE
+Before compiling you want to have a look at the Makefile.kbuild +file to enable/disable wanted features. Actually there are: +- CONFIG_BATMAN_ADV_DAT enables the Distributed ARP Table
To compile against your currently installed kernel, just type:
# make diff --git a/distributed-arp-table.h b/distributed-arp-table.h index d3fb8b1..496b7f3 100644 --- a/distributed-arp-table.h +++ b/distributed-arp-table.h @@ -28,6 +28,8 @@ #define ARP_HW_DST(skb) (ARP_HW_SRC(skb) + ETH_ALEN + 4) #define ARP_IP_DST(skb) (*(uint32_t *)(ARP_HW_SRC(skb) + ETH_ALEN * 2 + 4))
+#ifdef CONFIG_BATMAN_ADV_BAT
Took me some time to find this typo :P please use CONFIG_BATMAN_ADV_DAT, not CONFIG_BATMAN_ADV_BAT or else it won't compile.
uint16_t arp_get_type(struct bat_priv *bat_priv, struct sk_buff *skb); bool arp_snoop_outgoing_request(struct bat_priv *bat_priv, struct sk_buff *skb); @@ -36,6 +38,16 @@ bool arp_snoop_incoming_request(struct bat_priv *bat_priv, bool arp_snoop_outgoing_reply(struct bat_priv *bat_priv, struct sk_buff *skb); bool arp_snoop_incoming_reply(struct bat_priv *bat_priv, struct sk_buff *skb);
+#else
+#define arp_get_type(...) (0) +#define arp_snoop_outgoing_request(...) (0) +#define arp_snoop_incoming_request(...) (0) +#define arp_snoop_outgoing_reply(...) +#define arp_snoop_incoming_reply(...)
+#endif
/* hash function to choose an entry in a hash table of given size */ /* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ static inline uint32_t hash_ipv4(const void *data, uint32_t size) -- 1.7.3.4
On Tue, Nov 15, 2011 at 09:27:23PM +0100, Simon Wunderlich wrote:
+#ifdef CONFIG_BATMAN_ADV_BAT
Took me some time to find this typo :P please use CONFIG_BATMAN_ADV_DAT, not CONFIG_BATMAN_ADV_BAT or else it won't compile.
nice catch! Don't ask me when I introduced this typ0 because I don't know! Thank Simon,
* use CONFIG_BATMAN_ADV_DAT instead of CONFIG_BATMAN_ADV_BAT * use skb_reset_mac_header() to avoid crashes because of null pointers within arp_get_type()
I've reviewed the DAT patchset, all of the previous comments have been integrated well. It also runs well within my VMs (after applying this patch).
Please integrate this patch into the previous one and resend it. You may add my Reviewed-By tag.
Signed-off-by: Simon Wunderlich siwu@hrz.tu-chemnitz.de --- distributed-arp-table.h | 2 +- soft-interface.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/distributed-arp-table.h b/distributed-arp-table.h index 496b7f3..bd32c89 100644 --- a/distributed-arp-table.h +++ b/distributed-arp-table.h @@ -28,7 +28,7 @@ #define ARP_HW_DST(skb) (ARP_HW_SRC(skb) + ETH_ALEN + 4) #define ARP_IP_DST(skb) (*(uint32_t *)(ARP_HW_SRC(skb) + ETH_ALEN * 2 + 4))
-#ifdef CONFIG_BATMAN_ADV_BAT +#ifdef CONFIG_BATMAN_ADV_DAT
uint16_t arp_get_type(struct bat_priv *bat_priv, struct sk_buff *skb); bool arp_snoop_outgoing_request(struct bat_priv *bat_priv, diff --git a/soft-interface.c b/soft-interface.c index 9107b22..786bc71 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -592,6 +592,8 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) goto end; }
+ skb_reset_mac_header(skb); + /** * if we have a another chosen mesh exit node in range * it will transport the packets to the mesh
b.a.t.m.a.n@lists.open-mesh.org