[PATCH net 00/12] net: iflink and link-netnsid fixes
by Sabrina Dubroca
In a lot of places, we use this kind of comparison to detect if a
device has a lower link:
dev->ifindex != dev_get_iflink(dev)
This seems to be a leftover of the pre-netns days, when the ifindex
was unique over the whole system. Nowadays, with network namespaces,
it's very easy to create a device with the same ifindex as its lower
link:
ip netns add main
ip netns add peer
ip -net main link add dummy0 type dummy
ip -net main link add link dummy0 macvlan0 netns peer type macvlan
ip -net main link show type dummy
9: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop ...
ip -net peer link show type macvlan
9: macvlan0@if9: <BROADCAST,MULTICAST> mtu 1500 qdisc noop ...
To detect if a device has a lower link, we can simply check the
existence of the dev->netdev_ops->ndo_get_iflink operation, instead of
checking its return value. In particular, I attempted to fix one of
these checks in commit feadc4b6cf42 ("rtnetlink: always put IFLA_LINK
for links with a link-netnsid"), but this patch isn't correct, since
tunnel devices can export IFLA_LINK_NETNSID without IFLA_LINK. That
patch needs to be reverted.
This series will fix all those bogus comparisons, and export missing
IFLA_LINK_NETNSID attributes in bridge and ipv6 dumps.
ipvlan and geneve are also missing the get_link_net operation, so
userspace can't know when those device are cross-netns. There are a
couple of other device types that have an ndo_get_iflink op but no
get_link_net (virt_wifi, ipoib), and should probably also have a
get_link_net.
Sabrina Dubroca (12):
ipvlan: add get_link_net
geneve: add get_link_net
Revert "rtnetlink: always put IFLA_LINK for links with a link-netnsid"
rtnetlink: always put IFLA_LINK for links with ndo_get_iflink
bridge: always put IFLA_LINK for ports with ndo_get_iflink
bridge: advertise IFLA_LINK_NETNSID when dumping bridge ports
ipv6: always put IFLA_LINK for devices with ndo_get_iflink
ipv6: advertise IFLA_LINK_NETNSID when dumping ipv6 addresses
net: link_watch: fix operstate when the link has the same index as the
device
net: link_watch: fix detection of urgent events
batman-adv: fix iflink detection in batadv_is_on_batman_iface
batman-adv: fix detection of lower link in batadv_get_real_netdevice
drivers/net/can/vxcan.c | 2 +-
drivers/net/geneve.c | 8 ++++++++
drivers/net/ipvlan/ipvlan_main.c | 9 +++++++++
drivers/net/veth.c | 2 +-
include/net/rtnetlink.h | 4 ++++
net/batman-adv/hard-interface.c | 4 ++--
net/bridge/br_netlink.c | 4 +++-
net/core/link_watch.c | 4 ++--
net/core/rtnetlink.c | 25 ++++++++++++-------------
net/ipv6/addrconf.c | 11 ++++++++++-
10 files changed, 52 insertions(+), 21 deletions(-)
--
2.28.0
8 months, 3 weeks
WARNING in sta_info_alloc
by syzbot
Hello,
syzbot found the following issue on:
HEAD commit: 549738f1 Linux 5.9-rc8
git tree: upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=15b97ba3900000
kernel config: https://syzkaller.appspot.com/x/.config?x=c06bcf3cc963d91c
dashboard link: https://syzkaller.appspot.com/bug?extid=45d7c243c006f39dc55a
compiler: gcc (GCC) 10.1.0-syz 20200507
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=12bae9c0500000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=1099b1c0500000
The issue was bisected to:
commit 643c332d519bdfbf80d21f40d1c0aa0ccf3ec1cb
Author: Zi Shen Lim <zlim.lnx(a)gmail.com>
Date: Thu Jun 9 04:18:50 2016 +0000
arm64: bpf: optimize LD_ABS, LD_IND
bisection log: https://syzkaller.appspot.com/x/bisect.txt?x=11d44477900000
final oops: https://syzkaller.appspot.com/x/report.txt?x=13d44477900000
console output: https://syzkaller.appspot.com/x/log.txt?x=15d44477900000
IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+45d7c243c006f39dc55a(a)syzkaller.appspotmail.com
Fixes: 643c332d519b ("arm64: bpf: optimize LD_ABS, LD_IND")
------------[ cut here ]------------
WARNING: CPU: 0 PID: 6879 at net/mac80211/ieee80211_i.h:1447 ieee80211_get_sband net/mac80211/ieee80211_i.h:1447 [inline]
WARNING: CPU: 0 PID: 6879 at net/mac80211/ieee80211_i.h:1447 sta_info_alloc+0x1900/0x1f90 net/mac80211/sta_info.c:469
Kernel panic - not syncing: panic_on_warn set ...
CPU: 0 PID: 6879 Comm: syz-executor071 Not tainted 5.9.0-rc8-syzkaller #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
__dump_stack lib/dump_stack.c:77 [inline]
dump_stack+0x198/0x1fd lib/dump_stack.c:118
panic+0x382/0x7fb kernel/panic.c:231
__warn.cold+0x20/0x4b kernel/panic.c:600
report_bug+0x1bd/0x210 lib/bug.c:198
handle_bug+0x38/0x90 arch/x86/kernel/traps.c:234
exc_invalid_op+0x14/0x40 arch/x86/kernel/traps.c:254
asm_exc_invalid_op+0x12/0x20 arch/x86/include/asm/idtentry.h:536
RIP: 0010:ieee80211_get_sband net/mac80211/ieee80211_i.h:1447 [inline]
RIP: 0010:sta_info_alloc+0x1900/0x1f90 net/mac80211/sta_info.c:469
Code: 00 00 00 00 00 fc ff df 48 89 fa 48 c1 ea 03 80 3c 02 00 0f 85 f0 04 00 00 49 8b 9f 60 01 00 00 e9 fc f6 ff ff e8 80 20 b6 f9 <0f> 0b e8 e9 62 66 00 31 ff 89 c3 89 c6 e8 ce 1c b6 f9 85 db 74 1d
RSP: 0018:ffffc9000539f498 EFLAGS: 00010293
RAX: 0000000000000000 RBX: 0000000000000001 RCX: ffffffff87c01d61
RDX: ffff8880a91ec3c0 RSI: ffffffff87c01e10 RDI: 0000000000000005
RBP: ffff8880896e0c80 R08: 0000000000000001 R09: ffffffff8d0c29e7
R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
R13: ffff8880896e31b0 R14: dffffc0000000000 R15: ffff888092f06000
ieee80211_add_station+0x28c/0x660 net/mac80211/cfg.c:1586
rdev_add_station net/wireless/rdev-ops.h:190 [inline]
nl80211_new_station+0xde7/0x1440 net/wireless/nl80211.c:6294
genl_family_rcv_msg_doit net/netlink/genetlink.c:669 [inline]
genl_family_rcv_msg net/netlink/genetlink.c:714 [inline]
genl_rcv_msg+0x61d/0x980 net/netlink/genetlink.c:731
netlink_rcv_skb+0x15a/0x430 net/netlink/af_netlink.c:2470
genl_rcv+0x24/0x40 net/netlink/genetlink.c:742
netlink_unicast_kernel net/netlink/af_netlink.c:1304 [inline]
netlink_unicast+0x533/0x7d0 net/netlink/af_netlink.c:1330
netlink_sendmsg+0x856/0xd90 net/netlink/af_netlink.c:1919
sock_sendmsg_nosec net/socket.c:651 [inline]
sock_sendmsg+0xcf/0x120 net/socket.c:671
____sys_sendmsg+0x6e8/0x810 net/socket.c:2353
___sys_sendmsg+0xf3/0x170 net/socket.c:2407
__sys_sendmsg+0xe5/0x1b0 net/socket.c:2440
do_syscall_64+0x2d/0x70 arch/x86/entry/common.c:46
entry_SYSCALL_64_after_hwframe+0x44/0xa9
RIP: 0033:0x441999
Code: e8 dc 05 03 00 48 83 c4 18 c3 0f 1f 80 00 00 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 0f 83 7b 0d fc ff c3 66 2e 0f 1f 84 00 00 00 00
RSP: 002b:00007ffd9fa54bf8 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 0000000000441999
RDX: 0000000000000000 RSI: 0000000020000040 RDI: 0000000000000005
RBP: 000000306e616c77 R08: 0000000000000000 R09: 0000002000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000032
R13: 0000000000000000 R14: 000000000000000c R15: 0000000000000004
Kernel Offset: disabled
Rebooting in 86400 seconds..
---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller(a)googlegroups.com.
syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
For information about bisection process see: https://goo.gl/tpsmEJ#bisection
syzbot can test patches for this issue, for details see:
https://goo.gl/tpsmEJ#testing-patches
1 year, 6 months
[PATCH v2] batman-adv: bcast: queue per interface, if needed
by Linus Lüssing
Currently we schedule a broadcast packet like:
3x: [ [(re-)queue] --> for(hard-if): maybe-transmit ]
The intention of queueing a broadcast packet multiple times is to
increase robustness for wireless interfaces. However on interfaces
which we only broadcast on once the queueing induces an unnecessary
penalty. This patch restructures the queueing to be performed on a per
interface basis:
for(hard-if):
- transmit
- if wireless: [queue] --> transmit --> [requeue] --> transmit
Next to the performance benefits on non-wireless interfaces this
should also make it easier to apply alternative strategies for
transmissions on wireless interfaces in the future (for instance sending
via unicast transmissions on wireless interfaces, without queueing in
batman-adv, if appropriate).
Signed-off-by: Linus Lüssing <linus.luessing(a)c0d3.blue>
---
Changelog v2:
* removed the other two patches from the patchset for now, only
the broadcast queueing cleanup to start with
* fixed spelling of "unnecessary" in commit message (thanks Sven)
* removed now superflous kerneldoc for hard_iface in
batadv_forw_packet_bcasts_left() (thanks Sven)
* removed delay check for queued (re)broadcasts in
batadv_forw_bcast_packet_if(): the only case where a delay is set
for this function is for a delayed, DAT fallback ARP Request from
this node, then however num_bcasts will be >=1, too, and the fallback
ARP Request will be scheduled anyway
---
net/batman-adv/main.h | 1 -
net/batman-adv/routing.c | 9 +-
net/batman-adv/send.c | 392 ++++++++++++++++++++++----------
net/batman-adv/send.h | 12 +-
net/batman-adv/soft-interface.c | 12 +-
5 files changed, 288 insertions(+), 138 deletions(-)
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index 8f0102b7..baa9fcbe 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -88,7 +88,6 @@
/* number of packets to send for broadcasts on different interface types */
#define BATADV_NUM_BCASTS_DEFAULT 1
#define BATADV_NUM_BCASTS_WIRELESS 3
-#define BATADV_NUM_BCASTS_MAX 3
/* length of the single packet used by the TP meter */
#define BATADV_TP_PACKET_LEN ETH_DATA_LEN
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
index 40f5cffd..bb9e93e3 100644
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -1182,9 +1182,9 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
struct batadv_bcast_packet *bcast_packet;
struct ethhdr *ethhdr;
int hdr_size = sizeof(*bcast_packet);
- int ret = NET_RX_DROP;
s32 seq_diff;
u32 seqno;
+ int ret;
/* drop packet if it has not necessary minimum size */
if (unlikely(!pskb_may_pull(skb, hdr_size)))
@@ -1210,7 +1210,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
if (batadv_is_my_mac(bat_priv, bcast_packet->orig))
goto free_skb;
- if (bcast_packet->ttl < 2)
+ if (bcast_packet->ttl-- < 2)
goto free_skb;
orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig);
@@ -1249,7 +1249,9 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
batadv_skb_set_priority(skb, sizeof(struct batadv_bcast_packet));
/* rebroadcast packet */
- batadv_add_bcast_packet_to_list(bat_priv, skb, 1, false);
+ ret = batadv_forw_bcast_packet(bat_priv, skb, 0, false);
+ if (ret == NETDEV_TX_BUSY)
+ goto free_skb;
/* don't hand the broadcast up if it is from an originator
* from the same backbone.
@@ -1275,6 +1277,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
spin_unlock_bh(&orig_node->bcast_seqno_lock);
free_skb:
kfree_skb(skb);
+ ret = NET_RX_DROP;
out:
if (orig_node)
batadv_orig_node_put(orig_node);
diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c
index 157abe92..1db6b217 100644
--- a/net/batman-adv/send.c
+++ b/net/batman-adv/send.c
@@ -737,57 +737,52 @@ void batadv_forw_packet_ogmv1_queue(struct batadv_priv *bat_priv,
}
/**
- * batadv_add_bcast_packet_to_list() - queue broadcast packet for multiple sends
+ * batadv_forw_bcast_packet_to_list() - queue broadcast packet for transmissions
* @bat_priv: the bat priv with all the soft interface information
* @skb: broadcast packet to add
* @delay: number of jiffies to wait before sending
* @own_packet: true if it is a self-generated broadcast packet
+ * @if_in: the interface where the packet was received on
+ * @if_out: the outgoing interface to queue on
*
- * add a broadcast packet to the queue and setup timers. broadcast packets
+ * Adds a broadcast packet to the queue and sets up timers. Broadcast packets
* are sent multiple times to increase probability for being received.
*
- * The skb is not consumed, so the caller should make sure that the
- * skb is freed.
+ * This call clones the given skb, hence the caller needs to take into
+ * account that the data segment of the original skb might not be
+ * modifiable anymore.
*
* Return: NETDEV_TX_OK on success and NETDEV_TX_BUSY on errors.
*/
-int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
- const struct sk_buff *skb,
- unsigned long delay,
- bool own_packet)
+static int batadv_forw_bcast_packet_to_list(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ unsigned long delay,
+ bool own_packet,
+ struct batadv_hard_iface *if_in,
+ struct batadv_hard_iface *if_out)
{
- struct batadv_hard_iface *primary_if;
struct batadv_forw_packet *forw_packet;
- struct batadv_bcast_packet *bcast_packet;
+ unsigned long send_time = jiffies;
struct sk_buff *newskb;
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (!primary_if)
+ newskb = skb_clone(skb, GFP_ATOMIC);
+ if (!newskb)
goto err;
- newskb = skb_copy(skb, GFP_ATOMIC);
- if (!newskb) {
- batadv_hardif_put(primary_if);
- goto err;
- }
-
- forw_packet = batadv_forw_packet_alloc(primary_if, NULL,
+ forw_packet = batadv_forw_packet_alloc(if_in, if_out,
&bat_priv->bcast_queue_left,
bat_priv, newskb);
- batadv_hardif_put(primary_if);
if (!forw_packet)
goto err_packet_free;
- /* as we have a copy now, it is safe to decrease the TTL */
- bcast_packet = (struct batadv_bcast_packet *)newskb->data;
- bcast_packet->ttl--;
-
forw_packet->own = own_packet;
INIT_DELAYED_WORK(&forw_packet->delayed_work,
batadv_send_outstanding_bcast_packet);
- batadv_forw_packet_bcast_queue(bat_priv, forw_packet, jiffies + delay);
+ send_time += delay ? delay : msecs_to_jiffies(5);
+
+ batadv_forw_packet_bcast_queue(bat_priv, forw_packet, send_time);
return NETDEV_TX_OK;
err_packet_free:
@@ -796,10 +791,234 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
return NETDEV_TX_BUSY;
}
+/**
+ * batadv_forw_bcast_packet_if() - forward and queue a broadcast packet
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: broadcast packet to add
+ * @delay: number of jiffies to wait before sending
+ * @own_packet: true if it is a self-generated broadcast packet
+ * @if_in: the interface where the packet was received on
+ * @if_out: the outgoing interface to forward to
+ *
+ * Transmits a broadcast packet on the specified interface either immediately
+ * or if a delay is given after that. Furthermore, queues additional
+ * retransmissions if this interface is a wireless one.
+ *
+ * This call clones the given skb, hence the caller needs to take into
+ * account that the data segment of the original skb might not be
+ * modifiable anymore.
+ *
+ * Return: NETDEV_TX_OK on success and NETDEV_TX_BUSY on errors.
+ */
+static int batadv_forw_bcast_packet_if(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ unsigned long delay,
+ bool own_packet,
+ struct batadv_hard_iface *if_in,
+ struct batadv_hard_iface *if_out)
+{
+ unsigned int num_bcasts = if_out->num_bcasts;
+ struct sk_buff *newskb;
+ int ret = NETDEV_TX_OK;
+
+ if (!delay) {
+ newskb = skb_clone(skb, GFP_ATOMIC);
+ if (!newskb)
+ return NETDEV_TX_BUSY;
+
+ batadv_send_broadcast_skb(newskb, if_out);
+ num_bcasts--;
+ }
+
+ /* delayed broadcast or rebroadcasts? */
+ if (num_bcasts >= 1) {
+ BATADV_SKB_CB(skb)->num_bcasts = num_bcasts;
+
+ ret = batadv_forw_bcast_packet_to_list(bat_priv, skb, delay,
+ own_packet, if_in,
+ if_out);
+ }
+
+ return ret;
+}
+
+/**
+ * batadv_send_no_broadcast() - check whether (re)broadcast is necessary
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: broadcast packet to check
+ * @own_packet: true if it is a self-generated broadcast packet
+ * @if_out: the outgoing interface checked and considered for (re)broadcast
+ *
+ * Return: False if a packet needs to be (re)broadcasted on the given interface,
+ * true otherwise.
+ */
+static bool batadv_send_no_broadcast(struct batadv_priv *bat_priv,
+ struct sk_buff *skb, bool own_packet,
+ struct batadv_hard_iface *if_out)
+{
+ struct batadv_hardif_neigh_node *neigh_node = NULL;
+ struct batadv_bcast_packet *bcast_packet;
+ u8 *orig_neigh;
+ u8 *neigh_addr;
+ char *type;
+ int ret;
+
+ if (!own_packet) {
+ neigh_addr = eth_hdr(skb)->h_source;
+ neigh_node = batadv_hardif_neigh_get(if_out,
+ neigh_addr);
+ }
+
+ bcast_packet = (struct batadv_bcast_packet *)skb->data;
+ orig_neigh = neigh_node ? neigh_node->orig : NULL;
+
+ ret = batadv_hardif_no_broadcast(if_out, bcast_packet->orig,
+ orig_neigh);
+
+ if (neigh_node)
+ batadv_hardif_neigh_put(neigh_node);
+
+ /* ok, may broadcast */
+ if (!ret)
+ return false;
+
+ /* no broadcast */
+ switch (ret) {
+ case BATADV_HARDIF_BCAST_NORECIPIENT:
+ type = "no neighbor";
+ break;
+ case BATADV_HARDIF_BCAST_DUPFWD:
+ type = "single neighbor is source";
+ break;
+ case BATADV_HARDIF_BCAST_DUPORIG:
+ type = "single neighbor is originator";
+ break;
+ default:
+ type = "unknown";
+ }
+
+ batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
+ "BCAST packet from orig %pM on %s suppressed: %s\n",
+ bcast_packet->orig,
+ if_out->net_dev->name, type);
+
+ return true;
+}
+
+/**
+ * __batadv_forw_bcast_packet() - forward and queue a broadcast packet
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: broadcast packet to add
+ * @delay: number of jiffies to wait before sending
+ * @own_packet: true if it is a self-generated broadcast packet
+ *
+ * Transmits a broadcast packet either immediately or if a delay is given
+ * after that. Furthermore, queues additional retransmissions on wireless
+ * interfaces.
+ *
+ * This call clones the given skb, hence the caller needs to take into
+ * account that the data segment of the given skb might not be
+ * modifiable anymore.
+ *
+ * Return: NETDEV_TX_OK on success and NETDEV_TX_BUSY on errors.
+ */
+static int __batadv_forw_bcast_packet(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ unsigned long delay,
+ bool own_packet)
+{
+ struct batadv_hard_iface *hard_iface;
+ struct batadv_hard_iface *primary_if;
+ int ret = NETDEV_TX_OK;
+
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ return NETDEV_TX_BUSY;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
+ if (hard_iface->soft_iface != bat_priv->soft_iface)
+ continue;
+
+ if (!kref_get_unless_zero(&hard_iface->refcount))
+ continue;
+
+ if (batadv_send_no_broadcast(bat_priv, skb, own_packet,
+ hard_iface)) {
+ batadv_hardif_put(hard_iface);
+ continue;
+ }
+
+ ret = batadv_forw_bcast_packet_if(bat_priv, skb, delay,
+ own_packet, primary_if,
+ hard_iface);
+ batadv_hardif_put(hard_iface);
+
+ if (ret == NETDEV_TX_BUSY)
+ break;
+ }
+ rcu_read_unlock();
+
+ batadv_hardif_put(primary_if);
+ return ret;
+}
+
+/**
+ * batadv_forw_bcast_packet() - forward and queue a broadcast packet
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: broadcast packet to add
+ * @delay: number of jiffies to wait before sending
+ * @own_packet: true if it is a self-generated broadcast packet
+ *
+ * Transmits a broadcast packet either immediately or if a delay is given
+ * after that. Furthermore, queues additional retransmissions on wireless
+ * interfaces.
+ *
+ * This call might reallocate skb data.
+ *
+ * Return: NETDEV_TX_OK on success and NETDEV_TX_BUSY on errors.
+ */
+int batadv_forw_bcast_packet(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ unsigned long delay,
+ bool own_packet)
+{
+ int ret = __batadv_forw_bcast_packet(bat_priv, skb, delay, own_packet);
+
+ if (ret == NETDEV_TX_BUSY)
+ return ret;
+
+ /* __batadv_forw_bcast_packet clones, make sure original
+ * skb stays writeable
+ */
+ return (skb_cow(skb, 0) < 0) ? NETDEV_TX_BUSY : NETDEV_TX_OK;
+}
+
+/**
+ * batadv_send_bcast_packet() - send and queue a broadcast packet
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: broadcast packet to add
+ * @delay: number of jiffies to wait before sending
+ * @own_packet: true if it is a self-generated broadcast packet
+ *
+ * Transmits a broadcast packet either immediately or if a delay is given
+ * after that. Furthermore, queues additional retransmissions on wireless
+ * interfaces.
+ *
+ * Consumes the provided skb.
+ */
+void batadv_send_bcast_packet(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ unsigned long delay,
+ bool own_packet)
+{
+ __batadv_forw_bcast_packet(bat_priv, skb, delay, own_packet);
+ consume_skb(skb);
+}
+
/**
* batadv_forw_packet_bcasts_left() - check if a retransmission is necessary
* @forw_packet: the forwarding packet to check
- * @hard_iface: the interface to check on
*
* Checks whether a given packet has any (re)transmissions left on the provided
* interface.
@@ -811,28 +1030,20 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
* Return: True if (re)transmissions are left, false otherwise.
*/
static bool
-batadv_forw_packet_bcasts_left(struct batadv_forw_packet *forw_packet,
- struct batadv_hard_iface *hard_iface)
+batadv_forw_packet_bcasts_left(struct batadv_forw_packet *forw_packet)
{
- unsigned int max;
-
- if (hard_iface)
- max = hard_iface->num_bcasts;
- else
- max = BATADV_NUM_BCASTS_MAX;
-
- return BATADV_SKB_CB(forw_packet->skb)->num_bcasts < max;
+ return BATADV_SKB_CB(forw_packet->skb)->num_bcasts;
}
/**
- * batadv_forw_packet_bcasts_inc() - increment retransmission counter of a
+ * batadv_forw_packet_bcasts_dec() - decrement retransmission counter of a
* packet
- * @forw_packet: the packet to increase the counter for
+ * @forw_packet: the packet to decrease the counter for
*/
static void
-batadv_forw_packet_bcasts_inc(struct batadv_forw_packet *forw_packet)
+batadv_forw_packet_bcasts_dec(struct batadv_forw_packet *forw_packet)
{
- BATADV_SKB_CB(forw_packet->skb)->num_bcasts++;
+ BATADV_SKB_CB(forw_packet->skb)->num_bcasts--;
}
/**
@@ -843,30 +1054,30 @@ batadv_forw_packet_bcasts_inc(struct batadv_forw_packet *forw_packet)
*/
bool batadv_forw_packet_is_rebroadcast(struct batadv_forw_packet *forw_packet)
{
- return BATADV_SKB_CB(forw_packet->skb)->num_bcasts > 0;
+ unsigned char num_bcasts = BATADV_SKB_CB(forw_packet->skb)->num_bcasts;
+
+ return num_bcasts != forw_packet->if_outgoing->num_bcasts;
}
+/**
+ * batadv_send_outstanding_bcast_packet() - transmit a queued broadcast packet
+ * @work: work queue item
+ *
+ * Transmits a queued broadcast packet and if necessary reschedules it.
+ */
static void batadv_send_outstanding_bcast_packet(struct work_struct *work)
{
- struct batadv_hard_iface *hard_iface;
- struct batadv_hardif_neigh_node *neigh_node;
+ unsigned long send_time = jiffies + msecs_to_jiffies(5);
+ struct batadv_forw_packet *forw_packet;
struct delayed_work *delayed_work;
- struct batadv_forw_packet *forw_packet;
- struct batadv_bcast_packet *bcast_packet;
+ struct batadv_priv *bat_priv;
struct sk_buff *skb1;
- struct net_device *soft_iface;
- struct batadv_priv *bat_priv;
- unsigned long send_time = jiffies + msecs_to_jiffies(5);
bool dropped = false;
- u8 *neigh_addr;
- u8 *orig_neigh;
- int ret = 0;
delayed_work = to_delayed_work(work);
forw_packet = container_of(delayed_work, struct batadv_forw_packet,
delayed_work);
- soft_iface = forw_packet->if_incoming->soft_iface;
- bat_priv = netdev_priv(soft_iface);
+ bat_priv = netdev_priv(forw_packet->if_incoming->soft_iface);
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) {
dropped = true;
@@ -878,76 +1089,15 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work)
goto out;
}
- bcast_packet = (struct batadv_bcast_packet *)forw_packet->skb->data;
+ /* send a copy of the saved skb */
+ skb1 = skb_clone(forw_packet->skb, GFP_ATOMIC);
+ if (!skb1)
+ goto out;
- /* rebroadcast packet */
- rcu_read_lock();
- list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
- if (hard_iface->soft_iface != soft_iface)
- continue;
+ batadv_send_broadcast_skb(skb1, forw_packet->if_outgoing);
+ batadv_forw_packet_bcasts_dec(forw_packet);
- if (!batadv_forw_packet_bcasts_left(forw_packet, hard_iface))
- continue;
-
- if (forw_packet->own) {
- neigh_node = NULL;
- } else {
- neigh_addr = eth_hdr(forw_packet->skb)->h_source;
- neigh_node = batadv_hardif_neigh_get(hard_iface,
- neigh_addr);
- }
-
- orig_neigh = neigh_node ? neigh_node->orig : NULL;
-
- ret = batadv_hardif_no_broadcast(hard_iface, bcast_packet->orig,
- orig_neigh);
-
- if (ret) {
- char *type;
-
- switch (ret) {
- case BATADV_HARDIF_BCAST_NORECIPIENT:
- type = "no neighbor";
- break;
- case BATADV_HARDIF_BCAST_DUPFWD:
- type = "single neighbor is source";
- break;
- case BATADV_HARDIF_BCAST_DUPORIG:
- type = "single neighbor is originator";
- break;
- default:
- type = "unknown";
- }
-
- batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "BCAST packet from orig %pM on %s suppressed: %s\n",
- bcast_packet->orig,
- hard_iface->net_dev->name, type);
-
- if (neigh_node)
- batadv_hardif_neigh_put(neigh_node);
-
- continue;
- }
-
- if (neigh_node)
- batadv_hardif_neigh_put(neigh_node);
-
- if (!kref_get_unless_zero(&hard_iface->refcount))
- continue;
-
- /* send a copy of the saved skb */
- skb1 = skb_clone(forw_packet->skb, GFP_ATOMIC);
- if (skb1)
- batadv_send_broadcast_skb(skb1, hard_iface);
-
- batadv_hardif_put(hard_iface);
- }
- rcu_read_unlock();
-
- batadv_forw_packet_bcasts_inc(forw_packet);
-
- /* if we still have some more bcasts to send */
- if (batadv_forw_packet_bcasts_left(forw_packet, NULL)) {
+ if (batadv_forw_packet_bcasts_left(forw_packet)) {
batadv_forw_packet_bcast_queue(bat_priv, forw_packet,
send_time);
return;
diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h
index 2b0daf8b..08af251b 100644
--- a/net/batman-adv/send.h
+++ b/net/batman-adv/send.h
@@ -39,10 +39,14 @@ int batadv_send_broadcast_skb(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface);
int batadv_send_unicast_skb(struct sk_buff *skb,
struct batadv_neigh_node *neigh_node);
-int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
- const struct sk_buff *skb,
- unsigned long delay,
- bool own_packet);
+int batadv_forw_bcast_packet(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ unsigned long delay,
+ bool own_packet);
+void batadv_send_bcast_packet(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ unsigned long delay,
+ bool own_packet);
void
batadv_purge_outstanding_packets(struct batadv_priv *bat_priv,
const struct batadv_hard_iface *hard_iface);
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index 6b8181bc..a21884c0 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -191,7 +191,7 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
struct vlan_ethhdr *vhdr;
unsigned int header_len = 0;
int data_len = skb->len, ret;
- unsigned long brd_delay = 1;
+ unsigned long brd_delay = 0;
bool do_bcast = false, client_added;
unsigned short vid;
u32 seqno;
@@ -330,7 +330,7 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
bcast_packet = (struct batadv_bcast_packet *)skb->data;
bcast_packet->version = BATADV_COMPAT_VERSION;
- bcast_packet->ttl = BATADV_TTL;
+ bcast_packet->ttl = BATADV_TTL - 1;
/* batman packet type: broadcast */
bcast_packet->packet_type = BATADV_BCAST;
@@ -346,13 +346,7 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
seqno = atomic_inc_return(&bat_priv->bcast_seqno);
bcast_packet->seqno = htonl(seqno);
- batadv_add_bcast_packet_to_list(bat_priv, skb, brd_delay, true);
-
- /* a copy is stored in the bcast list, therefore removing
- * the original skb.
- */
- consume_skb(skb);
-
+ batadv_send_bcast_packet(bat_priv, skb, brd_delay, true);
/* unicast packet */
} else {
/* DHCP packets going to a server will use the GW feature */
--
2.31.0
1 year, 8 months
How to mesh over ethernet VLAN?
by Andi Depressivum
Hello everybody,
What's the correct setup to mesh also over an existing ethernet VLAN link?
My core network with all routers & servers is running on VLAN 3
(eth0.3 in the routers). How do I have to setup batman-adv to a) mesh
also over this existing link and b) propagate this VLAN also over
wireless links. Bridging eth0.3 with bat0.3 and adding eth0.3 also as
a hardif doesn't work, as it will remove eth0.3 from the bridge.
If I add eth0 instead as hardif the routers are only seeing each other
over wireless link. Or do I have to use a separate VLAN for ethernet
meshing which isn't bridged to a bat0.X interface?
Thanks a lot!
1 year, 9 months
[PATCH] batctl: Add JSON debug support
by Alexander Sarmanow
A JSON output of the debug tables is still missing. Corresponding JSON
output is added for originators, neighbors, translocal, transglobal and
interfaces tables. Same parameters of the debug tables can be used for
the JSON, except the "-w [interval]" (not useful). The table header is
implemented as a JSON equivalent and can be also optionally omitted.
Signed-off-by: Alexander Sarmanow <asarmanow(a)gmail.com>
---
Makefile | 5 ++
backbonetable.c | 2 +-
claimtable.c | 2 +-
dat_cache.c | 2 +-
debug.c | 15 +++-
gateways.c | 4 +-
interfaces_json.c | 167 +++++++++++++++++++++++++++++++++++++++++++++
main.c | 14 ++--
main.h | 3 +
mcast_flags.c | 4 +-
neighbors.c | 2 +-
neighbors_json.c | 109 +++++++++++++++++++++++++++++
netlink.c | 70 +++++++++++++++----
netlink.h | 9 ++-
originators.c | 4 +-
originators_json.c | 154 +++++++++++++++++++++++++++++++++++++++++
transglobal.c | 2 +-
transglobal_json.c | 146 +++++++++++++++++++++++++++++++++++++++
translocal.c | 2 +-
translocal_json.c | 139 +++++++++++++++++++++++++++++++++++++
20 files changed, 823 insertions(+), 32 deletions(-)
create mode 100644 interfaces_json.c
create mode 100644 neighbors_json.c
create mode 100644 originators_json.c
create mode 100644 transglobal_json.c
create mode 100644 translocal_json.c
diff --git a/Makefile b/Makefile
index 98bf695..84c5de6 100755
--- a/Makefile
+++ b/Makefile
@@ -71,6 +71,11 @@ $(eval $(call add_command,traceroute,y))
$(eval $(call add_command,transglobal,y))
$(eval $(call add_command,translate,y))
$(eval $(call add_command,translocal,y))
+$(eval $(call add_command,originators_json,y))
+$(eval $(call add_command,neighbors_json,y))
+$(eval $(call add_command,translocal_json,y))
+$(eval $(call add_command,transglobal_json,y))
+$(eval $(call add_command,interfaces_json,y))
MANPAGE = man/batctl.8
diff --git a/backbonetable.c b/backbonetable.c
index 17fbd1d..714ae55 100644
--- a/backbonetable.c
+++ b/backbonetable.c
@@ -98,7 +98,7 @@ static int netlink_print_bla_backbone(struct state *state, char *orig_iface,
orig_timeout, watch_interval,
"Originator VID last seen (CRC )\n",
BATADV_CMD_GET_BLA_BACKBONE,
- bla_backbone_callback);
+ bla_backbone_callback, false);
}
static struct debug_table_data batctl_debug_table_backbonetable = {
diff --git a/claimtable.c b/claimtable.c
index b6bf9f5..a1c3868 100644
--- a/claimtable.c
+++ b/claimtable.c
@@ -103,7 +103,7 @@ static int netlink_print_bla_claim(struct state *state, char *orig_iface,
orig_timeout, watch_interval,
"Client VID Originator [o] (CRC )\n",
BATADV_CMD_GET_BLA_CLAIM,
- bla_claim_callback);
+ bla_claim_callback, false);
}
static struct debug_table_data batctl_debug_table_claimtable = {
diff --git a/dat_cache.c b/dat_cache.c
index 8d47171..15a212b 100644
--- a/dat_cache.c
+++ b/dat_cache.c
@@ -116,7 +116,7 @@ static int netlink_print_dat_cache(struct state *state, char *orig_iface,
ret = netlink_print_common(state, orig_iface, read_opts,
orig_timeout, watch_interval, header,
BATADV_CMD_GET_DAT_CACHE,
- dat_cache_callback);
+ dat_cache_callback, false);
free(header);
return ret;
diff --git a/debug.c b/debug.c
index 458c137..5d5024e 100644
--- a/debug.c
+++ b/debug.c
@@ -27,7 +27,9 @@ static void debug_table_usage(struct state *state)
fprintf(stderr, " \t -h print this help\n");
fprintf(stderr, " \t -n don't replace mac addresses with bat-host names\n");
fprintf(stderr, " \t -H don't show the header\n");
- fprintf(stderr, " \t -w [interval] watch mode - refresh the table continuously\n");
+
+ if (state->cmd->type != DEBUGJSON)
+ fprintf(stderr, " \t -w [interval] watch mode - refresh the table continuously\n");
if (debug_table->option_timeout_interval)
fprintf(stderr, " \t -t timeout interval - don't print originators not seen for x.y seconds \n");
@@ -60,6 +62,12 @@ int handle_debug_table(struct state *state, int argc, char **argv)
read_opt &= ~USE_BAT_HOSTS;
break;
case 'w':
+ if (state->cmd->type == DEBUGJSON) {
+ fprintf(stderr, "Error - unrecognised option '-%c'\n", optchar);
+ debug_table_usage(state);
+ return EXIT_FAILURE;
+ }
+
read_opt |= CLR_CONT_READ;
if (optarg[0] == '-') {
optind--;
@@ -123,6 +131,11 @@ int handle_debug_table(struct state *state, int argc, char **argv)
} else if (optopt == 'i') {
fprintf(stderr, "Error - option '-i' needs an interface as argument\n");
} else if (optopt == 'w') {
+ if (state->cmd->type == DEBUGJSON) {
+ fprintf(stderr, "Error - unrecognised option '-w'\n");
+ debug_table_usage(state);
+ return EXIT_FAILURE;
+ }
read_opt |= CLR_CONT_READ;
break;
}
diff --git a/gateways.c b/gateways.c
index 7625bd8..4bfa1ed 100644
--- a/gateways.c
+++ b/gateways.c
@@ -123,7 +123,7 @@ static int netlink_print_gateways(struct state *state, char *orig_iface,
/* only parse routing algorithm name */
last_err = -EINVAL;
info_header = netlink_get_info(state->mesh_ifindex,
- BATADV_CMD_GET_ORIGINATORS, NULL);
+ BATADV_CMD_GET_ORIGINATORS, NULL, false);
free(info_header);
if (strlen(algo_name_buf) == 0)
@@ -141,7 +141,7 @@ static int netlink_print_gateways(struct state *state, char *orig_iface,
orig_timeout, watch_interval,
header,
BATADV_CMD_GET_GATEWAYS,
- gateways_callback);
+ gateways_callback, false);
}
static struct debug_table_data batctl_debug_table_gateways = {
diff --git a/interfaces_json.c b/interfaces_json.c
new file mode 100644
index 0000000..65cde95
--- /dev/null
+++ b/interfaces_json.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner <mareklindner(a)neomailbox.ch>
+ * Alexander Sarmanow <asarmanow(a)gmail.com>
+ *
+ * License-Filename: LICENSES/preferred/GPL-2.0
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <net/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+#include <netlink/netlink.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+
+#include "main.h"
+#include "sys.h"
+#include "functions.h"
+#include "debug.h"
+
+#define IFACE_STATUS_LEN 256
+
+static int get_iface_status_netlink_parse(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *attrs[NUM_BATADV_ATTR];
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ char *iface_status = arg;
+ struct genlmsghdr *ghdr;
+
+ if (!genlmsg_valid_hdr(nlh, 0))
+ return NL_OK;
+
+ ghdr = nlmsg_data(nlh);
+ if (ghdr->cmd != BATADV_CMD_GET_HARDIF)
+ return NL_OK;
+
+ if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
+ genlmsg_len(ghdr), batadv_netlink_policy))
+ return NL_OK;
+
+ if (attrs[BATADV_ATTR_ACTIVE])
+ strncpy(iface_status, "active", IFACE_STATUS_LEN);
+ else
+ strncpy(iface_status, "inactive", IFACE_STATUS_LEN);
+
+ iface_status[IFACE_STATUS_LEN - 1] = '\0';
+
+ return NL_STOP;
+}
+
+static char *get_iface_status_netlink(unsigned int meshif, unsigned int hardif,
+ char *iface_status)
+{
+ char *ret_status = NULL;
+ struct nl_sock *sock;
+ struct nl_msg *msg;
+ int batadv_family;
+ struct nl_cb *cb;
+ int ret;
+
+ iface_status[0] = '\0';
+
+ sock = nl_socket_alloc();
+ if (!sock)
+ return NULL;
+
+ ret = genl_connect(sock);
+ if (ret < 0)
+ goto err_free_sock;
+
+ batadv_family = genl_ctrl_resolve(sock, BATADV_NL_NAME);
+ if (batadv_family < 0)
+ goto err_free_sock;
+
+ cb = nl_cb_alloc(NL_CB_DEFAULT);
+ if (!cb)
+ goto err_free_sock;
+
+ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_iface_status_netlink_parse,
+ iface_status);
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ goto err_free_cb;
+
+ genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, batadv_family,
+ 0, 0, BATADV_CMD_GET_HARDIF, 1);
+
+ nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, meshif);
+ nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX, hardif);
+
+ ret = nl_send_auto_complete(sock, msg);
+ if (ret < 0)
+ goto err_free_msg;
+
+ nl_recvmsgs(sock, cb);
+
+ if (strlen(iface_status) > 0)
+ ret_status = iface_status;
+
+err_free_msg:
+ nlmsg_free(msg);
+err_free_cb:
+ nl_cb_put(cb);
+err_free_sock:
+ nl_socket_free(sock);
+
+ return ret_status;
+}
+
+static int interfaces_callback(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *attrs[BATADV_ATTR_MAX+1];
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ struct print_opts *opts = arg;
+ struct genlmsghdr *ghdr;
+ char iface_status[IFACE_STATUS_LEN];
+ const char *status;
+ char *ifname;
+ int ifindex;
+ int master;
+
+ ghdr = nlmsg_data(nlh);
+
+ if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
+ genlmsg_len(ghdr), batadv_netlink_policy)) {
+ fputs("Received invalid data from kernel.\n", stderr);
+ exit(1);
+ }
+
+ ifname = nla_data(attrs[BATADV_ATTR_HARD_IFNAME]);
+ master = nla_get_u32(attrs[BATADV_ATTR_MESH_IFINDEX]);
+ ifindex = nla_get_u32(attrs[BATADV_ATTR_HARD_IFINDEX]);
+
+ status = get_iface_status_netlink(master, ifindex, iface_status);
+ if (!status)
+ status = "<error reading status>\n";
+
+ printf("{\"%s\":\"%s\"}", ifname, status);
+
+ return NL_OK;
+}
+
+static int netlink_print_interfaces_json(struct state *state, char *orig_iface,
+ int read_opts, float orig_timeout,
+ float watch_interval)
+{
+ return netlink_print_common(state, orig_iface, read_opts, orig_timeout,
+ watch_interval, NULL, BATADV_CMD_GET_HARDIF,
+ interfaces_callback, true);
+}
+
+static struct debug_table_data batctl_debug_table_interfaces = {
+ .netlink_fn = netlink_print_interfaces_json,
+};
+
+COMMAND_NAMED(DEBUGJSON, interfaces_json, "ifj", handle_debug_table,
+ COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
+ &batctl_debug_table_interfaces, "");
diff --git a/main.c b/main.c
index d9b63f3..1371bc0 100644
--- a/main.c
+++ b/main.c
@@ -43,6 +43,10 @@ static void print_usage(void)
.label = "debug tables: \tdisplay the corresponding debug table\n",
.types = BIT(DEBUGTABLE),
},
+ {
+ .label = "debug JSONs: \tdisplay the corresponding debug JSON\n",
+ .types = BIT(DEBUGJSON),
+ },
};
const char *default_prefixes[] = {
"",
@@ -67,9 +71,9 @@ static void print_usage(void)
char buf[64];
size_t i;
- fprintf(stderr, "Usage: batctl [options] command|debug table [parameters]\n");
+ fprintf(stderr, "Usage: batctl [options] command|debug table|debug json [parameters]\n");
fprintf(stderr, "options:\n");
- fprintf(stderr, " \t-h print this help (or 'batctl <command|debug table> -h' for the parameter help)\n");
+ fprintf(stderr, " \t-h print this help (or 'batctl <command|debug table|debug json> -h' for the parameter help)\n");
fprintf(stderr, " \t-v print version\n");
for (i = 0; i < sizeof(type) / sizeof(*type); i++) {
@@ -87,6 +91,7 @@ static void print_usage(void)
continue;
switch (cmd->type) {
+ case DEBUGJSON:
case DEBUGTABLE:
case SUBCOMMAND_MIF:
prefixes = meshif_prefixes;
@@ -167,7 +172,8 @@ static const struct command *find_command(struct state *state, const char *name)
/* fall through */
case SP_MESHIF:
types |= BIT(SUBCOMMAND_MIF) |
- BIT(DEBUGTABLE);
+ BIT(DEBUGTABLE) |
+ BIT(DEBUGJSON);
break;
case SP_VLAN:
types = BIT(SUBCOMMAND_VID);
@@ -380,7 +386,7 @@ int main(int argc, char **argv)
cmd = find_command(&state, argv[0]);
if (!cmd) {
fprintf(stderr,
- "Error - no valid command or debug table specified: %s\n",
+ "Error - no valid command or debug table/JSON specified: %s\n",
argv[0]);
goto err;
}
diff --git a/main.h b/main.h
index 81b7a27..2efd136 100644
--- a/main.h
+++ b/main.h
@@ -44,6 +44,8 @@ extern char module_ver_path[];
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
+#define BOOL_STRING(x) (x ? "true" : "false")
+
#ifndef container_of
#define container_of(ptr, type, member) __extension__ ({ \
const __typeof__(((type *)0)->member) *__pmember = (ptr); \
@@ -69,6 +71,7 @@ enum command_type {
SUBCOMMAND_VID,
SUBCOMMAND_HIF,
DEBUGTABLE,
+ DEBUGJSON,
};
struct state {
diff --git a/mcast_flags.c b/mcast_flags.c
index 721f549..6fc2780 100644
--- a/mcast_flags.c
+++ b/mcast_flags.c
@@ -105,7 +105,7 @@ static int netlink_print_mcast_flags(struct state *state, char *orig_iface,
/* only parse own multicast flags */
info_header = netlink_get_info(state->mesh_ifindex,
- BATADV_CMD_GET_MCAST_FLAGS, NULL);
+ BATADV_CMD_GET_MCAST_FLAGS, NULL, false);
free(info_header);
if (mcast_flags == -EOPNOTSUPP || mcast_flags_priv == -EOPNOTSUPP)
@@ -147,7 +147,7 @@ static int netlink_print_mcast_flags(struct state *state, char *orig_iface,
ret = netlink_print_common(state, orig_iface, read_opts,
orig_timeout, watch_interval, header,
BATADV_CMD_GET_MCAST_FLAGS,
- mcast_flags_callback);
+ mcast_flags_callback, false);
free(header);
return ret;
diff --git a/neighbors.c b/neighbors.c
index af76d0f..a54045b 100644
--- a/neighbors.c
+++ b/neighbors.c
@@ -114,7 +114,7 @@ static int netlink_print_neighbors(struct state *state, char *orig_iface,
orig_timeout, watch_interval,
"IF Neighbor last-seen\n",
BATADV_CMD_GET_NEIGHBORS,
- neighbors_callback);
+ neighbors_callback, false);
}
static struct debug_table_data batctl_debug_table_neighbors = {
diff --git a/neighbors_json.c b/neighbors_json.c
new file mode 100644
index 0000000..d89dc7a
--- /dev/null
+++ b/neighbors_json.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) B.A.T.M.A.N. contributors:
+ *
+ * Andrew Lunn <andrew(a)lunn.ch>
+ * Alexander Sarmanow <asarmanow(a)gmail.com>
+ *
+ * License-Filename: LICENSES/preferred/GPL-2.0
+ */
+
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "batadv_packet.h"
+#include "batman_adv.h"
+#include "bat-hosts.h"
+#include "debug.h"
+#include "functions.h"
+#include "main.h"
+#include "netlink.h"
+
+static const int neighbors_mandatory[] = {
+ BATADV_ATTR_NEIGH_ADDRESS,
+ BATADV_ATTR_HARD_IFINDEX,
+ BATADV_ATTR_LAST_SEEN_MSECS,
+};
+
+static int neighbors_callback(struct nl_msg *msg, void *arg)
+{
+ unsigned throughput_kbits;
+ struct nlattr *attrs[BATADV_ATTR_MAX+1];
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ int last_seen_msecs;
+ struct print_opts *opts = arg;
+ struct bat_host *bat_host;
+ char ifname[IF_NAMESIZE];
+ struct genlmsghdr *ghdr;
+ uint8_t *neigh;
+
+ if (!genlmsg_valid_hdr(nlh, 0)) {
+ fputs("Received invalid data from kernel.\n", stderr);
+ exit(1);
+ }
+
+ ghdr = nlmsg_data(nlh);
+
+ if (ghdr->cmd != BATADV_CMD_GET_NEIGHBORS)
+ return NL_OK;
+
+ if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
+ genlmsg_len(ghdr), batadv_netlink_policy)) {
+ fputs("Received invalid data from kernel.\n", stderr);
+ exit(1);
+ }
+
+ if (missing_mandatory_attrs(attrs, neighbors_mandatory,
+ ARRAY_SIZE(neighbors_mandatory))) {
+ fputs("Missing attributes from kernel\n", stderr);
+ exit(1);
+ }
+
+ neigh = nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]);
+ bat_host = bat_hosts_find_by_mac((char *)neigh);
+
+ if (!if_indextoname(nla_get_u32(attrs[BATADV_ATTR_HARD_IFINDEX]),
+ ifname))
+ ifname[0] = '\0';
+
+ last_seen_msecs = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]);
+
+ if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host)
+ printf("{\"neighbor\":\"%02x:%02x:%02x:%02x:%02x:%02x\",",
+ neigh[0], neigh[1], neigh[2],
+ neigh[3], neigh[4], neigh[5]);
+ else
+ printf("{\"neighbor\":\"%s\",", bat_host->name);
+
+ if (attrs[BATADV_ATTR_THROUGHPUT]) {
+ throughput_kbits = nla_get_u32(attrs[BATADV_ATTR_THROUGHPUT]);
+ printf("\"throughput_kbits\":%u,", throughput_kbits);
+ }
+
+ printf("\"last_seen\":%d,\"ifname\":\"%s\"}", last_seen_msecs, ifname);
+
+ return NL_OK;
+}
+
+static int netlink_print_neighbors_json(struct state *state, char *orig_iface,
+ int read_opts, float orig_timeout,
+ float watch_interval)
+{
+ return netlink_print_common(state, orig_iface, read_opts,
+ orig_timeout, watch_interval, NULL,
+ BATADV_CMD_GET_NEIGHBORS,
+ neighbors_callback, true);
+}
+
+static struct debug_table_data batctl_debug_table_neighbors = {
+ .netlink_fn = netlink_print_neighbors_json,
+};
+
+COMMAND_NAMED(DEBUGJSON, neighbors_json, "nj", handle_debug_table,
+ COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
+ &batctl_debug_table_neighbors, "");
diff --git a/netlink.c b/netlink.c
index 31c9b01..f42643e 100644
--- a/netlink.c
+++ b/netlink.c
@@ -318,15 +318,29 @@ static int info_callback(struct nl_msg *msg, void *arg)
else
extra_header = "";
- ret = asprintf(&opts->remaining_header,
- "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%02x:%02x:%02x:%02x:%02x:%02x (%s/%02x:%02x:%02x:%02x:%02x:%02x %s)%s]\n%s",
- version, primary_if,
- primary_mac[0], primary_mac[1], primary_mac[2],
- primary_mac[3], primary_mac[4], primary_mac[5],
- mesh_name,
- mesh_mac[0], mesh_mac[1], mesh_mac[2],
- mesh_mac[3], mesh_mac[4], mesh_mac[5],
- algo_name, extra_info, extra_header);
+ if (opts->is_json) {
+ ret = asprintf(&opts->remaining_header,
+ "{\"version\":\"%s\",\"main_if\":\"%s\",\"main_mac\":\"%02x:%02x:%02x:%02x:%02x:%02x\",\"mesh_if\":\"%s\",\"mesh_mac\":\"%02x:%02x:%02x:%02x:%02x:%02x\",\"algo_name\":\"%s\",\"extra_info\":\"%s\",\"data\":[",
+ version, primary_if,
+ primary_mac[0], primary_mac[1],
+ primary_mac[2], primary_mac[3],
+ primary_mac[4], primary_mac[5],
+ mesh_name,
+ mesh_mac[0], mesh_mac[1], mesh_mac[2],
+ mesh_mac[3], mesh_mac[4], mesh_mac[5],
+ algo_name, extra_info);
+ } else {
+ ret = asprintf(&opts->remaining_header,
+ "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%02x:%02x:%02x:%02x:%02x:%02x (%s/%02x:%02x:%02x:%02x:%02x:%02x %s)%s]\n%s",
+ version, primary_if,
+ primary_mac[0], primary_mac[1],
+ primary_mac[2], primary_mac[3],
+ primary_mac[4], primary_mac[5],
+ mesh_name,
+ mesh_mac[0], mesh_mac[1], mesh_mac[2],
+ mesh_mac[3], mesh_mac[4], mesh_mac[5],
+ algo_name, extra_info, extra_header);
+ }
if (ret < 0)
opts->remaining_header = NULL;
@@ -342,7 +356,8 @@ static int info_callback(struct nl_msg *msg, void *arg)
return NL_STOP;
}
-char *netlink_get_info(int ifindex, uint8_t nl_cmd, const char *header)
+char *netlink_get_info(int ifindex, uint8_t nl_cmd, const char *header,
+ bool is_json)
{
struct nl_sock *sock;
struct nl_msg *msg;
@@ -353,6 +368,7 @@ char *netlink_get_info(int ifindex, uint8_t nl_cmd, const char *header)
.nl_cmd = nl_cmd,
.remaining_header = NULL,
.static_header = header,
+ .is_json = is_json,
};
sock = nl_socket_alloc();
@@ -399,12 +415,34 @@ char *netlink_get_info(int ifindex, uint8_t nl_cmd, const char *header)
void netlink_print_remaining_header(struct print_opts *opts)
{
- if (!opts->remaining_header)
+ if (!opts->remaining_header) {
+ if (!opts->is_json)
+ return;
+
+ if (opts->is_first)
+ fputs("[", stdout);
+ if (opts->is_last) {
+ if (!(opts->read_opt & SKIP_HEADER))
+ fputs("]}\n", stdout);
+ else
+ fputs("]\n", stdout);
+ }
+ if (!opts->is_first && !opts->is_last)
+ fputs(",", stdout);
+
+ opts->is_first = false;
+
return;
+ }
fputs(opts->remaining_header, stdout);
free(opts->remaining_header);
opts->remaining_header = NULL;
+
+ opts->is_first = false;
+
+ if (opts->is_last && opts->is_json)
+ fputs("]}\n", stdout);
}
int netlink_print_common_cb(struct nl_msg *msg, void *arg)
@@ -419,7 +457,7 @@ int netlink_print_common_cb(struct nl_msg *msg, void *arg)
int netlink_print_common(struct state *state, char *orig_iface, int read_opt,
float orig_timeout, float watch_interval,
const char *header, uint8_t nl_cmd,
- nl_recvmsg_msg_cb_t callback)
+ nl_recvmsg_msg_cb_t callback, bool is_json)
{
struct print_opts opts = {
.read_opt = read_opt,
@@ -427,6 +465,9 @@ int netlink_print_common(struct state *state, char *orig_iface, int read_opt,
.watch_interval = watch_interval,
.remaining_header = NULL,
.callback = callback,
+ .is_json = is_json,
+ .is_first = true,
+ .is_last = false,
};
int hardifindex = 0;
struct nl_msg *msg;
@@ -460,7 +501,8 @@ int netlink_print_common(struct state *state, char *orig_iface, int read_opt,
if (!(read_opt & SKIP_HEADER))
opts.remaining_header = netlink_get_info(state->mesh_ifindex,
nl_cmd,
- header);
+ header,
+ is_json);
msg = nlmsg_alloc();
if (!msg)
@@ -481,6 +523,8 @@ int netlink_print_common(struct state *state, char *orig_iface, int read_opt,
last_err = 0;
nl_recvmsgs(state->sock, state->cb);
+ opts.is_last = true;
+
/* the header should still be printed when no entry was received */
if (!last_err)
netlink_print_remaining_header(&opts);
diff --git a/netlink.h b/netlink.h
index 4ee2f39..b7adade 100644
--- a/netlink.h
+++ b/netlink.h
@@ -12,6 +12,7 @@
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <stdint.h>
+#include <stdbool.h>
struct state;
@@ -21,8 +22,12 @@ struct print_opts {
float watch_interval;
nl_recvmsg_msg_cb_t callback;
char *remaining_header;
+ char *remaining_entry;
const char *static_header;
uint8_t nl_cmd;
+ bool is_json;
+ bool is_first;
+ bool is_last;
};
struct ether_addr;
@@ -30,7 +35,7 @@ struct ether_addr;
int netlink_create(struct state *state);
void netlink_destroy(struct state *state);
-char *netlink_get_info(int ifindex, uint8_t nl_cmd, const char *header);
+char *netlink_get_info(int ifindex, uint8_t nl_cmd, const char *header, bool is_json);
int translate_mac_netlink(const char *mesh_iface, const struct ether_addr *mac,
struct ether_addr *mac_out);
int get_nexthop_netlink(const char *mesh_iface, const struct ether_addr *mac,
@@ -46,7 +51,7 @@ int missing_mandatory_attrs(struct nlattr *attrs[], const int mandatory[],
int netlink_print_common(struct state *state, char *orig_iface, int read_opt,
float orig_timeout, float watch_interval,
const char *header, uint8_t nl_cmd,
- nl_recvmsg_msg_cb_t callback);
+ nl_recvmsg_msg_cb_t callback, bool is_json);
int netlink_print_common_cb(struct nl_msg *msg, void *arg);
int netlink_stop_callback(struct nl_msg *msg, void *arg);
diff --git a/originators.c b/originators.c
index 8a29dd7..4acf2d0 100644
--- a/originators.c
+++ b/originators.c
@@ -175,7 +175,7 @@ static int netlink_print_originators(struct state *state, char *orig_iface,
/* only parse routing algorithm name */
last_err = -EINVAL;
info_header = netlink_get_info(state->mesh_ifindex,
- BATADV_CMD_GET_ORIGINATORS, NULL);
+ BATADV_CMD_GET_ORIGINATORS, NULL, false);
free(info_header);
if (strlen(algo_name_buf) == 0)
@@ -192,7 +192,7 @@ static int netlink_print_originators(struct state *state, char *orig_iface,
return netlink_print_common(state, orig_iface, read_opts,
orig_timeout, watch_interval, header,
BATADV_CMD_GET_ORIGINATORS,
- originators_callback);
+ originators_callback, false);
}
static struct debug_table_data batctl_debug_table_originators = {
diff --git a/originators_json.c b/originators_json.c
new file mode 100644
index 0000000..d0caec3
--- /dev/null
+++ b/originators_json.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) B.A.T.M.A.N. contributors:
+ *
+ * Andrew Lunn <andrew(a)lunn.ch>
+ * Sven Eckelmann <sven(a)narfation.org>
+ * Alexander Sarmanow <asarmanow(a)gmail.com>
+ *
+ * License-Filename: LICENSES/preferred/GPL-2.0
+ */
+
+#include <errno.h>
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "batadv_packet.h"
+#include "batman_adv.h"
+#include "bat-hosts.h"
+#include "debug.h"
+#include "functions.h"
+#include "main.h"
+#include "netlink.h"
+
+static const int originators_mandatory[] = {
+ BATADV_ATTR_ORIG_ADDRESS,
+ BATADV_ATTR_NEIGH_ADDRESS,
+ BATADV_ATTR_HARD_IFINDEX,
+ BATADV_ATTR_LAST_SEEN_MSECS,
+};
+
+static int originators_callback(struct nl_msg *msg, void *arg)
+{
+ unsigned throughput_kbits;
+ struct nlattr *attrs[BATADV_ATTR_MAX+1];
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ int last_seen_msecs;
+ struct print_opts *opts = arg;
+ struct bat_host *bat_host;
+ struct genlmsghdr *ghdr;
+ char ifname[IF_NAMESIZE];
+ float last_seen;
+ uint8_t *neigh;
+ uint8_t *orig;
+ bool c = false;
+ uint8_t tq;
+
+ if (!genlmsg_valid_hdr(nlh, 0)) {
+ fputs("Received invalid data from kernel.\n", stderr);
+ exit(1);
+ }
+
+ ghdr = nlmsg_data(nlh);
+
+ if (ghdr->cmd != BATADV_CMD_GET_ORIGINATORS)
+ return NL_OK;
+
+ if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
+ genlmsg_len(ghdr), batadv_netlink_policy)) {
+ fputs("Received invalid data from kernel.\n", stderr);
+ exit(1);
+ }
+
+ if (missing_mandatory_attrs(attrs, originators_mandatory,
+ ARRAY_SIZE(originators_mandatory))) {
+ fputs("Missing attributes from kernel\n", stderr);
+ exit(1);
+ }
+
+ orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]);
+ neigh = nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]);
+
+ if (!if_indextoname(nla_get_u32(attrs[BATADV_ATTR_HARD_IFINDEX]),
+ ifname))
+ ifname[0] = '\0';
+
+ if (attrs[BATADV_ATTR_FLAG_BEST])
+ c = true;
+
+ last_seen_msecs = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]);
+ last_seen = (float)last_seen_msecs / 1000.0;
+
+ /* skip timed out originators */
+ if (opts->read_opt & NO_OLD_ORIGS)
+ if (last_seen > opts->orig_timeout)
+ return NL_OK;
+
+ printf("{\"best\":%s,", BOOL_STRING(c));
+
+ if (!(opts->read_opt & USE_BAT_HOSTS)) {
+ printf("\"originator\":\"%02x:%02x:%02x:%02x:%02x:%02x\",",
+ orig[0], orig[1], orig[2],
+ orig[3], orig[4], orig[5]);
+ printf("\"next_hop\":\"%02x:%02x:%02x:%02x:%02x:%02x\",",
+ neigh[0], neigh[1], neigh[2],
+ neigh[3], neigh[4], neigh[5]);
+ } else {
+ bat_host = bat_hosts_find_by_mac((char *)orig);
+ if (bat_host) {
+ printf("\"originator\":\"%s\",",
+ bat_host->name);
+ } else {
+ printf("\"originator\":\"%02x:%02x:%02x:%02x:%02x:%02x\",",
+ orig[0], orig[1], orig[2],
+ orig[3], orig[4], orig[5]);
+ }
+ bat_host = bat_hosts_find_by_mac((char *)neigh);
+ if (bat_host)
+ printf("\"next_hop\":\"%s\",",
+ bat_host->name);
+ else
+ printf("\"next_hop\":\"%02x:%02x:%02x:%02x:%02x:%02x\",",
+ neigh[0], neigh[1], neigh[2],
+ neigh[3], neigh[4], neigh[5]);
+ }
+ if (attrs[BATADV_ATTR_THROUGHPUT]) {
+ throughput_kbits = nla_get_u32(attrs[BATADV_ATTR_THROUGHPUT]);
+ printf("\"throughput_kbits\":%u,", throughput_kbits);
+ }
+ if (attrs[BATADV_ATTR_TQ]) {
+ tq = nla_get_u8(attrs[BATADV_ATTR_TQ]);
+ printf("\"tq\":%i,", tq);
+ }
+
+ printf("\"last_seen\":%d,\"outgoing_iface\":\"%s\"}",
+ last_seen_msecs, ifname);
+
+ return NL_OK;
+}
+
+static int netlink_print_originators_json(struct state *state, char *orig_iface,
+ int read_opts, float orig_timeout,
+ float watch_interval)
+{
+ return netlink_print_common(state, orig_iface, read_opts,
+ orig_timeout, watch_interval, NULL,
+ BATADV_CMD_GET_ORIGINATORS,
+ originators_callback, true);
+}
+
+static struct debug_table_data batctl_debug_table_originators = {
+ .netlink_fn = netlink_print_originators_json,
+ .option_timeout_interval = 1,
+ .option_orig_iface = 1,
+};
+
+COMMAND_NAMED(DEBUGJSON, originators_json, "oj", handle_debug_table,
+ COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
+ &batctl_debug_table_originators, "");
diff --git a/transglobal.c b/transglobal.c
index 4eae95d..abe0339 100644
--- a/transglobal.c
+++ b/transglobal.c
@@ -132,7 +132,7 @@ static int netlink_print_transglobal(struct state *state, char *orig_iface,
orig_timeout, watch_interval,
" Client VID Flags Last ttvn Via ttvn (CRC )\n",
BATADV_CMD_GET_TRANSTABLE_GLOBAL,
- transglobal_callback);
+ transglobal_callback, false);
}
static struct debug_table_data batctl_debug_table_transglobal = {
diff --git a/transglobal_json.c b/transglobal_json.c
new file mode 100644
index 0000000..a687f57
--- /dev/null
+++ b/transglobal_json.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) B.A.T.M.A.N. contributors:
+ *
+ * Andrew Lunn <andrew(a)lunn.ch>
+ * Sven Eckelmann <sven(a)narfation.org>
+ * Alexander Sarmanow <asarmanow(a)gmail.com>
+ *
+ * License-Filename: LICENSES/preferred/GPL-2.0
+ */
+
+#include <netinet/if_ether.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "batadv_packet.h"
+#include "batman_adv.h"
+#include "bat-hosts.h"
+#include "debug.h"
+#include "functions.h"
+#include "main.h"
+#include "netlink.h"
+
+static const int transglobal_mandatory[] = {
+ BATADV_ATTR_TT_ADDRESS,
+ BATADV_ATTR_ORIG_ADDRESS,
+ BATADV_ATTR_TT_VID,
+ BATADV_ATTR_TT_TTVN,
+ BATADV_ATTR_TT_LAST_TTVN,
+ BATADV_ATTR_TT_CRC32,
+ BATADV_ATTR_TT_FLAGS,
+};
+
+static int transglobal_callback(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *attrs[BATADV_ATTR_MAX+1];
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ struct print_opts *opts = arg;
+ struct bat_host *bat_host;
+ struct genlmsghdr *ghdr;
+ char c, r, w, i, t;
+ uint8_t last_ttvn;
+ uint32_t crc32;
+ uint32_t flags;
+ uint8_t *addr;
+ uint8_t *orig;
+ uint8_t ttvn;
+ int16_t vid;
+
+ if (!genlmsg_valid_hdr(nlh, 0)) {
+ fputs("Received invalid data from kernel.\n", stderr);
+ exit(1);
+ }
+
+ ghdr = nlmsg_data(nlh);
+
+ if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_GLOBAL)
+ return NL_OK;
+
+ if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
+ genlmsg_len(ghdr), batadv_netlink_policy)) {
+ fputs("Received invalid data from kernel.\n", stderr);
+ exit(1);
+ }
+
+ if (missing_mandatory_attrs(attrs, transglobal_mandatory,
+ ARRAY_SIZE(transglobal_mandatory))) {
+ fputs("Missing attributes from kernel\n", stderr);
+ exit(1);
+ }
+
+ addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]);
+ orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]);
+ vid = nla_get_u16(attrs[BATADV_ATTR_TT_VID]);
+ ttvn = nla_get_u8(attrs[BATADV_ATTR_TT_TTVN]);
+ last_ttvn = nla_get_u8(attrs[BATADV_ATTR_TT_LAST_TTVN]);
+ crc32 = nla_get_u32(attrs[BATADV_ATTR_TT_CRC32]);
+ flags = nla_get_u32(attrs[BATADV_ATTR_TT_FLAGS]);
+
+ if (opts->read_opt & MULTICAST_ONLY && !(addr[0] & 0x01))
+ return NL_OK;
+
+ if (opts->read_opt & UNICAST_ONLY && (addr[0] & 0x01))
+ return NL_OK;
+
+ c = false, r = false, w = false, i = false, t = false;
+ if (attrs[BATADV_ATTR_FLAG_BEST])
+ c = true;
+ if (flags & BATADV_TT_CLIENT_ROAM)
+ r = true;
+ if (flags & BATADV_TT_CLIENT_WIFI)
+ w = true;
+ if (flags & BATADV_TT_CLIENT_ISOLA)
+ i = true;
+ if (flags & BATADV_TT_CLIENT_TEMP)
+ t = true;
+
+ printf("{\"best\":%s,", BOOL_STRING(c));
+
+ bat_host = bat_hosts_find_by_mac((char *)addr);
+ if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host)
+ printf("\"client\":\"%02x:%02x:%02x:%02x:%02x:%02x\",",
+ addr[0], addr[1], addr[2],
+ addr[3], addr[4], addr[5]);
+ else
+ printf("\"client\":\"%s\",", bat_host->name);
+
+ printf("\"vid\":%i,\"flag_r\":%s,\"flag_w\":%s,\"flag_i\":%s,\"flag_t\":%s,\"ttvn\":%u,",
+ BATADV_PRINT_VID(vid), BOOL_STRING(r), BOOL_STRING(w),
+ BOOL_STRING(i), BOOL_STRING(t), ttvn);
+
+ bat_host = bat_hosts_find_by_mac((char *)orig);
+ if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host)
+ printf("\"orig\":\"%02x:%02x:%02x:%02x:%02x:%02x\",",
+ orig[0], orig[1], orig[2],
+ orig[3], orig[4], orig[5]);
+ else
+ printf("\"orig\":\"%s\",", bat_host->name);
+
+ printf("\"last_ttvn\":%u,\"crc\":\"0x%.8x\"}", last_ttvn, crc32);
+
+ return NL_OK;
+}
+
+static int netlink_print_transglobal_json(struct state *state, char *orig_iface,
+ int read_opts, float orig_timeout,
+ float watch_interval)
+{
+ return netlink_print_common(state, orig_iface, read_opts,
+ orig_timeout, watch_interval, NULL,
+ BATADV_CMD_GET_TRANSTABLE_GLOBAL,
+ transglobal_callback, true);
+}
+
+static struct debug_table_data batctl_debug_table_transglobal = {
+ .netlink_fn = netlink_print_transglobal_json,
+ .option_unicast_only = 1,
+ .option_multicast_only = 1,
+};
+
+COMMAND_NAMED(DEBUGJSON, transglobal_json, "tgj", handle_debug_table,
+ COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
+ &batctl_debug_table_transglobal, "");
diff --git a/translocal.c b/translocal.c
index a3ad3da..d72125c 100644
--- a/translocal.c
+++ b/translocal.c
@@ -128,7 +128,7 @@ static int netlink_print_translocal(struct state *state, char *orig_iface,
orig_timeout, watch_interval,
"Client VID Flags Last seen (CRC )\n",
BATADV_CMD_GET_TRANSTABLE_LOCAL,
- translocal_callback);
+ translocal_callback, false);
}
static struct debug_table_data batctl_debug_table_translocal = {
diff --git a/translocal_json.c b/translocal_json.c
new file mode 100644
index 0000000..ad2ff46
--- /dev/null
+++ b/translocal_json.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) B.A.T.M.A.N. contributors:
+ *
+ * Andrew Lunn <andrew(a)lunn.ch>
+ * Sven Eckelmann <sven(a)narfation.org>
+ * Alexander Sarmanow <asarmanow(a)gmail.com>
+ *
+ * License-Filename: LICENSES/preferred/GPL-2.0
+ */
+
+#include <netinet/if_ether.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "batadv_packet.h"
+#include "batman_adv.h"
+#include "bat-hosts.h"
+#include "debug.h"
+#include "functions.h"
+#include "main.h"
+#include "netlink.h"
+
+static const int translocal_mandatory[] = {
+ BATADV_ATTR_TT_ADDRESS,
+ BATADV_ATTR_TT_VID,
+ BATADV_ATTR_TT_CRC32,
+ BATADV_ATTR_TT_FLAGS,
+};
+
+static int translocal_callback(struct nl_msg *msg, void *arg)
+{
+ int last_seen_msecs = 0;
+ struct nlattr *attrs[BATADV_ATTR_MAX+1];
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ struct print_opts *opts = arg;
+ struct bat_host *bat_host;
+ struct genlmsghdr *ghdr;
+ char r, p, n, x, w, i;
+ uint8_t *addr;
+ int16_t vid;
+ uint32_t crc32;
+ uint32_t flags;
+
+ if (!genlmsg_valid_hdr(nlh, 0)) {
+ fputs("Received invalid data from kernel.\n", stderr);
+ exit(1);
+ }
+
+ ghdr = nlmsg_data(nlh);
+
+ if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_LOCAL)
+ return NL_OK;
+
+ if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
+ genlmsg_len(ghdr), batadv_netlink_policy)) {
+ fputs("Received invalid data from kernel.\n", stderr);
+ exit(1);
+ }
+
+ if (missing_mandatory_attrs(attrs, translocal_mandatory,
+ ARRAY_SIZE(translocal_mandatory))) {
+ fputs("Missing attributes from kernel\n", stderr);
+ exit(1);
+ }
+
+ addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]);
+ vid = nla_get_u16(attrs[BATADV_ATTR_TT_VID]);
+ crc32 = nla_get_u32(attrs[BATADV_ATTR_TT_CRC32]);
+ flags = nla_get_u32(attrs[BATADV_ATTR_TT_FLAGS]);
+ last_seen_msecs = 0;
+
+ if (opts->read_opt & MULTICAST_ONLY && !(addr[0] & 0x01))
+ return NL_OK;
+
+ if (opts->read_opt & UNICAST_ONLY && (addr[0] & 0x01))
+ return NL_OK;
+
+ r = false, p = false, n = false, x = false, w = false, i = false;
+ if (flags & BATADV_TT_CLIENT_ROAM)
+ r = true;
+ if (flags & BATADV_TT_CLIENT_NEW)
+ n = true;
+ if (flags & BATADV_TT_CLIENT_PENDING)
+ x = true;
+ if (flags & BATADV_TT_CLIENT_WIFI)
+ w = true;
+ if (flags & BATADV_TT_CLIENT_ISOLA)
+ i = true;
+
+ if (flags & BATADV_TT_CLIENT_NOPURGE) {
+ p = true;
+ } else {
+ if (!attrs[BATADV_ATTR_LAST_SEEN_MSECS]) {
+ fputs("Received invalid data from kernel.\n", stderr);
+ exit(1);
+ }
+
+ last_seen_msecs = nla_get_u32(
+ attrs[BATADV_ATTR_LAST_SEEN_MSECS]);
+ }
+
+ bat_host = bat_hosts_find_by_mac((char *)addr);
+ if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host)
+ printf("{\"client\":\"%02x:%02x:%02x:%02x:%02x:%02x\",",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ else
+ printf("{\"client\":\"%s\",", bat_host->name);
+
+ printf("\"vid\":%i,\"flag_r\":%s,\"flag_n\":%s,\"flag_x\":%s,\"flag_w\":%s,\"flag_i\":%s,\"flag_p\":%s,\"last_seen\":%d,\"crc\":\"0x%.8x\"}",
+ BATADV_PRINT_VID(vid), BOOL_STRING(r), BOOL_STRING(n),
+ BOOL_STRING(x), BOOL_STRING(w), BOOL_STRING(i), BOOL_STRING(p),
+ last_seen_msecs, crc32);
+
+ return NL_OK;
+}
+
+static int netlink_print_translocal_json(struct state *state, char *orig_iface,
+ int read_opts, float orig_timeout,
+ float watch_interval)
+{
+ return netlink_print_common(state, orig_iface, read_opts,
+ orig_timeout, watch_interval, NULL,
+ BATADV_CMD_GET_TRANSTABLE_LOCAL,
+ translocal_callback, true);
+}
+
+static struct debug_table_data batctl_debug_table_translocal = {
+ .netlink_fn = netlink_print_translocal_json,
+ .option_unicast_only = 1,
+ .option_multicast_only = 1,
+};
+
+COMMAND_NAMED(DEBUGJSON, translocal_json, "tlj", handle_debug_table,
+ COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
+ &batctl_debug_table_translocal, "");
--
2.25.1
1 year, 9 months
Callback Function
by BrainGeek
With ALFRED I've noticed a parameter you can pass into it labeled "-c" which is described to be a callback. I've tried things like "-c poweroff" just to make it obvious that the callback worked, but to no avail. How can I properly use this feature and how does it work?
I'm currently running ALFRED and batman-adv on my Raspberry Pi 4. All the software is up-to-date.
1 year, 9 months
[RFC PATCH] batman-adv: convert ifmcaddr6 to RCU
by Linus Lüssing
From: Taehee Yoo <ap420073(a)gmail.com>
The ifmcaddr6 has been protected by inet6_dev->lock(rwlock) so that
the critical section is atomic context. In order to switch this context,
changing locking is needed. The ifmcaddr6 actually already protected by
RTNL So if it's converted to use RCU, its control path context can be
switched to sleepable.
Suggested-by: Cong Wang <xiyou.wangcong(a)gmail.com>
Signed-off-by: Taehee Yoo <ap420073(a)gmail.com>
Signed-off-by: David S. Miller <davem(a)davemloft.net>
[linus.luessing(a)c0d3.blue: Add compat code]
Signed-off-by: Linus Lüssing <linus.luessing(a)c0d3.blue>
---
@Sven: maybe something like this?
Seems to compile fine for me, without warnings and seems to run fine and
use the compat code for me.
I tried to create and add it to compat-include/net/if_inet6.h, as that
would fit better for the "struct ifmcaddr6", but couldn't get that
to work, lots of errors. And when adding it to compat.h the compat code
does not seem to be used at all.
Also let me know if the kernel version is correct or if it should be
5.14 instead?
compat-include/net/addrconf.h | 85 +++++++++++++++++++++++++++++++++++
net/batman-adv/multicast.c | 6 +--
2 files changed, 88 insertions(+), 3 deletions(-)
diff --git a/compat-include/net/addrconf.h b/compat-include/net/addrconf.h
index 30124124..080e1dda 100644
--- a/compat-include/net/addrconf.h
+++ b/compat-include/net/addrconf.h
@@ -25,4 +25,89 @@ static inline int batadv_ipv6_mc_check_mld(struct sk_buff *skb)
#endif /* LINUX_VERSION_IS_LESS(5, 1, 0) */
+#if LINUX_VERSION_IS_LESS(5, 13, 0)
+
+static bool batadv_mcast_mla_is_duplicate(u8 *mcast_addr,
+ struct hlist_head *mcast_list);
+
+static inline int
+compat_batadv_mcast_mla_softif_get_ipv6(struct net_device *dev,
+ struct hlist_head *mcast_list,
+ struct batadv_mcast_mla_flags *flags,
+ u8 *mcast_addr,
+ struct batadv_hw_addr *new,
+ struct inet6_dev *in6_dev)
+
+{
+ struct ifmcaddr6 *pmc6;
+ int ret = 0;
+
+ if (flags->tvlv_flags & BATADV_MCAST_WANT_ALL_IPV6)
+ return 0;
+
+ rcu_read_lock();
+
+ in6_dev = __in6_dev_get(dev);
+ if (!in6_dev) {
+ rcu_read_unlock();
+ return 0;
+ }
+
+ read_lock_bh(&in6_dev->lock);
+ for (pmc6 = in6_dev->mc_list; pmc6; pmc6 = pmc6->next) {
+ if (IPV6_ADDR_MC_SCOPE(&pmc6->mca_addr) <
+ IPV6_ADDR_SCOPE_LINKLOCAL)
+ continue;
+
+ if (flags->tvlv_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES &&
+ ipv6_addr_is_ll_all_nodes(&pmc6->mca_addr))
+ continue;
+
+ if (!(flags->tvlv_flags & BATADV_MCAST_WANT_NO_RTR6) &&
+ IPV6_ADDR_MC_SCOPE(&pmc6->mca_addr) >
+ IPV6_ADDR_SCOPE_LINKLOCAL)
+ continue;
+
+ ipv6_eth_mc_map(&pmc6->mca_addr, mcast_addr);
+
+ if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list))
+ continue;
+
+ new = kmalloc(sizeof(*new), GFP_ATOMIC);
+ if (!new) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ ether_addr_copy(new->addr, mcast_addr);
+ hlist_add_head(&new->list, mcast_list);
+ ret++;
+ }
+ read_unlock_bh(&in6_dev->lock);
+ rcu_read_unlock();
+
+ return ret;
+}
+
+#define ifmcaddr6 \
+ net_device *orig_dev = dev; \
+ return compat_batadv_mcast_mla_softif_get_ipv6(orig_dev, \
+ mcast_list, \
+ flags, \
+ mcast_addr, \
+ new = NULL, \
+ in6_dev = NULL); \
+ } \
+ static inline int \
+ __unused_batadv_mcast_mla_softif_get_ipv6(struct net_device *dev, \
+ struct hlist_head *mcast_list, \
+ struct batadv_mcast_mla_flags *flags) \
+ { \
+ struct batadv_hw_addr *new; \
+ struct inet6_dev *in6_dev; \
+ u8 mcast_addr[ETH_ALEN]; \
+ struct ifmcaddr6
+
+#endif /* LINUX_VERSION_IS_LESS(5, 13, 0) */
+
#endif /* _NET_BATMAN_ADV_COMPAT_NET_ADDRCONF_H_ */
diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c
index 28166402..1d63c8cb 100644
--- a/net/batman-adv/multicast.c
+++ b/net/batman-adv/multicast.c
@@ -454,8 +454,9 @@ batadv_mcast_mla_softif_get_ipv6(struct net_device *dev,
return 0;
}
- read_lock_bh(&in6_dev->lock);
- for (pmc6 = in6_dev->mc_list; pmc6; pmc6 = pmc6->next) {
+ for (pmc6 = rcu_dereference(in6_dev->mc_list);
+ pmc6;
+ pmc6 = rcu_dereference(pmc6->next)) {
if (IPV6_ADDR_MC_SCOPE(&pmc6->mca_addr) <
IPV6_ADDR_SCOPE_LINKLOCAL)
continue;
@@ -484,7 +485,6 @@ batadv_mcast_mla_softif_get_ipv6(struct net_device *dev,
hlist_add_head(&new->list, mcast_list);
ret++;
}
- read_unlock_bh(&in6_dev->lock);
rcu_read_unlock();
return ret;
--
2.31.0
1 year, 9 months
[RFC PATCH] batman-adv: convert ifmcaddr6 to RCU
by Sven Eckelmann
From: Taehee Yoo <ap420073(a)gmail.com>
The ifmcaddr6 has been protected by inet6_dev->lock(rwlock) so that
the critical section is atomic context. In order to switch this context,
changing locking is needed. The ifmcaddr6 actually already protected by
RTNL So if it's converted to use RCU, its control path context can be
switched to sleepable.
Suggested-by: Cong Wang <xiyou.wangcong(a)gmail.com>
Signed-off-by: Taehee Yoo <ap420073(a)gmail.com>
Signed-off-by: David S. Miller <davem(a)davemloft.net>
---
This patch is already upstream. But the compat code is missing in
batman-adv. So we have to find a way to call the read_lock_bh/read_unlock_bh
without adding it in multicast.c and not to use rcu_dereference without
removing it from multicast.c... does anyone have an idea?
net/batman-adv/multicast.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c
index 28166402..1d63c8cb 100644
--- a/net/batman-adv/multicast.c
+++ b/net/batman-adv/multicast.c
@@ -454,8 +454,9 @@ batadv_mcast_mla_softif_get_ipv6(struct net_device *dev,
return 0;
}
- read_lock_bh(&in6_dev->lock);
- for (pmc6 = in6_dev->mc_list; pmc6; pmc6 = pmc6->next) {
+ for (pmc6 = rcu_dereference(in6_dev->mc_list);
+ pmc6;
+ pmc6 = rcu_dereference(pmc6->next)) {
if (IPV6_ADDR_MC_SCOPE(&pmc6->mca_addr) <
IPV6_ADDR_SCOPE_LINKLOCAL)
continue;
@@ -484,7 +485,6 @@ batadv_mcast_mla_softif_get_ipv6(struct net_device *dev,
hlist_add_head(&new->list, mcast_list);
ret++;
}
- read_unlock_bh(&in6_dev->lock);
rcu_read_unlock();
return ret;
--
2.30.2
1 year, 9 months