18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright (C) 2009-2020  B.A.T.M.A.N. contributors:
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Marek Lindner
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "gateway_client.h"
88c2ecf20Sopenharmony_ci#include "main.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/atomic.h>
118c2ecf20Sopenharmony_ci#include <linux/byteorder/generic.h>
128c2ecf20Sopenharmony_ci#include <linux/errno.h>
138c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
148c2ecf20Sopenharmony_ci#include <linux/gfp.h>
158c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
168c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
178c2ecf20Sopenharmony_ci#include <linux/in.h>
188c2ecf20Sopenharmony_ci#include <linux/ip.h>
198c2ecf20Sopenharmony_ci#include <linux/ipv6.h>
208c2ecf20Sopenharmony_ci#include <linux/kernel.h>
218c2ecf20Sopenharmony_ci#include <linux/kref.h>
228c2ecf20Sopenharmony_ci#include <linux/list.h>
238c2ecf20Sopenharmony_ci#include <linux/lockdep.h>
248c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
258c2ecf20Sopenharmony_ci#include <linux/netlink.h>
268c2ecf20Sopenharmony_ci#include <linux/rculist.h>
278c2ecf20Sopenharmony_ci#include <linux/rcupdate.h>
288c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
298c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
308c2ecf20Sopenharmony_ci#include <linux/slab.h>
318c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
328c2ecf20Sopenharmony_ci#include <linux/stddef.h>
338c2ecf20Sopenharmony_ci#include <linux/udp.h>
348c2ecf20Sopenharmony_ci#include <net/sock.h>
358c2ecf20Sopenharmony_ci#include <uapi/linux/batadv_packet.h>
368c2ecf20Sopenharmony_ci#include <uapi/linux/batman_adv.h>
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#include "hard-interface.h"
398c2ecf20Sopenharmony_ci#include "log.h"
408c2ecf20Sopenharmony_ci#include "netlink.h"
418c2ecf20Sopenharmony_ci#include "originator.h"
428c2ecf20Sopenharmony_ci#include "routing.h"
438c2ecf20Sopenharmony_ci#include "soft-interface.h"
448c2ecf20Sopenharmony_ci#include "translation-table.h"
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* These are the offsets of the "hw type" and "hw address length" in the dhcp
478c2ecf20Sopenharmony_ci * packet starting at the beginning of the dhcp header
488c2ecf20Sopenharmony_ci */
498c2ecf20Sopenharmony_ci#define BATADV_DHCP_HTYPE_OFFSET	1
508c2ecf20Sopenharmony_ci#define BATADV_DHCP_HLEN_OFFSET		2
518c2ecf20Sopenharmony_ci/* Value of htype representing Ethernet */
528c2ecf20Sopenharmony_ci#define BATADV_DHCP_HTYPE_ETHERNET	0x01
538c2ecf20Sopenharmony_ci/* This is the offset of the "chaddr" field in the dhcp packet starting at the
548c2ecf20Sopenharmony_ci * beginning of the dhcp header
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_ci#define BATADV_DHCP_CHADDR_OFFSET	28
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/**
598c2ecf20Sopenharmony_ci * batadv_gw_node_release() - release gw_node from lists and queue for free
608c2ecf20Sopenharmony_ci *  after rcu grace period
618c2ecf20Sopenharmony_ci * @ref: kref pointer of the gw_node
628c2ecf20Sopenharmony_ci */
638c2ecf20Sopenharmony_civoid batadv_gw_node_release(struct kref *ref)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct batadv_gw_node *gw_node;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	gw_node = container_of(ref, struct batadv_gw_node, refcount);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	batadv_orig_node_put(gw_node->orig_node);
708c2ecf20Sopenharmony_ci	kfree_rcu(gw_node, rcu);
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/**
748c2ecf20Sopenharmony_ci * batadv_gw_get_selected_gw_node() - Get currently selected gateway
758c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
768c2ecf20Sopenharmony_ci *
778c2ecf20Sopenharmony_ci * Return: selected gateway (with increased refcnt), NULL on errors
788c2ecf20Sopenharmony_ci */
798c2ecf20Sopenharmony_cistruct batadv_gw_node *
808c2ecf20Sopenharmony_cibatadv_gw_get_selected_gw_node(struct batadv_priv *bat_priv)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct batadv_gw_node *gw_node;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	rcu_read_lock();
858c2ecf20Sopenharmony_ci	gw_node = rcu_dereference(bat_priv->gw.curr_gw);
868c2ecf20Sopenharmony_ci	if (!gw_node)
878c2ecf20Sopenharmony_ci		goto out;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (!kref_get_unless_zero(&gw_node->refcount))
908c2ecf20Sopenharmony_ci		gw_node = NULL;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ciout:
938c2ecf20Sopenharmony_ci	rcu_read_unlock();
948c2ecf20Sopenharmony_ci	return gw_node;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/**
988c2ecf20Sopenharmony_ci * batadv_gw_get_selected_orig() - Get originator of currently selected gateway
998c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
1008c2ecf20Sopenharmony_ci *
1018c2ecf20Sopenharmony_ci * Return: orig_node of selected gateway (with increased refcnt), NULL on errors
1028c2ecf20Sopenharmony_ci */
1038c2ecf20Sopenharmony_cistruct batadv_orig_node *
1048c2ecf20Sopenharmony_cibatadv_gw_get_selected_orig(struct batadv_priv *bat_priv)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct batadv_gw_node *gw_node;
1078c2ecf20Sopenharmony_ci	struct batadv_orig_node *orig_node = NULL;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	gw_node = batadv_gw_get_selected_gw_node(bat_priv);
1108c2ecf20Sopenharmony_ci	if (!gw_node)
1118c2ecf20Sopenharmony_ci		goto out;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	rcu_read_lock();
1148c2ecf20Sopenharmony_ci	orig_node = gw_node->orig_node;
1158c2ecf20Sopenharmony_ci	if (!orig_node)
1168c2ecf20Sopenharmony_ci		goto unlock;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (!kref_get_unless_zero(&orig_node->refcount))
1198c2ecf20Sopenharmony_ci		orig_node = NULL;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ciunlock:
1228c2ecf20Sopenharmony_ci	rcu_read_unlock();
1238c2ecf20Sopenharmony_ciout:
1248c2ecf20Sopenharmony_ci	if (gw_node)
1258c2ecf20Sopenharmony_ci		batadv_gw_node_put(gw_node);
1268c2ecf20Sopenharmony_ci	return orig_node;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic void batadv_gw_select(struct batadv_priv *bat_priv,
1308c2ecf20Sopenharmony_ci			     struct batadv_gw_node *new_gw_node)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct batadv_gw_node *curr_gw_node;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	spin_lock_bh(&bat_priv->gw.list_lock);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (new_gw_node)
1378c2ecf20Sopenharmony_ci		kref_get(&new_gw_node->refcount);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	curr_gw_node = rcu_replace_pointer(bat_priv->gw.curr_gw, new_gw_node,
1408c2ecf20Sopenharmony_ci					   true);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (curr_gw_node)
1438c2ecf20Sopenharmony_ci		batadv_gw_node_put(curr_gw_node);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	spin_unlock_bh(&bat_priv->gw.list_lock);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/**
1498c2ecf20Sopenharmony_ci * batadv_gw_reselect() - force a gateway reselection
1508c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
1518c2ecf20Sopenharmony_ci *
1528c2ecf20Sopenharmony_ci * Set a flag to remind the GW component to perform a new gateway reselection.
1538c2ecf20Sopenharmony_ci * However this function does not ensure that the current gateway is going to be
1548c2ecf20Sopenharmony_ci * deselected. The reselection mechanism may elect the same gateway once again.
1558c2ecf20Sopenharmony_ci *
1568c2ecf20Sopenharmony_ci * This means that invoking batadv_gw_reselect() does not guarantee a gateway
1578c2ecf20Sopenharmony_ci * change and therefore a uevent is not necessarily expected.
1588c2ecf20Sopenharmony_ci */
1598c2ecf20Sopenharmony_civoid batadv_gw_reselect(struct batadv_priv *bat_priv)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	atomic_set(&bat_priv->gw.reselect, 1);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci/**
1658c2ecf20Sopenharmony_ci * batadv_gw_check_client_stop() - check if client mode has been switched off
1668c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
1678c2ecf20Sopenharmony_ci *
1688c2ecf20Sopenharmony_ci * This function assumes the caller has checked that the gw state *is actually
1698c2ecf20Sopenharmony_ci * changing*. This function is not supposed to be called when there is no state
1708c2ecf20Sopenharmony_ci * change.
1718c2ecf20Sopenharmony_ci */
1728c2ecf20Sopenharmony_civoid batadv_gw_check_client_stop(struct batadv_priv *bat_priv)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct batadv_gw_node *curr_gw;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (atomic_read(&bat_priv->gw.mode) != BATADV_GW_MODE_CLIENT)
1778c2ecf20Sopenharmony_ci		return;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
1808c2ecf20Sopenharmony_ci	if (!curr_gw)
1818c2ecf20Sopenharmony_ci		return;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* deselect the current gateway so that next time that client mode is
1848c2ecf20Sopenharmony_ci	 * enabled a proper GW_ADD event can be sent
1858c2ecf20Sopenharmony_ci	 */
1868c2ecf20Sopenharmony_ci	batadv_gw_select(bat_priv, NULL);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* if batman-adv is switching the gw client mode off and a gateway was
1898c2ecf20Sopenharmony_ci	 * already selected, send a DEL uevent
1908c2ecf20Sopenharmony_ci	 */
1918c2ecf20Sopenharmony_ci	batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_DEL, NULL);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	batadv_gw_node_put(curr_gw);
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci/**
1978c2ecf20Sopenharmony_ci * batadv_gw_election() - Elect the best gateway
1988c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
1998c2ecf20Sopenharmony_ci */
2008c2ecf20Sopenharmony_civoid batadv_gw_election(struct batadv_priv *bat_priv)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	struct batadv_gw_node *curr_gw = NULL;
2038c2ecf20Sopenharmony_ci	struct batadv_gw_node *next_gw = NULL;
2048c2ecf20Sopenharmony_ci	struct batadv_neigh_node *router = NULL;
2058c2ecf20Sopenharmony_ci	struct batadv_neigh_ifinfo *router_ifinfo = NULL;
2068c2ecf20Sopenharmony_ci	char gw_addr[18] = { '\0' };
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (atomic_read(&bat_priv->gw.mode) != BATADV_GW_MODE_CLIENT)
2098c2ecf20Sopenharmony_ci		goto out;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (!bat_priv->algo_ops->gw.get_best_gw_node)
2128c2ecf20Sopenharmony_ci		goto out;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (!batadv_atomic_dec_not_zero(&bat_priv->gw.reselect) && curr_gw)
2178c2ecf20Sopenharmony_ci		goto out;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/* if gw.reselect is set to 1 it means that a previous call to
2208c2ecf20Sopenharmony_ci	 * gw.is_eligible() said that we have a new best GW, therefore it can
2218c2ecf20Sopenharmony_ci	 * now be picked from the list and selected
2228c2ecf20Sopenharmony_ci	 */
2238c2ecf20Sopenharmony_ci	next_gw = bat_priv->algo_ops->gw.get_best_gw_node(bat_priv);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (curr_gw == next_gw)
2268c2ecf20Sopenharmony_ci		goto out;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (next_gw) {
2298c2ecf20Sopenharmony_ci		sprintf(gw_addr, "%pM", next_gw->orig_node->orig);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		router = batadv_orig_router_get(next_gw->orig_node,
2328c2ecf20Sopenharmony_ci						BATADV_IF_DEFAULT);
2338c2ecf20Sopenharmony_ci		if (!router) {
2348c2ecf20Sopenharmony_ci			batadv_gw_reselect(bat_priv);
2358c2ecf20Sopenharmony_ci			goto out;
2368c2ecf20Sopenharmony_ci		}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci		router_ifinfo = batadv_neigh_ifinfo_get(router,
2398c2ecf20Sopenharmony_ci							BATADV_IF_DEFAULT);
2408c2ecf20Sopenharmony_ci		if (!router_ifinfo) {
2418c2ecf20Sopenharmony_ci			batadv_gw_reselect(bat_priv);
2428c2ecf20Sopenharmony_ci			goto out;
2438c2ecf20Sopenharmony_ci		}
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (curr_gw && !next_gw) {
2478c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
2488c2ecf20Sopenharmony_ci			   "Removing selected gateway - no gateway in range\n");
2498c2ecf20Sopenharmony_ci		batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_DEL,
2508c2ecf20Sopenharmony_ci				    NULL);
2518c2ecf20Sopenharmony_ci	} else if (!curr_gw && next_gw) {
2528c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
2538c2ecf20Sopenharmony_ci			   "Adding route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n",
2548c2ecf20Sopenharmony_ci			   next_gw->orig_node->orig,
2558c2ecf20Sopenharmony_ci			   next_gw->bandwidth_down / 10,
2568c2ecf20Sopenharmony_ci			   next_gw->bandwidth_down % 10,
2578c2ecf20Sopenharmony_ci			   next_gw->bandwidth_up / 10,
2588c2ecf20Sopenharmony_ci			   next_gw->bandwidth_up % 10,
2598c2ecf20Sopenharmony_ci			   router_ifinfo->bat_iv.tq_avg);
2608c2ecf20Sopenharmony_ci		batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_ADD,
2618c2ecf20Sopenharmony_ci				    gw_addr);
2628c2ecf20Sopenharmony_ci	} else {
2638c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
2648c2ecf20Sopenharmony_ci			   "Changing route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n",
2658c2ecf20Sopenharmony_ci			   next_gw->orig_node->orig,
2668c2ecf20Sopenharmony_ci			   next_gw->bandwidth_down / 10,
2678c2ecf20Sopenharmony_ci			   next_gw->bandwidth_down % 10,
2688c2ecf20Sopenharmony_ci			   next_gw->bandwidth_up / 10,
2698c2ecf20Sopenharmony_ci			   next_gw->bandwidth_up % 10,
2708c2ecf20Sopenharmony_ci			   router_ifinfo->bat_iv.tq_avg);
2718c2ecf20Sopenharmony_ci		batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_CHANGE,
2728c2ecf20Sopenharmony_ci				    gw_addr);
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	batadv_gw_select(bat_priv, next_gw);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ciout:
2788c2ecf20Sopenharmony_ci	if (curr_gw)
2798c2ecf20Sopenharmony_ci		batadv_gw_node_put(curr_gw);
2808c2ecf20Sopenharmony_ci	if (next_gw)
2818c2ecf20Sopenharmony_ci		batadv_gw_node_put(next_gw);
2828c2ecf20Sopenharmony_ci	if (router)
2838c2ecf20Sopenharmony_ci		batadv_neigh_node_put(router);
2848c2ecf20Sopenharmony_ci	if (router_ifinfo)
2858c2ecf20Sopenharmony_ci		batadv_neigh_ifinfo_put(router_ifinfo);
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci/**
2898c2ecf20Sopenharmony_ci * batadv_gw_check_election() - Elect orig node as best gateway when eligible
2908c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
2918c2ecf20Sopenharmony_ci * @orig_node: orig node which is to be checked
2928c2ecf20Sopenharmony_ci */
2938c2ecf20Sopenharmony_civoid batadv_gw_check_election(struct batadv_priv *bat_priv,
2948c2ecf20Sopenharmony_ci			      struct batadv_orig_node *orig_node)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct batadv_orig_node *curr_gw_orig;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* abort immediately if the routing algorithm does not support gateway
2998c2ecf20Sopenharmony_ci	 * election
3008c2ecf20Sopenharmony_ci	 */
3018c2ecf20Sopenharmony_ci	if (!bat_priv->algo_ops->gw.is_eligible)
3028c2ecf20Sopenharmony_ci		return;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	curr_gw_orig = batadv_gw_get_selected_orig(bat_priv);
3058c2ecf20Sopenharmony_ci	if (!curr_gw_orig)
3068c2ecf20Sopenharmony_ci		goto reselect;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	/* this node already is the gateway */
3098c2ecf20Sopenharmony_ci	if (curr_gw_orig == orig_node)
3108c2ecf20Sopenharmony_ci		goto out;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (!bat_priv->algo_ops->gw.is_eligible(bat_priv, curr_gw_orig,
3138c2ecf20Sopenharmony_ci						orig_node))
3148c2ecf20Sopenharmony_ci		goto out;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cireselect:
3178c2ecf20Sopenharmony_ci	batadv_gw_reselect(bat_priv);
3188c2ecf20Sopenharmony_ciout:
3198c2ecf20Sopenharmony_ci	if (curr_gw_orig)
3208c2ecf20Sopenharmony_ci		batadv_orig_node_put(curr_gw_orig);
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci/**
3248c2ecf20Sopenharmony_ci * batadv_gw_node_add() - add gateway node to list of available gateways
3258c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
3268c2ecf20Sopenharmony_ci * @orig_node: originator announcing gateway capabilities
3278c2ecf20Sopenharmony_ci * @gateway: announced bandwidth information
3288c2ecf20Sopenharmony_ci *
3298c2ecf20Sopenharmony_ci * Has to be called with the appropriate locks being acquired
3308c2ecf20Sopenharmony_ci * (gw.list_lock).
3318c2ecf20Sopenharmony_ci */
3328c2ecf20Sopenharmony_cistatic void batadv_gw_node_add(struct batadv_priv *bat_priv,
3338c2ecf20Sopenharmony_ci			       struct batadv_orig_node *orig_node,
3348c2ecf20Sopenharmony_ci			       struct batadv_tvlv_gateway_data *gateway)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	struct batadv_gw_node *gw_node;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	lockdep_assert_held(&bat_priv->gw.list_lock);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (gateway->bandwidth_down == 0)
3418c2ecf20Sopenharmony_ci		return;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	gw_node = kzalloc(sizeof(*gw_node), GFP_ATOMIC);
3448c2ecf20Sopenharmony_ci	if (!gw_node)
3458c2ecf20Sopenharmony_ci		return;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	kref_init(&gw_node->refcount);
3488c2ecf20Sopenharmony_ci	INIT_HLIST_NODE(&gw_node->list);
3498c2ecf20Sopenharmony_ci	kref_get(&orig_node->refcount);
3508c2ecf20Sopenharmony_ci	gw_node->orig_node = orig_node;
3518c2ecf20Sopenharmony_ci	gw_node->bandwidth_down = ntohl(gateway->bandwidth_down);
3528c2ecf20Sopenharmony_ci	gw_node->bandwidth_up = ntohl(gateway->bandwidth_up);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	kref_get(&gw_node->refcount);
3558c2ecf20Sopenharmony_ci	hlist_add_head_rcu(&gw_node->list, &bat_priv->gw.gateway_list);
3568c2ecf20Sopenharmony_ci	bat_priv->gw.generation++;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
3598c2ecf20Sopenharmony_ci		   "Found new gateway %pM -> gw bandwidth: %u.%u/%u.%u MBit\n",
3608c2ecf20Sopenharmony_ci		   orig_node->orig,
3618c2ecf20Sopenharmony_ci		   ntohl(gateway->bandwidth_down) / 10,
3628c2ecf20Sopenharmony_ci		   ntohl(gateway->bandwidth_down) % 10,
3638c2ecf20Sopenharmony_ci		   ntohl(gateway->bandwidth_up) / 10,
3648c2ecf20Sopenharmony_ci		   ntohl(gateway->bandwidth_up) % 10);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	/* don't return reference to new gw_node */
3678c2ecf20Sopenharmony_ci	batadv_gw_node_put(gw_node);
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci/**
3718c2ecf20Sopenharmony_ci * batadv_gw_node_get() - retrieve gateway node from list of available gateways
3728c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
3738c2ecf20Sopenharmony_ci * @orig_node: originator announcing gateway capabilities
3748c2ecf20Sopenharmony_ci *
3758c2ecf20Sopenharmony_ci * Return: gateway node if found or NULL otherwise.
3768c2ecf20Sopenharmony_ci */
3778c2ecf20Sopenharmony_cistruct batadv_gw_node *batadv_gw_node_get(struct batadv_priv *bat_priv,
3788c2ecf20Sopenharmony_ci					  struct batadv_orig_node *orig_node)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct batadv_gw_node *gw_node_tmp, *gw_node = NULL;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	rcu_read_lock();
3838c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(gw_node_tmp, &bat_priv->gw.gateway_list,
3848c2ecf20Sopenharmony_ci				 list) {
3858c2ecf20Sopenharmony_ci		if (gw_node_tmp->orig_node != orig_node)
3868c2ecf20Sopenharmony_ci			continue;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		if (!kref_get_unless_zero(&gw_node_tmp->refcount))
3898c2ecf20Sopenharmony_ci			continue;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci		gw_node = gw_node_tmp;
3928c2ecf20Sopenharmony_ci		break;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci	rcu_read_unlock();
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	return gw_node;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci/**
4008c2ecf20Sopenharmony_ci * batadv_gw_node_update() - update list of available gateways with changed
4018c2ecf20Sopenharmony_ci *  bandwidth information
4028c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
4038c2ecf20Sopenharmony_ci * @orig_node: originator announcing gateway capabilities
4048c2ecf20Sopenharmony_ci * @gateway: announced bandwidth information
4058c2ecf20Sopenharmony_ci */
4068c2ecf20Sopenharmony_civoid batadv_gw_node_update(struct batadv_priv *bat_priv,
4078c2ecf20Sopenharmony_ci			   struct batadv_orig_node *orig_node,
4088c2ecf20Sopenharmony_ci			   struct batadv_tvlv_gateway_data *gateway)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	struct batadv_gw_node *gw_node, *curr_gw = NULL;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	spin_lock_bh(&bat_priv->gw.list_lock);
4138c2ecf20Sopenharmony_ci	gw_node = batadv_gw_node_get(bat_priv, orig_node);
4148c2ecf20Sopenharmony_ci	if (!gw_node) {
4158c2ecf20Sopenharmony_ci		batadv_gw_node_add(bat_priv, orig_node, gateway);
4168c2ecf20Sopenharmony_ci		spin_unlock_bh(&bat_priv->gw.list_lock);
4178c2ecf20Sopenharmony_ci		goto out;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci	spin_unlock_bh(&bat_priv->gw.list_lock);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	if (gw_node->bandwidth_down == ntohl(gateway->bandwidth_down) &&
4228c2ecf20Sopenharmony_ci	    gw_node->bandwidth_up == ntohl(gateway->bandwidth_up))
4238c2ecf20Sopenharmony_ci		goto out;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
4268c2ecf20Sopenharmony_ci		   "Gateway bandwidth of originator %pM changed from %u.%u/%u.%u MBit to %u.%u/%u.%u MBit\n",
4278c2ecf20Sopenharmony_ci		   orig_node->orig,
4288c2ecf20Sopenharmony_ci		   gw_node->bandwidth_down / 10,
4298c2ecf20Sopenharmony_ci		   gw_node->bandwidth_down % 10,
4308c2ecf20Sopenharmony_ci		   gw_node->bandwidth_up / 10,
4318c2ecf20Sopenharmony_ci		   gw_node->bandwidth_up % 10,
4328c2ecf20Sopenharmony_ci		   ntohl(gateway->bandwidth_down) / 10,
4338c2ecf20Sopenharmony_ci		   ntohl(gateway->bandwidth_down) % 10,
4348c2ecf20Sopenharmony_ci		   ntohl(gateway->bandwidth_up) / 10,
4358c2ecf20Sopenharmony_ci		   ntohl(gateway->bandwidth_up) % 10);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	gw_node->bandwidth_down = ntohl(gateway->bandwidth_down);
4388c2ecf20Sopenharmony_ci	gw_node->bandwidth_up = ntohl(gateway->bandwidth_up);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if (ntohl(gateway->bandwidth_down) == 0) {
4418c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
4428c2ecf20Sopenharmony_ci			   "Gateway %pM removed from gateway list\n",
4438c2ecf20Sopenharmony_ci			   orig_node->orig);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci		/* Note: We don't need a NULL check here, since curr_gw never
4468c2ecf20Sopenharmony_ci		 * gets dereferenced.
4478c2ecf20Sopenharmony_ci		 */
4488c2ecf20Sopenharmony_ci		spin_lock_bh(&bat_priv->gw.list_lock);
4498c2ecf20Sopenharmony_ci		if (!hlist_unhashed(&gw_node->list)) {
4508c2ecf20Sopenharmony_ci			hlist_del_init_rcu(&gw_node->list);
4518c2ecf20Sopenharmony_ci			batadv_gw_node_put(gw_node);
4528c2ecf20Sopenharmony_ci			bat_priv->gw.generation++;
4538c2ecf20Sopenharmony_ci		}
4548c2ecf20Sopenharmony_ci		spin_unlock_bh(&bat_priv->gw.list_lock);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci		curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
4578c2ecf20Sopenharmony_ci		if (gw_node == curr_gw)
4588c2ecf20Sopenharmony_ci			batadv_gw_reselect(bat_priv);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		if (curr_gw)
4618c2ecf20Sopenharmony_ci			batadv_gw_node_put(curr_gw);
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ciout:
4658c2ecf20Sopenharmony_ci	if (gw_node)
4668c2ecf20Sopenharmony_ci		batadv_gw_node_put(gw_node);
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci/**
4708c2ecf20Sopenharmony_ci * batadv_gw_node_delete() - Remove orig_node from gateway list
4718c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
4728c2ecf20Sopenharmony_ci * @orig_node: orig node which is currently in process of being removed
4738c2ecf20Sopenharmony_ci */
4748c2ecf20Sopenharmony_civoid batadv_gw_node_delete(struct batadv_priv *bat_priv,
4758c2ecf20Sopenharmony_ci			   struct batadv_orig_node *orig_node)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	struct batadv_tvlv_gateway_data gateway;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	gateway.bandwidth_down = 0;
4808c2ecf20Sopenharmony_ci	gateway.bandwidth_up = 0;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	batadv_gw_node_update(bat_priv, orig_node, &gateway);
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci/**
4868c2ecf20Sopenharmony_ci * batadv_gw_node_free() - Free gateway information from soft interface
4878c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
4888c2ecf20Sopenharmony_ci */
4898c2ecf20Sopenharmony_civoid batadv_gw_node_free(struct batadv_priv *bat_priv)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	struct batadv_gw_node *gw_node;
4928c2ecf20Sopenharmony_ci	struct hlist_node *node_tmp;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	spin_lock_bh(&bat_priv->gw.list_lock);
4958c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(gw_node, node_tmp,
4968c2ecf20Sopenharmony_ci				  &bat_priv->gw.gateway_list, list) {
4978c2ecf20Sopenharmony_ci		hlist_del_init_rcu(&gw_node->list);
4988c2ecf20Sopenharmony_ci		batadv_gw_node_put(gw_node);
4998c2ecf20Sopenharmony_ci		bat_priv->gw.generation++;
5008c2ecf20Sopenharmony_ci	}
5018c2ecf20Sopenharmony_ci	spin_unlock_bh(&bat_priv->gw.list_lock);
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci#ifdef CONFIG_BATMAN_ADV_DEBUGFS
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci/**
5078c2ecf20Sopenharmony_ci * batadv_gw_client_seq_print_text() - Print the gateway table in a seq file
5088c2ecf20Sopenharmony_ci * @seq: seq file to print on
5098c2ecf20Sopenharmony_ci * @offset: not used
5108c2ecf20Sopenharmony_ci *
5118c2ecf20Sopenharmony_ci * Return: always 0
5128c2ecf20Sopenharmony_ci */
5138c2ecf20Sopenharmony_ciint batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	struct net_device *net_dev = (struct net_device *)seq->private;
5168c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(net_dev);
5178c2ecf20Sopenharmony_ci	struct batadv_hard_iface *primary_if;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	primary_if = batadv_seq_print_text_primary_if_get(seq);
5208c2ecf20Sopenharmony_ci	if (!primary_if)
5218c2ecf20Sopenharmony_ci		return 0;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s %s)]\n",
5248c2ecf20Sopenharmony_ci		   BATADV_SOURCE_VERSION, primary_if->net_dev->name,
5258c2ecf20Sopenharmony_ci		   primary_if->net_dev->dev_addr, net_dev->name,
5268c2ecf20Sopenharmony_ci		   bat_priv->algo_ops->name);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	batadv_hardif_put(primary_if);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	if (!bat_priv->algo_ops->gw.print) {
5318c2ecf20Sopenharmony_ci		seq_puts(seq,
5328c2ecf20Sopenharmony_ci			 "No printing function for this routing protocol\n");
5338c2ecf20Sopenharmony_ci		return 0;
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	bat_priv->algo_ops->gw.print(bat_priv, seq);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	return 0;
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci#endif
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci/**
5438c2ecf20Sopenharmony_ci * batadv_gw_dump() - Dump gateways into a message
5448c2ecf20Sopenharmony_ci * @msg: Netlink message to dump into
5458c2ecf20Sopenharmony_ci * @cb: Control block containing additional options
5468c2ecf20Sopenharmony_ci *
5478c2ecf20Sopenharmony_ci * Return: Error code, or length of message
5488c2ecf20Sopenharmony_ci */
5498c2ecf20Sopenharmony_ciint batadv_gw_dump(struct sk_buff *msg, struct netlink_callback *cb)
5508c2ecf20Sopenharmony_ci{
5518c2ecf20Sopenharmony_ci	struct batadv_hard_iface *primary_if = NULL;
5528c2ecf20Sopenharmony_ci	struct net *net = sock_net(cb->skb->sk);
5538c2ecf20Sopenharmony_ci	struct net_device *soft_iface;
5548c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv;
5558c2ecf20Sopenharmony_ci	int ifindex;
5568c2ecf20Sopenharmony_ci	int ret;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	ifindex = batadv_netlink_get_ifindex(cb->nlh,
5598c2ecf20Sopenharmony_ci					     BATADV_ATTR_MESH_IFINDEX);
5608c2ecf20Sopenharmony_ci	if (!ifindex)
5618c2ecf20Sopenharmony_ci		return -EINVAL;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	soft_iface = dev_get_by_index(net, ifindex);
5648c2ecf20Sopenharmony_ci	if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
5658c2ecf20Sopenharmony_ci		ret = -ENODEV;
5668c2ecf20Sopenharmony_ci		goto out;
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	bat_priv = netdev_priv(soft_iface);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	primary_if = batadv_primary_if_get_selected(bat_priv);
5728c2ecf20Sopenharmony_ci	if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
5738c2ecf20Sopenharmony_ci		ret = -ENOENT;
5748c2ecf20Sopenharmony_ci		goto out;
5758c2ecf20Sopenharmony_ci	}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	if (!bat_priv->algo_ops->gw.dump) {
5788c2ecf20Sopenharmony_ci		ret = -EOPNOTSUPP;
5798c2ecf20Sopenharmony_ci		goto out;
5808c2ecf20Sopenharmony_ci	}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	bat_priv->algo_ops->gw.dump(msg, cb, bat_priv);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	ret = msg->len;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ciout:
5878c2ecf20Sopenharmony_ci	if (primary_if)
5888c2ecf20Sopenharmony_ci		batadv_hardif_put(primary_if);
5898c2ecf20Sopenharmony_ci	if (soft_iface)
5908c2ecf20Sopenharmony_ci		dev_put(soft_iface);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	return ret;
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci/**
5968c2ecf20Sopenharmony_ci * batadv_gw_dhcp_recipient_get() - check if a packet is a DHCP message
5978c2ecf20Sopenharmony_ci * @skb: the packet to check
5988c2ecf20Sopenharmony_ci * @header_len: a pointer to the batman-adv header size
5998c2ecf20Sopenharmony_ci * @chaddr: buffer where the client address will be stored. Valid
6008c2ecf20Sopenharmony_ci *  only if the function returns BATADV_DHCP_TO_CLIENT
6018c2ecf20Sopenharmony_ci *
6028c2ecf20Sopenharmony_ci * This function may re-allocate the data buffer of the skb passed as argument.
6038c2ecf20Sopenharmony_ci *
6048c2ecf20Sopenharmony_ci * Return:
6058c2ecf20Sopenharmony_ci * - BATADV_DHCP_NO if the packet is not a dhcp message or if there was an error
6068c2ecf20Sopenharmony_ci *   while parsing it
6078c2ecf20Sopenharmony_ci * - BATADV_DHCP_TO_SERVER if this is a message going to the DHCP server
6088c2ecf20Sopenharmony_ci * - BATADV_DHCP_TO_CLIENT if this is a message going to a DHCP client
6098c2ecf20Sopenharmony_ci */
6108c2ecf20Sopenharmony_cienum batadv_dhcp_recipient
6118c2ecf20Sopenharmony_cibatadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len,
6128c2ecf20Sopenharmony_ci			     u8 *chaddr)
6138c2ecf20Sopenharmony_ci{
6148c2ecf20Sopenharmony_ci	enum batadv_dhcp_recipient ret = BATADV_DHCP_NO;
6158c2ecf20Sopenharmony_ci	struct ethhdr *ethhdr;
6168c2ecf20Sopenharmony_ci	struct iphdr *iphdr;
6178c2ecf20Sopenharmony_ci	struct ipv6hdr *ipv6hdr;
6188c2ecf20Sopenharmony_ci	struct udphdr *udphdr;
6198c2ecf20Sopenharmony_ci	struct vlan_ethhdr *vhdr;
6208c2ecf20Sopenharmony_ci	int chaddr_offset;
6218c2ecf20Sopenharmony_ci	__be16 proto;
6228c2ecf20Sopenharmony_ci	u8 *p;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	/* check for ethernet header */
6258c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, *header_len + ETH_HLEN))
6268c2ecf20Sopenharmony_ci		return BATADV_DHCP_NO;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	ethhdr = eth_hdr(skb);
6298c2ecf20Sopenharmony_ci	proto = ethhdr->h_proto;
6308c2ecf20Sopenharmony_ci	*header_len += ETH_HLEN;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	/* check for initial vlan header */
6338c2ecf20Sopenharmony_ci	if (proto == htons(ETH_P_8021Q)) {
6348c2ecf20Sopenharmony_ci		if (!pskb_may_pull(skb, *header_len + VLAN_HLEN))
6358c2ecf20Sopenharmony_ci			return BATADV_DHCP_NO;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci		vhdr = vlan_eth_hdr(skb);
6388c2ecf20Sopenharmony_ci		proto = vhdr->h_vlan_encapsulated_proto;
6398c2ecf20Sopenharmony_ci		*header_len += VLAN_HLEN;
6408c2ecf20Sopenharmony_ci	}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	/* check for ip header */
6438c2ecf20Sopenharmony_ci	switch (proto) {
6448c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
6458c2ecf20Sopenharmony_ci		if (!pskb_may_pull(skb, *header_len + sizeof(*iphdr)))
6468c2ecf20Sopenharmony_ci			return BATADV_DHCP_NO;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci		iphdr = (struct iphdr *)(skb->data + *header_len);
6498c2ecf20Sopenharmony_ci		*header_len += iphdr->ihl * 4;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci		/* check for udp header */
6528c2ecf20Sopenharmony_ci		if (iphdr->protocol != IPPROTO_UDP)
6538c2ecf20Sopenharmony_ci			return BATADV_DHCP_NO;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci		break;
6568c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
6578c2ecf20Sopenharmony_ci		if (!pskb_may_pull(skb, *header_len + sizeof(*ipv6hdr)))
6588c2ecf20Sopenharmony_ci			return BATADV_DHCP_NO;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci		ipv6hdr = (struct ipv6hdr *)(skb->data + *header_len);
6618c2ecf20Sopenharmony_ci		*header_len += sizeof(*ipv6hdr);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci		/* check for udp header */
6648c2ecf20Sopenharmony_ci		if (ipv6hdr->nexthdr != IPPROTO_UDP)
6658c2ecf20Sopenharmony_ci			return BATADV_DHCP_NO;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci		break;
6688c2ecf20Sopenharmony_ci	default:
6698c2ecf20Sopenharmony_ci		return BATADV_DHCP_NO;
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, *header_len + sizeof(*udphdr)))
6738c2ecf20Sopenharmony_ci		return BATADV_DHCP_NO;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	udphdr = (struct udphdr *)(skb->data + *header_len);
6768c2ecf20Sopenharmony_ci	*header_len += sizeof(*udphdr);
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	/* check for bootp port */
6798c2ecf20Sopenharmony_ci	switch (proto) {
6808c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
6818c2ecf20Sopenharmony_ci		if (udphdr->dest == htons(67))
6828c2ecf20Sopenharmony_ci			ret = BATADV_DHCP_TO_SERVER;
6838c2ecf20Sopenharmony_ci		else if (udphdr->source == htons(67))
6848c2ecf20Sopenharmony_ci			ret = BATADV_DHCP_TO_CLIENT;
6858c2ecf20Sopenharmony_ci		break;
6868c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
6878c2ecf20Sopenharmony_ci		if (udphdr->dest == htons(547))
6888c2ecf20Sopenharmony_ci			ret = BATADV_DHCP_TO_SERVER;
6898c2ecf20Sopenharmony_ci		else if (udphdr->source == htons(547))
6908c2ecf20Sopenharmony_ci			ret = BATADV_DHCP_TO_CLIENT;
6918c2ecf20Sopenharmony_ci		break;
6928c2ecf20Sopenharmony_ci	}
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	chaddr_offset = *header_len + BATADV_DHCP_CHADDR_OFFSET;
6958c2ecf20Sopenharmony_ci	/* store the client address if the message is going to a client */
6968c2ecf20Sopenharmony_ci	if (ret == BATADV_DHCP_TO_CLIENT) {
6978c2ecf20Sopenharmony_ci		if (!pskb_may_pull(skb, chaddr_offset + ETH_ALEN))
6988c2ecf20Sopenharmony_ci			return BATADV_DHCP_NO;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci		/* check if the DHCP packet carries an Ethernet DHCP */
7018c2ecf20Sopenharmony_ci		p = skb->data + *header_len + BATADV_DHCP_HTYPE_OFFSET;
7028c2ecf20Sopenharmony_ci		if (*p != BATADV_DHCP_HTYPE_ETHERNET)
7038c2ecf20Sopenharmony_ci			return BATADV_DHCP_NO;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci		/* check if the DHCP packet carries a valid Ethernet address */
7068c2ecf20Sopenharmony_ci		p = skb->data + *header_len + BATADV_DHCP_HLEN_OFFSET;
7078c2ecf20Sopenharmony_ci		if (*p != ETH_ALEN)
7088c2ecf20Sopenharmony_ci			return BATADV_DHCP_NO;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci		ether_addr_copy(chaddr, skb->data + chaddr_offset);
7118c2ecf20Sopenharmony_ci	}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	return ret;
7148c2ecf20Sopenharmony_ci}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci/**
7178c2ecf20Sopenharmony_ci * batadv_gw_out_of_range() - check if the dhcp request destination is the best
7188c2ecf20Sopenharmony_ci *  gateway
7198c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
7208c2ecf20Sopenharmony_ci * @skb: the outgoing packet
7218c2ecf20Sopenharmony_ci *
7228c2ecf20Sopenharmony_ci * Check if the skb is a DHCP request and if it is sent to the current best GW
7238c2ecf20Sopenharmony_ci * server. Due to topology changes it may be the case that the GW server
7248c2ecf20Sopenharmony_ci * previously selected is not the best one anymore.
7258c2ecf20Sopenharmony_ci *
7268c2ecf20Sopenharmony_ci * This call might reallocate skb data.
7278c2ecf20Sopenharmony_ci * Must be invoked only when the DHCP packet is going TO a DHCP SERVER.
7288c2ecf20Sopenharmony_ci *
7298c2ecf20Sopenharmony_ci * Return: true if the packet destination is unicast and it is not the best gw,
7308c2ecf20Sopenharmony_ci * false otherwise.
7318c2ecf20Sopenharmony_ci */
7328c2ecf20Sopenharmony_cibool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
7338c2ecf20Sopenharmony_ci			    struct sk_buff *skb)
7348c2ecf20Sopenharmony_ci{
7358c2ecf20Sopenharmony_ci	struct batadv_neigh_node *neigh_curr = NULL;
7368c2ecf20Sopenharmony_ci	struct batadv_neigh_node *neigh_old = NULL;
7378c2ecf20Sopenharmony_ci	struct batadv_orig_node *orig_dst_node = NULL;
7388c2ecf20Sopenharmony_ci	struct batadv_gw_node *gw_node = NULL;
7398c2ecf20Sopenharmony_ci	struct batadv_gw_node *curr_gw = NULL;
7408c2ecf20Sopenharmony_ci	struct batadv_neigh_ifinfo *curr_ifinfo, *old_ifinfo;
7418c2ecf20Sopenharmony_ci	struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
7428c2ecf20Sopenharmony_ci	bool out_of_range = false;
7438c2ecf20Sopenharmony_ci	u8 curr_tq_avg;
7448c2ecf20Sopenharmony_ci	unsigned short vid;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	vid = batadv_get_vid(skb, 0);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	if (is_multicast_ether_addr(ethhdr->h_dest))
7498c2ecf20Sopenharmony_ci		goto out;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	orig_dst_node = batadv_transtable_search(bat_priv, ethhdr->h_source,
7528c2ecf20Sopenharmony_ci						 ethhdr->h_dest, vid);
7538c2ecf20Sopenharmony_ci	if (!orig_dst_node)
7548c2ecf20Sopenharmony_ci		goto out;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	gw_node = batadv_gw_node_get(bat_priv, orig_dst_node);
7578c2ecf20Sopenharmony_ci	if (!gw_node)
7588c2ecf20Sopenharmony_ci		goto out;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	switch (atomic_read(&bat_priv->gw.mode)) {
7618c2ecf20Sopenharmony_ci	case BATADV_GW_MODE_SERVER:
7628c2ecf20Sopenharmony_ci		/* If we are a GW then we are our best GW. We can artificially
7638c2ecf20Sopenharmony_ci		 * set the tq towards ourself as the maximum value
7648c2ecf20Sopenharmony_ci		 */
7658c2ecf20Sopenharmony_ci		curr_tq_avg = BATADV_TQ_MAX_VALUE;
7668c2ecf20Sopenharmony_ci		break;
7678c2ecf20Sopenharmony_ci	case BATADV_GW_MODE_CLIENT:
7688c2ecf20Sopenharmony_ci		curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
7698c2ecf20Sopenharmony_ci		if (!curr_gw)
7708c2ecf20Sopenharmony_ci			goto out;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci		/* packet is going to our gateway */
7738c2ecf20Sopenharmony_ci		if (curr_gw->orig_node == orig_dst_node)
7748c2ecf20Sopenharmony_ci			goto out;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci		/* If the dhcp packet has been sent to a different gw,
7778c2ecf20Sopenharmony_ci		 * we have to evaluate whether the old gw is still
7788c2ecf20Sopenharmony_ci		 * reliable enough
7798c2ecf20Sopenharmony_ci		 */
7808c2ecf20Sopenharmony_ci		neigh_curr = batadv_find_router(bat_priv, curr_gw->orig_node,
7818c2ecf20Sopenharmony_ci						NULL);
7828c2ecf20Sopenharmony_ci		if (!neigh_curr)
7838c2ecf20Sopenharmony_ci			goto out;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci		curr_ifinfo = batadv_neigh_ifinfo_get(neigh_curr,
7868c2ecf20Sopenharmony_ci						      BATADV_IF_DEFAULT);
7878c2ecf20Sopenharmony_ci		if (!curr_ifinfo)
7888c2ecf20Sopenharmony_ci			goto out;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci		curr_tq_avg = curr_ifinfo->bat_iv.tq_avg;
7918c2ecf20Sopenharmony_ci		batadv_neigh_ifinfo_put(curr_ifinfo);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci		break;
7948c2ecf20Sopenharmony_ci	case BATADV_GW_MODE_OFF:
7958c2ecf20Sopenharmony_ci	default:
7968c2ecf20Sopenharmony_ci		goto out;
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	neigh_old = batadv_find_router(bat_priv, orig_dst_node, NULL);
8008c2ecf20Sopenharmony_ci	if (!neigh_old)
8018c2ecf20Sopenharmony_ci		goto out;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	old_ifinfo = batadv_neigh_ifinfo_get(neigh_old, BATADV_IF_DEFAULT);
8048c2ecf20Sopenharmony_ci	if (!old_ifinfo)
8058c2ecf20Sopenharmony_ci		goto out;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	if ((curr_tq_avg - old_ifinfo->bat_iv.tq_avg) > BATADV_GW_THRESHOLD)
8088c2ecf20Sopenharmony_ci		out_of_range = true;
8098c2ecf20Sopenharmony_ci	batadv_neigh_ifinfo_put(old_ifinfo);
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ciout:
8128c2ecf20Sopenharmony_ci	if (orig_dst_node)
8138c2ecf20Sopenharmony_ci		batadv_orig_node_put(orig_dst_node);
8148c2ecf20Sopenharmony_ci	if (curr_gw)
8158c2ecf20Sopenharmony_ci		batadv_gw_node_put(curr_gw);
8168c2ecf20Sopenharmony_ci	if (gw_node)
8178c2ecf20Sopenharmony_ci		batadv_gw_node_put(gw_node);
8188c2ecf20Sopenharmony_ci	if (neigh_old)
8198c2ecf20Sopenharmony_ci		batadv_neigh_node_put(neigh_old);
8208c2ecf20Sopenharmony_ci	if (neigh_curr)
8218c2ecf20Sopenharmony_ci		batadv_neigh_node_put(neigh_curr);
8228c2ecf20Sopenharmony_ci	return out_of_range;
8238c2ecf20Sopenharmony_ci}
824