10b873931SAntonio Quartulli /* Copyright (C) 2009-2013 B.A.T.M.A.N. contributors: 2c6c8fea2SSven Eckelmann * 3c6c8fea2SSven Eckelmann * Marek Lindner 4c6c8fea2SSven Eckelmann * 5c6c8fea2SSven Eckelmann * This program is free software; you can redistribute it and/or 6c6c8fea2SSven Eckelmann * modify it under the terms of version 2 of the GNU General Public 7c6c8fea2SSven Eckelmann * License as published by the Free Software Foundation. 8c6c8fea2SSven Eckelmann * 9c6c8fea2SSven Eckelmann * This program is distributed in the hope that it will be useful, but 10c6c8fea2SSven Eckelmann * WITHOUT ANY WARRANTY; without even the implied warranty of 11c6c8fea2SSven Eckelmann * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12c6c8fea2SSven Eckelmann * General Public License for more details. 13c6c8fea2SSven Eckelmann * 14c6c8fea2SSven Eckelmann * You should have received a copy of the GNU General Public License 15ebf38fb7SAntonio Quartulli * along with this program; if not, see <http://www.gnu.org/licenses/>. 16c6c8fea2SSven Eckelmann */ 17c6c8fea2SSven Eckelmann 18c6c8fea2SSven Eckelmann #include "main.h" 19b706b13bSSven Eckelmann #include "sysfs.h" 20c6c8fea2SSven Eckelmann #include "gateway_client.h" 21c6c8fea2SSven Eckelmann #include "gateway_common.h" 22c6c8fea2SSven Eckelmann #include "hard-interface.h" 2357f0c07cSLinus Lüssing #include "originator.h" 24be7af5cfSMarek Lindner #include "translation-table.h" 2543676ab5SAntonio Quartulli #include "routing.h" 26c6c8fea2SSven Eckelmann #include <linux/ip.h> 27c6c8fea2SSven Eckelmann #include <linux/ipv6.h> 28c6c8fea2SSven Eckelmann #include <linux/udp.h> 29c6c8fea2SSven Eckelmann #include <linux/if_vlan.h> 30c6c8fea2SSven Eckelmann 3143676ab5SAntonio Quartulli /* This is the offset of the options field in a dhcp packet starting at 329cfc7bd6SSven Eckelmann * the beginning of the dhcp header 339cfc7bd6SSven Eckelmann */ 34347c80f0SSven Eckelmann #define BATADV_DHCP_OPTIONS_OFFSET 240 35347c80f0SSven Eckelmann #define BATADV_DHCP_REQUEST 3 3643676ab5SAntonio Quartulli 3756303d34SSven Eckelmann static void batadv_gw_node_free_ref(struct batadv_gw_node *gw_node) 3825b6d3c1SMarek Lindner { 3925b6d3c1SMarek Lindner if (atomic_dec_and_test(&gw_node->refcount)) 40eb340b2fSPaul E. McKenney kfree_rcu(gw_node, rcu); 41c6c8fea2SSven Eckelmann } 42c6c8fea2SSven Eckelmann 4356303d34SSven Eckelmann static struct batadv_gw_node * 4456303d34SSven Eckelmann batadv_gw_get_selected_gw_node(struct batadv_priv *bat_priv) 45c6c8fea2SSven Eckelmann { 4656303d34SSven Eckelmann struct batadv_gw_node *gw_node; 47c6c8fea2SSven Eckelmann 485d02b3cdSLinus Lüssing rcu_read_lock(); 49807736f6SSven Eckelmann gw_node = rcu_dereference(bat_priv->gw.curr_gw); 50c4aac1abSMarek Lindner if (!gw_node) 515d02b3cdSLinus Lüssing goto out; 52c6c8fea2SSven Eckelmann 53c4aac1abSMarek Lindner if (!atomic_inc_not_zero(&gw_node->refcount)) 54c4aac1abSMarek Lindner gw_node = NULL; 55c4aac1abSMarek Lindner 56c4aac1abSMarek Lindner out: 57c4aac1abSMarek Lindner rcu_read_unlock(); 58c4aac1abSMarek Lindner return gw_node; 59c4aac1abSMarek Lindner } 60c4aac1abSMarek Lindner 6156303d34SSven Eckelmann struct batadv_orig_node * 6256303d34SSven Eckelmann batadv_gw_get_selected_orig(struct batadv_priv *bat_priv) 63c4aac1abSMarek Lindner { 6456303d34SSven Eckelmann struct batadv_gw_node *gw_node; 6556303d34SSven Eckelmann struct batadv_orig_node *orig_node = NULL; 66c4aac1abSMarek Lindner 671409a834SSven Eckelmann gw_node = batadv_gw_get_selected_gw_node(bat_priv); 68c4aac1abSMarek Lindner if (!gw_node) 697b36e8eeSMarek Lindner goto out; 705d02b3cdSLinus Lüssing 71c4aac1abSMarek Lindner rcu_read_lock(); 72c4aac1abSMarek Lindner orig_node = gw_node->orig_node; 73c4aac1abSMarek Lindner if (!orig_node) 74c4aac1abSMarek Lindner goto unlock; 75c4aac1abSMarek Lindner 767b36e8eeSMarek Lindner if (!atomic_inc_not_zero(&orig_node->refcount)) 777b36e8eeSMarek Lindner orig_node = NULL; 7843c70ad5SLinus Lüssing 79c4aac1abSMarek Lindner unlock: 805d02b3cdSLinus Lüssing rcu_read_unlock(); 81c4aac1abSMarek Lindner out: 82c6c8fea2SSven Eckelmann if (gw_node) 831409a834SSven Eckelmann batadv_gw_node_free_ref(gw_node); 84c4aac1abSMarek Lindner return orig_node; 85c6c8fea2SSven Eckelmann } 86c6c8fea2SSven Eckelmann 8756303d34SSven Eckelmann static void batadv_gw_select(struct batadv_priv *bat_priv, 8856303d34SSven Eckelmann struct batadv_gw_node *new_gw_node) 89c6c8fea2SSven Eckelmann { 9056303d34SSven Eckelmann struct batadv_gw_node *curr_gw_node; 91c6c8fea2SSven Eckelmann 92807736f6SSven Eckelmann spin_lock_bh(&bat_priv->gw.list_lock); 93c4aac1abSMarek Lindner 9425b6d3c1SMarek Lindner if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount)) 9525b6d3c1SMarek Lindner new_gw_node = NULL; 96c6c8fea2SSven Eckelmann 97807736f6SSven Eckelmann curr_gw_node = rcu_dereference_protected(bat_priv->gw.curr_gw, 1); 98807736f6SSven Eckelmann rcu_assign_pointer(bat_priv->gw.curr_gw, new_gw_node); 9925b6d3c1SMarek Lindner 10025b6d3c1SMarek Lindner if (curr_gw_node) 1011409a834SSven Eckelmann batadv_gw_node_free_ref(curr_gw_node); 102c4aac1abSMarek Lindner 103807736f6SSven Eckelmann spin_unlock_bh(&bat_priv->gw.list_lock); 104c4aac1abSMarek Lindner } 105c4aac1abSMarek Lindner 10656303d34SSven Eckelmann void batadv_gw_deselect(struct batadv_priv *bat_priv) 107c4aac1abSMarek Lindner { 108807736f6SSven Eckelmann atomic_set(&bat_priv->gw.reselect, 1); 109c6c8fea2SSven Eckelmann } 110c6c8fea2SSven Eckelmann 11156303d34SSven Eckelmann static struct batadv_gw_node * 11256303d34SSven Eckelmann batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv) 113c6c8fea2SSven Eckelmann { 11456303d34SSven Eckelmann struct batadv_neigh_node *router; 11556303d34SSven Eckelmann struct batadv_gw_node *gw_node, *curr_gw = NULL; 116c6c8fea2SSven Eckelmann uint32_t max_gw_factor = 0, tmp_gw_factor = 0; 117c67893d1SSven Eckelmann uint32_t gw_divisor; 1182265c141SAntonio Quartulli uint8_t max_tq = 0; 119c67893d1SSven Eckelmann uint8_t tq_avg; 12056303d34SSven Eckelmann struct batadv_orig_node *orig_node; 121c6c8fea2SSven Eckelmann 122c67893d1SSven Eckelmann gw_divisor = BATADV_TQ_LOCAL_WINDOW_SIZE * BATADV_TQ_LOCAL_WINDOW_SIZE; 123c67893d1SSven Eckelmann gw_divisor *= 64; 124c67893d1SSven Eckelmann 125c6c8fea2SSven Eckelmann rcu_read_lock(); 126b67bfe0dSSasha Levin hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) { 127e1a5382fSLinus Lüssing if (gw_node->deleted) 128c6c8fea2SSven Eckelmann continue; 129c6c8fea2SSven Eckelmann 13084d5e5e0SSven Eckelmann orig_node = gw_node->orig_node; 1317d211efcSSven Eckelmann router = batadv_orig_node_get_router(orig_node); 132e1a5382fSLinus Lüssing if (!router) 133c6c8fea2SSven Eckelmann continue; 134c6c8fea2SSven Eckelmann 1352265c141SAntonio Quartulli if (!atomic_inc_not_zero(&gw_node->refcount)) 1362265c141SAntonio Quartulli goto next; 1372265c141SAntonio Quartulli 1380538f759SAntonio Quartulli tq_avg = router->bat_iv.tq_avg; 139c67893d1SSven Eckelmann 140c6c8fea2SSven Eckelmann switch (atomic_read(&bat_priv->gw_sel_class)) { 141c6c8fea2SSven Eckelmann case 1: /* fast connection */ 142414254e3SMarek Lindner tmp_gw_factor = tq_avg * tq_avg; 143414254e3SMarek Lindner tmp_gw_factor *= gw_node->bandwidth_down; 144414254e3SMarek Lindner tmp_gw_factor *= 100 * 100; 145c67893d1SSven Eckelmann tmp_gw_factor /= gw_divisor; 146c6c8fea2SSven Eckelmann 147c6c8fea2SSven Eckelmann if ((tmp_gw_factor > max_gw_factor) || 148c6c8fea2SSven Eckelmann ((tmp_gw_factor == max_gw_factor) && 149c67893d1SSven Eckelmann (tq_avg > max_tq))) { 1502265c141SAntonio Quartulli if (curr_gw) 1511409a834SSven Eckelmann batadv_gw_node_free_ref(curr_gw); 1522265c141SAntonio Quartulli curr_gw = gw_node; 1532265c141SAntonio Quartulli atomic_inc(&curr_gw->refcount); 1542265c141SAntonio Quartulli } 155c6c8fea2SSven Eckelmann break; 156c6c8fea2SSven Eckelmann 1579cfc7bd6SSven Eckelmann default: /* 2: stable connection (use best statistic) 158c6c8fea2SSven Eckelmann * 3: fast-switch (use best statistic but change as 159c6c8fea2SSven Eckelmann * soon as a better gateway appears) 160c6c8fea2SSven Eckelmann * XX: late-switch (use best statistic but change as 161c6c8fea2SSven Eckelmann * soon as a better gateway appears which has 162c6c8fea2SSven Eckelmann * $routing_class more tq points) 1639cfc7bd6SSven Eckelmann */ 164c67893d1SSven Eckelmann if (tq_avg > max_tq) { 1652265c141SAntonio Quartulli if (curr_gw) 1661409a834SSven Eckelmann batadv_gw_node_free_ref(curr_gw); 1672265c141SAntonio Quartulli curr_gw = gw_node; 1682265c141SAntonio Quartulli atomic_inc(&curr_gw->refcount); 1692265c141SAntonio Quartulli } 170c6c8fea2SSven Eckelmann break; 171c6c8fea2SSven Eckelmann } 172c6c8fea2SSven Eckelmann 173c67893d1SSven Eckelmann if (tq_avg > max_tq) 174c67893d1SSven Eckelmann max_tq = tq_avg; 175c6c8fea2SSven Eckelmann 176c6c8fea2SSven Eckelmann if (tmp_gw_factor > max_gw_factor) 177c6c8fea2SSven Eckelmann max_gw_factor = tmp_gw_factor; 178e1a5382fSLinus Lüssing 1791409a834SSven Eckelmann batadv_gw_node_free_ref(gw_node); 1802265c141SAntonio Quartulli 1812265c141SAntonio Quartulli next: 1827d211efcSSven Eckelmann batadv_neigh_node_free_ref(router); 183c6c8fea2SSven Eckelmann } 1842265c141SAntonio Quartulli rcu_read_unlock(); 185c6c8fea2SSven Eckelmann 1862265c141SAntonio Quartulli return curr_gw; 1872265c141SAntonio Quartulli } 188e1a5382fSLinus Lüssing 189c6eaa3f0SAntonio Quartulli /** 190c6eaa3f0SAntonio Quartulli * batadv_gw_check_client_stop - check if client mode has been switched off 191c6eaa3f0SAntonio Quartulli * @bat_priv: the bat priv with all the soft interface information 192c6eaa3f0SAntonio Quartulli * 193c6eaa3f0SAntonio Quartulli * This function assumes the caller has checked that the gw state *is actually 194c6eaa3f0SAntonio Quartulli * changing*. This function is not supposed to be called when there is no state 195c6eaa3f0SAntonio Quartulli * change. 196c6eaa3f0SAntonio Quartulli */ 197c6eaa3f0SAntonio Quartulli void batadv_gw_check_client_stop(struct batadv_priv *bat_priv) 198c6eaa3f0SAntonio Quartulli { 199c6eaa3f0SAntonio Quartulli struct batadv_gw_node *curr_gw; 200c6eaa3f0SAntonio Quartulli 201c6eaa3f0SAntonio Quartulli if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_CLIENT) 202c6eaa3f0SAntonio Quartulli return; 203c6eaa3f0SAntonio Quartulli 204c6eaa3f0SAntonio Quartulli curr_gw = batadv_gw_get_selected_gw_node(bat_priv); 205c6eaa3f0SAntonio Quartulli if (!curr_gw) 206c6eaa3f0SAntonio Quartulli return; 207c6eaa3f0SAntonio Quartulli 208*f3163181SAntonio Quartulli /* deselect the current gateway so that next time that client mode is 209*f3163181SAntonio Quartulli * enabled a proper GW_ADD event can be sent 210*f3163181SAntonio Quartulli */ 211*f3163181SAntonio Quartulli batadv_gw_select(bat_priv, NULL); 212*f3163181SAntonio Quartulli 213c6eaa3f0SAntonio Quartulli /* if batman-adv is switching the gw client mode off and a gateway was 214c6eaa3f0SAntonio Quartulli * already selected, send a DEL uevent 215c6eaa3f0SAntonio Quartulli */ 216c6eaa3f0SAntonio Quartulli batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_DEL, NULL); 217c6eaa3f0SAntonio Quartulli 218c6eaa3f0SAntonio Quartulli batadv_gw_node_free_ref(curr_gw); 219c6eaa3f0SAntonio Quartulli } 220c6eaa3f0SAntonio Quartulli 22156303d34SSven Eckelmann void batadv_gw_election(struct batadv_priv *bat_priv) 2222265c141SAntonio Quartulli { 22356303d34SSven Eckelmann struct batadv_gw_node *curr_gw = NULL, *next_gw = NULL; 22456303d34SSven Eckelmann struct batadv_neigh_node *router = NULL; 22519595e05SAntonio Quartulli char gw_addr[18] = { '\0' }; 2262265c141SAntonio Quartulli 227cd646ab1SSven Eckelmann if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_CLIENT) 2282265c141SAntonio Quartulli goto out; 2292265c141SAntonio Quartulli 2301409a834SSven Eckelmann curr_gw = batadv_gw_get_selected_gw_node(bat_priv); 2312265c141SAntonio Quartulli 232807736f6SSven Eckelmann if (!batadv_atomic_dec_not_zero(&bat_priv->gw.reselect) && curr_gw) 233caa0bf64SMarek Lindner goto out; 234caa0bf64SMarek Lindner 2351409a834SSven Eckelmann next_gw = batadv_gw_get_best_gw_node(bat_priv); 2362265c141SAntonio Quartulli 2372265c141SAntonio Quartulli if (curr_gw == next_gw) 2382265c141SAntonio Quartulli goto out; 2392265c141SAntonio Quartulli 2402265c141SAntonio Quartulli if (next_gw) { 24119595e05SAntonio Quartulli sprintf(gw_addr, "%pM", next_gw->orig_node->orig); 24219595e05SAntonio Quartulli 2437d211efcSSven Eckelmann router = batadv_orig_node_get_router(next_gw->orig_node); 2442265c141SAntonio Quartulli if (!router) { 2457cf06bc6SSven Eckelmann batadv_gw_deselect(bat_priv); 2462265c141SAntonio Quartulli goto out; 2472265c141SAntonio Quartulli } 2482265c141SAntonio Quartulli } 2492265c141SAntonio Quartulli 2502265c141SAntonio Quartulli if ((curr_gw) && (!next_gw)) { 25139c75a51SSven Eckelmann batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 2522265c141SAntonio Quartulli "Removing selected gateway - no gateway in range\n"); 25339c75a51SSven Eckelmann batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_DEL, 25439c75a51SSven Eckelmann NULL); 2552265c141SAntonio Quartulli } else if ((!curr_gw) && (next_gw)) { 25639c75a51SSven Eckelmann batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 257414254e3SMarek Lindner "Adding route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n", 2581eda58bfSSven Eckelmann next_gw->orig_node->orig, 259414254e3SMarek Lindner next_gw->bandwidth_down / 10, 260414254e3SMarek Lindner next_gw->bandwidth_down % 10, 261414254e3SMarek Lindner next_gw->bandwidth_up / 10, 2620538f759SAntonio Quartulli next_gw->bandwidth_up % 10, router->bat_iv.tq_avg); 26339c75a51SSven Eckelmann batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_ADD, 26439c75a51SSven Eckelmann gw_addr); 2652265c141SAntonio Quartulli } else { 26639c75a51SSven Eckelmann batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 267414254e3SMarek Lindner "Changing route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n", 2681eda58bfSSven Eckelmann next_gw->orig_node->orig, 269414254e3SMarek Lindner next_gw->bandwidth_down / 10, 270414254e3SMarek Lindner next_gw->bandwidth_down % 10, 271414254e3SMarek Lindner next_gw->bandwidth_up / 10, 2720538f759SAntonio Quartulli next_gw->bandwidth_up % 10, router->bat_iv.tq_avg); 27339c75a51SSven Eckelmann batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_CHANGE, 27439c75a51SSven Eckelmann gw_addr); 275c6c8fea2SSven Eckelmann } 276c6c8fea2SSven Eckelmann 2771409a834SSven Eckelmann batadv_gw_select(bat_priv, next_gw); 2782265c141SAntonio Quartulli 279c4aac1abSMarek Lindner out: 280c4aac1abSMarek Lindner if (curr_gw) 2811409a834SSven Eckelmann batadv_gw_node_free_ref(curr_gw); 2822265c141SAntonio Quartulli if (next_gw) 2831409a834SSven Eckelmann batadv_gw_node_free_ref(next_gw); 2842265c141SAntonio Quartulli if (router) 2857d211efcSSven Eckelmann batadv_neigh_node_free_ref(router); 286c6c8fea2SSven Eckelmann } 287c6c8fea2SSven Eckelmann 28856303d34SSven Eckelmann void batadv_gw_check_election(struct batadv_priv *bat_priv, 28956303d34SSven Eckelmann struct batadv_orig_node *orig_node) 290c6c8fea2SSven Eckelmann { 29156303d34SSven Eckelmann struct batadv_orig_node *curr_gw_orig; 29256303d34SSven Eckelmann struct batadv_neigh_node *router_gw = NULL, *router_orig = NULL; 293c6c8fea2SSven Eckelmann uint8_t gw_tq_avg, orig_tq_avg; 294c6c8fea2SSven Eckelmann 2957cf06bc6SSven Eckelmann curr_gw_orig = batadv_gw_get_selected_orig(bat_priv); 29657f0c07cSLinus Lüssing if (!curr_gw_orig) 29757f0c07cSLinus Lüssing goto deselect; 298c6c8fea2SSven Eckelmann 2997d211efcSSven Eckelmann router_gw = batadv_orig_node_get_router(curr_gw_orig); 300e1a5382fSLinus Lüssing if (!router_gw) 301e1a5382fSLinus Lüssing goto deselect; 302c6c8fea2SSven Eckelmann 303c6c8fea2SSven Eckelmann /* this node already is the gateway */ 30457f0c07cSLinus Lüssing if (curr_gw_orig == orig_node) 305e1a5382fSLinus Lüssing goto out; 306c6c8fea2SSven Eckelmann 3077d211efcSSven Eckelmann router_orig = batadv_orig_node_get_router(orig_node); 308e1a5382fSLinus Lüssing if (!router_orig) 309e1a5382fSLinus Lüssing goto out; 310c6c8fea2SSven Eckelmann 3110538f759SAntonio Quartulli gw_tq_avg = router_gw->bat_iv.tq_avg; 3120538f759SAntonio Quartulli orig_tq_avg = router_orig->bat_iv.tq_avg; 313c6c8fea2SSven Eckelmann 314c6c8fea2SSven Eckelmann /* the TQ value has to be better */ 315c6c8fea2SSven Eckelmann if (orig_tq_avg < gw_tq_avg) 3165d02b3cdSLinus Lüssing goto out; 317c6c8fea2SSven Eckelmann 3189cfc7bd6SSven Eckelmann /* if the routing class is greater than 3 the value tells us how much 319c6c8fea2SSven Eckelmann * greater the TQ value of the new gateway must be 3209cfc7bd6SSven Eckelmann */ 321c6c8fea2SSven Eckelmann if ((atomic_read(&bat_priv->gw_sel_class) > 3) && 322c6c8fea2SSven Eckelmann (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw_sel_class))) 3235d02b3cdSLinus Lüssing goto out; 324c6c8fea2SSven Eckelmann 32539c75a51SSven Eckelmann batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 32686ceb360SSven Eckelmann "Restarting gateway selection: better gateway found (tq curr: %i, tq new: %i)\n", 327c6c8fea2SSven Eckelmann gw_tq_avg, orig_tq_avg); 328c6c8fea2SSven Eckelmann 329c6c8fea2SSven Eckelmann deselect: 3307cf06bc6SSven Eckelmann batadv_gw_deselect(bat_priv); 3315d02b3cdSLinus Lüssing out: 33257f0c07cSLinus Lüssing if (curr_gw_orig) 3337d211efcSSven Eckelmann batadv_orig_node_free_ref(curr_gw_orig); 334e1a5382fSLinus Lüssing if (router_gw) 3357d211efcSSven Eckelmann batadv_neigh_node_free_ref(router_gw); 336e1a5382fSLinus Lüssing if (router_orig) 3377d211efcSSven Eckelmann batadv_neigh_node_free_ref(router_orig); 33857f0c07cSLinus Lüssing 3395d02b3cdSLinus Lüssing return; 340c6c8fea2SSven Eckelmann } 341c6c8fea2SSven Eckelmann 342414254e3SMarek Lindner /** 343414254e3SMarek Lindner * batadv_gw_node_add - add gateway node to list of available gateways 344414254e3SMarek Lindner * @bat_priv: the bat priv with all the soft interface information 345414254e3SMarek Lindner * @orig_node: originator announcing gateway capabilities 346414254e3SMarek Lindner * @gateway: announced bandwidth information 347414254e3SMarek Lindner */ 34856303d34SSven Eckelmann static void batadv_gw_node_add(struct batadv_priv *bat_priv, 34956303d34SSven Eckelmann struct batadv_orig_node *orig_node, 350414254e3SMarek Lindner struct batadv_tvlv_gateway_data *gateway) 351c6c8fea2SSven Eckelmann { 35256303d34SSven Eckelmann struct batadv_gw_node *gw_node; 353414254e3SMarek Lindner 354414254e3SMarek Lindner if (gateway->bandwidth_down == 0) 355414254e3SMarek Lindner return; 356c6c8fea2SSven Eckelmann 357704509b8SSven Eckelmann gw_node = kzalloc(sizeof(*gw_node), GFP_ATOMIC); 358c6c8fea2SSven Eckelmann if (!gw_node) 359c6c8fea2SSven Eckelmann return; 360c6c8fea2SSven Eckelmann 361c6c8fea2SSven Eckelmann INIT_HLIST_NODE(&gw_node->list); 362c6c8fea2SSven Eckelmann gw_node->orig_node = orig_node; 36325b6d3c1SMarek Lindner atomic_set(&gw_node->refcount, 1); 364c6c8fea2SSven Eckelmann 365807736f6SSven Eckelmann spin_lock_bh(&bat_priv->gw.list_lock); 366807736f6SSven Eckelmann hlist_add_head_rcu(&gw_node->list, &bat_priv->gw.list); 367807736f6SSven Eckelmann spin_unlock_bh(&bat_priv->gw.list_lock); 368c6c8fea2SSven Eckelmann 36939c75a51SSven Eckelmann batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 370414254e3SMarek Lindner "Found new gateway %pM -> gw bandwidth: %u.%u/%u.%u MBit\n", 371414254e3SMarek Lindner orig_node->orig, 372414254e3SMarek Lindner ntohl(gateway->bandwidth_down) / 10, 373414254e3SMarek Lindner ntohl(gateway->bandwidth_down) % 10, 374414254e3SMarek Lindner ntohl(gateway->bandwidth_up) / 10, 375414254e3SMarek Lindner ntohl(gateway->bandwidth_up) % 10); 376c6c8fea2SSven Eckelmann } 377c6c8fea2SSven Eckelmann 378414254e3SMarek Lindner /** 379414254e3SMarek Lindner * batadv_gw_node_get - retrieve gateway node from list of available gateways 380414254e3SMarek Lindner * @bat_priv: the bat priv with all the soft interface information 381414254e3SMarek Lindner * @orig_node: originator announcing gateway capabilities 382414254e3SMarek Lindner * 383414254e3SMarek Lindner * Returns gateway node if found or NULL otherwise. 38471e4aa9cSAntonio Quartulli */ 385414254e3SMarek Lindner static struct batadv_gw_node * 386414254e3SMarek Lindner batadv_gw_node_get(struct batadv_priv *bat_priv, 387414254e3SMarek Lindner struct batadv_orig_node *orig_node) 388414254e3SMarek Lindner { 389414254e3SMarek Lindner struct batadv_gw_node *gw_node_tmp, *gw_node = NULL; 390c6c8fea2SSven Eckelmann 391c6c8fea2SSven Eckelmann rcu_read_lock(); 392414254e3SMarek Lindner hlist_for_each_entry_rcu(gw_node_tmp, &bat_priv->gw.list, list) { 393414254e3SMarek Lindner if (gw_node_tmp->orig_node != orig_node) 394c6c8fea2SSven Eckelmann continue; 395c6c8fea2SSven Eckelmann 396414254e3SMarek Lindner if (gw_node_tmp->deleted) 397414254e3SMarek Lindner continue; 398414254e3SMarek Lindner 399414254e3SMarek Lindner if (!atomic_inc_not_zero(&gw_node_tmp->refcount)) 400414254e3SMarek Lindner continue; 401414254e3SMarek Lindner 402414254e3SMarek Lindner gw_node = gw_node_tmp; 403414254e3SMarek Lindner break; 404414254e3SMarek Lindner } 405414254e3SMarek Lindner rcu_read_unlock(); 406414254e3SMarek Lindner 407414254e3SMarek Lindner return gw_node; 408414254e3SMarek Lindner } 409414254e3SMarek Lindner 410414254e3SMarek Lindner /** 411414254e3SMarek Lindner * batadv_gw_node_update - update list of available gateways with changed 412414254e3SMarek Lindner * bandwidth information 413414254e3SMarek Lindner * @bat_priv: the bat priv with all the soft interface information 414414254e3SMarek Lindner * @orig_node: originator announcing gateway capabilities 415414254e3SMarek Lindner * @gateway: announced bandwidth information 416414254e3SMarek Lindner */ 417414254e3SMarek Lindner void batadv_gw_node_update(struct batadv_priv *bat_priv, 418414254e3SMarek Lindner struct batadv_orig_node *orig_node, 419414254e3SMarek Lindner struct batadv_tvlv_gateway_data *gateway) 420414254e3SMarek Lindner { 421414254e3SMarek Lindner struct batadv_gw_node *gw_node, *curr_gw = NULL; 422414254e3SMarek Lindner 423414254e3SMarek Lindner gw_node = batadv_gw_node_get(bat_priv, orig_node); 424414254e3SMarek Lindner if (!gw_node) { 425414254e3SMarek Lindner batadv_gw_node_add(bat_priv, orig_node, gateway); 426414254e3SMarek Lindner goto out; 427414254e3SMarek Lindner } 428414254e3SMarek Lindner 429414254e3SMarek Lindner if ((gw_node->bandwidth_down == ntohl(gateway->bandwidth_down)) && 430414254e3SMarek Lindner (gw_node->bandwidth_up == ntohl(gateway->bandwidth_up))) 431414254e3SMarek Lindner goto out; 432414254e3SMarek Lindner 43339c75a51SSven Eckelmann batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 434414254e3SMarek Lindner "Gateway bandwidth of originator %pM changed from %u.%u/%u.%u MBit to %u.%u/%u.%u MBit\n", 435414254e3SMarek Lindner orig_node->orig, 436414254e3SMarek Lindner gw_node->bandwidth_down / 10, 437414254e3SMarek Lindner gw_node->bandwidth_down % 10, 438414254e3SMarek Lindner gw_node->bandwidth_up / 10, 439414254e3SMarek Lindner gw_node->bandwidth_up % 10, 440414254e3SMarek Lindner ntohl(gateway->bandwidth_down) / 10, 441414254e3SMarek Lindner ntohl(gateway->bandwidth_down) % 10, 442414254e3SMarek Lindner ntohl(gateway->bandwidth_up) / 10, 443414254e3SMarek Lindner ntohl(gateway->bandwidth_up) % 10); 444414254e3SMarek Lindner 445414254e3SMarek Lindner gw_node->bandwidth_down = ntohl(gateway->bandwidth_down); 446414254e3SMarek Lindner gw_node->bandwidth_up = ntohl(gateway->bandwidth_up); 447c6c8fea2SSven Eckelmann 448c6c8fea2SSven Eckelmann gw_node->deleted = 0; 449414254e3SMarek Lindner if (ntohl(gateway->bandwidth_down) == 0) { 450c6c8fea2SSven Eckelmann gw_node->deleted = jiffies; 45139c75a51SSven Eckelmann batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 452c6c8fea2SSven Eckelmann "Gateway %pM removed from gateway list\n", 453c6c8fea2SSven Eckelmann orig_node->orig); 454c6c8fea2SSven Eckelmann 455414254e3SMarek Lindner /* Note: We don't need a NULL check here, since curr_gw never 456414254e3SMarek Lindner * gets dereferenced. 457414254e3SMarek Lindner */ 458414254e3SMarek Lindner curr_gw = batadv_gw_get_selected_gw_node(bat_priv); 459c4aac1abSMarek Lindner if (gw_node == curr_gw) 4607cf06bc6SSven Eckelmann batadv_gw_deselect(bat_priv); 461414254e3SMarek Lindner } 46271e4aa9cSAntonio Quartulli 463414254e3SMarek Lindner out: 464c4aac1abSMarek Lindner if (curr_gw) 4651409a834SSven Eckelmann batadv_gw_node_free_ref(curr_gw); 466414254e3SMarek Lindner if (gw_node) 467414254e3SMarek Lindner batadv_gw_node_free_ref(gw_node); 468c6c8fea2SSven Eckelmann } 469c6c8fea2SSven Eckelmann 47056303d34SSven Eckelmann void batadv_gw_node_delete(struct batadv_priv *bat_priv, 47156303d34SSven Eckelmann struct batadv_orig_node *orig_node) 472c6c8fea2SSven Eckelmann { 473414254e3SMarek Lindner struct batadv_tvlv_gateway_data gateway; 474414254e3SMarek Lindner 475414254e3SMarek Lindner gateway.bandwidth_down = 0; 476414254e3SMarek Lindner gateway.bandwidth_up = 0; 477414254e3SMarek Lindner 478414254e3SMarek Lindner batadv_gw_node_update(bat_priv, orig_node, &gateway); 479c6c8fea2SSven Eckelmann } 480c6c8fea2SSven Eckelmann 48156303d34SSven Eckelmann void batadv_gw_node_purge(struct batadv_priv *bat_priv) 482c6c8fea2SSven Eckelmann { 48356303d34SSven Eckelmann struct batadv_gw_node *gw_node, *curr_gw; 484b67bfe0dSSasha Levin struct hlist_node *node_tmp; 48542d0b044SSven Eckelmann unsigned long timeout = msecs_to_jiffies(2 * BATADV_PURGE_TIMEOUT); 486b4e17054SSven Eckelmann int do_deselect = 0; 487c4aac1abSMarek Lindner 4881409a834SSven Eckelmann curr_gw = batadv_gw_get_selected_gw_node(bat_priv); 489c6c8fea2SSven Eckelmann 490807736f6SSven Eckelmann spin_lock_bh(&bat_priv->gw.list_lock); 491c6c8fea2SSven Eckelmann 492b67bfe0dSSasha Levin hlist_for_each_entry_safe(gw_node, node_tmp, 493807736f6SSven Eckelmann &bat_priv->gw.list, list) { 494c6c8fea2SSven Eckelmann if (((!gw_node->deleted) || 495c6c8fea2SSven Eckelmann (time_before(jiffies, gw_node->deleted + timeout))) && 49639c75a51SSven Eckelmann atomic_read(&bat_priv->mesh_state) == BATADV_MESH_ACTIVE) 497c6c8fea2SSven Eckelmann continue; 498c6c8fea2SSven Eckelmann 499c4aac1abSMarek Lindner if (curr_gw == gw_node) 500c4aac1abSMarek Lindner do_deselect = 1; 501c6c8fea2SSven Eckelmann 502c6c8fea2SSven Eckelmann hlist_del_rcu(&gw_node->list); 5031409a834SSven Eckelmann batadv_gw_node_free_ref(gw_node); 504c6c8fea2SSven Eckelmann } 505c6c8fea2SSven Eckelmann 506807736f6SSven Eckelmann spin_unlock_bh(&bat_priv->gw.list_lock); 507c4aac1abSMarek Lindner 508c4aac1abSMarek Lindner /* gw_deselect() needs to acquire the gw_list_lock */ 509c4aac1abSMarek Lindner if (do_deselect) 5107cf06bc6SSven Eckelmann batadv_gw_deselect(bat_priv); 511c4aac1abSMarek Lindner 512c4aac1abSMarek Lindner if (curr_gw) 5131409a834SSven Eckelmann batadv_gw_node_free_ref(curr_gw); 514c6c8fea2SSven Eckelmann } 515c6c8fea2SSven Eckelmann 5169cfc7bd6SSven Eckelmann /* fails if orig_node has no router */ 51756303d34SSven Eckelmann static int batadv_write_buffer_text(struct batadv_priv *bat_priv, 5181409a834SSven Eckelmann struct seq_file *seq, 51956303d34SSven Eckelmann const struct batadv_gw_node *gw_node) 520c6c8fea2SSven Eckelmann { 52156303d34SSven Eckelmann struct batadv_gw_node *curr_gw; 52256303d34SSven Eckelmann struct batadv_neigh_node *router; 523414254e3SMarek Lindner int ret = -1; 524c6c8fea2SSven Eckelmann 5257d211efcSSven Eckelmann router = batadv_orig_node_get_router(gw_node->orig_node); 526e1a5382fSLinus Lüssing if (!router) 527e1a5382fSLinus Lüssing goto out; 528e1a5382fSLinus Lüssing 5291409a834SSven Eckelmann curr_gw = batadv_gw_get_selected_gw_node(bat_priv); 5305d02b3cdSLinus Lüssing 531414254e3SMarek Lindner ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %u.%u/%u.%u MBit\n", 5325d02b3cdSLinus Lüssing (curr_gw == gw_node ? "=>" : " "), 533c6c8fea2SSven Eckelmann gw_node->orig_node->orig, 5340538f759SAntonio Quartulli router->bat_iv.tq_avg, router->addr, 535e1a5382fSLinus Lüssing router->if_incoming->net_dev->name, 536414254e3SMarek Lindner gw_node->bandwidth_down / 10, 537414254e3SMarek Lindner gw_node->bandwidth_down % 10, 538414254e3SMarek Lindner gw_node->bandwidth_up / 10, 539414254e3SMarek Lindner gw_node->bandwidth_up % 10); 5405d02b3cdSLinus Lüssing 5417d211efcSSven Eckelmann batadv_neigh_node_free_ref(router); 542c4aac1abSMarek Lindner if (curr_gw) 5431409a834SSven Eckelmann batadv_gw_node_free_ref(curr_gw); 544e1a5382fSLinus Lüssing out: 5455d02b3cdSLinus Lüssing return ret; 546c6c8fea2SSven Eckelmann } 547c6c8fea2SSven Eckelmann 5487cf06bc6SSven Eckelmann int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset) 549c6c8fea2SSven Eckelmann { 550c6c8fea2SSven Eckelmann struct net_device *net_dev = (struct net_device *)seq->private; 55156303d34SSven Eckelmann struct batadv_priv *bat_priv = netdev_priv(net_dev); 55256303d34SSven Eckelmann struct batadv_hard_iface *primary_if; 55356303d34SSven Eckelmann struct batadv_gw_node *gw_node; 55430da63a6SMarek Lindner int gw_count = 0; 555c6c8fea2SSven Eckelmann 55630da63a6SMarek Lindner primary_if = batadv_seq_print_text_primary_if_get(seq); 55730da63a6SMarek Lindner if (!primary_if) 55832ae9b22SMarek Lindner goto out; 559c6c8fea2SSven Eckelmann 56086ceb360SSven Eckelmann seq_printf(seq, 561414254e3SMarek Lindner " %-12s (%s/%i) %17s [%10s]: advertised uplink bandwidth ... [B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n", 56242d0b044SSven Eckelmann "Gateway", "#", BATADV_TQ_MAX_VALUE, "Nexthop", "outgoingIF", 56342d0b044SSven Eckelmann BATADV_SOURCE_VERSION, primary_if->net_dev->name, 56432ae9b22SMarek Lindner primary_if->net_dev->dev_addr, net_dev->name); 565c6c8fea2SSven Eckelmann 566c6c8fea2SSven Eckelmann rcu_read_lock(); 567b67bfe0dSSasha Levin hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) { 568c6c8fea2SSven Eckelmann if (gw_node->deleted) 569c6c8fea2SSven Eckelmann continue; 570c6c8fea2SSven Eckelmann 571e1a5382fSLinus Lüssing /* fails if orig_node has no router */ 5721409a834SSven Eckelmann if (batadv_write_buffer_text(bat_priv, seq, gw_node) < 0) 573c6c8fea2SSven Eckelmann continue; 574c6c8fea2SSven Eckelmann 575c6c8fea2SSven Eckelmann gw_count++; 576c6c8fea2SSven Eckelmann } 577c6c8fea2SSven Eckelmann rcu_read_unlock(); 578c6c8fea2SSven Eckelmann 579c6c8fea2SSven Eckelmann if (gw_count == 0) 5800c814653SAntonio Quartulli seq_puts(seq, "No gateways in range ...\n"); 581c6c8fea2SSven Eckelmann 58232ae9b22SMarek Lindner out: 58332ae9b22SMarek Lindner if (primary_if) 584e5d89254SSven Eckelmann batadv_hardif_free_ref(primary_if); 58530da63a6SMarek Lindner return 0; 586c6c8fea2SSven Eckelmann } 587c6c8fea2SSven Eckelmann 5889d2c9488SLinus Lüssing /* this call might reallocate skb data */ 5891409a834SSven Eckelmann static bool batadv_is_type_dhcprequest(struct sk_buff *skb, int header_len) 59043676ab5SAntonio Quartulli { 59143676ab5SAntonio Quartulli int ret = false; 59243676ab5SAntonio Quartulli unsigned char *p; 59343676ab5SAntonio Quartulli int pkt_len; 59443676ab5SAntonio Quartulli 59543676ab5SAntonio Quartulli if (skb_linearize(skb) < 0) 59643676ab5SAntonio Quartulli goto out; 59743676ab5SAntonio Quartulli 59843676ab5SAntonio Quartulli pkt_len = skb_headlen(skb); 59943676ab5SAntonio Quartulli 600347c80f0SSven Eckelmann if (pkt_len < header_len + BATADV_DHCP_OPTIONS_OFFSET + 1) 60143676ab5SAntonio Quartulli goto out; 60243676ab5SAntonio Quartulli 603347c80f0SSven Eckelmann p = skb->data + header_len + BATADV_DHCP_OPTIONS_OFFSET; 604347c80f0SSven Eckelmann pkt_len -= header_len + BATADV_DHCP_OPTIONS_OFFSET + 1; 60543676ab5SAntonio Quartulli 60643676ab5SAntonio Quartulli /* Access the dhcp option lists. Each entry is made up by: 607015758d0SAntonio Quartulli * - octet 1: option type 608015758d0SAntonio Quartulli * - octet 2: option data len (only if type != 255 and 0) 6099cfc7bd6SSven Eckelmann * - octet 3: option data 6109cfc7bd6SSven Eckelmann */ 61143676ab5SAntonio Quartulli while (*p != 255 && !ret) { 612015758d0SAntonio Quartulli /* p now points to the first octet: option type */ 61343676ab5SAntonio Quartulli if (*p == 53) { 61443676ab5SAntonio Quartulli /* type 53 is the message type option. 6159cfc7bd6SSven Eckelmann * Jump the len octet and go to the data octet 6169cfc7bd6SSven Eckelmann */ 61743676ab5SAntonio Quartulli if (pkt_len < 2) 61843676ab5SAntonio Quartulli goto out; 61943676ab5SAntonio Quartulli p += 2; 62043676ab5SAntonio Quartulli 62143676ab5SAntonio Quartulli /* check if the message type is what we need */ 622347c80f0SSven Eckelmann if (*p == BATADV_DHCP_REQUEST) 62343676ab5SAntonio Quartulli ret = true; 62443676ab5SAntonio Quartulli break; 62543676ab5SAntonio Quartulli } else if (*p == 0) { 62643676ab5SAntonio Quartulli /* option type 0 (padding), just go forward */ 62743676ab5SAntonio Quartulli if (pkt_len < 1) 62843676ab5SAntonio Quartulli goto out; 62943676ab5SAntonio Quartulli pkt_len--; 63043676ab5SAntonio Quartulli p++; 63143676ab5SAntonio Quartulli } else { 63243676ab5SAntonio Quartulli /* This is any other option. So we get the length... */ 63343676ab5SAntonio Quartulli if (pkt_len < 1) 63443676ab5SAntonio Quartulli goto out; 63543676ab5SAntonio Quartulli pkt_len--; 63643676ab5SAntonio Quartulli p++; 63743676ab5SAntonio Quartulli 63843676ab5SAntonio Quartulli /* ...and then we jump over the data */ 6399205cc52SAntonio Quartulli if (pkt_len < 1 + (*p)) 64043676ab5SAntonio Quartulli goto out; 6419205cc52SAntonio Quartulli pkt_len -= 1 + (*p); 6429205cc52SAntonio Quartulli p += 1 + (*p); 64343676ab5SAntonio Quartulli } 64443676ab5SAntonio Quartulli } 64543676ab5SAntonio Quartulli out: 64643676ab5SAntonio Quartulli return ret; 64743676ab5SAntonio Quartulli } 64843676ab5SAntonio Quartulli 6499d2c9488SLinus Lüssing /* this call might reallocate skb data */ 6507cf06bc6SSven Eckelmann bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) 651c6c8fea2SSven Eckelmann { 652c6c8fea2SSven Eckelmann struct ethhdr *ethhdr; 653c6c8fea2SSven Eckelmann struct iphdr *iphdr; 654c6c8fea2SSven Eckelmann struct ipv6hdr *ipv6hdr; 655c6c8fea2SSven Eckelmann struct udphdr *udphdr; 656f7f8ed56SAntonio Quartulli struct vlan_ethhdr *vhdr; 657f7f8ed56SAntonio Quartulli __be16 proto; 658c6c8fea2SSven Eckelmann 659c6c8fea2SSven Eckelmann /* check for ethernet header */ 660be7af5cfSMarek Lindner if (!pskb_may_pull(skb, *header_len + ETH_HLEN)) 661be7af5cfSMarek Lindner return false; 662c6c8fea2SSven Eckelmann ethhdr = (struct ethhdr *)skb->data; 663f7f8ed56SAntonio Quartulli proto = ethhdr->h_proto; 664be7af5cfSMarek Lindner *header_len += ETH_HLEN; 665c6c8fea2SSven Eckelmann 666c6c8fea2SSven Eckelmann /* check for initial vlan header */ 667f7f8ed56SAntonio Quartulli if (proto == htons(ETH_P_8021Q)) { 668be7af5cfSMarek Lindner if (!pskb_may_pull(skb, *header_len + VLAN_HLEN)) 669be7af5cfSMarek Lindner return false; 670f7f8ed56SAntonio Quartulli 671f7f8ed56SAntonio Quartulli vhdr = (struct vlan_ethhdr *)skb->data; 672f7f8ed56SAntonio Quartulli proto = vhdr->h_vlan_encapsulated_proto; 673be7af5cfSMarek Lindner *header_len += VLAN_HLEN; 674c6c8fea2SSven Eckelmann } 675c6c8fea2SSven Eckelmann 676c6c8fea2SSven Eckelmann /* check for ip header */ 677f7f8ed56SAntonio Quartulli switch (proto) { 678f7f8ed56SAntonio Quartulli case htons(ETH_P_IP): 679be7af5cfSMarek Lindner if (!pskb_may_pull(skb, *header_len + sizeof(*iphdr))) 680be7af5cfSMarek Lindner return false; 681be7af5cfSMarek Lindner iphdr = (struct iphdr *)(skb->data + *header_len); 682be7af5cfSMarek Lindner *header_len += iphdr->ihl * 4; 683c6c8fea2SSven Eckelmann 684c6c8fea2SSven Eckelmann /* check for udp header */ 685c6c8fea2SSven Eckelmann if (iphdr->protocol != IPPROTO_UDP) 686be7af5cfSMarek Lindner return false; 687c6c8fea2SSven Eckelmann 688c6c8fea2SSven Eckelmann break; 689f7f8ed56SAntonio Quartulli case htons(ETH_P_IPV6): 690be7af5cfSMarek Lindner if (!pskb_may_pull(skb, *header_len + sizeof(*ipv6hdr))) 691be7af5cfSMarek Lindner return false; 692be7af5cfSMarek Lindner ipv6hdr = (struct ipv6hdr *)(skb->data + *header_len); 693be7af5cfSMarek Lindner *header_len += sizeof(*ipv6hdr); 694c6c8fea2SSven Eckelmann 695c6c8fea2SSven Eckelmann /* check for udp header */ 696c6c8fea2SSven Eckelmann if (ipv6hdr->nexthdr != IPPROTO_UDP) 697be7af5cfSMarek Lindner return false; 698c6c8fea2SSven Eckelmann 699c6c8fea2SSven Eckelmann break; 700c6c8fea2SSven Eckelmann default: 701be7af5cfSMarek Lindner return false; 702c6c8fea2SSven Eckelmann } 703c6c8fea2SSven Eckelmann 704be7af5cfSMarek Lindner if (!pskb_may_pull(skb, *header_len + sizeof(*udphdr))) 705be7af5cfSMarek Lindner return false; 7069d2c9488SLinus Lüssing 7079d2c9488SLinus Lüssing /* skb->data might have been reallocated by pskb_may_pull() */ 7089d2c9488SLinus Lüssing ethhdr = (struct ethhdr *)skb->data; 7099d2c9488SLinus Lüssing if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) 7109d2c9488SLinus Lüssing ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN); 7119d2c9488SLinus Lüssing 712be7af5cfSMarek Lindner udphdr = (struct udphdr *)(skb->data + *header_len); 713be7af5cfSMarek Lindner *header_len += sizeof(*udphdr); 714c6c8fea2SSven Eckelmann 715c6c8fea2SSven Eckelmann /* check for bootp port */ 716f7f8ed56SAntonio Quartulli if ((proto == htons(ETH_P_IP)) && 717293e9338SAntonio Quartulli (udphdr->dest != htons(67))) 718be7af5cfSMarek Lindner return false; 719c6c8fea2SSven Eckelmann 720f7f8ed56SAntonio Quartulli if ((proto == htons(ETH_P_IPV6)) && 721293e9338SAntonio Quartulli (udphdr->dest != htons(547))) 722be7af5cfSMarek Lindner return false; 723c6c8fea2SSven Eckelmann 724be7af5cfSMarek Lindner return true; 725be7af5cfSMarek Lindner } 726c6c8fea2SSven Eckelmann 727bbb877edSAntonio Quartulli /** 728bbb877edSAntonio Quartulli * batadv_gw_out_of_range - check if the dhcp request destination is the best gw 729bbb877edSAntonio Quartulli * @bat_priv: the bat priv with all the soft interface information 730bbb877edSAntonio Quartulli * @skb: the outgoing packet 731bbb877edSAntonio Quartulli * 732bbb877edSAntonio Quartulli * Check if the skb is a DHCP request and if it is sent to the current best GW 733bbb877edSAntonio Quartulli * server. Due to topology changes it may be the case that the GW server 734bbb877edSAntonio Quartulli * previously selected is not the best one anymore. 735bbb877edSAntonio Quartulli * 736bbb877edSAntonio Quartulli * Returns true if the packet destination is unicast and it is not the best gw, 737bbb877edSAntonio Quartulli * false otherwise. 738bbb877edSAntonio Quartulli * 739bbb877edSAntonio Quartulli * This call might reallocate skb data. 740bbb877edSAntonio Quartulli */ 74156303d34SSven Eckelmann bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, 7429d2c9488SLinus Lüssing struct sk_buff *skb) 743be7af5cfSMarek Lindner { 74456303d34SSven Eckelmann struct batadv_neigh_node *neigh_curr = NULL, *neigh_old = NULL; 74556303d34SSven Eckelmann struct batadv_orig_node *orig_dst_node = NULL; 746414254e3SMarek Lindner struct batadv_gw_node *gw_node = NULL, *curr_gw = NULL; 7479d2c9488SLinus Lüssing struct ethhdr *ethhdr; 748be7af5cfSMarek Lindner bool ret, out_of_range = false; 749be7af5cfSMarek Lindner unsigned int header_len = 0; 750be7af5cfSMarek Lindner uint8_t curr_tq_avg; 751bbb877edSAntonio Quartulli unsigned short vid; 752bbb877edSAntonio Quartulli 753bbb877edSAntonio Quartulli vid = batadv_get_vid(skb, 0); 754be7af5cfSMarek Lindner 7557cf06bc6SSven Eckelmann ret = batadv_gw_is_dhcp_target(skb, &header_len); 756be7af5cfSMarek Lindner if (!ret) 757be7af5cfSMarek Lindner goto out; 758be7af5cfSMarek Lindner 7599d2c9488SLinus Lüssing ethhdr = (struct ethhdr *)skb->data; 76008c36d3eSSven Eckelmann orig_dst_node = batadv_transtable_search(bat_priv, ethhdr->h_source, 761bbb877edSAntonio Quartulli ethhdr->h_dest, vid); 762be7af5cfSMarek Lindner if (!orig_dst_node) 763be7af5cfSMarek Lindner goto out; 764be7af5cfSMarek Lindner 765414254e3SMarek Lindner gw_node = batadv_gw_node_get(bat_priv, orig_dst_node); 766414254e3SMarek Lindner if (!gw_node->bandwidth_down == 0) 767be7af5cfSMarek Lindner goto out; 768be7af5cfSMarek Lindner 7691409a834SSven Eckelmann ret = batadv_is_type_dhcprequest(skb, header_len); 770be7af5cfSMarek Lindner if (!ret) 771be7af5cfSMarek Lindner goto out; 772be7af5cfSMarek Lindner 773be7af5cfSMarek Lindner switch (atomic_read(&bat_priv->gw_mode)) { 774cd646ab1SSven Eckelmann case BATADV_GW_MODE_SERVER: 775be7af5cfSMarek Lindner /* If we are a GW then we are our best GW. We can artificially 7769cfc7bd6SSven Eckelmann * set the tq towards ourself as the maximum value 7779cfc7bd6SSven Eckelmann */ 77842d0b044SSven Eckelmann curr_tq_avg = BATADV_TQ_MAX_VALUE; 779be7af5cfSMarek Lindner break; 780cd646ab1SSven Eckelmann case BATADV_GW_MODE_CLIENT: 7811409a834SSven Eckelmann curr_gw = batadv_gw_get_selected_gw_node(bat_priv); 782c4aac1abSMarek Lindner if (!curr_gw) 783be7af5cfSMarek Lindner goto out; 784c6c8fea2SSven Eckelmann 785be7af5cfSMarek Lindner /* packet is going to our gateway */ 786be7af5cfSMarek Lindner if (curr_gw->orig_node == orig_dst_node) 787be7af5cfSMarek Lindner goto out; 788be7af5cfSMarek Lindner 78943676ab5SAntonio Quartulli /* If the dhcp packet has been sent to a different gw, 79043676ab5SAntonio Quartulli * we have to evaluate whether the old gw is still 7919cfc7bd6SSven Eckelmann * reliable enough 7929cfc7bd6SSven Eckelmann */ 79330d3c511SSven Eckelmann neigh_curr = batadv_find_router(bat_priv, curr_gw->orig_node, 79430d3c511SSven Eckelmann NULL); 795be7af5cfSMarek Lindner if (!neigh_curr) 796be7af5cfSMarek Lindner goto out; 797be7af5cfSMarek Lindner 7980538f759SAntonio Quartulli curr_tq_avg = neigh_curr->bat_iv.tq_avg; 799be7af5cfSMarek Lindner break; 800cd646ab1SSven Eckelmann case BATADV_GW_MODE_OFF: 801be7af5cfSMarek Lindner default: 802be7af5cfSMarek Lindner goto out; 80343676ab5SAntonio Quartulli } 804be7af5cfSMarek Lindner 80530d3c511SSven Eckelmann neigh_old = batadv_find_router(bat_priv, orig_dst_node, NULL); 8062ef04f47SDan Carpenter if (!neigh_old) 807be7af5cfSMarek Lindner goto out; 808be7af5cfSMarek Lindner 8090538f759SAntonio Quartulli if (curr_tq_avg - neigh_old->bat_iv.tq_avg > BATADV_GW_THRESHOLD) 810be7af5cfSMarek Lindner out_of_range = true; 811be7af5cfSMarek Lindner 812be7af5cfSMarek Lindner out: 813be7af5cfSMarek Lindner if (orig_dst_node) 8147d211efcSSven Eckelmann batadv_orig_node_free_ref(orig_dst_node); 815be7af5cfSMarek Lindner if (curr_gw) 8161409a834SSven Eckelmann batadv_gw_node_free_ref(curr_gw); 817414254e3SMarek Lindner if (gw_node) 818414254e3SMarek Lindner batadv_gw_node_free_ref(gw_node); 81943676ab5SAntonio Quartulli if (neigh_old) 8207d211efcSSven Eckelmann batadv_neigh_node_free_ref(neigh_old); 82143676ab5SAntonio Quartulli if (neigh_curr) 8227d211efcSSven Eckelmann batadv_neigh_node_free_ref(neigh_curr); 823be7af5cfSMarek Lindner return out_of_range; 824c6c8fea2SSven Eckelmann } 825