forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
910 lines
23 KiB
910 lines
23 KiB
// 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/atomic.h> |
|
#include <linux/cache.h> |
|
#include <linux/errno.h> |
|
#include <linux/if_ether.h> |
|
#include <linux/init.h> |
|
#include <linux/jiffies.h> |
|
#include <linux/kref.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 "gateway_common.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->soft_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->soft_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); |
|
INIT_WORK(&hardif_neigh->bat_v.metric_work, |
|
batadv_v_elp_throughput_metric_update); |
|
} |
|
|
|
/** |
|
* 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 soft 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 soft 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; |
|
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 { |
|
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { |
|
if (hard_iface->soft_iface != bat_priv->soft_iface) |
|
continue; |
|
|
|
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 soft 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 soft 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 soft 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 soft 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 soft interface information |
|
*/ |
|
static void batadv_v_init_sel_class(struct batadv_priv *bat_priv) |
|
{ |
|
/* set default throughput difference threshold to 5Mbps */ |
|
atomic_set(&bat_priv->gw.sel_class, 50); |
|
} |
|
|
|
static ssize_t batadv_v_store_sel_class(struct batadv_priv *bat_priv, |
|
char *buff, size_t count) |
|
{ |
|
u32 old_class, class; |
|
|
|
if (!batadv_parse_throughput(bat_priv->soft_iface, buff, |
|
"B.A.T.M.A.N. V GW selection class", |
|
&class)) |
|
return -EINVAL; |
|
|
|
old_class = atomic_read(&bat_priv->gw.sel_class); |
|
atomic_set(&bat_priv->gw.sel_class, class); |
|
|
|
if (old_class != class) |
|
batadv_gw_reselect(bat_priv); |
|
|
|
return count; |
|
} |
|
|
|
/** |
|
* 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 soft 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 a originator would be selected as GW |
|
* @bat_priv: the bat priv with all the soft 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 = atomic_read(&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 soft 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 soft 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, |
|
.store_sel_class = batadv_v_store_sel_class, |
|
.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 |
|
*/ |
|
atomic_set(&hard_iface->bat_v.throughput_override, 0); |
|
atomic_set(&hard_iface->bat_v.elp_interval, 500); |
|
|
|
hard_iface->bat_v.aggr_len = 0; |
|
skb_queue_head_init(&hard_iface->bat_v.aggr_list); |
|
INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq, |
|
batadv_v_ogm_aggr_work); |
|
} |
|
|
|
/** |
|
* 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; |
|
}
|
|
|