Project

General

Profile

Bug #153 » 0001-batman-adv-gw-performance.patch

Marek Lindner, 09/08/2011 02:48 PM

View differences:

gateway_client.c
25 25
#include "gateway_common.h"
26 26
#include "hard-interface.h"
27 27
#include "originator.h"
28
#include "routing.h"
29
#include <linux/ip.h>
30
#include <linux/ipv6.h>
31
#include <linux/udp.h>
32
#include <linux/if_vlan.h>
33

  
34
/* This is the offset of the options field in a dhcp packet starting at
35
 * the beginning of the dhcp header */
36
#define DHCP_OPTIONS_OFFSET 240
37
#define DHCP_REQUEST 3
38

  
39
static void gw_node_free_ref(struct gw_node *gw_node)
40
{
41
	if (atomic_dec_and_test(&gw_node->refcount))
42
		kfree_rcu(gw_node, rcu);
43
}
44

  
45
static struct gw_node *gw_get_selected_gw_node(struct bat_priv *bat_priv)
46
{
47
	struct gw_node *gw_node;
48

  
49
	rcu_read_lock();
50
	gw_node = rcu_dereference(bat_priv->curr_gw);
51
	if (!gw_node)
52
		goto out;
53

  
54
	if (!atomic_inc_not_zero(&gw_node->refcount))
55
		gw_node = NULL;
56

  
57
out:
58
	rcu_read_unlock();
59
	return gw_node;
60
}
61 28

  
62 29
struct orig_node *gw_get_selected_orig(struct bat_priv *bat_priv)
63 30
{
......
513 480
		hardif_free_ref(primary_if);
514 481
	return ret;
515 482
}
516

  
517
static bool is_type_dhcprequest(struct sk_buff *skb, int header_len)
518
{
519
	int ret = false;
520
	unsigned char *p;
521
	int pkt_len;
522

  
523
	if (skb_linearize(skb) < 0)
524
		goto out;
525

  
526
	pkt_len = skb_headlen(skb);
527

  
528
	if (pkt_len < header_len + DHCP_OPTIONS_OFFSET + 1)
529
		goto out;
530

  
531
	p = skb->data + header_len + DHCP_OPTIONS_OFFSET;
532
	pkt_len -= header_len + DHCP_OPTIONS_OFFSET + 1;
533

  
534
	/* Access the dhcp option lists. Each entry is made up by:
535
	 * - octect 1: option type
536
	 * - octect 2: option data len (only if type != 255 and 0)
537
	 * - octect 3: option data */
538
	while (*p != 255 && !ret) {
539
		/* p now points to the first octect: option type */
540
		if (*p == 53) {
541
			/* type 53 is the message type option.
542
			 * Jump the len octect and go to the data octect */
543
			if (pkt_len < 2)
544
				goto out;
545
			p += 2;
546

  
547
			/* check if the message type is what we need */
548
			if (*p == DHCP_REQUEST)
549
				ret = true;
550
			break;
551
		} else if (*p == 0) {
552
			/* option type 0 (padding), just go forward */
553
			if (pkt_len < 1)
554
				goto out;
555
			pkt_len--;
556
			p++;
557
		} else {
558
			/* This is any other option. So we get the length... */
559
			if (pkt_len < 1)
560
				goto out;
561
			pkt_len--;
562
			p++;
563

  
564
			/* ...and then we jump over the data */
565
			if (pkt_len < *p)
566
				goto out;
567
			pkt_len -= *p;
568
			p += (*p);
569
		}
570
	}
571
out:
572
	return ret;
573
}
574

  
575
int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb,
576
		 struct orig_node *old_gw)
577
{
578
	struct ethhdr *ethhdr;
579
	struct iphdr *iphdr;
580
	struct ipv6hdr *ipv6hdr;
581
	struct udphdr *udphdr;
582
	struct gw_node *curr_gw;
583
	struct neigh_node *neigh_curr = NULL, *neigh_old = NULL;
584
	unsigned int header_len = 0;
585
	int ret = 1;
586

  
587
	if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF)
588
		return 0;
589

  
590
	/* check for ethernet header */
591
	if (!pskb_may_pull(skb, header_len + ETH_HLEN))
592
		return 0;
593
	ethhdr = (struct ethhdr *)skb->data;
594
	header_len += ETH_HLEN;
595

  
596
	/* check for initial vlan header */
597
	if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) {
598
		if (!pskb_may_pull(skb, header_len + VLAN_HLEN))
599
			return 0;
600
		ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN);
601
		header_len += VLAN_HLEN;
602
	}
603

  
604
	/* check for ip header */
605
	switch (ntohs(ethhdr->h_proto)) {
606
	case ETH_P_IP:
607
		if (!pskb_may_pull(skb, header_len + sizeof(*iphdr)))
608
			return 0;
609
		iphdr = (struct iphdr *)(skb->data + header_len);
610
		header_len += iphdr->ihl * 4;
611

  
612
		/* check for udp header */
613
		if (iphdr->protocol != IPPROTO_UDP)
614
			return 0;
615

  
616
		break;
617
	case ETH_P_IPV6:
618
		if (!pskb_may_pull(skb, header_len + sizeof(*ipv6hdr)))
619
			return 0;
620
		ipv6hdr = (struct ipv6hdr *)(skb->data + header_len);
621
		header_len += sizeof(*ipv6hdr);
622

  
623
		/* check for udp header */
624
		if (ipv6hdr->nexthdr != IPPROTO_UDP)
625
			return 0;
626

  
627
		break;
628
	default:
629
		return 0;
630
	}
631

  
632
	if (!pskb_may_pull(skb, header_len + sizeof(*udphdr)))
633
		return 0;
634
	udphdr = (struct udphdr *)(skb->data + header_len);
635
	header_len += sizeof(*udphdr);
636

  
637
	/* check for bootp port */
638
	if ((ntohs(ethhdr->h_proto) == ETH_P_IP) &&
639
	     (ntohs(udphdr->dest) != 67))
640
		return 0;
641

  
642
	if ((ntohs(ethhdr->h_proto) == ETH_P_IPV6) &&
643
	    (ntohs(udphdr->dest) != 547))
644
		return 0;
645

  
646
	if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER)
647
		return -1;
648

  
649
	curr_gw = gw_get_selected_gw_node(bat_priv);
650
	if (!curr_gw)
651
		return 0;
652

  
653
	/* If old_gw != NULL then this packet is unicast.
654
	 * So, at this point we have to check the message type: if it is a
655
	 * DHCPREQUEST we have to decide whether to drop it or not */
656
	if (old_gw && curr_gw->orig_node != old_gw) {
657
		if (is_type_dhcprequest(skb, header_len)) {
658
			/* If the dhcp packet has been sent to a different gw,
659
			 * we have to evaluate whether the old gw is still
660
			 * reliable enough */
661
			neigh_curr = find_router(bat_priv, curr_gw->orig_node,
662
						 NULL);
663
			neigh_old = find_router(bat_priv, old_gw, NULL);
664
			if (!neigh_curr || !neigh_old)
665
				goto free_neigh;
666
			if (neigh_curr->tq_avg - neigh_old->tq_avg <
667
								GW_THRESHOLD)
668
				ret = -1;
669
		}
670
	}
671
free_neigh:
672
	if (neigh_old)
673
		neigh_node_free_ref(neigh_old);
674
	if (neigh_curr)
675
		neigh_node_free_ref(neigh_curr);
676
	if (curr_gw)
677
		gw_node_free_ref(curr_gw);
678
	return ret;
679
}
gateway_client.h
22 22
#ifndef _NET_BATMAN_ADV_GATEWAY_CLIENT_H_
23 23
#define _NET_BATMAN_ADV_GATEWAY_CLIENT_H_
24 24

  
25
/* This is the offset of the options field in a dhcp packet starting at
26
 * the beginning of the dhcp header */
27
#define DHCP_OPTIONS_OFFSET 240
28
#define DHCP_REQUEST 3
29

  
30
#include "translation-table.h"
31
#include "originator.h"
32
#include "routing.h"
33
#include <linux/ip.h>
34
#include <linux/ipv6.h>
35
#include <linux/udp.h>
36
#include <linux/if_vlan.h>
37

  
25 38
void gw_deselect(struct bat_priv *bat_priv);
26 39
void gw_election(struct bat_priv *bat_priv);
27 40
struct orig_node *gw_get_selected_orig(struct bat_priv *bat_priv);
......
31 44
void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node);
32 45
void gw_node_purge(struct bat_priv *bat_priv);
33 46
int gw_client_seq_print_text(struct seq_file *seq, void *offset);
34
int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb,
35
		 struct orig_node *old_gw);
