[v4,5/7] batman-adv: Distributed ARP Table - add snooping functions for ARP messages
Commit Message
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>
---
distributed-arp-table.c | 226 +++++++++++++++++++++++++++++++++++++++++++++++
distributed-arp-table.h | 10 ++
main.h | 2 +
send.c | 6 ++
soft-interface.c | 16 +++-
5 files changed, 259 insertions(+), 1 deletions(-)
Comments
On Friday, November 25, 2011 06:21:17 Antonio Quartulli wrote:
> @@ -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);
What is this include doing here ? If I am not mistaken you removed it from
your patchset already. Is this really the latest version ?
Regards,
Marek
Hi Antonio
General question. In the Linux ARP decode code is:
/*
* Check for bad requests for 127.x.x.x and requests for multicast
* addresses. If this is one such, delete it.
*/
if (ipv4_is_loopback(tip) || ipv4_is_multicast(tip))
goto out;
I don't see the same filtering here. What would happen if you did
receiver and cached such a bad request?
In a similar direction, how does duplicate address detection work?
i.e. i ARP my own address to see if somebody else is using it?
Or do i just need to RTFM your GSOC documentation :-)
Andrew
On Fri, Nov 25, 2011 at 09:45:56AM +0100, Andrew Lunn wrote:
> Hi Antonio
>
> General question. In the Linux ARP decode code is:
>
> /*
> * Check for bad requests for 127.x.x.x and requests for multicast
> * addresses. If this is one such, delete it.
> */
> if (ipv4_is_loopback(tip) || ipv4_is_multicast(tip))
> goto out;
>
> I don't see the same filtering here. What would happen if you did
> receiver and cached such a bad request?
atually there isnot such control over the arp message content. In case
of, let's say, a malicious ARP message of this type, it is stored like
any other one.
>
> In a similar direction, how does duplicate address detection work?
> i.e. i ARP my own address to see if somebody else is using it?
>
> Or do i just need to RTFM your GSOC documentation :-)
>
Don't think so. Actually I/we didn't think too much about this kind of
cases. Well, a duplicate entry is simply overwritten: I mean, if we
already have the entry [IPa,MACa] in the table, any other ARP reply containing
[IPa,MACb] will update the older one and MACa will be lost.
Do you expect a different behaviour? Can I do it better?
Cheers,
On Fri, Nov 25, 2011 at 12:17:08PM +0100, Antonio Quartulli wrote:
> On Fri, Nov 25, 2011 at 09:45:56AM +0100, Andrew Lunn wrote:
> > Hi Antonio
> >
> > General question. In the Linux ARP decode code is:
> >
> > /*
> > * Check for bad requests for 127.x.x.x and requests for multicast
> > * addresses. If this is one such, delete it.
> > */
> > if (ipv4_is_loopback(tip) || ipv4_is_multicast(tip))
> > goto out;
> >
> > I don't see the same filtering here. What would happen if you did
> > receiver and cached such a bad request?
>
> atually there isnot such control over the arp message content. In case
> of, let's say, a malicious ARP message of this type, it is stored like
> any other one.
It might make sense to drop such messages, since they are
invalid. However, nothing obvious comes to mind which would go wrong
if you did cache them, other than somebody could DOS you by sending
lots of ARP entries for multicast addresses.
> > In a similar direction, how does duplicate address detection work?
> > i.e. i ARP my own address to see if somebody else is using it?
> Don't think so. Actually I/we didn't think too much about this kind of
> cases. Well, a duplicate entry is simply overwritten: I mean, if we
> already have the entry [IPa,MACa] in the table, any other ARP reply containing
> [IPa,MACb] will update the older one and MACa will be lost.
The basic idea with duplicate address detection is to send out an ARP
request for your own address. If you get an answer, you know somebody
is using the address. I think Windoz then shuts the interface down, or
at least gives a warning. So in the case of duplicate address
detection, you want to fallback to broadcasting the ARP request and
see if anybody answers. You can detect if a node is performing
aduplicate detection, if the ARP requests source MAC address is the
same as the answer in the cache. If so, fall back to broadcasting
rather than answering from the cache.
Looking at RFC 3927 might also be interesting, since it uses ARP
messages in a different way.
Also, i know some dhcp servers try to ping an IP address before giving
it out, just to be sure it is not in use. Answering the ARP request
from what could be an out of date cache entry doesn't i think causes a
problem, so long as the ping that follows it does not get
answered. But maybe some DHCP servers just perform an ARP request?
Some things to think about...
Andrew
On Fri, Nov 25, 2011 at 10:09:11 +0100, Andrew Lunn wrote:
> It might make sense to drop such messages, since they are
> invalid. However, nothing obvious comes to mind which would go wrong
> if you did cache them, other than somebody could DOS you by sending
> lots of ARP entries for multicast addresses.
Yes, I was thinking the same. A DOS is the major threat we could face without
this validity check. However, since we use the kernel function to add such
entries into the table it might be that the kernel already does this checks for
us. I'll look towards it
>
> > > In a similar direction, how does duplicate address detection work?
> > > i.e. i ARP my own address to see if somebody else is using it?
>
> > Don't think so. Actually I/we didn't think too much about this kind of
> > cases. Well, a duplicate entry is simply overwritten: I mean, if we
> > already have the entry [IPa,MACa] in the table, any other ARP reply containing
> > [IPa,MACb] will update the older one and MACa will be lost.
>
> The basic idea with duplicate address detection is to send out an ARP
> request for your own address. If you get an answer, you know somebody
> is using the address. I think Windoz then shuts the interface down, or
> at least gives a warning. So in the case of duplicate address
> detection, you want to fallback to broadcasting the ARP request and
> see if anybody answers. You can detect if a node is performing
> aduplicate detection, if the ARP requests source MAC address is the
> same as the answer in the cache. If so, fall back to broadcasting
> rather than answering from the cache.
>
> Looking at RFC 3927 might also be interesting, since it uses ARP
> messages in a different way.
>
> Also, i know some dhcp servers try to ping an IP address before giving
> it out, just to be sure it is not in use. Answering the ARP request
> from what could be an out of date cache entry doesn't i think causes a
> problem, so long as the ping that follows it does not get
> answered. But maybe some DHCP servers just perform an ARP request?
>
Mh...but I think this behaviour is somehow left untouched. The mechanisms you
are describing are higher-level matters. Since we only limit to fill tables and
get answer, all the other mechanisms/procedures which use the tables should
still continue to work as they are. If a windows client will issue an ARP req to
see is someone else is using the same IP, DAT will simply answer as a normal
table would do. Therefore I think that this kind of worries are not matter of
DAT.
I hope I clearly explained my thought.
Then we could always add more feature in order to "facilitate" such mechanisms,
but up to now I think that DAT simply provides the same behaviour a normal table
would do.
Thank you for your comments!
Cheers,
@@ -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"
@@ -197,6 +199,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 */
static uint16_t arp_get_type(struct bat_priv *bat_priv, struct sk_buff *skb)
@@ -239,3 +266,202 @@ static 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);
+
+ if (!skb_new)
+ goto out;
+
+ 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;
+}
+
+bool arp_drop_broadcast_packet(struct bat_priv *bat_priv,
+ struct forw_packet *forw_packet)
+{
+ struct neighbour *n;
+
+ /* 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),
+ forw_packet->if_incoming->soft_iface);
+ /* check if we already know this neigh */
+ if (n && (n->nud_state & NUD_CONNECTED))
+ return true;
+
+ bat_dbg(DBG_ARP, bat_priv, "ARP request: fallback\n");
+ }
+ return false;
+}
@@ -27,6 +27,7 @@
#include <linux/if_arp.h>
struct bat_priv;
+struct forw_packet;
/*
* dat_addr_t is the type used for all DHT indexes. If it is changed,
@@ -43,6 +44,15 @@ struct bat_priv;
#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))
+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);
+bool arp_drop_broadcast_packet(struct bat_priv *bat_priv,
+ struct forw_packet *forw_packet);
+
/* 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)
@@ -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
@@ -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
@@ -275,6 +278,9 @@ static void send_outstanding_bcast_packet(struct work_struct *work)
if (atomic_read(&bat_priv->mesh_state) == MESH_DEACTIVATING)
goto out;
+ if (arp_drop_broadcast_packet(bat_priv, forw_packet))
+ goto out;
+
/* rebroadcast packet */
rcu_read_lock();
list_for_each_entry_rcu(hard_iface, &hardif_list, list) {
@@ -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"
@@ -567,6 +568,7 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
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 +589,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 +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,6 +668,8 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
goto dropped;
}
+ arp_snoop_outgoing_reply(bat_priv, skb);
+
ret = unicast_send_skb(skb, bat_priv);
if (ret != 0)
goto dropped_freed;
@@ -716,6 +725,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