Hello people,
**** This is the third version of this patchset. The whole code has been reviewed (Thanks Simon!) and a bug related to the skb_mac_header() return value has been fixed (thanks again Simon!). ****
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 Reviewed-by: Simon Wunderlich siwu@hrz.tu-chemnitz.de --- 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 Reviewed-by: Simon Wunderlich siwu@hrz.tu-chemnitz.de --- 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 464439f..e706193 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 Reviewed-by: Simon Wunderlich siwu@hrz.tu-chemnitz.de --- 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 e706193..7b4229c 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 987c75a..cd6c9ea 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 e9eb043..5f0030e 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 Reviewed-by: Simon Wunderlich siwu@hrz.tu-chemnitz.de --- 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)
On Mon, Nov 21, 2011 at 11:53:10PM +0100, Antonio Quartulli wrote:
Hi Antonio
+/* 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" };
type_str could be marked as const. Saves a little bit of space on the BSS.
- if (type >= 1 && type <= 10)
scnprintf(buf, 30, "%s", type_str[type - 1]);
type_str currently has 6 entries, so if somebody sends you an ARP request with type greater than 6, you go off the end of the array. Better to use ARRAY_SIZE().
Andrew
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 Reviewed-by: Simon Wunderlich siwu@hrz.tu-chemnitz.de --- distributed-arp-table.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++ distributed-arp-table.h | 6 ++ main.h | 2 + routing.c | 1 + send.c | 18 ++++ soft-interface.c | 25 ++++++- 6 files changed, 254 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 7b4229c..2b1c4b4 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 cd6c9ea..5ef9bba 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; @@ -587,6 +591,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 @@ -628,6 +634,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 +656,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 +670,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 +693,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 +734,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
Hi Antonio
- 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);
Can arp_create fail? Should you check the return value before calling unicast_send_skb()?
Andrew
Hello Andrew,
On Wed, Nov 23, 2011 at 05:11:51 +0100, Andrew Lunn wrote:
Hi Antonio
- 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);
Can arp_create fail? Should you check the return value before calling unicast_send_skb()?
Thank you for the comment. Anyway this patchset is now a bit "old", because after posting it I continued to work on it and to correct some bugs (like this one).
Therefore I would not go ahead in inspecting these patches. Sorry for this.
Cheers,
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 Reviewed-by: Simon Wunderlich siwu@hrz.tu-chemnitz.de --- main.h | 4 ++++ soft-interface.c | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 0 deletions(-)
diff --git a/main.h b/main.h index 2b1c4b4..6216abd 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 5ef9bba..786bc71 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"
@@ -834,6 +835,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;
@@ -849,6 +851,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);
This patch makes it possible possible to decide whether to compile DAT or not.
Signed-off-by: Antonio Quartulli ordex@autistici.org Reviewed-by: Simon Wunderlich siwu@hrz.tu-chemnitz.de --- 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 related feature +# CONFIG_BATMAN_ADV_DAT=y # Distributed ARP Table
# 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 b4fdc8d..8a50eda 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..bd32c89 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_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, 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)
b.a.t.m.a.n@lists.open-mesh.org