[3/3] batman-adv: send DHCP requests directly to the chosen gw

Message ID 1262619976-5632-3-git-send-email-lindner_marek@yahoo.de (mailing list archive)
State Accepted, archived
Headers

Commit Message

Marek Lindner Jan. 4, 2010, 3:46 p.m. UTC
  If the gateway client mode is active batman-adv will send the broadcasted
DHCP requests via unicast to the currently selected best gateway. Therefore
attached clients can profit from batman's knowledge about the network
topology.

Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
---
 batman-adv-kernelland/gateway_client.c |   45 ++++++++++++++++++++++++++++++++
 batman-adv-kernelland/gateway_client.h |    2 +
 batman-adv-kernelland/soft-interface.c |   17 ++++++++++--
 3 files changed, 61 insertions(+), 3 deletions(-)
  

Comments

Andrew Lunn Jan. 5, 2010, 6:43 a.m. UTC | #1
> +bool gw_is_target(struct sk_buff *skb)
> +{
> +	struct ethhdr *ethhdr;
> +	struct iphdr *iphdr;
> +	struct udphdr *udphdr;
> +
> +	if (atomic_read(&gw_mode) != GW_MODE_CLIENT)
> +		return false;
> +
> +	if (!curr_gateway)
> +		return false;
> +
> +	ethhdr = (struct ethhdr *)skb->data;
> +	if (ntohs(ethhdr->h_proto) != ETH_P_IP)
> +		return false;
> +
> +	iphdr = (struct iphdr *)(skb->data + ETH_HLEN);
> +
> +	if (iphdr->protocol != IPPROTO_UDP)
> +		return false;
> +
> +	udphdr = (struct udphdr *)(skb->data + ETH_HLEN + (iphdr->ihl * 4));
> +
> +	if (ntohs(udphdr->dest) != 67)
> +		return false;
> +
> +	return true;
> +}
> -	/* ethernet packet should be broadcasted */
> -	if (is_bcast(ethhdr->h_dest) || is_mcast(ethhdr->h_dest)) {
> +	if (is_bcast(ethhdr->h_dest) || is_mcast(ethhdr->h_dest))
> +		bcast_dst = true;
> +
> +	if ((bcast_dst) && gw_is_target(skb))
> +		do_bcast = false;

Say the DHCP server is running in client mode. It has also been
requested to broadcast its replies, not unicast the replies.

http://blogs.technet.com/teamdhcp/archive/2009/02/12/dhcp-broadcast-flag-handling-in-windows-7.aspx

If i'm reading this code correctly, it will end up sending the DHCP
reply messages by unicast to the best gateway, not the DHCP client?

      Andrew
  
Marek Lindner Jan. 5, 2010, 10 a.m. UTC | #2
On Tuesday 05 January 2010 14:43:49 you wrote:
> Say the DHCP server is running in client mode. It has also been
> requested to broadcast its replies, not unicast the replies.
> 
> http://blogs.technet.com/teamdhcp/archive/2009/02/12/dhcp-broadcast-flag-ha
> ndling-in-windows-7.aspx
> 
> If i'm reading this code correctly, it will end up sending the DHCP
> reply messages by unicast to the best gateway, not the DHCP client?

I don't really get your question.
The batman-adv gwclient will encapsulate the DHCP discover / request packets 
in ethernet unicast headers instead of broadcast headers to make sure only the 
best gateway receives these broadcasts.
The server's replies won't be affected at all.

Regards,
Marek
  
Andrew Lunn Jan. 5, 2010, 11:09 a.m. UTC | #3
> On Tuesday 05 January 2010 14:43:49 you wrote:
> > Say the DHCP server is running in client mode. It has also been
> > requested to broadcast its replies, not unicast the replies.
> >
> > http://blogs.technet.com/teamdhcp/archive/2009/02/12/dhcp-broadcast-flag-ha
> > ndling-in-windows-7.aspx
> >
> > If i'm reading this code correctly, it will end up sending the DHCP
> > reply messages by unicast to the best gateway, not the DHCP client?
>
> I don't really get your question.
> The batman-adv gwclient will encapsulate the DHCP discover / request packets
> in ethernet unicast headers instead of broadcast headers to make sure only the
> best gateway receives these broadcasts.
> The server's replies won't be affected at all.

Ah, just read the RFC. I though the client was free to choose its
port, so it could choose port 67. If that was true, gw_is_target()
would return true for the reply and the reply would then be sent to
the gateway.

However, the RFC says the client must use port 68, so this can never
happen. So there is no problem here...

        Andrew
  

Patch

diff --git a/batman-adv-kernelland/gateway_client.c b/batman-adv-kernelland/gateway_client.c
index 55789ad..434af2b 100644
--- a/batman-adv-kernelland/gateway_client.c
+++ b/batman-adv-kernelland/gateway_client.c
@@ -20,6 +20,8 @@ 
 #include "main.h"
 #include "gateway_client.h"
 #include "gateway_common.h"
+#include <linux/ip.h>
+#include <linux/udp.h>
 
 LIST_HEAD(gw_list);
 DEFINE_SPINLOCK(curr_gw_lock);
@@ -27,6 +29,20 @@  DEFINE_SPINLOCK(gw_list_lock);
 atomic_t gw_clnt_class;
 static struct gw_node *curr_gateway;
 
+void *gw_get_selected(void)
+{
+	struct gw_node *curr_gateway_tmp = NULL;
+
+	spin_lock(&curr_gw_lock);
+	curr_gateway_tmp = curr_gateway;
+	spin_unlock(&curr_gw_lock);
+
+	if (!curr_gateway_tmp)
+		return NULL;
+
+	return curr_gateway_tmp->orig_node;
+}
+
 void gw_deselect(void)
 {
 	spin_lock(&curr_gw_lock);
@@ -333,3 +349,32 @@  int gw_client_fill_buffer_text(unsigned char *buff, int buff_len)
 
 	return bytes_written;
 }
+
+bool gw_is_target(struct sk_buff *skb)
+{
+	struct ethhdr *ethhdr;
+	struct iphdr *iphdr;
+	struct udphdr *udphdr;
+
+	if (atomic_read(&gw_mode) != GW_MODE_CLIENT)
+		return false;
+
+	if (!curr_gateway)
+		return false;
+
+	ethhdr = (struct ethhdr *)skb->data;
+	if (ntohs(ethhdr->h_proto) != ETH_P_IP)
+		return false;
+
+	iphdr = (struct iphdr *)(skb->data + ETH_HLEN);
+
+	if (iphdr->protocol != IPPROTO_UDP)
+		return false;
+
+	udphdr = (struct udphdr *)(skb->data + ETH_HLEN + (iphdr->ihl * 4));
+
+	if (ntohs(udphdr->dest) != 67)
+		return false;
+
+	return true;
+}
diff --git a/batman-adv-kernelland/gateway_client.h b/batman-adv-kernelland/gateway_client.h
index 5eb1e4c..fc7a0df 100644
--- a/batman-adv-kernelland/gateway_client.h
+++ b/batman-adv-kernelland/gateway_client.h
@@ -21,9 +21,11 @@  extern atomic_t gw_clnt_class;
 
 void gw_deselect(void);
 void gw_election(void);
+void *gw_get_selected(void);
 void gw_check_election(struct orig_node *orig_node);
 void gw_node_update(struct orig_node *orig_node, uint8_t new_gwflags);
 void gw_node_delete(struct orig_node *orig_node);
 void gw_node_purge_deleted(void);
 void gw_node_list_free(void);
 int gw_client_fill_buffer_text(unsigned char *buff, int buff_len);
+bool gw_is_target(struct sk_buff *skb);
diff --git a/batman-adv-kernelland/soft-interface.c b/batman-adv-kernelland/soft-interface.c
index ee9aa39..80f7a23 100644
--- a/batman-adv-kernelland/soft-interface.c
+++ b/batman-adv-kernelland/soft-interface.c
@@ -26,6 +26,7 @@ 
 #include "translation-table.h"
 #include "types.h"
 #include "hash.h"
+#include "gateway_client.h"
 #include <linux/ethtool.h>
 #include <linux/etherdevice.h>
 #include "compat.h"
@@ -181,6 +182,7 @@  int interface_tx(struct sk_buff *skb, struct net_device *dev)
 	uint8_t dstaddr[6];
 	int data_len = skb->len;
 	unsigned long flags;
+	bool bcast_dst = false, do_bcast = true;
 
 	if (atomic_read(&module_state) != MODULE_ACTIVE)
 		goto dropped;
@@ -189,9 +191,14 @@  int interface_tx(struct sk_buff *skb, struct net_device *dev)
 	/* TODO: check this for locks */
 	hna_local_add(ethhdr->h_source);
 
-	/* ethernet packet should be broadcasted */
-	if (is_bcast(ethhdr->h_dest) || is_mcast(ethhdr->h_dest)) {
+	if (is_bcast(ethhdr->h_dest) || is_mcast(ethhdr->h_dest))
+		bcast_dst = true;
+
+	if ((bcast_dst) && gw_is_target(skb))
+		do_bcast = false;
 
+	/* ethernet packet should be broadcasted */
+	if (bcast_dst && do_bcast) {
 		if (my_skb_push(skb, sizeof(struct bcast_packet)) < 0)
 			goto dropped;
 
@@ -219,8 +226,12 @@  int interface_tx(struct sk_buff *skb, struct net_device *dev)
 	/* unicast packet */
 	} else {
 		spin_lock_irqsave(&orig_hash_lock, flags);
+
 		/* get routing information */
-		orig_node = ((struct orig_node *)hash_find(orig_hash,
+		if (bcast_dst)
+			orig_node = (struct orig_node *)gw_get_selected();
+		else
+			orig_node = ((struct orig_node *)hash_find(orig_hash,
 							   ethhdr->h_dest));
 
 		/* check for hna host */