Hi, this is the second take of my netlink API patches. As mentioned before, the netlink API is superior to the current debugfs API in many aspects:
* debugfs is broken (see PATCH 1 for details) * Netlink is namespace-aware, and can be used in unprivileged containers without problems * Netlink packets are more machine-readable than text files, and can be easily extended without potentially breaking userspace * On older kernels, seq_file can't fall back to vmalloc if kmalloc fails, which often leads to OOM when reading "originators" in large meshes, as the whole file must fit into a single buffer
Of course, are also a few downsides; when the data is too big to fit into a single netlink packet, the provided data may be inconsistent (entries may be missing or duplicated.) This will happen in large meshes only, and be improbable in any case.
The patches have been developed on top of the netns patchset, but should be applicable independently (maybe with minor changes.)
All netlink queries returning lists of any kind can only be used with NLM_F_DUMP queries, so that arbitrarity large responses are possible (split across multiple packets if necessary.)
At the moment, the following debugfs files have corresponding netlink APIs:
* routing_algos * neighbors * originators * transtable_global * transtable_local * (hardinterfaces for a softif can be queried)
The following files are still missing:
* gateways * bla_claim_table * bla_backbone_table * dat_cache * nc_nodes
Obviously, documentation is also a TODO. Comments about the API design are very welcome...
Regards, Matthias
Matthias Schiffer (5): batman-adv: add generic netlink query API to replace debugfs files batman-adv: netlink: add translation table query batman-adv: netlink: add originator and neighbor table queries batman-adv: add B.A.T.M.A.N. IV bat_{orig,neigh}_dump implementations batman-adv: add B.A.T.M.A.N. V bat_{orig,neigh}_dump implementations
Makefile | 1 + include/uapi/linux/batman_adv.h | 101 ++++++++++++ net/batman-adv/Makefile | 1 + net/batman-adv/bat_iv_ogm.c | 268 ++++++++++++++++++++++++++++++++ net/batman-adv/bat_v.c | 250 ++++++++++++++++++++++++++++++ net/batman-adv/main.c | 48 ++++++ net/batman-adv/main.h | 1 + net/batman-adv/netlink.c | 276 +++++++++++++++++++++++++++++++++ net/batman-adv/netlink.h | 36 +++++ net/batman-adv/originator.c | 142 +++++++++++++++++ net/batman-adv/originator.h | 2 + net/batman-adv/packet.h | 36 ----- net/batman-adv/translation-table.c | 305 +++++++++++++++++++++++++++++++++++++ net/batman-adv/translation-table.h | 2 + net/batman-adv/types.h | 7 + 15 files changed, 1440 insertions(+), 36 deletions(-) create mode 100644 include/uapi/linux/batman_adv.h create mode 100644 net/batman-adv/netlink.c create mode 100644 net/batman-adv/netlink.h
debugfs is currently severely broken virtually everywhere in the kernel where files are dynamically added and removed (see http://lkml.iu.edu/hypermail/linux/kernel/1506.1/02196.html for some details). In addition to that, debugfs is not namespace-aware.
Also, the debugfs interface will try to fix the whole list of originators/ TT entries into a single buffer. The situation has improved in recent kernels,as the seq_file infrastructure will fall back to vmalloc now when kmalloc fails. Still, converting all information to text and potentially retrying multiple times until the buffer is big enough is very inefficient.
This commit adds generic infrastructur for the netlink interface to batman-adv and implements the following command:
* BATADV_CMD_GET_ROUTING_ALGOS: will return the list of supported routing algorithms * BATADV_CMD_GET_MESH_INFO: will return basic information about a batman-adv softif (name, index and MAC address for both the softif and the primary hardif; routing algorithm; batman-adv version) * BATADV_CMD_GET_HARDIFS: will return the list of hardifs (including index, name and MAC address) of all hardifs for a given softif
Signed-off-by: Matthias Schiffer mschiffer@universe-factory.net --- Makefile | 1 + include/uapi/linux/batman_adv.h | 49 ++++++++ net/batman-adv/Makefile | 1 + net/batman-adv/main.c | 48 ++++++++ net/batman-adv/main.h | 1 + net/batman-adv/netlink.c | 251 ++++++++++++++++++++++++++++++++++++++++ net/batman-adv/netlink.h | 36 ++++++ 7 files changed, 387 insertions(+) create mode 100644 include/uapi/linux/batman_adv.h create mode 100644 net/batman-adv/netlink.c create mode 100644 net/batman-adv/netlink.h
diff --git a/Makefile b/Makefile index dc45cc8..c6cabd4 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,7 @@ REVISION= $(shell if [ -d "$(PWD)/.git" ]; then \ fi) export NOSTDINC_FLAGS := \ -I$(PWD)/compat-include/ \ + -I$(PWD)/include/ \ -include $(PWD)/compat.h \ $(CFLAGS)
diff --git a/include/uapi/linux/batman_adv.h b/include/uapi/linux/batman_adv.h new file mode 100644 index 0000000..b9b7dfd --- /dev/null +++ b/include/uapi/linux/batman_adv.h @@ -0,0 +1,49 @@ +/* Copyright (C) 2016 B.A.T.M.A.N. contributors: + * + * Matthias Schiffer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. + */ + +#ifndef _UAPI_LINUX_BATMAN_ADV_H_ +#define _UAPI_LINUX_BATMAN_ADV_H_ + +#define BATADV_NL_NAME "batadv" + +enum { + BATADV_ATTR_UNSPEC, + BATADV_ATTR_VERSION, + BATADV_ATTR_ALGO_NAME, + BATADV_ATTR_MESH_IFINDEX, + BATADV_ATTR_MESH_IFNAME, + BATADV_ATTR_MESH_ADDRESS, + BATADV_ATTR_HARD_IFINDEX, + BATADV_ATTR_HARD_IFNAME, + BATADV_ATTR_HARD_ADDRESS, + BATADV_ATTR_ACTIVE, + __BATADV_ATTR_MAX, +}; + +#define BATADV_ATTR_MAX (__BATADV_ATTR_MAX - 1) + +enum { + BATADV_CMD_UNSPEC, + BATADV_CMD_GET_ROUTING_ALGOS, + BATADV_CMD_GET_MESH_INFO, + BATADV_CMD_GET_HARDIFS, + __BATADV_CMD_MAX, +}; + +#define BATADV_CMD_MAX (__BATADV_CMD_MAX - 1) + +#endif /* _UAPI_LINUX_BATMAN_ADV_H_ */ diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile index 797cf2f..4e5adba 100644 --- a/net/batman-adv/Makefile +++ b/net/batman-adv/Makefile @@ -33,6 +33,7 @@ batman-adv-y += hash.o batman-adv-y += icmp_socket.o batman-adv-y += main.o batman-adv-$(CONFIG_BATMAN_ADV_MCAST) += multicast.o +batman-adv-y += netlink.o batman-adv-$(CONFIG_BATMAN_ADV_NC) += network-coding.o batman-adv-y += originator.o batman-adv-y += routing.o diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index d64ddb9..895b7b6 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -47,6 +47,7 @@ #include <linux/workqueue.h> #include <net/dsfield.h> #include <net/rtnetlink.h> +#include <uapi/linux/batman_adv.h>
#include "bat_algo.h" #include "bridge_loop_avoidance.h" @@ -57,6 +58,7 @@ #include "hard-interface.h" #include "icmp_socket.h" #include "multicast.h" +#include "netlink.h" #include "network-coding.h" #include "originator.h" #include "packet.h" @@ -101,6 +103,7 @@ static int __init batadv_init(void)
register_netdevice_notifier(&batadv_hard_if_notifier); rtnl_link_register(&batadv_link_ops); + batadv_netlink_register();
pr_info("B.A.T.M.A.N. advanced %s (compatibility version %i) loaded\n", BATADV_SOURCE_VERSION, BATADV_COMPAT_VERSION); @@ -111,6 +114,7 @@ static int __init batadv_init(void) static void __exit batadv_exit(void) { batadv_debugfs_destroy(); + batadv_netlink_unregister(); rtnl_link_unregister(&batadv_link_ops); unregister_netdevice_notifier(&batadv_hard_if_notifier); batadv_hardif_remove_interfaces(); @@ -598,6 +602,50 @@ int batadv_algo_seq_print_text(struct seq_file *seq, void *offset) return 0; }
+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; +} + +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; +} + /** * batadv_skb_crc32 - calculate CRC32 of the whole packet and skip bytes in * the header diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index e602408..3cae306 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -217,6 +217,7 @@ void batadv_recv_handler_unregister(u8 packet_type); 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); __be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr);
/** diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c new file mode 100644 index 0000000..6682f78 --- /dev/null +++ b/net/batman-adv/netlink.c @@ -0,0 +1,251 @@ +/* Copyright (C) 2016 B.A.T.M.A.N. contributors: + * + * Matthias Schiffer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. + */ + +#include "main.h" +#include "netlink.h" + +#include <linux/netdevice.h> +#include <net/sock.h> +#include <uapi/linux/batman_adv.h> + +#include "hard-interface.h" +#include "originator.h" +#include "soft-interface.h" +#include "translation-table.h" + +struct genl_family batadv_netlink_family = { + .id = GENL_ID_GENERATE, + .hdrsize = 0, + .name = BATADV_NL_NAME, + .version = 1, + .maxattr = BATADV_ATTR_MAX, +}; + +static int +batadv_netlink_mesh_info_put(struct sk_buff *msg, struct net_device *soft_iface) +{ + int ret = -ENOBUFS; + struct batadv_priv *bat_priv = netdev_priv(soft_iface); + struct batadv_hard_iface *primary_if = NULL; + struct net_device *hard_iface; + + if (nla_put_string(msg, BATADV_ATTR_VERSION, BATADV_SOURCE_VERSION) || + nla_put_string(msg, BATADV_ATTR_ALGO_NAME, + bat_priv->bat_algo_ops->name) || + 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)) + goto out; + + primary_if = batadv_primary_if_get_selected(bat_priv); + if (primary_if && primary_if->if_status == BATADV_IF_ACTIVE) { + hard_iface = primary_if->net_dev; + + if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX, + hard_iface->ifindex) || + nla_put_string(msg, BATADV_ATTR_HARD_IFNAME, + hard_iface->name) || + nla_put(msg, BATADV_ATTR_HARD_ADDRESS, ETH_ALEN, + hard_iface->dev_addr)) + goto out; + } + + ret = 0; + + out: + if (primary_if) + batadv_hardif_put(primary_if); + + return ret; +} + +static int +batadv_netlink_get_mesh_info(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = genl_info_net(info); + int ret; + struct sk_buff *msg = NULL; + void *msg_head; + int ifindex; + struct net_device *soft_iface; + + if (!info->attrs[BATADV_ATTR_MESH_IFINDEX]) + return -EINVAL; + + ifindex = nla_get_u32(info->attrs[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; + } + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) { + ret = -ENOMEM; + goto out; + } + + msg_head = genlmsg_put(msg, info->snd_portid, info->snd_seq, + &batadv_netlink_family, 0, + BATADV_CMD_GET_MESH_INFO); + if (!msg_head) { + ret = -ENOBUFS; + goto out; + } + + ret = batadv_netlink_mesh_info_put(msg, soft_iface); + + out: + if (soft_iface) + dev_put(soft_iface); + + if (ret) { + if (msg) + nlmsg_free(msg); + return ret; + } + + genlmsg_end(msg, msg_head); + return genlmsg_reply(msg, info); +} + +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; +} + +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 nla_policy batadv_netlink_policy[BATADV_ATTR_MAX + 1] = { + [BATADV_ATTR_MESH_IFINDEX] = { .type = NLA_U32 }, +}; + +static struct genl_ops batadv_netlink_ops[] = { + { + .cmd = BATADV_CMD_GET_ROUTING_ALGOS, + .flags = GENL_ADMIN_PERM, + .policy = batadv_netlink_policy, + .dumpit = batadv_algo_dump, + }, + { + .cmd = BATADV_CMD_GET_MESH_INFO, + .flags = GENL_ADMIN_PERM, + .policy = batadv_netlink_policy, + .doit = batadv_netlink_get_mesh_info, + }, + { + .cmd = BATADV_CMD_GET_HARDIFS, + .flags = GENL_ADMIN_PERM, + .policy = batadv_netlink_policy, + .dumpit = batadv_netlink_dump_hardifs, + }, +}; + +void __init batadv_netlink_register(void) +{ + int ret; + + ret = genl_register_family_with_ops(&batadv_netlink_family, + batadv_netlink_ops); + if (ret) + pr_warn("unable to register netlink family"); +} + +void batadv_netlink_unregister(void) +{ + genl_unregister_family(&batadv_netlink_family); +} diff --git a/net/batman-adv/netlink.h b/net/batman-adv/netlink.h new file mode 100644 index 0000000..31022d4 --- /dev/null +++ b/net/batman-adv/netlink.h @@ -0,0 +1,36 @@ +/* Copyright (C) 2016 B.A.T.M.A.N. contributors: + * + * Matthias Schiffer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. + */ + +#ifndef _NET_BATMAN_ADV_NETLINK_H_ +#define _NET_BATMAN_ADV_NETLINK_H_ + +#include <net/genetlink.h> + +void batadv_netlink_register(void); +void batadv_netlink_unregister(void); + +static inline 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; +} + +extern struct genl_family batadv_netlink_family; + +#endif /* _NET_BATMAN_ADV_NETLINK_H_ */
This adds the commands BATADV_CMD_GET_TRANSTABLE_LOCAL and BATADV_CMD_GET_TRANSTABLE_GLOBAL, which correspond to the transtable_local and transtable_global debugfs files.
The batadv_tt_client_flags enum is moved to the UAPI to expose it as part of the netlink API.
Signed-off-by: Matthias Schiffer mschiffer@universe-factory.net --- include/uapi/linux/batman_adv.h | 47 ++++++ net/batman-adv/netlink.c | 12 ++ net/batman-adv/packet.h | 36 ----- net/batman-adv/translation-table.c | 305 +++++++++++++++++++++++++++++++++++++ net/batman-adv/translation-table.h | 2 + 5 files changed, 366 insertions(+), 36 deletions(-)
diff --git a/include/uapi/linux/batman_adv.h b/include/uapi/linux/batman_adv.h index b9b7dfd..25dee3c 100644 --- a/include/uapi/linux/batman_adv.h +++ b/include/uapi/linux/batman_adv.h @@ -20,6 +20,42 @@
#define BATADV_NL_NAME "batadv"
+/** + * 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_ATTR_UNSPEC, BATADV_ATTR_VERSION, @@ -31,6 +67,15 @@ enum { BATADV_ATTR_HARD_IFNAME, BATADV_ATTR_HARD_ADDRESS, BATADV_ATTR_ACTIVE, + BATADV_ATTR_ORIG_ADDRESS, + 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_MAX, };
@@ -41,6 +86,8 @@ enum { BATADV_CMD_GET_ROUTING_ALGOS, BATADV_CMD_GET_MESH_INFO, BATADV_CMD_GET_HARDIFS, + BATADV_CMD_GET_TRANSTABLE_LOCAL, + BATADV_CMD_GET_TRANSTABLE_GLOBAL, __BATADV_CMD_MAX, };
diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index 6682f78..50047d4 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -233,6 +233,18 @@ static struct genl_ops batadv_netlink_ops[] = { .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, + }, };
void __init batadv_netlink_register(void) diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h index 8a8d7ca..e7eb5eb 100644 --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@ -126,42 +126,6 @@ enum batadv_tt_data_flags { };
/** - * 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 */ diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index ce63a51..1e1f2a1 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -43,11 +43,15 @@ #include <linux/stddef.h> #include <linux/string.h> #include <linux/workqueue.h> +#include <net/net_namespace.h> +#include <net/sock.h> +#include <uapi/linux/batman_adv.h>
#include "bridge_loop_avoidance.h" #include "hard-interface.h" #include "hash.h" #include "multicast.h" +#include "netlink.h" #include "originator.h" #include "packet.h" #include "soft-interface.h" @@ -1064,6 +1068,138 @@ out: return 0; }
+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, + BATADV_PRINT_VID(common->vid)) || + nla_put_u32(msg, BATADV_ATTR_TT_FLAGS, common->flags)) + goto nla_put_failure; + + if (!(common->flags & BATADV_TT_CLIENT_NOPURGE)) { + if (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; +} + +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; +} + +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 = NULL; + 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, @@ -1729,6 +1865,175 @@ out: return 0; }
+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, + BATADV_PRINT_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; +} + +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; +} + +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; +} + +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 = NULL; + 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 diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h index 7c7e2c0..6d8b5b2 100644 --- a/net/batman-adv/translation-table.h +++ b/net/batman-adv/translation-table.h @@ -33,6 +33,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);
Add BATADV_CMD_GET_ORIGINATORS and BATADV_CMD_GET_NEIGHBORS commands, using handlers bat_orig_dump and bat_neigh_dump in batadv_algo_ops. Will always return -EOPNOTSUPP for now, as no implementations exist yet.
Signed-off-by: Matthias Schiffer mschiffer@universe-factory.net --- include/uapi/linux/batman_adv.h | 2 + net/batman-adv/netlink.c | 13 ++++ net/batman-adv/originator.c | 142 ++++++++++++++++++++++++++++++++++++++++ net/batman-adv/originator.h | 2 + net/batman-adv/types.h | 7 ++ 5 files changed, 166 insertions(+)
diff --git a/include/uapi/linux/batman_adv.h b/include/uapi/linux/batman_adv.h index 25dee3c..35d8e8c 100644 --- a/include/uapi/linux/batman_adv.h +++ b/include/uapi/linux/batman_adv.h @@ -88,6 +88,8 @@ enum { BATADV_CMD_GET_HARDIFS, BATADV_CMD_GET_TRANSTABLE_LOCAL, BATADV_CMD_GET_TRANSTABLE_GLOBAL, + BATADV_CMD_GET_ORIGINATORS, + BATADV_CMD_GET_NEIGHBORS, __BATADV_CMD_MAX, };
diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index 50047d4..3b40eb5 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -212,6 +212,7 @@ batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb)
static struct nla_policy batadv_netlink_policy[BATADV_ATTR_MAX + 1] = { [BATADV_ATTR_MESH_IFINDEX] = { .type = NLA_U32 }, + [BATADV_ATTR_HARD_IFINDEX] = { .type = NLA_U32 }, };
static struct genl_ops batadv_netlink_ops[] = { @@ -245,6 +246,18 @@ static struct genl_ops batadv_netlink_ops[] = { .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, + }, };
void __init batadv_netlink_register(void) diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index e63d6a5..d1b16e7 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -33,6 +33,8 @@ #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/workqueue.h> +#include <net/sock.h> +#include <uapi/linux/batman_adv.h>
#include "distributed-arp-table.h" #include "fragmentation.h" @@ -40,8 +42,10 @@ #include "hard-interface.h" #include "hash.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 */ @@ -719,6 +723,75 @@ int batadv_hardif_neigh_seq_print_text(struct seq_file *seq, void *offset) return 0; }
+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 = NULL; + 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->bat_algo_ops->bat_neigh_dump) { + ret = -EOPNOTSUPP; + goto out; + } + + bat_priv->bat_algo_ops->bat_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 @@ -1310,6 +1383,75 @@ out: return 0; }
+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 = NULL; + 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->bat_algo_ops->bat_orig_dump) { + ret = -EOPNOTSUPP; + goto out; + } + + bat_priv->bat_algo_ops->bat_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) { diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h index 64a8951..a8eb520 100644 --- a/net/batman-adv/originator.h +++ b/net/batman-adv/originator.h @@ -61,6 +61,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 +73,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); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 9abfb3e..befed63 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -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> @@ -1296,9 +1297,15 @@ struct batadv_algo_ops { struct batadv_hard_iface *if_outgoing2); void (*bat_neigh_print)(struct batadv_priv *priv, struct seq_file *seq); void (*bat_neigh_free)(struct batadv_neigh_node *neigh); + void (*bat_neigh_dump)(struct sk_buff *msg, struct netlink_callback *cb, + struct batadv_priv *priv, + struct batadv_hard_iface *hard_iface); /* orig_node handling API */ void (*bat_orig_print)(struct batadv_priv *priv, struct seq_file *seq, struct batadv_hard_iface *hard_iface); + void (*bat_orig_dump)(struct sk_buff *msg, struct netlink_callback *cb, + struct batadv_priv *priv, + struct batadv_hard_iface *hard_iface); void (*bat_orig_free)(struct batadv_orig_node *orig_node); int (*bat_orig_add_if)(struct batadv_orig_node *orig_node, int max_if_num);
Signed-off-by: Matthias Schiffer mschiffer@universe-factory.net --- include/uapi/linux/batman_adv.h | 2 + net/batman-adv/bat_iv_ogm.c | 268 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 270 insertions(+)
diff --git a/include/uapi/linux/batman_adv.h b/include/uapi/linux/batman_adv.h index 35d8e8c..baf4ac8 100644 --- a/include/uapi/linux/batman_adv.h +++ b/include/uapi/linux/batman_adv.h @@ -76,6 +76,8 @@ enum { BATADV_ATTR_TT_FLAGS, BATADV_ATTR_FLAG_BEST, BATADV_ATTR_LAST_SEEN_MSECS, + BATADV_ATTR_NEIGH_ADDRESS, + BATADV_ATTR_TQ, __BATADV_ATTR_MAX, };
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 2c65668..5aa413e 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -47,10 +47,12 @@ #include <linux/string.h> #include <linux/types.h> #include <linux/workqueue.h> +#include <uapi/linux/batman_adv.h>
#include "bitarray.h" #include "hard-interface.h" #include "hash.h" +#include "netlink.h" #include "network-coding.h" #include "originator.h" #include "packet.h" @@ -1916,6 +1918,171 @@ next: seq_puts(seq, "No batman nodes in range ...\n"); }
+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; +} + +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; + + 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; +} + +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; +} + +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; +} + +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 @@ -1968,6 +2135,105 @@ static void batadv_iv_neigh_print(struct batadv_priv *bat_priv, seq_puts(seq, "No batman nodes in range ...\n"); }
+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; +} + +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; +} + +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 @@ -2062,7 +2328,9 @@ static struct batadv_algo_ops batadv_batman_iv __read_mostly = { .bat_neigh_cmp = batadv_iv_ogm_neigh_cmp, .bat_neigh_is_similar_or_better = batadv_iv_ogm_neigh_is_sob, .bat_neigh_print = batadv_iv_neigh_print, + .bat_neigh_dump = batadv_iv_ogm_neigh_dump, .bat_orig_print = batadv_iv_ogm_orig_print, + .bat_orig_dump = batadv_iv_ogm_orig_dump, .bat_orig_free = batadv_iv_ogm_orig_free, .bat_orig_add_if = batadv_iv_ogm_orig_add_if, .bat_orig_del_if = batadv_iv_ogm_orig_del_if,
Signed-off-by: Matthias Schiffer mschiffer@universe-factory.net --- include/uapi/linux/batman_adv.h | 1 + net/batman-adv/bat_v.c | 250 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 251 insertions(+)
diff --git a/include/uapi/linux/batman_adv.h b/include/uapi/linux/batman_adv.h index baf4ac8..27f277f 100644 --- a/include/uapi/linux/batman_adv.h +++ b/include/uapi/linux/batman_adv.h @@ -78,6 +78,7 @@ enum { BATADV_ATTR_LAST_SEEN_MSECS, BATADV_ATTR_NEIGH_ADDRESS, BATADV_ATTR_TQ, + BATADV_ATTR_THROUGHPUT, __BATADV_ATTR_MAX, };
diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c index 3315b9a..203c9cd 100644 --- a/net/batman-adv/bat_v.c +++ b/net/batman-adv/bat_v.c @@ -29,10 +29,12 @@ #include <linux/seq_file.h> #include <linux/types.h> #include <linux/workqueue.h> +#include <uapi/linux/batman_adv.h>
#include "bat_v_elp.h" #include "bat_v_ogm.h" #include "hash.h" +#include "netlink.h" #include "originator.h" #include "packet.h"
@@ -171,6 +173,107 @@ static void batadv_v_neigh_print(struct batadv_priv *bat_priv, seq_puts(seq, "No batman nodes in range ...\n"); }
+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; +} + +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; +} + +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 @@ -239,6 +342,151 @@ next: seq_puts(seq, "No batman nodes in range ...\n"); }
+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); + + 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; +} + +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; +} + +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; +} + +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, @@ -282,9 +530,11 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = { .bat_ogm_emit = batadv_v_ogm_emit, .bat_ogm_schedule = batadv_v_ogm_schedule, .bat_orig_print = batadv_v_orig_print, + .bat_orig_dump = batadv_v_orig_dump, .bat_neigh_cmp = batadv_v_neigh_cmp, .bat_neigh_is_similar_or_better = batadv_v_neigh_is_sob, .bat_neigh_print = batadv_v_neigh_print, + .bat_neigh_dump = batadv_v_neigh_dump, };
/**
On 03/17/2016 05:45 PM, Matthias Schiffer wrote:
Hi, this is the second take of my netlink API patches. As mentioned before, the netlink API is superior to the current debugfs API in many aspects:
- debugfs is broken (see PATCH 1 for details)
- Netlink is namespace-aware, and can be used in unprivileged containers without problems
- Netlink packets are more machine-readable than text files, and can be easily extended without potentially breaking userspace
- On older kernels, seq_file can't fall back to vmalloc if kmalloc fails, which often leads to OOM when reading "originators" in large meshes, as the whole file must fit into a single buffer
Of course, are also a few downsides; when the data is too big to fit into a single netlink packet, the provided data may be inconsistent (entries may be missing or duplicated.) This will happen in large meshes only, and be improbable in any case.
The patches have been developed on top of the netns patchset, but should be applicable independently (maybe with minor changes.)
All netlink queries returning lists of any kind can only be used with NLM_F_DUMP queries, so that arbitrarity large responses are possible (split across multiple packets if necessary.)
At the moment, the following debugfs files have corresponding netlink APIs:
- routing_algos
- neighbors
- originators
- transtable_global
- transtable_local
- (hardinterfaces for a softif can be queried)
The following files are still missing:
- gateways
- bla_claim_table
- bla_backbone_table
- dat_cache
- nc_nodes
Obviously, documentation is also a TODO. Comments about the API design are very welcome...
Regards, Matthias
And here is the new version of the PoC userspace tool. Build with:
gcc -o batnl batnl.c $$(pkg-config --cflags --libs libnl-1) -Wall
On Thursday 17 March 2016 18:04:56 Matthias Schiffer wrote:
And here is the new version of the PoC userspace tool. Build with:
gcc -o batnl batnl.c $$(pkg-config --cflags --libs libnl-1) -Wall
Thanks for the patches.
I've only looked at the test querier for a second and I am a little bit confused that you access things like attrs[BATADV_ATTR_ALGO_NAME] without checking if this is available or that the received message is valid (according to the nla_parse policy).
I know that this is only a PoC but wanted to mention it in case somebody wants to use it as reference for a batctl/alfred implementation.
I like that the netlink interface doesn't introduce complex structs at the moment (but I have to think a little bit about it). The unnamed enums freak me out a little bit (names would be nice so it is easier to write kernel-doc for it).
I just ran the build_test stuff through your branch so you get an early feedback about some common problems. See the attached mail (the unused symbols seem to be only symptoms of the build failures with old kernels < 3.12).
Kind regards, Sven
On 03/18/2016 08:09 AM, Sven Eckelmann wrote:
On Thursday 17 March 2016 18:04:56 Matthias Schiffer wrote:
And here is the new version of the PoC userspace tool. Build with:
gcc -o batnl batnl.c $$(pkg-config --cflags --libs libnl-1) -Wall
Thanks for the patches.
I've only looked at the test querier for a second and I am a little bit confused that you access things like attrs[BATADV_ATTR_ALGO_NAME] without checking if this is available or that the received message is valid (according to the nla_parse policy).
I know that this is only a PoC but wanted to mention it in case somebody wants to use it as reference for a batctl/alfred implementation.
I like that the netlink interface doesn't introduce complex structs at the moment (but I have to think a little bit about it). The unnamed enums freak me out a little bit (names would be nice so it is easier to write kernel-doc for it).
I just ran the build_test stuff through your branch so you get an early feedback about some common problems. See the attached mail (the unused symbols seem to be only symptoms of the build failures with old kernels < 3.12).
Kind regards, Sven
Hi, regarding the first issue: I guess I should just add validation, as doing so is simple and can only improve robustness. One line of thought that seems to be common when dealing with such ABIs is that the kernel would break its ABI contract by sending invalid messages, and that userspace behaviour would be unspecified anyways if the kernel misbehaves.
I guess we can just give the enums names. Looking through the other UAPI files, some enums have names, others don't.
Thanks for the build testing, I haven't put too much thought into the compat issues yet. The genl_register_family_with_ops error should be easy to fix, the struct netlink_skb_parms differences might be more problematic.
Regards, Matthias
On Friday 18 March 2016 12:53:37 Matthias Schiffer wrote: [...]
Thanks for the build testing, I haven't put too much thought into the compat issues yet. The genl_register_family_with_ops error should be easy to fix, the struct netlink_skb_parms differences might be more problematic.
The genl_register_family_with_ops error can most likely be solved by
https://git.kernel.org/cgit/linux/kernel/git/backports/backports.git/tree/ba...
The netlink_skb_parms most likely can be fixed by redefining NETLINK_CB to something which casts to a structure with the name portid for the field pid for Linux < 3.7.
struct netlink_skb_parms_compat { struct scm_creds creds; __u32 portid; /* was renamed in 3.7 from "pid" */ __u32 dst_group; __u32 flags; struct sock *sk; };
Kind regards, Sven
On Thursday 17 March 2016 17:45:30 Matthias Schiffer wrote: [...]
At the moment, the following debugfs files have corresponding netlink APIs:
- routing_algos
- neighbors
- originators
- transtable_global
- transtable_local
- (hardinterfaces for a softif can be queried)
The following files are still missing:
- gateways
- bla_claim_table
- bla_backbone_table
- dat_cache
- nc_nodes
What about the "socket" file?
Kind regards, Sven
On 03/18/2016 12:23 PM, Sven Eckelmann wrote:
On Thursday 17 March 2016 17:45:30 Matthias Schiffer wrote: [...]
At the moment, the following debugfs files have corresponding netlink APIs:
- routing_algos
- neighbors
- originators
- transtable_global
- transtable_local
- (hardinterfaces for a softif can be queried)
The following files are still missing:
- gateways
- bla_claim_table
- bla_backbone_table
- dat_cache
- nc_nodes
What about the "socket" file?
Kind regards, Sven
Ah, I forgot about that one because it is very different from the others.
I think it should be possible to convert "socket" as well by wrapping the BATADV_ICMP packets in netlink packets. Or maybe it would be possible to drop that altogether and just do ping/traceroute in userspace with AF_PACKET or similar?
Regards, Matthias
On Friday 18 March 2016 13:00:54 Matthias Schiffer wrote: [...]
What about the "socket" file?
[...]
Ah, I forgot about that one because it is very different from the others.
I think it should be possible to convert "socket" as well by wrapping the BATADV_ICMP packets in netlink packets. Or maybe it would be possible to drop that altogether and just do ping/traceroute in userspace with AF_PACKET or similar?
The latter could be problematic for TP meter implementation [1,2,3,4,5]. Antonio, do you have an opinion about that?
Kind regards, Sven
[1] https://patchwork.open-mesh.org/patch/15807/ [2] https://patchwork.open-mesh.org/patch/15808/ [3] https://patchwork.open-mesh.org/patch/15809/ [4] https://patchwork.open-mesh.org/patch/15810/ [5] https://patchwork.open-mesh.org/patch/15811/
On Fri, Mar 18, 2016 at 01:04:50PM +0100, Sven Eckelmann wrote:
On Friday 18 March 2016 13:00:54 Matthias Schiffer wrote: [...]
What about the "socket" file?
[...]
Ah, I forgot about that one because it is very different from the others.
I think it should be possible to convert "socket" as well by wrapping the BATADV_ICMP packets in netlink packets. Or maybe it would be possible to drop that altogether and just do ping/traceroute in userspace with AF_PACKET or similar?
The latter could be problematic for TP meter implementation [1,2,3,4,5]. Antonio, do you have an opinion about that?
Personally I wouldn't mind doing ping/traceroute 100% in userspace.
The TP meter itself does not require to exchange real packets between user and kernel space, but only requires the availability of a command to start and stop a measurement session. I believe these could be implemented as yet another netlink command/message type.
Cheers,
On Saturday 19 March 2016 16:49:34 Antonio Quartulli wrote:
On Fri, Mar 18, 2016 at 01:04:50PM +0100, Sven Eckelmann wrote:
On Friday 18 March 2016 13:00:54 Matthias Schiffer wrote: [...]
What about the "socket" file?
[...]
Ah, I forgot about that one because it is very different from the others.
I think it should be possible to convert "socket" as well by wrapping the BATADV_ICMP packets in netlink packets. Or maybe it would be possible to drop that altogether and just do ping/traceroute in userspace with AF_PACKET or similar?
The latter could be problematic for TP meter implementation [1,2,3,4,5]. Antonio, do you have an opinion about that?
Personally I wouldn't mind doing ping/traceroute 100% in userspace.
How can this be implemented 100% in userspace? Lets ignore the reply/forwarding part for a second and just concentrate on the initiator of the ping/traceroute. It can use AF_PACKET in userspace to send batadv packets directly on the hardif. But here comes the first problem... which is the correct hardif? There can be multiple and the userspace does not really know which one is the one the kernel would use at a particular time. So you have to implement the same algorithms in userspace the emulate the kernel part (or you may do something completely different). And still then you would have problems when the kernel is tweaked and the userspace didn't receive this tweak.
Maybe it is necessary to ask the kernel before sending the ping by asking him "hey, what interface would use use right now and what other parameters are now required". Similar to what "ip route get 8.8.8.8" would do.
Or am I just overcomplicated things now?
Kind regards, Sven
On Saturday 19 March 2016 10:19:39 Sven Eckelmann wrote:
What about the "socket" file?
[...]
How can this be implemented 100% in userspace? Lets ignore the reply/forwarding part for a second and just concentrate on the initiator of the ping/traceroute. It can use AF_PACKET in userspace to send batadv packets directly on the hardif. But here comes the first problem... which is the correct hardif? There can be multiple and the userspace does not really know which one is the one the kernel would use at a particular time. So you have to implement the same algorithms in userspace the emulate the kernel part (or you may do something completely different). And still then you would have problems when the kernel is tweaked and the userspace didn't receive this tweak.
Maybe it is necessary to ask the kernel before sending the ping by asking him "hey, what interface would use use right now and what other parameters are now required". Similar to what "ip route get 8.8.8.8" would do.
Or am I just overcomplicated things now?
It can also be postponed to later. No need to have this in netns from the start.
But RTM_GETROUTE-like NLM_F_REQUEST requests sound feasible for a userspace based ICMP-like request initiation. But most likely with generic messages instead of structs.
Kind regards, Sven
On Thursday 17 March 2016 17:45:30 Matthias Schiffer wrote:
Hi, this is the second take of my netlink API patches. As mentioned before, the netlink API is superior to the current debugfs API in many aspects:
- debugfs is broken (see PATCH 1 for details)
- Netlink is namespace-aware, and can be used in unprivileged containers without problems
- Netlink packets are more machine-readable than text files, and can be easily extended without potentially breaking userspace
- On older kernels, seq_file can't fall back to vmalloc if kmalloc fails, which often leads to OOM when reading "originators" in large meshes, as the whole file must fit into a single buffer
Of course, are also a few downsides; when the data is too big to fit into a single netlink packet, the provided data may be inconsistent (entries may be missing or duplicated.) This will happen in large meshes only, and be improbable in any case.
The patches have been developed on top of the netns patchset, but should be applicable independently (maybe with minor changes.)
All netlink queries returning lists of any kind can only be used with NLM_F_DUMP queries, so that arbitrarity large responses are possible (split across multiple packets if necessary.)
At the moment, the following debugfs files have corresponding netlink APIs:
- routing_algos
- neighbors
- originators
- transtable_global
- transtable_local
- (hardinterfaces for a softif can be queried)
The following files are still missing:
- gateways
- bla_claim_table
- bla_backbone_table
- dat_cache
- nc_nodes
Obviously, documentation is also a TODO. Comments about the API design are very welcome...
Regards, Matthias
Hi Matthias,
thanks for reposting this and sorry for the long silence. Since we have the namespace work from Andrew now which would require netlink support, I think its a good time to integrate this. :)
I'll try to look into your patchset this week into more detail and give feedback. In the meantime, just a few question:
* Would you like to add netlink support for the missing files as well? (gateways, bla, dat, nc) * Would you also like to do the batctl support? I guess we can have batctl behave "compatible" by trying netlink first and fall back to debugfs for the next few years * Also open is the icmp socket stuff, which we could postpone and implement later.
Just wanted to check with your availability first, but we would like to support the netlink adoption. :)
Thanks! Simon
The following files are still missing:
- gateways
- bla_claim_table
- bla_backbone_table
- dat_cache
- nc_nodes
Obviously, documentation is also a TODO. Comments about the API design are very welcome...
Regards, Matthias
Hi Matthias,
thanks for reposting this and sorry for the long silence. Since we have the namespace work from Andrew now which would require netlink support, I think its a good time to integrate this. :)
Hi Simon, Matthias
I picked up your patches and played with them when combined with my netns patches. All works well.
I also started on integrating the user space code with batctl. I've got as far as integrating netlink user space code for what you had, and i added gateways, and started on bla_claim_table.
I'm on vacation this week, and i'm not likely to be reading emails again this week.
I should be able to post code next week.
Andrew
Hey Andrew,
On Wednesday 20 April 2016 04:31:36 Andrew Lunn wrote:
The following files are still missing:
- gateways
- bla_claim_table
- bla_backbone_table
- dat_cache
- nc_nodes
Obviously, documentation is also a TODO. Comments about the API design are very welcome...
Regards, Matthias
Hi Matthias,
thanks for reposting this and sorry for the long silence. Since we have the namespace work from Andrew now which would require netlink support, I think its a good time to integrate this. :)
Hi Simon, Matthias
I picked up your patches and played with them when combined with my netns patches. All works well.
I also started on integrating the user space code with batctl. I've got as far as integrating netlink user space code for what you had, and i added gateways, and started on bla_claim_table.
I'm on vacation this week, and i'm not likely to be reading emails again this week.
I should be able to post code next week.
Cool! Thanks a lot for picking it up, I'm looking forward to your patches and will make sure to review them. :)
Thanks again, Simon
On 04/18/2016 04:50 PM, Simon Wunderlich wrote:
On Thursday 17 March 2016 17:45:30 Matthias Schiffer wrote:
Hi, this is the second take of my netlink API patches. As mentioned before, the netlink API is superior to the current debugfs API in many aspects:
- debugfs is broken (see PATCH 1 for details)
- Netlink is namespace-aware, and can be used in unprivileged containers without problems
- Netlink packets are more machine-readable than text files, and can be easily extended without potentially breaking userspace
- On older kernels, seq_file can't fall back to vmalloc if kmalloc fails, which often leads to OOM when reading "originators" in large meshes, as the whole file must fit into a single buffer
Of course, are also a few downsides; when the data is too big to fit into a single netlink packet, the provided data may be inconsistent (entries may be missing or duplicated.) This will happen in large meshes only, and be improbable in any case.
The patches have been developed on top of the netns patchset, but should be applicable independently (maybe with minor changes.)
All netlink queries returning lists of any kind can only be used with NLM_F_DUMP queries, so that arbitrarity large responses are possible (split across multiple packets if necessary.)
At the moment, the following debugfs files have corresponding netlink APIs:
- routing_algos
- neighbors
- originators
- transtable_global
- transtable_local
- (hardinterfaces for a softif can be queried)
The following files are still missing:
- gateways
- bla_claim_table
- bla_backbone_table
- dat_cache
- nc_nodes
Obviously, documentation is also a TODO. Comments about the API design are very welcome...
Regards, Matthias
Hi Matthias,
thanks for reposting this and sorry for the long silence. Since we have the namespace work from Andrew now which would require netlink support, I think its a good time to integrate this. :)
I'll try to look into your patchset this week into more detail and give feedback. In the meantime, just a few question:
- Would you like to add netlink support for the missing files as well?
(gateways, bla, dat, nc)
- Would you also like to do the batctl support? I guess we can have batctl
behave "compatible" by trying netlink first and fall back to debugfs for the next few years
- Also open is the icmp socket stuff, which we could postpone and implement
later.
Just wanted to check with your availability first, but we would like to support the netlink adoption. :)
Thanks! Simon
As Andrew has already started to integrate and extend my patchset, I'll happily leave further development to him - I'm quite busy with other projects at the moment, so it would probably take a lot longer if I were to work on this myself.
As I've mentioned before, regarding the batctl support, I'd love to see a little library ("libbatadv"?) on top of libnl that could be used by batctl, alfred and other monitoring tools (like the ones we are using in the Gluon Freifunk firmwares).
Regards, Matthias
On Wednesday 20 April 2016 09:32:19 Matthias Schiffer wrote: [...]
As I've mentioned before, regarding the batctl support, I'd love to see a little library ("libbatadv"?) on top of libnl that could be used by batctl, alfred and other monitoring tools (like the ones we are using in the Gluon Freifunk firmwares).
Hm, then we would have to make everything ABI stable. So we would loose the flexibility of netlink (adding more fields) again just to keep the library stable.
Or what kind of functionality should this library expose that can be easily be kept stable?
Kind regards, Sven
On 04/20/2016 09:39 AM, Sven Eckelmann wrote:
On Wednesday 20 April 2016 09:32:19 Matthias Schiffer wrote: [...]
As I've mentioned before, regarding the batctl support, I'd love to see a little library ("libbatadv"?) on top of libnl that could be used by batctl, alfred and other monitoring tools (like the ones we are using in the Gluon Freifunk firmwares).
Hm, then we would have to make everything ABI stable. So we would loose the flexibility of netlink (adding more fields) again just to keep the library stable.
Or what kind of functionality should this library expose that can be easily be kept stable?
Kind regards, Sven
I don't understand. The same code that would we added to batctl and alfred could just be made a library with a nicer API, so other projects can easily reuse it. Of course, the library API/ABI would need to be designed in a way that keeps it extensible without breaking ABI.
I'm thinking of functions like "iterate over all originators", which would query the netlink API, and then just call a callback with the netlink attribute set for each originator. Just a thin wrapper making things more usable.
Regards, Matthias
On Wednesday 20 April 2016 09:49:25 Matthias Schiffer wrote:
On 04/20/2016 09:39 AM, Sven Eckelmann wrote:
On Wednesday 20 April 2016 09:32:19 Matthias Schiffer wrote: [...]
As I've mentioned before, regarding the batctl support, I'd love to see a little library ("libbatadv"?) on top of libnl that could be used by batctl, alfred and other monitoring tools (like the ones we are using in the Gluon Freifunk firmwares).
Hm, then we would have to make everything ABI stable. So we would loose the flexibility of netlink (adding more fields) again just to keep the library stable.
[...]
I don't understand. The same code that would we added to batctl and alfred could just be made a library with a nicer API, so other projects can easily reuse it. Of course, the library API/ABI would need to be designed in a way that keeps it extensible without breaking ABI.
I'm thinking of functions like "iterate over all originators", which would query the netlink API, and then just call a callback with the netlink attribute set for each originator. Just a thin wrapper making things more usable.
Ok, I thought that you want to have complete abstraction of the netlink interface. But this now just sounds like you want to have some helper functions to make the initial request to the kernel. The callback with the netlink data is already part of libnl.
Kind regards, Sven
b.a.t.m.a.n@lists.open-mesh.org