Commit 3ea00443 authored by David S. Miller's avatar David S. Miller

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

Simon Wunderlich says:

====================
This feature patchset includes the following changes:

 - two patches with minimal clean up work by Antonio Quartulli and
   Simon Wunderlich

 - eight patches of B.A.T.M.A.N. V, API and documentation clean
   up work, by Antonio Quartulli and Marek Lindner

 - Andrew Lunn fixed the skb priority adoption when forwarding
   fragmented packets (two patches)

 - Multicast optimization support is now enabled for bridges which
   comes with some protocol updates, by Linus Luessing
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ca9354a1 4e3e823b
What: /sys/class/net/<iface>/batman-adv/throughput_override
Date: Feb 2014
Contact: Antonio Quartulli <antonio@meshcoding.com>
description:
Defines the throughput value to be used by B.A.T.M.A.N. V
when estimating the link throughput using this interface.
If the value is set to 0 then batman-adv will try to
estimate the throughput by itself.
What: /sys/class/net/<iface>/batman-adv/elp_interval What: /sys/class/net/<iface>/batman-adv/elp_interval
Date: Feb 2014 Date: Feb 2014
Contact: Linus Lüssing <linus.luessing@web.de> Contact: Linus Lüssing <linus.luessing@web.de>
Description: Description:
Defines the interval in milliseconds in which batman Defines the interval in milliseconds in which batman
sends its probing packets for link quality measurements. emits probing packets for neighbor sensing (ELP).
What: /sys/class/net/<iface>/batman-adv/iface_status What: /sys/class/net/<iface>/batman-adv/iface_status
Date: May 2010 Date: May 2010
...@@ -28,3 +19,12 @@ Description: ...@@ -28,3 +19,12 @@ Description:
The /sys/class/net/<iface>/batman-adv/mesh_iface file The /sys/class/net/<iface>/batman-adv/mesh_iface file
displays the batman mesh interface this <iface> displays the batman mesh interface this <iface>
currently is associated with. currently is associated with.
What: /sys/class/net/<iface>/batman-adv/throughput_override
Date: Feb 2014
Contact: Antonio Quartulli <a@unstable.cc>
description:
Defines the throughput value to be used by B.A.T.M.A.N. V
when estimating the link throughput using this interface.
If the value is set to 0 then batman-adv will try to
estimate the throughput by itself.
...@@ -66,7 +66,7 @@ config BATMAN_ADV_NC ...@@ -66,7 +66,7 @@ config BATMAN_ADV_NC
config BATMAN_ADV_MCAST config BATMAN_ADV_MCAST
bool "Multicast optimisation" bool "Multicast optimisation"
depends on BATMAN_ADV depends on BATMAN_ADV && INET && !(BRIDGE=m && BATMAN_ADV=y)
default n default n
help help
This option enables the multicast optimisation which aims to This option enables the multicast optimisation which aims to
......
...@@ -18,13 +18,14 @@ ...@@ -18,13 +18,14 @@
#ifndef _NET_BATMAN_ADV_BAT_ALGO_H_ #ifndef _NET_BATMAN_ADV_BAT_ALGO_H_
#define _NET_BATMAN_ADV_BAT_ALGO_H_ #define _NET_BATMAN_ADV_BAT_ALGO_H_
struct batadv_priv; #include "main.h"
int batadv_iv_init(void); int batadv_iv_init(void);
#ifdef CONFIG_BATMAN_ADV_BATMAN_V #ifdef CONFIG_BATMAN_ADV_BATMAN_V
int batadv_v_init(void); int batadv_v_init(void);
void batadv_v_hardif_init(struct batadv_hard_iface *hardif);
int batadv_v_mesh_init(struct batadv_priv *bat_priv); int batadv_v_mesh_init(struct batadv_priv *bat_priv);
void batadv_v_mesh_free(struct batadv_priv *bat_priv); void batadv_v_mesh_free(struct batadv_priv *bat_priv);
...@@ -35,6 +36,10 @@ static inline int batadv_v_init(void) ...@@ -35,6 +36,10 @@ static inline int batadv_v_init(void)
return 0; return 0;
} }
static inline void batadv_v_hardif_init(struct batadv_hard_iface *hardif)
{
}
static inline int batadv_v_mesh_init(struct batadv_priv *bat_priv) static inline int batadv_v_mesh_init(struct batadv_priv *bat_priv)
{ {
return 0; return 0;
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/lockdep.h> #include <linux/lockdep.h>
...@@ -58,6 +59,8 @@ ...@@ -58,6 +59,8 @@
#include "send.h" #include "send.h"
#include "translation-table.h" #include "translation-table.h"
static void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work);
/** /**
* enum batadv_dup_status - duplicate status * enum batadv_dup_status - duplicate status
* @BATADV_NO_DUP: the packet is no duplicate * @BATADV_NO_DUP: the packet is no duplicate
...@@ -336,7 +339,8 @@ batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface, ...@@ -336,7 +339,8 @@ batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface,
{ {
struct batadv_neigh_node *neigh_node; struct batadv_neigh_node *neigh_node;
neigh_node = batadv_neigh_node_new(orig_node, hard_iface, neigh_addr); neigh_node = batadv_neigh_node_get_or_create(orig_node,
hard_iface, neigh_addr);
if (!neigh_node) if (!neigh_node)
goto out; goto out;
...@@ -730,7 +734,7 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, ...@@ -730,7 +734,7 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
/* start timer for this packet */ /* start timer for this packet */
INIT_DELAYED_WORK(&forw_packet_aggr->delayed_work, INIT_DELAYED_WORK(&forw_packet_aggr->delayed_work,
batadv_send_outstanding_bat_ogm_packet); batadv_iv_send_outstanding_bat_ogm_packet);
queue_delayed_work(batadv_event_workqueue, queue_delayed_work(batadv_event_workqueue,
&forw_packet_aggr->delayed_work, &forw_packet_aggr->delayed_work,
send_time - jiffies); send_time - jiffies);
...@@ -937,6 +941,19 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) ...@@ -937,6 +941,19 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
u16 tvlv_len = 0; u16 tvlv_len = 0;
unsigned long send_time; unsigned long send_time;
if ((hard_iface->if_status == BATADV_IF_NOT_IN_USE) ||
(hard_iface->if_status == BATADV_IF_TO_BE_REMOVED))
return;
/* the interface gets activated here to avoid race conditions between
* the moment of activating the interface in
* hardif_activate_interface() where the originator mac is set and
* outdated packets (especially uninitialized mac addresses) in the
* packet queue
*/
if (hard_iface->if_status == BATADV_IF_TO_BE_ACTIVATED)
hard_iface->if_status = BATADV_IF_ACTIVE;
primary_if = batadv_primary_if_get_selected(bat_priv); primary_if = batadv_primary_if_get_selected(bat_priv);
if (hard_iface == primary_if) { if (hard_iface == primary_if) {
...@@ -1778,6 +1795,45 @@ static void batadv_iv_ogm_process(const struct sk_buff *skb, int ogm_offset, ...@@ -1778,6 +1795,45 @@ static void batadv_iv_ogm_process(const struct sk_buff *skb, int ogm_offset,
batadv_orig_node_put(orig_node); batadv_orig_node_put(orig_node);
} }
static void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work)
{
struct delayed_work *delayed_work;
struct batadv_forw_packet *forw_packet;
struct batadv_priv *bat_priv;
delayed_work = to_delayed_work(work);
forw_packet = container_of(delayed_work, struct batadv_forw_packet,
delayed_work);
bat_priv = netdev_priv(forw_packet->if_incoming->soft_iface);
spin_lock_bh(&bat_priv->forw_bat_list_lock);
hlist_del(&forw_packet->list);
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
goto out;
batadv_iv_ogm_emit(forw_packet);
/* we have to have at least one packet in the queue to determine the
* queues wake up time unless we are shutting down.
*
* only re-schedule if this is the "original" copy, e.g. the OGM of the
* primary interface should only be rescheduled once per period, but
* this function will be called for the forw_packet instances of the
* other secondary interfaces as well.
*/
if (forw_packet->own &&
forw_packet->if_incoming == forw_packet->if_outgoing)
batadv_iv_ogm_schedule(forw_packet->if_incoming);
out:
/* don't count own packet */
if (!forw_packet->own)
atomic_inc(&bat_priv->batman_queue_left);
batadv_forw_packet_free(forw_packet);
}
static int batadv_iv_ogm_receive(struct sk_buff *skb, static int batadv_iv_ogm_receive(struct sk_buff *skb,
struct batadv_hard_iface *if_incoming) struct batadv_hard_iface *if_incoming)
{ {
...@@ -1794,7 +1850,8 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb, ...@@ -1794,7 +1850,8 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb,
/* did we receive a B.A.T.M.A.N. IV OGM packet on an interface /* did we receive a B.A.T.M.A.N. IV OGM packet on an interface
* that does not have B.A.T.M.A.N. IV enabled ? * that does not have B.A.T.M.A.N. IV enabled ?
*/ */
if (bat_priv->bat_algo_ops->bat_ogm_emit != batadv_iv_ogm_emit) if (bat_priv->bat_algo_ops->bat_iface_enable !=
batadv_iv_ogm_iface_enable)
return NET_RX_DROP; return NET_RX_DROP;
batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_RX); batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_RX);
...@@ -2052,14 +2109,19 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1, ...@@ -2052,14 +2109,19 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1,
return ret; return ret;
} }
static void batadv_iv_iface_activate(struct batadv_hard_iface *hard_iface)
{
/* begin scheduling originator messages on that interface */
batadv_iv_ogm_schedule(hard_iface);
}
static struct batadv_algo_ops batadv_batman_iv __read_mostly = { static struct batadv_algo_ops batadv_batman_iv __read_mostly = {
.name = "BATMAN_IV", .name = "BATMAN_IV",
.bat_iface_activate = batadv_iv_iface_activate,
.bat_iface_enable = batadv_iv_ogm_iface_enable, .bat_iface_enable = batadv_iv_ogm_iface_enable,
.bat_iface_disable = batadv_iv_ogm_iface_disable, .bat_iface_disable = batadv_iv_ogm_iface_disable,
.bat_iface_update_mac = batadv_iv_ogm_iface_update_mac, .bat_iface_update_mac = batadv_iv_ogm_iface_update_mac,
.bat_primary_iface_set = batadv_iv_ogm_primary_iface_set, .bat_primary_iface_set = batadv_iv_ogm_primary_iface_set,
.bat_ogm_schedule = batadv_iv_ogm_schedule,
.bat_ogm_emit = batadv_iv_ogm_emit,
.bat_neigh_cmp = batadv_iv_ogm_neigh_cmp, .bat_neigh_cmp = batadv_iv_ogm_neigh_cmp,
.bat_neigh_is_similar_or_better = batadv_iv_ogm_neigh_is_sob, .bat_neigh_is_similar_or_better = batadv_iv_ogm_neigh_is_sob,
.bat_neigh_print = batadv_iv_neigh_print, .bat_neigh_print = batadv_iv_neigh_print,
......
...@@ -70,11 +70,6 @@ static int batadv_v_iface_enable(struct batadv_hard_iface *hard_iface) ...@@ -70,11 +70,6 @@ static int batadv_v_iface_enable(struct batadv_hard_iface *hard_iface)
if (ret < 0) if (ret < 0)
batadv_v_elp_iface_disable(hard_iface); batadv_v_elp_iface_disable(hard_iface);
/* enable link throughput auto-detection by setting the throughput
* override to zero
*/
atomic_set(&hard_iface->bat_v.throughput_override, 0);
return ret; return ret;
} }
...@@ -119,14 +114,6 @@ batadv_v_hardif_neigh_init(struct batadv_hardif_neigh_node *hardif_neigh) ...@@ -119,14 +114,6 @@ batadv_v_hardif_neigh_init(struct batadv_hardif_neigh_node *hardif_neigh)
batadv_v_elp_throughput_metric_update); batadv_v_elp_throughput_metric_update);
} }
static void batadv_v_ogm_schedule(struct batadv_hard_iface *hard_iface)
{
}
static void batadv_v_ogm_emit(struct batadv_forw_packet *forw_packet)
{
}
/** /**
* batadv_v_orig_print_neigh - print neighbors for the originator table * batadv_v_orig_print_neigh - print neighbors for the originator table
* @orig_node: the orig_node for which the neighbors are printed * @orig_node: the orig_node for which the neighbors are printed
...@@ -340,14 +327,26 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = { ...@@ -340,14 +327,26 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = {
.bat_iface_update_mac = batadv_v_iface_update_mac, .bat_iface_update_mac = batadv_v_iface_update_mac,
.bat_primary_iface_set = batadv_v_primary_iface_set, .bat_primary_iface_set = batadv_v_primary_iface_set,
.bat_hardif_neigh_init = batadv_v_hardif_neigh_init, .bat_hardif_neigh_init = batadv_v_hardif_neigh_init,
.bat_ogm_emit = batadv_v_ogm_emit,
.bat_ogm_schedule = batadv_v_ogm_schedule,
.bat_orig_print = batadv_v_orig_print, .bat_orig_print = batadv_v_orig_print,
.bat_neigh_cmp = batadv_v_neigh_cmp, .bat_neigh_cmp = batadv_v_neigh_cmp,
.bat_neigh_is_similar_or_better = batadv_v_neigh_is_sob, .bat_neigh_is_similar_or_better = batadv_v_neigh_is_sob,
.bat_neigh_print = batadv_v_neigh_print, .bat_neigh_print = batadv_v_neigh_print,
}; };
/**
* batadv_v_hardif_init - initialize the algorithm specific fields in the
* hard-interface object
* @hard_iface: the hard-interface to initialize
*/
void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface)
{
/* enable link throughput auto-detection by setting the throughput
* override to zero
*/
atomic_set(&hard_iface->bat_v.throughput_override, 0);
atomic_set(&hard_iface->bat_v.elp_interval, 500);
}
/** /**
* batadv_v_mesh_init - initialize the B.A.T.M.A.N. V private resources for a * batadv_v_mesh_init - initialize the B.A.T.M.A.N. V private resources for a
* mesh * mesh
......
...@@ -344,7 +344,6 @@ int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface) ...@@ -344,7 +344,6 @@ int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface)
/* randomize initial seqno to avoid collision */ /* randomize initial seqno to avoid collision */
get_random_bytes(&random_seqno, sizeof(random_seqno)); get_random_bytes(&random_seqno, sizeof(random_seqno));
atomic_set(&hard_iface->bat_v.elp_seqno, random_seqno); atomic_set(&hard_iface->bat_v.elp_seqno, random_seqno);
atomic_set(&hard_iface->bat_v.elp_interval, 500);
/* assume full-duplex by default */ /* assume full-duplex by default */
hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX; hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX;
...@@ -443,7 +442,8 @@ static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv, ...@@ -443,7 +442,8 @@ static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv,
if (!orig_neigh) if (!orig_neigh)
return; return;
neigh = batadv_neigh_node_new(orig_neigh, if_incoming, neigh_addr); neigh = batadv_neigh_node_get_or_create(orig_neigh,
if_incoming, neigh_addr);
if (!neigh) if (!neigh)
goto orig_free; goto orig_free;
......
...@@ -683,8 +683,8 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset, ...@@ -683,8 +683,8 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset,
if (!orig_node) if (!orig_node)
return; return;
neigh_node = batadv_neigh_node_new(orig_node, if_incoming, neigh_node = batadv_neigh_node_get_or_create(orig_node, if_incoming,
ethhdr->h_source); ethhdr->h_source);
if (!neigh_node) if (!neigh_node)
goto out; goto out;
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#include "distributed-arp-table.h" #include "distributed-arp-table.h"
#include "gateway_client.h" #include "gateway_client.h"
#include "icmp_socket.h" #include "icmp_socket.h"
#include "multicast.h"
#include "network-coding.h" #include "network-coding.h"
#include "originator.h" #include "originator.h"
#include "translation-table.h" #include "translation-table.h"
...@@ -363,6 +364,22 @@ static int batadv_nc_nodes_open(struct inode *inode, struct file *file) ...@@ -363,6 +364,22 @@ static int batadv_nc_nodes_open(struct inode *inode, struct file *file)
} }
#endif #endif
#ifdef CONFIG_BATMAN_ADV_MCAST
/**
* batadv_mcast_flags_open - prepare file handler for reads from mcast_flags
* @inode: inode which was opened
* @file: file handle to be initialized
*
* Return: 0 on success or negative error number in case of failure
*/
static int batadv_mcast_flags_open(struct inode *inode, struct file *file)
{
struct net_device *net_dev = (struct net_device *)inode->i_private;
return single_open(file, batadv_mcast_flags_seq_print_text, net_dev);
}
#endif
#define BATADV_DEBUGINFO(_name, _mode, _open) \ #define BATADV_DEBUGINFO(_name, _mode, _open) \
struct batadv_debuginfo batadv_debuginfo_##_name = { \ struct batadv_debuginfo batadv_debuginfo_##_name = { \
.attr = { \ .attr = { \
...@@ -407,6 +424,9 @@ static BATADV_DEBUGINFO(transtable_local, S_IRUGO, ...@@ -407,6 +424,9 @@ static BATADV_DEBUGINFO(transtable_local, S_IRUGO,
#ifdef CONFIG_BATMAN_ADV_NC #ifdef CONFIG_BATMAN_ADV_NC
static BATADV_DEBUGINFO(nc_nodes, S_IRUGO, batadv_nc_nodes_open); static BATADV_DEBUGINFO(nc_nodes, S_IRUGO, batadv_nc_nodes_open);
#endif #endif
#ifdef CONFIG_BATMAN_ADV_MCAST
static BATADV_DEBUGINFO(mcast_flags, S_IRUGO, batadv_mcast_flags_open);
#endif
static struct batadv_debuginfo *batadv_mesh_debuginfos[] = { static struct batadv_debuginfo *batadv_mesh_debuginfos[] = {
&batadv_debuginfo_neighbors, &batadv_debuginfo_neighbors,
...@@ -423,6 +443,9 @@ static struct batadv_debuginfo *batadv_mesh_debuginfos[] = { ...@@ -423,6 +443,9 @@ static struct batadv_debuginfo *batadv_mesh_debuginfos[] = {
&batadv_debuginfo_transtable_local, &batadv_debuginfo_transtable_local,
#ifdef CONFIG_BATMAN_ADV_NC #ifdef CONFIG_BATMAN_ADV_NC
&batadv_debuginfo_nc_nodes, &batadv_debuginfo_nc_nodes,
#endif
#ifdef CONFIG_BATMAN_ADV_MCAST
&batadv_debuginfo_mcast_flags,
#endif #endif
NULL, NULL,
}; };
......
...@@ -27,7 +27,6 @@ ...@@ -27,7 +27,6 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/lockdep.h> #include <linux/lockdep.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/pkt_sched.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
...@@ -414,7 +413,7 @@ static struct sk_buff *batadv_frag_create(struct sk_buff *skb, ...@@ -414,7 +413,7 @@ static struct sk_buff *batadv_frag_create(struct sk_buff *skb,
if (!skb_fragment) if (!skb_fragment)
goto err; goto err;
skb->priority = TC_PRIO_CONTROL; skb_fragment->priority = skb->priority;
/* Eat the last mtu-bytes of the skb */ /* Eat the last mtu-bytes of the skb */
skb_reserve(skb_fragment, header_size + ETH_HLEN); skb_reserve(skb_fragment, header_size + ETH_HLEN);
...@@ -473,6 +472,15 @@ bool batadv_frag_send_packet(struct sk_buff *skb, ...@@ -473,6 +472,15 @@ bool batadv_frag_send_packet(struct sk_buff *skb,
frag_header.reserved = 0; frag_header.reserved = 0;
frag_header.no = 0; frag_header.no = 0;
frag_header.total_size = htons(skb->len); frag_header.total_size = htons(skb->len);
/* skb->priority values from 256->263 are magic values to
* directly indicate a specific 802.1d priority. This is used
* to allow 802.1d priority to be passed directly in from VLAN
* tags, etc.
*/
if (skb->priority >= 256 && skb->priority <= 263)
frag_header.priority = skb->priority - 256;
ether_addr_copy(frag_header.orig, primary_if->net_dev->dev_addr); ether_addr_copy(frag_header.orig, primary_if->net_dev->dev_addr);
ether_addr_copy(frag_header.dest, orig_node->orig); ether_addr_copy(frag_header.dest, orig_node->orig);
......
...@@ -192,7 +192,7 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv) ...@@ -192,7 +192,7 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
tq_avg = router_ifinfo->bat_iv.tq_avg; tq_avg = router_ifinfo->bat_iv.tq_avg;
switch (atomic_read(&bat_priv->gw_sel_class)) { switch (atomic_read(&bat_priv->gw.sel_class)) {
case 1: /* fast connection */ case 1: /* fast connection */
tmp_gw_factor = tq_avg * tq_avg; tmp_gw_factor = tq_avg * tq_avg;
tmp_gw_factor *= gw_node->bandwidth_down; tmp_gw_factor *= gw_node->bandwidth_down;
...@@ -255,7 +255,7 @@ void batadv_gw_check_client_stop(struct batadv_priv *bat_priv) ...@@ -255,7 +255,7 @@ void batadv_gw_check_client_stop(struct batadv_priv *bat_priv)
{ {
struct batadv_gw_node *curr_gw; struct batadv_gw_node *curr_gw;
if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_CLIENT) if (atomic_read(&bat_priv->gw.mode) != BATADV_GW_MODE_CLIENT)
return; return;
curr_gw = batadv_gw_get_selected_gw_node(bat_priv); curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
...@@ -283,7 +283,7 @@ void batadv_gw_election(struct batadv_priv *bat_priv) ...@@ -283,7 +283,7 @@ void batadv_gw_election(struct batadv_priv *bat_priv)
struct batadv_neigh_ifinfo *router_ifinfo = NULL; struct batadv_neigh_ifinfo *router_ifinfo = NULL;
char gw_addr[18] = { '\0' }; char gw_addr[18] = { '\0' };
if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_CLIENT) if (atomic_read(&bat_priv->gw.mode) != BATADV_GW_MODE_CLIENT)
goto out; goto out;
curr_gw = batadv_gw_get_selected_gw_node(bat_priv); curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
...@@ -402,8 +402,8 @@ void batadv_gw_check_election(struct batadv_priv *bat_priv, ...@@ -402,8 +402,8 @@ void batadv_gw_check_election(struct batadv_priv *bat_priv,
/* if the routing class is greater than 3 the value tells us how much /* if the routing class is greater than 3 the value tells us how much
* greater the TQ value of the new gateway must be * greater the TQ value of the new gateway must be
*/ */
if ((atomic_read(&bat_priv->gw_sel_class) > 3) && if ((atomic_read(&bat_priv->gw.sel_class) > 3) &&
(orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw_sel_class))) (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw.sel_class)))
goto out; goto out;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
...@@ -638,8 +638,7 @@ int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset) ...@@ -638,8 +638,7 @@ int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset)
goto out; goto out;
seq_printf(seq, seq_printf(seq,
" %-12s (%s/%i) %17s [%10s]: advertised uplink bandwidth ... [B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n", " Gateway (#/255) Nexthop [outgoingIF]: advertised uplink bandwidth ... [B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
"Gateway", "#", BATADV_TQ_MAX_VALUE, "Nexthop", "outgoingIF",
BATADV_SOURCE_VERSION, primary_if->net_dev->name, BATADV_SOURCE_VERSION, primary_if->net_dev->name,
primary_if->net_dev->dev_addr, net_dev->name); primary_if->net_dev->dev_addr, net_dev->name);
...@@ -821,7 +820,7 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, ...@@ -821,7 +820,7 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
if (!gw_node) if (!gw_node)
goto out; goto out;
switch (atomic_read(&bat_priv->gw_mode)) { switch (atomic_read(&bat_priv->gw.mode)) {
case BATADV_GW_MODE_SERVER: case BATADV_GW_MODE_SERVER:
/* If we are a GW then we are our best GW. We can artificially /* If we are a GW then we are our best GW. We can artificially
* set the tq towards ourself as the maximum value * set the tq towards ourself as the maximum value
......
...@@ -144,7 +144,7 @@ void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv) ...@@ -144,7 +144,7 @@ void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv)
u32 down, up; u32 down, up;
char gw_mode; char gw_mode;
gw_mode = atomic_read(&bat_priv->gw_mode); gw_mode = atomic_read(&bat_priv->gw.mode);
switch (gw_mode) { switch (gw_mode) {
case BATADV_GW_MODE_OFF: case BATADV_GW_MODE_OFF:
...@@ -241,8 +241,8 @@ static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, ...@@ -241,8 +241,8 @@ static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
/* restart gateway selection if fast or late switching was enabled */ /* restart gateway selection if fast or late switching was enabled */
if ((gateway.bandwidth_down != 0) && if ((gateway.bandwidth_down != 0) &&
(atomic_read(&bat_priv->gw_mode) == BATADV_GW_MODE_CLIENT) && (atomic_read(&bat_priv->gw.mode) == BATADV_GW_MODE_CLIENT) &&
(atomic_read(&bat_priv->gw_sel_class) > 2)) (atomic_read(&bat_priv->gw.sel_class) > 2))
batadv_gw_check_election(bat_priv, orig); batadv_gw_check_election(bat_priv, orig);
} }
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include "bat_algo.h"
#include "bridge_loop_avoidance.h" #include "bridge_loop_avoidance.h"
#include "debugfs.h" #include "debugfs.h"
#include "distributed-arp-table.h" #include "distributed-arp-table.h"
...@@ -553,9 +554,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, ...@@ -553,9 +554,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
batadv_hardif_recalc_extra_skbroom(soft_iface); batadv_hardif_recalc_extra_skbroom(soft_iface);
/* begin scheduling originator messages on that interface */
batadv_schedule_bat_ogm(hard_iface);
out: out:
return 0; return 0;
...@@ -686,6 +684,8 @@ batadv_hardif_add_interface(struct net_device *net_dev) ...@@ -686,6 +684,8 @@ batadv_hardif_add_interface(struct net_device *net_dev)
if (batadv_is_wifi_netdev(net_dev)) if (batadv_is_wifi_netdev(net_dev))
hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS; hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
batadv_v_hardif_init(hard_iface);
/* extra reference for return */ /* extra reference for return */
kref_init(&hard_iface->refcount); kref_init(&hard_iface->refcount);
kref_get(&hard_iface->refcount); kref_get(&hard_iface->refcount);
......
...@@ -569,8 +569,6 @@ int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops) ...@@ -569,8 +569,6 @@ int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops)
!bat_algo_ops->bat_iface_disable || !bat_algo_ops->bat_iface_disable ||
!bat_algo_ops->bat_iface_update_mac || !bat_algo_ops->bat_iface_update_mac ||
!bat_algo_ops->bat_primary_iface_set || !bat_algo_ops->bat_primary_iface_set ||
!bat_algo_ops->bat_ogm_schedule ||
!bat_algo_ops->bat_ogm_emit ||
!bat_algo_ops->bat_neigh_cmp || !bat_algo_ops->bat_neigh_cmp ||
!bat_algo_ops->bat_neigh_is_similar_or_better) { !bat_algo_ops->bat_neigh_is_similar_or_better) {
pr_info("Routing algo '%s' does not implement required ops\n", pr_info("Routing algo '%s' does not implement required ops\n",
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
#define BATADV_DRIVER_DEVICE "batman-adv" #define BATADV_DRIVER_DEVICE "batman-adv"
#ifndef BATADV_SOURCE_VERSION #ifndef BATADV_SOURCE_VERSION
#define BATADV_SOURCE_VERSION "2016.2" #define BATADV_SOURCE_VERSION "2016.3"
#endif #endif
/* B.A.T.M.A.N. parameters */ /* B.A.T.M.A.N. parameters */
...@@ -231,6 +231,7 @@ __be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr); ...@@ -231,6 +231,7 @@ __be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr);
* @BATADV_DBG_BLA: bridge loop avoidance messages * @BATADV_DBG_BLA: bridge loop avoidance messages
* @BATADV_DBG_DAT: ARP snooping and DAT related messages * @BATADV_DBG_DAT: ARP snooping and DAT related messages
* @BATADV_DBG_NC: network coding related messages * @BATADV_DBG_NC: network coding related messages
* @BATADV_DBG_MCAST: multicast related messages
* @BATADV_DBG_ALL: the union of all the above log levels * @BATADV_DBG_ALL: the union of all the above log levels
*/ */
enum batadv_dbg_level { enum batadv_dbg_level {
...@@ -240,7 +241,8 @@ enum batadv_dbg_level { ...@@ -240,7 +241,8 @@ enum batadv_dbg_level {
BATADV_DBG_BLA = BIT(3), BATADV_DBG_BLA = BIT(3),
BATADV_DBG_DAT = BIT(4), BATADV_DBG_DAT = BIT(4),
BATADV_DBG_NC = BIT(5), BATADV_DBG_NC = BIT(5),
BATADV_DBG_ALL = 63, BATADV_DBG_MCAST = BIT(6),
BATADV_DBG_ALL = 127,
}; };
#ifdef CONFIG_BATMAN_ADV_DEBUG #ifdef CONFIG_BATMAN_ADV_DEBUG
......
...@@ -25,17 +25,23 @@ ...@@ -25,17 +25,23 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/icmpv6.h>
#include <linux/if_bridge.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/in6.h> #include <linux/igmp.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/in6.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/ipv6.h> #include <linux/ipv6.h>
#include <linux/kernel.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/lockdep.h> #include <linux/lockdep.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/printk.h>
#include <linux/rculist.h> #include <linux/rculist.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/seq_file.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
...@@ -43,18 +49,55 @@ ...@@ -43,18 +49,55 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/types.h> #include <linux/types.h>
#include <net/addrconf.h> #include <net/addrconf.h>
#include <net/if_inet6.h>
#include <net/ip.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include "hard-interface.h"
#include "hash.h"
#include "packet.h" #include "packet.h"
#include "translation-table.h" #include "translation-table.h"
/**
* batadv_mcast_get_bridge - get the bridge on top of the softif if it exists
* @soft_iface: netdev struct of the mesh interface
*
* If the given soft interface has a bridge on top then the refcount
* of the according net device is increased.
*
* Return: NULL if no such bridge exists. Otherwise the net device of the
* bridge.
*/
static struct net_device *batadv_mcast_get_bridge(struct net_device *soft_iface)
{
struct net_device *upper = soft_iface;
rcu_read_lock();
do {
upper = netdev_master_upper_dev_get_rcu(upper);
} while (upper && !(upper->priv_flags & IFF_EBRIDGE));
if (upper)
dev_hold(upper);
rcu_read_unlock();
return upper;
}
/** /**
* batadv_mcast_mla_softif_get - get softif multicast listeners * batadv_mcast_mla_softif_get - get softif multicast listeners
* @dev: the device to collect multicast addresses from * @dev: the device to collect multicast addresses from
* @mcast_list: a list to put found addresses into * @mcast_list: a list to put found addresses into
* *
* Collect multicast addresses of the local multicast listeners * Collects multicast addresses of multicast listeners residing
* on the given soft interface, dev, in the given mcast_list. * on this kernel on the given soft interface, dev, in
* the given mcast_list. In general, multicast listeners provided by
* your multicast receiving applications run directly on this node.
*
* If there is a bridge interface on top of dev, collects from that one
* instead. Just like with IP addresses and routes, multicast listeners
* will(/should) register to the bridge interface instead of an
* enslaved bat0.
* *
* Return: -ENOMEM on memory allocation error or the number of * Return: -ENOMEM on memory allocation error or the number of
* items added to the mcast_list otherwise. * items added to the mcast_list otherwise.
...@@ -62,12 +105,13 @@ ...@@ -62,12 +105,13 @@
static int batadv_mcast_mla_softif_get(struct net_device *dev, static int batadv_mcast_mla_softif_get(struct net_device *dev,
struct hlist_head *mcast_list) struct hlist_head *mcast_list)
{ {
struct net_device *bridge = batadv_mcast_get_bridge(dev);
struct netdev_hw_addr *mc_list_entry; struct netdev_hw_addr *mc_list_entry;
struct batadv_hw_addr *new; struct batadv_hw_addr *new;
int ret = 0; int ret = 0;
netif_addr_lock_bh(dev); netif_addr_lock_bh(bridge ? bridge : dev);
netdev_for_each_mc_addr(mc_list_entry, dev) { netdev_for_each_mc_addr(mc_list_entry, bridge ? bridge : dev) {
new = kmalloc(sizeof(*new), GFP_ATOMIC); new = kmalloc(sizeof(*new), GFP_ATOMIC);
if (!new) { if (!new) {
ret = -ENOMEM; ret = -ENOMEM;
...@@ -78,7 +122,10 @@ static int batadv_mcast_mla_softif_get(struct net_device *dev, ...@@ -78,7 +122,10 @@ static int batadv_mcast_mla_softif_get(struct net_device *dev,
hlist_add_head(&new->list, mcast_list); hlist_add_head(&new->list, mcast_list);
ret++; ret++;
} }
netif_addr_unlock_bh(dev); netif_addr_unlock_bh(bridge ? bridge : dev);
if (bridge)
dev_put(bridge);
return ret; return ret;
} }
...@@ -103,6 +150,83 @@ static bool batadv_mcast_mla_is_duplicate(u8 *mcast_addr, ...@@ -103,6 +150,83 @@ static bool batadv_mcast_mla_is_duplicate(u8 *mcast_addr,
return false; return false;
} }
/**
* batadv_mcast_mla_br_addr_cpy - copy a bridge multicast address
* @dst: destination to write to - a multicast MAC address
* @src: source to read from - a multicast IP address
*
* Converts a given multicast IPv4/IPv6 address from a bridge
* to its matching multicast MAC address and copies it into the given
* destination buffer.
*
* Caller needs to make sure the destination buffer can hold
* at least ETH_ALEN bytes.
*/
static void batadv_mcast_mla_br_addr_cpy(char *dst, const struct br_ip *src)
{
if (src->proto == htons(ETH_P_IP))
ip_eth_mc_map(src->u.ip4, dst);
#if IS_ENABLED(CONFIG_IPV6)
else if (src->proto == htons(ETH_P_IPV6))
ipv6_eth_mc_map(&src->u.ip6, dst);
#endif
else
eth_zero_addr(dst);
}
/**
* batadv_mcast_mla_bridge_get - get bridged-in multicast listeners
* @dev: a bridge slave whose bridge to collect multicast addresses from
* @mcast_list: a list to put found addresses into
*
* Collects multicast addresses of multicast listeners residing
* on foreign, non-mesh devices which we gave access to our mesh via
* a bridge on top of the given soft interface, dev, in the given
* mcast_list.
*
* Return: -ENOMEM on memory allocation error or the number of
* items added to the mcast_list otherwise.
*/
static int batadv_mcast_mla_bridge_get(struct net_device *dev,
struct hlist_head *mcast_list)
{
struct list_head bridge_mcast_list = LIST_HEAD_INIT(bridge_mcast_list);
struct br_ip_list *br_ip_entry, *tmp;
struct batadv_hw_addr *new;
u8 mcast_addr[ETH_ALEN];
int ret;
/* we don't need to detect these devices/listeners, the IGMP/MLD
* snooping code of the Linux bridge already does that for us
*/
ret = br_multicast_list_adjacent(dev, &bridge_mcast_list);
if (ret < 0)
goto out;
list_for_each_entry(br_ip_entry, &bridge_mcast_list, list) {
batadv_mcast_mla_br_addr_cpy(mcast_addr, &br_ip_entry->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);
}
out:
list_for_each_entry_safe(br_ip_entry, tmp, &bridge_mcast_list, list) {
list_del(&br_ip_entry->list);
kfree(br_ip_entry);
}
return ret;
}
/** /**
* batadv_mcast_mla_list_free - free a list of multicast addresses * batadv_mcast_mla_list_free - free a list of multicast addresses
* @bat_priv: the bat priv with all the soft interface information * @bat_priv: the bat priv with all the soft interface information
...@@ -213,6 +337,122 @@ static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv) ...@@ -213,6 +337,122 @@ static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv)
return upper; return upper;
} }
/**
* batadv_mcast_querier_log - debug output regarding the querier status on link
* @bat_priv: the bat priv with all the soft interface information
* @str_proto: a string for the querier protocol (e.g. "IGMP" or "MLD")
* @old_state: the previous querier state on our link
* @new_state: the new querier state on our link
*
* Outputs debug messages to the logging facility with log level 'mcast'
* regarding changes to the querier status on the link which are relevant
* to our multicast optimizations.
*
* Usually this is about whether a querier appeared or vanished in
* our mesh or whether the querier is in the suboptimal position of being
* behind our local bridge segment: Snooping switches will directly
* forward listener reports to the querier, therefore batman-adv and
* the bridge will potentially not see these listeners - the querier is
* potentially shadowing listeners from us then.
*
* This is only interesting for nodes with a bridge on top of their
* soft interface.
*/
static void
batadv_mcast_querier_log(struct batadv_priv *bat_priv, char *str_proto,
struct batadv_mcast_querier_state *old_state,
struct batadv_mcast_querier_state *new_state)
{
if (!old_state->exists && new_state->exists)
batadv_info(bat_priv->soft_iface, "%s Querier appeared\n",
str_proto);
else if (old_state->exists && !new_state->exists)
batadv_info(bat_priv->soft_iface,
"%s Querier disappeared - multicast optimizations disabled\n",
str_proto);
else if (!bat_priv->mcast.bridged && !new_state->exists)
batadv_info(bat_priv->soft_iface,
"No %s Querier present - multicast optimizations disabled\n",
str_proto);
if (new_state->exists) {
if ((!old_state->shadowing && new_state->shadowing) ||
(!old_state->exists && new_state->shadowing))
batadv_dbg(BATADV_DBG_MCAST, bat_priv,
"%s Querier is behind our bridged segment: Might shadow listeners\n",
str_proto);
else if (old_state->shadowing && !new_state->shadowing)
batadv_dbg(BATADV_DBG_MCAST, bat_priv,
"%s Querier is not behind our bridged segment\n",
str_proto);
}
}
/**
* batadv_mcast_bridge_log - debug output for topology changes in bridged setups
* @bat_priv: the bat priv with all the soft interface information
* @bridged: a flag about whether the soft interface is currently bridged or not
* @querier_ipv4: (maybe) new status of a potential, selected IGMP querier
* @querier_ipv6: (maybe) new status of a potential, selected MLD querier
*
* If no bridges are ever used on this node, then this function does nothing.
*
* Otherwise this function outputs debug information to the 'mcast' log level
* which might be relevant to our multicast optimizations.
*
* More precisely, it outputs information when a bridge interface is added or
* removed from a soft interface. And when a bridge is present, it further
* outputs information about the querier state which is relevant for the
* multicast flags this node is going to set.
*/
static void
batadv_mcast_bridge_log(struct batadv_priv *bat_priv, bool bridged,
struct batadv_mcast_querier_state *querier_ipv4,
struct batadv_mcast_querier_state *querier_ipv6)
{
if (!bat_priv->mcast.bridged && bridged)
batadv_dbg(BATADV_DBG_MCAST, bat_priv,
"Bridge added: Setting Unsnoopables(U)-flag\n");
else if (bat_priv->mcast.bridged && !bridged)
batadv_dbg(BATADV_DBG_MCAST, bat_priv,
"Bridge removed: Unsetting Unsnoopables(U)-flag\n");
if (bridged) {
batadv_mcast_querier_log(bat_priv, "IGMP",
&bat_priv->mcast.querier_ipv4,
querier_ipv4);
batadv_mcast_querier_log(bat_priv, "MLD",
&bat_priv->mcast.querier_ipv6,
querier_ipv6);
}
}
/**
* batadv_mcast_flags_logs - output debug information about mcast flag changes
* @bat_priv: the bat priv with all the soft interface information
* @flags: flags indicating the new multicast state
*
* Whenever the multicast flags this nodes announces changes (@mcast_flags vs.
* bat_priv->mcast.flags), this notifies userspace via the 'mcast' log level.
*/
static void batadv_mcast_flags_log(struct batadv_priv *bat_priv, u8 flags)
{
u8 old_flags = bat_priv->mcast.flags;
char str_old_flags[] = "[...]";
sprintf(str_old_flags, "[%c%c%c]",
(old_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) ? 'U' : '.',
(old_flags & BATADV_MCAST_WANT_ALL_IPV4) ? '4' : '.',
(old_flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.');
batadv_dbg(BATADV_DBG_MCAST, bat_priv,
"Changing multicast flags from '%s' to '[%c%c%c]'\n",
bat_priv->mcast.enabled ? str_old_flags : "<undefined>",
(flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) ? 'U' : '.',
(flags & BATADV_MCAST_WANT_ALL_IPV4) ? '4' : '.',
(flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.');
}
/** /**
* batadv_mcast_mla_tvlv_update - update multicast tvlv * batadv_mcast_mla_tvlv_update - update multicast tvlv
* @bat_priv: the bat priv with all the soft interface information * @bat_priv: the bat priv with all the soft interface information
...@@ -220,38 +460,73 @@ static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv) ...@@ -220,38 +460,73 @@ static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv)
* Updates the own multicast tvlv with our current multicast related settings, * Updates the own multicast tvlv with our current multicast related settings,
* capabilities and inabilities. * capabilities and inabilities.
* *
* Return: true if the tvlv container is registered afterwards. Otherwise * Return: false if we want all IPv4 && IPv6 multicast traffic and true
* returns false. * otherwise.
*/ */
static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv) static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv)
{ {
struct batadv_tvlv_mcast_data mcast_data; struct batadv_tvlv_mcast_data mcast_data;
struct batadv_mcast_querier_state querier4 = {false, false};
struct batadv_mcast_querier_state querier6 = {false, false};
struct net_device *dev = bat_priv->soft_iface;
bool bridged;
mcast_data.flags = BATADV_NO_FLAGS; mcast_data.flags = BATADV_NO_FLAGS;
memset(mcast_data.reserved, 0, sizeof(mcast_data.reserved)); memset(mcast_data.reserved, 0, sizeof(mcast_data.reserved));
/* Avoid attaching MLAs, if there is a bridge on top of our soft bridged = batadv_mcast_has_bridge(bat_priv);
* interface, we don't support that yet (TODO) if (!bridged)
goto update;
#if !IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)
pr_warn_once("No bridge IGMP snooping compiled - multicast optimizations disabled\n");
#endif
querier4.exists = br_multicast_has_querier_anywhere(dev, ETH_P_IP);
querier4.shadowing = br_multicast_has_querier_adjacent(dev, ETH_P_IP);
querier6.exists = br_multicast_has_querier_anywhere(dev, ETH_P_IPV6);
querier6.shadowing = br_multicast_has_querier_adjacent(dev, ETH_P_IPV6);
mcast_data.flags |= BATADV_MCAST_WANT_ALL_UNSNOOPABLES;
/* 1) If no querier exists at all, then multicast listeners on
* our local TT clients behind the bridge will keep silent.
* 2) If the selected querier is on one of our local TT clients,
* behind the bridge, then this querier might shadow multicast
* listeners on our local TT clients, behind this bridge.
*
* In both cases, we will signalize other batman nodes that
* we need all multicast traffic of the according protocol.
*/ */
if (batadv_mcast_has_bridge(bat_priv)) { if (!querier4.exists || querier4.shadowing)
if (bat_priv->mcast.enabled) { mcast_data.flags |= BATADV_MCAST_WANT_ALL_IPV4;
batadv_tvlv_container_unregister(bat_priv,
BATADV_TVLV_MCAST, 1);
bat_priv->mcast.enabled = false;
}
return false; if (!querier6.exists || querier6.shadowing)
} mcast_data.flags |= BATADV_MCAST_WANT_ALL_IPV6;
update:
batadv_mcast_bridge_log(bat_priv, bridged, &querier4, &querier6);
bat_priv->mcast.querier_ipv4.exists = querier4.exists;
bat_priv->mcast.querier_ipv4.shadowing = querier4.shadowing;
bat_priv->mcast.querier_ipv6.exists = querier6.exists;
bat_priv->mcast.querier_ipv6.shadowing = querier6.shadowing;
bat_priv->mcast.bridged = bridged;
if (!bat_priv->mcast.enabled || if (!bat_priv->mcast.enabled ||
mcast_data.flags != bat_priv->mcast.flags) { mcast_data.flags != bat_priv->mcast.flags) {
batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 1, batadv_mcast_flags_log(bat_priv, mcast_data.flags);
batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 2,
&mcast_data, sizeof(mcast_data)); &mcast_data, sizeof(mcast_data));
bat_priv->mcast.flags = mcast_data.flags; bat_priv->mcast.flags = mcast_data.flags;
bat_priv->mcast.enabled = true; bat_priv->mcast.enabled = true;
} }
return true; return !(mcast_data.flags &
(BATADV_MCAST_WANT_ALL_IPV4 + BATADV_MCAST_WANT_ALL_IPV6));
} }
/** /**
...@@ -274,6 +549,10 @@ void batadv_mcast_mla_update(struct batadv_priv *bat_priv) ...@@ -274,6 +549,10 @@ void batadv_mcast_mla_update(struct batadv_priv *bat_priv)
if (ret < 0) if (ret < 0)
goto out; goto out;
ret = batadv_mcast_mla_bridge_get(soft_iface, &mcast_list);
if (ret < 0)
goto out;
update: update:
batadv_mcast_mla_tt_retract(bat_priv, &mcast_list); batadv_mcast_mla_tt_retract(bat_priv, &mcast_list);
batadv_mcast_mla_tt_add(bat_priv, &mcast_list); batadv_mcast_mla_tt_add(bat_priv, &mcast_list);
...@@ -282,6 +561,31 @@ void batadv_mcast_mla_update(struct batadv_priv *bat_priv) ...@@ -282,6 +561,31 @@ void batadv_mcast_mla_update(struct batadv_priv *bat_priv)
batadv_mcast_mla_list_free(bat_priv, &mcast_list); batadv_mcast_mla_list_free(bat_priv, &mcast_list);
} }
/**
* batadv_mcast_is_report_ipv4 - check for IGMP reports
* @skb: the ethernet frame destined for the mesh
*
* This call might reallocate skb data.
*
* Checks whether the given frame is a valid IGMP report.
*
* Return: If so then true, otherwise false.
*/
static bool batadv_mcast_is_report_ipv4(struct sk_buff *skb)
{
if (ip_mc_check_igmp(skb, NULL) < 0)
return false;
switch (igmp_hdr(skb)->type) {
case IGMP_HOST_MEMBERSHIP_REPORT:
case IGMPV2_HOST_MEMBERSHIP_REPORT:
case IGMPV3_HOST_MEMBERSHIP_REPORT:
return true;
}
return false;
}
/** /**
* batadv_mcast_forw_mode_check_ipv4 - check for optimized forwarding potential * batadv_mcast_forw_mode_check_ipv4 - check for optimized forwarding potential
* @bat_priv: the bat priv with all the soft interface information * @bat_priv: the bat priv with all the soft interface information
...@@ -304,6 +608,9 @@ static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv, ...@@ -304,6 +608,9 @@ static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv,
if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*iphdr))) if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*iphdr)))
return -ENOMEM; return -ENOMEM;
if (batadv_mcast_is_report_ipv4(skb))
return -EINVAL;
iphdr = ip_hdr(skb); iphdr = ip_hdr(skb);
/* TODO: Implement Multicast Router Discovery (RFC4286), /* TODO: Implement Multicast Router Discovery (RFC4286),
...@@ -320,6 +627,31 @@ static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv, ...@@ -320,6 +627,31 @@ static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv,
return 0; return 0;
} }
#if IS_ENABLED(CONFIG_IPV6)
/**
* batadv_mcast_is_report_ipv6 - check for MLD reports
* @skb: the ethernet frame destined for the mesh
*
* This call might reallocate skb data.
*
* Checks whether the given frame is a valid MLD report.
*
* Return: If so then true, otherwise false.
*/
static bool batadv_mcast_is_report_ipv6(struct sk_buff *skb)
{
if (ipv6_mc_check_mld(skb, NULL) < 0)
return false;
switch (icmp6_hdr(skb)->icmp6_type) {
case ICMPV6_MGM_REPORT:
case ICMPV6_MLD2_REPORT:
return true;
}
return false;
}
/** /**
* batadv_mcast_forw_mode_check_ipv6 - check for optimized forwarding potential * batadv_mcast_forw_mode_check_ipv6 - check for optimized forwarding potential
* @bat_priv: the bat priv with all the soft interface information * @bat_priv: the bat priv with all the soft interface information
...@@ -341,6 +673,9 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv, ...@@ -341,6 +673,9 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv,
if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*ip6hdr))) if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*ip6hdr)))
return -ENOMEM; return -ENOMEM;
if (batadv_mcast_is_report_ipv6(skb))
return -EINVAL;
ip6hdr = ipv6_hdr(skb); ip6hdr = ipv6_hdr(skb);
/* TODO: Implement Multicast Router Discovery (RFC4286), /* TODO: Implement Multicast Router Discovery (RFC4286),
...@@ -357,6 +692,7 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv, ...@@ -357,6 +692,7 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv,
return 0; return 0;
} }
#endif
/** /**
* batadv_mcast_forw_mode_check - check for optimized forwarding potential * batadv_mcast_forw_mode_check - check for optimized forwarding potential
...@@ -385,9 +721,11 @@ static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv, ...@@ -385,9 +721,11 @@ static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv,
case ETH_P_IP: case ETH_P_IP:
return batadv_mcast_forw_mode_check_ipv4(bat_priv, skb, return batadv_mcast_forw_mode_check_ipv4(bat_priv, skb,
is_unsnoopable); is_unsnoopable);
#if IS_ENABLED(CONFIG_IPV6)
case ETH_P_IPV6: case ETH_P_IPV6:
return batadv_mcast_forw_mode_check_ipv6(bat_priv, skb, return batadv_mcast_forw_mode_check_ipv6(bat_priv, skb,
is_unsnoopable); is_unsnoopable);
#endif
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -728,18 +1066,18 @@ static void batadv_mcast_want_ipv6_update(struct batadv_priv *bat_priv, ...@@ -728,18 +1066,18 @@ static void batadv_mcast_want_ipv6_update(struct batadv_priv *bat_priv,
} }
/** /**
* batadv_mcast_tvlv_ogm_handler_v1 - process incoming multicast tvlv container * batadv_mcast_tvlv_ogm_handler - process incoming multicast tvlv container
* @bat_priv: the bat priv with all the soft interface information * @bat_priv: the bat priv with all the soft interface information
* @orig: the orig_node of the ogm * @orig: the orig_node of the ogm
* @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags) * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
* @tvlv_value: tvlv buffer containing the multicast data * @tvlv_value: tvlv buffer containing the multicast data
* @tvlv_value_len: tvlv buffer length * @tvlv_value_len: tvlv buffer length
*/ */
static void batadv_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig, struct batadv_orig_node *orig,
u8 flags, u8 flags,
void *tvlv_value, void *tvlv_value,
u16 tvlv_value_len) u16 tvlv_value_len)
{ {
bool orig_mcast_enabled = !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND); bool orig_mcast_enabled = !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
u8 mcast_flags = BATADV_NO_FLAGS; u8 mcast_flags = BATADV_NO_FLAGS;
...@@ -789,19 +1127,120 @@ static void batadv_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, ...@@ -789,19 +1127,120 @@ static void batadv_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
*/ */
void batadv_mcast_init(struct batadv_priv *bat_priv) void batadv_mcast_init(struct batadv_priv *bat_priv)
{ {
batadv_tvlv_handler_register(bat_priv, batadv_mcast_tvlv_ogm_handler_v1, batadv_tvlv_handler_register(bat_priv, batadv_mcast_tvlv_ogm_handler,
NULL, BATADV_TVLV_MCAST, 1, NULL, BATADV_TVLV_MCAST, 2,
BATADV_TVLV_HANDLER_OGM_CIFNOTFND); BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
} }
/**
* batadv_mcast_flags_print_header - print own mcast flags to debugfs table
* @bat_priv: the bat priv with all the soft interface information
* @seq: debugfs table seq_file struct
*
* Prints our own multicast flags including a more specific reason why
* they are set, that is prints the bridge and querier state too, to
* the debugfs table specified via @seq.
*/
static void batadv_mcast_flags_print_header(struct batadv_priv *bat_priv,
struct seq_file *seq)
{
u8 flags = bat_priv->mcast.flags;
char querier4, querier6, shadowing4, shadowing6;
bool bridged = bat_priv->mcast.bridged;
if (bridged) {
querier4 = bat_priv->mcast.querier_ipv4.exists ? '.' : '4';
querier6 = bat_priv->mcast.querier_ipv6.exists ? '.' : '6';
shadowing4 = bat_priv->mcast.querier_ipv4.shadowing ? '4' : '.';
shadowing6 = bat_priv->mcast.querier_ipv6.shadowing ? '6' : '.';
} else {
querier4 = '?';
querier6 = '?';
shadowing4 = '?';
shadowing6 = '?';
}
seq_printf(seq, "Multicast flags (own flags: [%c%c%c])\n",
(flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) ? 'U' : '.',
(flags & BATADV_MCAST_WANT_ALL_IPV4) ? '4' : '.',
(flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.');
seq_printf(seq, "* Bridged [U]\t\t\t\t%c\n", bridged ? 'U' : '.');
seq_printf(seq, "* No IGMP/MLD Querier [4/6]:\t\t%c/%c\n",
querier4, querier6);
seq_printf(seq, "* Shadowing IGMP/MLD Querier [4/6]:\t%c/%c\n",
shadowing4, shadowing6);
seq_puts(seq, "-------------------------------------------\n");
seq_printf(seq, " %-10s %s\n", "Originator", "Flags");
}
/**
* batadv_mcast_flags_seq_print_text - print the mcast flags of other nodes
* @seq: seq file to print on
* @offset: not used
*
* This prints a table of (primary) originators and their according
* multicast flags, including (in the header) our own.
*
* Return: always 0
*/
int batadv_mcast_flags_seq_print_text(struct seq_file *seq, void *offset)
{
struct net_device *net_dev = (struct net_device *)seq->private;
struct batadv_priv *bat_priv = netdev_priv(net_dev);
struct batadv_hard_iface *primary_if;
struct batadv_hashtable *hash = bat_priv->orig_hash;
struct batadv_orig_node *orig_node;
struct hlist_head *head;
u8 flags;
u32 i;
primary_if = batadv_seq_print_text_primary_if_get(seq);
if (!primary_if)
return 0;
batadv_mcast_flags_print_header(bat_priv, seq);
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
rcu_read_lock();
hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
if (!test_bit(BATADV_ORIG_CAPA_HAS_MCAST,
&orig_node->capa_initialized))
continue;
if (!test_bit(BATADV_ORIG_CAPA_HAS_MCAST,
&orig_node->capabilities)) {
seq_printf(seq, "%pM -\n", orig_node->orig);
continue;
}
flags = orig_node->mcast_flags;
seq_printf(seq, "%pM [%c%c%c]\n", orig_node->orig,
(flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES)
? 'U' : '.',
(flags & BATADV_MCAST_WANT_ALL_IPV4)
? '4' : '.',
(flags & BATADV_MCAST_WANT_ALL_IPV6)
? '6' : '.');
}
rcu_read_unlock();
}
batadv_hardif_put(primary_if);
return 0;
}
/** /**
* batadv_mcast_free - free the multicast optimizations structures * batadv_mcast_free - free the multicast optimizations structures
* @bat_priv: the bat priv with all the soft interface information * @bat_priv: the bat priv with all the soft interface information
*/ */
void batadv_mcast_free(struct batadv_priv *bat_priv) void batadv_mcast_free(struct batadv_priv *bat_priv)
{ {
batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_MCAST, 1); batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_MCAST, 2);
batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_MCAST, 1); batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_MCAST, 2);
spin_lock_bh(&bat_priv->tt.commit_lock); spin_lock_bh(&bat_priv->tt.commit_lock);
batadv_mcast_mla_tt_retract(bat_priv, NULL); batadv_mcast_mla_tt_retract(bat_priv, NULL);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "main.h" #include "main.h"
struct seq_file;
struct sk_buff; struct sk_buff;
/** /**
...@@ -46,6 +47,8 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb, ...@@ -46,6 +47,8 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
void batadv_mcast_init(struct batadv_priv *bat_priv); void batadv_mcast_init(struct batadv_priv *bat_priv);
int batadv_mcast_flags_seq_print_text(struct seq_file *seq, void *offset);
void batadv_mcast_free(struct batadv_priv *bat_priv); void batadv_mcast_free(struct batadv_priv *bat_priv);
void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node); void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node);
......
...@@ -251,10 +251,8 @@ static void batadv_neigh_node_release(struct kref *ref) ...@@ -251,10 +251,8 @@ static void batadv_neigh_node_release(struct kref *ref)
struct hlist_node *node_tmp; struct hlist_node *node_tmp;
struct batadv_neigh_node *neigh_node; struct batadv_neigh_node *neigh_node;
struct batadv_neigh_ifinfo *neigh_ifinfo; struct batadv_neigh_ifinfo *neigh_ifinfo;
struct batadv_algo_ops *bao;
neigh_node = container_of(ref, struct batadv_neigh_node, refcount); neigh_node = container_of(ref, struct batadv_neigh_node, refcount);
bao = neigh_node->orig_node->bat_priv->bat_algo_ops;
hlist_for_each_entry_safe(neigh_ifinfo, node_tmp, hlist_for_each_entry_safe(neigh_ifinfo, node_tmp,
&neigh_node->ifinfo_list, list) { &neigh_node->ifinfo_list, list) {
...@@ -263,9 +261,6 @@ static void batadv_neigh_node_release(struct kref *ref) ...@@ -263,9 +261,6 @@ static void batadv_neigh_node_release(struct kref *ref)
batadv_hardif_neigh_put(neigh_node->hardif_neigh); batadv_hardif_neigh_put(neigh_node->hardif_neigh);
if (bao->bat_neigh_free)
bao->bat_neigh_free(neigh_node);
batadv_hardif_put(neigh_node->if_incoming); batadv_hardif_put(neigh_node->if_incoming);
kfree_rcu(neigh_node, rcu); kfree_rcu(neigh_node, rcu);
...@@ -602,19 +597,19 @@ batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface, ...@@ -602,19 +597,19 @@ batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface,
} }
/** /**
* batadv_neigh_node_new - create and init a new neigh_node object * batadv_neigh_node_create - create a neigh node object
* @orig_node: originator object representing the neighbour * @orig_node: originator object representing the neighbour
* @hard_iface: the interface where the neighbour is connected to * @hard_iface: the interface where the neighbour is connected to
* @neigh_addr: the mac address of the neighbour interface * @neigh_addr: the mac address of the neighbour interface
* *
* Allocates a new neigh_node object and initialises all the generic fields. * Allocates a new neigh_node object and initialises all the generic fields.
* *
* Return: neighbor when found. Othwerwise NULL * Return: the neighbour node if found or created or NULL otherwise.
*/ */
struct batadv_neigh_node * static struct batadv_neigh_node *
batadv_neigh_node_new(struct batadv_orig_node *orig_node, batadv_neigh_node_create(struct batadv_orig_node *orig_node,
struct batadv_hard_iface *hard_iface, struct batadv_hard_iface *hard_iface,
const u8 *neigh_addr) const u8 *neigh_addr)
{ {
struct batadv_neigh_node *neigh_node; struct batadv_neigh_node *neigh_node;
struct batadv_hardif_neigh_node *hardif_neigh = NULL; struct batadv_hardif_neigh_node *hardif_neigh = NULL;
...@@ -666,6 +661,29 @@ batadv_neigh_node_new(struct batadv_orig_node *orig_node, ...@@ -666,6 +661,29 @@ batadv_neigh_node_new(struct batadv_orig_node *orig_node,
return neigh_node; return neigh_node;
} }
/**
* batadv_neigh_node_get_or_create - retrieve or create a neigh node object
* @orig_node: originator object representing the neighbour
* @hard_iface: the interface where the neighbour is connected to
* @neigh_addr: the mac address of the neighbour interface
*
* Return: the neighbour node if found or created or NULL otherwise.
*/
struct batadv_neigh_node *
batadv_neigh_node_get_or_create(struct batadv_orig_node *orig_node,
struct batadv_hard_iface *hard_iface,
const u8 *neigh_addr)
{
struct batadv_neigh_node *neigh_node = NULL;
/* first check without locking to avoid the overhead */
neigh_node = batadv_neigh_node_get(orig_node, hard_iface, neigh_addr);
if (neigh_node)
return neigh_node;
return batadv_neigh_node_create(orig_node, hard_iface, neigh_addr);
}
/** /**
* batadv_hardif_neigh_seq_print_text - print the single hop neighbour list * batadv_hardif_neigh_seq_print_text - print the single hop neighbour list
* @seq: neighbour table seq_file struct * @seq: neighbour table seq_file struct
......
...@@ -46,9 +46,9 @@ batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface, ...@@ -46,9 +46,9 @@ batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface,
void void
batadv_hardif_neigh_put(struct batadv_hardif_neigh_node *hardif_neigh); batadv_hardif_neigh_put(struct batadv_hardif_neigh_node *hardif_neigh);
struct batadv_neigh_node * struct batadv_neigh_node *
batadv_neigh_node_new(struct batadv_orig_node *orig_node, batadv_neigh_node_get_or_create(struct batadv_orig_node *orig_node,
struct batadv_hard_iface *hard_iface, struct batadv_hard_iface *hard_iface,
const u8 *neigh_addr); const u8 *neigh_addr);
void batadv_neigh_node_put(struct batadv_neigh_node *neigh_node); void batadv_neigh_node_put(struct batadv_neigh_node *neigh_node);
struct batadv_neigh_node * struct batadv_neigh_node *
batadv_orig_router_get(struct batadv_orig_node *orig_node, batadv_orig_router_get(struct batadv_orig_node *orig_node,
......
...@@ -420,6 +420,7 @@ struct batadv_unicast_4addr_packet { ...@@ -420,6 +420,7 @@ struct batadv_unicast_4addr_packet {
* @dest: final destination used when routing fragments * @dest: final destination used when routing fragments
* @orig: originator of the fragment used when merging the packet * @orig: originator of the fragment used when merging the packet
* @no: fragment number within this sequence * @no: fragment number within this sequence
* @priority: priority of frame, from ToS IP precedence or 802.1p
* @reserved: reserved byte for alignment * @reserved: reserved byte for alignment
* @seqno: sequence identification * @seqno: sequence identification
* @total_size: size of the merged packet * @total_size: size of the merged packet
...@@ -430,9 +431,11 @@ struct batadv_frag_packet { ...@@ -430,9 +431,11 @@ struct batadv_frag_packet {
u8 ttl; u8 ttl;
#if defined(__BIG_ENDIAN_BITFIELD) #if defined(__BIG_ENDIAN_BITFIELD)
u8 no:4; u8 no:4;
u8 reserved:4; u8 priority:3;
u8 reserved:1;
#elif defined(__LITTLE_ENDIAN_BITFIELD) #elif defined(__LITTLE_ENDIAN_BITFIELD)
u8 reserved:4; u8 reserved:1;
u8 priority:3;
u8 no:4; u8 no:4;
#else #else
#error "unknown bitfield endianness" #error "unknown bitfield endianness"
......
...@@ -1007,6 +1007,8 @@ int batadv_recv_frag_packet(struct sk_buff *skb, ...@@ -1007,6 +1007,8 @@ int batadv_recv_frag_packet(struct sk_buff *skb,
if (!orig_node_src) if (!orig_node_src)
goto out; goto out;
skb->priority = frag_packet->priority + 256;
/* Route the fragment if it is not for us and too big to be merged. */ /* Route the fragment if it is not for us and too big to be merged. */
if (!batadv_is_my_mac(bat_priv, frag_packet->dest) && if (!batadv_is_my_mac(bat_priv, frag_packet->dest) &&
batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) { batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) {
......
...@@ -428,27 +428,7 @@ int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb, ...@@ -428,27 +428,7 @@ int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb,
orig_node, vid); orig_node, vid);
} }
void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface) void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
if ((hard_iface->if_status == BATADV_IF_NOT_IN_USE) ||
(hard_iface->if_status == BATADV_IF_TO_BE_REMOVED))
return;
/* the interface gets activated here to avoid race conditions between
* the moment of activating the interface in
* hardif_activate_interface() where the originator mac is set and
* outdated packets (especially uninitialized mac addresses) in the
* packet queue
*/
if (hard_iface->if_status == BATADV_IF_TO_BE_ACTIVATED)
hard_iface->if_status = BATADV_IF_ACTIVE;
bat_priv->bat_algo_ops->bat_ogm_schedule(hard_iface);
}
static void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet)
{ {
kfree_skb(forw_packet->skb); kfree_skb(forw_packet->skb);
if (forw_packet->if_incoming) if (forw_packet->if_incoming)
...@@ -604,45 +584,6 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) ...@@ -604,45 +584,6 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work)
atomic_inc(&bat_priv->bcast_queue_left); atomic_inc(&bat_priv->bcast_queue_left);
} }
void batadv_send_outstanding_bat_ogm_packet(struct work_struct *work)
{
struct delayed_work *delayed_work;
struct batadv_forw_packet *forw_packet;
struct batadv_priv *bat_priv;
delayed_work = to_delayed_work(work);
forw_packet = container_of(delayed_work, struct batadv_forw_packet,
delayed_work);
bat_priv = netdev_priv(forw_packet->if_incoming->soft_iface);
spin_lock_bh(&bat_priv->forw_bat_list_lock);
hlist_del(&forw_packet->list);
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
goto out;
bat_priv->bat_algo_ops->bat_ogm_emit(forw_packet);
/* we have to have at least one packet in the queue to determine the
* queues wake up time unless we are shutting down.
*
* only re-schedule if this is the "original" copy, e.g. the OGM of the
* primary interface should only be rescheduled once per period, but
* this function will be called for the forw_packet instances of the
* other secondary interfaces as well.
*/
if (forw_packet->own &&
forw_packet->if_incoming == forw_packet->if_outgoing)
batadv_schedule_bat_ogm(forw_packet->if_incoming);
out:
/* don't count own packet */
if (!forw_packet->own)
atomic_inc(&bat_priv->batman_queue_left);
batadv_forw_packet_free(forw_packet);
}
void void
batadv_purge_outstanding_packets(struct batadv_priv *bat_priv, batadv_purge_outstanding_packets(struct batadv_priv *bat_priv,
const struct batadv_hard_iface *hard_iface) const struct batadv_hard_iface *hard_iface)
......
...@@ -26,8 +26,8 @@ ...@@ -26,8 +26,8 @@
#include "packet.h" #include "packet.h"
struct sk_buff; struct sk_buff;
struct work_struct;
void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet);
int batadv_send_skb_to_orig(struct sk_buff *skb, int batadv_send_skb_to_orig(struct sk_buff *skb,
struct batadv_orig_node *orig_node, struct batadv_orig_node *orig_node,
struct batadv_hard_iface *recv_if); struct batadv_hard_iface *recv_if);
...@@ -38,11 +38,9 @@ int batadv_send_broadcast_skb(struct sk_buff *skb, ...@@ -38,11 +38,9 @@ int batadv_send_broadcast_skb(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface); struct batadv_hard_iface *hard_iface);
int batadv_send_unicast_skb(struct sk_buff *skb, int batadv_send_unicast_skb(struct sk_buff *skb,
struct batadv_neigh_node *neigh_node); struct batadv_neigh_node *neigh_node);
void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface);
int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
const struct sk_buff *skb, const struct sk_buff *skb,
unsigned long delay); unsigned long delay);
void batadv_send_outstanding_bat_ogm_packet(struct work_struct *work);
void void
batadv_purge_outstanding_packets(struct batadv_priv *bat_priv, batadv_purge_outstanding_packets(struct batadv_priv *bat_priv,
const struct batadv_hard_iface *hard_iface); const struct batadv_hard_iface *hard_iface);
......
...@@ -255,7 +255,7 @@ static int batadv_interface_tx(struct sk_buff *skb, ...@@ -255,7 +255,7 @@ static int batadv_interface_tx(struct sk_buff *skb,
if (batadv_compare_eth(ethhdr->h_dest, ectp_addr)) if (batadv_compare_eth(ethhdr->h_dest, ectp_addr))
goto dropped; goto dropped;
gw_mode = atomic_read(&bat_priv->gw_mode); gw_mode = atomic_read(&bat_priv->gw.mode);
if (is_multicast_ether_addr(ethhdr->h_dest)) { if (is_multicast_ether_addr(ethhdr->h_dest)) {
/* if gw mode is off, broadcast every packet */ /* if gw mode is off, broadcast every packet */
if (gw_mode == BATADV_GW_MODE_OFF) { if (gw_mode == BATADV_GW_MODE_OFF) {
...@@ -808,6 +808,10 @@ static int batadv_softif_init_late(struct net_device *dev) ...@@ -808,6 +808,10 @@ static int batadv_softif_init_late(struct net_device *dev)
atomic_set(&bat_priv->distributed_arp_table, 1); atomic_set(&bat_priv->distributed_arp_table, 1);
#endif #endif
#ifdef CONFIG_BATMAN_ADV_MCAST #ifdef CONFIG_BATMAN_ADV_MCAST
bat_priv->mcast.querier_ipv4.exists = false;
bat_priv->mcast.querier_ipv4.shadowing = false;
bat_priv->mcast.querier_ipv6.exists = false;
bat_priv->mcast.querier_ipv6.shadowing = false;
bat_priv->mcast.flags = BATADV_NO_FLAGS; bat_priv->mcast.flags = BATADV_NO_FLAGS;
atomic_set(&bat_priv->multicast_mode, 1); atomic_set(&bat_priv->multicast_mode, 1);
atomic_set(&bat_priv->mcast.num_disabled, 0); atomic_set(&bat_priv->mcast.num_disabled, 0);
...@@ -815,8 +819,8 @@ static int batadv_softif_init_late(struct net_device *dev) ...@@ -815,8 +819,8 @@ static int batadv_softif_init_late(struct net_device *dev)
atomic_set(&bat_priv->mcast.num_want_all_ipv4, 0); atomic_set(&bat_priv->mcast.num_want_all_ipv4, 0);
atomic_set(&bat_priv->mcast.num_want_all_ipv6, 0); atomic_set(&bat_priv->mcast.num_want_all_ipv6, 0);
#endif #endif
atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF); atomic_set(&bat_priv->gw.mode, BATADV_GW_MODE_OFF);
atomic_set(&bat_priv->gw_sel_class, 20); atomic_set(&bat_priv->gw.sel_class, 20);
atomic_set(&bat_priv->gw.bandwidth_down, 100); atomic_set(&bat_priv->gw.bandwidth_down, 100);
atomic_set(&bat_priv->gw.bandwidth_up, 20); atomic_set(&bat_priv->gw.bandwidth_up, 20);
atomic_set(&bat_priv->orig_interval, 1000); atomic_set(&bat_priv->orig_interval, 1000);
......
...@@ -389,12 +389,12 @@ static int batadv_store_uint_attr(const char *buff, size_t count, ...@@ -389,12 +389,12 @@ static int batadv_store_uint_attr(const char *buff, size_t count,
return count; return count;
} }
static inline ssize_t static ssize_t __batadv_store_uint_attr(const char *buff, size_t count,
__batadv_store_uint_attr(const char *buff, size_t count, int min, int max,
int min, int max, void (*post_func)(struct net_device *),
void (*post_func)(struct net_device *), const struct attribute *attr,
const struct attribute *attr, atomic_t *attr_store,
atomic_t *attr_store, struct net_device *net_dev) struct net_device *net_dev)
{ {
int ret; int ret;
...@@ -427,7 +427,7 @@ static ssize_t batadv_show_gw_mode(struct kobject *kobj, struct attribute *attr, ...@@ -427,7 +427,7 @@ static ssize_t batadv_show_gw_mode(struct kobject *kobj, struct attribute *attr,
struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
int bytes_written; int bytes_written;
switch (atomic_read(&bat_priv->gw_mode)) { switch (atomic_read(&bat_priv->gw.mode)) {
case BATADV_GW_MODE_CLIENT: case BATADV_GW_MODE_CLIENT:
bytes_written = sprintf(buff, "%s\n", bytes_written = sprintf(buff, "%s\n",
BATADV_GW_MODE_CLIENT_NAME); BATADV_GW_MODE_CLIENT_NAME);
...@@ -476,10 +476,10 @@ static ssize_t batadv_store_gw_mode(struct kobject *kobj, ...@@ -476,10 +476,10 @@ static ssize_t batadv_store_gw_mode(struct kobject *kobj,
return -EINVAL; return -EINVAL;
} }
if (atomic_read(&bat_priv->gw_mode) == gw_mode_tmp) if (atomic_read(&bat_priv->gw.mode) == gw_mode_tmp)
return count; return count;
switch (atomic_read(&bat_priv->gw_mode)) { switch (atomic_read(&bat_priv->gw.mode)) {
case BATADV_GW_MODE_CLIENT: case BATADV_GW_MODE_CLIENT:
curr_gw_mode_str = BATADV_GW_MODE_CLIENT_NAME; curr_gw_mode_str = BATADV_GW_MODE_CLIENT_NAME;
break; break;
...@@ -508,7 +508,7 @@ static ssize_t batadv_store_gw_mode(struct kobject *kobj, ...@@ -508,7 +508,7 @@ static ssize_t batadv_store_gw_mode(struct kobject *kobj,
* state * state
*/ */
batadv_gw_check_client_stop(bat_priv); batadv_gw_check_client_stop(bat_priv);
atomic_set(&bat_priv->gw_mode, (unsigned int)gw_mode_tmp); atomic_set(&bat_priv->gw.mode, (unsigned int)gw_mode_tmp);
batadv_gw_tvlv_container_update(bat_priv); batadv_gw_tvlv_container_update(bat_priv);
return count; return count;
} }
...@@ -624,7 +624,7 @@ BATADV_ATTR_SIF_UINT(orig_interval, orig_interval, S_IRUGO | S_IWUSR, ...@@ -624,7 +624,7 @@ BATADV_ATTR_SIF_UINT(orig_interval, orig_interval, S_IRUGO | S_IWUSR,
2 * BATADV_JITTER, INT_MAX, NULL); 2 * BATADV_JITTER, INT_MAX, NULL);
BATADV_ATTR_SIF_UINT(hop_penalty, hop_penalty, S_IRUGO | S_IWUSR, 0, BATADV_ATTR_SIF_UINT(hop_penalty, hop_penalty, S_IRUGO | S_IWUSR, 0,
BATADV_TQ_MAX_VALUE, NULL); BATADV_TQ_MAX_VALUE, NULL);
BATADV_ATTR_SIF_UINT(gw_sel_class, gw_sel_class, S_IRUGO | S_IWUSR, 1, BATADV_ATTR_SIF_UINT(gw_sel_class, gw.sel_class, S_IRUGO | S_IWUSR, 1,
BATADV_TQ_MAX_VALUE, batadv_post_gw_reselect); BATADV_TQ_MAX_VALUE, batadv_post_gw_reselect);
static BATADV_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, batadv_show_gw_bwidth, static BATADV_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, batadv_show_gw_bwidth,
batadv_store_gw_bwidth); batadv_store_gw_bwidth);
......
...@@ -996,7 +996,6 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) ...@@ -996,7 +996,6 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
struct batadv_tt_local_entry *tt_local; struct batadv_tt_local_entry *tt_local;
struct batadv_hard_iface *primary_if; struct batadv_hard_iface *primary_if;
struct hlist_head *head; struct hlist_head *head;
unsigned short vid;
u32 i; u32 i;
int last_seen_secs; int last_seen_secs;
int last_seen_msecs; int last_seen_msecs;
...@@ -1023,7 +1022,6 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) ...@@ -1023,7 +1022,6 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
tt_local = container_of(tt_common_entry, tt_local = container_of(tt_common_entry,
struct batadv_tt_local_entry, struct batadv_tt_local_entry,
common); common);
vid = tt_common_entry->vid;
last_seen_jiffies = jiffies - tt_local->last_seen; last_seen_jiffies = jiffies - tt_local->last_seen;
last_seen_msecs = jiffies_to_msecs(last_seen_jiffies); last_seen_msecs = jiffies_to_msecs(last_seen_jiffies);
last_seen_secs = last_seen_msecs / 1000; last_seen_secs = last_seen_msecs / 1000;
......
...@@ -707,6 +707,8 @@ struct batadv_priv_debug_log { ...@@ -707,6 +707,8 @@ struct batadv_priv_debug_log {
* @list: list of available gateway nodes * @list: list of available gateway nodes
* @list_lock: lock protecting gw_list & curr_gw * @list_lock: lock protecting gw_list & curr_gw
* @curr_gw: pointer to currently selected gateway node * @curr_gw: pointer to currently selected gateway node
* @mode: gateway operation: off, client or server (see batadv_gw_modes)
* @sel_class: gateway selection class (applies if gw_mode client)
* @bandwidth_down: advertised uplink download bandwidth (if gw_mode server) * @bandwidth_down: advertised uplink download bandwidth (if gw_mode server)
* @bandwidth_up: advertised uplink upload bandwidth (if gw_mode server) * @bandwidth_up: advertised uplink upload bandwidth (if gw_mode server)
* @reselect: bool indicating a gateway re-selection is in progress * @reselect: bool indicating a gateway re-selection is in progress
...@@ -715,6 +717,8 @@ struct batadv_priv_gw { ...@@ -715,6 +717,8 @@ struct batadv_priv_gw {
struct hlist_head list; struct hlist_head list;
spinlock_t list_lock; /* protects gw_list & curr_gw */ spinlock_t list_lock; /* protects gw_list & curr_gw */
struct batadv_gw_node __rcu *curr_gw; /* rcu protected pointer */ struct batadv_gw_node __rcu *curr_gw; /* rcu protected pointer */
atomic_t mode;
atomic_t sel_class;
atomic_t bandwidth_down; atomic_t bandwidth_down;
atomic_t bandwidth_up; atomic_t bandwidth_up;
atomic_t reselect; atomic_t reselect;
...@@ -750,6 +754,17 @@ struct batadv_priv_dat { ...@@ -750,6 +754,17 @@ struct batadv_priv_dat {
#endif #endif
#ifdef CONFIG_BATMAN_ADV_MCAST #ifdef CONFIG_BATMAN_ADV_MCAST
/**
* struct batadv_mcast_querier_state - IGMP/MLD querier state when bridged
* @exists: whether a querier exists in the mesh
* @shadowing: if a querier exists, whether it is potentially shadowing
* multicast listeners (i.e. querier is behind our own bridge segment)
*/
struct batadv_mcast_querier_state {
bool exists;
bool shadowing;
};
/** /**
* struct batadv_priv_mcast - per mesh interface mcast data * struct batadv_priv_mcast - per mesh interface mcast data
* @mla_list: list of multicast addresses we are currently announcing via TT * @mla_list: list of multicast addresses we are currently announcing via TT
...@@ -757,8 +772,11 @@ struct batadv_priv_dat { ...@@ -757,8 +772,11 @@ struct batadv_priv_dat {
* multicast traffic * multicast traffic
* @want_all_ipv4_list: a list of orig_nodes wanting all IPv4 multicast traffic * @want_all_ipv4_list: a list of orig_nodes wanting all IPv4 multicast traffic
* @want_all_ipv6_list: a list of orig_nodes wanting all IPv6 multicast traffic * @want_all_ipv6_list: a list of orig_nodes wanting all IPv6 multicast traffic
* @querier_ipv4: the current state of an IGMP querier in the mesh
* @querier_ipv6: the current state of an MLD querier in the mesh
* @flags: the flags we have last sent in our mcast tvlv * @flags: the flags we have last sent in our mcast tvlv
* @enabled: whether the multicast tvlv is currently enabled * @enabled: whether the multicast tvlv is currently enabled
* @bridged: whether the soft interface has a bridge on top
* @num_disabled: number of nodes that have no mcast tvlv * @num_disabled: number of nodes that have no mcast tvlv
* @num_want_all_unsnoopables: number of nodes wanting unsnoopable IP traffic * @num_want_all_unsnoopables: number of nodes wanting unsnoopable IP traffic
* @num_want_all_ipv4: counter for items in want_all_ipv4_list * @num_want_all_ipv4: counter for items in want_all_ipv4_list
...@@ -771,8 +789,11 @@ struct batadv_priv_mcast { ...@@ -771,8 +789,11 @@ struct batadv_priv_mcast {
struct hlist_head want_all_unsnoopables_list; struct hlist_head want_all_unsnoopables_list;
struct hlist_head want_all_ipv4_list; struct hlist_head want_all_ipv4_list;
struct hlist_head want_all_ipv6_list; struct hlist_head want_all_ipv6_list;
struct batadv_mcast_querier_state querier_ipv4;
struct batadv_mcast_querier_state querier_ipv6;
u8 flags; u8 flags;
bool enabled; bool enabled;
bool bridged;
atomic_t num_disabled; atomic_t num_disabled;
atomic_t num_want_all_unsnoopables; atomic_t num_want_all_unsnoopables;
atomic_t num_want_all_ipv4; atomic_t num_want_all_ipv4;
...@@ -865,8 +886,6 @@ struct batadv_priv_bat_v { ...@@ -865,8 +886,6 @@ struct batadv_priv_bat_v {
* enabled * enabled
* @multicast_mode: Enable or disable multicast optimizations on this node's * @multicast_mode: Enable or disable multicast optimizations on this node's
* sender/originating side * sender/originating side
* @gw_mode: gateway operation: off, client or server (see batadv_gw_modes)
* @gw_sel_class: gateway selection class (applies if gw_mode client)
* @orig_interval: OGM broadcast interval in milliseconds * @orig_interval: OGM broadcast interval in milliseconds
* @hop_penalty: penalty which will be applied to an OGM's tq-field on every hop * @hop_penalty: penalty which will be applied to an OGM's tq-field on every hop
* @log_level: configured log level (see batadv_dbg_level) * @log_level: configured log level (see batadv_dbg_level)
...@@ -922,8 +941,6 @@ struct batadv_priv { ...@@ -922,8 +941,6 @@ struct batadv_priv {
#ifdef CONFIG_BATMAN_ADV_MCAST #ifdef CONFIG_BATMAN_ADV_MCAST
atomic_t multicast_mode; atomic_t multicast_mode;
#endif #endif
atomic_t gw_mode;
atomic_t gw_sel_class;
atomic_t orig_interval; atomic_t orig_interval;
atomic_t hop_penalty; atomic_t hop_penalty;
#ifdef CONFIG_BATMAN_ADV_DEBUG #ifdef CONFIG_BATMAN_ADV_DEBUG
...@@ -1271,8 +1288,6 @@ struct batadv_forw_packet { ...@@ -1271,8 +1288,6 @@ struct batadv_forw_packet {
* @bat_iface_update_mac: (re-)init mac addresses of the protocol information * @bat_iface_update_mac: (re-)init mac addresses of the protocol information
* belonging to this hard-interface * belonging to this hard-interface
* @bat_primary_iface_set: called when primary interface is selected / changed * @bat_primary_iface_set: called when primary interface is selected / changed
* @bat_ogm_schedule: prepare a new outgoing OGM for the send queue
* @bat_ogm_emit: send scheduled OGM
* @bat_hardif_neigh_init: called on creation of single hop entry * @bat_hardif_neigh_init: called on creation of single hop entry
* @bat_neigh_cmp: compare the metrics of two neighbors for their respective * @bat_neigh_cmp: compare the metrics of two neighbors for their respective
* outgoing interfaces * outgoing interfaces
...@@ -1280,8 +1295,6 @@ struct batadv_forw_packet { ...@@ -1280,8 +1295,6 @@ struct batadv_forw_packet {
* better than neigh2 for their respective outgoing interface from the metric * better than neigh2 for their respective outgoing interface from the metric
* prospective * prospective
* @bat_neigh_print: print the single hop neighbor list (optional) * @bat_neigh_print: print the single hop neighbor list (optional)
* @bat_neigh_free: free the resources allocated by the routing algorithm for a
* neigh_node object
* @bat_orig_print: print the originator table (optional) * @bat_orig_print: print the originator table (optional)
* @bat_orig_free: free the resources allocated by the routing algorithm for an * @bat_orig_free: free the resources allocated by the routing algorithm for an
* orig_node object * orig_node object
...@@ -1298,8 +1311,6 @@ struct batadv_algo_ops { ...@@ -1298,8 +1311,6 @@ struct batadv_algo_ops {
void (*bat_iface_disable)(struct batadv_hard_iface *hard_iface); void (*bat_iface_disable)(struct batadv_hard_iface *hard_iface);
void (*bat_iface_update_mac)(struct batadv_hard_iface *hard_iface); void (*bat_iface_update_mac)(struct batadv_hard_iface *hard_iface);
void (*bat_primary_iface_set)(struct batadv_hard_iface *hard_iface); void (*bat_primary_iface_set)(struct batadv_hard_iface *hard_iface);
void (*bat_ogm_schedule)(struct batadv_hard_iface *hard_iface);
void (*bat_ogm_emit)(struct batadv_forw_packet *forw_packet);
/* neigh_node handling API */ /* neigh_node handling API */
void (*bat_hardif_neigh_init)(struct batadv_hardif_neigh_node *neigh); void (*bat_hardif_neigh_init)(struct batadv_hardif_neigh_node *neigh);
int (*bat_neigh_cmp)(struct batadv_neigh_node *neigh1, int (*bat_neigh_cmp)(struct batadv_neigh_node *neigh1,
...@@ -1312,7 +1323,6 @@ struct batadv_algo_ops { ...@@ -1312,7 +1323,6 @@ struct batadv_algo_ops {
struct batadv_neigh_node *neigh2, struct batadv_neigh_node *neigh2,
struct batadv_hard_iface *if_outgoing2); struct batadv_hard_iface *if_outgoing2);
void (*bat_neigh_print)(struct batadv_priv *priv, struct seq_file *seq); void (*bat_neigh_print)(struct batadv_priv *priv, struct seq_file *seq);
void (*bat_neigh_free)(struct batadv_neigh_node *neigh);
/* orig_node handling API */ /* orig_node handling API */
void (*bat_orig_print)(struct batadv_priv *priv, struct seq_file *seq, void (*bat_orig_print)(struct batadv_priv *priv, struct seq_file *seq,
struct batadv_hard_iface *hard_iface); struct batadv_hard_iface *hard_iface);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment