Commit 00062a93 authored by David S. Miller's avatar David S. Miller

Merge tag 'batadv-next-for-davem-20160816' of git://git.open-mesh.org/linux-merge

Simon Wunderlich says:

====================
pull request for net-next: batman-adv 2016-08-16

This feature patchset is all about adding netlink support, which should
supersede our debugfs configuration interface in the long run. It is
especially necessary when batman-adv should be used in different
namespaces, since debugfs can not differentiate between those.

More specifically, the following changes are included:

 - Two fixes for namespace handling by Andrew Lunn, checking also the
   namespaces for parent interfaces, and supress debugfs entries
   for non-default netns

 - Implement various netlink commands for the new interface, by
   Matthias Schiffer, Andrew Lunn, Sven Eckelmann and Simon Wunderlich
   (13 patches):
    * routing algorithm list
    * hardif list
    * translation tables (local and global)
    * TTVN for the translation tables
    * originator and neighbor tables for B.A.T.M.A.N. IV
      and B.A.T.M.A.N. V
    * gateway dump functionality for B.A.T.M.A.N. IV
      and B.A.T.M.A.N. V
    * Bridge Loop Avoidance claims, and corresponding BLA group
    * Bridge Loop Avoidance backbone tables

 - Finally, mark batman-adv as netns compatible, by Andrew Lunn (1 patch)
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 268ef4b7 4c09a08b
......@@ -22,6 +22,42 @@
#define BATADV_NL_MCAST_GROUP_TPMETER "tpmeter"
/**
* enum batadv_tt_client_flags - TT client specific flags
* @BATADV_TT_CLIENT_DEL: the client has to be deleted from the table
* @BATADV_TT_CLIENT_ROAM: the client roamed to/from another node and the new
* update telling its new real location has not been received/sent yet
* @BATADV_TT_CLIENT_WIFI: this client is connected through a wifi interface.
* This information is used by the "AP Isolation" feature
* @BATADV_TT_CLIENT_ISOLA: this client is considered "isolated". This
* information is used by the Extended Isolation feature
* @BATADV_TT_CLIENT_NOPURGE: this client should never be removed from the table
* @BATADV_TT_CLIENT_NEW: this client has been added to the local table but has
* not been announced yet
* @BATADV_TT_CLIENT_PENDING: this client is marked for removal but it is kept
* in the table for one more originator interval for consistency purposes
* @BATADV_TT_CLIENT_TEMP: this global client has been detected to be part of
* the network but no nnode has already announced it
*
* Bits from 0 to 7 are called _remote flags_ because they are sent on the wire.
* Bits from 8 to 15 are called _local flags_ because they are used for local
* computations only.
*
* Bits from 4 to 7 - a subset of remote flags - are ensured to be in sync with
* the other nodes in the network. To achieve this goal these flags are included
* in the TT CRC computation.
*/
enum batadv_tt_client_flags {
BATADV_TT_CLIENT_DEL = (1 << 0),
BATADV_TT_CLIENT_ROAM = (1 << 1),
BATADV_TT_CLIENT_WIFI = (1 << 4),
BATADV_TT_CLIENT_ISOLA = (1 << 5),
BATADV_TT_CLIENT_NOPURGE = (1 << 8),
BATADV_TT_CLIENT_NEW = (1 << 9),
BATADV_TT_CLIENT_PENDING = (1 << 10),
BATADV_TT_CLIENT_TEMP = (1 << 11),
};
/**
* enum batadv_nl_attrs - batman-adv netlink attributes
*
......@@ -40,6 +76,26 @@
* @BATADV_ATTR_TPMETER_BYTES: amount of acked bytes during run
* @BATADV_ATTR_TPMETER_COOKIE: session cookie to match tp_meter session
* @BATADV_ATTR_PAD: attribute used for padding for 64-bit alignment
* @BATADV_ATTR_ACTIVE: Flag indicating if the hard interface is active
* @BATADV_ATTR_TT_ADDRESS: Client MAC address
* @BATADV_ATTR_TT_TTVN: Translation table version
* @BATADV_ATTR_TT_LAST_TTVN: Previous translation table version
* @BATADV_ATTR_TT_CRC32: CRC32 over translation table
* @BATADV_ATTR_TT_VID: VLAN ID
* @BATADV_ATTR_TT_FLAGS: Translation table client flags
* @BATADV_ATTR_FLAG_BEST: Flags indicating entry is the best
* @BATADV_ATTR_LAST_SEEN_MSECS: Time in milliseconds since last seen
* @BATADV_ATTR_NEIGH_ADDRESS: Neighbour MAC address
* @BATADV_ATTR_TQ: TQ to neighbour
* @BATADV_ATTR_THROUGHPUT: Estimated throughput to Neighbour
* @BATADV_ATTR_BANDWIDTH_UP: Reported uplink bandwidth
* @BATADV_ATTR_BANDWIDTH_DOWN: Reported downlink bandwidth
* @BATADV_ATTR_ROUTER: Gateway router MAC address
* @BATADV_ATTR_BLA_OWN: Flag indicating own originator
* @BATADV_ATTR_BLA_ADDRESS: Bridge loop avoidance claim MAC address
* @BATADV_ATTR_BLA_VID: BLA VLAN ID
* @BATADV_ATTR_BLA_BACKBONE: BLA gateway originator MAC address
* @BATADV_ATTR_BLA_CRC: BLA CRC
* @__BATADV_ATTR_AFTER_LAST: internal use
* @NUM_BATADV_ATTR: total number of batadv_nl_attrs available
* @BATADV_ATTR_MAX: highest attribute number currently defined
......@@ -60,6 +116,26 @@ enum batadv_nl_attrs {
BATADV_ATTR_TPMETER_BYTES,
BATADV_ATTR_TPMETER_COOKIE,
BATADV_ATTR_PAD,
BATADV_ATTR_ACTIVE,
BATADV_ATTR_TT_ADDRESS,
BATADV_ATTR_TT_TTVN,
BATADV_ATTR_TT_LAST_TTVN,
BATADV_ATTR_TT_CRC32,
BATADV_ATTR_TT_VID,
BATADV_ATTR_TT_FLAGS,
BATADV_ATTR_FLAG_BEST,
BATADV_ATTR_LAST_SEEN_MSECS,
BATADV_ATTR_NEIGH_ADDRESS,
BATADV_ATTR_TQ,
BATADV_ATTR_THROUGHPUT,
BATADV_ATTR_BANDWIDTH_UP,
BATADV_ATTR_BANDWIDTH_DOWN,
BATADV_ATTR_ROUTER,
BATADV_ATTR_BLA_OWN,
BATADV_ATTR_BLA_ADDRESS,
BATADV_ATTR_BLA_VID,
BATADV_ATTR_BLA_BACKBONE,
BATADV_ATTR_BLA_CRC,
/* add attributes above here, update the policy in netlink.c */
__BATADV_ATTR_AFTER_LAST,
NUM_BATADV_ATTR = __BATADV_ATTR_AFTER_LAST,
......@@ -73,6 +149,15 @@ enum batadv_nl_attrs {
* @BATADV_CMD_GET_MESH_INFO: Query basic information about batman-adv device
* @BATADV_CMD_TP_METER: Start a tp meter session
* @BATADV_CMD_TP_METER_CANCEL: Cancel a tp meter session
* @BATADV_CMD_GET_ROUTING_ALGOS: Query the list of routing algorithms.
* @BATADV_CMD_GET_HARDIFS: Query list of hard interfaces
* @BATADV_CMD_GET_TRANSTABLE_LOCAL: Query list of local translations
* @BATADV_CMD_GET_TRANSTABLE_GLOBAL Query list of global translations
* @BATADV_CMD_GET_ORIGINATORS: Query list of originators
* @BATADV_CMD_GET_NEIGHBORS: Query list of neighbours
* @BATADV_CMD_GET_GATEWAYS: Query list of gateways
* @BATADV_CMD_GET_BLA_CLAIM: Query list of bridge loop avoidance claims
* @BATADV_CMD_GET_BLA_BACKBONE: Query list of bridge loop avoidance backbones
* @__BATADV_CMD_AFTER_LAST: internal use
* @BATADV_CMD_MAX: highest used command number
*/
......@@ -81,6 +166,15 @@ enum batadv_nl_commands {
BATADV_CMD_GET_MESH_INFO,
BATADV_CMD_TP_METER,
BATADV_CMD_TP_METER_CANCEL,
BATADV_CMD_GET_ROUTING_ALGOS,
BATADV_CMD_GET_HARDIFS,
BATADV_CMD_GET_TRANSTABLE_LOCAL,
BATADV_CMD_GET_TRANSTABLE_GLOBAL,
BATADV_CMD_GET_ORIGINATORS,
BATADV_CMD_GET_NEIGHBORS,
BATADV_CMD_GET_GATEWAYS,
BATADV_CMD_GET_BLA_CLAIM,
BATADV_CMD_GET_BLA_BACKBONE,
/* add new commands above here */
__BATADV_CMD_AFTER_LAST,
BATADV_CMD_MAX = __BATADV_CMD_AFTER_LAST - 1
......
......@@ -20,12 +20,18 @@
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/moduleparam.h>
#include <linux/netlink.h>
#include <linux/printk.h>
#include <linux/seq_file.h>
#include <linux/skbuff.h>
#include <linux/stddef.h>
#include <linux/string.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;
......@@ -138,3 +144,65 @@ static struct kparam_string batadv_param_string_ra = {
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;
}
......@@ -22,7 +22,9 @@
#include <linux/types.h>
struct netlink_callback;
struct seq_file;
struct sk_buff;
extern char batadv_routing_algo[];
extern struct list_head batadv_hardif_list;
......@@ -31,5 +33,6 @@ void batadv_algo_init(void);
int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops);
int batadv_algo_select(struct batadv_priv *bat_priv, char *name);
int batadv_algo_seq_print_text(struct seq_file *seq, void *offset);
int batadv_algo_dump(struct sk_buff *msg, struct netlink_callback *cb);
#endif /* _NET_BATMAN_ADV_BAT_ALGO_H_ */
......@@ -35,6 +35,7 @@
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/netdevice.h>
#include <linux/netlink.h>
#include <linux/pkt_sched.h>
#include <linux/printk.h>
#include <linux/random.h>
......@@ -48,6 +49,9 @@
#include <linux/string.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <net/genetlink.h>
#include <net/netlink.h>
#include <uapi/linux/batman_adv.h>
#include "bat_algo.h"
#include "bitarray.h"
......@@ -55,6 +59,7 @@
#include "hard-interface.h"
#include "hash.h"
#include "log.h"
#include "netlink.h"
#include "network-coding.h"
#include "originator.h"
#include "packet.h"
......@@ -1947,6 +1952,235 @@ static void batadv_iv_ogm_orig_print(struct batadv_priv *bat_priv,
seq_puts(seq, "No batman nodes in range ...\n");
}
/**
* batadv_iv_ogm_neigh_get_tq_avg - Get the TQ average for a neighbour on a
* given outgoing interface.
* @neigh_node: Neighbour of interest
* @if_outgoing: Outgoing interface of interest
* @tq_avg: Pointer of where to store the TQ average
*
* Return: False if no average TQ available, otherwise true.
*/
static bool
batadv_iv_ogm_neigh_get_tq_avg(struct batadv_neigh_node *neigh_node,
struct batadv_hard_iface *if_outgoing,
u8 *tq_avg)
{
struct batadv_neigh_ifinfo *n_ifinfo;
n_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing);
if (!n_ifinfo)
return false;
*tq_avg = n_ifinfo->bat_iv.tq_avg;
batadv_neigh_ifinfo_put(n_ifinfo);
return true;
}
/**
* batadv_iv_ogm_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_iv_ogm_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)
{
void *hdr;
u8 tq_avg;
unsigned int last_seen_msecs;
last_seen_msecs = jiffies_to_msecs(jiffies - orig_node->last_seen);
if (!batadv_iv_ogm_neigh_get_tq_avg(neigh_node, if_outgoing, &tq_avg))
return 0;
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_u32(msg, BATADV_ATTR_HARD_IFINDEX,
neigh_node->if_incoming->net_dev->ifindex) ||
nla_put_u8(msg, BATADV_ATTR_TQ, tq_avg) ||
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_iv_ogm_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_iv_ogm_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;
u8 tq_avg_best;
neigh_node_best = batadv_orig_router_get(orig_node, if_outgoing);
if (!neigh_node_best)
goto out;
if (!batadv_iv_ogm_neigh_get_tq_avg(neigh_node_best, if_outgoing,
&tq_avg_best))
goto out;
if (tq_avg_best == 0)
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_iv_ogm_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:
if (neigh_node_best)
batadv_neigh_node_put(neigh_node_best);
*sub_s = 0;
return 0;
}
/**
* batadv_iv_ogm_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_iv_ogm_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_iv_ogm_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_iv_ogm_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_iv_ogm_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_iv_ogm_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;
}
/**
* batadv_iv_hardif_neigh_print - print a single hop neighbour node
* @seq: neighbour table seq_file struct
......@@ -2043,6 +2277,136 @@ static bool batadv_iv_ogm_neigh_diff(struct batadv_neigh_node *neigh1,
return ret;
}
/**
* batadv_iv_ogm_neigh_dump_neigh - Dump a neighbour into a netlink message
* @msg: Netlink message to dump into
* @portid: Port making netlink request
* @seq: Sequence number of netlink message
* @hardif_neigh: Neighbour to be dumped
*
* Return: Error code, or 0 on success
*/
static int
batadv_iv_ogm_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;
last_seen_msecs = jiffies_to_msecs(jiffies - hardif_neigh->last_seen);
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_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))
goto nla_put_failure;
genlmsg_end(msg, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
}
/**
* batadv_iv_ogm_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: Hard interface to dump the neighbours for
* @idx_s: Number of entries to skip
*
* This function assumes the caller holds rcu_read_lock().
*
* Return: Error code, or 0 on success
*/
static int
batadv_iv_ogm_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_iv_ogm_neigh_dump_neigh(msg, portid, seq,
hardif_neigh)) {
*idx_s = idx - 1;
return -EMSGSIZE;
}
}
*idx_s = 0;
return 0;
}
/**
* batadv_iv_ogm_neigh_dump - Dump the neighbours 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 dump to this hard interfaace
*/
static void
batadv_iv_ogm_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_iv_ogm_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_iv_ogm_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_iv_ogm_neigh_cmp - compare the metrics of two neighbors
* @neigh1: the first neighbor object of the comparison
......@@ -2317,6 +2681,110 @@ static void batadv_iv_gw_print(struct batadv_priv *bat_priv,
seq_puts(seq, "No gateways in range ...\n");
}
/**
* batadv_iv_gw_dump_entry - Dump a gateway 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
* @gw_node: Gateway to be dumped
*
* Return: Error code, or 0 on success
*/
static int batadv_iv_gw_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
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;
int ret = -EINVAL;
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, seq, &batadv_netlink_family,
NLM_F_MULTI, BATADV_CMD_GET_GATEWAYS);
if (!hdr) {
ret = -ENOBUFS;
goto out;
}
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) ||
nla_put_u8(msg, BATADV_ATTR_TQ, router_ifinfo->bat_iv.tq_avg) ||
nla_put(msg, BATADV_ATTR_ROUTER, ETH_ALEN,
router->addr) ||
nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
router->if_incoming->net_dev->name) ||
nla_put_u32(msg, BATADV_ATTR_BANDWIDTH_DOWN,
gw_node->bandwidth_down) ||
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:
if (router_ifinfo)
batadv_neigh_ifinfo_put(router_ifinfo);
if (router)
batadv_neigh_node_put(router);
return ret;
}
/**
* batadv_iv_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_iv_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;
rcu_read_lock();
hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) {
if (idx++ < idx_skip)
continue;
if (batadv_iv_gw_dump_entry(msg, portid, cb->nlh->nlmsg_seq,
bat_priv, gw_node)) {
idx_skip = idx - 1;
goto unlock;
}
}
idx_skip = idx;
unlock:
rcu_read_unlock();
cb->args[0] = idx_skip;
}
static struct batadv_algo_ops batadv_batman_iv __read_mostly = {
.name = "BATMAN_IV",
.iface = {
......@@ -2330,9 +2798,11 @@ static struct batadv_algo_ops batadv_batman_iv __read_mostly = {
.cmp = batadv_iv_ogm_neigh_cmp,
.is_similar_or_better = batadv_iv_ogm_neigh_is_sob,
.print = batadv_iv_neigh_print,
.dump = batadv_iv_ogm_neigh_dump,
},
.orig = {
.print = batadv_iv_ogm_orig_print,
.dump = batadv_iv_ogm_orig_dump,
.free = batadv_iv_ogm_orig_free,
.add_if = batadv_iv_ogm_orig_add_if,
.del_if = batadv_iv_ogm_orig_del_if,
......@@ -2341,6 +2811,7 @@ static struct batadv_algo_ops batadv_batman_iv __read_mostly = {
.get_best_gw_node = batadv_iv_gw_get_best_gw_node,
.is_eligible = batadv_iv_gw_is_eligible,
.print = batadv_iv_gw_print,
.dump = batadv_iv_gw_dump,
},
};
......
......@@ -22,17 +22,22 @@
#include <linux/bug.h>
#include <linux/cache.h>
#include <linux/errno.h>
#include <linux/if_ether.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/netdevice.h>
#include <linux/netlink.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/seq_file.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <net/genetlink.h>
#include <net/netlink.h>
#include <uapi/linux/batman_adv.h>
#include "bat_algo.h"
#include "bat_v_elp.h"
......@@ -42,9 +47,12 @@
#include "hard-interface.h"
#include "hash.h"
#include "log.h"
#include "netlink.h"
#include "originator.h"
#include "packet.h"
struct sk_buff;
static void batadv_v_iface_activate(struct batadv_hard_iface *hard_iface)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
......@@ -205,6 +213,138 @@ static void batadv_v_neigh_print(struct batadv_priv *bat_priv,
seq_puts(seq, "No batman nodes in range ...\n");
}
/**
* 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_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_print - print the originator table
* @bat_priv: the bat priv with all the soft interface information
......@@ -272,6 +412,204 @@ static void batadv_v_orig_print(struct batadv_priv *bat_priv,
seq_puts(seq, "No batman nodes in range ...\n");
}
/**
* 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_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:
if (neigh_node_best)
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,
......@@ -559,6 +897,130 @@ static void batadv_v_gw_print(struct batadv_priv *bat_priv,
seq_puts(seq, "No gateways in range ...\n");
}
/**
* batadv_v_gw_dump_entry - Dump a gateway 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
* @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, u32 seq,
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;
int ret = -EINVAL;
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, seq, &batadv_netlink_family,
NLM_F_MULTI, BATADV_CMD_GET_GATEWAYS);
if (!hdr) {
ret = -ENOBUFS;
goto out;
}
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_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:
if (router_ifinfo)
batadv_neigh_ifinfo_put(router_ifinfo);
if (router)
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;
rcu_read_lock();
hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) {
if (idx++ < idx_skip)
continue;
if (batadv_v_gw_dump_entry(msg, portid, cb->nlh->nlmsg_seq,
bat_priv, gw_node)) {
idx_skip = idx - 1;
goto unlock;
}
}
idx_skip = idx;
unlock:
rcu_read_unlock();
cb->args[0] = idx_skip;
}
static struct batadv_algo_ops batadv_batman_v __read_mostly = {
.name = "BATMAN_V",
.iface = {
......@@ -573,9 +1035,11 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = {
.cmp = batadv_v_neigh_cmp,
.is_similar_or_better = batadv_v_neigh_is_sob,
.print = batadv_v_neigh_print,
.dump = batadv_v_neigh_dump,
},
.orig = {
.print = batadv_v_orig_print,
.dump = batadv_v_orig_dump,
},
.gw = {
.store_sel_class = batadv_v_store_sel_class,
......@@ -583,6 +1047,7 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = {
.get_best_gw_node = batadv_v_gw_get_best_gw_node,
.is_eligible = batadv_v_gw_is_eligible,
.print = batadv_v_gw_print,
.dump = batadv_v_gw_dump,
},
};
......
......@@ -35,6 +35,7 @@
#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/seq_file.h>
......@@ -45,12 +46,18 @@
#include <linux/string.h>
#include <linux/workqueue.h>
#include <net/arp.h>
#include <net/genetlink.h>
#include <net/netlink.h>
#include <net/sock.h>
#include <uapi/linux/batman_adv.h>
#include "hard-interface.h"
#include "hash.h"
#include "log.h"
#include "netlink.h"
#include "originator.h"
#include "packet.h"
#include "soft-interface.h"
#include "sysfs.h"
#include "translation-table.h"
......@@ -2051,6 +2058,168 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
return 0;
}
/**
* batadv_bla_claim_dump_entry - dump one entry of the claim table
* to a netlink socket
* @msg: buffer for the message
* @portid: netlink port
* @seq: Sequence number of netlink message
* @primary_if: primary interface
* @claim: entry to dump
*
* Return: 0 or error code.
*/
static int
batadv_bla_claim_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
struct batadv_hard_iface *primary_if,
struct batadv_bla_claim *claim)
{
u8 *primary_addr = primary_if->net_dev->dev_addr;
u16 backbone_crc;
bool is_own;
void *hdr;
int ret = -EINVAL;
hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
NLM_F_MULTI, BATADV_CMD_GET_BLA_CLAIM);
if (!hdr) {
ret = -ENOBUFS;
goto out;
}
is_own = batadv_compare_eth(claim->backbone_gw->orig,
primary_addr);
spin_lock_bh(&claim->backbone_gw->crc_lock);
backbone_crc = claim->backbone_gw->crc;
spin_unlock_bh(&claim->backbone_gw->crc_lock);
if (is_own)
if (nla_put_flag(msg, BATADV_ATTR_BLA_OWN)) {
genlmsg_cancel(msg, hdr);
goto out;
}
if (nla_put(msg, BATADV_ATTR_BLA_ADDRESS, ETH_ALEN, claim->addr) ||
nla_put_u16(msg, BATADV_ATTR_BLA_VID, claim->vid) ||
nla_put(msg, BATADV_ATTR_BLA_BACKBONE, ETH_ALEN,
claim->backbone_gw->orig) ||
nla_put_u16(msg, BATADV_ATTR_BLA_CRC,
backbone_crc)) {
genlmsg_cancel(msg, hdr);
goto out;
}
genlmsg_end(msg, hdr);
ret = 0;
out:
return ret;
}
/**
* batadv_bla_claim_dump_bucket - dump one bucket of the claim table
* to a netlink socket
* @msg: buffer for the message
* @portid: netlink port
* @seq: Sequence number of netlink message
* @primary_if: primary interface
* @head: bucket to dump
* @idx_skip: How many entries to skip
*
* Return: always 0.
*/
static int
batadv_bla_claim_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
struct batadv_hard_iface *primary_if,
struct hlist_head *head, int *idx_skip)
{
struct batadv_bla_claim *claim;
int idx = 0;
rcu_read_lock();
hlist_for_each_entry_rcu(claim, head, hash_entry) {
if (idx++ < *idx_skip)
continue;
if (batadv_bla_claim_dump_entry(msg, portid, seq,
primary_if, claim)) {
*idx_skip = idx - 1;
goto unlock;
}
}
*idx_skip = idx;
unlock:
rcu_read_unlock();
return 0;
}
/**
* batadv_bla_claim_dump - dump claim table to a netlink socket
* @msg: buffer for the message
* @cb: callback structure containing arguments
*
* Return: message length.
*/
int batadv_bla_claim_dump(struct sk_buff *msg, struct netlink_callback *cb)
{
struct batadv_hard_iface *primary_if = NULL;
int portid = NETLINK_CB(cb->skb).portid;
struct net *net = sock_net(cb->skb->sk);
struct net_device *soft_iface;
struct batadv_hashtable *hash;
struct batadv_priv *bat_priv;
int bucket = cb->args[0];
struct hlist_head *head;
int idx = cb->args[1];
int ifindex;
int ret = 0;
ifindex = batadv_netlink_get_ifindex(cb->nlh,
BATADV_ATTR_MESH_IFINDEX);
if (!ifindex)
return -EINVAL;
soft_iface = dev_get_by_index(net, ifindex);
if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
ret = -ENODEV;
goto out;
}
bat_priv = netdev_priv(soft_iface);
hash = bat_priv->bla.claim_hash;
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
ret = -ENOENT;
goto out;
}
while (bucket < hash->size) {
head = &hash->table[bucket];
if (batadv_bla_claim_dump_bucket(msg, portid,
cb->nlh->nlmsg_seq,
primary_if, head, &idx))
break;
bucket++;
}
cb->args[0] = bucket;
cb->args[1] = idx;
ret = msg->len;
out:
if (primary_if)
batadv_hardif_put(primary_if);
if (soft_iface)
dev_put(soft_iface);
return ret;
}
/**
* batadv_bla_backbone_table_seq_print_text - print the backbone table in a seq
* file
......@@ -2114,3 +2283,167 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset)
batadv_hardif_put(primary_if);
return 0;
}
/**
* batadv_bla_backbone_dump_entry - dump one entry of the backbone table
* to a netlink socket
* @msg: buffer for the message
* @portid: netlink port
* @seq: Sequence number of netlink message
* @primary_if: primary interface
* @backbone_gw: entry to dump
*
* Return: 0 or error code.
*/
static int
batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
struct batadv_hard_iface *primary_if,
struct batadv_bla_backbone_gw *backbone_gw)
{
u8 *primary_addr = primary_if->net_dev->dev_addr;
u16 backbone_crc;
bool is_own;
int msecs;
void *hdr;
int ret = -EINVAL;
hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
NLM_F_MULTI, BATADV_CMD_GET_BLA_BACKBONE);
if (!hdr) {
ret = -ENOBUFS;
goto out;
}
is_own = batadv_compare_eth(backbone_gw->orig, primary_addr);
spin_lock_bh(&backbone_gw->crc_lock);
backbone_crc = backbone_gw->crc;
spin_unlock_bh(&backbone_gw->crc_lock);
msecs = jiffies_to_msecs(jiffies - backbone_gw->lasttime);
if (is_own)
if (nla_put_flag(msg, BATADV_ATTR_BLA_OWN)) {
genlmsg_cancel(msg, hdr);
goto out;
}
if (nla_put(msg, BATADV_ATTR_BLA_BACKBONE, ETH_ALEN,
backbone_gw->orig) ||
nla_put_u16(msg, BATADV_ATTR_BLA_VID, backbone_gw->vid) ||
nla_put_u16(msg, BATADV_ATTR_BLA_CRC,
backbone_crc) ||
nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS, msecs)) {
genlmsg_cancel(msg, hdr);
goto out;
}
genlmsg_end(msg, hdr);
ret = 0;
out:
return ret;
}
/**
* batadv_bla_backbone_dump_bucket - dump one bucket of the backbone table
* to a netlink socket
* @msg: buffer for the message
* @portid: netlink port
* @seq: Sequence number of netlink message
* @primary_if: primary interface
* @head: bucket to dump
* @idx_skip: How many entries to skip
*
* Return: always 0.
*/
static int
batadv_bla_backbone_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
struct batadv_hard_iface *primary_if,
struct hlist_head *head, int *idx_skip)
{
struct batadv_bla_backbone_gw *backbone_gw;
int idx = 0;
rcu_read_lock();
hlist_for_each_entry_rcu(backbone_gw, head, hash_entry) {
if (idx++ < *idx_skip)
continue;
if (batadv_bla_backbone_dump_entry(msg, portid, seq,
primary_if, backbone_gw)) {
*idx_skip = idx - 1;
goto unlock;
}
}
*idx_skip = idx;
unlock:
rcu_read_unlock();
return 0;
}
/**
* batadv_bla_backbone_dump - dump backbone table to a netlink socket
* @msg: buffer for the message
* @cb: callback structure containing arguments
*
* Return: message length.
*/
int batadv_bla_backbone_dump(struct sk_buff *msg, struct netlink_callback *cb)
{
struct batadv_hard_iface *primary_if = NULL;
int portid = NETLINK_CB(cb->skb).portid;
struct net *net = sock_net(cb->skb->sk);
struct net_device *soft_iface;
struct batadv_hashtable *hash;
struct batadv_priv *bat_priv;
int bucket = cb->args[0];
struct hlist_head *head;
int idx = cb->args[1];
int ifindex;
int ret = 0;
ifindex = batadv_netlink_get_ifindex(cb->nlh,
BATADV_ATTR_MESH_IFINDEX);
if (!ifindex)
return -EINVAL;
soft_iface = dev_get_by_index(net, ifindex);
if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
ret = -ENODEV;
goto out;
}
bat_priv = netdev_priv(soft_iface);
hash = bat_priv->bla.backbone_hash;
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
ret = -ENOENT;
goto out;
}
while (bucket < hash->size) {
head = &hash->table[bucket];
if (batadv_bla_backbone_dump_bucket(msg, portid,
cb->nlh->nlmsg_seq,
primary_if, head, &idx))
break;
bucket++;
}
cb->args[0] = bucket;
cb->args[1] = idx;
ret = msg->len;
out:
if (primary_if)
batadv_hardif_put(primary_if);
if (soft_iface)
dev_put(soft_iface);
return ret;
}
......@@ -23,6 +23,7 @@
#include <linux/types.h>
struct net_device;
struct netlink_callback;
struct seq_file;
struct sk_buff;
......@@ -35,8 +36,10 @@ bool batadv_bla_is_backbone_gw(struct sk_buff *skb,
struct batadv_orig_node *orig_node,
int hdr_size);
int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset);
int batadv_bla_claim_dump(struct sk_buff *msg, struct netlink_callback *cb);
int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq,
void *offset);
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,
......@@ -47,7 +50,7 @@ void batadv_bla_update_orig_address(struct batadv_priv *bat_priv,
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);
int batadv_bla_claim_dump(struct sk_buff *msg, struct netlink_callback *cb);
#define BATADV_BLA_CRC_INIT 0
#else /* ifdef CONFIG_BATMAN_ADV_BLA */
......@@ -112,6 +115,18 @@ 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;
}
#endif /* ifdef CONFIG_BATMAN_ADV_BLA */
#endif /* ifndef _NET_BATMAN_ADV_BLA_H_ */
......@@ -31,6 +31,7 @@
#include <linux/stddef.h>
#include <linux/stringify.h>
#include <linux/sysfs.h>
#include <net/net_namespace.h>
#include "bat_algo.h"
#include "bridge_loop_avoidance.h"
......@@ -305,12 +306,16 @@ void batadv_debugfs_destroy(void)
*/
int batadv_debugfs_add_hardif(struct batadv_hard_iface *hard_iface)
{
struct net *net = dev_net(hard_iface->net_dev);
struct batadv_debuginfo **bat_debug;
struct dentry *file;
if (!batadv_debugfs)
goto out;
if (net != &init_net)
return 0;
hard_iface->debug_dir = debugfs_create_dir(hard_iface->net_dev->name,
batadv_debugfs);
if (!hard_iface->debug_dir)
......@@ -341,6 +346,11 @@ int batadv_debugfs_add_hardif(struct batadv_hard_iface *hard_iface)
*/
void batadv_debugfs_del_hardif(struct batadv_hard_iface *hard_iface)
{
struct net *net = dev_net(hard_iface->net_dev);
if (net != &init_net)
return;
if (batadv_debugfs) {
debugfs_remove_recursive(hard_iface->debug_dir);
hard_iface->debug_dir = NULL;
......@@ -351,11 +361,15 @@ int batadv_debugfs_add_meshif(struct net_device *dev)
{
struct batadv_priv *bat_priv = netdev_priv(dev);
struct batadv_debuginfo **bat_debug;
struct net *net = dev_net(dev);
struct dentry *file;
if (!batadv_debugfs)
goto out;
if (net != &init_net)
return 0;
bat_priv->debug_dir = debugfs_create_dir(dev->name, batadv_debugfs);
if (!bat_priv->debug_dir)
goto out;
......@@ -392,6 +406,10 @@ int batadv_debugfs_add_meshif(struct net_device *dev)
void batadv_debugfs_del_meshif(struct net_device *dev)
{
struct batadv_priv *bat_priv = netdev_priv(dev);
struct net *net = dev_net(dev);
if (net != &init_net)
return;
batadv_debug_log_cleanup(bat_priv);
......
......@@ -20,6 +20,7 @@
#include <linux/atomic.h>
#include <linux/byteorder/generic.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/fs.h>
#include <linux/if_ether.h>
......@@ -31,6 +32,7 @@
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/netlink.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/seq_file.h>
......@@ -39,13 +41,17 @@
#include <linux/spinlock.h>
#include <linux/stddef.h>
#include <linux/udp.h>
#include <net/sock.h>
#include <uapi/linux/batman_adv.h>
#include "gateway_common.h"
#include "hard-interface.h"
#include "log.h"
#include "netlink.h"
#include "originator.h"
#include "packet.h"
#include "routing.h"
#include "soft-interface.h"
#include "sysfs.h"
#include "translation-table.h"
......@@ -500,6 +506,59 @@ int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset)
return 0;
}
/**
* 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 *net = sock_net(cb->skb->sk);
struct net_device *soft_iface;
struct batadv_priv *bat_priv;
int ifindex;
int ret;
ifindex = batadv_netlink_get_ifindex(cb->nlh,
BATADV_ATTR_MESH_IFINDEX);
if (!ifindex)
return -EINVAL;
soft_iface = dev_get_by_index(net, ifindex);
if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
ret = -ENODEV;
goto out;
}
bat_priv = netdev_priv(soft_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:
if (primary_if)
batadv_hardif_put(primary_if);
if (soft_iface)
dev_put(soft_iface);
return ret;
}
/**
* batadv_gw_dhcp_recipient_get - check if a packet is a DHCP message
* @skb: the packet to check
......
......@@ -23,6 +23,7 @@
#include <linux/types.h>
struct batadv_tvlv_gateway_data;
struct netlink_callback;
struct seq_file;
struct sk_buff;
......@@ -43,6 +44,7 @@ void batadv_gw_node_put(struct batadv_gw_node *gw_node);
struct batadv_gw_node *
batadv_gw_get_selected_gw_node(struct batadv_priv *bat_priv);
int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset);
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,
......
......@@ -35,6 +35,8 @@
#include <linux/rtnetlink.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <net/net_namespace.h>
#include <net/rtnetlink.h>
#include "bat_v.h"
#include "bridge_loop_avoidance.h"
......@@ -83,26 +85,56 @@ batadv_hardif_get_by_netdev(const struct net_device *net_dev)
return hard_iface;
}
/**
* batadv_getlink_net - return link net namespace (of use fallback)
* @netdev: net_device to check
* @fallback_net: return in case get_link_net is not available for @netdev
*
* Return: result of rtnl_link_ops->get_link_net or @fallback_net
*/
static const struct net *batadv_getlink_net(const struct net_device *netdev,
const struct net *fallback_net)
{
if (!netdev->rtnl_link_ops)
return fallback_net;
if (!netdev->rtnl_link_ops->get_link_net)
return fallback_net;
return netdev->rtnl_link_ops->get_link_net(netdev);
}
/**
* batadv_mutual_parents - check if two devices are each others parent
* @dev1: 1st net_device
* @dev2: 2nd net_device
* @dev1: 1st net dev
* @net1: 1st devices netns
* @dev2: 2nd net dev
* @net2: 2nd devices netns
*
* veth devices come in pairs and each is the parent of the other!
*
* Return: true if the devices are each others parent, otherwise false
*/
static bool batadv_mutual_parents(const struct net_device *dev1,
const struct net_device *dev2)
const struct net *net1,
const struct net_device *dev2,
const struct net *net2)
{
int dev1_parent_iflink = dev_get_iflink(dev1);
int dev2_parent_iflink = dev_get_iflink(dev2);
const struct net *dev1_parent_net;
const struct net *dev2_parent_net;
dev1_parent_net = batadv_getlink_net(dev1, net1);
dev2_parent_net = batadv_getlink_net(dev2, net2);
if (!dev1_parent_iflink || !dev2_parent_iflink)
return false;
return (dev1_parent_iflink == dev2->ifindex) &&
(dev2_parent_iflink == dev1->ifindex);
(dev2_parent_iflink == dev1->ifindex) &&
net_eq(dev1_parent_net, net2) &&
net_eq(dev2_parent_net, net1);
}
/**
......@@ -120,8 +152,9 @@ static bool batadv_mutual_parents(const struct net_device *dev1,
*/
static bool batadv_is_on_batman_iface(const struct net_device *net_dev)
{
struct net_device *parent_dev;
struct net *net = dev_net(net_dev);
struct net_device *parent_dev;
const struct net *parent_net;
bool ret;
/* check if this is a batman-adv mesh interface */
......@@ -133,13 +166,16 @@ static bool batadv_is_on_batman_iface(const struct net_device *net_dev)
dev_get_iflink(net_dev) == net_dev->ifindex)
return false;
parent_net = batadv_getlink_net(net_dev, net);
/* recurse over the parent device */
parent_dev = __dev_get_by_index(net, dev_get_iflink(net_dev));
parent_dev = __dev_get_by_index((struct net *)parent_net,
dev_get_iflink(net_dev));
/* if we got a NULL parent_dev there is something broken.. */
if (WARN(!parent_dev, "Cannot find parent device"))
return false;
if (batadv_mutual_parents(net_dev, parent_dev))
if (batadv_mutual_parents(net_dev, net, parent_dev, parent_net))
return false;
ret = batadv_is_on_batman_iface(parent_dev);
......
......@@ -18,6 +18,8 @@
#include "netlink.h"
#include "main.h"
#include <linux/atomic.h>
#include <linux/byteorder/generic.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/genetlink.h>
......@@ -26,24 +28,33 @@
#include <linux/netdevice.h>
#include <linux/netlink.h>
#include <linux/printk.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/skbuff.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include <net/genetlink.h>
#include <net/netlink.h>
#include <net/sock.h>
#include <uapi/linux/batman_adv.h>
#include "bat_algo.h"
#include "bridge_loop_avoidance.h"
#include "gateway_client.h"
#include "hard-interface.h"
#include "originator.h"
#include "packet.h"
#include "soft-interface.h"
#include "tp_meter.h"
#include "translation-table.h"
struct sk_buff;
static struct genl_family batadv_netlink_family = {
struct genl_family batadv_netlink_family = {
.id = GENL_ID_GENERATE,
.hdrsize = 0,
.name = BATADV_NL_NAME,
.version = 1,
.maxattr = BATADV_ATTR_MAX,
.netnsok = true,
};
/* multicast groups */
......@@ -69,8 +80,43 @@ static struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = {
[BATADV_ATTR_TPMETER_TEST_TIME] = { .type = NLA_U32 },
[BATADV_ATTR_TPMETER_BYTES] = { .type = NLA_U64 },
[BATADV_ATTR_TPMETER_COOKIE] = { .type = NLA_U32 },
[BATADV_ATTR_ACTIVE] = { .type = NLA_FLAG },
[BATADV_ATTR_TT_ADDRESS] = { .len = ETH_ALEN },
[BATADV_ATTR_TT_TTVN] = { .type = NLA_U8 },
[BATADV_ATTR_TT_LAST_TTVN] = { .type = NLA_U8 },
[BATADV_ATTR_TT_CRC32] = { .type = NLA_U32 },
[BATADV_ATTR_TT_VID] = { .type = NLA_U16 },
[BATADV_ATTR_TT_FLAGS] = { .type = NLA_U32 },
[BATADV_ATTR_FLAG_BEST] = { .type = NLA_FLAG },
[BATADV_ATTR_LAST_SEEN_MSECS] = { .type = NLA_U32 },
[BATADV_ATTR_NEIGH_ADDRESS] = { .len = ETH_ALEN },
[BATADV_ATTR_TQ] = { .type = NLA_U8 },
[BATADV_ATTR_THROUGHPUT] = { .type = NLA_U32 },
[BATADV_ATTR_BANDWIDTH_UP] = { .type = NLA_U32 },
[BATADV_ATTR_BANDWIDTH_DOWN] = { .type = NLA_U32 },
[BATADV_ATTR_ROUTER] = { .len = ETH_ALEN },
[BATADV_ATTR_BLA_OWN] = { .type = NLA_FLAG },
[BATADV_ATTR_BLA_ADDRESS] = { .len = ETH_ALEN },
[BATADV_ATTR_BLA_VID] = { .type = NLA_U16 },
[BATADV_ATTR_BLA_BACKBONE] = { .len = ETH_ALEN },
[BATADV_ATTR_BLA_CRC] = { .type = NLA_U16 },
};
/**
* batadv_netlink_get_ifindex - Extract an interface index from a message
* @nlh: Message header
* @attrtype: Attribute which holds an interface index
*
* Return: interface index, or 0.
*/
int
batadv_netlink_get_ifindex(const struct nlmsghdr *nlh, int attrtype)
{
struct nlattr *attr = nlmsg_find_attr(nlh, GENL_HDRLEN, attrtype);
return attr ? nla_get_u32(attr) : 0;
}
/**
* batadv_netlink_mesh_info_put - fill in generic information about mesh
* interface
......@@ -93,8 +139,16 @@ batadv_netlink_mesh_info_put(struct sk_buff *msg, struct net_device *soft_iface)
nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, soft_iface->ifindex) ||
nla_put_string(msg, BATADV_ATTR_MESH_IFNAME, soft_iface->name) ||
nla_put(msg, BATADV_ATTR_MESH_ADDRESS, ETH_ALEN,
soft_iface->dev_addr))
soft_iface->dev_addr) ||
nla_put_u8(msg, BATADV_ATTR_TT_TTVN,
(u8)atomic_read(&bat_priv->tt.vn)))
goto out;
#ifdef CONFIG_BATMAN_ADV_BLA
if (nla_put_u16(msg, BATADV_ATTR_BLA_CRC,
ntohs(bat_priv->bla.claim_dest.group)))
goto out;
#endif
primary_if = batadv_primary_if_get_selected(bat_priv);
if (primary_if && primary_if->if_status == BATADV_IF_ACTIVE) {
......@@ -380,6 +434,106 @@ batadv_netlink_tp_meter_cancel(struct sk_buff *skb, struct genl_info *info)
return ret;
}
/**
* batadv_netlink_dump_hardif_entry - Dump one hard interface into a message
* @msg: Netlink message to dump into
* @portid: Port making netlink request
* @seq: Sequence number of netlink message
* @hard_iface: Hard interface to dump
*
* Return: error code, or 0 on success
*/
static int
batadv_netlink_dump_hardif_entry(struct sk_buff *msg, u32 portid, u32 seq,
struct batadv_hard_iface *hard_iface)
{
struct net_device *net_dev = hard_iface->net_dev;
void *hdr;
hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, NLM_F_MULTI,
BATADV_CMD_GET_HARDIFS);
if (!hdr)
return -EMSGSIZE;
if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
net_dev->ifindex) ||
nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
net_dev->name) ||
nla_put(msg, BATADV_ATTR_HARD_ADDRESS, ETH_ALEN,
net_dev->dev_addr))
goto nla_put_failure;
if (hard_iface->if_status == BATADV_IF_ACTIVE) {
if (nla_put_flag(msg, BATADV_ATTR_ACTIVE))
goto nla_put_failure;
}
genlmsg_end(msg, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
}
/**
* batadv_netlink_dump_hardifs - Dump all hard interface into a messages
* @msg: Netlink message to dump into
* @cb: Parameters from query
*
* Return: error code, or length of reply message on success
*/
static int
batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb)
{
struct net *net = sock_net(cb->skb->sk);
struct net_device *soft_iface;
struct batadv_hard_iface *hard_iface;
int ifindex;
int portid = NETLINK_CB(cb->skb).portid;
int seq = cb->nlh->nlmsg_seq;
int skip = cb->args[0];
int i = 0;
ifindex = batadv_netlink_get_ifindex(cb->nlh,
BATADV_ATTR_MESH_IFINDEX);
if (!ifindex)
return -EINVAL;
soft_iface = dev_get_by_index(net, ifindex);
if (!soft_iface)
return -ENODEV;
if (!batadv_softif_is_valid(soft_iface)) {
dev_put(soft_iface);
return -ENODEV;
}
rcu_read_lock();
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
if (hard_iface->soft_iface != soft_iface)
continue;
if (i++ < skip)
continue;
if (batadv_netlink_dump_hardif_entry(msg, portid, seq,
hard_iface)) {
i--;
break;
}
}
rcu_read_unlock();
dev_put(soft_iface);
cb->args[0] = i;
return msg->len;
}
static struct genl_ops batadv_netlink_ops[] = {
{
.cmd = BATADV_CMD_GET_MESH_INFO,
......@@ -399,6 +553,61 @@ static struct genl_ops batadv_netlink_ops[] = {
.policy = batadv_netlink_policy,
.doit = batadv_netlink_tp_meter_cancel,
},
{
.cmd = BATADV_CMD_GET_ROUTING_ALGOS,
.flags = GENL_ADMIN_PERM,
.policy = batadv_netlink_policy,
.dumpit = batadv_algo_dump,
},
{
.cmd = BATADV_CMD_GET_HARDIFS,
.flags = GENL_ADMIN_PERM,
.policy = batadv_netlink_policy,
.dumpit = batadv_netlink_dump_hardifs,
},
{
.cmd = BATADV_CMD_GET_TRANSTABLE_LOCAL,
.flags = GENL_ADMIN_PERM,
.policy = batadv_netlink_policy,
.dumpit = batadv_tt_local_dump,
},
{
.cmd = BATADV_CMD_GET_TRANSTABLE_GLOBAL,
.flags = GENL_ADMIN_PERM,
.policy = batadv_netlink_policy,
.dumpit = batadv_tt_global_dump,
},
{
.cmd = BATADV_CMD_GET_ORIGINATORS,
.flags = GENL_ADMIN_PERM,
.policy = batadv_netlink_policy,
.dumpit = batadv_orig_dump,
},
{
.cmd = BATADV_CMD_GET_NEIGHBORS,
.flags = GENL_ADMIN_PERM,
.policy = batadv_netlink_policy,
.dumpit = batadv_hardif_neigh_dump,
},
{
.cmd = BATADV_CMD_GET_GATEWAYS,
.flags = GENL_ADMIN_PERM,
.policy = batadv_netlink_policy,
.dumpit = batadv_gw_dump,
},
{
.cmd = BATADV_CMD_GET_BLA_CLAIM,
.flags = GENL_ADMIN_PERM,
.policy = batadv_netlink_policy,
.dumpit = batadv_bla_claim_dump,
},
{
.cmd = BATADV_CMD_GET_BLA_BACKBONE,
.flags = GENL_ADMIN_PERM,
.policy = batadv_netlink_policy,
.dumpit = batadv_bla_backbone_dump,
},
};
/**
......
......@@ -21,12 +21,18 @@
#include "main.h"
#include <linux/types.h>
#include <net/genetlink.h>
struct nlmsghdr;
void batadv_netlink_register(void);
void batadv_netlink_unregister(void);
int batadv_netlink_get_ifindex(const struct nlmsghdr *nlh, int attrtype);
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_ */
......@@ -28,11 +28,15 @@
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/netdevice.h>
#include <linux/netlink.h>
#include <linux/rculist.h>
#include <linux/seq_file.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <net/sock.h>
#include <uapi/linux/batman_adv.h>
#include "bat_algo.h"
#include "distributed-arp-table.h"
......@@ -42,8 +46,10 @@
#include "hash.h"
#include "log.h"
#include "multicast.h"
#include "netlink.h"
#include "network-coding.h"
#include "routing.h"
#include "soft-interface.h"
#include "translation-table.h"
/* hash class keys */
......@@ -720,6 +726,83 @@ int batadv_hardif_neigh_seq_print_text(struct seq_file *seq, void *offset)
return 0;
}
/**
* batadv_hardif_neigh_dump - Dump to netlink the neighbor infos for a specific
* outgoing interface
* @msg: message to dump into
* @cb: parameters for the dump
*
* Return: 0 or error value
*/
int batadv_hardif_neigh_dump(struct sk_buff *msg, struct netlink_callback *cb)
{
struct net *net = sock_net(cb->skb->sk);
struct net_device *soft_iface;
struct net_device *hard_iface = NULL;
struct batadv_hard_iface *hardif = BATADV_IF_DEFAULT;
struct batadv_priv *bat_priv;
struct batadv_hard_iface *primary_if = NULL;
int ret;
int ifindex, hard_ifindex;
ifindex = batadv_netlink_get_ifindex(cb->nlh, BATADV_ATTR_MESH_IFINDEX);
if (!ifindex)
return -EINVAL;
soft_iface = dev_get_by_index(net, ifindex);
if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
ret = -ENODEV;
goto out;
}
bat_priv = netdev_priv(soft_iface);
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
ret = -ENOENT;
goto out;
}
hard_ifindex = batadv_netlink_get_ifindex(cb->nlh,
BATADV_ATTR_HARD_IFINDEX);
if (hard_ifindex) {
hard_iface = dev_get_by_index(net, hard_ifindex);
if (hard_iface)
hardif = batadv_hardif_get_by_netdev(hard_iface);
if (!hardif) {
ret = -ENODEV;
goto out;
}
if (hardif->soft_iface != soft_iface) {
ret = -ENOENT;
goto out;
}
}
if (!bat_priv->algo_ops->neigh.dump) {
ret = -EOPNOTSUPP;
goto out;
}
bat_priv->algo_ops->neigh.dump(msg, cb, bat_priv, hardif);
ret = msg->len;
out:
if (hardif)
batadv_hardif_put(hardif);
if (hard_iface)
dev_put(hard_iface);
if (primary_if)
batadv_hardif_put(primary_if);
if (soft_iface)
dev_put(soft_iface);
return ret;
}
/**
* batadv_orig_ifinfo_release - release orig_ifinfo from lists and queue for
* free after rcu grace period
......@@ -1330,6 +1413,83 @@ int batadv_orig_hardif_seq_print_text(struct seq_file *seq, void *offset)
return 0;
}
/**
* batadv_orig_dump - Dump to netlink the originator infos for a specific
* outgoing interface
* @msg: message to dump into
* @cb: parameters for the dump
*
* Return: 0 or error value
*/
int batadv_orig_dump(struct sk_buff *msg, struct netlink_callback *cb)
{
struct net *net = sock_net(cb->skb->sk);
struct net_device *soft_iface;
struct net_device *hard_iface = NULL;
struct batadv_hard_iface *hardif = BATADV_IF_DEFAULT;
struct batadv_priv *bat_priv;
struct batadv_hard_iface *primary_if = NULL;
int ret;
int ifindex, hard_ifindex;
ifindex = batadv_netlink_get_ifindex(cb->nlh, BATADV_ATTR_MESH_IFINDEX);
if (!ifindex)
return -EINVAL;
soft_iface = dev_get_by_index(net, ifindex);
if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
ret = -ENODEV;
goto out;
}
bat_priv = netdev_priv(soft_iface);
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
ret = -ENOENT;
goto out;
}
hard_ifindex = batadv_netlink_get_ifindex(cb->nlh,
BATADV_ATTR_HARD_IFINDEX);
if (hard_ifindex) {
hard_iface = dev_get_by_index(net, hard_ifindex);
if (hard_iface)
hardif = batadv_hardif_get_by_netdev(hard_iface);
if (!hardif) {
ret = -ENODEV;
goto out;
}
if (hardif->soft_iface != soft_iface) {
ret = -ENOENT;
goto out;
}
}
if (!bat_priv->algo_ops->orig.dump) {
ret = -EOPNOTSUPP;
goto out;
}
bat_priv->algo_ops->orig.dump(msg, cb, bat_priv, hardif);
ret = msg->len;
out:
if (hardif)
batadv_hardif_put(hardif);
if (hard_iface)
dev_put(hard_iface);
if (primary_if)
batadv_hardif_put(primary_if);
if (soft_iface)
dev_put(soft_iface);
return ret;
}
int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface,
int max_if_num)
{
......
......@@ -31,7 +31,9 @@
#include "hash.h"
struct netlink_callback;
struct seq_file;
struct sk_buff;
bool batadv_compare_orig(const struct hlist_node *node, const void *data2);
int batadv_originator_init(struct batadv_priv *bat_priv);
......@@ -61,6 +63,7 @@ batadv_neigh_ifinfo_get(struct batadv_neigh_node *neigh,
struct batadv_hard_iface *if_outgoing);
void batadv_neigh_ifinfo_put(struct batadv_neigh_ifinfo *neigh_ifinfo);
int batadv_hardif_neigh_dump(struct sk_buff *msg, struct netlink_callback *cb);
int batadv_hardif_neigh_seq_print_text(struct seq_file *seq, void *offset);
struct batadv_orig_ifinfo *
......@@ -72,6 +75,7 @@ batadv_orig_ifinfo_new(struct batadv_orig_node *orig_node,
void batadv_orig_ifinfo_put(struct batadv_orig_ifinfo *orig_ifinfo);
int batadv_orig_seq_print_text(struct seq_file *seq, void *offset);
int batadv_orig_dump(struct sk_buff *msg, struct netlink_callback *cb);
int batadv_orig_hardif_seq_print_text(struct seq_file *seq, void *offset);
int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface,
int max_if_num);
......
......@@ -128,42 +128,6 @@ enum batadv_tt_data_flags {
BATADV_TT_FULL_TABLE = BIT(4),
};
/**
* enum batadv_tt_client_flags - TT client specific flags
* @BATADV_TT_CLIENT_DEL: the client has to be deleted from the table
* @BATADV_TT_CLIENT_ROAM: the client roamed to/from another node and the new
* update telling its new real location has not been received/sent yet
* @BATADV_TT_CLIENT_WIFI: this client is connected through a wifi interface.
* This information is used by the "AP Isolation" feature
* @BATADV_TT_CLIENT_ISOLA: this client is considered "isolated". This
* information is used by the Extended Isolation feature
* @BATADV_TT_CLIENT_NOPURGE: this client should never be removed from the table
* @BATADV_TT_CLIENT_NEW: this client has been added to the local table but has
* not been announced yet
* @BATADV_TT_CLIENT_PENDING: this client is marked for removal but it is kept
* in the table for one more originator interval for consistency purposes
* @BATADV_TT_CLIENT_TEMP: this global client has been detected to be part of
* the network but no nnode has already announced it
*
* Bits from 0 to 7 are called _remote flags_ because they are sent on the wire.
* Bits from 8 to 15 are called _local flags_ because they are used for local
* computations only.
*
* Bits from 4 to 7 - a subset of remote flags - are ensured to be in sync with
* the other nodes in the network. To achieve this goal these flags are included
* in the TT CRC computation.
*/
enum batadv_tt_client_flags {
BATADV_TT_CLIENT_DEL = BIT(0),
BATADV_TT_CLIENT_ROAM = BIT(1),
BATADV_TT_CLIENT_WIFI = BIT(4),
BATADV_TT_CLIENT_ISOLA = BIT(5),
BATADV_TT_CLIENT_NOPURGE = BIT(8),
BATADV_TT_CLIENT_NEW = BIT(9),
BATADV_TT_CLIENT_PENDING = BIT(10),
BATADV_TT_CLIENT_TEMP = BIT(11),
};
/**
* enum batadv_vlan_flags - flags for the four MSB of any vlan ID field
* @BATADV_VLAN_HAS_TAG: whether the field contains a valid vlan tag or not
......
......@@ -37,20 +37,27 @@
#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/seq_file.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/workqueue.h>
#include <net/genetlink.h>
#include <net/netlink.h>
#include <net/sock.h>
#include <uapi/linux/batman_adv.h>
#include "bridge_loop_avoidance.h"
#include "hard-interface.h"
#include "hash.h"
#include "log.h"
#include "multicast.h"
#include "netlink.h"
#include "originator.h"
#include "packet.h"
#include "soft-interface.h"
......@@ -1108,6 +1115,164 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
return 0;
}
/**
* batadv_tt_local_dump_entry - Dump one TT local 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
* @common: tt local & tt global common data
*
* Return: Error code, or 0 on success
*/
static int
batadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
struct batadv_priv *bat_priv,
struct batadv_tt_common_entry *common)
{
void *hdr;
struct batadv_softif_vlan *vlan;
struct batadv_tt_local_entry *local;
unsigned int last_seen_msecs;
u32 crc;
local = container_of(common, struct batadv_tt_local_entry, common);
last_seen_msecs = jiffies_to_msecs(jiffies - local->last_seen);
vlan = batadv_softif_vlan_get(bat_priv, common->vid);
if (!vlan)
return 0;
crc = vlan->tt.crc;
batadv_softif_vlan_put(vlan);
hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
NLM_F_MULTI,
BATADV_CMD_GET_TRANSTABLE_LOCAL);
if (!hdr)
return -ENOBUFS;
if (nla_put(msg, BATADV_ATTR_TT_ADDRESS, ETH_ALEN, common->addr) ||
nla_put_u32(msg, BATADV_ATTR_TT_CRC32, crc) ||
nla_put_u16(msg, BATADV_ATTR_TT_VID, common->vid) ||
nla_put_u32(msg, BATADV_ATTR_TT_FLAGS, common->flags))
goto nla_put_failure;
if (!(common->flags & BATADV_TT_CLIENT_NOPURGE) &&
nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS, last_seen_msecs))
goto nla_put_failure;
genlmsg_end(msg, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
}
/**
* batadv_tt_local_dump_bucket - Dump one TT local 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
* @head: Pointer to the list containing the local tt entries
* @idx_s: Number of entries to skip
*
* Return: Error code, or 0 on success
*/
static int
batadv_tt_local_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
struct batadv_priv *bat_priv,
struct hlist_head *head, int *idx_s)
{
struct batadv_tt_common_entry *common;
int idx = 0;
rcu_read_lock();
hlist_for_each_entry_rcu(common, head, hash_entry) {
if (idx++ < *idx_s)
continue;
if (batadv_tt_local_dump_entry(msg, portid, seq, bat_priv,
common)) {
rcu_read_unlock();
*idx_s = idx - 1;
return -EMSGSIZE;
}
}
rcu_read_unlock();
*idx_s = 0;
return 0;
}
/**
* batadv_tt_local_dump - Dump TT local entries into a message
* @msg: Netlink message to dump into
* @cb: Parameters from query
*
* Return: Error code, or 0 on success
*/
int batadv_tt_local_dump(struct sk_buff *msg, struct netlink_callback *cb)
{
struct net *net = sock_net(cb->skb->sk);
struct net_device *soft_iface;
struct batadv_priv *bat_priv;
struct batadv_hard_iface *primary_if = NULL;
struct batadv_hashtable *hash;
struct hlist_head *head;
int ret;
int ifindex;
int bucket = cb->args[0];
int idx = cb->args[1];
int portid = NETLINK_CB(cb->skb).portid;
ifindex = batadv_netlink_get_ifindex(cb->nlh, BATADV_ATTR_MESH_IFINDEX);
if (!ifindex)
return -EINVAL;
soft_iface = dev_get_by_index(net, ifindex);
if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
ret = -ENODEV;
goto out;
}
bat_priv = netdev_priv(soft_iface);
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
ret = -ENOENT;
goto out;
}
hash = bat_priv->tt.local_hash;
while (bucket < hash->size) {
head = &hash->table[bucket];
if (batadv_tt_local_dump_bucket(msg, portid, cb->nlh->nlmsg_seq,
bat_priv, head, &idx))
break;
bucket++;
}
ret = msg->len;
out:
if (primary_if)
batadv_hardif_put(primary_if);
if (soft_iface)
dev_put(soft_iface);
cb->args[0] = bucket;
cb->args[1] = idx;
return ret;
}
static void
batadv_tt_local_set_pending(struct batadv_priv *bat_priv,
struct batadv_tt_local_entry *tt_local_entry,
......@@ -1755,6 +1920,218 @@ int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset)
return 0;
}
/**
* batadv_tt_global_dump_subentry - Dump all TT local entries into a message
* @msg: Netlink message to dump into
* @portid: Port making netlink request
* @seq: Sequence number of netlink message
* @common: tt local & tt global common data
* @orig: Originator node announcing a non-mesh client
* @best: Is the best originator for the TT entry
*
* Return: Error code, or 0 on success
*/
static int
batadv_tt_global_dump_subentry(struct sk_buff *msg, u32 portid, u32 seq,
struct batadv_tt_common_entry *common,
struct batadv_tt_orig_list_entry *orig,
bool best)
{
void *hdr;
struct batadv_orig_node_vlan *vlan;
u8 last_ttvn;
u32 crc;
vlan = batadv_orig_node_vlan_get(orig->orig_node,
common->vid);
if (!vlan)
return 0;
crc = vlan->tt.crc;
batadv_orig_node_vlan_put(vlan);
hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
NLM_F_MULTI,
BATADV_CMD_GET_TRANSTABLE_GLOBAL);
if (!hdr)
return -ENOBUFS;
last_ttvn = atomic_read(&orig->orig_node->last_ttvn);
if (nla_put(msg, BATADV_ATTR_TT_ADDRESS, ETH_ALEN, common->addr) ||
nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN,
orig->orig_node->orig) ||
nla_put_u8(msg, BATADV_ATTR_TT_TTVN, orig->ttvn) ||
nla_put_u8(msg, BATADV_ATTR_TT_LAST_TTVN, last_ttvn) ||
nla_put_u32(msg, BATADV_ATTR_TT_CRC32, crc) ||
nla_put_u16(msg, BATADV_ATTR_TT_VID, common->vid) ||
nla_put_u32(msg, BATADV_ATTR_TT_FLAGS, common->flags))
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_tt_global_dump_entry - Dump one TT global 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
* @common: tt local & tt global common data
* @sub_s: Number of entries to skip
*
* This function assumes the caller holds rcu_read_lock().
*
* Return: Error code, or 0 on success
*/
static int
batadv_tt_global_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
struct batadv_priv *bat_priv,
struct batadv_tt_common_entry *common, int *sub_s)
{
struct batadv_tt_orig_list_entry *orig_entry, *best_entry;
struct batadv_tt_global_entry *global;
struct hlist_head *head;
int sub = 0;
bool best;
global = container_of(common, struct batadv_tt_global_entry, common);
best_entry = batadv_transtable_best_orig(bat_priv, global);
head = &global->orig_list;
hlist_for_each_entry_rcu(orig_entry, head, list) {
if (sub++ < *sub_s)
continue;
best = (orig_entry == best_entry);
if (batadv_tt_global_dump_subentry(msg, portid, seq, common,
orig_entry, best)) {
*sub_s = sub - 1;
return -EMSGSIZE;
}
}
*sub_s = 0;
return 0;
}
/**
* batadv_tt_global_dump_bucket - Dump one TT local 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
* @head: Pointer to the list containing the global tt entries
* @idx_s: Number of entries to skip
* @sub: Number of entries to skip
*
* Return: Error code, or 0 on success
*/
static int
batadv_tt_global_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
struct batadv_priv *bat_priv,
struct hlist_head *head, int *idx_s, int *sub)
{
struct batadv_tt_common_entry *common;
int idx = 0;
rcu_read_lock();
hlist_for_each_entry_rcu(common, head, hash_entry) {
if (idx++ < *idx_s)
continue;
if (batadv_tt_global_dump_entry(msg, portid, seq, bat_priv,
common, sub)) {
rcu_read_unlock();
*idx_s = idx - 1;
return -EMSGSIZE;
}
}
rcu_read_unlock();
*idx_s = 0;
*sub = 0;
return 0;
}
/**
* batadv_tt_global_dump - Dump TT global entries into a message
* @msg: Netlink message to dump into
* @cb: Parameters from query
*
* Return: Error code, or length of message on success
*/
int batadv_tt_global_dump(struct sk_buff *msg, struct netlink_callback *cb)
{
struct net *net = sock_net(cb->skb->sk);
struct net_device *soft_iface;
struct batadv_priv *bat_priv;
struct batadv_hard_iface *primary_if = NULL;
struct batadv_hashtable *hash;
struct hlist_head *head;
int ret;
int ifindex;
int bucket = cb->args[0];
int idx = cb->args[1];
int sub = cb->args[2];
int portid = NETLINK_CB(cb->skb).portid;
ifindex = batadv_netlink_get_ifindex(cb->nlh, BATADV_ATTR_MESH_IFINDEX);
if (!ifindex)
return -EINVAL;
soft_iface = dev_get_by_index(net, ifindex);
if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
ret = -ENODEV;
goto out;
}
bat_priv = netdev_priv(soft_iface);
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
ret = -ENOENT;
goto out;
}
hash = bat_priv->tt.global_hash;
while (bucket < hash->size) {
head = &hash->table[bucket];
if (batadv_tt_global_dump_bucket(msg, portid,
cb->nlh->nlmsg_seq, bat_priv,
head, &idx, &sub))
break;
bucket++;
}
ret = msg->len;
out:
if (primary_if)
batadv_hardif_put(primary_if);
if (soft_iface)
dev_put(soft_iface);
cb->args[0] = bucket;
cb->args[1] = idx;
cb->args[2] = sub;
return ret;
}
/**
* _batadv_tt_global_del_orig_entry - remove and free an orig_entry
* @tt_global_entry: the global entry to remove the orig_entry from
......
......@@ -22,8 +22,10 @@
#include <linux/types.h>
struct netlink_callback;
struct net_device;
struct seq_file;
struct sk_buff;
int batadv_tt_init(struct batadv_priv *bat_priv);
bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
......@@ -33,6 +35,8 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv,
const char *message, bool roaming);
int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset);
int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset);
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);
......
......@@ -28,6 +28,7 @@
#include <linux/if_ether.h>
#include <linux/kref.h>
#include <linux/netdevice.h>
#include <linux/netlink.h>
#include <linux/sched.h> /* for linux/wait.h */
#include <linux/spinlock.h>
#include <linux/types.h>
......@@ -1418,6 +1419,7 @@ struct batadv_algo_iface_ops {
* @is_similar_or_better: check if neigh1 is equally similar or better than
* neigh2 for their respective outgoing interface from the metric prospective
* @print: print the single hop neighbor list (optional)
* @dump: dump neighbors to a netlink socket (optional)
*/
struct batadv_algo_neigh_ops {
void (*hardif_init)(struct batadv_hardif_neigh_node *neigh);
......@@ -1430,6 +1432,9 @@ struct batadv_algo_neigh_ops {
struct batadv_neigh_node *neigh2,
struct batadv_hard_iface *if_outgoing2);
void (*print)(struct batadv_priv *priv, struct seq_file *seq);
void (*dump)(struct sk_buff *msg, struct netlink_callback *cb,
struct batadv_priv *priv,
struct batadv_hard_iface *hard_iface);
};
/**
......@@ -1441,6 +1446,7 @@ struct batadv_algo_neigh_ops {
* @del_if: ask the routing algorithm to apply the needed changes to the
* orig_node due to an hard-interface being removed from the mesh (optional)
* @print: print the originator table (optional)
* @dump: dump originators to a netlink socket (optional)
*/
struct batadv_algo_orig_ops {
void (*free)(struct batadv_orig_node *orig_node);
......@@ -1449,6 +1455,9 @@ struct batadv_algo_orig_ops {
int del_if_num);
void (*print)(struct batadv_priv *priv, struct seq_file *seq,
struct batadv_hard_iface *hard_iface);
void (*dump)(struct sk_buff *msg, struct netlink_callback *cb,
struct batadv_priv *priv,
struct batadv_hard_iface *hard_iface);
};
/**
......@@ -1460,6 +1469,7 @@ struct batadv_algo_orig_ops {
* @is_eligible: check if a newly discovered GW is a potential candidate for
* the election as best GW (optional)
* @print: print the gateway table (optional)
* @dump: dump gateways to a netlink socket (optional)
*/
struct batadv_algo_gw_ops {
ssize_t (*store_sel_class)(struct batadv_priv *bat_priv, char *buff,
......@@ -1471,6 +1481,8 @@ struct batadv_algo_gw_ops {
struct batadv_orig_node *curr_gw_orig,
struct batadv_orig_node *orig_node);
void (*print)(struct batadv_priv *bat_priv, struct seq_file *seq);
void (*dump)(struct sk_buff *msg, struct netlink_callback *cb,
struct batadv_priv *priv);
};
/**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment