batman-adv: refactoring gateway handling code

Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
Acked-by: Antonio Quartulli <ordex@autistici.org>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
This commit is contained in:
Marek Lindner 2011-09-08 13:12:53 +02:00 committed by Sven Eckelmann
parent 25a92b138d
commit be7af5cf9c
3 changed files with 125 additions and 74 deletions

View File

@ -25,6 +25,7 @@
#include "gateway_common.h" #include "gateway_common.h"
#include "hard-interface.h" #include "hard-interface.h"
#include "originator.h" #include "originator.h"
#include "translation-table.h"
#include "routing.h" #include "routing.h"
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/ipv6.h> #include <linux/ipv6.h>
@ -572,108 +573,142 @@ out:
return ret; return ret;
} }
int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, bool gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len)
struct orig_node *old_gw)
{ {
struct ethhdr *ethhdr; struct ethhdr *ethhdr;
struct iphdr *iphdr; struct iphdr *iphdr;
struct ipv6hdr *ipv6hdr; struct ipv6hdr *ipv6hdr;
struct udphdr *udphdr; struct udphdr *udphdr;
struct gw_node *curr_gw;
struct neigh_node *neigh_curr = NULL, *neigh_old = NULL;
unsigned int header_len = 0;
int ret = 1;
if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF)
return 0;
/* check for ethernet header */ /* check for ethernet header */
if (!pskb_may_pull(skb, header_len + ETH_HLEN)) if (!pskb_may_pull(skb, *header_len + ETH_HLEN))
return 0; return false;
ethhdr = (struct ethhdr *)skb->data; ethhdr = (struct ethhdr *)skb->data;
header_len += ETH_HLEN; *header_len += ETH_HLEN;
/* check for initial vlan header */ /* check for initial vlan header */
if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) { if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) {
if (!pskb_may_pull(skb, header_len + VLAN_HLEN)) if (!pskb_may_pull(skb, *header_len + VLAN_HLEN))
return 0; return false;
ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN); ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN);
header_len += VLAN_HLEN; *header_len += VLAN_HLEN;
} }
/* check for ip header */ /* check for ip header */
switch (ntohs(ethhdr->h_proto)) { switch (ntohs(ethhdr->h_proto)) {
case ETH_P_IP: case ETH_P_IP:
if (!pskb_may_pull(skb, header_len + sizeof(*iphdr))) if (!pskb_may_pull(skb, *header_len + sizeof(*iphdr)))
return 0; return false;
iphdr = (struct iphdr *)(skb->data + header_len); iphdr = (struct iphdr *)(skb->data + *header_len);
header_len += iphdr->ihl * 4; *header_len += iphdr->ihl * 4;
/* check for udp header */ /* check for udp header */
if (iphdr->protocol != IPPROTO_UDP) if (iphdr->protocol != IPPROTO_UDP)
return 0; return false;
break; break;
case ETH_P_IPV6: case ETH_P_IPV6:
if (!pskb_may_pull(skb, header_len + sizeof(*ipv6hdr))) if (!pskb_may_pull(skb, *header_len + sizeof(*ipv6hdr)))
return 0; return false;
ipv6hdr = (struct ipv6hdr *)(skb->data + header_len); ipv6hdr = (struct ipv6hdr *)(skb->data + *header_len);
header_len += sizeof(*ipv6hdr); *header_len += sizeof(*ipv6hdr);
/* check for udp header */ /* check for udp header */
if (ipv6hdr->nexthdr != IPPROTO_UDP) if (ipv6hdr->nexthdr != IPPROTO_UDP)
return 0; return false;
break; break;
default: default:
return 0; return false;
} }
if (!pskb_may_pull(skb, header_len + sizeof(*udphdr))) if (!pskb_may_pull(skb, *header_len + sizeof(*udphdr)))
return 0; return false;
udphdr = (struct udphdr *)(skb->data + header_len); udphdr = (struct udphdr *)(skb->data + *header_len);
header_len += sizeof(*udphdr); *header_len += sizeof(*udphdr);
/* check for bootp port */ /* check for bootp port */
if ((ntohs(ethhdr->h_proto) == ETH_P_IP) && if ((ntohs(ethhdr->h_proto) == ETH_P_IP) &&
(ntohs(udphdr->dest) != 67)) (ntohs(udphdr->dest) != 67))
return 0; return false;
if ((ntohs(ethhdr->h_proto) == ETH_P_IPV6) && if ((ntohs(ethhdr->h_proto) == ETH_P_IPV6) &&
(ntohs(udphdr->dest) != 547)) (ntohs(udphdr->dest) != 547))
return 0; return false;
if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER) return true;
return -1; }
curr_gw = gw_get_selected_gw_node(bat_priv); bool gw_out_of_range(struct bat_priv *bat_priv,
if (!curr_gw) struct sk_buff *skb, struct ethhdr *ethhdr)
return 0; {
struct neigh_node *neigh_curr = NULL, *neigh_old = NULL;
struct orig_node *orig_dst_node = NULL;
struct gw_node *curr_gw = NULL;
bool ret, out_of_range = false;
unsigned int header_len = 0;
uint8_t curr_tq_avg;
/* If old_gw != NULL then this packet is unicast. ret = gw_is_dhcp_target(skb, &header_len);
* So, at this point we have to check the message type: if it is a if (!ret)
* DHCPREQUEST we have to decide whether to drop it or not */ goto out;
if (old_gw && curr_gw->orig_node != old_gw) {
if (is_type_dhcprequest(skb, header_len)) { orig_dst_node = transtable_search(bat_priv, ethhdr->h_source,
/* If the dhcp packet has been sent to a different gw, ethhdr->h_dest);
* we have to evaluate whether the old gw is still if (!orig_dst_node)
* reliable enough */ goto out;
neigh_curr = find_router(bat_priv, curr_gw->orig_node,
NULL); if (!orig_dst_node->gw_flags)
neigh_old = find_router(bat_priv, old_gw, NULL); goto out;
if (!neigh_curr || !neigh_old)
goto free_neigh; ret = is_type_dhcprequest(skb, header_len);
if (neigh_curr->tq_avg - neigh_old->tq_avg < if (!ret)
GW_THRESHOLD) goto out;
ret = -1;
} switch (atomic_read(&bat_priv->gw_mode)) {
case GW_MODE_SERVER:
/* If we are a GW then we are our best GW. We can artificially
* set the tq towards ourself as the maximum value */
curr_tq_avg = TQ_MAX_VALUE;
break;
case GW_MODE_CLIENT:
curr_gw = gw_get_selected_gw_node(bat_priv);
if (!curr_gw)
goto out;
/* packet is going to our gateway */
if (curr_gw->orig_node == orig_dst_node)
goto out;
/* If the dhcp packet has been sent to a different gw,
* we have to evaluate whether the old gw is still
* reliable enough */
neigh_curr = find_router(bat_priv, curr_gw->orig_node, NULL);
if (!neigh_curr)
goto out;
curr_tq_avg = neigh_curr->tq_avg;
break;
case GW_MODE_OFF:
default:
goto out;
} }
free_neigh:
neigh_old = find_router(bat_priv, orig_dst_node, NULL);
if (!!neigh_old)
goto out;
if (curr_tq_avg - neigh_old->tq_avg > GW_THRESHOLD)
out_of_range = true;
out:
if (orig_dst_node)
orig_node_free_ref(orig_dst_node);
if (curr_gw)
gw_node_free_ref(curr_gw);
if (neigh_old) if (neigh_old)
neigh_node_free_ref(neigh_old); neigh_node_free_ref(neigh_old);
if (neigh_curr) if (neigh_curr)
neigh_node_free_ref(neigh_curr); neigh_node_free_ref(neigh_curr);
if (curr_gw) return out_of_range;
gw_node_free_ref(curr_gw);
return ret;
} }

