mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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:
		
							parent
							
								
									25a92b138d
								
							
						
					
					
						commit
						be7af5cf9c
					
				
					 3 changed files with 125 additions and 74 deletions
				
			
		| 
						 | 
					@ -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 @@ static bool is_type_dhcprequest(struct sk_buff *skb, int header_len)
 | 
				
			||||||
	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;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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_ */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
 | 
				
			||||||
		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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue