cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

gateway_client.c (21207B)


      1// SPDX-License-Identifier: GPL-2.0
      2/* Copyright (C) B.A.T.M.A.N. contributors:
      3 *
      4 * Marek Lindner
      5 */
      6
      7#include "gateway_client.h"
      8#include "main.h"
      9
     10#include <linux/atomic.h>
     11#include <linux/byteorder/generic.h>
     12#include <linux/container_of.h>
     13#include <linux/errno.h>
     14#include <linux/etherdevice.h>
     15#include <linux/gfp.h>
     16#include <linux/if_ether.h>
     17#include <linux/if_vlan.h>
     18#include <linux/in.h>
     19#include <linux/ip.h>
     20#include <linux/ipv6.h>
     21#include <linux/kernel.h>
     22#include <linux/kref.h>
     23#include <linux/list.h>
     24#include <linux/lockdep.h>
     25#include <linux/netdevice.h>
     26#include <linux/netlink.h>
     27#include <linux/rculist.h>
     28#include <linux/rcupdate.h>
     29#include <linux/skbuff.h>
     30#include <linux/slab.h>
     31#include <linux/spinlock.h>
     32#include <linux/stddef.h>
     33#include <linux/udp.h>
     34#include <net/sock.h>
     35#include <uapi/linux/batadv_packet.h>
     36#include <uapi/linux/batman_adv.h>
     37
     38#include "hard-interface.h"
     39#include "log.h"
     40#include "netlink.h"
     41#include "originator.h"
     42#include "routing.h"
     43#include "soft-interface.h"
     44#include "translation-table.h"
     45
     46/* These are the offsets of the "hw type" and "hw address length" in the dhcp
     47 * packet starting at the beginning of the dhcp header
     48 */
     49#define BATADV_DHCP_HTYPE_OFFSET	1
     50#define BATADV_DHCP_HLEN_OFFSET		2
     51/* Value of htype representing Ethernet */
     52#define BATADV_DHCP_HTYPE_ETHERNET	0x01
     53/* This is the offset of the "chaddr" field in the dhcp packet starting at the
     54 * beginning of the dhcp header
     55 */
     56#define BATADV_DHCP_CHADDR_OFFSET	28
     57
     58/**
     59 * batadv_gw_node_release() - release gw_node from lists and queue for free
     60 *  after rcu grace period
     61 * @ref: kref pointer of the gw_node
     62 */
     63void batadv_gw_node_release(struct kref *ref)
     64{
     65	struct batadv_gw_node *gw_node;
     66
     67	gw_node = container_of(ref, struct batadv_gw_node, refcount);
     68
     69	batadv_orig_node_put(gw_node->orig_node);
     70	kfree_rcu(gw_node, rcu);
     71}
     72
     73/**
     74 * batadv_gw_get_selected_gw_node() - Get currently selected gateway
     75 * @bat_priv: the bat priv with all the soft interface information
     76 *
     77 * Return: selected gateway (with increased refcnt), NULL on errors
     78 */
     79struct batadv_gw_node *
     80batadv_gw_get_selected_gw_node(struct batadv_priv *bat_priv)
     81{
     82	struct batadv_gw_node *gw_node;
     83
     84	rcu_read_lock();
     85	gw_node = rcu_dereference(bat_priv->gw.curr_gw);
     86	if (!gw_node)
     87		goto out;
     88
     89	if (!kref_get_unless_zero(&gw_node->refcount))
     90		gw_node = NULL;
     91
     92out:
     93	rcu_read_unlock();
     94	return gw_node;
     95}
     96
     97/**
     98 * batadv_gw_get_selected_orig() - Get originator of currently selected gateway
     99 * @bat_priv: the bat priv with all the soft interface information
    100 *
    101 * Return: orig_node of selected gateway (with increased refcnt), NULL on errors
    102 */
    103struct batadv_orig_node *
    104batadv_gw_get_selected_orig(struct batadv_priv *bat_priv)
    105{
    106	struct batadv_gw_node *gw_node;
    107	struct batadv_orig_node *orig_node = NULL;
    108
    109	gw_node = batadv_gw_get_selected_gw_node(bat_priv);
    110	if (!gw_node)
    111		goto out;
    112
    113	rcu_read_lock();
    114	orig_node = gw_node->orig_node;
    115	if (!orig_node)
    116		goto unlock;
    117
    118	if (!kref_get_unless_zero(&orig_node->refcount))
    119		orig_node = NULL;
    120
    121unlock:
    122	rcu_read_unlock();
    123out:
    124	batadv_gw_node_put(gw_node);
    125	return orig_node;
    126}
    127
    128static void batadv_gw_select(struct batadv_priv *bat_priv,
    129			     struct batadv_gw_node *new_gw_node)
    130{
    131	struct batadv_gw_node *curr_gw_node;
    132
    133	spin_lock_bh(&bat_priv->gw.list_lock);
    134
    135	if (new_gw_node)
    136		kref_get(&new_gw_node->refcount);
    137
    138	curr_gw_node = rcu_replace_pointer(bat_priv->gw.curr_gw, new_gw_node,
    139					   true);
    140
    141	batadv_gw_node_put(curr_gw_node);
    142
    143	spin_unlock_bh(&bat_priv->gw.list_lock);
    144}
    145
    146/**
    147 * batadv_gw_reselect() - force a gateway reselection
    148 * @bat_priv: the bat priv with all the soft interface information
    149 *
    150 * Set a flag to remind the GW component to perform a new gateway reselection.
    151 * However this function does not ensure that the current gateway is going to be
    152 * deselected. The reselection mechanism may elect the same gateway once again.
    153 *
    154 * This means that invoking batadv_gw_reselect() does not guarantee a gateway
    155 * change and therefore a uevent is not necessarily expected.
    156 */
    157void batadv_gw_reselect(struct batadv_priv *bat_priv)
    158{
    159	atomic_set(&bat_priv->gw.reselect, 1);
    160}
    161
    162/**
    163 * batadv_gw_check_client_stop() - check if client mode has been switched off
    164 * @bat_priv: the bat priv with all the soft interface information
    165 *
    166 * This function assumes the caller has checked that the gw state *is actually
    167 * changing*. This function is not supposed to be called when there is no state
    168 * change.
    169 */
    170void batadv_gw_check_client_stop(struct batadv_priv *bat_priv)
    171{
    172	struct batadv_gw_node *curr_gw;
    173
    174	if (atomic_read(&bat_priv->gw.mode) != BATADV_GW_MODE_CLIENT)
    175		return;
    176
    177	curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
    178	if (!curr_gw)
    179		return;
    180
    181	/* deselect the current gateway so that next time that client mode is
    182	 * enabled a proper GW_ADD event can be sent
    183	 */
    184	batadv_gw_select(bat_priv, NULL);
    185
    186	/* if batman-adv is switching the gw client mode off and a gateway was
    187	 * already selected, send a DEL uevent
    188	 */
    189	batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_DEL, NULL);
    190
    191	batadv_gw_node_put(curr_gw);
    192}
    193
    194/**
    195 * batadv_gw_election() - Elect the best gateway
    196 * @bat_priv: the bat priv with all the soft interface information
    197 */
    198void batadv_gw_election(struct batadv_priv *bat_priv)
    199{
    200	struct batadv_gw_node *curr_gw = NULL;
    201	struct batadv_gw_node *next_gw = NULL;
    202	struct batadv_neigh_node *router = NULL;
    203	struct batadv_neigh_ifinfo *router_ifinfo = NULL;
    204	char gw_addr[18] = { '\0' };
    205
    206	if (atomic_read(&bat_priv->gw.mode) != BATADV_GW_MODE_CLIENT)
    207		goto out;
    208
    209	if (!bat_priv->algo_ops->gw.get_best_gw_node)
    210		goto out;
    211
    212	curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
    213
    214	if (!batadv_atomic_dec_not_zero(&bat_priv->gw.reselect) && curr_gw)
    215		goto out;
    216
    217	/* if gw.reselect is set to 1 it means that a previous call to
    218	 * gw.is_eligible() said that we have a new best GW, therefore it can
    219	 * now be picked from the list and selected
    220	 */
    221	next_gw = bat_priv->algo_ops->gw.get_best_gw_node(bat_priv);
    222
    223	if (curr_gw == next_gw)
    224		goto out;
    225
    226	if (next_gw) {
    227		sprintf(gw_addr, "%pM", next_gw->orig_node->orig);
    228
    229		router = batadv_orig_router_get(next_gw->orig_node,
    230						BATADV_IF_DEFAULT);
    231		if (!router) {
    232			batadv_gw_reselect(bat_priv);
    233			goto out;
    234		}
    235
    236		router_ifinfo = batadv_neigh_ifinfo_get(router,
    237							BATADV_IF_DEFAULT);
    238		if (!router_ifinfo) {
    239			batadv_gw_reselect(bat_priv);
    240			goto out;
    241		}
    242	}
    243
    244	if (curr_gw && !next_gw) {
    245		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
    246			   "Removing selected gateway - no gateway in range\n");
    247		batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_DEL,
    248				    NULL);
    249	} else if (!curr_gw && next_gw) {
    250		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
    251			   "Adding route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n",
    252			   next_gw->orig_node->orig,
    253			   next_gw->bandwidth_down / 10,
    254			   next_gw->bandwidth_down % 10,
    255			   next_gw->bandwidth_up / 10,
    256			   next_gw->bandwidth_up % 10,
    257			   router_ifinfo->bat_iv.tq_avg);
    258		batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_ADD,
    259				    gw_addr);
    260	} else {
    261		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
    262			   "Changing route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n",
    263			   next_gw->orig_node->orig,
    264			   next_gw->bandwidth_down / 10,
    265			   next_gw->bandwidth_down % 10,
    266			   next_gw->bandwidth_up / 10,
    267			   next_gw->bandwidth_up % 10,
    268			   router_ifinfo->bat_iv.tq_avg);
    269		batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_CHANGE,
    270				    gw_addr);
    271	}
    272
    273	batadv_gw_select(bat_priv, next_gw);
    274
    275out:
    276	batadv_gw_node_put(curr_gw);
    277	batadv_gw_node_put(next_gw);
    278	batadv_neigh_node_put(router);
    279	batadv_neigh_ifinfo_put(router_ifinfo);
    280}
    281
    282/**
    283 * batadv_gw_check_election() - Elect orig node as best gateway when eligible
    284 * @bat_priv: the bat priv with all the soft interface information
    285 * @orig_node: orig node which is to be checked
    286 */
    287void batadv_gw_check_election(struct batadv_priv *bat_priv,
    288			      struct batadv_orig_node *orig_node)
    289{
    290	struct batadv_orig_node *curr_gw_orig;
    291
    292	/* abort immediately if the routing algorithm does not support gateway
    293	 * election
    294	 */
    295	if (!bat_priv->algo_ops->gw.is_eligible)
    296		return;
    297
    298	curr_gw_orig = batadv_gw_get_selected_orig(bat_priv);
    299	if (!curr_gw_orig)
    300		goto reselect;
    301
    302	/* this node already is the gateway */
    303	if (curr_gw_orig == orig_node)
    304		goto out;
    305
    306	if (!bat_priv->algo_ops->gw.is_eligible(bat_priv, curr_gw_orig,
    307						orig_node))
    308		goto out;
    309
    310reselect:
    311	batadv_gw_reselect(bat_priv);
    312out:
    313	batadv_orig_node_put(curr_gw_orig);
    314}
    315
    316/**
    317 * batadv_gw_node_add() - add gateway node to list of available gateways
    318 * @bat_priv: the bat priv with all the soft interface information
    319 * @orig_node: originator announcing gateway capabilities
    320 * @gateway: announced bandwidth information
    321 *
    322 * Has to be called with the appropriate locks being acquired
    323 * (gw.list_lock).
    324 */
    325static void batadv_gw_node_add(struct batadv_priv *bat_priv,
    326			       struct batadv_orig_node *orig_node,
    327			       struct batadv_tvlv_gateway_data *gateway)
    328{
    329	struct batadv_gw_node *gw_node;
    330
    331	lockdep_assert_held(&bat_priv->gw.list_lock);
    332
    333	if (gateway->bandwidth_down == 0)
    334		return;
    335
    336	gw_node = kzalloc(sizeof(*gw_node), GFP_ATOMIC);
    337	if (!gw_node)
    338		return;
    339
    340	kref_init(&gw_node->refcount);
    341	INIT_HLIST_NODE(&gw_node->list);
    342	kref_get(&orig_node->refcount);
    343	gw_node->orig_node = orig_node;
    344	gw_node->bandwidth_down = ntohl(gateway->bandwidth_down);
    345	gw_node->bandwidth_up = ntohl(gateway->bandwidth_up);
    346
    347	kref_get(&gw_node->refcount);
    348	hlist_add_head_rcu(&gw_node->list, &bat_priv->gw.gateway_list);
    349	bat_priv->gw.generation++;
    350
    351	batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
    352		   "Found new gateway %pM -> gw bandwidth: %u.%u/%u.%u MBit\n",
    353		   orig_node->orig,
    354		   ntohl(gateway->bandwidth_down) / 10,
    355		   ntohl(gateway->bandwidth_down) % 10,
    356		   ntohl(gateway->bandwidth_up) / 10,
    357		   ntohl(gateway->bandwidth_up) % 10);
    358
    359	/* don't return reference to new gw_node */
    360	batadv_gw_node_put(gw_node);
    361}
    362
    363/**
    364 * batadv_gw_node_get() - retrieve gateway node from list of available gateways
    365 * @bat_priv: the bat priv with all the soft interface information
    366 * @orig_node: originator announcing gateway capabilities
    367 *
    368 * Return: gateway node if found or NULL otherwise.
    369 */
    370struct batadv_gw_node *batadv_gw_node_get(struct batadv_priv *bat_priv,
    371					  struct batadv_orig_node *orig_node)
    372{
    373	struct batadv_gw_node *gw_node_tmp, *gw_node = NULL;
    374
    375	rcu_read_lock();
    376	hlist_for_each_entry_rcu(gw_node_tmp, &bat_priv->gw.gateway_list,
    377				 list) {
    378		if (gw_node_tmp->orig_node != orig_node)
    379			continue;
    380
    381		if (!kref_get_unless_zero(&gw_node_tmp->refcount))
    382			continue;
    383
    384		gw_node = gw_node_tmp;
    385		break;
    386	}
    387	rcu_read_unlock();
    388
    389	return gw_node;
    390}
    391
    392/**
    393 * batadv_gw_node_update() - update list of available gateways with changed
    394 *  bandwidth information
    395 * @bat_priv: the bat priv with all the soft interface information
    396 * @orig_node: originator announcing gateway capabilities
    397 * @gateway: announced bandwidth information
    398 */
    399void batadv_gw_node_update(struct batadv_priv *bat_priv,
    400			   struct batadv_orig_node *orig_node,
    401			   struct batadv_tvlv_gateway_data *gateway)
    402{
    403	struct batadv_gw_node *gw_node, *curr_gw = NULL;
    404
    405	spin_lock_bh(&bat_priv->gw.list_lock);
    406	gw_node = batadv_gw_node_get(bat_priv, orig_node);
    407	if (!gw_node) {
    408		batadv_gw_node_add(bat_priv, orig_node, gateway);
    409		spin_unlock_bh(&bat_priv->gw.list_lock);
    410		goto out;
    411	}
    412	spin_unlock_bh(&bat_priv->gw.list_lock);
    413
    414	if (gw_node->bandwidth_down == ntohl(gateway->bandwidth_down) &&
    415	    gw_node->bandwidth_up == ntohl(gateway->bandwidth_up))
    416		goto out;
    417
    418	batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
    419		   "Gateway bandwidth of originator %pM changed from %u.%u/%u.%u MBit to %u.%u/%u.%u MBit\n",
    420		   orig_node->orig,
    421		   gw_node->bandwidth_down / 10,
    422		   gw_node->bandwidth_down % 10,
    423		   gw_node->bandwidth_up / 10,
    424		   gw_node->bandwidth_up % 10,
    425		   ntohl(gateway->bandwidth_down) / 10,
    426		   ntohl(gateway->bandwidth_down) % 10,
    427		   ntohl(gateway->bandwidth_up) / 10,
    428		   ntohl(gateway->bandwidth_up) % 10);
    429
    430	gw_node->bandwidth_down = ntohl(gateway->bandwidth_down);
    431	gw_node->bandwidth_up = ntohl(gateway->bandwidth_up);
    432
    433	if (ntohl(gateway->bandwidth_down) == 0) {
    434		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
    435			   "Gateway %pM removed from gateway list\n",
    436			   orig_node->orig);
    437
    438		/* Note: We don't need a NULL check here, since curr_gw never
    439		 * gets dereferenced.
    440		 */
    441		spin_lock_bh(&bat_priv->gw.list_lock);
    442		if (!hlist_unhashed(&gw_node->list)) {
    443			hlist_del_init_rcu(&gw_node->list);
    444			batadv_gw_node_put(gw_node);
    445			bat_priv->gw.generation++;
    446		}
    447		spin_unlock_bh(&bat_priv->gw.list_lock);
    448
    449		curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
    450		if (gw_node == curr_gw)
    451			batadv_gw_reselect(bat_priv);
    452
    453		batadv_gw_node_put(curr_gw);
    454	}
    455
    456out:
    457	batadv_gw_node_put(gw_node);
    458}
    459
    460/**
    461 * batadv_gw_node_delete() - Remove orig_node from gateway list
    462 * @bat_priv: the bat priv with all the soft interface information
    463 * @orig_node: orig node which is currently in process of being removed
    464 */
    465void batadv_gw_node_delete(struct batadv_priv *bat_priv,
    466			   struct batadv_orig_node *orig_node)
    467{
    468	struct batadv_tvlv_gateway_data gateway;
    469
    470	gateway.bandwidth_down = 0;
    471	gateway.bandwidth_up = 0;
    472
    473	batadv_gw_node_update(bat_priv, orig_node, &gateway);
    474}
    475
    476/**
    477 * batadv_gw_node_free() - Free gateway information from soft interface
    478 * @bat_priv: the bat priv with all the soft interface information
    479 */
    480void batadv_gw_node_free(struct batadv_priv *bat_priv)
    481{
    482	struct batadv_gw_node *gw_node;
    483	struct hlist_node *node_tmp;
    484
    485	spin_lock_bh(&bat_priv->gw.list_lock);
    486	hlist_for_each_entry_safe(gw_node, node_tmp,
    487				  &bat_priv->gw.gateway_list, list) {
    488		hlist_del_init_rcu(&gw_node->list);
    489		batadv_gw_node_put(gw_node);
    490		bat_priv->gw.generation++;
    491	}
    492	spin_unlock_bh(&bat_priv->gw.list_lock);
    493}
    494
    495/**
    496 * batadv_gw_dump() - Dump gateways into a message
    497 * @msg: Netlink message to dump into
    498 * @cb: Control block containing additional options
    499 *
    500 * Return: Error code, or length of message
    501 */
    502int batadv_gw_dump(struct sk_buff *msg, struct netlink_callback *cb)
    503{
    504	struct batadv_hard_iface *primary_if = NULL;
    505	struct net *net = sock_net(cb->skb->sk);
    506	struct net_device *soft_iface;
    507	struct batadv_priv *bat_priv;
    508	int ifindex;
    509	int ret;
    510
    511	ifindex = batadv_netlink_get_ifindex(cb->nlh,
    512					     BATADV_ATTR_MESH_IFINDEX);
    513	if (!ifindex)
    514		return -EINVAL;
    515
    516	soft_iface = dev_get_by_index(net, ifindex);
    517	if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
    518		ret = -ENODEV;
    519		goto out;
    520	}
    521
    522	bat_priv = netdev_priv(soft_iface);
    523
    524	primary_if = batadv_primary_if_get_selected(bat_priv);
    525	if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
    526		ret = -ENOENT;
    527		goto out;
    528	}
    529
    530	if (!bat_priv->algo_ops->gw.dump) {
    531		ret = -EOPNOTSUPP;
    532		goto out;
    533	}
    534
    535	bat_priv->algo_ops->gw.dump(msg, cb, bat_priv);
    536
    537	ret = msg->len;
    538
    539out:
    540	batadv_hardif_put(primary_if);
    541	dev_put(soft_iface);
    542
    543	return ret;
    544}
    545
    546/**
    547 * batadv_gw_dhcp_recipient_get() - check if a packet is a DHCP message
    548 * @skb: the packet to check
    549 * @header_len: a pointer to the batman-adv header size
    550 * @chaddr: buffer where the client address will be stored. Valid
    551 *  only if the function returns BATADV_DHCP_TO_CLIENT
    552 *
    553 * This function may re-allocate the data buffer of the skb passed as argument.
    554 *
    555 * Return:
    556 * - BATADV_DHCP_NO if the packet is not a dhcp message or if there was an error
    557 *   while parsing it
    558 * - BATADV_DHCP_TO_SERVER if this is a message going to the DHCP server
    559 * - BATADV_DHCP_TO_CLIENT if this is a message going to a DHCP client
    560 */
    561enum batadv_dhcp_recipient
    562batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len,
    563			     u8 *chaddr)
    564{
    565	enum batadv_dhcp_recipient ret = BATADV_DHCP_NO;
    566	struct ethhdr *ethhdr;
    567	struct iphdr *iphdr;
    568	struct ipv6hdr *ipv6hdr;
    569	struct udphdr *udphdr;
    570	struct vlan_ethhdr *vhdr;
    571	int chaddr_offset;
    572	__be16 proto;
    573	u8 *p;
    574
    575	/* check for ethernet header */
    576	if (!pskb_may_pull(skb, *header_len + ETH_HLEN))
    577		return BATADV_DHCP_NO;
    578
    579	ethhdr = eth_hdr(skb);
    580	proto = ethhdr->h_proto;
    581	*header_len += ETH_HLEN;
    582
    583	/* check for initial vlan header */
    584	if (proto == htons(ETH_P_8021Q)) {
    585		if (!pskb_may_pull(skb, *header_len + VLAN_HLEN))
    586			return BATADV_DHCP_NO;
    587
    588		vhdr = vlan_eth_hdr(skb);
    589		proto = vhdr->h_vlan_encapsulated_proto;
    590		*header_len += VLAN_HLEN;
    591	}
    592
    593	/* check for ip header */
    594	switch (proto) {
    595	case htons(ETH_P_IP):
    596		if (!pskb_may_pull(skb, *header_len + sizeof(*iphdr)))
    597			return BATADV_DHCP_NO;
    598
    599		iphdr = (struct iphdr *)(skb->data + *header_len);
    600		*header_len += iphdr->ihl * 4;
    601
    602		/* check for udp header */
    603		if (iphdr->protocol != IPPROTO_UDP)
    604			return BATADV_DHCP_NO;
    605
    606		break;
    607	case htons(ETH_P_IPV6):
    608		if (!pskb_may_pull(skb, *header_len + sizeof(*ipv6hdr)))
    609			return BATADV_DHCP_NO;
    610
    611		ipv6hdr = (struct ipv6hdr *)(skb->data + *header_len);
    612		*header_len += sizeof(*ipv6hdr);
    613
    614		/* check for udp header */
    615		if (ipv6hdr->nexthdr != IPPROTO_UDP)
    616			return BATADV_DHCP_NO;
    617
    618		break;
    619	default:
    620		return BATADV_DHCP_NO;
    621	}
    622
    623	if (!pskb_may_pull(skb, *header_len + sizeof(*udphdr)))
    624		return BATADV_DHCP_NO;
    625
    626	udphdr = (struct udphdr *)(skb->data + *header_len);
    627	*header_len += sizeof(*udphdr);
    628
    629	/* check for bootp port */
    630	switch (proto) {
    631	case htons(ETH_P_IP):
    632		if (udphdr->dest == htons(67))
    633			ret = BATADV_DHCP_TO_SERVER;
    634		else if (udphdr->source == htons(67))
    635			ret = BATADV_DHCP_TO_CLIENT;
    636		break;
    637	case htons(ETH_P_IPV6):
    638		if (udphdr->dest == htons(547))
    639			ret = BATADV_DHCP_TO_SERVER;
    640		else if (udphdr->source == htons(547))
    641			ret = BATADV_DHCP_TO_CLIENT;
    642		break;
    643	}
    644
    645	chaddr_offset = *header_len + BATADV_DHCP_CHADDR_OFFSET;
    646	/* store the client address if the message is going to a client */
    647	if (ret == BATADV_DHCP_TO_CLIENT) {
    648		if (!pskb_may_pull(skb, chaddr_offset + ETH_ALEN))
    649			return BATADV_DHCP_NO;
    650
    651		/* check if the DHCP packet carries an Ethernet DHCP */
    652		p = skb->data + *header_len + BATADV_DHCP_HTYPE_OFFSET;
    653		if (*p != BATADV_DHCP_HTYPE_ETHERNET)
    654			return BATADV_DHCP_NO;
    655
    656		/* check if the DHCP packet carries a valid Ethernet address */
    657		p = skb->data + *header_len + BATADV_DHCP_HLEN_OFFSET;
    658		if (*p != ETH_ALEN)
    659			return BATADV_DHCP_NO;
    660
    661		ether_addr_copy(chaddr, skb->data + chaddr_offset);
    662	}
    663
    664	return ret;
    665}
    666
    667/**
    668 * batadv_gw_out_of_range() - check if the dhcp request destination is the best
    669 *  gateway
    670 * @bat_priv: the bat priv with all the soft interface information
    671 * @skb: the outgoing packet
    672 *
    673 * Check if the skb is a DHCP request and if it is sent to the current best GW
    674 * server. Due to topology changes it may be the case that the GW server
    675 * previously selected is not the best one anymore.
    676 *
    677 * This call might reallocate skb data.
    678 * Must be invoked only when the DHCP packet is going TO a DHCP SERVER.
    679 *
    680 * Return: true if the packet destination is unicast and it is not the best gw,
    681 * false otherwise.
    682 */
    683bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
    684			    struct sk_buff *skb)
    685{
    686	struct batadv_neigh_node *neigh_curr = NULL;
    687	struct batadv_neigh_node *neigh_old = NULL;
    688	struct batadv_orig_node *orig_dst_node = NULL;
    689	struct batadv_gw_node *gw_node = NULL;
    690	struct batadv_gw_node *curr_gw = NULL;
    691	struct batadv_neigh_ifinfo *curr_ifinfo, *old_ifinfo;
    692	struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
    693	bool out_of_range = false;
    694	u8 curr_tq_avg;
    695	unsigned short vid;
    696
    697	vid = batadv_get_vid(skb, 0);
    698
    699	if (is_multicast_ether_addr(ethhdr->h_dest))
    700		goto out;
    701
    702	orig_dst_node = batadv_transtable_search(bat_priv, ethhdr->h_source,
    703						 ethhdr->h_dest, vid);
    704	if (!orig_dst_node)
    705		goto out;
    706
    707	gw_node = batadv_gw_node_get(bat_priv, orig_dst_node);
    708	if (!gw_node)
    709		goto out;
    710
    711	switch (atomic_read(&bat_priv->gw.mode)) {
    712	case BATADV_GW_MODE_SERVER:
    713		/* If we are a GW then we are our best GW. We can artificially
    714		 * set the tq towards ourself as the maximum value
    715		 */
    716		curr_tq_avg = BATADV_TQ_MAX_VALUE;
    717		break;
    718	case BATADV_GW_MODE_CLIENT:
    719		curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
    720		if (!curr_gw)
    721			goto out;
    722
    723		/* packet is going to our gateway */
    724		if (curr_gw->orig_node == orig_dst_node)
    725			goto out;
    726
    727		/* If the dhcp packet has been sent to a different gw,
    728		 * we have to evaluate whether the old gw is still
    729		 * reliable enough
    730		 */
    731		neigh_curr = batadv_find_router(bat_priv, curr_gw->orig_node,
    732						NULL);
    733		if (!neigh_curr)
    734			goto out;
    735
    736		curr_ifinfo = batadv_neigh_ifinfo_get(neigh_curr,
    737						      BATADV_IF_DEFAULT);
    738		if (!curr_ifinfo)
    739			goto out;
    740
    741		curr_tq_avg = curr_ifinfo->bat_iv.tq_avg;
    742		batadv_neigh_ifinfo_put(curr_ifinfo);
    743
    744		break;
    745	case BATADV_GW_MODE_OFF:
    746	default:
    747		goto out;
    748	}
    749
    750	neigh_old = batadv_find_router(bat_priv, orig_dst_node, NULL);
    751	if (!neigh_old)
    752		goto out;
    753
    754	old_ifinfo = batadv_neigh_ifinfo_get(neigh_old, BATADV_IF_DEFAULT);
    755	if (!old_ifinfo)
    756		goto out;
    757
    758	if ((curr_tq_avg - old_ifinfo->bat_iv.tq_avg) > BATADV_GW_THRESHOLD)
    759		out_of_range = true;
    760	batadv_neigh_ifinfo_put(old_ifinfo);
    761
    762out:
    763	batadv_orig_node_put(orig_dst_node);
    764	batadv_gw_node_put(curr_gw);
    765	batadv_gw_node_put(gw_node);
    766	batadv_neigh_node_put(neigh_old);
    767	batadv_neigh_node_put(neigh_curr);
    768	return out_of_range;
    769}