diff options
Diffstat (limited to 'net/batman-adv/gateway_client.c')
-rw-r--r-- | net/batman-adv/gateway_client.c | 238 |
1 files changed, 179 insertions, 59 deletions
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 24aee561f3d8..8b25b52a4764 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c | |||
@@ -20,15 +20,22 @@ | |||
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include "main.h" | 22 | #include "main.h" |
23 | #include "bat_sysfs.h" | ||
23 | #include "gateway_client.h" | 24 | #include "gateway_client.h" |
24 | #include "gateway_common.h" | 25 | #include "gateway_common.h" |
25 | #include "hard-interface.h" | 26 | #include "hard-interface.h" |
26 | #include "originator.h" | 27 | #include "originator.h" |
28 | #include "routing.h" | ||
27 | #include <linux/ip.h> | 29 | #include <linux/ip.h> |
28 | #include <linux/ipv6.h> | 30 | #include <linux/ipv6.h> |
29 | #include <linux/udp.h> | 31 | #include <linux/udp.h> |
30 | #include <linux/if_vlan.h> | 32 | #include <linux/if_vlan.h> |
31 | 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 | |||
32 | static void gw_node_free_ref(struct gw_node *gw_node) | 39 | static void gw_node_free_ref(struct gw_node *gw_node) |
33 | { | 40 | { |
34 | if (atomic_dec_and_test(&gw_node->refcount)) | 41 | if (atomic_dec_and_test(&gw_node->refcount)) |
@@ -97,40 +104,19 @@ static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node) | |||
97 | 104 | ||
98 | void gw_deselect(struct bat_priv *bat_priv) | 105 | void gw_deselect(struct bat_priv *bat_priv) |
99 | { | 106 | { |
100 | gw_select(bat_priv, NULL); | 107 | atomic_set(&bat_priv->gw_reselect, 1); |
101 | } | 108 | } |
102 | 109 | ||
103 | void gw_election(struct bat_priv *bat_priv) | 110 | static struct gw_node *gw_get_best_gw_node(struct bat_priv *bat_priv) |
104 | { | 111 | { |
105 | struct hlist_node *node; | ||
106 | struct gw_node *gw_node, *curr_gw = NULL, *curr_gw_tmp = NULL; | ||
107 | struct neigh_node *router; | 112 | struct neigh_node *router; |
108 | uint8_t max_tq = 0; | 113 | struct hlist_node *node; |
114 | struct gw_node *gw_node, *curr_gw = NULL; | ||
109 | uint32_t max_gw_factor = 0, tmp_gw_factor = 0; | 115 | uint32_t max_gw_factor = 0, tmp_gw_factor = 0; |
116 | uint8_t max_tq = 0; | ||
110 | int down, up; | 117 | int down, up; |
111 | 118 | ||
112 | /** | ||
113 | * The batman daemon checks here if we already passed a full originator | ||
114 | * cycle in order to make sure we don't choose the first gateway we | ||
115 | * hear about. This check is based on the daemon's uptime which we | ||
116 | * don't have. | ||
117 | **/ | ||
118 | if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) | ||
119 | return; | ||
120 | |||
121 | curr_gw = gw_get_selected_gw_node(bat_priv); | ||
122 | if (curr_gw) | ||
123 | goto out; | ||
124 | |||
125 | rcu_read_lock(); | 119 | rcu_read_lock(); |
126 | if (hlist_empty(&bat_priv->gw_list)) { | ||
127 | bat_dbg(DBG_BATMAN, bat_priv, | ||
128 | "Removing selected gateway - " | ||
129 | "no gateway in range\n"); | ||
130 | gw_deselect(bat_priv); | ||
131 | goto unlock; | ||
132 | } | ||
133 | |||
134 | hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { | 120 | hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { |
135 | if (gw_node->deleted) | 121 | if (gw_node->deleted) |
136 | continue; | 122 | continue; |
@@ -139,6 +125,9 @@ void gw_election(struct bat_priv *bat_priv) | |||
139 | if (!router) | 125 | if (!router) |
140 | continue; | 126 | continue; |
141 | 127 | ||
128 | if (!atomic_inc_not_zero(&gw_node->refcount)) | ||
129 | goto next; | ||
130 | |||
142 | switch (atomic_read(&bat_priv->gw_sel_class)) { | 131 | switch (atomic_read(&bat_priv->gw_sel_class)) { |
143 | case 1: /* fast connection */ | 132 | case 1: /* fast connection */ |
144 | gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, | 133 | gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, |
@@ -151,8 +140,12 @@ void gw_election(struct bat_priv *bat_priv) | |||
151 | 140 | ||
152 | if ((tmp_gw_factor > max_gw_factor) || | 141 | if ((tmp_gw_factor > max_gw_factor) || |
153 | ((tmp_gw_factor == max_gw_factor) && | 142 | ((tmp_gw_factor == max_gw_factor) && |
154 | (router->tq_avg > max_tq))) | 143 | (router->tq_avg > max_tq))) { |
155 | curr_gw_tmp = gw_node; | 144 | if (curr_gw) |
145 | gw_node_free_ref(curr_gw); | ||
146 | curr_gw = gw_node; | ||
147 | atomic_inc(&curr_gw->refcount); | ||
148 | } | ||
156 | break; | 149 | break; |
157 | 150 | ||
158 | default: /** | 151 | default: /** |
@@ -163,8 +156,12 @@ void gw_election(struct bat_priv *bat_priv) | |||
163 | * soon as a better gateway appears which has | 156 | * soon as a better gateway appears which has |
164 | * $routing_class more tq points) | 157 | * $routing_class more tq points) |
165 | **/ | 158 | **/ |
166 | if (router->tq_avg > max_tq) | 159 | if (router->tq_avg > max_tq) { |
167 | curr_gw_tmp = gw_node; | 160 | if (curr_gw) |
161 | gw_node_free_ref(curr_gw); | ||
162 | curr_gw = gw_node; | ||
163 | atomic_inc(&curr_gw->refcount); | ||
164 | } | ||
168 | break; | 165 | break; |
169 | } | 166 | } |
170 | 167 | ||
@@ -174,42 +171,81 @@ void gw_election(struct bat_priv *bat_priv) | |||
174 | if (tmp_gw_factor > max_gw_factor) | 171 | if (tmp_gw_factor > max_gw_factor) |
175 | max_gw_factor = tmp_gw_factor; | 172 | max_gw_factor = tmp_gw_factor; |
176 | 173 | ||
174 | gw_node_free_ref(gw_node); | ||
175 | |||
176 | next: | ||
177 | neigh_node_free_ref(router); | 177 | neigh_node_free_ref(router); |
178 | } | 178 | } |
179 | rcu_read_unlock(); | ||
179 | 180 | ||
180 | if (curr_gw != curr_gw_tmp) { | 181 | return curr_gw; |
181 | router = orig_node_get_router(curr_gw_tmp->orig_node); | 182 | } |
182 | if (!router) | ||
183 | goto unlock; | ||
184 | 183 | ||
185 | if ((curr_gw) && (!curr_gw_tmp)) | 184 | void gw_election(struct bat_priv *bat_priv) |
186 | bat_dbg(DBG_BATMAN, bat_priv, | 185 | { |
187 | "Removing selected gateway - " | 186 | struct gw_node *curr_gw = NULL, *next_gw = NULL; |
188 | "no gateway in range\n"); | 187 | struct neigh_node *router = NULL; |
189 | else if ((!curr_gw) && (curr_gw_tmp)) | 188 | char gw_addr[18] = { '\0' }; |
190 | bat_dbg(DBG_BATMAN, bat_priv, | ||
191 | "Adding route to gateway %pM " | ||
192 | "(gw_flags: %i, tq: %i)\n", | ||
193 | curr_gw_tmp->orig_node->orig, | ||
194 | curr_gw_tmp->orig_node->gw_flags, | ||
195 | router->tq_avg); | ||
196 | else | ||
197 | bat_dbg(DBG_BATMAN, bat_priv, | ||
198 | "Changing route to gateway %pM " | ||
199 | "(gw_flags: %i, tq: %i)\n", | ||
200 | curr_gw_tmp->orig_node->orig, | ||
201 | curr_gw_tmp->orig_node->gw_flags, | ||
202 | router->tq_avg); | ||
203 | 189 | ||
204 | neigh_node_free_ref(router); | 190 | /** |
205 | gw_select(bat_priv, curr_gw_tmp); | 191 | * The batman daemon checks here if we already passed a full originator |
192 | * cycle in order to make sure we don't choose the first gateway we | ||
193 | * hear about. This check is based on the daemon's uptime which we | ||
194 | * don't have. | ||
195 | **/ | ||
196 | if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) | ||
197 | goto out; | ||
198 | |||
199 | if (!atomic_dec_not_zero(&bat_priv->gw_reselect)) | ||
200 | goto out; | ||
201 | |||
202 | curr_gw = gw_get_selected_gw_node(bat_priv); | ||
203 | |||
204 | next_gw = gw_get_best_gw_node(bat_priv); | ||
205 | |||
206 | if (curr_gw == next_gw) | ||
207 | goto out; | ||
208 | |||
209 | if (next_gw) { | ||
210 | sprintf(gw_addr, "%pM", next_gw->orig_node->orig); | ||
211 | |||
212 | router = orig_node_get_router(next_gw->orig_node); | ||
213 | if (!router) { | ||
214 | gw_deselect(bat_priv); | ||
215 | goto out; | ||
216 | } | ||
206 | } | 217 | } |
207 | 218 | ||
208 | unlock: | 219 | if ((curr_gw) && (!next_gw)) { |
209 | rcu_read_unlock(); | 220 | bat_dbg(DBG_BATMAN, bat_priv, |
221 | "Removing selected gateway - no gateway in range\n"); | ||
222 | throw_uevent(bat_priv, UEV_GW, UEV_DEL, NULL); | ||
223 | } else if ((!curr_gw) && (next_gw)) { | ||
224 | bat_dbg(DBG_BATMAN, bat_priv, | ||
225 | "Adding route to gateway %pM (gw_flags: %i, tq: %i)\n", | ||
226 | next_gw->orig_node->orig, | ||
227 | next_gw->orig_node->gw_flags, | ||
228 | router->tq_avg); | ||
229 | throw_uevent(bat_priv, UEV_GW, UEV_ADD, gw_addr); | ||
230 | } else { | ||
231 | bat_dbg(DBG_BATMAN, bat_priv, | ||
232 | "Changing route to gateway %pM " | ||
233 | "(gw_flags: %i, tq: %i)\n", | ||
234 | next_gw->orig_node->orig, | ||
235 | next_gw->orig_node->gw_flags, | ||
236 | router->tq_avg); | ||
237 | throw_uevent(bat_priv, UEV_GW, UEV_CHANGE, gw_addr); | ||
238 | } | ||
239 | |||
240 | gw_select(bat_priv, next_gw); | ||
241 | |||
210 | out: | 242 | out: |
211 | if (curr_gw) | 243 | if (curr_gw) |
212 | gw_node_free_ref(curr_gw); | 244 | gw_node_free_ref(curr_gw); |
245 | if (next_gw) | ||
246 | gw_node_free_ref(next_gw); | ||
247 | if (router) | ||
248 | neigh_node_free_ref(router); | ||
213 | } | 249 | } |
214 | 250 | ||
215 | void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) | 251 | void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) |
@@ -360,7 +396,7 @@ void gw_node_purge(struct bat_priv *bat_priv) | |||
360 | struct gw_node *gw_node, *curr_gw; | 396 | struct gw_node *gw_node, *curr_gw; |
361 | struct hlist_node *node, *node_tmp; | 397 | struct hlist_node *node, *node_tmp; |
362 | unsigned long timeout = 2 * PURGE_TIMEOUT * HZ; | 398 | unsigned long timeout = 2 * PURGE_TIMEOUT * HZ; |
363 | char do_deselect = 0; | 399 | int do_deselect = 0; |
364 | 400 | ||
365 | curr_gw = gw_get_selected_gw_node(bat_priv); | 401 | curr_gw = gw_get_selected_gw_node(bat_priv); |
366 | 402 | ||
@@ -479,14 +515,75 @@ out: | |||
479 | return ret; | 515 | return ret; |
480 | } | 516 | } |
481 | 517 | ||
482 | int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) | 518 | static bool is_type_dhcprequest(struct sk_buff *skb, int header_len) |
519 | { | ||
520 | int ret = false; | ||
521 | unsigned char *p; | ||
522 | int pkt_len; | ||
523 | |||
524 | if (skb_linearize(skb) < 0) | ||
525 | goto out; | ||
526 | |||
527 | pkt_len = skb_headlen(skb); | ||
528 | |||
529 | if (pkt_len < header_len + DHCP_OPTIONS_OFFSET + 1) | ||
530 | goto out; | ||
531 | |||
532 | p = skb->data + header_len + DHCP_OPTIONS_OFFSET; | ||
533 | pkt_len -= header_len + DHCP_OPTIONS_OFFSET + 1; | ||
534 | |||
535 | /* Access the dhcp option lists. Each entry is made up by: | ||
536 | * - octect 1: option type | ||
537 | * - octect 2: option data len (only if type != 255 and 0) | ||
538 | * - octect 3: option data */ | ||
539 | while (*p != 255 && !ret) { | ||
540 | /* p now points to the first octect: option type */ | ||
541 | if (*p == 53) { | ||
542 | /* type 53 is the message type option. | ||
543 | * Jump the len octect and go to the data octect */ | ||
544 | if (pkt_len < 2) | ||
545 | goto out; | ||
546 | p += 2; | ||
547 | |||
548 | /* check if the message type is what we need */ | ||
549 | if (*p == DHCP_REQUEST) | ||
550 | ret = true; | ||
551 | break; | ||
552 | } else if (*p == 0) { | ||
553 | /* option type 0 (padding), just go forward */ | ||
554 | if (pkt_len < 1) | ||
555 | goto out; | ||
556 | pkt_len--; | ||
557 | p++; | ||
558 | } else { | ||
559 | /* This is any other option. So we get the length... */ | ||
560 | if (pkt_len < 1) | ||
561 | goto out; | ||
562 | pkt_len--; | ||
563 | p++; | ||
564 | |||
565 | /* ...and then we jump over the data */ | ||
566 | if (pkt_len < *p) | ||
567 | goto out; | ||
568 | pkt_len -= *p; | ||
569 | p += (*p); | ||
570 | } | ||
571 | } | ||
572 | out: | ||
573 | return ret; | ||
574 | } | ||
575 | |||
576 | int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, | ||
577 | struct orig_node *old_gw) | ||
483 | { | 578 | { |
484 | struct ethhdr *ethhdr; | 579 | struct ethhdr *ethhdr; |
485 | struct iphdr *iphdr; | 580 | struct iphdr *iphdr; |
486 | struct ipv6hdr *ipv6hdr; | 581 | struct ipv6hdr *ipv6hdr; |
487 | struct udphdr *udphdr; | 582 | struct udphdr *udphdr; |
488 | struct gw_node *curr_gw; | 583 | struct gw_node *curr_gw; |
584 | struct neigh_node *neigh_curr = NULL, *neigh_old = NULL; | ||
489 | unsigned int header_len = 0; | 585 | unsigned int header_len = 0; |
586 | int ret = 1; | ||
490 | 587 | ||
491 | if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF) | 588 | if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF) |
492 | return 0; | 589 | return 0; |
@@ -554,7 +651,30 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) | |||
554 | if (!curr_gw) | 651 | if (!curr_gw) |
555 | return 0; | 652 | return 0; |
556 | 653 | ||
654 | /* If old_gw != NULL then this packet is unicast. | ||
655 | * So, at this point we have to check the message type: if it is a | ||
656 | * DHCPREQUEST we have to decide whether to drop it or not */ | ||
657 | if (old_gw && curr_gw->orig_node != old_gw) { | ||
658 | if (is_type_dhcprequest(skb, header_len)) { | ||
659 | /* If the dhcp packet has been sent to a different gw, | ||
660 | * we have to evaluate whether the old gw is still | ||
661 | * reliable enough */ | ||
662 | neigh_curr = find_router(bat_priv, curr_gw->orig_node, | ||
663 | NULL); | ||
664 | neigh_old = find_router(bat_priv, old_gw, NULL); | ||
665 | if (!neigh_curr || !neigh_old) | ||
666 | goto free_neigh; | ||
667 | if (neigh_curr->tq_avg - neigh_old->tq_avg < | ||
668 | GW_THRESHOLD) | ||
669 | ret = -1; | ||
670 | } | ||
671 | } | ||
672 | free_neigh: | ||
673 | if (neigh_old) | ||
674 | neigh_node_free_ref(neigh_old); | ||
675 | if (neigh_curr) | ||
676 | neigh_node_free_ref(neigh_curr); | ||
557 | if (curr_gw) | 677 | if (curr_gw) |
558 | gw_node_free_ref(curr_gw); | 678 | gw_node_free_ref(curr_gw); |
559 | return 1; | 679 | return ret; |
560 | } | 680 | } |