Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) B.A.T.M.A.N. contributors:
#
# Marek Lindner, Simon Wunderlich
#
# B.A.T.M.A.N meshing protocol
#
config BATMAN_ADV
tristate "B.A.T.M.A.N. Advanced Meshing Protocol"
select CRC32
help
B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is
a routing protocol for multi-hop ad-hoc mesh networks. The
networks may be wired or wireless. See
https://www.open-mesh.org/ for more information and user space
tools.
config BATMAN_ADV_BATMAN_V
bool "B.A.T.M.A.N. V protocol"
depends on BATMAN_ADV && !(CFG80211=m && BATMAN_ADV=y)
default y
help
This option enables the B.A.T.M.A.N. V protocol, the successor
of the currently used B.A.T.M.A.N. IV protocol. The main
changes include splitting of the OGM protocol into a neighbor
discovery protocol (Echo Location Protocol, ELP) and a new OGM
Protocol OGMv2 for flooding protocol information through the
network, as well as a throughput based metric.
B.A.T.M.A.N. V is currently considered experimental and not
compatible to B.A.T.M.A.N. IV networks.
config BATMAN_ADV_BLA
bool "Bridge Loop Avoidance"
depends on BATMAN_ADV && INET
select CRC16
select NET_CRC32C
default y
help
This option enables BLA (Bridge Loop Avoidance), a mechanism
to avoid Ethernet frames looping when mesh nodes are connected
to both the same LAN and the same mesh. If you will never use
more than one mesh node in the same LAN, you can safely remove
this feature and save some space.
config BATMAN_ADV_DAT
bool "Distributed ARP Table"
depends on BATMAN_ADV && INET
default y
help
This option enables DAT (Distributed ARP Table), a DHT based
mechanism that increases ARP reliability on sparse wireless
mesh networks. If you think that your network does not need
this option you can safely remove it and save some space.
config BATMAN_ADV_MCAST
bool "Multicast optimisation"
depends on BATMAN_ADV && INET && !(BRIDGE=m && BATMAN_ADV=y)
default y
help
This option enables the multicast optimisation which aims to
reduce the air overhead while improving the reliability of
multicast messages.
config BATMAN_ADV_DEBUG
bool "B.A.T.M.A.N. debugging"
depends on BATMAN_ADV
help
This is an option for use by developers; most people should
say N here. This enables compilation of support for
outputting debugging information to the tracing buffer. The output is
controlled via the batadv netdev specific log_level setting.
config BATMAN_ADV_TRACING
bool "B.A.T.M.A.N. tracing support"
depends on BATMAN_ADV
depends on EVENT_TRACING
help
This is an option for use by developers; most people should
say N here. Select this option to gather traces like the debug
messages using the generic tracing infrastructure of the kernel.
BATMAN_ADV_DEBUG must also be selected to get trace events for
batadv_dbg.
+35
View File
@@ -0,0 +1,35 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) B.A.T.M.A.N. contributors:
#
# Marek Lindner, Simon Wunderlich
obj-$(CONFIG_BATMAN_ADV) += batman-adv.o
batman-adv-y += bat_algo.o
batman-adv-y += bat_iv_ogm.o
batman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v.o
batman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v_elp.o
batman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v_ogm.o
batman-adv-y += bitarray.o
batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o
batman-adv-$(CONFIG_BATMAN_ADV_DAT) += distributed-arp-table.o
batman-adv-y += fragmentation.o
batman-adv-y += gateway_client.o
batman-adv-y += gateway_common.o
batman-adv-y += hard-interface.o
batman-adv-y += hash.o
batman-adv-$(CONFIG_BATMAN_ADV_DEBUG) += log.o
batman-adv-y += main.o
batman-adv-y += mesh-interface.o
batman-adv-$(CONFIG_BATMAN_ADV_MCAST) += multicast.o
batman-adv-$(CONFIG_BATMAN_ADV_MCAST) += multicast_forw.o
batman-adv-y += netlink.o
batman-adv-y += originator.o
batman-adv-y += routing.o
batman-adv-y += send.o
batman-adv-$(CONFIG_BATMAN_ADV_TRACING) += trace.o
batman-adv-y += tp_meter.o
batman-adv-y += translation-table.o
batman-adv-y += tvlv.o
batman-adv-y += version.o
CFLAGS_trace.o := -I$(src)
+210
View File
@@ -0,0 +1,210 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*/
#include "main.h"
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/moduleparam.h>
#include <linux/netlink.h>
#include <linux/printk.h>
#include <linux/skbuff.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/types.h>
#include <net/genetlink.h>
#include <net/netlink.h>
#include <uapi/linux/batman_adv.h>
#include "bat_algo.h"
#include "netlink.h"
char batadv_routing_algo[20] = "BATMAN_IV";
static struct hlist_head batadv_algo_list;
/**
* batadv_algo_init() - Initialize batman-adv algorithm management data
* structures
*/
void batadv_algo_init(void)
{
INIT_HLIST_HEAD(&batadv_algo_list);
}
/**
* batadv_algo_get() - Search for algorithm with specific name
* @name: algorithm name to find
*
* Return: Pointer to batadv_algo_ops on success, NULL otherwise
*/
struct batadv_algo_ops *batadv_algo_get(const char *name)
{
struct batadv_algo_ops *bat_algo_ops = NULL, *bat_algo_ops_tmp;
hlist_for_each_entry(bat_algo_ops_tmp, &batadv_algo_list, list) {
if (strcmp(bat_algo_ops_tmp->name, name) != 0)
continue;
bat_algo_ops = bat_algo_ops_tmp;
break;
}
return bat_algo_ops;
}
/**
* batadv_algo_register() - Register callbacks for a mesh algorithm
* @bat_algo_ops: mesh algorithm callbacks to add
*
* Return: 0 on success or negative error number in case of failure
*/
int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops)
{
struct batadv_algo_ops *bat_algo_ops_tmp;
bat_algo_ops_tmp = batadv_algo_get(bat_algo_ops->name);
if (bat_algo_ops_tmp) {
pr_info("Trying to register already registered routing algorithm: %s\n",
bat_algo_ops->name);
return -EEXIST;
}
/* all algorithms must implement all ops (for now) */
if (!bat_algo_ops->iface.enable ||
!bat_algo_ops->iface.disable ||
!bat_algo_ops->iface.update_mac ||
!bat_algo_ops->iface.primary_set ||
!bat_algo_ops->neigh.cmp ||
!bat_algo_ops->neigh.is_similar_or_better) {
pr_info("Routing algo '%s' does not implement required ops\n",
bat_algo_ops->name);
return -EINVAL;
}
INIT_HLIST_NODE(&bat_algo_ops->list);
hlist_add_head(&bat_algo_ops->list, &batadv_algo_list);
return 0;
}
/**
* batadv_algo_select() - Select algorithm of mesh interface
* @bat_priv: the bat priv with all the mesh interface information
* @name: name of the algorithm to select
*
* The algorithm callbacks for the mesh interface will be set when the algorithm
* with the correct name was found. Any previous selected algorithm will not be
* deinitialized and the new selected algorithm will also not be initialized.
* It is therefore not allowed to call batadv_algo_select outside the creation
* function of the mesh interface.
*
* Return: 0 on success or negative error number in case of failure
*/
int batadv_algo_select(struct batadv_priv *bat_priv, const char *name)
{
struct batadv_algo_ops *bat_algo_ops;
bat_algo_ops = batadv_algo_get(name);
if (!bat_algo_ops)
return -EINVAL;
bat_priv->algo_ops = bat_algo_ops;
return 0;
}
static int batadv_param_set_ra(const char *val, const struct kernel_param *kp)
{
struct batadv_algo_ops *bat_algo_ops;
char *algo_name = (char *)val;
size_t name_len = strlen(algo_name);
if (name_len > 0 && algo_name[name_len - 1] == '\n')
algo_name[name_len - 1] = '\0';
bat_algo_ops = batadv_algo_get(algo_name);
if (!bat_algo_ops) {
pr_err("Routing algorithm '%s' is not supported\n", algo_name);
return -EINVAL;
}
return param_set_copystring(algo_name, kp);
}
static const struct kernel_param_ops batadv_param_ops_ra = {
.set = batadv_param_set_ra,
.get = param_get_string,
};
static struct kparam_string batadv_param_string_ra = {
.maxlen = sizeof(batadv_routing_algo),
.string = batadv_routing_algo,
};
module_param_cb(routing_algo, &batadv_param_ops_ra, &batadv_param_string_ra,
0644);
/**
* batadv_algo_dump_entry() - fill in information about one supported routing
* algorithm
* @msg: netlink message to be sent back
* @portid: Port to reply to
* @seq: Sequence number of message
* @bat_algo_ops: Algorithm to be dumped
*
* Return: Error number, or 0 on success
*/
static int batadv_algo_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
struct batadv_algo_ops *bat_algo_ops)
{
void *hdr;
hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
NLM_F_MULTI, BATADV_CMD_GET_ROUTING_ALGOS);
if (!hdr)
return -EMSGSIZE;
if (nla_put_string(msg, BATADV_ATTR_ALGO_NAME, bat_algo_ops->name))
goto nla_put_failure;
genlmsg_end(msg, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
}
/**
* batadv_algo_dump() - fill in information about supported routing
* algorithms
* @msg: netlink message to be sent back
* @cb: Parameters to the netlink request
*
* Return: Length of reply message.
*/
int batadv_algo_dump(struct sk_buff *msg, struct netlink_callback *cb)
{
int portid = NETLINK_CB(cb->skb).portid;
struct batadv_algo_ops *bat_algo_ops;
int skip = cb->args[0];
int i = 0;
hlist_for_each_entry(bat_algo_ops, &batadv_algo_list, list) {
if (i++ < skip)
continue;
if (batadv_algo_dump_entry(msg, portid, cb->nlh->nlmsg_seq,
bat_algo_ops)) {
i--;
break;
}
}
cb->args[0] = i;
return msg->len;
}
+23
View File
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner, Linus Lüssing
*/
#ifndef _NET_BATMAN_ADV_BAT_ALGO_H_
#define _NET_BATMAN_ADV_BAT_ALGO_H_
#include "main.h"
#include <linux/netlink.h>
#include <linux/skbuff.h>
extern char batadv_routing_algo[];
void batadv_algo_init(void);
struct batadv_algo_ops *batadv_algo_get(const char *name);
int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops);
int batadv_algo_select(struct batadv_priv *bat_priv, const char *name);
int batadv_algo_dump(struct sk_buff *msg, struct netlink_callback *cb);
#endif /* _NET_BATMAN_ADV_BAT_ALGO_H_ */
File diff suppressed because it is too large Load Diff
+14
View File
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*/
#ifndef _NET_BATMAN_ADV_BAT_IV_OGM_H_
#define _NET_BATMAN_ADV_BAT_IV_OGM_H_
#include "main.h"
int batadv_iv_init(void);
#endif /* _NET_BATMAN_ADV_BAT_IV_OGM_H_ */
+890
View File
@@ -0,0 +1,890 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Linus Lüssing, Marek Lindner
*/
#include "bat_v.h"
#include "main.h"
#include <linux/cache.h>
#include <linux/compiler.h>
#include <linux/errno.h>
#include <linux/if_ether.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/kref.h>
#include <linux/limits.h>
#include <linux/list.h>
#include <linux/minmax.h>
#include <linux/netdevice.h>
#include <linux/netlink.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <net/genetlink.h>
#include <net/netlink.h>
#include <uapi/linux/batadv_packet.h>
#include <uapi/linux/batman_adv.h>
#include "bat_algo.h"
#include "bat_v_elp.h"
#include "bat_v_ogm.h"
#include "gateway_client.h"
#include "hard-interface.h"
#include "hash.h"
#include "log.h"
#include "netlink.h"
#include "originator.h"
static void batadv_v_iface_activate(struct batadv_hard_iface *hard_iface)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
struct batadv_hard_iface *primary_if;
primary_if = batadv_primary_if_get_selected(bat_priv);
if (primary_if) {
batadv_v_elp_iface_activate(primary_if, hard_iface);
batadv_hardif_put(primary_if);
}
/* B.A.T.M.A.N. V does not use any queuing mechanism, therefore it can
* set the interface as ACTIVE right away, without any risk of race
* condition
*/
if (hard_iface->if_status == BATADV_IF_TO_BE_ACTIVATED)
hard_iface->if_status = BATADV_IF_ACTIVE;
}
static int batadv_v_iface_enable(struct batadv_hard_iface *hard_iface)
{
int ret;
ret = batadv_v_elp_iface_enable(hard_iface);
if (ret < 0)
return ret;
ret = batadv_v_ogm_iface_enable(hard_iface);
if (ret < 0)
batadv_v_elp_iface_disable(hard_iface);
return ret;
}
static void batadv_v_iface_disable(struct batadv_hard_iface *hard_iface)
{
batadv_v_ogm_iface_disable(hard_iface);
batadv_v_elp_iface_disable(hard_iface);
}
static void batadv_v_primary_iface_set(struct batadv_hard_iface *hard_iface)
{
batadv_v_elp_primary_iface_set(hard_iface);
batadv_v_ogm_primary_iface_set(hard_iface);
}
/**
* batadv_v_iface_update_mac() - react to hard-interface MAC address change
* @hard_iface: the modified interface
*
* If the modified interface is the primary one, update the originator
* address in the ELP and OGM messages to reflect the new MAC address.
*/
static void batadv_v_iface_update_mac(struct batadv_hard_iface *hard_iface)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
struct batadv_hard_iface *primary_if;
primary_if = batadv_primary_if_get_selected(bat_priv);
if (primary_if != hard_iface)
goto out;
batadv_v_primary_iface_set(hard_iface);
out:
batadv_hardif_put(primary_if);
}
static void
batadv_v_hardif_neigh_init(struct batadv_hardif_neigh_node *hardif_neigh)
{
ewma_throughput_init(&hardif_neigh->bat_v.throughput);
}
/**
* batadv_v_neigh_dump_neigh() - Dump a neighbour into a message
* @msg: Netlink message to dump into
* @portid: Port making netlink request
* @seq: Sequence number of netlink message
* @hardif_neigh: Neighbour to dump
*
* Return: Error code, or 0 on success
*/
static int
batadv_v_neigh_dump_neigh(struct sk_buff *msg, u32 portid, u32 seq,
struct batadv_hardif_neigh_node *hardif_neigh)
{
void *hdr;
unsigned int last_seen_msecs;
u32 throughput;
last_seen_msecs = jiffies_to_msecs(jiffies - hardif_neigh->last_seen);
throughput = ewma_throughput_read(&hardif_neigh->bat_v.throughput);
throughput = throughput * 100;
hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, NLM_F_MULTI,
BATADV_CMD_GET_NEIGHBORS);
if (!hdr)
return -ENOBUFS;
if (nla_put(msg, BATADV_ATTR_NEIGH_ADDRESS, ETH_ALEN,
hardif_neigh->addr) ||
nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
hardif_neigh->if_incoming->net_dev->name) ||
nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
hardif_neigh->if_incoming->net_dev->ifindex) ||
nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS,
last_seen_msecs) ||
nla_put_u32(msg, BATADV_ATTR_THROUGHPUT, throughput))
goto nla_put_failure;
genlmsg_end(msg, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
}
/**
* batadv_v_neigh_dump_hardif() - Dump the neighbours of a hard interface into
* a message
* @msg: Netlink message to dump into
* @portid: Port making netlink request
* @seq: Sequence number of netlink message
* @bat_priv: The bat priv with all the mesh interface information
* @hard_iface: The hard interface to be dumped
* @idx_s: Entries to be skipped
*
* This function assumes the caller holds rcu_read_lock().
*
* Return: Error code, or 0 on success
*/
static int
batadv_v_neigh_dump_hardif(struct sk_buff *msg, u32 portid, u32 seq,
struct batadv_priv *bat_priv,
struct batadv_hard_iface *hard_iface,
int *idx_s)
{
struct batadv_hardif_neigh_node *hardif_neigh;
int idx = 0;
hlist_for_each_entry_rcu(hardif_neigh,
&hard_iface->neigh_list, list) {
if (idx++ < *idx_s)
continue;
if (batadv_v_neigh_dump_neigh(msg, portid, seq, hardif_neigh)) {
*idx_s = idx - 1;
return -EMSGSIZE;
}
}
*idx_s = 0;
return 0;
}
/**
* batadv_v_neigh_dump() - Dump the neighbours of a hard interface into a
* message
* @msg: Netlink message to dump into
* @cb: Control block containing additional options
* @bat_priv: The bat priv with all the mesh interface information
* @single_hardif: Limit dumping to this hard interface
*/
static void
batadv_v_neigh_dump(struct sk_buff *msg, struct netlink_callback *cb,
struct batadv_priv *bat_priv,
struct batadv_hard_iface *single_hardif)
{
struct batadv_hard_iface *hard_iface;
struct list_head *iter;
int i_hardif = 0;
int i_hardif_s = cb->args[0];
int idx = cb->args[1];
int portid = NETLINK_CB(cb->skb).portid;
rcu_read_lock();
if (single_hardif) {
if (i_hardif_s == 0) {
if (batadv_v_neigh_dump_hardif(msg, portid,
cb->nlh->nlmsg_seq,
bat_priv, single_hardif,
&idx) == 0)
i_hardif++;
}
} else {
netdev_for_each_lower_private_rcu(bat_priv->mesh_iface, hard_iface, iter) {
if (i_hardif++ < i_hardif_s)
continue;
if (batadv_v_neigh_dump_hardif(msg, portid,
cb->nlh->nlmsg_seq,
bat_priv, hard_iface,
&idx)) {
i_hardif--;
break;
}
}
}
rcu_read_unlock();
cb->args[0] = i_hardif;
cb->args[1] = idx;
}
/**
* batadv_v_orig_dump_subentry() - Dump an originator subentry into a message
* @msg: Netlink message to dump into
* @portid: Port making netlink request
* @seq: Sequence number of netlink message
* @bat_priv: The bat priv with all the mesh interface information
* @if_outgoing: Limit dump to entries with this outgoing interface
* @orig_node: Originator to dump
* @neigh_node: Single hops neighbour
* @best: Is the best originator
*
* Return: Error code, or 0 on success
*/
static int
batadv_v_orig_dump_subentry(struct sk_buff *msg, u32 portid, u32 seq,
struct batadv_priv *bat_priv,
struct batadv_hard_iface *if_outgoing,
struct batadv_orig_node *orig_node,
struct batadv_neigh_node *neigh_node,
bool best)
{
struct batadv_neigh_ifinfo *n_ifinfo;
unsigned int last_seen_msecs;
u32 throughput;
void *hdr;
n_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing);
if (!n_ifinfo)
return 0;
throughput = n_ifinfo->bat_v.throughput * 100;
batadv_neigh_ifinfo_put(n_ifinfo);
last_seen_msecs = jiffies_to_msecs(jiffies - orig_node->last_seen);
if (if_outgoing != BATADV_IF_DEFAULT &&
if_outgoing != neigh_node->if_incoming)
return 0;
hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, NLM_F_MULTI,
BATADV_CMD_GET_ORIGINATORS);
if (!hdr)
return -ENOBUFS;
if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN, orig_node->orig) ||
nla_put(msg, BATADV_ATTR_NEIGH_ADDRESS, ETH_ALEN,
neigh_node->addr) ||
nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
neigh_node->if_incoming->net_dev->name) ||
nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
neigh_node->if_incoming->net_dev->ifindex) ||
nla_put_u32(msg, BATADV_ATTR_THROUGHPUT, throughput) ||
nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS,
last_seen_msecs))
goto nla_put_failure;
if (best && nla_put_flag(msg, BATADV_ATTR_FLAG_BEST))
goto nla_put_failure;
genlmsg_end(msg, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
}
/**
* batadv_v_orig_dump_entry() - Dump an originator entry into a message
* @msg: Netlink message to dump into
* @portid: Port making netlink request
* @seq: Sequence number of netlink message
* @bat_priv: The bat priv with all the mesh interface information
* @if_outgoing: Limit dump to entries with this outgoing interface
* @orig_node: Originator to dump
* @sub_s: Number of sub entries to skip
*
* This function assumes the caller holds rcu_read_lock().
*
* Return: Error code, or 0 on success
*/
static int
batadv_v_orig_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
struct batadv_priv *bat_priv,
struct batadv_hard_iface *if_outgoing,
struct batadv_orig_node *orig_node, int *sub_s)
{
struct batadv_neigh_node *neigh_node_best;
struct batadv_neigh_node *neigh_node;
int sub = 0;
bool best;
neigh_node_best = batadv_orig_router_get(orig_node, if_outgoing);
if (!neigh_node_best)
goto out;
hlist_for_each_entry_rcu(neigh_node, &orig_node->neigh_list, list) {
if (sub++ < *sub_s)
continue;
best = (neigh_node == neigh_node_best);
if (batadv_v_orig_dump_subentry(msg, portid, seq, bat_priv,
if_outgoing, orig_node,
neigh_node, best)) {
batadv_neigh_node_put(neigh_node_best);
*sub_s = sub - 1;
return -EMSGSIZE;
}
}
out:
batadv_neigh_node_put(neigh_node_best);
*sub_s = 0;
return 0;
}
/**
* batadv_v_orig_dump_bucket() - Dump an originator bucket into a message
* @msg: Netlink message to dump into
* @portid: Port making netlink request
* @seq: Sequence number of netlink message
* @bat_priv: The bat priv with all the mesh interface information
* @if_outgoing: Limit dump to entries with this outgoing interface
* @head: Bucket to be dumped
* @idx_s: Number of entries to be skipped
* @sub: Number of sub entries to be skipped
*
* Return: Error code, or 0 on success
*/
static int
batadv_v_orig_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
struct batadv_priv *bat_priv,
struct batadv_hard_iface *if_outgoing,
struct hlist_head *head, int *idx_s, int *sub)
{
struct batadv_orig_node *orig_node;
int idx = 0;
rcu_read_lock();
hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
if (idx++ < *idx_s)
continue;
if (batadv_v_orig_dump_entry(msg, portid, seq, bat_priv,
if_outgoing, orig_node, sub)) {
rcu_read_unlock();
*idx_s = idx - 1;
return -EMSGSIZE;
}
}
rcu_read_unlock();
*idx_s = 0;
*sub = 0;
return 0;
}
/**
* batadv_v_orig_dump() - Dump the originators into a message
* @msg: Netlink message to dump into
* @cb: Control block containing additional options
* @bat_priv: The bat priv with all the mesh interface information
* @if_outgoing: Limit dump to entries with this outgoing interface
*/
static void
batadv_v_orig_dump(struct sk_buff *msg, struct netlink_callback *cb,
struct batadv_priv *bat_priv,
struct batadv_hard_iface *if_outgoing)
{
struct batadv_hashtable *hash = bat_priv->orig_hash;
struct hlist_head *head;
int bucket = cb->args[0];
int idx = cb->args[1];
int sub = cb->args[2];
int portid = NETLINK_CB(cb->skb).portid;
while (bucket < hash->size) {
head = &hash->table[bucket];
if (batadv_v_orig_dump_bucket(msg, portid,
cb->nlh->nlmsg_seq,
bat_priv, if_outgoing, head, &idx,
&sub))
break;
bucket++;
}
cb->args[0] = bucket;
cb->args[1] = idx;
cb->args[2] = sub;
}
static int batadv_v_neigh_cmp(struct batadv_neigh_node *neigh1,
struct batadv_hard_iface *if_outgoing1,
struct batadv_neigh_node *neigh2,
struct batadv_hard_iface *if_outgoing2)
{
struct batadv_neigh_ifinfo *ifinfo1, *ifinfo2;
int ret = 0;
ifinfo1 = batadv_neigh_ifinfo_get(neigh1, if_outgoing1);
if (!ifinfo1)
goto err_ifinfo1;
ifinfo2 = batadv_neigh_ifinfo_get(neigh2, if_outgoing2);
if (!ifinfo2)
goto err_ifinfo2;
ret = ifinfo1->bat_v.throughput - ifinfo2->bat_v.throughput;
batadv_neigh_ifinfo_put(ifinfo2);
err_ifinfo2:
batadv_neigh_ifinfo_put(ifinfo1);
err_ifinfo1:
return ret;
}
static bool batadv_v_neigh_is_sob(struct batadv_neigh_node *neigh1,
struct batadv_hard_iface *if_outgoing1,
struct batadv_neigh_node *neigh2,
struct batadv_hard_iface *if_outgoing2)
{
struct batadv_neigh_ifinfo *ifinfo1, *ifinfo2;
u32 threshold;
bool ret = false;
ifinfo1 = batadv_neigh_ifinfo_get(neigh1, if_outgoing1);
if (!ifinfo1)
goto err_ifinfo1;
ifinfo2 = batadv_neigh_ifinfo_get(neigh2, if_outgoing2);
if (!ifinfo2)
goto err_ifinfo2;
threshold = ifinfo1->bat_v.throughput / 4;
threshold = ifinfo1->bat_v.throughput - threshold;
ret = ifinfo2->bat_v.throughput > threshold;
batadv_neigh_ifinfo_put(ifinfo2);
err_ifinfo2:
batadv_neigh_ifinfo_put(ifinfo1);
err_ifinfo1:
return ret;
}
/**
* batadv_v_init_sel_class() - initialize GW selection class
* @bat_priv: the bat priv with all the mesh interface information
*/
static void batadv_v_init_sel_class(struct batadv_priv *bat_priv)
{
/* set default throughput difference threshold to 5Mbps */
WRITE_ONCE(bat_priv->gw.sel_class, 50);
}
/**
* batadv_v_gw_throughput_get() - retrieve the GW-bandwidth for a given GW
* @gw_node: the GW to retrieve the metric for
* @bw: the pointer where the metric will be stored. The metric is computed as
* the minimum between the GW advertised throughput and the path throughput to
* it in the mesh
*
* Return: 0 on success, -1 on failure
*/
static int batadv_v_gw_throughput_get(struct batadv_gw_node *gw_node, u32 *bw)
{
struct batadv_neigh_ifinfo *router_ifinfo = NULL;
struct batadv_orig_node *orig_node;
struct batadv_neigh_node *router;
int ret = -1;
orig_node = gw_node->orig_node;
router = batadv_orig_router_get(orig_node, BATADV_IF_DEFAULT);
if (!router)
goto out;
router_ifinfo = batadv_neigh_ifinfo_get(router, BATADV_IF_DEFAULT);
if (!router_ifinfo)
goto out;
/* the GW metric is computed as the minimum between the path throughput
* to reach the GW itself and the advertised bandwidth.
* This gives us an approximation of the effective throughput that the
* client can expect via this particular GW node
*/
*bw = router_ifinfo->bat_v.throughput;
*bw = min_t(u32, *bw, gw_node->bandwidth_down);
ret = 0;
out:
batadv_neigh_node_put(router);
batadv_neigh_ifinfo_put(router_ifinfo);
return ret;
}
/**
* batadv_v_gw_get_best_gw_node() - retrieve the best GW node
* @bat_priv: the bat priv with all the mesh interface information
*
* Return: the GW node having the best GW-metric, NULL if no GW is known
*/
static struct batadv_gw_node *
batadv_v_gw_get_best_gw_node(struct batadv_priv *bat_priv)
{
struct batadv_gw_node *gw_node, *curr_gw = NULL;
u32 max_bw = 0, bw;
rcu_read_lock();
hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.gateway_list, list) {
if (!kref_get_unless_zero(&gw_node->refcount))
continue;
if (batadv_v_gw_throughput_get(gw_node, &bw) < 0)
goto next;
if (curr_gw && bw <= max_bw)
goto next;
batadv_gw_node_put(curr_gw);
curr_gw = gw_node;
kref_get(&curr_gw->refcount);
max_bw = bw;
next:
batadv_gw_node_put(gw_node);
}
rcu_read_unlock();
return curr_gw;
}
/**
* batadv_v_gw_is_eligible() - check if an originator would be selected as GW
* @bat_priv: the bat priv with all the mesh interface information
* @curr_gw_orig: originator representing the currently selected GW
* @orig_node: the originator representing the new candidate
*
* Return: true if orig_node can be selected as current GW, false otherwise
*/
static bool batadv_v_gw_is_eligible(struct batadv_priv *bat_priv,
struct batadv_orig_node *curr_gw_orig,
struct batadv_orig_node *orig_node)
{
struct batadv_gw_node *curr_gw, *orig_gw = NULL;
u32 gw_throughput, orig_throughput, threshold;
bool ret = false;
threshold = READ_ONCE(bat_priv->gw.sel_class);
curr_gw = batadv_gw_node_get(bat_priv, curr_gw_orig);
if (!curr_gw) {
ret = true;
goto out;
}
if (batadv_v_gw_throughput_get(curr_gw, &gw_throughput) < 0) {
ret = true;
goto out;
}
orig_gw = batadv_gw_node_get(bat_priv, orig_node);
if (!orig_gw)
goto out;
if (batadv_v_gw_throughput_get(orig_gw, &orig_throughput) < 0)
goto out;
if (orig_throughput < gw_throughput)
goto out;
if ((orig_throughput - gw_throughput) < threshold)
goto out;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Restarting gateway selection: better gateway found (throughput curr: %u, throughput new: %u)\n",
gw_throughput, orig_throughput);
ret = true;
out:
batadv_gw_node_put(curr_gw);
batadv_gw_node_put(orig_gw);
return ret;
}
/**
* batadv_v_gw_dump_entry() - Dump a gateway into a message
* @msg: Netlink message to dump into
* @portid: Port making netlink request
* @cb: Control block containing additional options
* @bat_priv: The bat priv with all the mesh interface information
* @gw_node: Gateway to be dumped
*
* Return: Error code, or 0 on success
*/
static int batadv_v_gw_dump_entry(struct sk_buff *msg, u32 portid,
struct netlink_callback *cb,
struct batadv_priv *bat_priv,
struct batadv_gw_node *gw_node)
{
struct batadv_neigh_ifinfo *router_ifinfo = NULL;
struct batadv_neigh_node *router;
struct batadv_gw_node *curr_gw = NULL;
int ret = 0;
void *hdr;
router = batadv_orig_router_get(gw_node->orig_node, BATADV_IF_DEFAULT);
if (!router)
goto out;
router_ifinfo = batadv_neigh_ifinfo_get(router, BATADV_IF_DEFAULT);
if (!router_ifinfo)
goto out;
curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
&batadv_netlink_family, NLM_F_MULTI,
BATADV_CMD_GET_GATEWAYS);
if (!hdr) {
ret = -ENOBUFS;
goto out;
}
genl_dump_check_consistent(cb, hdr);
ret = -EMSGSIZE;
if (curr_gw == gw_node) {
if (nla_put_flag(msg, BATADV_ATTR_FLAG_BEST)) {
genlmsg_cancel(msg, hdr);
goto out;
}
}
if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN,
gw_node->orig_node->orig)) {
genlmsg_cancel(msg, hdr);
goto out;
}
if (nla_put_u32(msg, BATADV_ATTR_THROUGHPUT,
router_ifinfo->bat_v.throughput)) {
genlmsg_cancel(msg, hdr);
goto out;
}
if (nla_put(msg, BATADV_ATTR_ROUTER, ETH_ALEN, router->addr)) {
genlmsg_cancel(msg, hdr);
goto out;
}
if (nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
router->if_incoming->net_dev->name)) {
genlmsg_cancel(msg, hdr);
goto out;
}
if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
router->if_incoming->net_dev->ifindex)) {
genlmsg_cancel(msg, hdr);
goto out;
}
if (nla_put_u32(msg, BATADV_ATTR_BANDWIDTH_DOWN,
gw_node->bandwidth_down)) {
genlmsg_cancel(msg, hdr);
goto out;
}
if (nla_put_u32(msg, BATADV_ATTR_BANDWIDTH_UP, gw_node->bandwidth_up)) {
genlmsg_cancel(msg, hdr);
goto out;
}
genlmsg_end(msg, hdr);
ret = 0;
out:
batadv_gw_node_put(curr_gw);
batadv_neigh_ifinfo_put(router_ifinfo);
batadv_neigh_node_put(router);
return ret;
}
/**
* batadv_v_gw_dump() - Dump gateways into a message
* @msg: Netlink message to dump into
* @cb: Control block containing additional options
* @bat_priv: The bat priv with all the mesh interface information
*/
static void batadv_v_gw_dump(struct sk_buff *msg, struct netlink_callback *cb,
struct batadv_priv *bat_priv)
{
int portid = NETLINK_CB(cb->skb).portid;
struct batadv_gw_node *gw_node;
int idx_skip = cb->args[0];
int idx = 0;
spin_lock_bh(&bat_priv->gw.list_lock);
cb->seq = bat_priv->gw.generation << 1 | 1;
hlist_for_each_entry(gw_node, &bat_priv->gw.gateway_list, list) {
if (idx++ < idx_skip)
continue;
if (batadv_v_gw_dump_entry(msg, portid, cb, bat_priv,
gw_node)) {
idx_skip = idx - 1;
goto unlock;
}
}
idx_skip = idx;
unlock:
spin_unlock_bh(&bat_priv->gw.list_lock);
cb->args[0] = idx_skip;
}
static struct batadv_algo_ops batadv_batman_v __read_mostly = {
.name = "BATMAN_V",
.iface = {
.activate = batadv_v_iface_activate,
.enable = batadv_v_iface_enable,
.disable = batadv_v_iface_disable,
.update_mac = batadv_v_iface_update_mac,
.primary_set = batadv_v_primary_iface_set,
},
.neigh = {
.hardif_init = batadv_v_hardif_neigh_init,
.cmp = batadv_v_neigh_cmp,
.is_similar_or_better = batadv_v_neigh_is_sob,
.dump = batadv_v_neigh_dump,
},
.orig = {
.dump = batadv_v_orig_dump,
},
.gw = {
.init_sel_class = batadv_v_init_sel_class,
.sel_class_max = U32_MAX,
.get_best_gw_node = batadv_v_gw_get_best_gw_node,
.is_eligible = batadv_v_gw_is_eligible,
.dump = batadv_v_gw_dump,
},
};
/**
* batadv_v_hardif_init() - initialize the algorithm specific fields in the
* hard-interface object
* @hard_iface: the hard-interface to initialize
*/
void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface)
{
/* enable link throughput auto-detection by setting the throughput
* override to zero
*/
WRITE_ONCE(hard_iface->bat_v.throughput_override, 0);
WRITE_ONCE(hard_iface->bat_v.elp_interval, 500);
hard_iface->bat_v.aggr_len = 0;
skb_queue_head_init(&hard_iface->bat_v.aggr_list);
hard_iface->bat_v.aggr_list_enabled = false;
INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq,
batadv_v_ogm_aggr_work);
/* make sure it doesn't run until interface gets enabled */
disable_delayed_work(&hard_iface->bat_v.aggr_wq);
}
/**
* batadv_v_mesh_init() - initialize the B.A.T.M.A.N. V private resources for a
* mesh
* @bat_priv: the object representing the mesh interface to initialise
*
* Return: 0 on success or a negative error code otherwise
*/
int batadv_v_mesh_init(struct batadv_priv *bat_priv)
{
int ret = 0;
ret = batadv_v_ogm_init(bat_priv);
if (ret < 0)
return ret;
return 0;
}
/**
* batadv_v_mesh_free() - free the B.A.T.M.A.N. V private resources for a mesh
* @bat_priv: the object representing the mesh interface to free
*/
void batadv_v_mesh_free(struct batadv_priv *bat_priv)
{
batadv_v_ogm_free(bat_priv);
}
/**
* batadv_v_init() - B.A.T.M.A.N. V initialization function
*
* Description: Takes care of initializing all the subcomponents.
* It is invoked upon module load only.
*
* Return: 0 on success or a negative error code otherwise
*/
int __init batadv_v_init(void)
{
int ret;
/* B.A.T.M.A.N. V echo location protocol packet */
ret = batadv_recv_handler_register(BATADV_ELP,
batadv_v_elp_packet_recv);
if (ret < 0)
return ret;
ret = batadv_recv_handler_register(BATADV_OGM2,
batadv_v_ogm_packet_recv);
if (ret < 0)
goto elp_unregister;
ret = batadv_algo_register(&batadv_batman_v);
if (ret < 0)
goto ogm_unregister;
return ret;
ogm_unregister:
batadv_recv_handler_unregister(BATADV_OGM2);
elp_unregister:
batadv_recv_handler_unregister(BATADV_ELP);
return ret;
}
+41
View File
@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner, Linus Lüssing
*/
#ifndef _NET_BATMAN_ADV_BAT_V_H_
#define _NET_BATMAN_ADV_BAT_V_H_
#include "main.h"
#ifdef CONFIG_BATMAN_ADV_BATMAN_V
int batadv_v_init(void);
void batadv_v_hardif_init(struct batadv_hard_iface *hardif);
int batadv_v_mesh_init(struct batadv_priv *bat_priv);
void batadv_v_mesh_free(struct batadv_priv *bat_priv);
#else
static inline int batadv_v_init(void)
{
return 0;
}
static inline void batadv_v_hardif_init(struct batadv_hard_iface *hardif)
{
}
static inline int batadv_v_mesh_init(struct batadv_priv *bat_priv)
{
return 0;
}
static inline void batadv_v_mesh_free(struct batadv_priv *bat_priv)
{
}
#endif /* CONFIG_BATMAN_ADV_BATMAN_V */
#endif /* _NET_BATMAN_ADV_BAT_V_H_ */
+607
View File
@@ -0,0 +1,607 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Linus Lüssing, Marek Lindner
*/
#include "bat_v_elp.h"
#include "main.h"
#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/byteorder/generic.h>
#include <linux/compiler.h>
#include <linux/container_of.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/gfp.h>
#include <linux/if_ether.h>
#include <linux/jiffies.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/minmax.h>
#include <linux/netdevice.h>
#include <linux/nl80211.h>
#include <linux/random.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/rtnetlink.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <net/cfg80211.h>
#include <uapi/linux/batadv_packet.h>
#include "bat_v_ogm.h"
#include "hard-interface.h"
#include "log.h"
#include "originator.h"
#include "routing.h"
#include "send.h"
/**
* struct batadv_v_metric_queue_entry - list of hardif neighbors which require
* and metric update
*/
struct batadv_v_metric_queue_entry {
/** @hardif_neigh: hardif neighbor scheduled for metric update */
struct batadv_hardif_neigh_node *hardif_neigh;
/** @list: list node for metric_queue */
struct list_head list;
};
/**
* batadv_v_elp_start_timer() - restart timer for ELP periodic work
* @hard_iface: the interface for which the timer has to be reset
*/
static void batadv_v_elp_start_timer(struct batadv_hard_iface *hard_iface)
{
unsigned int msecs;
msecs = READ_ONCE(hard_iface->bat_v.elp_interval) - BATADV_JITTER;
msecs += get_random_u32_below(2 * BATADV_JITTER);
queue_delayed_work(batadv_event_workqueue, &hard_iface->bat_v.elp_wq,
msecs_to_jiffies(msecs));
}
/**
* batadv_v_elp_get_throughput() - get the throughput towards a neighbour
* @neigh: the neighbour for which the throughput has to be obtained
* @pthroughput: calculated throughput towards the given neighbour in multiples
* of 100kpbs (a value of '1' equals 0.1Mbps, '10' equals 1Mbps, etc).
*
* Return: true when value behind @pthroughput was set
*/
static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh,
u32 *pthroughput)
{
struct batadv_hard_iface *hard_iface = neigh->if_incoming;
struct net_device *mesh_iface = hard_iface->mesh_iface;
struct ethtool_link_ksettings link_settings;
struct net_device *real_netdev;
struct station_info sinfo;
u32 wifi_flags;
u32 throughput;
int ret;
/* don't query throughput when no longer associated with any
* batman-adv interface
*/
if (!mesh_iface)
return false;
/* if the user specified a customised value for this interface, then
* return it directly
*/
throughput = READ_ONCE(hard_iface->bat_v.throughput_override);
if (throughput != 0) {
*pthroughput = throughput;
return true;
}
/* if this is a wireless device, then ask its throughput through
* cfg80211 API
*/
wifi_flags = batadv_hardif_get_wifi_flags(hard_iface);
if (batadv_is_wifi(wifi_flags)) {
if (!batadv_is_cfg80211(wifi_flags))
/* unsupported WiFi driver version */
goto default_throughput;
/* only use rtnl_trylock because the elp worker will be cancelled while
* the rntl_lock is held. the disable_delayed_work_sync() would otherwise
* wait forever when the elp work_item was started and it is then also
* trying to rtnl_lock
*/
if (!rtnl_trylock())
return false;
real_netdev = __batadv_get_real_netdev(hard_iface->net_dev);
rtnl_unlock();
if (!real_netdev)
goto default_throughput;
ret = cfg80211_get_station(real_netdev, neigh->addr, &sinfo);
if (!ret) {
/* free the TID stats immediately */
cfg80211_sinfo_release_content(&sinfo);
}
dev_put(real_netdev);
if (ret == -ENOENT) {
/* Node is not associated anymore! It would be
* possible to delete this neighbor. For now set
* the throughput metric to 0.
*/
*pthroughput = 0;
return true;
}
if (ret)
goto default_throughput;
if (sinfo.filled & BIT(NL80211_STA_INFO_EXPECTED_THROUGHPUT)) {
*pthroughput = sinfo.expected_throughput / 100;
return true;
}
/* try to estimate the expected throughput based on reported tx
* rates
*/
if (sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE)) {
*pthroughput = cfg80211_calculate_bitrate(&sinfo.txrate) / 3;
return true;
}
goto default_throughput;
}
/* only use rtnl_trylock because the elp worker will be cancelled while
* the rntl_lock is held. the disable_delayed_work_sync() would otherwise
* wait forever when the elp work_item was started and it is then also
* trying to rtnl_lock
*/
if (!rtnl_trylock())
return false;
/* if not a wifi interface, check if this device provides data via
* ethtool (e.g. an Ethernet adapter)
*/
ret = __ethtool_get_link_ksettings(hard_iface->net_dev, &link_settings);
rtnl_unlock();
if (ret == 0) {
/* link characteristics might change over time */
if (link_settings.base.duplex == DUPLEX_FULL)
hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX;
else
hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX;
throughput = link_settings.base.speed;
if (throughput && throughput != SPEED_UNKNOWN) {
*pthroughput = throughput * 10;
return true;
}
}
default_throughput:
if (!(hard_iface->bat_v.flags & BATADV_WARNING_DEFAULT)) {
batadv_info(mesh_iface,
"WiFi driver or ethtool info does not provide information about link speeds on interface %s, therefore defaulting to hardcoded throughput values of %u.%1u Mbps. Consider overriding the throughput manually or checking your driver.\n",
hard_iface->net_dev->name,
BATADV_THROUGHPUT_DEFAULT_VALUE / 10,
BATADV_THROUGHPUT_DEFAULT_VALUE % 10);
hard_iface->bat_v.flags |= BATADV_WARNING_DEFAULT;
}
/* if none of the above cases apply, return the base_throughput */
*pthroughput = BATADV_THROUGHPUT_DEFAULT_VALUE;
return true;
}
/**
* batadv_v_elp_throughput_metric_update() - worker updating the throughput
* metric of a single hop neighbour
* @neigh: the neighbour to probe
*/
static void
batadv_v_elp_throughput_metric_update(struct batadv_hardif_neigh_node *neigh)
{
u32 throughput;
bool valid;
valid = batadv_v_elp_get_throughput(neigh, &throughput);
if (!valid)
return;
ewma_throughput_add(&neigh->bat_v.throughput, throughput);
}
/**
* batadv_v_elp_wifi_neigh_probe() - send link probing packets to a neighbour
* @neigh: the neighbour to probe
*
* Sends a predefined number of unicast wifi packets to a given neighbour in
* order to trigger the throughput estimation on this link by the RC algorithm.
* Packets are sent only if there is not enough payload unicast traffic towards
* this neighbour..
*
* Return: True on success and false in case of error during skb preparation.
*/
static bool
batadv_v_elp_wifi_neigh_probe(struct batadv_hardif_neigh_node *neigh)
{
struct batadv_hard_iface *hard_iface = neigh->if_incoming;
struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
unsigned long last_tx_diff;
struct sk_buff *skb;
int probe_len, i;
int elp_skb_len;
/* this probing routine is for Wifi neighbours only */
if (!batadv_is_wifi_hardif(hard_iface))
return true;
/* probe the neighbor only if no unicast packets have been sent
* to it in the last 100 milliseconds: this is the rate control
* algorithm sampling interval (minstrel). In this way, if not
* enough traffic has been sent to the neighbor, batman-adv can
* generate 2 probe packets and push the RC algorithm to perform
* the sampling
*/
last_tx_diff = jiffies_to_msecs(jiffies - neigh->bat_v.last_unicast_tx);
if (last_tx_diff <= BATADV_ELP_PROBE_MAX_TX_DIFF)
return true;
probe_len = max_t(int, sizeof(struct batadv_elp_packet),
BATADV_ELP_MIN_PROBE_SIZE);
for (i = 0; i < BATADV_ELP_PROBES_PER_NODE; i++) {
elp_skb_len = hard_iface->bat_v.elp_skb->len;
skb = skb_copy_expand(hard_iface->bat_v.elp_skb, 0,
probe_len - elp_skb_len,
GFP_ATOMIC);
if (!skb)
return false;
/* Tell the skb to get as big as the allocated space (we want
* the packet to be exactly of that size to make the link
* throughput estimation effective.
*/
skb_put_zero(skb, probe_len - hard_iface->bat_v.elp_skb->len);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Sending unicast (probe) ELP packet on interface %s to %pM\n",
hard_iface->net_dev->name, neigh->addr);
batadv_send_skb_packet(skb, hard_iface, neigh->addr);
}
return true;
}
/**
* batadv_v_elp_periodic_work() - ELP periodic task per interface
* @work: work queue item
*
* Emits broadcast ELP messages in regular intervals.
*/
static void batadv_v_elp_periodic_work(struct work_struct *work)
{
struct batadv_v_metric_queue_entry *metric_entry;
struct batadv_v_metric_queue_entry *metric_safe;
struct batadv_hardif_neigh_node *hardif_neigh;
struct batadv_hard_iface *hard_iface;
struct batadv_hard_iface_bat_v *bat_v;
struct batadv_elp_packet *elp_packet;
struct list_head metric_queue;
struct batadv_priv *bat_priv;
struct sk_buff *skb;
u32 elp_interval;
bat_v = container_of(work, struct batadv_hard_iface_bat_v, elp_wq.work);
hard_iface = container_of(bat_v, struct batadv_hard_iface, bat_v);
bat_priv = netdev_priv(hard_iface->mesh_iface);
if (READ_ONCE(bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
goto out;
/* we are in the process of shutting this interface down */
if (hard_iface->if_status == BATADV_IF_NOT_IN_USE ||
hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
goto out;
/* the interface was enabled but may not be ready yet */
if (hard_iface->if_status != BATADV_IF_ACTIVE)
goto restart_timer;
skb = skb_copy(hard_iface->bat_v.elp_skb, GFP_ATOMIC);
if (!skb)
goto restart_timer;
elp_packet = (struct batadv_elp_packet *)skb->data;
elp_packet->seqno = htonl(atomic_read(&hard_iface->bat_v.elp_seqno));
elp_interval = READ_ONCE(hard_iface->bat_v.elp_interval);
elp_packet->elp_interval = htonl(elp_interval);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Sending broadcast ELP packet on interface %s, seqno %u\n",
hard_iface->net_dev->name,
atomic_read(&hard_iface->bat_v.elp_seqno));
batadv_send_broadcast_skb(skb, hard_iface);
atomic_inc(&hard_iface->bat_v.elp_seqno);
INIT_LIST_HEAD(&metric_queue);
/* The throughput metric is updated on each sent packet. This way, if a
* node is dead and no longer sends packets, batman-adv is still able to
* react timely to its death.
*
* The throughput metric is updated by following these steps:
* 1) if the hard_iface is wifi => send a number of unicast ELPs for
* probing/sampling to each neighbor
* 2) update the throughput metric value of each neighbor (note that the
* value retrieved in this step might be 100ms old because the
* probing packets at point 1) could still be in the HW queue)
*/
rcu_read_lock();
hlist_for_each_entry_rcu(hardif_neigh, &hard_iface->neigh_list, list) {
if (!batadv_v_elp_wifi_neigh_probe(hardif_neigh))
/* if something goes wrong while probing, better to stop
* sending packets immediately and reschedule the task
*/
break;
if (!kref_get_unless_zero(&hardif_neigh->refcount))
continue;
/* Reading the estimated throughput from cfg80211 is a task that
* may sleep and that is not allowed in an rcu protected
* context. Therefore add it to metric_queue and process it
* outside rcu protected context.
*/
metric_entry = kzalloc_obj(*metric_entry, GFP_ATOMIC);
if (!metric_entry) {
batadv_hardif_neigh_put(hardif_neigh);
continue;
}
metric_entry->hardif_neigh = hardif_neigh;
list_add(&metric_entry->list, &metric_queue);
}
rcu_read_unlock();
list_for_each_entry_safe(metric_entry, metric_safe, &metric_queue, list) {
batadv_v_elp_throughput_metric_update(metric_entry->hardif_neigh);
batadv_hardif_neigh_put(metric_entry->hardif_neigh);
list_del(&metric_entry->list);
kfree(metric_entry);
}
restart_timer:
batadv_v_elp_start_timer(hard_iface);
out:
return;
}
/**
* batadv_v_elp_iface_enable() - setup the ELP interface private resources
* @hard_iface: interface for which the data has to be prepared
*
* Return: 0 on success or a -ENOMEM in case of failure.
*/
int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface)
{
static const size_t tvlv_padding = sizeof(__be32);
struct batadv_elp_packet *elp_packet;
unsigned char *elp_buff;
u32 random_seqno;
size_t size;
int res = -ENOMEM;
size = ETH_HLEN + NET_IP_ALIGN + BATADV_ELP_HLEN + tvlv_padding;
hard_iface->bat_v.elp_skb = dev_alloc_skb(size);
if (!hard_iface->bat_v.elp_skb)
goto out;
skb_reserve(hard_iface->bat_v.elp_skb, ETH_HLEN + NET_IP_ALIGN);
elp_buff = skb_put_zero(hard_iface->bat_v.elp_skb,
BATADV_ELP_HLEN + tvlv_padding);
elp_packet = (struct batadv_elp_packet *)elp_buff;
elp_packet->packet_type = BATADV_ELP;
elp_packet->version = BATADV_COMPAT_VERSION;
/* randomize initial seqno to avoid collision */
get_random_bytes(&random_seqno, sizeof(random_seqno));
atomic_set(&hard_iface->bat_v.elp_seqno, random_seqno);
/* assume full-duplex by default */
hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX;
/* warn the user (again) if there is no throughput data is available */
hard_iface->bat_v.flags &= ~BATADV_WARNING_DEFAULT;
if (batadv_is_wifi_hardif(hard_iface))
hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX;
INIT_DELAYED_WORK(&hard_iface->bat_v.elp_wq,
batadv_v_elp_periodic_work);
batadv_v_elp_start_timer(hard_iface);
res = 0;
out:
return res;
}
/**
* batadv_v_elp_iface_disable() - release ELP interface private resources
* @hard_iface: interface for which the resources have to be released
*/
void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface)
{
disable_delayed_work_sync(&hard_iface->bat_v.elp_wq);
dev_kfree_skb(hard_iface->bat_v.elp_skb);
hard_iface->bat_v.elp_skb = NULL;
}
/**
* batadv_v_elp_iface_activate() - update the ELP buffer belonging to the given
* hard-interface
* @primary_iface: the new primary interface
* @hard_iface: interface holding the to-be-updated buffer
*/
void batadv_v_elp_iface_activate(struct batadv_hard_iface *primary_iface,
struct batadv_hard_iface *hard_iface)
{
struct batadv_elp_packet *elp_packet;
struct sk_buff *skb;
if (!hard_iface->bat_v.elp_skb)
return;
skb = hard_iface->bat_v.elp_skb;
elp_packet = (struct batadv_elp_packet *)skb->data;
ether_addr_copy(elp_packet->orig,
primary_iface->net_dev->dev_addr);
}
/**
* batadv_v_elp_primary_iface_set() - change internal data to reflect the new
* primary interface
* @primary_iface: the new primary interface
*/
void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface)
{
struct batadv_hard_iface *hard_iface;
struct list_head *iter;
/* update orig field of every elp iface belonging to this mesh */
rcu_read_lock();
netdev_for_each_lower_private_rcu(primary_iface->mesh_iface, hard_iface, iter)
batadv_v_elp_iface_activate(primary_iface, hard_iface);
rcu_read_unlock();
}
/**
* batadv_v_elp_neigh_update() - update an ELP neighbour node
* @bat_priv: the bat priv with all the mesh interface information
* @neigh_addr: the neighbour interface address
* @if_incoming: the interface the packet was received through
* @elp_packet: the received ELP packet
*
* Updates the ELP neighbour node state with the data received within the new
* ELP packet.
*/
static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv,
u8 *neigh_addr,
struct batadv_hard_iface *if_incoming,
struct batadv_elp_packet *elp_packet)
{
struct batadv_neigh_node *neigh;
struct batadv_orig_node *orig_neigh;
struct batadv_hardif_neigh_node *hardif_neigh;
s32 seqno_diff;
s32 elp_latest_seqno;
orig_neigh = batadv_v_ogm_orig_get(bat_priv, elp_packet->orig);
if (!orig_neigh)
return;
neigh = batadv_neigh_node_get_or_create(orig_neigh,
if_incoming, neigh_addr);
if (!neigh)
goto orig_free;
hardif_neigh = batadv_hardif_neigh_get(if_incoming, neigh_addr);
if (!hardif_neigh)
goto neigh_free;
elp_latest_seqno = hardif_neigh->bat_v.elp_latest_seqno;
seqno_diff = ntohl(elp_packet->seqno) - elp_latest_seqno;
/* known or older sequence numbers are ignored. However always adopt
* if the router seems to have been restarted.
*/
if (seqno_diff < 1 && seqno_diff > -BATADV_ELP_MAX_AGE)
goto hardif_free;
neigh->last_seen = jiffies;
hardif_neigh->last_seen = jiffies;
hardif_neigh->bat_v.elp_latest_seqno = ntohl(elp_packet->seqno);
hardif_neigh->bat_v.elp_interval = ntohl(elp_packet->elp_interval);
hardif_free:
batadv_hardif_neigh_put(hardif_neigh);
neigh_free:
batadv_neigh_node_put(neigh);
orig_free:
batadv_orig_node_put(orig_neigh);
}
/**
* batadv_v_elp_packet_recv() - main ELP packet handler
* @skb: the received packet
* @if_incoming: the interface this packet was received through
*
* Return: NET_RX_SUCCESS and consumes the skb if the packet was properly
* processed or NET_RX_DROP in case of failure.
*/
int batadv_v_elp_packet_recv(struct sk_buff *skb,
struct batadv_hard_iface *if_incoming)
{
struct batadv_priv *bat_priv = netdev_priv(if_incoming->mesh_iface);
struct batadv_elp_packet *elp_packet;
struct batadv_hard_iface *primary_if;
struct ethhdr *ethhdr;
bool res;
int ret = NET_RX_DROP;
res = batadv_check_management_packet(skb, if_incoming, BATADV_ELP_HLEN);
if (!res)
goto free_skb;
ethhdr = eth_hdr(skb);
if (batadv_is_my_mac(bat_priv, ethhdr->h_source))
goto free_skb;
/* did we receive a B.A.T.M.A.N. V ELP packet on an interface
* that does not have B.A.T.M.A.N. V ELP enabled ?
*/
if (strcmp(bat_priv->algo_ops->name, "BATMAN_V") != 0)
goto free_skb;
elp_packet = (struct batadv_elp_packet *)skb->data;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Received ELP packet from %pM seqno %u ORIG: %pM\n",
ethhdr->h_source, ntohl(elp_packet->seqno),
elp_packet->orig);
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if)
goto free_skb;
batadv_v_elp_neigh_update(bat_priv, ethhdr->h_source, if_incoming,
elp_packet);
ret = NET_RX_SUCCESS;
batadv_hardif_put(primary_if);
free_skb:
if (ret == NET_RX_SUCCESS)
consume_skb(skb);
else
kfree_skb(skb);
return ret;
}
+22
View File
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Linus Lüssing, Marek Lindner
*/
#ifndef _NET_BATMAN_ADV_BAT_V_ELP_H_
#define _NET_BATMAN_ADV_BAT_V_ELP_H_
#include "main.h"
#include <linux/skbuff.h>
int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface);
void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface);
void batadv_v_elp_iface_activate(struct batadv_hard_iface *primary_iface,
struct batadv_hard_iface *hard_iface);
void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface);
int batadv_v_elp_packet_recv(struct sk_buff *skb,
struct batadv_hard_iface *if_incoming);
#endif /* _NET_BATMAN_ADV_BAT_V_ELP_H_ */
File diff suppressed because it is too large Load Diff
+27
View File
@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Antonio Quartulli
*/
#ifndef _NET_BATMAN_ADV_BAT_V_OGM_H_
#define _NET_BATMAN_ADV_BAT_V_OGM_H_
#include "main.h"
#include <linux/skbuff.h>
#include <linux/types.h>
#include <linux/workqueue.h>
int batadv_v_ogm_init(struct batadv_priv *bat_priv);
void batadv_v_ogm_free(struct batadv_priv *bat_priv);
void batadv_v_ogm_aggr_work(struct work_struct *work);
int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface);
void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface);
struct batadv_orig_node *batadv_v_ogm_orig_get(struct batadv_priv *bat_priv,
const u8 *addr);
void batadv_v_ogm_primary_iface_set(struct batadv_hard_iface *primary_iface);
int batadv_v_ogm_packet_recv(struct sk_buff *skb,
struct batadv_hard_iface *if_incoming);
#endif /* _NET_BATMAN_ADV_BAT_V_OGM_H_ */
+89
View File
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Simon Wunderlich, Marek Lindner
*/
#include "bitarray.h"
#include "main.h"
#include <linux/bitmap.h>
#include "log.h"
/* shift the packet array by n places. */
static void batadv_bitmap_shift_left(unsigned long *seq_bits, s32 n)
{
if (n <= 0 || n >= BATADV_TQ_LOCAL_WINDOW_SIZE)
return;
bitmap_shift_left(seq_bits, seq_bits, n, BATADV_TQ_LOCAL_WINDOW_SIZE);
}
/**
* batadv_bit_get_packet() - receive and process one packet within the sequence
* number window
* @priv: the bat priv with all the mesh interface information
* @seq_bits: pointer to the sequence number bitmap of received packets
* @seq_num_diff: difference between the current/received sequence number and
* the last sequence number
* @set_mark: whether this packet should be marked in seq_bits
*
* Return: true if the window was moved (either new or very old),
* false if the window was not moved/shifted.
*/
bool batadv_bit_get_packet(void *priv, unsigned long *seq_bits,
s32 seq_num_diff, int set_mark)
{
struct batadv_priv *bat_priv = priv;
/* sequence number is slightly older. We already got a sequence number
* higher than this one, so we just mark it.
*/
if (seq_num_diff <= 0 && seq_num_diff > -BATADV_TQ_LOCAL_WINDOW_SIZE) {
if (set_mark)
batadv_set_bit(seq_bits, -seq_num_diff);
return false;
}
/* sequence number is slightly newer, so we shift the window and
* set the mark if required
*/
if (seq_num_diff > 0 && seq_num_diff < BATADV_TQ_LOCAL_WINDOW_SIZE) {
batadv_bitmap_shift_left(seq_bits, seq_num_diff);
if (set_mark)
batadv_set_bit(seq_bits, 0);
return true;
}
/* sequence number is much newer, probably missed a lot of packets */
if (seq_num_diff >= BATADV_TQ_LOCAL_WINDOW_SIZE &&
seq_num_diff < BATADV_EXPECTED_SEQNO_RANGE) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"We missed a lot of packets (%i) !\n",
seq_num_diff - 1);
bitmap_zero(seq_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
if (set_mark)
batadv_set_bit(seq_bits, 0);
return true;
}
/* received a much older packet. The other host either restarted
* or the old packet got delayed somewhere in the network. The
* packet should be dropped without calling this function if the
* seqno window is protected.
*
* seq_num_diff <= -BATADV_TQ_LOCAL_WINDOW_SIZE
* or
* seq_num_diff >= BATADV_EXPECTED_SEQNO_RANGE
*/
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Other host probably restarted!\n");
bitmap_zero(seq_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
if (set_mark)
batadv_set_bit(seq_bits, 0);
return true;
}
+56
View File
@@ -0,0 +1,56 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Simon Wunderlich, Marek Lindner
*/
#ifndef _NET_BATMAN_ADV_BITARRAY_H_
#define _NET_BATMAN_ADV_BITARRAY_H_
#include "main.h"
#include <linux/bitops.h>
#include <linux/compiler.h>
#include <linux/stddef.h>
#include <linux/types.h>
/**
* batadv_test_bit() - check if bit is set in the current window
*
* @seq_bits: pointer to the sequence number bitmap of received packets
* @last_seqno: latest sequence number in seq_bits
* @curr_seqno: sequence number to test for
*
* Return: true if the corresponding bit in the given seq_bits indicates true
* and curr_seqno is within range of last_seqno. Otherwise returns false.
*/
static inline bool batadv_test_bit(const unsigned long *seq_bits,
u32 last_seqno, u32 curr_seqno)
{
s32 diff;
diff = last_seqno - curr_seqno;
if (diff < 0 || diff >= BATADV_TQ_LOCAL_WINDOW_SIZE)
return false;
return test_bit(diff, seq_bits) != 0;
}
/**
* batadv_set_bit() - Turn corresponding bit on, so we can remember that we got
* the packet
* @seq_bits: bitmap of the packet receive window
* @n: relative sequence number of newly received packet
*/
static inline void batadv_set_bit(unsigned long *seq_bits, s32 n)
{
/* if too old, just drop it */
if (n < 0 || n >= BATADV_TQ_LOCAL_WINDOW_SIZE)
return;
set_bit(n, seq_bits); /* turn the position on */
}
bool batadv_bit_get_packet(void *priv, unsigned long *seq_bits,
s32 seq_num_diff, int set_mark);
#endif /* _NET_BATMAN_ADV_BITARRAY_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,132 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Simon Wunderlich
*/
#ifndef _NET_BATMAN_ADV_BLA_H_
#define _NET_BATMAN_ADV_BLA_H_
#include "main.h"
#include <linux/compiler.h>
#include <linux/netdevice.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/stddef.h>
#include <linux/types.h>
/**
* batadv_bla_is_loopdetect_mac() - check if the mac address is from a loop
* detect frame sent by bridge loop avoidance
* @mac: mac address to check
*
* Return: true if it looks like a loop detect frame
* (mac starts with BA:BE), false otherwise
*/
static inline bool batadv_bla_is_loopdetect_mac(const uint8_t *mac)
{
if (mac[0] == 0xba && mac[1] == 0xbe)
return true;
return false;
}
#ifdef CONFIG_BATMAN_ADV_BLA
bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
unsigned short vid, int packet_type);
bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
unsigned short vid);
bool batadv_bla_is_backbone_gw(struct sk_buff *skb,
struct batadv_orig_node *orig_node,
int hdr_size);
int batadv_bla_claim_dump(struct sk_buff *msg, struct netlink_callback *cb);
int batadv_bla_backbone_dump(struct sk_buff *msg, struct netlink_callback *cb);
bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, u8 *orig,
unsigned short vid);
bool batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
struct sk_buff *skb);
void batadv_bla_update_orig_address(struct batadv_priv *bat_priv,
struct batadv_hard_iface *primary_if,
struct batadv_hard_iface *oldif);
void batadv_bla_status_update(struct net_device *net_dev);
int batadv_bla_init(struct batadv_priv *bat_priv);
void batadv_bla_free(struct batadv_priv *bat_priv);
#ifdef CONFIG_BATMAN_ADV_DAT
bool batadv_bla_check_claim(struct batadv_priv *bat_priv, u8 *addr,
unsigned short vid);
#endif
#define BATADV_BLA_CRC_INIT 0
#else /* ifdef CONFIG_BATMAN_ADV_BLA */
static inline bool batadv_bla_rx(struct batadv_priv *bat_priv,
struct sk_buff *skb, unsigned short vid,
int packet_type)
{
return false;
}
static inline bool batadv_bla_tx(struct batadv_priv *bat_priv,
struct sk_buff *skb, unsigned short vid)
{
return false;
}
static inline bool batadv_bla_is_backbone_gw(struct sk_buff *skb,
struct batadv_orig_node *orig_node,
int hdr_size)
{
return false;
}
static inline bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv,
u8 *orig, unsigned short vid)
{
return false;
}
static inline bool
batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
struct sk_buff *skb)
{
return false;
}
static inline void
batadv_bla_update_orig_address(struct batadv_priv *bat_priv,
struct batadv_hard_iface *primary_if,
struct batadv_hard_iface *oldif)
{
}
static inline int batadv_bla_init(struct batadv_priv *bat_priv)
{
return 1;
}
static inline void batadv_bla_free(struct batadv_priv *bat_priv)
{
}
static inline int batadv_bla_claim_dump(struct sk_buff *msg,
struct netlink_callback *cb)
{
return -EOPNOTSUPP;
}
static inline int batadv_bla_backbone_dump(struct sk_buff *msg,
struct netlink_callback *cb)
{
return -EOPNOTSUPP;
}
static inline
bool batadv_bla_check_claim(struct batadv_priv *bat_priv, u8 *addr,
unsigned short vid)
{
return true;
}
#endif /* ifdef CONFIG_BATMAN_ADV_BLA */
#endif /* ifndef _NET_BATMAN_ADV_BLA_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,186 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Antonio Quartulli
*/
#ifndef _NET_BATMAN_ADV_DISTRIBUTED_ARP_TABLE_H_
#define _NET_BATMAN_ADV_DISTRIBUTED_ARP_TABLE_H_
#include "main.h"
#include <linux/compiler.h>
#include <linux/netdevice.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/types.h>
#include <uapi/linux/batadv_packet.h>
#include "originator.h"
#ifdef CONFIG_BATMAN_ADV_DAT
/* BATADV_DAT_ADDR_MAX - maximum address value in the DHT space */
#define BATADV_DAT_ADDR_MAX ((batadv_dat_addr_t)~(batadv_dat_addr_t)0)
void batadv_dat_status_update(struct net_device *net_dev);
bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
struct sk_buff *skb);
bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
struct sk_buff *skb, int hdr_size);
void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
struct sk_buff *skb);
bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
struct sk_buff *skb, int hdr_size);
void batadv_dat_snoop_outgoing_dhcp_ack(struct batadv_priv *bat_priv,
struct sk_buff *skb,
__be16 proto,
unsigned short vid);
void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv,
struct sk_buff *skb, int hdr_size);
bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
struct batadv_forw_packet *forw_packet);
/**
* batadv_dat_init_orig_node_addr() - assign a DAT address to the orig_node
* @orig_node: the node to assign the DAT address to
*/
static inline void
batadv_dat_init_orig_node_addr(struct batadv_orig_node *orig_node)
{
u32 addr;
addr = batadv_choose_orig(orig_node->orig, BATADV_DAT_ADDR_MAX);
orig_node->dat_addr = (batadv_dat_addr_t)addr;
}
/**
* batadv_dat_init_own_addr() - assign a DAT address to the node itself
* @bat_priv: the bat priv with all the mesh interface information
* @primary_if: a pointer to the primary interface
*/
static inline void
batadv_dat_init_own_addr(struct batadv_priv *bat_priv,
struct batadv_hard_iface *primary_if)
{
u32 addr;
addr = batadv_choose_orig(primary_if->net_dev->dev_addr,
BATADV_DAT_ADDR_MAX);
bat_priv->dat.addr = (batadv_dat_addr_t)addr;
}
int batadv_dat_init(struct batadv_priv *bat_priv);
void batadv_dat_free(struct batadv_priv *bat_priv);
int batadv_dat_cache_dump(struct sk_buff *msg, struct netlink_callback *cb);
/**
* batadv_dat_inc_counter() - increment the correct DAT packet counter
* @bat_priv: the bat priv with all the mesh interface information
* @subtype: the 4addr subtype of the packet to be counted
*
* Updates the ethtool statistics for the received packet if it is a DAT subtype
*/
static inline void batadv_dat_inc_counter(struct batadv_priv *bat_priv,
u8 subtype)
{
switch (subtype) {
case BATADV_P_DAT_DHT_GET:
batadv_inc_counter(bat_priv,
BATADV_CNT_DAT_GET_RX);
break;
case BATADV_P_DAT_DHT_PUT:
batadv_inc_counter(bat_priv,
BATADV_CNT_DAT_PUT_RX);
break;
}
}
#else
static inline void batadv_dat_status_update(struct net_device *net_dev)
{
}
static inline bool
batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
struct sk_buff *skb)
{
return false;
}
static inline bool
batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
struct sk_buff *skb, int hdr_size)
{
return false;
}
static inline bool
batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
struct sk_buff *skb)
{
return false;
}
static inline bool
batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
struct sk_buff *skb, int hdr_size)
{
return false;
}
static inline void
batadv_dat_snoop_outgoing_dhcp_ack(struct batadv_priv *bat_priv,
struct sk_buff *skb, __be16 proto,
unsigned short vid)
{
}
static inline void
batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv,
struct sk_buff *skb, int hdr_size)
{
}
static inline bool
batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
struct batadv_forw_packet *forw_packet)
{
return false;
}
static inline void
batadv_dat_init_orig_node_addr(struct batadv_orig_node *orig_node)
{
}
static inline void batadv_dat_init_own_addr(struct batadv_priv *bat_priv,
struct batadv_hard_iface *iface)
{
}
static inline int batadv_dat_init(struct batadv_priv *bat_priv)
{
return 0;
}
static inline void batadv_dat_free(struct batadv_priv *bat_priv)
{
}
static inline int
batadv_dat_cache_dump(struct sk_buff *msg, struct netlink_callback *cb)
{
return -EOPNOTSUPP;
}
static inline void batadv_dat_inc_counter(struct batadv_priv *bat_priv,
u8 subtype)
{
}
#endif /* CONFIG_BATMAN_ADV_DAT */
#endif /* _NET_BATMAN_ADV_DISTRIBUTED_ARP_TABLE_H_ */
+626
View File
@@ -0,0 +1,626 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Martin Hundebøll <martin@hundeboll.net>
*/
#include "fragmentation.h"
#include "main.h"
#include <linux/atomic.h>
#include <linux/bug.h>
#include <linux/byteorder/generic.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/gfp.h>
#include <linux/if_ether.h>
#include <linux/jiffies.h>
#include <linux/lockdep.h>
#include <linux/minmax.h>
#include <linux/netdevice.h>
#include <linux/overflow.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <uapi/linux/batadv_packet.h>
#include "hard-interface.h"
#include "originator.h"
#include "send.h"
/**
* batadv_frag_clear_chain() - delete entries in the fragment buffer chain
* @head: head of chain with entries.
* @dropped: whether the chain is cleared because all fragments are dropped
*
* Free fragments in the passed hlist. Should be called with appropriate lock.
*/
static void batadv_frag_clear_chain(struct hlist_head *head, bool dropped)
{
struct batadv_frag_list_entry *entry;
struct hlist_node *node;
hlist_for_each_entry_safe(entry, node, head, list) {
hlist_del(&entry->list);
if (dropped)
kfree_skb(entry->skb);
else
consume_skb(entry->skb);
kfree(entry);
}
}
/**
* batadv_frag_purge_orig() - free fragments associated to an orig
* @orig_node: originator to free fragments from
* @check_cb: optional function to tell if an entry should be purged
*/
void batadv_frag_purge_orig(struct batadv_orig_node *orig_node,
bool (*check_cb)(struct batadv_frag_table_entry *))
{
struct batadv_frag_table_entry *chain;
u8 i;
for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) {
chain = &orig_node->fragments[i];
spin_lock_bh(&chain->lock);
if (!check_cb || check_cb(chain)) {
batadv_frag_clear_chain(&chain->fragment_list, true);
chain->size = 0;
}
spin_unlock_bh(&chain->lock);
}
}
/**
* batadv_frag_size_limit() - maximum possible size of packet to be fragmented
*
* Return: the maximum size of payload that can be fragmented.
*/
static size_t batadv_frag_size_limit(void)
{
size_t limit = BATADV_FRAG_MAX_FRAG_SIZE;
limit -= sizeof(struct batadv_frag_packet);
limit *= BATADV_FRAG_MAX_FRAGMENTS;
return limit;
}
/**
* batadv_frag_init_chain() - check and prepare fragment chain for new fragment
* @chain: chain in fragments table to init
* @seqno: sequence number of the received fragment
*
* Make chain ready for a fragment with sequence number "seqno". Delete existing
* entries if they have an "old" sequence number.
*
* Caller must hold chain->lock.
*
* Return: true if chain is empty and the caller can just insert the new
* fragment without searching for the right position.
*/
static bool batadv_frag_init_chain(struct batadv_frag_table_entry *chain,
u16 seqno)
{
lockdep_assert_held(&chain->lock);
if (chain->seqno == seqno)
return false;
if (!hlist_empty(&chain->fragment_list))
batadv_frag_clear_chain(&chain->fragment_list, true);
chain->size = 0;
chain->seqno = seqno;
return true;
}
/**
* batadv_frag_insert_packet() - insert a fragment into a fragment chain
* @orig_node: originator that the fragment was received from
* @skb: skb to insert
* @chain_out: list head to attach complete chains of fragments to
*
* Insert a new fragment into the reverse ordered chain in the right table
* entry. The hash table entry is cleared if "old" fragments exist in it.
*
* Return: true if skb is buffered, false on error. If the chain has all the
* fragments needed to merge the packet, the chain is moved to the passed head
* to avoid locking the chain in the table.
*/
static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node,
struct sk_buff *skb,
struct hlist_head *chain_out)
{
struct batadv_frag_table_entry *chain;
struct batadv_frag_list_entry *frag_entry_new = NULL, *frag_entry_curr;
struct batadv_frag_list_entry *frag_entry_last = NULL;
struct batadv_frag_packet *frag_packet;
u8 bucket;
u16 seqno, hdr_size = sizeof(struct batadv_frag_packet);
bool overflow = false;
bool ret = false;
size_t data_len;
/* Linearize packet to avoid linearizing 16 packets in a row when doing
* the later merge. Non-linear merge should be added to remove this
* linearization.
*/
if (skb_linearize(skb) < 0)
goto err;
frag_packet = (struct batadv_frag_packet *)skb->data;
data_len = skb->len - hdr_size;
seqno = ntohs(frag_packet->seqno);
bucket = seqno % BATADV_FRAG_BUFFER_COUNT;
frag_entry_new = kmalloc_obj(*frag_entry_new, GFP_ATOMIC);
if (!frag_entry_new)
goto err;
frag_entry_new->skb = skb;
frag_entry_new->no = frag_packet->no;
/* Select entry in the "chain table" and delete any prior fragments
* with another sequence number. batadv_frag_init_chain() returns true,
* if the list is empty at return.
*/
chain = &orig_node->fragments[bucket];
spin_lock_bh(&chain->lock);
if (batadv_frag_init_chain(chain, seqno)) {
hlist_add_head(&frag_entry_new->list, &chain->fragment_list);
chain->size = data_len;
chain->timestamp = jiffies;
chain->total_size = ntohs(frag_packet->total_size);
ret = true;
goto out;
}
/* Find the position for the new fragment. */
hlist_for_each_entry(frag_entry_curr, &chain->fragment_list, list) {
/* Drop packet if fragment already exists. */
if (frag_entry_curr->no == frag_entry_new->no)
goto err_unlock;
/* Order fragments from highest to lowest. */
if (frag_entry_curr->no < frag_entry_new->no) {
hlist_add_before(&frag_entry_new->list,
&frag_entry_curr->list);
if (check_add_overflow(chain->size, data_len,
&chain->size))
overflow = true;
chain->timestamp = jiffies;
ret = true;
goto out;
}
/* store current entry because it could be the last in list */
frag_entry_last = frag_entry_curr;
}
/* Reached the end of the list, so insert after 'frag_entry_last'. */
if (likely(frag_entry_last)) {
hlist_add_behind(&frag_entry_new->list, &frag_entry_last->list);
if (check_add_overflow(chain->size, data_len, &chain->size))
overflow = true;
chain->timestamp = jiffies;
ret = true;
}
out:
if (overflow || chain->size > batadv_frag_size_limit() ||
chain->total_size != ntohs(frag_packet->total_size) ||
chain->total_size > batadv_frag_size_limit()) {
/* Clear chain if total size of either the list or the packet
* exceeds the maximum size of one merged packet. Don't allow
* packets to have different total_size.
*/
batadv_frag_clear_chain(&chain->fragment_list, true);
chain->size = 0;
} else if (ntohs(frag_packet->total_size) == chain->size) {
/* All fragments received. Hand over chain to caller. */
hlist_move_list(&chain->fragment_list, chain_out);
chain->size = 0;
}
err_unlock:
spin_unlock_bh(&chain->lock);
err:
if (!ret) {
kfree(frag_entry_new);
kfree_skb(skb);
}
return ret;
}
/**
* batadv_frag_merge_packets() - merge a chain of fragments
* @chain: head of chain with fragments
*
* Expand the first skb in the chain and copy the content of the remaining
* skb's into the expanded one. After doing so, clear the chain.
*
* Return: the merged skb or NULL on error.
*/
static struct sk_buff *
batadv_frag_merge_packets(struct hlist_head *chain)
{
struct batadv_frag_packet *packet;
struct batadv_frag_list_entry *entry;
struct sk_buff *skb_out;
int size, hdr_size = sizeof(struct batadv_frag_packet);
bool dropped = false;
/* Remove first entry, as this is the destination for the rest of the
* fragments.
*/
entry = hlist_entry(chain->first, struct batadv_frag_list_entry, list);
hlist_del(&entry->list);
skb_out = entry->skb;
kfree(entry);
packet = (struct batadv_frag_packet *)skb_out->data;
size = ntohs(packet->total_size) + hdr_size;
/* Make room for the rest of the fragments. */
if (pskb_expand_head(skb_out, 0, size - skb_out->len, GFP_ATOMIC) < 0) {
kfree_skb(skb_out);
skb_out = NULL;
dropped = true;
goto free;
}
/* Move the existing MAC header to just before the payload. (Override
* the fragment header.)
*/
skb_pull(skb_out, hdr_size);
skb_out->ip_summed = CHECKSUM_NONE;
memmove(skb_out->data - ETH_HLEN, skb_mac_header(skb_out), ETH_HLEN);
skb_set_mac_header(skb_out, -ETH_HLEN);
skb_reset_network_header(skb_out);
skb_reset_transport_header(skb_out);
/* Copy the payload of the each fragment into the last skb */
hlist_for_each_entry(entry, chain, list) {
size = entry->skb->len - hdr_size;
skb_put_data(skb_out, entry->skb->data + hdr_size, size);
}
free:
/* Locking is not needed, because 'chain' is not part of any orig. */
batadv_frag_clear_chain(chain, dropped);
return skb_out;
}
/**
* batadv_skb_is_frag() - check if newly merged skb contains unicast fragment
* @skb: newly merged skb
*
* Return: true if the newly merged skb is of type BATADV_UNICAST_FRAG, false
* otherwise
*/
static bool batadv_skb_is_frag(struct sk_buff *skb)
{
struct batadv_ogm_packet *batadv_ogm_packet;
/* packet should hold at least type and version */
if (unlikely(!pskb_may_pull(skb, 2)))
return false;
batadv_ogm_packet = (struct batadv_ogm_packet *)skb->data;
if (batadv_ogm_packet->version != BATADV_COMPAT_VERSION)
return false;
if (batadv_ogm_packet->packet_type != BATADV_UNICAST_FRAG)
return false;
return true;
}
/**
* batadv_frag_skb_buffer() - buffer fragment for later merge
* @skb: skb to buffer
* @orig_node_src: originator that the skb is received from
*
* Add fragment to buffer and merge fragments if possible.
*
* There are three possible outcomes: 1) Packet is merged: Return true and
* set *skb to merged packet; 2) Packet is buffered: Return true and set *skb
* to NULL; 3) Error: Return false and free skb.
*
* Return: true when the packet is merged or buffered, false when skb is not
* used.
*/
bool batadv_frag_skb_buffer(struct sk_buff **skb,
struct batadv_orig_node *orig_node_src)
{
struct sk_buff *skb_out = NULL;
struct hlist_head head = HLIST_HEAD_INIT;
bool ret = false;
/* Add packet to buffer and table entry if merge is possible. */
if (!batadv_frag_insert_packet(orig_node_src, *skb, &head))
goto out_err;
/* Leave if more fragments are needed to merge. */
if (hlist_empty(&head))
goto out;
skb_out = batadv_frag_merge_packets(&head);
if (!skb_out)
goto out_err;
/* fragment in fragment is not allowed. otherwise it is possible
* to exhaust the stack when receiving a matryoshka-style
* "fragments in a fragment packet"
*/
if (batadv_skb_is_frag(skb_out)) {
kfree_skb(skb_out);
skb_out = NULL;
goto out_err;
}
out:
ret = true;
out_err:
*skb = skb_out;
return ret;
}
/**
* batadv_frag_skb_fwd() - forward fragments that would exceed MTU when merged
* @skb: skb to forward
* @recv_if: interface that the skb is received on
* @orig_node_src: originator that the skb is received from
* @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and
* NET_RX_DROP when it was dropped; only valid when true is returned
*
* Look up the next-hop of the fragments payload and check if the merged packet
* will exceed the MTU towards the next-hop. If so, the fragment is forwarded
* without merging it.
*
* Return: true if the fragment is consumed/forwarded, false otherwise.
*/
bool batadv_frag_skb_fwd(struct sk_buff *skb,
struct batadv_hard_iface *recv_if,
struct batadv_orig_node *orig_node_src,
int *rx_result)
{
struct batadv_priv *bat_priv = netdev_priv(recv_if->mesh_iface);
struct batadv_neigh_node *neigh_node = NULL;
struct batadv_frag_packet *packet;
u16 total_size;
bool ret = false;
packet = (struct batadv_frag_packet *)skb->data;
neigh_node = batadv_orig_to_router(bat_priv, packet->dest, recv_if);
if (!neigh_node)
goto out;
/* Forward the fragment, if the merged packet would be too big to
* be assembled.
*/
total_size = ntohs(packet->total_size);
if (total_size > neigh_node->if_incoming->net_dev->mtu) {
if (packet->ttl < 2) {
kfree_skb(skb);
*rx_result = NET_RX_DROP;
ret = true;
goto out;
}
if (skb_cow(skb, ETH_HLEN) < 0) {
kfree_skb(skb);
*rx_result = NET_RX_DROP;
ret = true;
goto out;
}
packet = (struct batadv_frag_packet *)skb->data;
batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD);
batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES,
skb->len + ETH_HLEN);
packet->ttl--;
batadv_send_unicast_skb(skb, neigh_node);
*rx_result = NET_RX_SUCCESS;
ret = true;
}
out:
batadv_neigh_node_put(neigh_node);
return ret;
}
/**
* batadv_frag_create() - create a fragment from skb
* @net_dev: outgoing device for fragment
* @skb: skb to create fragment from
* @frag_head: header to use in new fragment
* @fragment_size: size of new fragment
*
* Split the passed skb into two fragments: A new one with size matching the
* passed mtu and the old one with the rest. The new skb contains data from the
* tail of the old skb.
*
* Return: the new fragment, NULL on error.
*/
static struct sk_buff *batadv_frag_create(struct net_device *net_dev,
struct sk_buff *skb,
struct batadv_frag_packet *frag_head,
unsigned int fragment_size)
{
unsigned int ll_reserved = LL_RESERVED_SPACE(net_dev);
unsigned int tailroom = net_dev->needed_tailroom;
struct sk_buff *skb_fragment;
unsigned int header_size = sizeof(*frag_head);
unsigned int mtu = fragment_size + header_size;
skb_fragment = dev_alloc_skb(ll_reserved + mtu + tailroom);
if (!skb_fragment)
goto err;
skb_fragment->priority = skb->priority;
/* Eat the last mtu-bytes of the skb */
skb_reserve(skb_fragment, ll_reserved + header_size);
skb_split(skb, skb_fragment, skb->len - fragment_size);
/* Add the header */
skb_push(skb_fragment, header_size);
memcpy(skb_fragment->data, frag_head, header_size);
err:
return skb_fragment;
}
/**
* batadv_frag_send_packet() - create up to 16 fragments from the passed skb
* @skb: skb to create fragments from
* @orig_node: final destination of the created fragments
* @neigh_node: next-hop of the created fragments
*
* Return: the netdev tx status or a negative errno code on a failure
*/
int batadv_frag_send_packet(struct sk_buff *skb,
struct batadv_orig_node *orig_node,
struct batadv_neigh_node *neigh_node)
{
struct net_device *net_dev = neigh_node->if_incoming->net_dev;
struct batadv_priv *bat_priv;
struct batadv_hard_iface *primary_if = NULL;
struct batadv_frag_packet frag_header;
struct sk_buff *skb_fragment;
unsigned int mtu = net_dev->mtu;
unsigned int header_size = sizeof(frag_header);
unsigned int max_fragment_size, num_fragments;
int ret;
/* To avoid merge and refragmentation at next-hops we never send
* fragments larger than BATADV_FRAG_MAX_FRAG_SIZE
*/
mtu = min_t(unsigned int, mtu, BATADV_FRAG_MAX_FRAG_SIZE);
max_fragment_size = mtu - header_size;
if (skb->len == 0 || max_fragment_size == 0) {
ret = -EINVAL;
goto free_skb;
}
num_fragments = (skb->len - 1) / max_fragment_size + 1;
max_fragment_size = (skb->len - 1) / num_fragments + 1;
/* Don't even try to fragment, if we need more than 16 fragments */
if (num_fragments > BATADV_FRAG_MAX_FRAGMENTS) {
ret = -EAGAIN;
goto free_skb;
}
bat_priv = orig_node->bat_priv;
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if) {
ret = -EINVAL;
goto free_skb;
}
/* GRO might have added fragments to the fragment list instead of
* frags[]. But this is not handled by skb_split and must be
* linearized to avoid incorrect length information after all
* batman-adv fragments were created and submitted to the
* hard-interface
*/
if (skb_has_frag_list(skb) && __skb_linearize(skb)) {
ret = -ENOMEM;
goto put_primary_if;
}
/* Create one header to be copied to all fragments */
frag_header.packet_type = BATADV_UNICAST_FRAG;
frag_header.version = BATADV_COMPAT_VERSION;
frag_header.ttl = BATADV_TTL;
frag_header.seqno = htons(atomic_inc_return(&bat_priv->frag_seqno));
frag_header.reserved = 0;
frag_header.no = 0;
frag_header.total_size = htons(skb->len);
/* skb->priority values from 256->263 are magic values to
* directly indicate a specific 802.1d priority. This is used
* to allow 802.1d priority to be passed directly in from VLAN
* tags, etc.
*/
if (skb->priority >= 256 && skb->priority <= 263)
frag_header.priority = skb->priority - 256;
else
frag_header.priority = 0;
ether_addr_copy(frag_header.orig, primary_if->net_dev->dev_addr);
ether_addr_copy(frag_header.dest, orig_node->orig);
/* Eat and send fragments from the tail of skb */
while (skb->len > max_fragment_size) {
/* The initial check in this function should cover this case */
if (unlikely(frag_header.no == BATADV_FRAG_MAX_FRAGMENTS - 1)) {
ret = -EINVAL;
goto put_primary_if;
}
skb_fragment = batadv_frag_create(net_dev, skb, &frag_header,
max_fragment_size);
if (!skb_fragment) {
ret = -ENOMEM;
goto put_primary_if;
}
batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX);
batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES,
skb_fragment->len + ETH_HLEN);
ret = batadv_send_unicast_skb(skb_fragment, neigh_node);
if (ret != NET_XMIT_SUCCESS) {
ret = NET_XMIT_DROP;
goto put_primary_if;
}
frag_header.no++;
}
/* make sure that there is at least enough head for the fragmentation
* and ethernet headers
*/
ret = skb_cow_head(skb, ETH_HLEN + header_size);
if (ret < 0)
goto put_primary_if;
skb_push(skb, header_size);
memcpy(skb->data, &frag_header, header_size);
/* Send the last fragment */
batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX);
batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES,
skb->len + ETH_HLEN);
ret = batadv_send_unicast_skb(skb, neigh_node);
/* skb was consumed */
skb = NULL;
put_primary_if:
batadv_hardif_put(primary_if);
free_skb:
kfree_skb(skb);
return ret;
}
@@ -0,0 +1,45 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Martin Hundebøll <martin@hundeboll.net>
*/
#ifndef _NET_BATMAN_ADV_FRAGMENTATION_H_
#define _NET_BATMAN_ADV_FRAGMENTATION_H_
#include "main.h"
#include <linux/compiler.h>
#include <linux/list.h>
#include <linux/skbuff.h>
#include <linux/stddef.h>
#include <linux/types.h>
void batadv_frag_purge_orig(struct batadv_orig_node *orig,
bool (*check_cb)(struct batadv_frag_table_entry *));
bool batadv_frag_skb_fwd(struct sk_buff *skb,
struct batadv_hard_iface *recv_if,
struct batadv_orig_node *orig_node_src,
int *rx_result);
bool batadv_frag_skb_buffer(struct sk_buff **skb,
struct batadv_orig_node *orig_node);
int batadv_frag_send_packet(struct sk_buff *skb,
struct batadv_orig_node *orig_node,
struct batadv_neigh_node *neigh_node);
/**
* batadv_frag_check_entry() - check if a list of fragments has timed out
* @frags_entry: table entry to check
*
* Return: true if the frags entry has timed out, false otherwise.
*/
static inline bool
batadv_frag_check_entry(struct batadv_frag_table_entry *frags_entry)
{
if (!hlist_empty(&frags_entry->fragment_list) &&
batadv_has_timed_out(frags_entry->timestamp, BATADV_FRAG_TIMEOUT))
return true;
return false;
}
#endif /* _NET_BATMAN_ADV_FRAGMENTATION_H_ */
+765
View File
@@ -0,0 +1,765 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner
*/
#include "gateway_client.h"
#include "main.h"
#include <linux/atomic.h>
#include <linux/bug.h>
#include <linux/byteorder/generic.h>
#include <linux/container_of.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/gfp.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/netdevice.h>
#include <linux/netlink.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/sprintf.h>
#include <linux/stddef.h>
#include <linux/udp.h>
#include <uapi/linux/batadv_packet.h>
#include <uapi/linux/batman_adv.h>
#include "hard-interface.h"
#include "log.h"
#include "netlink.h"
#include "originator.h"
#include "routing.h"
#include "translation-table.h"
/* These are the offsets of the "hw type" and "hw address length" in the dhcp
* packet starting at the beginning of the dhcp header
*/
#define BATADV_DHCP_HTYPE_OFFSET 1
#define BATADV_DHCP_HLEN_OFFSET 2
/* Value of htype representing Ethernet */
#define BATADV_DHCP_HTYPE_ETHERNET 0x01
/* This is the offset of the "chaddr" field in the dhcp packet starting at the
* beginning of the dhcp header
*/
#define BATADV_DHCP_CHADDR_OFFSET 28
/**
* batadv_gw_node_release() - release gw_node from lists and queue for free
* after rcu grace period
* @ref: kref pointer of the gw_node
*/
void batadv_gw_node_release(struct kref *ref)
{
struct batadv_gw_node *gw_node;
gw_node = container_of(ref, struct batadv_gw_node, refcount);
batadv_orig_node_put(gw_node->orig_node);
kfree_rcu(gw_node, rcu);
}
/**
* batadv_gw_get_selected_gw_node() - Get currently selected gateway
* @bat_priv: the bat priv with all the mesh interface information
*
* Return: selected gateway (with increased refcnt), NULL on errors
*/
struct batadv_gw_node *
batadv_gw_get_selected_gw_node(struct batadv_priv *bat_priv)
{
struct batadv_gw_node *gw_node;
rcu_read_lock();
gw_node = rcu_dereference(bat_priv->gw.curr_gw);
if (!gw_node)
goto out;
if (!kref_get_unless_zero(&gw_node->refcount))
gw_node = NULL;
out:
rcu_read_unlock();
return gw_node;
}
/**
* batadv_gw_get_selected_orig() - Get originator of currently selected gateway
* @bat_priv: the bat priv with all the mesh interface information
*
* Return: orig_node of selected gateway (with increased refcnt), NULL on errors
*/
struct batadv_orig_node *
batadv_gw_get_selected_orig(struct batadv_priv *bat_priv)
{
struct batadv_gw_node *gw_node;
struct batadv_orig_node *orig_node = NULL;
gw_node = batadv_gw_get_selected_gw_node(bat_priv);
if (!gw_node)
goto out;
rcu_read_lock();
orig_node = gw_node->orig_node;
if (!orig_node)
goto unlock;
if (!kref_get_unless_zero(&orig_node->refcount))
orig_node = NULL;
unlock:
rcu_read_unlock();
out:
batadv_gw_node_put(gw_node);
return orig_node;
}
static void batadv_gw_select(struct batadv_priv *bat_priv,
struct batadv_gw_node *new_gw_node)
{
struct batadv_gw_node *curr_gw_node;
spin_lock_bh(&bat_priv->gw.list_lock);
if (new_gw_node)
kref_get(&new_gw_node->refcount);
curr_gw_node = rcu_replace_pointer(bat_priv->gw.curr_gw, new_gw_node,
true);
batadv_gw_node_put(curr_gw_node);
spin_unlock_bh(&bat_priv->gw.list_lock);
}
/**
* batadv_gw_reselect() - force a gateway reselection
* @bat_priv: the bat priv with all the mesh interface information
*
* Set a flag to remind the GW component to perform a new gateway reselection.
* However this function does not ensure that the current gateway is going to be
* deselected. The reselection mechanism may elect the same gateway once again.
*
* This means that invoking batadv_gw_reselect() does not guarantee a gateway
* change and therefore a uevent is not necessarily expected.
*/
void batadv_gw_reselect(struct batadv_priv *bat_priv)
{
atomic_set(&bat_priv->gw.reselect, 1);
}
/**
* batadv_gw_check_client_stop() - check if client mode has been switched off
* @bat_priv: the bat priv with all the mesh interface information
*
* This function assumes the caller has checked that the gw state *is actually
* changing*. This function is not supposed to be called when there is no state
* change.
*/
void batadv_gw_check_client_stop(struct batadv_priv *bat_priv)
{
struct batadv_gw_node *curr_gw;
if (READ_ONCE(bat_priv->gw.mode) != BATADV_GW_MODE_CLIENT)
return;
curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
if (!curr_gw)
return;
/* deselect the current gateway so that next time that client mode is
* enabled a proper GW_ADD event can be sent
*/
batadv_gw_select(bat_priv, NULL);
/* if batman-adv is switching the gw client mode off and a gateway was
* already selected, send a DEL uevent
*/
batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_DEL, NULL);
batadv_gw_node_put(curr_gw);
}
/**
* batadv_gw_election() - Elect the best gateway
* @bat_priv: the bat priv with all the mesh interface information
*/
void batadv_gw_election(struct batadv_priv *bat_priv)
{
struct batadv_gw_node *curr_gw = NULL;
struct batadv_gw_node *next_gw = NULL;
struct batadv_neigh_node *router = NULL;
struct batadv_neigh_ifinfo *router_ifinfo = NULL;
char gw_addr[18] = { '\0' };
if (READ_ONCE(bat_priv->gw.mode) != BATADV_GW_MODE_CLIENT)
goto out;
if (!bat_priv->algo_ops->gw.get_best_gw_node)
goto out;
curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
if (atomic_xchg(&bat_priv->gw.reselect, 0) == 0 && curr_gw)
goto out;
/* if gw.reselect is set to 1 it means that a previous call to
* gw.is_eligible() said that we have a new best GW, therefore it can
* now be picked from the list and selected
*/
next_gw = bat_priv->algo_ops->gw.get_best_gw_node(bat_priv);
if (curr_gw == next_gw)
goto out;
if (next_gw) {
sprintf(gw_addr, "%pM", next_gw->orig_node->orig);
router = batadv_orig_router_get(next_gw->orig_node,
BATADV_IF_DEFAULT);
if (!router) {
batadv_gw_reselect(bat_priv);
goto out;
}
router_ifinfo = batadv_neigh_ifinfo_get(router,
BATADV_IF_DEFAULT);
if (!router_ifinfo) {
batadv_gw_reselect(bat_priv);
goto out;
}
}
if (curr_gw && !next_gw) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Removing selected gateway - no gateway in range\n");
batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_DEL,
NULL);
} else if (!curr_gw && next_gw) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Adding route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n",
next_gw->orig_node->orig,
next_gw->bandwidth_down / 10,
next_gw->bandwidth_down % 10,
next_gw->bandwidth_up / 10,
next_gw->bandwidth_up % 10,
router_ifinfo->bat_iv.tq_avg);
batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_ADD,
gw_addr);
} else {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Changing route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n",
next_gw->orig_node->orig,
next_gw->bandwidth_down / 10,
next_gw->bandwidth_down % 10,
next_gw->bandwidth_up / 10,
next_gw->bandwidth_up % 10,
router_ifinfo->bat_iv.tq_avg);
batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_CHANGE,
gw_addr);
}
batadv_gw_select(bat_priv, next_gw);
out:
batadv_gw_node_put(curr_gw);
batadv_gw_node_put(next_gw);
batadv_neigh_node_put(router);
batadv_neigh_ifinfo_put(router_ifinfo);
}
/**
* batadv_gw_check_election() - Elect orig node as best gateway when eligible
* @bat_priv: the bat priv with all the mesh interface information
* @orig_node: orig node which is to be checked
*/
void batadv_gw_check_election(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node)
{
struct batadv_orig_node *curr_gw_orig;
/* abort immediately if the routing algorithm does not support gateway
* election
*/
if (!bat_priv->algo_ops->gw.is_eligible)
return;
curr_gw_orig = batadv_gw_get_selected_orig(bat_priv);
if (!curr_gw_orig)
goto reselect;
/* this node already is the gateway */
if (curr_gw_orig == orig_node)
goto out;
if (!bat_priv->algo_ops->gw.is_eligible(bat_priv, curr_gw_orig,
orig_node))
goto out;
reselect:
batadv_gw_reselect(bat_priv);
out:
batadv_orig_node_put(curr_gw_orig);
}
/**
* batadv_gw_node_add() - add gateway node to list of available gateways
* @bat_priv: the bat priv with all the mesh interface information
* @orig_node: originator announcing gateway capabilities
* @gateway: announced bandwidth information
*
* Has to be called with the appropriate locks being acquired
* (gw.list_lock).
*/
static void batadv_gw_node_add(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
struct batadv_tvlv_gateway_data *gateway)
{
struct batadv_gw_node *gw_node;
lockdep_assert_held(&bat_priv->gw.list_lock);
if (gateway->bandwidth_down == 0)
return;
gw_node = kzalloc_obj(*gw_node, GFP_ATOMIC);
if (!gw_node)
return;
kref_init(&gw_node->refcount);
INIT_HLIST_NODE(&gw_node->list);
kref_get(&orig_node->refcount);
gw_node->orig_node = orig_node;
gw_node->bandwidth_down = ntohl(gateway->bandwidth_down);
gw_node->bandwidth_up = ntohl(gateway->bandwidth_up);
kref_get(&gw_node->refcount);
hlist_add_head_rcu(&gw_node->list, &bat_priv->gw.gateway_list);
bat_priv->gw.generation++;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Found new gateway %pM -> gw bandwidth: %u.%u/%u.%u MBit\n",
orig_node->orig,
ntohl(gateway->bandwidth_down) / 10,
ntohl(gateway->bandwidth_down) % 10,
ntohl(gateway->bandwidth_up) / 10,
ntohl(gateway->bandwidth_up) % 10);
/* don't return reference to new gw_node */
batadv_gw_node_put(gw_node);
}
/**
* batadv_gw_node_get() - retrieve gateway node from list of available gateways
* @bat_priv: the bat priv with all the mesh interface information
* @orig_node: originator announcing gateway capabilities
*
* Return: gateway node if found or NULL otherwise.
*/
struct batadv_gw_node *batadv_gw_node_get(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node)
{
struct batadv_gw_node *gw_node_tmp, *gw_node = NULL;
rcu_read_lock();
hlist_for_each_entry_rcu(gw_node_tmp, &bat_priv->gw.gateway_list,
list) {
if (gw_node_tmp->orig_node != orig_node)
continue;
if (!kref_get_unless_zero(&gw_node_tmp->refcount))
continue;
gw_node = gw_node_tmp;
break;
}
rcu_read_unlock();
return gw_node;
}
/**
* batadv_gw_node_update() - update list of available gateways with changed
* bandwidth information
* @bat_priv: the bat priv with all the mesh interface information
* @orig_node: originator announcing gateway capabilities
* @gateway: announced bandwidth information
*/
void batadv_gw_node_update(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
struct batadv_tvlv_gateway_data *gateway)
{
struct batadv_gw_node *gw_node, *curr_gw = NULL;
spin_lock_bh(&bat_priv->gw.list_lock);
gw_node = batadv_gw_node_get(bat_priv, orig_node);
if (!gw_node) {
batadv_gw_node_add(bat_priv, orig_node, gateway);
spin_unlock_bh(&bat_priv->gw.list_lock);
goto out;
}
spin_unlock_bh(&bat_priv->gw.list_lock);
if (gw_node->bandwidth_down == ntohl(gateway->bandwidth_down) &&
gw_node->bandwidth_up == ntohl(gateway->bandwidth_up))
goto out;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Gateway bandwidth of originator %pM changed from %u.%u/%u.%u MBit to %u.%u/%u.%u MBit\n",
orig_node->orig,
gw_node->bandwidth_down / 10,
gw_node->bandwidth_down % 10,
gw_node->bandwidth_up / 10,
gw_node->bandwidth_up % 10,
ntohl(gateway->bandwidth_down) / 10,
ntohl(gateway->bandwidth_down) % 10,
ntohl(gateway->bandwidth_up) / 10,
ntohl(gateway->bandwidth_up) % 10);
gw_node->bandwidth_down = ntohl(gateway->bandwidth_down);
gw_node->bandwidth_up = ntohl(gateway->bandwidth_up);
if (ntohl(gateway->bandwidth_down) == 0) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Gateway %pM removed from gateway list\n",
orig_node->orig);
/* Note: We don't need a NULL check here, since curr_gw never
* gets dereferenced.
*/
spin_lock_bh(&bat_priv->gw.list_lock);
if (!hlist_unhashed(&gw_node->list)) {
hlist_del_init_rcu(&gw_node->list);
batadv_gw_node_put(gw_node);
bat_priv->gw.generation++;
}
spin_unlock_bh(&bat_priv->gw.list_lock);
curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
if (gw_node == curr_gw)
batadv_gw_reselect(bat_priv);
batadv_gw_node_put(curr_gw);
}
out:
batadv_gw_node_put(gw_node);
}
/**
* batadv_gw_node_delete() - Remove orig_node from gateway list
* @bat_priv: the bat priv with all the mesh interface information
* @orig_node: orig node which is currently in process of being removed
*/
void batadv_gw_node_delete(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node)
{
struct batadv_tvlv_gateway_data gateway;
gateway.bandwidth_down = 0;
gateway.bandwidth_up = 0;
batadv_gw_node_update(bat_priv, orig_node, &gateway);
}
/**
* batadv_gw_node_free() - Free gateway information from mesh interface
* @bat_priv: the bat priv with all the mesh interface information
*/
void batadv_gw_node_free(struct batadv_priv *bat_priv)
{
struct batadv_gw_node *curr_gw;
struct batadv_gw_node *gw_node;
struct hlist_node *node_tmp;
spin_lock_bh(&bat_priv->gw.list_lock);
curr_gw = rcu_replace_pointer(bat_priv->gw.curr_gw, NULL, true);
batadv_gw_node_put(curr_gw);
hlist_for_each_entry_safe(gw_node, node_tmp,
&bat_priv->gw.gateway_list, list) {
hlist_del_init_rcu(&gw_node->list);
batadv_gw_node_put(gw_node);
bat_priv->gw.generation++;
}
spin_unlock_bh(&bat_priv->gw.list_lock);
}
/**
* batadv_gw_dump() - Dump gateways into a message
* @msg: Netlink message to dump into
* @cb: Control block containing additional options
*
* Return: Error code, or length of message
*/
int batadv_gw_dump(struct sk_buff *msg, struct netlink_callback *cb)
{
struct batadv_hard_iface *primary_if = NULL;
struct net_device *mesh_iface;
struct batadv_priv *bat_priv;
int ret;
mesh_iface = batadv_netlink_get_meshif(cb);
if (IS_ERR(mesh_iface))
return PTR_ERR(mesh_iface);
bat_priv = netdev_priv(mesh_iface);
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
ret = -ENOENT;
goto out;
}
if (!bat_priv->algo_ops->gw.dump) {
ret = -EOPNOTSUPP;
goto out;
}
bat_priv->algo_ops->gw.dump(msg, cb, bat_priv);
ret = msg->len;
out:
batadv_hardif_put(primary_if);
dev_put(mesh_iface);
return ret;
}
/**
* batadv_gw_dhcp_recipient_get() - check if a packet is a DHCP message
* @skb: the packet to check
* @header_len: a pointer to the batman-adv header size
* @chaddr: buffer where the client address will be stored. Valid
* only if the function returns BATADV_DHCP_TO_CLIENT
*
* This function may re-allocate the data buffer of the skb passed as argument.
*
* Return:
* - BATADV_DHCP_NO if the packet is not a dhcp message or if there was an error
* while parsing it
* - BATADV_DHCP_TO_SERVER if this is a message going to the DHCP server
* - BATADV_DHCP_TO_CLIENT if this is a message going to a DHCP client
*/
enum batadv_dhcp_recipient
batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len,
u8 *chaddr)
{
enum batadv_dhcp_recipient ret = BATADV_DHCP_NO;
struct ethhdr *ethhdr;
struct iphdr *iphdr;
struct ipv6hdr *ipv6hdr;
struct udphdr *udphdr;
struct vlan_ethhdr *vhdr;
int chaddr_offset;
__be16 proto;
u8 *p;
/* check for ethernet header */
if (!pskb_may_pull(skb, *header_len + ETH_HLEN))
return BATADV_DHCP_NO;
ethhdr = eth_hdr(skb);
proto = ethhdr->h_proto;
*header_len += ETH_HLEN;
/* check for initial vlan header */
if (proto == htons(ETH_P_8021Q)) {
if (!pskb_may_pull(skb, *header_len + VLAN_HLEN))
return BATADV_DHCP_NO;
vhdr = vlan_eth_hdr(skb);
proto = vhdr->h_vlan_encapsulated_proto;
*header_len += VLAN_HLEN;
}
/* check for ip header */
switch (proto) {
case htons(ETH_P_IP):
if (!pskb_may_pull(skb, *header_len + sizeof(*iphdr)))
return BATADV_DHCP_NO;
iphdr = (struct iphdr *)(skb->data + *header_len);
*header_len += iphdr->ihl * 4;
/* check for udp header */
if (iphdr->protocol != IPPROTO_UDP)
return BATADV_DHCP_NO;
break;
case htons(ETH_P_IPV6):
if (!pskb_may_pull(skb, *header_len + sizeof(*ipv6hdr)))
return BATADV_DHCP_NO;
ipv6hdr = (struct ipv6hdr *)(skb->data + *header_len);
*header_len += sizeof(*ipv6hdr);
/* check for udp header */
if (ipv6hdr->nexthdr != IPPROTO_UDP)
return BATADV_DHCP_NO;
break;
default:
return BATADV_DHCP_NO;
}
if (!pskb_may_pull(skb, *header_len + sizeof(*udphdr)))
return BATADV_DHCP_NO;
udphdr = (struct udphdr *)(skb->data + *header_len);
*header_len += sizeof(*udphdr);
/* check for bootp port */
switch (proto) {
case htons(ETH_P_IP):
if (udphdr->dest == htons(67))
ret = BATADV_DHCP_TO_SERVER;
else if (udphdr->source == htons(67))
ret = BATADV_DHCP_TO_CLIENT;
break;
case htons(ETH_P_IPV6):
if (udphdr->dest == htons(547))
ret = BATADV_DHCP_TO_SERVER;
else if (udphdr->source == htons(547))
ret = BATADV_DHCP_TO_CLIENT;
break;
}
chaddr_offset = *header_len + BATADV_DHCP_CHADDR_OFFSET;
/* store the client address if the message is going to a client */
if (ret == BATADV_DHCP_TO_CLIENT) {
if (!pskb_may_pull(skb, chaddr_offset + ETH_ALEN))
return BATADV_DHCP_NO;
/* check if the DHCP packet carries an Ethernet DHCP */
p = skb->data + *header_len + BATADV_DHCP_HTYPE_OFFSET;
if (*p != BATADV_DHCP_HTYPE_ETHERNET)
return BATADV_DHCP_NO;
/* check if the DHCP packet carries a valid Ethernet address */
p = skb->data + *header_len + BATADV_DHCP_HLEN_OFFSET;
if (*p != ETH_ALEN)
return BATADV_DHCP_NO;
ether_addr_copy(chaddr, skb->data + chaddr_offset);
}
return ret;
}
/**
* batadv_gw_out_of_range() - check if the dhcp request destination is the best
* gateway
* @bat_priv: the bat priv with all the mesh interface information
* @skb: the outgoing packet
*
* Check if the skb is a DHCP request and if it is sent to the current best GW
* server. Due to topology changes it may be the case that the GW server
* previously selected is not the best one anymore.
*
* This call might reallocate skb data.
* Must be invoked only when the DHCP packet is going TO a DHCP SERVER.
*
* Return: true if the packet destination is unicast and it is not the best gw,
* false otherwise.
*/
bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
struct sk_buff *skb)
{
struct batadv_neigh_node *neigh_curr = NULL;
struct batadv_neigh_node *neigh_old = NULL;
struct batadv_orig_node *orig_dst_node = NULL;
struct batadv_gw_node *gw_node = NULL;
struct batadv_gw_node *curr_gw = NULL;
struct batadv_neigh_ifinfo *curr_ifinfo, *old_ifinfo;
struct ethhdr *ethhdr;
bool out_of_range = false;
u8 curr_tq_avg;
unsigned short vid;
vid = batadv_get_vid(skb, 0);
ethhdr = (struct ethhdr *)skb->data;
if (is_multicast_ether_addr(ethhdr->h_dest))
goto out;
orig_dst_node = batadv_transtable_search(bat_priv, ethhdr->h_source,
ethhdr->h_dest, vid);
if (!orig_dst_node)
goto out;
gw_node = batadv_gw_node_get(bat_priv, orig_dst_node);
if (!gw_node)
goto out;
switch (READ_ONCE(bat_priv->gw.mode)) {
case BATADV_GW_MODE_SERVER:
/* If we are a GW then we are our best GW. We can artificially
* set the tq towards ourself as the maximum value
*/
curr_tq_avg = BATADV_TQ_MAX_VALUE;
break;
case BATADV_GW_MODE_CLIENT:
curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
if (!curr_gw)
goto out;
/* packet is going to our gateway */
if (curr_gw->orig_node == orig_dst_node)
goto out;
/* If the dhcp packet has been sent to a different gw,
* we have to evaluate whether the old gw is still
* reliable enough
*/
neigh_curr = batadv_find_router(bat_priv, curr_gw->orig_node,
NULL);
if (!neigh_curr)
goto out;
curr_ifinfo = batadv_neigh_ifinfo_get(neigh_curr,
BATADV_IF_DEFAULT);
if (!curr_ifinfo)
goto out;
curr_tq_avg = curr_ifinfo->bat_iv.tq_avg;
batadv_neigh_ifinfo_put(curr_ifinfo);
break;
case BATADV_GW_MODE_OFF:
default:
goto out;
}
neigh_old = batadv_find_router(bat_priv, orig_dst_node, NULL);
if (!neigh_old)
goto out;
old_ifinfo = batadv_neigh_ifinfo_get(neigh_old, BATADV_IF_DEFAULT);
if (!old_ifinfo)
goto out;
if ((curr_tq_avg - old_ifinfo->bat_iv.tq_avg) > BATADV_GW_THRESHOLD)
out_of_range = true;
batadv_neigh_ifinfo_put(old_ifinfo);
out:
batadv_orig_node_put(orig_dst_node);
batadv_gw_node_put(curr_gw);
batadv_gw_node_put(gw_node);
batadv_neigh_node_put(neigh_old);
batadv_neigh_node_put(neigh_curr);
return out_of_range;
}
@@ -0,0 +1,55 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner
*/
#ifndef _NET_BATMAN_ADV_GATEWAY_CLIENT_H_
#define _NET_BATMAN_ADV_GATEWAY_CLIENT_H_
#include "main.h"
#include <linux/kref.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/types.h>
#include <uapi/linux/batadv_packet.h>
void batadv_gw_check_client_stop(struct batadv_priv *bat_priv);
void batadv_gw_reselect(struct batadv_priv *bat_priv);
void batadv_gw_election(struct batadv_priv *bat_priv);
struct batadv_orig_node *
batadv_gw_get_selected_orig(struct batadv_priv *bat_priv);
void batadv_gw_check_election(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node);
void batadv_gw_node_update(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
struct batadv_tvlv_gateway_data *gateway);
void batadv_gw_node_delete(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node);
void batadv_gw_node_free(struct batadv_priv *bat_priv);
void batadv_gw_node_release(struct kref *ref);
struct batadv_gw_node *
batadv_gw_get_selected_gw_node(struct batadv_priv *bat_priv);
int batadv_gw_dump(struct sk_buff *msg, struct netlink_callback *cb);
bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, struct sk_buff *skb);
enum batadv_dhcp_recipient
batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len,
u8 *chaddr);
struct batadv_gw_node *batadv_gw_node_get(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node);
/**
* batadv_gw_node_put() - decrement the gw_node refcounter and possibly release
* it
* @gw_node: gateway node to free
*/
static inline void batadv_gw_node_put(struct batadv_gw_node *gw_node)
{
if (!gw_node)
return;
kref_put(&gw_node->refcount, batadv_gw_node_release);
}
#endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */
+114
View File
@@ -0,0 +1,114 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner
*/
#include "gateway_common.h"
#include "main.h"
#include <linux/byteorder/generic.h>
#include <linux/compiler.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include <uapi/linux/batadv_packet.h>
#include <uapi/linux/batman_adv.h>
#include "gateway_client.h"
#include "tvlv.h"
/**
* batadv_gw_tvlv_container_update() - update the gw tvlv container after
* gateway setting change
* @bat_priv: the bat priv with all the mesh interface information
*/
void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv)
{
struct batadv_tvlv_gateway_data gw;
enum batadv_gw_modes gw_mode;
u32 down, up;
gw_mode = READ_ONCE(bat_priv->gw.mode);
switch (gw_mode) {
case BATADV_GW_MODE_OFF:
case BATADV_GW_MODE_CLIENT:
batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1);
break;
case BATADV_GW_MODE_SERVER:
down = READ_ONCE(bat_priv->gw.bandwidth_down);
up = READ_ONCE(bat_priv->gw.bandwidth_up);
gw.bandwidth_down = htonl(down);
gw.bandwidth_up = htonl(up);
batadv_tvlv_container_register(bat_priv, BATADV_TVLV_GW, 1,
&gw, sizeof(gw));
break;
}
}
/**
* batadv_gw_tvlv_ogm_handler_v1() - process incoming gateway tvlv container
* @bat_priv: the bat priv with all the mesh interface information
* @orig: the orig_node of the ogm
* @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
* @tvlv_value: tvlv buffer containing the gateway data
* @tvlv_value_len: tvlv buffer length
*/
static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig,
u8 flags,
void *tvlv_value, u16 tvlv_value_len)
{
struct batadv_tvlv_gateway_data gateway, *gateway_ptr;
/* only fetch the tvlv value if the handler wasn't called via the
* CIFNOTFND flag and if there is data to fetch
*/
if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND ||
tvlv_value_len < sizeof(gateway)) {
gateway.bandwidth_down = 0;
gateway.bandwidth_up = 0;
} else {
gateway_ptr = tvlv_value;
gateway.bandwidth_down = gateway_ptr->bandwidth_down;
gateway.bandwidth_up = gateway_ptr->bandwidth_up;
if (gateway.bandwidth_down == 0 ||
gateway.bandwidth_up == 0) {
gateway.bandwidth_down = 0;
gateway.bandwidth_up = 0;
}
}
batadv_gw_node_update(bat_priv, orig, &gateway);
/* restart gateway selection */
if (gateway.bandwidth_down != 0 &&
READ_ONCE(bat_priv->gw.mode) == BATADV_GW_MODE_CLIENT)
batadv_gw_check_election(bat_priv, orig);
}
/**
* batadv_gw_init() - initialise the gateway handling internals
* @bat_priv: the bat priv with all the mesh interface information
*/
void batadv_gw_init(struct batadv_priv *bat_priv)
{
if (bat_priv->algo_ops->gw.init_sel_class)
bat_priv->algo_ops->gw.init_sel_class(bat_priv);
else
WRITE_ONCE(bat_priv->gw.sel_class, 1);
batadv_tvlv_handler_register(bat_priv, batadv_gw_tvlv_ogm_handler_v1,
NULL, NULL, BATADV_TVLV_GW, 1,
BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
}
/**
* batadv_gw_free() - free the gateway handling internals
* @bat_priv: the bat priv with all the mesh interface information
*/
void batadv_gw_free(struct batadv_priv *bat_priv)
{
batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1);
batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_GW, 1);
}
@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner
*/
#ifndef _NET_BATMAN_ADV_GATEWAY_COMMON_H_
#define _NET_BATMAN_ADV_GATEWAY_COMMON_H_
#include "main.h"
/**
* enum batadv_bandwidth_units - bandwidth unit types
*/
enum batadv_bandwidth_units {
/** @BATADV_BW_UNIT_KBIT: unit type kbit */
BATADV_BW_UNIT_KBIT,
/** @BATADV_BW_UNIT_MBIT: unit type mbit */
BATADV_BW_UNIT_MBIT,
};
#define BATADV_GW_MODE_OFF_NAME "off"
#define BATADV_GW_MODE_CLIENT_NAME "client"
#define BATADV_GW_MODE_SERVER_NAME "server"
void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv);
void batadv_gw_init(struct batadv_priv *bat_priv);
void batadv_gw_free(struct batadv_priv *bat_priv);
#endif /* _NET_BATMAN_ADV_GATEWAY_COMMON_H_ */
File diff suppressed because it is too large Load Diff
+155
View File
@@ -0,0 +1,155 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*/
#ifndef _NET_BATMAN_ADV_HARD_INTERFACE_H_
#define _NET_BATMAN_ADV_HARD_INTERFACE_H_
#include "main.h"
#include <linux/compiler.h>
#include <linux/init.h>
#include <linux/kref.h>
#include <linux/netdevice.h>
#include <linux/rcupdate.h>
#include <linux/stddef.h>
#include <linux/types.h>
/**
* enum batadv_hard_if_state - State of a hard interface
*/
enum batadv_hard_if_state {
/**
* @BATADV_IF_NOT_IN_USE: interface is not used as slave interface of a
* batman-adv mesh interface
*/
BATADV_IF_NOT_IN_USE,
/**
* @BATADV_IF_TO_BE_REMOVED: interface will be removed from mesh
* interface
*/
BATADV_IF_TO_BE_REMOVED,
/** @BATADV_IF_INACTIVE: interface is deactivated */
BATADV_IF_INACTIVE,
/** @BATADV_IF_ACTIVE: interface is used */
BATADV_IF_ACTIVE,
/** @BATADV_IF_TO_BE_ACTIVATED: interface is getting activated */
BATADV_IF_TO_BE_ACTIVATED,
};
/**
* enum batadv_hard_if_bcast - broadcast avoidance options
*/
enum batadv_hard_if_bcast {
/** @BATADV_HARDIF_BCAST_OK: Do broadcast on according hard interface */
BATADV_HARDIF_BCAST_OK = 0,
/**
* @BATADV_HARDIF_BCAST_NORECIPIENT: Broadcast not needed, there is no
* recipient
*/
BATADV_HARDIF_BCAST_NORECIPIENT,
/**
* @BATADV_HARDIF_BCAST_DUPFWD: There is just the neighbor we got it
* from
*/
BATADV_HARDIF_BCAST_DUPFWD,
/** @BATADV_HARDIF_BCAST_DUPORIG: There is just the originator */
BATADV_HARDIF_BCAST_DUPORIG,
};
extern struct notifier_block batadv_hard_if_notifier;
struct net_device *__batadv_get_real_netdev(struct net_device *net_device);
struct net_device *batadv_get_real_netdev(struct net_device *net_device);
u32 batadv_netdev_get_wifi_flags(struct net_device *net_dev);
u32 batadv_hardif_get_wifi_flags(struct batadv_hard_iface *hard_iface);
bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface);
struct batadv_hard_iface*
batadv_hardif_get_by_netdev(const struct net_device *net_dev);
int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
struct net_device *mesh_iface);
void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface);
int batadv_hardif_min_mtu(struct net_device *mesh_iface);
void batadv_update_min_mtu(struct net_device *mesh_iface);
void batadv_hardif_release(struct kref *ref);
int batadv_hardif_no_broadcast(struct batadv_hard_iface *if_outgoing,
u8 *orig_addr, u8 *orig_neigh);
int __init batadv_wifi_net_devices_init(void);
void batadv_wifi_net_devices_deinit(void);
/**
* batadv_hardif_put() - decrement the hard interface refcounter and possibly
* release it
* @hard_iface: the hard interface to free
*/
static inline void batadv_hardif_put(struct batadv_hard_iface *hard_iface)
{
if (!hard_iface)
return;
kref_put(&hard_iface->refcount, batadv_hardif_release);
}
/**
* batadv_primary_if_get_selected() - Get reference to primary interface
* @bat_priv: the bat priv with all the mesh interface information
*
* Return: primary interface (with increased refcnt), otherwise NULL
*/
static inline struct batadv_hard_iface *
batadv_primary_if_get_selected(struct batadv_priv *bat_priv)
{
struct batadv_hard_iface *hard_iface;
rcu_read_lock();
hard_iface = rcu_dereference(bat_priv->primary_if);
if (!hard_iface)
goto out;
if (!kref_get_unless_zero(&hard_iface->refcount))
hard_iface = NULL;
out:
rcu_read_unlock();
return hard_iface;
}
/**
* batadv_is_cfg80211() - check if the given hardif is a cfg80211
* wifi interface
* @wifi_flags: extracted batadv_hard_iface_wifi_flags of a net_device
*
* Return: true if the net device is a cfg80211 wireless device, false
* otherwise.
*/
static inline bool batadv_is_cfg80211(u32 wifi_flags)
{
u32 allowed_flags = 0;
allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT;
allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT;
return !!(wifi_flags & allowed_flags);
}
/**
* batadv_is_wifi() - check if flags belong to wifi interface
* @wifi_flags: extracted batadv_hard_iface_wifi_flags of a net_device
*
* Return: true if the net device is a 802.11 wireless device, false otherwise.
*/
static inline bool batadv_is_wifi(u32 wifi_flags)
{
return wifi_flags != 0;
}
#endif /* _NET_BATMAN_ADV_HARD_INTERFACE_H_ */
+83
View File
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Simon Wunderlich, Marek Lindner
*/
#include "hash.h"
#include "main.h"
#include <linux/gfp.h>
#include <linux/lockdep.h>
#include <linux/slab.h>
/* clears the hash */
static void batadv_hash_init(struct batadv_hashtable *hash)
{
u32 i;
for (i = 0; i < hash->size; i++) {
INIT_HLIST_HEAD(&hash->table[i]);
spin_lock_init(&hash->list_locks[i]);
}
atomic_set(&hash->generation, 0);
}
/**
* batadv_hash_destroy() - Free only the hashtable and the hash itself
* @hash: hash object to destroy
*/
void batadv_hash_destroy(struct batadv_hashtable *hash)
{
kfree(hash->list_locks);
kfree(hash->table);
kfree(hash);
}
/**
* batadv_hash_new() - Allocates and clears the hashtable
* @size: number of hash buckets to allocate
*
* Return: newly allocated hashtable, NULL on errors
*/
struct batadv_hashtable *batadv_hash_new(u32 size)
{
struct batadv_hashtable *hash;
hash = kmalloc_obj(*hash, GFP_ATOMIC);
if (!hash)
return NULL;
hash->table = kmalloc_objs(*hash->table, size, GFP_ATOMIC);
if (!hash->table)
goto free_hash;
hash->list_locks = kmalloc_objs(*hash->list_locks, size, GFP_ATOMIC);
if (!hash->list_locks)
goto free_table;
hash->size = size;
batadv_hash_init(hash);
return hash;
free_table:
kfree(hash->table);
free_hash:
kfree(hash);
return NULL;
}
/**
* batadv_hash_set_lock_class() - Set specific lockdep class for hash spinlocks
* @hash: hash object to modify
* @key: lockdep class key address
*/
void batadv_hash_set_lock_class(struct batadv_hashtable *hash,
struct lock_class_key *key)
{
u32 i;
for (i = 0; i < hash->size; i++)
lockdep_set_class(&hash->list_locks[i], key);
}
+157
View File
@@ -0,0 +1,157 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Simon Wunderlich, Marek Lindner
*/
#ifndef _NET_BATMAN_ADV_HASH_H_
#define _NET_BATMAN_ADV_HASH_H_
#include "main.h"
#include <linux/atomic.h>
#include <linux/compiler.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/rculist.h>
#include <linux/spinlock.h>
#include <linux/stddef.h>
#include <linux/types.h>
/* callback to a compare function. should compare 2 element data for their
* keys
*
* Return: true if same and false if not same
*/
typedef bool (*batadv_hashdata_compare_cb)(const struct hlist_node *,
const void *);
/* the hashfunction
*
* Return: an index based on the key in the data of the first argument and the
* size the second
*/
typedef u32 (*batadv_hashdata_choose_cb)(const void *, u32);
typedef void (*batadv_hashdata_free_cb)(struct hlist_node *, void *);
/**
* struct batadv_hashtable - Wrapper of simple hlist based hashtable
*/
struct batadv_hashtable {
/** @table: the hashtable itself with the buckets */
struct hlist_head *table;
/** @list_locks: spinlock for each hash list entry */
spinlock_t *list_locks;
/** @size: size of hashtable */
u32 size;
/** @generation: current (generation) sequence number */
atomic_t generation;
};
/* allocates and clears the hash */
struct batadv_hashtable *batadv_hash_new(u32 size);
/* set class key for all locks */
void batadv_hash_set_lock_class(struct batadv_hashtable *hash,
struct lock_class_key *key);
/* free only the hashtable and the hash itself. */
void batadv_hash_destroy(struct batadv_hashtable *hash);
/**
* batadv_hash_add() - adds data to the hashtable
* @hash: storage hash table
* @compare: callback to determine if 2 hash elements are identical
* @choose: callback calculating the hash index
* @data: data passed to the aforementioned callbacks as argument
* @data_node: to be added element
*
* Return: 0 on success, 1 if the element already is in the hash
* and -1 on error.
*/
static inline int batadv_hash_add(struct batadv_hashtable *hash,
batadv_hashdata_compare_cb compare,
batadv_hashdata_choose_cb choose,
const void *data,
struct hlist_node *data_node)
{
u32 index;
int ret = -1;
struct hlist_head *head;
struct hlist_node *node;
spinlock_t *list_lock; /* spinlock to protect write access */
if (!hash)
goto out;
index = choose(data, hash->size);
head = &hash->table[index];
list_lock = &hash->list_locks[index];
spin_lock_bh(list_lock);
hlist_for_each(node, head) {
if (!compare(node, data))
continue;
ret = 1;
goto unlock;
}
/* no duplicate found in list, add new element */
hlist_add_head_rcu(data_node, head);
atomic_inc(&hash->generation);
ret = 0;
unlock:
spin_unlock_bh(list_lock);
out:
return ret;
}
/**
* batadv_hash_remove() - Removes data from hash, if found
* @hash: hash table
* @compare: callback to determine if 2 hash elements are identical
* @choose: callback calculating the hash index
* @data: data passed to the aforementioned callbacks as argument
*
* data could be the structure you use with just the key filled, we just need
* the key for comparing.
*
* Return: returns pointer to data on success, so you can remove the used
* structure yourself, or NULL on error
*/
static inline void *batadv_hash_remove(struct batadv_hashtable *hash,
batadv_hashdata_compare_cb compare,
batadv_hashdata_choose_cb choose,
void *data)
{
u32 index;
struct hlist_node *node;
struct hlist_head *head;
void *data_save = NULL;
index = choose(data, hash->size);
head = &hash->table[index];
spin_lock_bh(&hash->list_locks[index]);
hlist_for_each(node, head) {
if (!compare(node, data))
continue;
data_save = node;
hlist_del_rcu(node);
atomic_inc(&hash->generation);
break;
}
spin_unlock_bh(&hash->list_locks[index]);
return data_save;
}
#endif /* _NET_BATMAN_ADV_HASH_H_ */
+36
View File
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner
*/
#include "log.h"
#include "main.h"
#include <linux/stdarg.h>
#include "trace.h"
/**
* batadv_debug_log() - Add debug log entry
* @bat_priv: the bat priv with all the mesh interface information
* @fmt: format string
*
* Return: 0 on success or negative error number in case of failure
*/
int batadv_debug_log(struct batadv_priv *bat_priv, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
trace_batadv_dbg(bat_priv, &vaf);
va_end(args);
return 0;
}
+140
View File
@@ -0,0 +1,140 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*/
#ifndef _NET_BATMAN_ADV_LOG_H_
#define _NET_BATMAN_ADV_LOG_H_
#include "main.h"
#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/compiler.h>
#include <linux/printk.h>
#ifdef CONFIG_BATMAN_ADV_DEBUG
int batadv_debug_log_setup(struct batadv_priv *bat_priv);
void batadv_debug_log_cleanup(struct batadv_priv *bat_priv);
#else
static inline int batadv_debug_log_setup(struct batadv_priv *bat_priv)
{
return 0;
}
static inline void batadv_debug_log_cleanup(struct batadv_priv *bat_priv)
{
}
#endif
/**
* enum batadv_dbg_level - available log levels
*/
enum batadv_dbg_level {
/** @BATADV_DBG_BATMAN: OGM and TQ computations related messages */
BATADV_DBG_BATMAN = BIT(0),
/** @BATADV_DBG_ROUTES: route added / changed / deleted */
BATADV_DBG_ROUTES = BIT(1),
/** @BATADV_DBG_TT: translation table messages */
BATADV_DBG_TT = BIT(2),
/** @BATADV_DBG_BLA: bridge loop avoidance messages */
BATADV_DBG_BLA = BIT(3),
/** @BATADV_DBG_DAT: ARP snooping and DAT related messages */
BATADV_DBG_DAT = BIT(4),
/** @BATADV_DBG_MCAST: multicast related messages */
BATADV_DBG_MCAST = BIT(6),
/** @BATADV_DBG_TP_METER: throughput meter messages */
BATADV_DBG_TP_METER = BIT(7),
/** @BATADV_DBG_ALL: the union of all the above log levels */
BATADV_DBG_ALL = 255,
};
#ifdef CONFIG_BATMAN_ADV_DEBUG
int batadv_debug_log(struct batadv_priv *bat_priv, const char *fmt, ...)
__printf(2, 3);
/**
* _batadv_dbg() - Store debug output with(out) rate limiting
* @type: type of debug message
* @bat_priv: the bat priv with all the mesh interface information
* @ratelimited: whether output should be rate limited
* @fmt: format string
* @arg: variable arguments
*/
#define _batadv_dbg(type, bat_priv, ratelimited, fmt, arg...) \
do { \
struct batadv_priv *__batpriv = (bat_priv); \
if (READ_ONCE(__batpriv->log_level) & (type) && \
(!(ratelimited) || net_ratelimit())) \
batadv_debug_log(__batpriv, fmt, ## arg); \
} \
while (0)
#else /* !CONFIG_BATMAN_ADV_DEBUG */
__printf(4, 5)
static inline void _batadv_dbg(int type __always_unused,
struct batadv_priv *bat_priv __always_unused,
int ratelimited __always_unused,
const char *fmt __always_unused, ...)
{
}
#endif
/**
* batadv_dbg() - Store debug output without rate limiting
* @type: type of debug message
* @bat_priv: the bat priv with all the mesh interface information
* @arg: format string and variable arguments
*/
#define batadv_dbg(type, bat_priv, arg...) \
_batadv_dbg(type, bat_priv, 0, ## arg)
/**
* batadv_dbg_ratelimited() - Store debug output with rate limiting
* @type: type of debug message
* @bat_priv: the bat priv with all the mesh interface information
* @arg: format string and variable arguments
*/
#define batadv_dbg_ratelimited(type, bat_priv, arg...) \
_batadv_dbg(type, bat_priv, 1, ## arg)
/**
* batadv_info() - Store message in debug buffer and print it to kmsg buffer
* @net_dev: the mesh interface net device
* @fmt: format string
* @arg: variable arguments
*/
#define batadv_info(net_dev, fmt, arg...) \
do { \
struct net_device *_netdev = (net_dev); \
struct batadv_priv *_batpriv = netdev_priv(_netdev); \
batadv_dbg(BATADV_DBG_ALL, _batpriv, fmt, ## arg); \
pr_info("%s: " fmt, _netdev->name, ## arg); \
} while (0)
/**
* batadv_err() - Store error in debug buffer and print it to kmsg buffer
* @net_dev: the mesh interface net device
* @fmt: format string
* @arg: variable arguments
*/
#define batadv_err(net_dev, fmt, arg...) \
do { \
struct net_device *_netdev = (net_dev); \
struct batadv_priv *_batpriv = netdev_priv(_netdev); \
batadv_dbg(BATADV_DBG_ALL, _batpriv, fmt, ## arg); \
pr_err("%s: " fmt, _netdev->name, ## arg); \
} while (0)
#endif /* _NET_BATMAN_ADV_LOG_H_ */
+713
View File
@@ -0,0 +1,713 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*/
#include "main.h"
#include <linux/array_size.h>
#include <linux/atomic.h>
#include <linux/build_bug.h>
#include <linux/byteorder/generic.h>
#include <linux/container_of.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gfp.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/init.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/kobject.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/printk.h>
#include <linux/rcupdate.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/sprintf.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/utsname.h>
#include <linux/workqueue.h>
#include <net/dsfield.h>
#include <net/genetlink.h>
#include <net/rtnetlink.h>
#include <uapi/linux/batadv_packet.h>
#include <uapi/linux/batman_adv.h>
#include "bat_algo.h"
#include "bat_iv_ogm.h"
#include "bat_v.h"
#include "bridge_loop_avoidance.h"
#include "distributed-arp-table.h"
#include "gateway_client.h"
#include "gateway_common.h"
#include "hard-interface.h"
#include "log.h"
#include "mesh-interface.h"
#include "multicast.h"
#include "netlink.h"
#include "originator.h"
#include "routing.h"
#include "send.h"
#include "tp_meter.h"
#include "translation-table.h"
/* List manipulations on hardif_list have to be rtnl_lock()'ed,
* list traversals just rcu-locked
*/
struct list_head batadv_hardif_list;
unsigned int batadv_hardif_generation;
static int (*batadv_rx_handler[256])(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
struct workqueue_struct *batadv_event_workqueue;
static void batadv_recv_handler_init(void);
#define BATADV_UEV_TYPE_VAR "BATTYPE="
#define BATADV_UEV_ACTION_VAR "BATACTION="
#define BATADV_UEV_DATA_VAR "BATDATA="
static char *batadv_uev_action_str[] = {
"add",
"del",
"change",
"loopdetect",
};
static char *batadv_uev_type_str[] = {
"gw",
"bla",
};
static int __init batadv_init(void)
{
int ret;
ret = batadv_tt_cache_init();
if (ret < 0)
return ret;
INIT_LIST_HEAD(&batadv_hardif_list);
batadv_algo_init();
batadv_recv_handler_init();
batadv_v_init();
batadv_iv_init();
batadv_tp_meter_init();
batadv_event_workqueue = create_singlethread_workqueue("bat_events");
if (!batadv_event_workqueue) {
ret = -ENOMEM;
goto err_create_wq;
}
ret = batadv_wifi_net_devices_init();
if (ret < 0)
goto err_init_wifi;
register_netdevice_notifier(&batadv_hard_if_notifier);
rtnl_link_register(&batadv_link_ops);
batadv_netlink_register();
pr_info("B.A.T.M.A.N. advanced %s (compatibility version %i) loaded\n",
init_utsname()->release, BATADV_COMPAT_VERSION);
return 0;
err_init_wifi:
destroy_workqueue(batadv_event_workqueue);
batadv_event_workqueue = NULL;
rcu_barrier();
err_create_wq:
batadv_tt_cache_destroy();
return ret;
}
static void __exit batadv_exit(void)
{
batadv_netlink_unregister();
rtnl_link_unregister(&batadv_link_ops);
unregister_netdevice_notifier(&batadv_hard_if_notifier);
destroy_workqueue(batadv_event_workqueue);
batadv_event_workqueue = NULL;
rcu_barrier();
batadv_wifi_net_devices_deinit();
batadv_tt_cache_destroy();
}
/**
* batadv_mesh_init() - Initialize mesh interface
* @mesh_iface: netdev struct of the mesh interface
*
* Return: 0 on success or negative error number in case of failure
*/
int batadv_mesh_init(struct net_device *mesh_iface)
{
struct batadv_priv *bat_priv = netdev_priv(mesh_iface);
int ret;
spin_lock_init(&bat_priv->forw_bat_list_lock);
spin_lock_init(&bat_priv->forw_bcast_list_lock);
spin_lock_init(&bat_priv->tt.changes_list_lock);
spin_lock_init(&bat_priv->tt.req_list_lock);
spin_lock_init(&bat_priv->tt.roam_list_lock);
spin_lock_init(&bat_priv->tt.last_changeset_lock);
spin_lock_init(&bat_priv->tt.commit_lock);
spin_lock_init(&bat_priv->gw.list_lock);
#ifdef CONFIG_BATMAN_ADV_MCAST
spin_lock_init(&bat_priv->mcast.mla_lock);
spin_lock_init(&bat_priv->mcast.want_lists_lock);
#endif
spin_lock_init(&bat_priv->tvlv.container_list_lock);
spin_lock_init(&bat_priv->tvlv.handler_list_lock);
spin_lock_init(&bat_priv->meshif_vlan_list_lock);
spin_lock_init(&bat_priv->tp_list_lock);
INIT_HLIST_HEAD(&bat_priv->forw_bat_list);
INIT_HLIST_HEAD(&bat_priv->forw_bcast_list);
INIT_HLIST_HEAD(&bat_priv->gw.gateway_list);
#ifdef CONFIG_BATMAN_ADV_MCAST
INIT_HLIST_HEAD(&bat_priv->mcast.want_all_unsnoopables_list);
INIT_HLIST_HEAD(&bat_priv->mcast.want_all_ipv4_list);
INIT_HLIST_HEAD(&bat_priv->mcast.want_all_ipv6_list);
#endif
INIT_LIST_HEAD(&bat_priv->tt.changes_list);
INIT_HLIST_HEAD(&bat_priv->tt.req_list);
INIT_LIST_HEAD(&bat_priv->tt.roam_list);
#ifdef CONFIG_BATMAN_ADV_MCAST
INIT_HLIST_HEAD(&bat_priv->mcast.mla_list);
#endif
INIT_HLIST_HEAD(&bat_priv->tvlv.container_list);
INIT_HLIST_HEAD(&bat_priv->tvlv.handler_list);
INIT_HLIST_HEAD(&bat_priv->meshif_vlan_list);
INIT_HLIST_HEAD(&bat_priv->tp_sender_list);
INIT_HLIST_HEAD(&bat_priv->tp_receiver_list);
bat_priv->gw.generation = 0;
ret = batadv_originator_init(bat_priv);
if (ret < 0) {
WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
goto err_orig;
}
ret = batadv_tt_init(bat_priv);
if (ret < 0) {
WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
goto err_tt;
}
ret = batadv_v_mesh_init(bat_priv);
if (ret < 0) {
WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
goto err_v;
}
ret = batadv_bla_init(bat_priv);
if (ret < 0) {
WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
goto err_bla;
}
ret = batadv_dat_init(bat_priv);
if (ret < 0) {
WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
goto err_dat;
}
batadv_gw_init(bat_priv);
batadv_mcast_init(bat_priv);
atomic_set(&bat_priv->gw.reselect, 0);
WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_ACTIVE);
return 0;
err_dat:
batadv_bla_free(bat_priv);
err_bla:
batadv_v_mesh_free(bat_priv);
err_v:
batadv_tt_free(bat_priv);
err_tt:
batadv_originator_free(bat_priv);
err_orig:
batadv_purge_outstanding_packets(bat_priv, NULL);
WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_INACTIVE);
return ret;
}
/**
* batadv_mesh_free() - Deinitialize mesh interface
* @mesh_iface: netdev struct of the mesh interface
*/
void batadv_mesh_free(struct net_device *mesh_iface)
{
struct batadv_priv *bat_priv = netdev_priv(mesh_iface);
struct batadv_meshif_vlan *vlan;
WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
batadv_purge_outstanding_packets(bat_priv, NULL);
batadv_tp_stop_all(bat_priv);
batadv_gw_node_free(bat_priv);
batadv_v_mesh_free(bat_priv);
batadv_dat_free(bat_priv);
batadv_bla_free(bat_priv);
batadv_mcast_free(bat_priv);
/* destroy the "untagged" VLAN */
vlan = batadv_meshif_vlan_get(bat_priv, BATADV_NO_FLAGS);
if (vlan) {
batadv_meshif_destroy_vlan(bat_priv, vlan);
batadv_meshif_vlan_put(vlan);
}
/* Free the TT and the originator tables only after having terminated
* all the other depending components which may use these structures for
* their purposes.
*/
batadv_tt_free(bat_priv);
/* Since the originator table clean up routine is accessing the TT
* tables as well, it has to be invoked after the TT tables have been
* freed and marked as empty. This ensures that no cleanup RCU callbacks
* accessing the TT data are scheduled for later execution.
*/
batadv_originator_free(bat_priv);
batadv_gw_free(bat_priv);
free_percpu(bat_priv->bat_counters);
bat_priv->bat_counters = NULL;
WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_INACTIVE);
}
/**
* batadv_is_my_mac() - check if the given mac address belongs to any of the
* real interfaces in the current mesh
* @bat_priv: the bat priv with all the mesh interface information
* @addr: the address to check
*
* Return: 'true' if the mac address was found, false otherwise.
*/
bool batadv_is_my_mac(struct batadv_priv *bat_priv, const u8 *addr)
{
const struct batadv_hard_iface *hard_iface;
struct list_head *iter;
bool is_my_mac = false;
rcu_read_lock();
netdev_for_each_lower_private_rcu(bat_priv->mesh_iface, hard_iface, iter) {
if (hard_iface->if_status != BATADV_IF_ACTIVE)
continue;
if (batadv_compare_eth(hard_iface->net_dev->dev_addr, addr)) {
is_my_mac = true;
break;
}
}
rcu_read_unlock();
return is_my_mac;
}
/**
* batadv_max_header_len() - calculate maximum encapsulation overhead for a
* payload packet
*
* Return: the maximum encapsulation overhead in bytes.
*/
int batadv_max_header_len(void)
{
int header_len = 0;
header_len = max_t(int, header_len,
sizeof(struct batadv_unicast_packet));
header_len = max_t(int, header_len,
sizeof(struct batadv_unicast_4addr_packet));
header_len = max_t(int, header_len,
sizeof(struct batadv_bcast_packet));
return header_len + ETH_HLEN;
}
/**
* batadv_skb_set_priority() - sets skb priority according to packet content
* @skb: the packet to be sent
* @offset: offset to the packet content
*
* This function sets a value between 256 and 263 (802.1d priority), which
* can be interpreted by the cfg80211 or other drivers.
*/
void batadv_skb_set_priority(struct sk_buff *skb, int offset)
{
struct iphdr ip_hdr_tmp, *ip_hdr;
struct ipv6hdr ip6_hdr_tmp, *ip6_hdr;
struct ethhdr ethhdr_tmp, *ethhdr;
struct vlan_ethhdr *vhdr, vhdr_tmp;
u32 prio;
/* already set, do nothing */
if (skb->priority >= 256 && skb->priority <= 263)
return;
ethhdr = skb_header_pointer(skb, offset, sizeof(*ethhdr), &ethhdr_tmp);
if (!ethhdr)
return;
switch (ethhdr->h_proto) {
case htons(ETH_P_8021Q):
vhdr = skb_header_pointer(skb, offset,
sizeof(*vhdr), &vhdr_tmp);
if (!vhdr)
return;
prio = ntohs(vhdr->h_vlan_TCI) & VLAN_PRIO_MASK;
prio = prio >> VLAN_PRIO_SHIFT;
break;
case htons(ETH_P_IP):
ip_hdr = skb_header_pointer(skb, offset + sizeof(*ethhdr),
sizeof(*ip_hdr), &ip_hdr_tmp);
if (!ip_hdr)
return;
prio = (ipv4_get_dsfield(ip_hdr) & 0xfc) >> 5;
break;
case htons(ETH_P_IPV6):
ip6_hdr = skb_header_pointer(skb, offset + sizeof(*ethhdr),
sizeof(*ip6_hdr), &ip6_hdr_tmp);
if (!ip6_hdr)
return;
prio = (ipv6_get_dsfield(ip6_hdr) & 0xfc) >> 5;
break;
default:
return;
}
skb->priority = prio + 256;
}
static int batadv_recv_unhandled_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if)
{
kfree_skb(skb);
return NET_RX_DROP;
}
/* incoming packets with the batman ethertype received on any active hard
* interface
*/
/**
* batadv_batman_skb_recv() - Handle incoming message from an hard interface
* @skb: the received packet
* @dev: the net device that the packet was received on
* @ptype: packet type of incoming packet (ETH_P_BATMAN)
* @orig_dev: the original receive net device (e.g. bonded device)
*
* Return: NET_RX_SUCCESS on success or NET_RX_DROP in case of failure
*/
int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *ptype,
struct net_device *orig_dev)
{
struct batadv_priv *bat_priv;
struct batadv_ogm_packet *batadv_ogm_packet;
struct batadv_hard_iface *hard_iface;
u8 idx;
hard_iface = container_of(ptype, struct batadv_hard_iface,
batman_adv_ptype);
/* Prevent processing a packet received on an interface which is getting
* shut down otherwise the packet may trigger de-reference errors
* further down in the receive path.
*/
if (!kref_get_unless_zero(&hard_iface->refcount))
goto err_out;
skb = skb_share_check(skb, GFP_ATOMIC);
/* skb was released by skb_share_check() */
if (!skb)
goto err_put;
/* packet should hold at least type and version */
if (unlikely(!pskb_may_pull(skb, 2)))
goto err_free;
/* expect a valid ethernet header here. */
if (unlikely(skb->mac_len != ETH_HLEN || !skb_mac_header(skb)))
goto err_free;
if (!hard_iface->mesh_iface)
goto err_free;
bat_priv = netdev_priv(hard_iface->mesh_iface);
if (READ_ONCE(bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
goto err_free;
/* discard frames on not active interfaces */
if (hard_iface->if_status != BATADV_IF_ACTIVE)
goto err_free;
batadv_ogm_packet = (struct batadv_ogm_packet *)skb->data;
if (batadv_ogm_packet->version != BATADV_COMPAT_VERSION) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Drop packet: incompatible batman version (%i)\n",
batadv_ogm_packet->version);
goto err_free;
}
/* reset control block to avoid left overs from previous users */
memset(skb->cb, 0, sizeof(struct batadv_skb_cb));
idx = batadv_ogm_packet->packet_type;
(*batadv_rx_handler[idx])(skb, hard_iface);
batadv_hardif_put(hard_iface);
/* return NET_RX_SUCCESS in any case as we
* most probably dropped the packet for
* routing-logical reasons.
*/
return NET_RX_SUCCESS;
err_free:
kfree_skb(skb);
err_put:
batadv_hardif_put(hard_iface);
err_out:
return NET_RX_DROP;
}
static void batadv_recv_handler_init(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(batadv_rx_handler); i++)
batadv_rx_handler[i] = batadv_recv_unhandled_packet;
for (i = BATADV_UNICAST_MIN; i <= BATADV_UNICAST_MAX; i++)
batadv_rx_handler[i] = batadv_recv_unhandled_unicast_packet;
/* compile time checks for sizes */
BUILD_BUG_ON(sizeof(struct batadv_bla_claim_dst) != 6);
BUILD_BUG_ON(sizeof(struct batadv_ogm_packet) != 24);
BUILD_BUG_ON(sizeof(struct batadv_icmp_header) != 20);
BUILD_BUG_ON(sizeof(struct batadv_icmp_packet) != 20);
BUILD_BUG_ON(sizeof(struct batadv_icmp_packet_rr) != 116);
BUILD_BUG_ON(sizeof(struct batadv_unicast_packet) != 10);
BUILD_BUG_ON(sizeof(struct batadv_unicast_4addr_packet) != 18);
BUILD_BUG_ON(sizeof(struct batadv_frag_packet) != 20);
BUILD_BUG_ON(sizeof(struct batadv_bcast_packet) != 14);
BUILD_BUG_ON(sizeof(struct batadv_coded_packet) != 46);
BUILD_BUG_ON(sizeof(struct batadv_unicast_tvlv_packet) != 20);
BUILD_BUG_ON(sizeof(struct batadv_tvlv_hdr) != 4);
BUILD_BUG_ON(sizeof(struct batadv_tvlv_gateway_data) != 8);
BUILD_BUG_ON(sizeof(struct batadv_tvlv_tt_vlan_data) != 8);
BUILD_BUG_ON(sizeof(struct batadv_tvlv_tt_change) != 12);
BUILD_BUG_ON(sizeof(struct batadv_tvlv_roam_adv) != 8);
i = sizeof_field(struct sk_buff, cb);
BUILD_BUG_ON(sizeof(struct batadv_skb_cb) > i);
/* broadcast packet */
batadv_rx_handler[BATADV_BCAST] = batadv_recv_bcast_packet;
/* multicast packet */
batadv_rx_handler[BATADV_MCAST] = batadv_recv_mcast_packet;
/* unicast packets ... */
/* unicast with 4 addresses packet */
batadv_rx_handler[BATADV_UNICAST_4ADDR] = batadv_recv_unicast_packet;
/* unicast packet */
batadv_rx_handler[BATADV_UNICAST] = batadv_recv_unicast_packet;
/* unicast tvlv packet */
batadv_rx_handler[BATADV_UNICAST_TVLV] = batadv_recv_unicast_tvlv;
/* batman icmp packet */
batadv_rx_handler[BATADV_ICMP] = batadv_recv_icmp_packet;
/* Fragmented packets */
batadv_rx_handler[BATADV_UNICAST_FRAG] = batadv_recv_frag_packet;
}
/**
* batadv_recv_handler_register() - Register handler for batman-adv packet type
* @packet_type: batadv_packettype which should be handled
* @recv_handler: receive handler for the packet type
*
* Return: 0 on success or negative error number in case of failure
*/
int
batadv_recv_handler_register(u8 packet_type,
int (*recv_handler)(struct sk_buff *,
struct batadv_hard_iface *))
{
int (*curr)(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
curr = batadv_rx_handler[packet_type];
if (curr != batadv_recv_unhandled_packet &&
curr != batadv_recv_unhandled_unicast_packet)
return -EBUSY;
batadv_rx_handler[packet_type] = recv_handler;
return 0;
}
/**
* batadv_recv_handler_unregister() - Unregister handler for packet type
* @packet_type: batadv_packettype which should no longer be handled
*/
void batadv_recv_handler_unregister(u8 packet_type)
{
batadv_rx_handler[packet_type] = batadv_recv_unhandled_packet;
}
/**
* batadv_get_vid() - extract the VLAN identifier from skb if any
* @skb: the buffer containing the packet
* @header_len: length of the batman header preceding the ethernet header
*
* The caller must ensure that at least @header_len + ETH_HLEN bytes are
* accessible after skb->data.
*
* Return: VID with the BATADV_VLAN_HAS_TAG flag when the packet embedded in the
* skb is vlan tagged. Otherwise BATADV_NO_FLAGS.
*/
unsigned short batadv_get_vid(struct sk_buff *skb, size_t header_len)
{
struct ethhdr *ethhdr = (struct ethhdr *)(skb->data + header_len);
struct vlan_ethhdr *vhdr;
unsigned short vid;
if (ethhdr->h_proto != htons(ETH_P_8021Q))
return BATADV_NO_FLAGS;
if (!pskb_may_pull(skb, header_len + VLAN_ETH_HLEN))
return BATADV_NO_FLAGS;
vhdr = (struct vlan_ethhdr *)(skb->data + header_len);
vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
/* VID 0 is only used to indicate "priority tag" frames which only
* contain priority information and no VID.
*/
if (vid == 0)
return BATADV_NO_FLAGS;
vid |= BATADV_VLAN_HAS_TAG;
return vid;
}
/**
* batadv_vlan_ap_isola_get() - return AP isolation status for the given vlan
* @bat_priv: the bat priv with all the mesh interface information
* @vid: the VLAN identifier for which the AP isolation attributed as to be
* looked up
*
* Return: true if AP isolation is on for the VLAN identified by vid, false
* otherwise
*/
bool batadv_vlan_ap_isola_get(struct batadv_priv *bat_priv, unsigned short vid)
{
bool ap_isolation_enabled = false;
struct batadv_meshif_vlan *vlan;
/* if the AP isolation is requested on a VLAN, then check for its
* setting in the proper VLAN private data structure
*/
vlan = batadv_meshif_vlan_get(bat_priv, vid);
if (vlan) {
ap_isolation_enabled = READ_ONCE(vlan->ap_isolation);
batadv_meshif_vlan_put(vlan);
}
return ap_isolation_enabled;
}
/**
* batadv_throw_uevent() - Send an uevent with batman-adv specific env data
* @bat_priv: the bat priv with all the mesh interface information
* @type: subsystem type of event. Stored in uevent's BATTYPE
* @action: action type of event. Stored in uevent's BATACTION
* @data: string with additional information to the event (ignored for
* BATADV_UEV_DEL). Stored in uevent's BATDATA
*
* Return: 0 on success or negative error number in case of failure
*/
int batadv_throw_uevent(struct batadv_priv *bat_priv, enum batadv_uev_type type,
enum batadv_uev_action action, const char *data)
{
int ret = -ENOMEM;
struct kobject *bat_kobj;
char *uevent_env[4] = { NULL, NULL, NULL, NULL };
bat_kobj = &bat_priv->mesh_iface->dev.kobj;
uevent_env[0] = kasprintf(GFP_ATOMIC,
"%s%s", BATADV_UEV_TYPE_VAR,
batadv_uev_type_str[type]);
if (!uevent_env[0])
goto report_error;
uevent_env[1] = kasprintf(GFP_ATOMIC,
"%s%s", BATADV_UEV_ACTION_VAR,
batadv_uev_action_str[action]);
if (!uevent_env[1])
goto free_first_env;
/* If the event is DEL, ignore the data field */
if (action != BATADV_UEV_DEL) {
uevent_env[2] = kasprintf(GFP_ATOMIC,
"%s%s", BATADV_UEV_DATA_VAR, data);
if (!uevent_env[2])
goto free_second_env;
}
ret = kobject_uevent_env(bat_kobj, KOBJ_CHANGE, uevent_env);
kfree(uevent_env[2]);
free_second_env:
kfree(uevent_env[1]);
free_first_env:
kfree(uevent_env[0]);
if (ret)
report_error:
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Impossible to send uevent for (%s,%s,%s) event (err: %d)\n",
batadv_uev_type_str[type],
batadv_uev_action_str[action],
(action == BATADV_UEV_DEL ? "NULL" : data), ret);
return ret;
}
module_init(batadv_init);
module_exit(batadv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR(BATADV_DRIVER_AUTHOR);
MODULE_DESCRIPTION(BATADV_DRIVER_DESC);
MODULE_ALIAS_RTNL_LINK("batadv");
MODULE_ALIAS_GENL_FAMILY(BATADV_NL_NAME);
+376
View File
@@ -0,0 +1,376 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*/
#ifndef _NET_BATMAN_ADV_MAIN_H_
#define _NET_BATMAN_ADV_MAIN_H_
#define BATADV_DRIVER_AUTHOR "Marek Lindner <marek.lindner@mailbox.org>, " \
"Simon Wunderlich <sw@simonwunderlich.de>"
#define BATADV_DRIVER_DESC "B.A.T.M.A.N. advanced"
#define BATADV_DRIVER_DEVICE "batman-adv"
/* B.A.T.M.A.N. parameters */
#define BATADV_TQ_MAX_VALUE 255
#define BATADV_THROUGHPUT_MAX_VALUE 0xFFFFFFFF
#define BATADV_JITTER 20
#define BATADV_MAX_MTU (ETH_MAX_MTU - batadv_max_header_len())
/* Time To Live of broadcast messages */
#define BATADV_TTL 50
/* maximum sequence number age of broadcast messages */
#define BATADV_BCAST_MAX_AGE 64
/* purge originators after time in seconds if no valid packet comes in
* -> TODO: check influence on BATADV_TQ_LOCAL_WINDOW_SIZE
*/
#define BATADV_PURGE_TIMEOUT 200000 /* 200 seconds */
#define BATADV_TT_LOCAL_TIMEOUT 600000 /* in milliseconds */
#define BATADV_TT_CLIENT_ROAM_TIMEOUT 600000 /* in milliseconds */
#define BATADV_TT_CLIENT_TEMP_TIMEOUT 600000 /* in milliseconds */
#define BATADV_TT_WORK_PERIOD 5000 /* 5 seconds */
#define BATADV_ORIG_WORK_PERIOD 1000 /* 1 second */
#define BATADV_MCAST_WORK_PERIOD 500 /* 0.5 seconds */
#define BATADV_DAT_ENTRY_TIMEOUT (5 * 60000) /* 5 mins in milliseconds */
/* sliding packet range of received originator messages in sequence numbers
* (should be a multiple of our word size)
*/
#define BATADV_TQ_LOCAL_WINDOW_SIZE 64
/* milliseconds we have to keep pending tt_req */
#define BATADV_TT_REQUEST_TIMEOUT 3000
#define BATADV_TQ_GLOBAL_WINDOW_SIZE 5
#define BATADV_TQ_LOCAL_BIDRECT_SEND_MINIMUM 1
#define BATADV_TQ_LOCAL_BIDRECT_RECV_MINIMUM 1
#define BATADV_TQ_TOTAL_BIDRECT_LIMIT 1
/* B.A.T.M.A.N. V */
#define BATADV_THROUGHPUT_DEFAULT_VALUE 10 /* 1 Mbps */
#define BATADV_ELP_PROBES_PER_NODE 2
#define BATADV_ELP_MIN_PROBE_SIZE 200 /* bytes */
#define BATADV_ELP_PROBE_MAX_TX_DIFF 100 /* milliseconds */
#define BATADV_ELP_MAX_AGE 64
#define BATADV_OGM_MAX_ORIGDIFF 5
#define BATADV_OGM_MAX_AGE 64
/* number of OGMs sent with the last tt diff */
#define BATADV_TT_OGM_APPEND_MAX 3
/* Time in which a client can roam at most ROAMING_MAX_COUNT times in
* milliseconds
*/
#define BATADV_ROAMING_MAX_TIME 20000
#define BATADV_ROAMING_MAX_COUNT 5
#define BATADV_NO_FLAGS 0
#define BATADV_NULL_IFINDEX 0 /* dummy ifindex used to avoid iface checks */
#define BATADV_NO_MARK 0
/* default interface for multi interface operation. The default interface is
* used for communication which originated locally (i.e. is not forwarded)
* or where special forwarding is not desired/necessary.
*/
#define BATADV_IF_DEFAULT ((struct batadv_hard_iface *)NULL)
#define BATADV_NUM_WORDS BITS_TO_LONGS(BATADV_TQ_LOCAL_WINDOW_SIZE)
#define BATADV_LOG_BUF_LEN 8192 /* has to be a power of 2 */
/* number of packets to send for broadcasts on different interface types */
#define BATADV_NUM_BCASTS_DEFAULT 1
#define BATADV_NUM_BCASTS_WIRELESS 3
/* length of the single packet used by the TP meter */
#define BATADV_TP_PACKET_LEN ETH_DATA_LEN
/* msecs after which an ARP_REQUEST is sent in broadcast as fallback */
#define ARP_REQ_DELAY 250
/* numbers of originator to contact for any PUT/GET DHT operation */
#define BATADV_DAT_CANDIDATES_NUM 3
/* BATADV_TQ_SIMILARITY_THRESHOLD - TQ points that a secondary metric can differ
* at most from the primary one in order to be still considered acceptable
*/
#define BATADV_TQ_SIMILARITY_THRESHOLD 50
#define BATADV_MAX_AGGREGATION_PACKETS 32
#define BATADV_MAX_AGGREGATION_BYTES 512
#define BATADV_MAX_AGGREGATION_MS 100
#define BATADV_BLA_PERIOD_LENGTH 10000 /* 10 seconds */
#define BATADV_BLA_BACKBONE_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 6)
#define BATADV_BLA_CLAIM_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 10)
#define BATADV_BLA_WAIT_PERIODS 3
#define BATADV_BLA_LOOPDETECT_PERIODS 6
#define BATADV_BLA_LOOPDETECT_TIMEOUT 3000 /* 3 seconds */
#define BATADV_DUPLIST_SIZE 16
#define BATADV_DUPLIST_TIMEOUT 500 /* 500 ms */
/* don't reset again within 30 seconds */
#define BATADV_RESET_PROTECTION_MS 30000
#define BATADV_EXPECTED_SEQNO_RANGE 65536
/**
* BATADV_TP_MAX_NUM - maximum number of simultaneously active tp sessions
*/
#define BATADV_TP_MAX_NUM 5
/**
* enum batadv_mesh_state - State of a mesh interface
*/
enum batadv_mesh_state {
/** @BATADV_MESH_INACTIVE: mesh interface is not yet running */
BATADV_MESH_INACTIVE,
/** @BATADV_MESH_ACTIVE: interface is up and running */
BATADV_MESH_ACTIVE,
/** @BATADV_MESH_DEACTIVATING: interface is getting shut down */
BATADV_MESH_DEACTIVATING,
};
#define BATADV_BCAST_QUEUE_LEN 256
#define BATADV_BATMAN_QUEUE_LEN 256
/**
* enum batadv_uev_action - action type of uevent
*/
enum batadv_uev_action {
/** @BATADV_UEV_ADD: gateway was selected (after none was selected) */
BATADV_UEV_ADD = 0,
/**
* @BATADV_UEV_DEL: selected gateway was removed and none is selected
* anymore
*/
BATADV_UEV_DEL,
/**
* @BATADV_UEV_CHANGE: a different gateway was selected as based gateway
*/
BATADV_UEV_CHANGE,
/**
* @BATADV_UEV_LOOPDETECT: loop was detected which cannot be handled by
* bridge loop avoidance
*/
BATADV_UEV_LOOPDETECT,
};
/**
* enum batadv_uev_type - Type of uevent
*/
enum batadv_uev_type {
/** @BATADV_UEV_GW: selected gateway was modified */
BATADV_UEV_GW = 0,
/** @BATADV_UEV_BLA: bridge loop avoidance event */
BATADV_UEV_BLA,
};
#define BATADV_GW_THRESHOLD 50
/* Number of fragment chains for each orig_node */
#define BATADV_FRAG_BUFFER_COUNT 8
/* Maximum number of fragments for one packet */
#define BATADV_FRAG_MAX_FRAGMENTS 16
/* Maxumim size of each fragment */
#define BATADV_FRAG_MAX_FRAG_SIZE 1280
/* Time to keep fragments while waiting for rest of the fragments */
#define BATADV_FRAG_TIMEOUT 10000
#define BATADV_DAT_CANDIDATE_NOT_FOUND 0
#define BATADV_DAT_CANDIDATE_ORIG 1
/* Debug Messages */
#ifdef pr_fmt
#undef pr_fmt
#endif
/* Append 'batman-adv: ' before kernel messages */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
/* Kernel headers */
#include <linux/atomic.h>
#include <linux/compiler.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
#include <linux/jiffies.h>
#include <linux/netdevice.h>
#include <linux/percpu.h>
#include <linux/skbuff.h>
#include <linux/types.h>
#include <uapi/linux/batadv_packet.h>
#include "types.h"
#include "main.h"
/**
* batadv_print_vid() - return printable version of vid information
* @vid: the VLAN identifier
*
* Return: -1 when no VLAN is used, VLAN id otherwise
*/
static inline int batadv_print_vid(unsigned short vid)
{
if (vid & BATADV_VLAN_HAS_TAG)
return (int)(vid & VLAN_VID_MASK);
else
return -1;
}
extern struct list_head batadv_hardif_list;
extern unsigned int batadv_hardif_generation;
extern struct workqueue_struct *batadv_event_workqueue;
int batadv_mesh_init(struct net_device *mesh_iface);
void batadv_mesh_free(struct net_device *mesh_iface);
bool batadv_is_my_mac(struct batadv_priv *bat_priv, const u8 *addr);
int batadv_max_header_len(void);
void batadv_skb_set_priority(struct sk_buff *skb, int offset);
int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *ptype,
struct net_device *orig_dev);
int
batadv_recv_handler_register(u8 packet_type,
int (*recv_handler)(struct sk_buff *,
struct batadv_hard_iface *));
void batadv_recv_handler_unregister(u8 packet_type);
/**
* batadv_compare_eth() - Compare two not u16 aligned Ethernet addresses
* @data1: Pointer to a six-byte array containing the Ethernet address
* @data2: Pointer other six-byte array containing the Ethernet address
*
* note: can't use ether_addr_equal() as it requires aligned memory
*
* Return: true if they are the same ethernet addr
*/
static inline bool batadv_compare_eth(const void *data1, const void *data2)
{
return ether_addr_equal_unaligned(data1, data2);
}
/**
* batadv_has_timed_out() - compares current time (jiffies) and timestamp +
* timeout
* @timestamp: base value to compare with (in jiffies)
* @timeout: added to base value before comparing (in milliseconds)
*
* Return: true if current time is after timestamp + timeout
*/
static inline bool batadv_has_timed_out(unsigned long timestamp,
unsigned int timeout)
{
return time_is_before_jiffies(timestamp + msecs_to_jiffies(timeout));
}
/**
* batadv_atomic_dec_not_zero() - Decrease unless the number is 0
* @v: pointer of type atomic_t
*
* Return: non-zero if v was not 0, and zero otherwise.
*/
#define batadv_atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0)
/**
* batadv_smallest_signed_int() - Returns the smallest signed integer in two's
* complement with the sizeof x
* @x: type of integer
*
* Return: smallest signed integer of type
*/
#define batadv_smallest_signed_int(x) (1u << (7u + 8u * (sizeof(x) - 1u)))
/**
* batadv_seq_before() - Checks if a sequence number x is a predecessor of y
* @x: potential predecessor of @y
* @y: value to compare @x against
*
* It handles overflows/underflows and can correctly check for a predecessor
* unless the variable sequence number has grown by more than
* 2**(bitwidth(x)-1)-1.
*
* This means that for a u8 with the maximum value 255, it would think:
*
* * when adding nothing - it is neither a predecessor nor a successor
* * before adding more than 127 to the starting value - it is a predecessor,
* * when adding 128 - it is neither a predecessor nor a successor,
* * after adding more than 127 to the starting value - it is a successor
*
* Return: true when x is a predecessor of y, false otherwise
*/
#define batadv_seq_before(x, y) ({ \
typeof(x)_d1 = (x); \
typeof(y)_d2 = (y); \
typeof(x)_dummy = (_d1 - _d2); \
(void)(&_d1 == &_d2); \
_dummy > batadv_smallest_signed_int(_dummy); \
})
/**
* batadv_seq_after() - Checks if a sequence number x is a successor of y
* @x: potential successor of @y
* @y: value to compare @x against
*
* It handles overflows/underflows and can correctly check for a successor
* unless the variable sequence number has grown by more than
* 2**(bitwidth(x)-1)-1.
*
* This means that for a u8 with the maximum value 255, it would think:
*
* * when adding nothing - it is neither a predecessor nor a successor
* * before adding more than 127 to the starting value - it is a predecessor,
* * when adding 128 - it is neither a predecessor nor a successor,
* * after adding more than 127 to the starting value - it is a successor
*
* Return: true when x is a successor of y, false otherwise
*/
#define batadv_seq_after(x, y) batadv_seq_before(y, x)
/**
* batadv_add_counter() - Add to per cpu statistics counter of mesh interface
* @bat_priv: the bat priv with all the mesh interface information
* @idx: counter index which should be modified
* @count: value to increase counter by
*
* Stop preemption on local cpu while incrementing the counter
*/
static inline void batadv_add_counter(struct batadv_priv *bat_priv, size_t idx,
size_t count)
{
this_cpu_add(bat_priv->bat_counters[idx], count);
}
/**
* batadv_inc_counter() - Increase per cpu statistics counter of mesh interface
* @b: the bat priv with all the mesh interface information
* @i: counter index which should be modified
*/
#define batadv_inc_counter(b, i) batadv_add_counter(b, i, 1)
/**
* BATADV_SKB_CB() - Get batadv_skb_cb from skb control buffer
* @__skb: skb holding the control buffer
*
* The members of the control buffer are defined in struct batadv_skb_cb in
* types.h. The macro is inspired by the similar macro TCP_SKB_CB() in tcp.h.
*
* Return: pointer to the batadv_skb_cb of the skb
*/
#define BATADV_SKB_CB(__skb) ((struct batadv_skb_cb *)&((__skb)->cb[0]))
unsigned short batadv_get_vid(struct sk_buff *skb, size_t header_len);
bool batadv_vlan_ap_isola_get(struct batadv_priv *bat_priv, unsigned short vid);
int batadv_throw_uevent(struct batadv_priv *bat_priv, enum batadv_uev_type type,
enum batadv_uev_action action, const char *data);
#endif /* _NET_BATMAN_ADV_MAIN_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner
*/
#ifndef _NET_BATMAN_ADV_MESH_INTERFACE_H_
#define _NET_BATMAN_ADV_MESH_INTERFACE_H_
#include "main.h"
#include <linux/kref.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/types.h>
int batadv_skb_head_push(struct sk_buff *skb, unsigned int len);
void batadv_interface_rx(struct net_device *mesh_iface,
struct sk_buff *skb, int hdr_size,
struct batadv_orig_node *orig_node);
bool batadv_meshif_is_valid(const struct net_device *net_dev);
extern struct rtnl_link_ops batadv_link_ops;
int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid);
void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv,
struct batadv_meshif_vlan *vlan);
void batadv_meshif_vlan_release(struct kref *ref);
struct batadv_meshif_vlan *batadv_meshif_vlan_get(struct batadv_priv *bat_priv,
unsigned short vid);
/**
* batadv_meshif_vlan_put() - decrease the vlan object refcounter and
* possibly release it
* @vlan: the vlan object to release
*/
static inline void batadv_meshif_vlan_put(struct batadv_meshif_vlan *vlan)
{
if (!vlan)
return;
kref_put(&vlan->refcount, batadv_meshif_vlan_release);
}
#endif /* _NET_BATMAN_ADV_MESH_INTERFACE_H_ */
File diff suppressed because it is too large Load Diff
+125
View File
@@ -0,0 +1,125 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Linus Lüssing
*/
#ifndef _NET_BATMAN_ADV_MULTICAST_H_
#define _NET_BATMAN_ADV_MULTICAST_H_
#include "main.h"
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/types.h>
/**
* enum batadv_forw_mode - the way a packet should be forwarded as
*/
enum batadv_forw_mode {
/**
* @BATADV_FORW_BCAST: forward the packet to all nodes via a batman-adv
* broadcast packet
*/
BATADV_FORW_BCAST,
/**
* @BATADV_FORW_UCASTS: forward the packet to some nodes via one
* or more batman-adv unicast packets
*/
BATADV_FORW_UCASTS,
/**
* @BATADV_FORW_MCAST: forward the packet to some nodes via a
* batman-adv multicast packet
*/
BATADV_FORW_MCAST,
/** @BATADV_FORW_NONE: don't forward, drop it */
BATADV_FORW_NONE,
};
#ifdef CONFIG_BATMAN_ADV_MCAST
enum batadv_forw_mode
batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
unsigned short vid, int *is_routable);
int batadv_mcast_forw_send(struct batadv_priv *bat_priv, struct sk_buff *skb,
unsigned short vid, int is_routable);
void batadv_mcast_init(struct batadv_priv *bat_priv);
int batadv_mcast_mesh_info_put(struct sk_buff *msg,
struct batadv_priv *bat_priv);
int batadv_mcast_flags_dump(struct sk_buff *msg, struct netlink_callback *cb);
void batadv_mcast_free(struct batadv_priv *bat_priv);
void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node);
/* multicast_forw.c */
int batadv_mcast_forw_tracker_tvlv_handler(struct batadv_priv *bat_priv,
struct sk_buff *skb);
unsigned int batadv_mcast_forw_packet_hdrlen(unsigned int num_dests);
bool batadv_mcast_forw_push(struct batadv_priv *bat_priv, struct sk_buff *skb,
unsigned short vid, int is_routable, int count);
int batadv_mcast_forw_mcsend(struct batadv_priv *bat_priv, struct sk_buff *skb);
#else
static inline enum batadv_forw_mode
batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
unsigned short vid, int *is_routable)
{
return BATADV_FORW_BCAST;
}
static inline int
batadv_mcast_forw_send(struct batadv_priv *bat_priv, struct sk_buff *skb,
unsigned short vid, int is_routable)
{
kfree_skb(skb);
return NET_XMIT_DROP;
}
static inline int batadv_mcast_init(struct batadv_priv *bat_priv)
{
return 0;
}
static inline int
batadv_mcast_mesh_info_put(struct sk_buff *msg, struct batadv_priv *bat_priv)
{
return 0;
}
static inline int batadv_mcast_flags_dump(struct sk_buff *msg,
struct netlink_callback *cb)
{
return -EOPNOTSUPP;
}
static inline void batadv_mcast_free(struct batadv_priv *bat_priv)
{
}
static inline void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node)
{
}
static inline int batadv_mcast_forw_mcsend(struct batadv_priv *bat_priv,
struct sk_buff *skb)
{
kfree_skb(skb);
return NET_XMIT_DROP;
}
#endif /* CONFIG_BATMAN_ADV_MCAST */
#endif /* _NET_BATMAN_ADV_MULTICAST_H_ */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+28
View File
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Matthias Schiffer
*/
#ifndef _NET_BATMAN_ADV_NETLINK_H_
#define _NET_BATMAN_ADV_NETLINK_H_
#include "main.h"
#include <linux/netlink.h>
#include <linux/types.h>
void batadv_netlink_register(void);
void batadv_netlink_unregister(void);
struct net_device *batadv_netlink_get_meshif(struct netlink_callback *cb);
struct batadv_hard_iface *
batadv_netlink_get_hardif(struct batadv_priv *bat_priv,
struct netlink_callback *cb);
int batadv_netlink_tpmeter_notify(struct batadv_priv *bat_priv, const u8 *dst,
u8 result, u32 test_time, u64 total_bytes,
u32 cookie);
extern struct genl_family batadv_netlink_family;
#endif /* _NET_BATMAN_ADV_NETLINK_H_ */
File diff suppressed because it is too large Load Diff
+170
View File
@@ -0,0 +1,170 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*/
#ifndef _NET_BATMAN_ADV_ORIGINATOR_H_
#define _NET_BATMAN_ADV_ORIGINATOR_H_
#include "main.h"
#include <linux/compiler.h>
#include <linux/if_ether.h>
#include <linux/jhash.h>
#include <linux/kref.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/types.h>
bool batadv_compare_orig(const struct hlist_node *node, const void *data2);
int batadv_originator_init(struct batadv_priv *bat_priv);
void batadv_originator_free(struct batadv_priv *bat_priv);
void batadv_purge_orig_ref(struct batadv_priv *bat_priv);
void batadv_orig_node_release(struct kref *ref);
struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
const u8 *addr);
struct batadv_hardif_neigh_node *
batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface,
const u8 *neigh_addr);
void batadv_hardif_neigh_release(struct kref *ref);
struct batadv_neigh_node *
batadv_neigh_node_get_or_create(struct batadv_orig_node *orig_node,
struct batadv_hard_iface *hard_iface,
const u8 *neigh_addr);
void batadv_neigh_node_release(struct kref *ref);
struct batadv_neigh_node *
batadv_orig_router_get(struct batadv_orig_node *orig_node,
const struct batadv_hard_iface *if_outgoing);
struct batadv_neigh_node *
batadv_orig_to_router(struct batadv_priv *bat_priv, u8 *orig_addr,
struct batadv_hard_iface *if_outgoing);
struct batadv_neigh_ifinfo *
batadv_neigh_ifinfo_new(struct batadv_neigh_node *neigh,
struct batadv_hard_iface *if_outgoing);
struct batadv_neigh_ifinfo *
batadv_neigh_ifinfo_get(struct batadv_neigh_node *neigh,
struct batadv_hard_iface *if_outgoing);
void batadv_neigh_ifinfo_release(struct kref *ref);
int batadv_hardif_neigh_dump(struct sk_buff *msg, struct netlink_callback *cb);
struct batadv_orig_ifinfo *
batadv_orig_ifinfo_get(struct batadv_orig_node *orig_node,
struct batadv_hard_iface *if_outgoing);
struct batadv_orig_ifinfo *
batadv_orig_ifinfo_new(struct batadv_orig_node *orig_node,
struct batadv_hard_iface *if_outgoing);
void batadv_orig_ifinfo_release(struct kref *ref);
int batadv_orig_dump(struct sk_buff *msg, struct netlink_callback *cb);
struct batadv_orig_node_vlan *
batadv_orig_node_vlan_new(struct batadv_orig_node *orig_node,
unsigned short vid);
struct batadv_orig_node_vlan *
batadv_orig_node_vlan_get(struct batadv_orig_node *orig_node,
unsigned short vid);
void batadv_orig_node_vlan_release(struct kref *ref);
/**
* batadv_choose_orig() - Return the index of the orig entry in the hash table
* @data: mac address of the originator node
* @size: the size of the hash table
*
* Return: the hash index where the object represented by @data should be
* stored at.
*/
static inline u32 batadv_choose_orig(const void *data, u32 size)
{
u32 hash = 0;
hash = jhash(data, ETH_ALEN, hash);
return hash % size;
}
struct batadv_orig_node *
batadv_orig_hash_find(struct batadv_priv *bat_priv, const void *data);
/**
* batadv_orig_node_vlan_put() - decrement the refcounter and possibly release
* the originator-vlan object
* @orig_vlan: the originator-vlan object to release
*/
static inline void
batadv_orig_node_vlan_put(struct batadv_orig_node_vlan *orig_vlan)
{
if (!orig_vlan)
return;
kref_put(&orig_vlan->refcount, batadv_orig_node_vlan_release);
}
/**
* batadv_neigh_ifinfo_put() - decrement the refcounter and possibly release
* the neigh_ifinfo
* @neigh_ifinfo: the neigh_ifinfo object to release
*/
static inline void
batadv_neigh_ifinfo_put(struct batadv_neigh_ifinfo *neigh_ifinfo)
{
if (!neigh_ifinfo)
return;
kref_put(&neigh_ifinfo->refcount, batadv_neigh_ifinfo_release);
}
/**
* batadv_hardif_neigh_put() - decrement the hardif neighbors refcounter
* and possibly release it
* @hardif_neigh: hardif neigh neighbor to free
*/
static inline void
batadv_hardif_neigh_put(struct batadv_hardif_neigh_node *hardif_neigh)
{
if (!hardif_neigh)
return;
kref_put(&hardif_neigh->refcount, batadv_hardif_neigh_release);
}
/**
* batadv_neigh_node_put() - decrement the neighbors refcounter and possibly
* release it
* @neigh_node: neigh neighbor to free
*/
static inline void batadv_neigh_node_put(struct batadv_neigh_node *neigh_node)
{
if (!neigh_node)
return;
kref_put(&neigh_node->refcount, batadv_neigh_node_release);
}
/**
* batadv_orig_ifinfo_put() - decrement the refcounter and possibly release
* the orig_ifinfo
* @orig_ifinfo: the orig_ifinfo object to release
*/
static inline void
batadv_orig_ifinfo_put(struct batadv_orig_ifinfo *orig_ifinfo)
{
if (!orig_ifinfo)
return;
kref_put(&orig_ifinfo->refcount, batadv_orig_ifinfo_release);
}
/**
* batadv_orig_node_put() - decrement the orig node refcounter and possibly
* release it
* @orig_node: the orig node to free
*/
static inline void batadv_orig_node_put(struct batadv_orig_node *orig_node)
{
if (!orig_node)
return;
kref_put(&orig_node->refcount, batadv_orig_node_release);
}
#endif /* _NET_BATMAN_ADV_ORIGINATOR_H_ */
File diff suppressed because it is too large Load Diff
+53
View File
@@ -0,0 +1,53 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*/
#ifndef _NET_BATMAN_ADV_ROUTING_H_
#define _NET_BATMAN_ADV_ROUTING_H_
#include "main.h"
#include <linux/skbuff.h>
#include <linux/types.h>
bool batadv_check_management_packet(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface,
int header_len);
void batadv_update_route(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
struct batadv_hard_iface *recv_if,
struct batadv_neigh_node *neigh_node);
int batadv_recv_icmp_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
int batadv_recv_unicast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
int batadv_recv_frag_packet(struct sk_buff *skb,
struct batadv_hard_iface *iface);
int batadv_recv_bcast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
#ifdef CONFIG_BATMAN_ADV_MCAST
int batadv_recv_mcast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
#else
static inline int batadv_recv_mcast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if)
{
kfree_skb(skb);
return NET_RX_DROP;
}
#endif
int batadv_recv_unicast_tvlv(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
int batadv_recv_unhandled_unicast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
struct batadv_neigh_node *
batadv_find_router(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
struct batadv_hard_iface *recv_if);
bool batadv_window_protected(struct batadv_priv *bat_priv, s32 seq_num_diff,
s32 seq_old_max_diff, unsigned long *last_reset,
bool *protection_started);
#endif /* _NET_BATMAN_ADV_ROUTING_H_ */
File diff suppressed because it is too large Load Diff
+116
View File
@@ -0,0 +1,116 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*/
#ifndef _NET_BATMAN_ADV_SEND_H_
#define _NET_BATMAN_ADV_SEND_H_
#include "main.h"
#include <linux/compiler.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <uapi/linux/batadv_packet.h>
void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet,
bool dropped);
struct batadv_forw_packet *
batadv_forw_packet_alloc(struct batadv_hard_iface *if_incoming,
struct batadv_hard_iface *if_outgoing,
atomic_t *queue_left,
struct batadv_priv *bat_priv,
struct sk_buff *skb);
bool batadv_forw_packet_steal(struct batadv_forw_packet *packet, spinlock_t *l);
void batadv_forw_packet_ogmv1_queue(struct batadv_priv *bat_priv,
struct batadv_forw_packet *forw_packet,
unsigned long send_time);
bool batadv_forw_packet_is_rebroadcast(struct batadv_forw_packet *forw_packet);
int batadv_send_skb_to_orig(struct sk_buff *skb,
struct batadv_orig_node *orig_node,
struct batadv_hard_iface *recv_if);
int batadv_send_skb_packet(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface,
const u8 *dst_addr);
int batadv_send_broadcast_skb(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface);
int batadv_send_unicast_skb(struct sk_buff *skb,
struct batadv_neigh_node *neigh_node);
int batadv_forw_bcast_packet(struct batadv_priv *bat_priv,
struct sk_buff *skb,
unsigned long delay,
bool own_packet);
void batadv_send_bcast_packet(struct batadv_priv *bat_priv,
struct sk_buff *skb,
unsigned long delay,
bool own_packet);
void
batadv_purge_outstanding_packets(struct batadv_priv *bat_priv,
const struct batadv_hard_iface *hard_iface);
bool batadv_send_skb_prepare_unicast_4addr(struct batadv_priv *bat_priv,
struct sk_buff *skb,
struct batadv_orig_node *orig_node,
int packet_subtype);
int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
struct sk_buff *skb, int packet_type,
int packet_subtype,
struct batadv_orig_node *orig_node,
unsigned short vid);
int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv,
struct sk_buff *skb, int packet_type,
int packet_subtype, u8 *dst_hint,
unsigned short vid);
int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb,
unsigned short vid);
/**
* batadv_send_skb_via_tt() - send an skb via TT lookup
* @bat_priv: the bat priv with all the mesh interface information
* @skb: the payload to send
* @dst_hint: can be used to override the destination contained in the skb
* @vid: the vid to be used to search the translation table
*
* Look up the recipient node for the destination address in the ethernet
* header via the translation table. Wrap the given skb into a batman-adv
* unicast header. Then send this frame to the according destination node.
*
* Return: NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
*/
static inline int batadv_send_skb_via_tt(struct batadv_priv *bat_priv,
struct sk_buff *skb, u8 *dst_hint,
unsigned short vid)
{
return batadv_send_skb_via_tt_generic(bat_priv, skb, BATADV_UNICAST, 0,
dst_hint, vid);
}
/**
* batadv_send_skb_via_tt_4addr() - send an skb via TT lookup
* @bat_priv: the bat priv with all the mesh interface information
* @skb: the payload to send
* @packet_subtype: the unicast 4addr packet subtype to use
* @dst_hint: can be used to override the destination contained in the skb
* @vid: the vid to be used to search the translation table
*
* Look up the recipient node for the destination address in the ethernet
* header via the translation table. Wrap the given skb into a batman-adv
* unicast-4addr header. Then send this frame to the according destination
* node.
*
* Return: NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
*/
static inline int batadv_send_skb_via_tt_4addr(struct batadv_priv *bat_priv,
struct sk_buff *skb,
int packet_subtype,
u8 *dst_hint,
unsigned short vid)
{
return batadv_send_skb_via_tt_generic(bat_priv, skb,
BATADV_UNICAST_4ADDR,
packet_subtype, dst_hint, vid);
}
#endif /* _NET_BATMAN_ADV_SEND_H_ */
File diff suppressed because it is too large Load Diff
+23
View File
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Edo Monticelli, Antonio Quartulli
*/
#ifndef _NET_BATMAN_ADV_TP_METER_H_
#define _NET_BATMAN_ADV_TP_METER_H_
#include "main.h"
#include <linux/skbuff.h>
#include <linux/types.h>
void batadv_tp_meter_init(void);
void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
u32 test_length, u32 *cookie);
void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
u8 return_value);
void batadv_tp_stop_all(struct batadv_priv *bat_priv);
void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb);
#endif /* _NET_BATMAN_ADV_TP_METER_H_ */
+8
View File
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Sven Eckelmann
*/
#define CREATE_TRACE_POINTS
#include "trace.h"
+64
View File
@@ -0,0 +1,64 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Sven Eckelmann
*/
#if !defined(_NET_BATMAN_ADV_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
#define _NET_BATMAN_ADV_TRACE_H_
#include "main.h"
#include <linux/netdevice.h>
#include <linux/percpu.h>
#include <linux/printk.h>
#include <linux/tracepoint.h>
#undef TRACE_SYSTEM
#define TRACE_SYSTEM batadv
/* provide dummy function when tracing is disabled */
#if !defined(CONFIG_BATMAN_ADV_TRACING)
#undef TRACE_EVENT
#define TRACE_EVENT(name, proto, ...) \
static inline void trace_ ## name(proto) {}
#endif /* CONFIG_BATMAN_ADV_TRACING */
TRACE_EVENT(batadv_dbg,
TP_PROTO(struct batadv_priv *bat_priv,
struct va_format *vaf),
TP_ARGS(bat_priv, vaf),
TP_STRUCT__entry(
__string(device, bat_priv->mesh_iface->name)
__string(driver, KBUILD_MODNAME)
__vstring(msg, vaf->fmt, vaf->va)
),
TP_fast_assign(
__assign_str(device);
__assign_str(driver);
__assign_vstr(msg, vaf->fmt, vaf->va);
),
TP_printk(
"%s %s %s",
__get_str(driver),
__get_str(device),
__get_str(msg)
)
);
#endif /* _NET_BATMAN_ADV_TRACE_H_ || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
/* This part must be outside protection */
#include <trace/define_trace.h>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,74 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich, Antonio Quartulli
*/
#ifndef _NET_BATMAN_ADV_TRANSLATION_TABLE_H_
#define _NET_BATMAN_ADV_TRANSLATION_TABLE_H_
#include "main.h"
#include <linux/kref.h>
#include <linux/netdevice.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/types.h>
int batadv_tt_init(struct batadv_priv *bat_priv);
bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr,
unsigned short vid, int ifindex, u32 mark);
u16 batadv_tt_local_remove(struct batadv_priv *bat_priv,
const u8 *addr, unsigned short vid,
const char *message, bool roaming);
int batadv_tt_local_dump(struct sk_buff *msg, struct netlink_callback *cb);
int batadv_tt_global_dump(struct sk_buff *msg, struct netlink_callback *cb);
void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
s32 match_vid, const char *message);
struct batadv_tt_global_entry *
batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
unsigned short vid);
void batadv_tt_global_entry_release(struct kref *ref);
int batadv_tt_global_hash_count(struct batadv_priv *bat_priv,
const u8 *addr, unsigned short vid);
struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
const u8 *src, const u8 *addr,
unsigned short vid);
void batadv_tt_free(struct batadv_priv *bat_priv);
bool batadv_is_my_client(struct batadv_priv *bat_priv, const u8 *addr,
unsigned short vid);
bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, u8 *src, u8 *dst,
unsigned short vid);
void batadv_tt_local_commit_changes(struct batadv_priv *bat_priv);
bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv,
u8 *addr, unsigned short vid);
bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv,
u8 *addr, unsigned short vid);
void batadv_tt_local_resize_to_mtu(struct net_device *mesh_iface);
bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
const unsigned char *addr,
unsigned short vid);
bool batadv_tt_global_is_isolated(struct batadv_priv *bat_priv,
const u8 *addr, unsigned short vid);
int batadv_tt_cache_init(void);
void batadv_tt_cache_destroy(void);
/**
* batadv_tt_global_entry_put() - decrement the tt_global_entry refcounter and
* possibly release it
* @tt_global_entry: tt_global_entry to be free'd
*/
static inline void
batadv_tt_global_entry_put(struct batadv_tt_global_entry *tt_global_entry)
{
if (!tt_global_entry)
return;
kref_put(&tt_global_entry->common.refcount,
batadv_tt_global_entry_release);
}
#endif /* _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ */
+748
View File
@@ -0,0 +1,748 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*/
#include "main.h"
#include <linux/bug.h>
#include <linux/byteorder/generic.h>
#include <linux/container_of.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/gfp.h>
#include <linux/if_ether.h>
#include <linux/kref.h>
#include <linux/limits.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/log2.h>
#include <linux/netdevice.h>
#include <linux/pkt_sched.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/types.h>
#include <uapi/linux/batadv_packet.h>
#include "originator.h"
#include "send.h"
#include "tvlv.h"
/**
* batadv_tvlv_handler_release() - release tvlv handler from lists and queue for
* free after rcu grace period
* @ref: kref pointer of the tvlv
*/
static void batadv_tvlv_handler_release(struct kref *ref)
{
struct batadv_tvlv_handler *tvlv_handler;
tvlv_handler = container_of(ref, struct batadv_tvlv_handler, refcount);
kfree_rcu(tvlv_handler, rcu);
}
/**
* batadv_tvlv_handler_put() - decrement the tvlv handler refcounter and
* possibly release it
* @tvlv_handler: the tvlv handler to free
*/
static void batadv_tvlv_handler_put(struct batadv_tvlv_handler *tvlv_handler)
{
if (!tvlv_handler)
return;
kref_put(&tvlv_handler->refcount, batadv_tvlv_handler_release);
}
/**
* batadv_tvlv_handler_get() - retrieve tvlv handler from the tvlv handler list
* based on the provided type and version (both need to match)
* @bat_priv: the bat priv with all the mesh interface information
* @type: tvlv handler type to look for
* @version: tvlv handler version to look for
*
* Return: tvlv handler if found or NULL otherwise.
*/
static struct batadv_tvlv_handler *
batadv_tvlv_handler_get(struct batadv_priv *bat_priv, u8 type, u8 version)
{
struct batadv_tvlv_handler *tvlv_handler_tmp, *tvlv_handler = NULL;
rcu_read_lock();
hlist_for_each_entry_rcu(tvlv_handler_tmp,
&bat_priv->tvlv.handler_list, list) {
if (tvlv_handler_tmp->type != type)
continue;
if (tvlv_handler_tmp->version != version)
continue;
if (!kref_get_unless_zero(&tvlv_handler_tmp->refcount))
continue;
tvlv_handler = tvlv_handler_tmp;
break;
}
rcu_read_unlock();
return tvlv_handler;
}
/**
* batadv_tvlv_container_release() - release tvlv from lists and free
* @ref: kref pointer of the tvlv
*/
static void batadv_tvlv_container_release(struct kref *ref)
{
struct batadv_tvlv_container *tvlv;
tvlv = container_of(ref, struct batadv_tvlv_container, refcount);
kfree(tvlv);
}
/**
* batadv_tvlv_container_put() - decrement the tvlv container refcounter and
* possibly release it
* @tvlv: the tvlv container to free
*/
static void batadv_tvlv_container_put(struct batadv_tvlv_container *tvlv)
{
if (!tvlv)
return;
kref_put(&tvlv->refcount, batadv_tvlv_container_release);
}
/**
* batadv_tvlv_container_get() - retrieve tvlv container from the tvlv container
* list based on the provided type and version (both need to match)
* @bat_priv: the bat priv with all the mesh interface information
* @type: tvlv container type to look for
* @version: tvlv container version to look for
*
* Has to be called with the appropriate locks being acquired
* (tvlv.container_list_lock).
*
* Return: tvlv container if found or NULL otherwise.
*/
static struct batadv_tvlv_container *
batadv_tvlv_container_get(struct batadv_priv *bat_priv, u8 type, u8 version)
{
struct batadv_tvlv_container *tvlv_tmp, *tvlv = NULL;
lockdep_assert_held(&bat_priv->tvlv.container_list_lock);
hlist_for_each_entry(tvlv_tmp, &bat_priv->tvlv.container_list, list) {
if (tvlv_tmp->tvlv_hdr.type != type)
continue;
if (tvlv_tmp->tvlv_hdr.version != version)
continue;
kref_get(&tvlv_tmp->refcount);
tvlv = tvlv_tmp;
break;
}
return tvlv;
}
/**
* batadv_tvlv_container_list_size() - calculate the size of the tvlv container
* list entries
* @bat_priv: the bat priv with all the mesh interface information
*
* Has to be called with the appropriate locks being acquired
* (tvlv.container_list_lock).
*
* Return: size of all currently registered tvlv containers in bytes.
*/
static size_t batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
{
struct batadv_tvlv_container *tvlv;
size_t tvlv_len = 0;
lockdep_assert_held(&bat_priv->tvlv.container_list_lock);
hlist_for_each_entry(tvlv, &bat_priv->tvlv.container_list, list) {
tvlv_len += sizeof(struct batadv_tvlv_hdr);
tvlv_len += ntohs(tvlv->tvlv_hdr.len);
}
return tvlv_len;
}
/**
* batadv_tvlv_container_remove() - remove tvlv container from the tvlv
* container list
* @bat_priv: the bat priv with all the mesh interface information
* @tvlv: the to be removed tvlv container
*
* Has to be called with the appropriate locks being acquired
* (tvlv.container_list_lock).
*/
static void batadv_tvlv_container_remove(struct batadv_priv *bat_priv,
struct batadv_tvlv_container *tvlv)
{
lockdep_assert_held(&bat_priv->tvlv.container_list_lock);
if (!tvlv)
return;
hlist_del(&tvlv->list);
/* first call to decrement the counter, second call to free */
batadv_tvlv_container_put(tvlv);
batadv_tvlv_container_put(tvlv);
}
/**
* batadv_tvlv_container_unregister() - unregister tvlv container based on the
* provided type and version (both need to match)
* @bat_priv: the bat priv with all the mesh interface information
* @type: tvlv container type to unregister
* @version: tvlv container type to unregister
*/
void batadv_tvlv_container_unregister(struct batadv_priv *bat_priv,
u8 type, u8 version)
{
struct batadv_tvlv_container *tvlv;
spin_lock_bh(&bat_priv->tvlv.container_list_lock);
tvlv = batadv_tvlv_container_get(bat_priv, type, version);
batadv_tvlv_container_remove(bat_priv, tvlv);
spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
}
/**
* batadv_tvlv_container_register() - register tvlv type, version and content
* to be propagated with each (primary interface) OGM
* @bat_priv: the bat priv with all the mesh interface information
* @type: tvlv container type
* @version: tvlv container version
* @tvlv_value: tvlv container content
* @tvlv_value_len: tvlv container content length
*
* If a container of the same type and version was already registered the new
* content is going to replace the old one.
*/
void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
u8 type, u8 version,
void *tvlv_value, u16 tvlv_value_len)
{
struct batadv_tvlv_container *tvlv_old, *tvlv_new;
if (!tvlv_value)
tvlv_value_len = 0;
tvlv_new = kzalloc(sizeof(*tvlv_new) + tvlv_value_len, GFP_ATOMIC);
if (!tvlv_new)
return;
tvlv_new->tvlv_hdr.version = version;
tvlv_new->tvlv_hdr.type = type;
tvlv_new->tvlv_hdr.len = htons(tvlv_value_len);
memcpy(tvlv_new + 1, tvlv_value, ntohs(tvlv_new->tvlv_hdr.len));
INIT_HLIST_NODE(&tvlv_new->list);
kref_init(&tvlv_new->refcount);
spin_lock_bh(&bat_priv->tvlv.container_list_lock);
tvlv_old = batadv_tvlv_container_get(bat_priv, type, version);
batadv_tvlv_container_remove(bat_priv, tvlv_old);
kref_get(&tvlv_new->refcount);
hlist_add_head(&tvlv_new->list, &bat_priv->tvlv.container_list);
spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
/* don't return reference to new tvlv_container */
batadv_tvlv_container_put(tvlv_new);
}
/**
* batadv_tvlv_realloc_packet_buff() - reallocate packet buffer to accommodate
* requested packet size
* @ogm_buff: ogm packet buffer
* @additional_packet_len: requested additional packet size on top of minimum
* size
*
* Return: true if the packet buffer could be changed to the requested size,
* false otherwise.
*/
static bool batadv_tvlv_realloc_packet_buff(struct batadv_ogm_buf *ogm_buff,
size_t additional_packet_len)
{
unsigned char *new_buff;
size_t newcapacity;
size_t newlen;
newlen = ogm_buff->header_length + additional_packet_len;
newcapacity = roundup_pow_of_two(newlen);
/* nothing to reallocate */
if (newcapacity == ogm_buff->capacity) {
ogm_buff->len = newlen;
return true;
}
new_buff = kmalloc(newcapacity, GFP_ATOMIC);
/* keep old buffer if kmalloc should fail */
if (!new_buff) {
/* continue to use oversize buffer if new data fits */
if (newlen <= ogm_buff->capacity) {
ogm_buff->len = newlen;
return true;
}
return false;
}
memcpy(new_buff, ogm_buff->buf, ogm_buff->header_length);
kfree(ogm_buff->buf);
ogm_buff->buf = new_buff;
ogm_buff->len = newlen;
ogm_buff->capacity = newcapacity;
return true;
}
/**
* batadv_tvlv_container_ogm_append() - append tvlv container content to given
* OGM packet buffer
* @bat_priv: the bat priv with all the mesh interface information
* @ogm_buff: ogm packet buffer
*
* The ogm packet might be enlarged or shrunk depending on the current size
* and the size of the to-be-appended tvlv containers.
*
* Return: size of all appended tvlv containers in bytes (max U16_MAX), negative
* if operation failed
*/
int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
struct batadv_ogm_buf *ogm_buff)
{
struct batadv_tvlv_container *tvlv;
struct batadv_tvlv_hdr *tvlv_hdr;
size_t tvlv_value_len;
void *tvlv_value;
int tvlv_len_ret;
bool ret;
spin_lock_bh(&bat_priv->tvlv.container_list_lock);
tvlv_value_len = batadv_tvlv_container_list_size(bat_priv);
if (tvlv_value_len > U16_MAX) {
tvlv_len_ret = -E2BIG;
goto end;
}
ret = batadv_tvlv_realloc_packet_buff(ogm_buff, tvlv_value_len);
if (!ret) {
tvlv_len_ret = -ENOMEM;
goto end;
}
tvlv_len_ret = tvlv_value_len;
if (!tvlv_value_len)
goto end;
tvlv_value = (u8 *)ogm_buff->buf + ogm_buff->header_length;
hlist_for_each_entry(tvlv, &bat_priv->tvlv.container_list, list) {
tvlv_hdr = tvlv_value;
tvlv_hdr->type = tvlv->tvlv_hdr.type;
tvlv_hdr->version = tvlv->tvlv_hdr.version;
tvlv_hdr->len = tvlv->tvlv_hdr.len;
tvlv_value = tvlv_hdr + 1;
memcpy(tvlv_value, tvlv + 1, ntohs(tvlv->tvlv_hdr.len));
tvlv_value = (u8 *)tvlv_value + ntohs(tvlv->tvlv_hdr.len);
}
end:
spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
return tvlv_len_ret;
}
/**
* batadv_tvlv_call_handler() - parse the given tvlv buffer to call the
* appropriate handlers
* @bat_priv: the bat priv with all the mesh interface information
* @tvlv_handler: tvlv callback function handling the tvlv content
* @packet_type: indicates for which packet type the TVLV handler is called
* @orig_node: orig node emitting the ogm packet
* @skb: the skb the TVLV handler is called for
* @tvlv_value: tvlv content
* @tvlv_value_len: tvlv content length
*
* Return: success if the handler was not found or the return value of the
* handler callback.
*/
static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
struct batadv_tvlv_handler *tvlv_handler,
u8 packet_type,
struct batadv_orig_node *orig_node,
struct sk_buff *skb, void *tvlv_value,
u16 tvlv_value_len)
{
unsigned int tvlv_offset;
u8 *src, *dst;
if (!tvlv_handler)
return NET_RX_SUCCESS;
switch (packet_type) {
case BATADV_IV_OGM:
case BATADV_OGM2:
if (!tvlv_handler->ogm_handler)
return NET_RX_SUCCESS;
if (!orig_node)
return NET_RX_SUCCESS;
tvlv_handler->ogm_handler(bat_priv, orig_node,
BATADV_NO_FLAGS,
tvlv_value, tvlv_value_len);
break;
case BATADV_UNICAST_TVLV:
if (!skb)
return NET_RX_SUCCESS;
if (!tvlv_handler->unicast_handler)
return NET_RX_SUCCESS;
src = ((struct batadv_unicast_tvlv_packet *)skb->data)->src;
dst = ((struct batadv_unicast_tvlv_packet *)skb->data)->dst;
return tvlv_handler->unicast_handler(bat_priv, src,
dst, tvlv_value,
tvlv_value_len);
case BATADV_MCAST:
if (!skb)
return NET_RX_SUCCESS;
if (!tvlv_handler->mcast_handler)
return NET_RX_SUCCESS;
tvlv_offset = (unsigned char *)tvlv_value - skb->data;
if (!skb_set_transport_header_careful(skb,
tvlv_offset + tvlv_value_len))
return -EINVAL;
skb_set_network_header(skb, tvlv_offset);
return tvlv_handler->mcast_handler(bat_priv, skb);
}
return NET_RX_SUCCESS;
}
/**
* batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container
* @tvlv_value: tvlv content
* @tvlv_value_len: tvlv content length
* @type: tvlv container type to look for
* @version: tvlv container version to look for
*
* Return: true if a container of the given type and version is present in the
* tvlv buffer, false otherwise.
*/
static bool batadv_tvlv_containers_contain(void *tvlv_value,
u16 tvlv_value_len, u8 type,
u8 version)
{
struct batadv_tvlv_hdr *tvlv_hdr;
u16 tvlv_value_cont_len;
while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
tvlv_hdr = tvlv_value;
tvlv_value_cont_len = ntohs(tvlv_hdr->len);
tvlv_value = tvlv_hdr + 1;
tvlv_value_len -= sizeof(*tvlv_hdr);
if (tvlv_value_cont_len > tvlv_value_len)
break;
/* the next tvlv header is accessed assuming (at least) 2-byte
* alignment, so it must start at an even offset.
*/
if (tvlv_value_cont_len & 1)
break;
if (tvlv_hdr->type == type && tvlv_hdr->version == version)
return true;
tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
tvlv_value_len -= tvlv_value_cont_len;
}
return false;
}
/**
* batadv_tvlv_containers_process() - parse the given tvlv buffer to call the
* appropriate handlers
* @bat_priv: the bat priv with all the mesh interface information
* @packet_type: indicates for which packet type the TVLV handler is called
* @orig_node: orig node emitting the ogm packet
* @skb: the skb the TVLV handler is called for
* @tvlv_value: tvlv content
* @tvlv_value_len: tvlv content length
*
* Return: success when processing an OGM or the return value of all called
* handler callbacks.
*/
int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
u8 packet_type,
struct batadv_orig_node *orig_node,
struct sk_buff *skb, void *tvlv_value,
u16 tvlv_value_len)
{
u16 tvlv_value_start_len = tvlv_value_len;
struct batadv_tvlv_handler *tvlv_handler;
void *tvlv_value_start = tvlv_value;
struct batadv_tvlv_hdr *tvlv_hdr;
u16 tvlv_value_cont_len;
u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
int ret = NET_RX_SUCCESS;
while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
tvlv_hdr = tvlv_value;
tvlv_value_cont_len = ntohs(tvlv_hdr->len);
tvlv_value = tvlv_hdr + 1;
tvlv_value_len -= sizeof(*tvlv_hdr);
if (tvlv_value_cont_len > tvlv_value_len)
break;
/* the next tvlv header is accessed assuming (at least) 2-byte
* alignment, so it must start at an even offset.
*/
if (tvlv_value_cont_len & 1)
break;
tvlv_handler = batadv_tvlv_handler_get(bat_priv,
tvlv_hdr->type,
tvlv_hdr->version);
ret |= batadv_tvlv_call_handler(bat_priv, tvlv_handler,
packet_type, orig_node, skb,
tvlv_value,
tvlv_value_cont_len);
batadv_tvlv_handler_put(tvlv_handler);
tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
tvlv_value_len -= tvlv_value_cont_len;
}
if (packet_type != BATADV_IV_OGM &&
packet_type != BATADV_OGM2)
return ret;
rcu_read_lock();
hlist_for_each_entry_rcu(tvlv_handler,
&bat_priv->tvlv.handler_list, list) {
if (!tvlv_handler->ogm_handler)
continue;
if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND))
continue;
/* if the corresponding container was present then the handler
* was already called from the loop above
*/
if (batadv_tvlv_containers_contain(tvlv_value_start,
tvlv_value_start_len,
tvlv_handler->type,
tvlv_handler->version))
continue;
tvlv_handler->ogm_handler(bat_priv, orig_node,
cifnotfound, NULL, 0);
}
rcu_read_unlock();
return NET_RX_SUCCESS;
}
/**
* batadv_tvlv_ogm_receive() - process an incoming ogm and call the appropriate
* handlers
* @bat_priv: the bat priv with all the mesh interface information
* @batadv_ogm_packet: ogm packet containing the tvlv containers
* @orig_node: orig node emitting the ogm packet
*/
void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
struct batadv_ogm_packet *batadv_ogm_packet,
struct batadv_orig_node *orig_node)
{
void *tvlv_value;
u16 tvlv_value_len;
if (!batadv_ogm_packet)
return;
tvlv_value_len = ntohs(batadv_ogm_packet->tvlv_len);
if (!tvlv_value_len)
return;
tvlv_value = batadv_ogm_packet + 1;
batadv_tvlv_containers_process(bat_priv, BATADV_IV_OGM, orig_node, NULL,
tvlv_value, tvlv_value_len);
}
/**
* batadv_tvlv_handler_register() - register tvlv handler based on the provided
* type and version (both need to match) for ogm tvlv payload and/or unicast
* payload
* @bat_priv: the bat priv with all the mesh interface information
* @optr: ogm tvlv handler callback function. This function receives the orig
* node, flags and the tvlv content as argument to process.
* @uptr: unicast tvlv handler callback function. This function receives the
* source & destination of the unicast packet as well as the tvlv content
* to process.
* @mptr: multicast packet tvlv handler callback function. This function
* receives the full skb to process, with the skb network header pointing
* to the current tvlv and the skb transport header pointing to the first
* byte after the current tvlv.
* @type: tvlv handler type to be registered
* @version: tvlv handler version to be registered
* @flags: flags to enable or disable TVLV API behavior
*/
void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
void (*optr)(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig,
u8 flags,
void *tvlv_value,
u16 tvlv_value_len),
int (*uptr)(struct batadv_priv *bat_priv,
u8 *src, u8 *dst,
void *tvlv_value,
u16 tvlv_value_len),
int (*mptr)(struct batadv_priv *bat_priv,
struct sk_buff *skb),
u8 type, u8 version, u8 flags)
{
struct batadv_tvlv_handler *tvlv_handler;
spin_lock_bh(&bat_priv->tvlv.handler_list_lock);
tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version);
if (tvlv_handler) {
spin_unlock_bh(&bat_priv->tvlv.handler_list_lock);
batadv_tvlv_handler_put(tvlv_handler);
return;
}
tvlv_handler = kzalloc_obj(*tvlv_handler, GFP_ATOMIC);
if (!tvlv_handler) {
spin_unlock_bh(&bat_priv->tvlv.handler_list_lock);
return;
}
tvlv_handler->ogm_handler = optr;
tvlv_handler->unicast_handler = uptr;
tvlv_handler->mcast_handler = mptr;
tvlv_handler->type = type;
tvlv_handler->version = version;
tvlv_handler->flags = flags;
kref_init(&tvlv_handler->refcount);
INIT_HLIST_NODE(&tvlv_handler->list);
kref_get(&tvlv_handler->refcount);
hlist_add_head_rcu(&tvlv_handler->list, &bat_priv->tvlv.handler_list);
spin_unlock_bh(&bat_priv->tvlv.handler_list_lock);
/* don't return reference to new tvlv_handler */
batadv_tvlv_handler_put(tvlv_handler);
}
/**
* batadv_tvlv_handler_unregister() - unregister tvlv handler based on the
* provided type and version (both need to match)
* @bat_priv: the bat priv with all the mesh interface information
* @type: tvlv handler type to be unregistered
* @version: tvlv handler version to be unregistered
*/
void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv,
u8 type, u8 version)
{
struct batadv_tvlv_handler *tvlv_handler;
tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version);
if (!tvlv_handler)
return;
batadv_tvlv_handler_put(tvlv_handler);
spin_lock_bh(&bat_priv->tvlv.handler_list_lock);
hlist_del_rcu(&tvlv_handler->list);
spin_unlock_bh(&bat_priv->tvlv.handler_list_lock);
batadv_tvlv_handler_put(tvlv_handler);
}
/**
* batadv_tvlv_unicast_send() - send a unicast packet with tvlv payload to the
* specified host
* @bat_priv: the bat priv with all the mesh interface information
* @src: source mac address of the unicast packet
* @dst: destination mac address of the unicast packet
* @type: tvlv type
* @version: tvlv version
* @tvlv_value: tvlv content
* @tvlv_value_len: tvlv content length
*/
void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, const u8 *src,
const u8 *dst, u8 type, u8 version,
void *tvlv_value, u16 tvlv_value_len)
{
struct batadv_unicast_tvlv_packet *unicast_tvlv_packet;
struct batadv_tvlv_hdr *tvlv_hdr;
struct batadv_orig_node *orig_node;
struct sk_buff *skb;
unsigned char *tvlv_buff;
unsigned int tvlv_len;
ssize_t hdr_len = sizeof(*unicast_tvlv_packet);
orig_node = batadv_orig_hash_find(bat_priv, dst);
if (!orig_node)
return;
tvlv_len = sizeof(*tvlv_hdr) + tvlv_value_len;
skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + hdr_len + tvlv_len);
if (!skb)
goto out;
skb->priority = TC_PRIO_CONTROL;
skb_reserve(skb, ETH_HLEN);
tvlv_buff = skb_put(skb, sizeof(*unicast_tvlv_packet) + tvlv_len);
unicast_tvlv_packet = (struct batadv_unicast_tvlv_packet *)tvlv_buff;
unicast_tvlv_packet->packet_type = BATADV_UNICAST_TVLV;
unicast_tvlv_packet->version = BATADV_COMPAT_VERSION;
unicast_tvlv_packet->ttl = BATADV_TTL;
unicast_tvlv_packet->reserved = 0;
unicast_tvlv_packet->tvlv_len = htons(tvlv_len);
unicast_tvlv_packet->align = 0;
ether_addr_copy(unicast_tvlv_packet->src, src);
ether_addr_copy(unicast_tvlv_packet->dst, dst);
tvlv_buff = (unsigned char *)(unicast_tvlv_packet + 1);
tvlv_hdr = (struct batadv_tvlv_hdr *)tvlv_buff;
tvlv_hdr->version = version;
tvlv_hdr->type = type;
tvlv_hdr->len = htons(tvlv_value_len);
tvlv_buff += sizeof(*tvlv_hdr);
memcpy(tvlv_buff, tvlv_value, tvlv_value_len);
batadv_send_skb_to_orig(skb, orig_node, NULL);
out:
batadv_orig_node_put(orig_node);
}
+51
View File
@@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*/
#ifndef _NET_BATMAN_ADV_TVLV_H_
#define _NET_BATMAN_ADV_TVLV_H_
#include "main.h"
#include <linux/skbuff.h>
#include <linux/types.h>
#include <uapi/linux/batadv_packet.h>
void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
u8 type, u8 version,
void *tvlv_value, u16 tvlv_value_len);
int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
struct batadv_ogm_buf *ogm_buff);
void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
struct batadv_ogm_packet *batadv_ogm_packet,
struct batadv_orig_node *orig_node);
void batadv_tvlv_container_unregister(struct batadv_priv *bat_priv,
u8 type, u8 version);
void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
void (*optr)(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig,
u8 flags,
void *tvlv_value,
u16 tvlv_value_len),
int (*uptr)(struct batadv_priv *bat_priv,
u8 *src, u8 *dst,
void *tvlv_value,
u16 tvlv_value_len),
int (*mptr)(struct batadv_priv *bat_priv,
struct sk_buff *skb),
u8 type, u8 version, u8 flags);
void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv,
u8 type, u8 version);
int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
u8 packet_type,
struct batadv_orig_node *orig_node,
struct sk_buff *skb, void *tvlv_buff,
u16 tvlv_buff_len);
void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, const u8 *src,
const u8 *dst, u8 type, u8 version,
void *tvlv_value, u16 tvlv_value_len);
#endif /* _NET_BATMAN_ADV_TVLV_H_ */
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
#include <generated/utsrelease.h>
#include <linux/module.h>
/* WARNING userspace tools like batctl were relying on
* /sys/module/batman_adv/version to check if the module was loaded. If it
* isn't present, they usually error out before finishing setup of the batadv
* interface. It should be kept until it is unlikely that there are active
* installations of these "broken" versions of these tools with recent kernels.
*/
MODULE_VERSION(UTS_RELEASE);