47

  
48
static inline void gw_node_free_ref(struct gw_node *gw_node)
49
{
50
	if (atomic_dec_and_test(&gw_node->refcount))
51
		kfree_rcu(gw_node, rcu);
52
}
53

  
54
static inline struct gw_node *gw_get_selected_gw_node(struct bat_priv *bat_priv)
55
{
56
	struct gw_node *gw_node;
57

  
58
	rcu_read_lock();
59
	gw_node = rcu_dereference(bat_priv->curr_gw);
60
	if (!gw_node)
61
		goto out;
62

  
63
	if (!atomic_inc_not_zero(&gw_node->refcount))
64
		gw_node = NULL;
65

  
66
out:
67
	rcu_read_unlock();
68
	return gw_node;
69
}
70

  
71
static inline bool is_type_dhcprequest(struct sk_buff *skb, int header_len)
72
{
73
	int ret = false;
74
	unsigned char *p;
75
	int pkt_len;
76

  
77
	if (skb_linearize(skb) < 0)
78
		goto out;
79

  
80
	pkt_len = skb_headlen(skb);
81

  
82
	if (pkt_len < header_len + DHCP_OPTIONS_OFFSET + 1)
83
		goto out;
84

  
85
	p = skb->data + header_len + DHCP_OPTIONS_OFFSET;
86
	pkt_len -= header_len + DHCP_OPTIONS_OFFSET + 1;
87

  
88
	/* Access the dhcp option lists. Each entry is made up by:
89
	 * - octect 1: option type
90
	 * - octect 2: option data len (only if type != 255 and 0)
91
	 * - octect 3: option data */
92
	while (*p != 255 && !ret) {
93
		/* p now points to the first octect: option type */
94
		if (*p == 53) {
95
			/* type 53 is the message type option.
96
			 * Jump the len octect and go to the data octect */
97
			if (pkt_len < 2)
98
				goto out;
99
			p += 2;
100

  
101
			/* check if the message type is what we need */
102
			if (*p == DHCP_REQUEST)
103
				ret = true;
104
			break;
105
		} else if (*p == 0) {
106
			/* option type 0 (padding), just go forward */
107
			if (pkt_len < 1)
108
				goto out;
109
			pkt_len--;
110
			p++;
111
		} else {
112
			/* This is any other option. So we get the length... */
113
			if (pkt_len < 1)
114
				goto out;
115
			pkt_len--;
116
			p++;
117

  
118
			/* ...and then we jump over the data */
119
			if (pkt_len < *p)
120
				goto out;
121
			pkt_len -= *p;
122
			p += (*p);
123
		}
124
	}
125
out:
126
	return ret;
127
}
128

  
129
static inline bool gw_is_dhcp_target(struct sk_buff *skb,
130
				     unsigned int *header_len)
131
{
132
	struct ethhdr *ethhdr;
133
	struct iphdr *iphdr;
134
	struct ipv6hdr *ipv6hdr;
135
	struct udphdr *udphdr;
136

  
137
	/* check for ethernet header */
138
	if (!pskb_may_pull(skb, *header_len + ETH_HLEN))
139
		return false;
140
	ethhdr = (struct ethhdr *)skb->data;
141
	*header_len += ETH_HLEN;
142

  
143
	/* check for initial vlan header */
144
	if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) {
145
		if (!pskb_may_pull(skb, *header_len + VLAN_HLEN))
146
			return false;
147
		ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN);
148
		*header_len += VLAN_HLEN;
149
	}
150

  
151
	/* check for ip header */
152
	switch (ntohs(ethhdr->h_proto)) {
153
	case ETH_P_IP:
154
		if (!pskb_may_pull(skb, *header_len + sizeof(*iphdr)))
155
			return false;
156
		iphdr = (struct iphdr *)(skb->data + *header_len);
157
		*header_len += iphdr->ihl * 4;
158

  
159
		/* check for udp header */
160
		if (iphdr->protocol != IPPROTO_UDP)
161
			return false;
162

  
163
		break;
164
	case ETH_P_IPV6:
165
		if (!pskb_may_pull(skb, *header_len + sizeof(*ipv6hdr)))
166
			return false;
167
		ipv6hdr = (struct ipv6hdr *)(skb->data + *header_len);
168
		*header_len += sizeof(*ipv6hdr);
169

  
170
		/* check for udp header */
171
		if (ipv6hdr->nexthdr != IPPROTO_UDP)
172
			return false;
173

  
174
		break;
175
	default:
176
		return false;
177
	}
178

  
179
	if (!pskb_may_pull(skb, *header_len + sizeof(*udphdr)))
180
		return false;
181
	udphdr = (struct udphdr *)(skb->data + *header_len);
182
	*header_len += sizeof(*udphdr);
183

  
184
	/* check for bootp port */
185
	if ((ntohs(ethhdr->h_proto) == ETH_P_IP) &&
186
	     (ntohs(udphdr->dest) != 67))
187
		return false;
188

  
189
	if ((ntohs(ethhdr->h_proto) == ETH_P_IPV6) &&
190
	    (ntohs(udphdr->dest) != 547))
191
		return false;
192

  
193
	return true;
194
}
195

  
196
static inline bool gw_out_of_range(struct bat_priv *bat_priv,
197
				   struct sk_buff *skb, struct ethhdr *ethhdr)
198
{
199
	struct neigh_node *neigh_curr = NULL, *neigh_old = NULL;
200
	struct orig_node *orig_dst_node = NULL;
201
	struct gw_node *curr_gw = NULL;
202
	bool ret, out_of_range = false;
203
	unsigned int header_len = 0;
204

  
205
	ret = gw_is_dhcp_target(skb, &header_len);
206
	if (!ret)
207
		goto out;
208

  
209
	orig_dst_node = transtable_search(bat_priv, ethhdr->h_dest);
210
	if (!orig_dst_node)
211
		goto out;
212

  
213
	if (!orig_dst_node->gw_flags)
214
		goto out;
215

  
216
	curr_gw = gw_get_selected_gw_node(bat_priv);
217
	if (!curr_gw)
218
		goto out;
219

  
220
	/* packet is going to our gateway */
221
	if (curr_gw->orig_node == orig_dst_node)
222
		goto out;
223

  
224
	/* TODO: what happens if the node itself is a gateway ? */
225

  
226
	ret = is_type_dhcprequest(skb, header_len);
227
	if (!ret)
228
		goto out;
229

  
230
	/* If the dhcp packet has been sent to a different gw,
231
	 * we have to evaluate whether the old gw is still
232
	 * reliable enough */
233
	neigh_curr = find_router(bat_priv, curr_gw->orig_node, NULL);
234
	if (!neigh_curr)
235
		goto out;
236

  
237
	neigh_old = find_router(bat_priv, orig_dst_node, NULL);
238
	if (!!neigh_old)
239
		goto out;
240

  
241
	if (neigh_curr->tq_avg - neigh_old->tq_avg > GW_THRESHOLD)
242
		out_of_range = true;
243

  
244
out:
245
	if (orig_dst_node)
246
		orig_node_free_ref(orig_dst_node);
247
	if (curr_gw)
248
		gw_node_free_ref(curr_gw);
249
	if (neigh_old)
250
		neigh_node_free_ref(neigh_old);
251
	if (neigh_curr)
252
		neigh_node_free_ref(neigh_curr);
253
	return out_of_range;
254
}
36 255

  
37 256
#endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */
soft-interface.c
562 562
	struct bcast_packet *bcast_packet;
563 563
	struct vlan_ethhdr *vhdr;
564 564
	struct softif_neigh *curr_softif_neigh = NULL;
565
	struct orig_node *orig_node = NULL;
565
	unsigned int header_len = 0;
566 566
	int data_len = skb->len, ret;
567 567
	short vid = -1;
568 568
	bool do_bcast = false;
......
597 597
	/* Register the client MAC in the transtable */
598 598
	tt_local_add(soft_iface, ethhdr->h_source);
599 599

  
600
	orig_node = transtable_search(bat_priv, ethhdr->h_dest);
601
	if (is_multicast_ether_addr(ethhdr->h_dest) ||
602
				(orig_node && orig_node->gw_flags)) {
603
		ret = gw_is_target(bat_priv, skb, orig_node);
600
	if (is_multicast_ether_addr(ethhdr->h_dest)) {
601
		do_bcast = true;
604 602

  
605
		if (ret < 0)
606
			goto dropped;
607

  
608
		if (ret == 0)
609
			do_bcast = true;
603
		switch (atomic_read(&bat_priv->gw_mode)) {
604
		case GW_MODE_SERVER:
605
			/* gateway servers should not send dhcp
606
			 * requests into the mesh */
607
			ret = gw_is_dhcp_target(skb, &header_len);
608
			if (ret)
609
				goto dropped;
610
			break;
611
		case GW_MODE_CLIENT:
612
			/* gateway clients should send dhcp requests
613
			 * via unicast to their gateway */
614
			ret = gw_is_dhcp_target(skb, &header_len);
615
			if (ret)
616
				do_bcast = false;
617
			break;
618
		case GW_MODE_OFF:
619
			printk(KERN_INFO "interface_tx(): bcast check: gw mode off\n");
620
			break;
621
		}
610 622
	}
611 623

  
612 624
	/* ethernet packet should be broadcasted */
......
642 654

  
643 655
	/* unicast packet */
644 656
	} else {
657
		if (atomic_read(&bat_priv->gw_mode) != GW_MODE_OFF) {
658
			ret = gw_out_of_range(bat_priv, skb, ethhdr);
659
			if (ret)
660
				goto dropped;
661
		}
662

  
645 663
		ret = unicast_send_skb(skb, bat_priv);
646 664
		if (ret != 0)
647 665
			goto dropped_freed;
......
660 678
		softif_neigh_free_ref(curr_softif_neigh);
661 679
	if (primary_if)
662 680
		hardif_free_ref(primary_if);
663
	if (orig_node)
664
		orig_node_free_ref(orig_node);
665 681
	return NETDEV_TX_OK;
666 682
}
667 683

  
668
- 
(2-2/3)