View File

@ -31,7 +31,8 @@ void gw_node_update(struct bat_priv *bat_priv,
void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node); void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node);
void gw_node_purge(struct bat_priv *bat_priv); void gw_node_purge(struct bat_priv *bat_priv);
int gw_client_seq_print_text(struct seq_file *seq, void *offset); int gw_client_seq_print_text(struct seq_file *seq, void *offset);
int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, bool gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len);
struct orig_node *old_gw); bool gw_out_of_range(struct bat_priv *bat_priv,
struct sk_buff *skb, struct ethhdr *ethhdr);
#endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */ #endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */

View File

@ -563,10 +563,10 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
struct bcast_packet *bcast_packet; struct bcast_packet *bcast_packet;
struct vlan_ethhdr *vhdr; struct vlan_ethhdr *vhdr;
struct softif_neigh *curr_softif_neigh = NULL; struct softif_neigh *curr_softif_neigh = NULL;
struct orig_node *orig_node = NULL; unsigned int header_len = 0;
int data_len = skb->len, ret; int data_len = skb->len, ret;
short vid = -1; short vid = -1;
bool do_bcast; bool do_bcast = false;
if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE) if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE)
goto dropped; goto dropped;
@ -598,17 +598,28 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
/* Register the client MAC in the transtable */ /* Register the client MAC in the transtable */
tt_local_add(soft_iface, ethhdr->h_source, skb->skb_iif); tt_local_add(soft_iface, ethhdr->h_source, skb->skb_iif);
orig_node = transtable_search(bat_priv, ethhdr->h_source, if (is_multicast_ether_addr(ethhdr->h_dest)) {
ethhdr->h_dest); do_bcast = true;
do_bcast = is_multicast_ether_addr(ethhdr->h_dest);
if (do_bcast || (orig_node && orig_node->gw_flags)) {
ret = gw_is_target(bat_priv, skb, orig_node);
if (ret < 0) switch (atomic_read(&bat_priv->gw_mode)) {
goto dropped; case GW_MODE_SERVER:
/* gateway servers should not send dhcp
if (ret) * requests into the mesh */
do_bcast = false; ret = gw_is_dhcp_target(skb, &header_len);
if (ret)
goto dropped;
break;
case GW_MODE_CLIENT:
/* gateway clients should send dhcp requests
* via unicast to their gateway */
ret = gw_is_dhcp_target(skb, &header_len);
if (ret)
do_bcast = false;
break;
case GW_MODE_OFF:
default:
break;
}
} }
/* ethernet packet should be broadcasted */ /* ethernet packet should be broadcasted */
@ -644,6 +655,12 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
/* unicast packet */ /* unicast packet */
} else { } else {
if (atomic_read(&bat_priv->gw_mode) != GW_MODE_OFF) {
ret = gw_out_of_range(bat_priv, skb, ethhdr);
if (ret)
goto dropped;
}
ret = unicast_send_skb(skb, bat_priv); ret = unicast_send_skb(skb, bat_priv);
if (ret != 0) if (ret != 0)
goto dropped_freed; goto dropped_freed;
@ -662,8 +679,6 @@ end:
softif_neigh_free_ref(curr_softif_neigh); softif_neigh_free_ref(curr_softif_neigh);
if (primary_if) if (primary_if)
hardif_free_ref(primary_if); hardif_free_ref(primary_if);
if (orig_node)
orig_node_free_ref(orig_node);
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }