mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-09 01:28:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
69
net/batman-adv/Kconfig
Normal file
69
net/batman-adv/Kconfig
Normal file
|
@ -0,0 +1,69 @@
|
|||
#
|
||||
# B.A.T.M.A.N meshing protocol
|
||||
#
|
||||
|
||||
config BATMAN_ADV
|
||||
tristate "B.A.T.M.A.N. Advanced Meshing Protocol"
|
||||
depends on NET
|
||||
select CRC16
|
||||
select LIBCRC32C
|
||||
default n
|
||||
help
|
||||
B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is
|
||||
a routing protocol for multi-hop ad-hoc mesh networks. The
|
||||
networks may be wired or wireless. See
|
||||
http://www.open-mesh.org/ for more information and user space
|
||||
tools.
|
||||
|
||||
config BATMAN_ADV_BLA
|
||||
bool "Bridge Loop Avoidance"
|
||||
depends on BATMAN_ADV && INET
|
||||
default y
|
||||
help
|
||||
This option enables BLA (Bridge Loop Avoidance), a mechanism
|
||||
to avoid Ethernet frames looping when mesh nodes are connected
|
||||
to both the same LAN and the same mesh. If you will never use
|
||||
more than one mesh node in the same LAN, you can safely remove
|
||||
this feature and save some space.
|
||||
|
||||
config BATMAN_ADV_DAT
|
||||
bool "Distributed ARP Table"
|
||||
depends on BATMAN_ADV && INET
|
||||
default n
|
||||
help
|
||||
This option enables DAT (Distributed ARP Table), a DHT based
|
||||
mechanism that increases ARP reliability on sparse wireless
|
||||
mesh networks. If you think that your network does not need
|
||||
this option you can safely remove it and save some space.
|
||||
|
||||
config BATMAN_ADV_NC
|
||||
bool "Network Coding"
|
||||
depends on BATMAN_ADV
|
||||
default n
|
||||
help
|
||||
This option enables network coding, a mechanism that aims to
|
||||
increase the overall network throughput by fusing multiple
|
||||
packets in one transmission.
|
||||
Note that interfaces controlled by batman-adv must be manually
|
||||
configured to have promiscuous mode enabled in order to make
|
||||
network coding work.
|
||||
If you think that your network does not need this feature you
|
||||
can safely disable it and save some space.
|
||||
|
||||
config BATMAN_ADV_MCAST
|
||||
bool "Multicast optimisation"
|
||||
depends on BATMAN_ADV
|
||||
default n
|
||||
help
|
||||
This option enables the multicast optimisation which aims to
|
||||
reduce the air overhead while improving the reliability of
|
||||
multicast messages.
|
||||
|
||||
config BATMAN_ADV_DEBUG
|
||||
bool "B.A.T.M.A.N. debugging"
|
||||
depends on BATMAN_ADV
|
||||
help
|
||||
This is an option for use by developers; most people should
|
||||
say N here. This enables compilation of support for
|
||||
outputting debugging information to the kernel log. The
|
||||
output is controlled via the module parameter debug.
|
39
net/batman-adv/Makefile
Normal file
39
net/batman-adv/Makefile
Normal file
|
@ -0,0 +1,39 @@
|
|||
#
|
||||
# Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
#
|
||||
# Marek Lindner, Simon Wunderlich
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
# License as published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_BATMAN_ADV) += batman-adv.o
|
||||
batman-adv-y += bat_iv_ogm.o
|
||||
batman-adv-y += bitarray.o
|
||||
batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o
|
||||
batman-adv-y += debugfs.o
|
||||
batman-adv-$(CONFIG_BATMAN_ADV_DAT) += distributed-arp-table.o
|
||||
batman-adv-y += fragmentation.o
|
||||
batman-adv-y += gateway_client.o
|
||||
batman-adv-y += gateway_common.o
|
||||
batman-adv-y += hard-interface.o
|
||||
batman-adv-y += hash.o
|
||||
batman-adv-y += icmp_socket.o
|
||||
batman-adv-y += main.o
|
||||
batman-adv-$(CONFIG_BATMAN_ADV_NC) += network-coding.o
|
||||
batman-adv-y += originator.o
|
||||
batman-adv-y += routing.o
|
||||
batman-adv-y += send.o
|
||||
batman-adv-y += soft-interface.o
|
||||
batman-adv-y += sysfs.o
|
||||
batman-adv-y += translation-table.o
|
||||
batman-adv-$(CONFIG_BATMAN_ADV_MCAST) += multicast.o
|
23
net/batman-adv/bat_algo.h
Normal file
23
net/batman-adv/bat_algo.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/* Copyright (C) 2011-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_BAT_ALGO_H_
|
||||
#define _NET_BATMAN_ADV_BAT_ALGO_H_
|
||||
|
||||
int batadv_iv_init(void);
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_BAT_ALGO_H_ */
|
1980
net/batman-adv/bat_iv_ogm.c
Normal file
1980
net/batman-adv/bat_iv_ogm.c
Normal file
File diff suppressed because it is too large
Load diff
93
net/batman-adv/bitarray.c
Normal file
93
net/batman-adv/bitarray.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
/* Copyright (C) 2006-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Simon Wunderlich, Marek Lindner
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "bitarray.h"
|
||||
|
||||
#include <linux/bitops.h>
|
||||
|
||||
/* shift the packet array by n places. */
|
||||
static void batadv_bitmap_shift_left(unsigned long *seq_bits, int32_t n)
|
||||
{
|
||||
if (n <= 0 || n >= BATADV_TQ_LOCAL_WINDOW_SIZE)
|
||||
return;
|
||||
|
||||
bitmap_shift_left(seq_bits, seq_bits, n, BATADV_TQ_LOCAL_WINDOW_SIZE);
|
||||
}
|
||||
|
||||
|
||||
/* receive and process one packet within the sequence number window.
|
||||
*
|
||||
* returns:
|
||||
* 1 if the window was moved (either new or very old)
|
||||
* 0 if the window was not moved/shifted.
|
||||
*/
|
||||
int batadv_bit_get_packet(void *priv, unsigned long *seq_bits,
|
||||
int32_t seq_num_diff, int set_mark)
|
||||
{
|
||||
struct batadv_priv *bat_priv = priv;
|
||||
|
||||
/* sequence number is slightly older. We already got a sequence number
|
||||
* higher than this one, so we just mark it.
|
||||
*/
|
||||
if (seq_num_diff <= 0 && seq_num_diff > -BATADV_TQ_LOCAL_WINDOW_SIZE) {
|
||||
if (set_mark)
|
||||
batadv_set_bit(seq_bits, -seq_num_diff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sequence number is slightly newer, so we shift the window and
|
||||
* set the mark if required
|
||||
*/
|
||||
if (seq_num_diff > 0 && seq_num_diff < BATADV_TQ_LOCAL_WINDOW_SIZE) {
|
||||
batadv_bitmap_shift_left(seq_bits, seq_num_diff);
|
||||
|
||||
if (set_mark)
|
||||
batadv_set_bit(seq_bits, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* sequence number is much newer, probably missed a lot of packets */
|
||||
if (seq_num_diff >= BATADV_TQ_LOCAL_WINDOW_SIZE &&
|
||||
seq_num_diff < BATADV_EXPECTED_SEQNO_RANGE) {
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"We missed a lot of packets (%i) !\n",
|
||||
seq_num_diff - 1);
|
||||
bitmap_zero(seq_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
|
||||
if (set_mark)
|
||||
batadv_set_bit(seq_bits, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* received a much older packet. The other host either restarted
|
||||
* or the old packet got delayed somewhere in the network. The
|
||||
* packet should be dropped without calling this function if the
|
||||
* seqno window is protected.
|
||||
*
|
||||
* seq_num_diff <= -BATADV_TQ_LOCAL_WINDOW_SIZE
|
||||
* or
|
||||
* seq_num_diff >= BATADV_EXPECTED_SEQNO_RANGE
|
||||
*/
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"Other host probably restarted!\n");
|
||||
|
||||
bitmap_zero(seq_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
|
||||
if (set_mark)
|
||||
batadv_set_bit(seq_bits, 0);
|
||||
|
||||
return 1;
|
||||
}
|
52
net/batman-adv/bitarray.h
Normal file
52
net/batman-adv/bitarray.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/* Copyright (C) 2006-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Simon Wunderlich, Marek Lindner
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_BITARRAY_H_
|
||||
#define _NET_BATMAN_ADV_BITARRAY_H_
|
||||
|
||||
/* Returns 1 if the corresponding bit in the given seq_bits indicates true
|
||||
* and curr_seqno is within range of last_seqno. Otherwise returns 0.
|
||||
*/
|
||||
static inline int batadv_test_bit(const unsigned long *seq_bits,
|
||||
uint32_t last_seqno, uint32_t curr_seqno)
|
||||
{
|
||||
int32_t diff;
|
||||
|
||||
diff = last_seqno - curr_seqno;
|
||||
if (diff < 0 || diff >= BATADV_TQ_LOCAL_WINDOW_SIZE)
|
||||
return 0;
|
||||
else
|
||||
return test_bit(diff, seq_bits) != 0;
|
||||
}
|
||||
|
||||
/* turn corresponding bit on, so we can remember that we got the packet */
|
||||
static inline void batadv_set_bit(unsigned long *seq_bits, int32_t n)
|
||||
{
|
||||
/* if too old, just drop it */
|
||||
if (n < 0 || n >= BATADV_TQ_LOCAL_WINDOW_SIZE)
|
||||
return;
|
||||
|
||||
set_bit(n, seq_bits); /* turn the position on */
|
||||
}
|
||||
|
||||
/* receive and process one packet, returns 1 if received seq_num is considered
|
||||
* new, 0 if old
|
||||
*/
|
||||
int batadv_bit_get_packet(void *priv, unsigned long *seq_bits,
|
||||
int32_t seq_num_diff, int set_mark);
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_BITARRAY_H_ */
|
1725
net/batman-adv/bridge_loop_avoidance.c
Normal file
1725
net/batman-adv/bridge_loop_avoidance.c
Normal file
File diff suppressed because it is too large
Load diff
108
net/batman-adv/bridge_loop_avoidance.h
Normal file
108
net/batman-adv/bridge_loop_avoidance.h
Normal file
|
@ -0,0 +1,108 @@
|
|||
/* Copyright (C) 2011-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Simon Wunderlich
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_BLA_H_
|
||||
#define _NET_BATMAN_ADV_BLA_H_
|
||||
|
||||
#ifdef CONFIG_BATMAN_ADV_BLA
|
||||
int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
|
||||
unsigned short vid, bool is_bcast);
|
||||
int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
|
||||
unsigned short vid);
|
||||
int batadv_bla_is_backbone_gw(struct sk_buff *skb,
|
||||
struct batadv_orig_node *orig_node, int hdr_size);
|
||||
int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset);
|
||||
int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq,
|
||||
void *offset);
|
||||
bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig,
|
||||
unsigned short vid);
|
||||
int batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb);
|
||||
void batadv_bla_update_orig_address(struct batadv_priv *bat_priv,
|
||||
struct batadv_hard_iface *primary_if,
|
||||
struct batadv_hard_iface *oldif);
|
||||
int batadv_bla_init(struct batadv_priv *bat_priv);
|
||||
void batadv_bla_free(struct batadv_priv *bat_priv);
|
||||
|
||||
#define BATADV_BLA_CRC_INIT 0
|
||||
#else /* ifdef CONFIG_BATMAN_ADV_BLA */
|
||||
|
||||
static inline int batadv_bla_rx(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb, unsigned short vid,
|
||||
bool is_bcast)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int batadv_bla_tx(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb, unsigned short vid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int batadv_bla_is_backbone_gw(struct sk_buff *skb,
|
||||
struct batadv_orig_node *orig_node,
|
||||
int hdr_size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int batadv_bla_claim_table_seq_print_text(struct seq_file *seq,
|
||||
void *offset)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq,
|
||||
void *offset)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv,
|
||||
uint8_t *orig,
|
||||
unsigned short vid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int
|
||||
batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
batadv_bla_update_orig_address(struct batadv_priv *bat_priv,
|
||||
struct batadv_hard_iface *primary_if,
|
||||
struct batadv_hard_iface *oldif)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int batadv_bla_init(struct batadv_priv *bat_priv)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void batadv_bla_free(struct batadv_priv *bat_priv)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* ifdef CONFIG_BATMAN_ADV_BLA */
|
||||
|
||||
#endif /* ifndef _NET_BATMAN_ADV_BLA_H_ */
|
561
net/batman-adv/debugfs.c
Normal file
561
net/batman-adv/debugfs.c
Normal file
|
@ -0,0 +1,561 @@
|
|||
/* Copyright (C) 2010-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "debugfs.h"
|
||||
#include "translation-table.h"
|
||||
#include "originator.h"
|
||||
#include "hard-interface.h"
|
||||
#include "gateway_common.h"
|
||||
#include "gateway_client.h"
|
||||
#include "soft-interface.h"
|
||||
#include "icmp_socket.h"
|
||||
#include "bridge_loop_avoidance.h"
|
||||
#include "distributed-arp-table.h"
|
||||
#include "network-coding.h"
|
||||
|
||||
static struct dentry *batadv_debugfs;
|
||||
|
||||
#ifdef CONFIG_BATMAN_ADV_DEBUG
|
||||
#define BATADV_LOG_BUFF_MASK (batadv_log_buff_len - 1)
|
||||
|
||||
static const int batadv_log_buff_len = BATADV_LOG_BUF_LEN;
|
||||
|
||||
static char *batadv_log_char_addr(struct batadv_priv_debug_log *debug_log,
|
||||
size_t idx)
|
||||
{
|
||||
return &debug_log->log_buff[idx & BATADV_LOG_BUFF_MASK];
|
||||
}
|
||||
|
||||
static void batadv_emit_log_char(struct batadv_priv_debug_log *debug_log,
|
||||
char c)
|
||||
{
|
||||
char *char_addr;
|
||||
|
||||
char_addr = batadv_log_char_addr(debug_log, debug_log->log_end);
|
||||
*char_addr = c;
|
||||
debug_log->log_end++;
|
||||
|
||||
if (debug_log->log_end - debug_log->log_start > batadv_log_buff_len)
|
||||
debug_log->log_start = debug_log->log_end - batadv_log_buff_len;
|
||||
}
|
||||
|
||||
__printf(2, 3)
|
||||
static int batadv_fdebug_log(struct batadv_priv_debug_log *debug_log,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
static char debug_log_buf[256];
|
||||
char *p;
|
||||
|
||||
if (!debug_log)
|
||||
return 0;
|
||||
|
||||
spin_lock_bh(&debug_log->lock);
|
||||
va_start(args, fmt);
|
||||
vscnprintf(debug_log_buf, sizeof(debug_log_buf), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
for (p = debug_log_buf; *p != 0; p++)
|
||||
batadv_emit_log_char(debug_log, *p);
|
||||
|
||||
spin_unlock_bh(&debug_log->lock);
|
||||
|
||||
wake_up(&debug_log->queue_wait);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int batadv_debug_log(struct batadv_priv *bat_priv, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
char tmp_log_buf[256];
|
||||
|
||||
va_start(args, fmt);
|
||||
vscnprintf(tmp_log_buf, sizeof(tmp_log_buf), fmt, args);
|
||||
batadv_fdebug_log(bat_priv->debug_log, "[%10u] %s",
|
||||
jiffies_to_msecs(jiffies), tmp_log_buf);
|
||||
va_end(args);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int batadv_log_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return -EBUSY;
|
||||
|
||||
nonseekable_open(inode, file);
|
||||
file->private_data = inode->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int batadv_log_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
module_put(THIS_MODULE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int batadv_log_empty(struct batadv_priv_debug_log *debug_log)
|
||||
{
|
||||
return !(debug_log->log_start - debug_log->log_end);
|
||||
}
|
||||
|
||||
static ssize_t batadv_log_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct batadv_priv *bat_priv = file->private_data;
|
||||
struct batadv_priv_debug_log *debug_log = bat_priv->debug_log;
|
||||
int error, i = 0;
|
||||
char *char_addr;
|
||||
char c;
|
||||
|
||||
if ((file->f_flags & O_NONBLOCK) && batadv_log_empty(debug_log))
|
||||
return -EAGAIN;
|
||||
|
||||
if (!buf)
|
||||
return -EINVAL;
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, buf, count))
|
||||
return -EFAULT;
|
||||
|
||||
error = wait_event_interruptible(debug_log->queue_wait,
|
||||
(!batadv_log_empty(debug_log)));
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
spin_lock_bh(&debug_log->lock);
|
||||
|
||||
while ((!error) && (i < count) &&
|
||||
(debug_log->log_start != debug_log->log_end)) {
|
||||
char_addr = batadv_log_char_addr(debug_log,
|
||||
debug_log->log_start);
|
||||
c = *char_addr;
|
||||
|
||||
debug_log->log_start++;
|
||||
|
||||
spin_unlock_bh(&debug_log->lock);
|
||||
|
||||
error = __put_user(c, buf);
|
||||
|
||||
spin_lock_bh(&debug_log->lock);
|
||||
|
||||
buf++;
|
||||
i++;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&debug_log->lock);
|
||||
|
||||
if (!error)
|
||||
return i;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static unsigned int batadv_log_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct batadv_priv *bat_priv = file->private_data;
|
||||
struct batadv_priv_debug_log *debug_log = bat_priv->debug_log;
|
||||
|
||||
poll_wait(file, &debug_log->queue_wait, wait);
|
||||
|
||||
if (!batadv_log_empty(debug_log))
|
||||
return POLLIN | POLLRDNORM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations batadv_log_fops = {
|
||||
.open = batadv_log_open,
|
||||
.release = batadv_log_release,
|
||||
.read = batadv_log_read,
|
||||
.poll = batadv_log_poll,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static int batadv_debug_log_setup(struct batadv_priv *bat_priv)
|
||||
{
|
||||
struct dentry *d;
|
||||
|
||||
if (!bat_priv->debug_dir)
|
||||
goto err;
|
||||
|
||||
bat_priv->debug_log = kzalloc(sizeof(*bat_priv->debug_log), GFP_ATOMIC);
|
||||
if (!bat_priv->debug_log)
|
||||
goto err;
|
||||
|
||||
spin_lock_init(&bat_priv->debug_log->lock);
|
||||
init_waitqueue_head(&bat_priv->debug_log->queue_wait);
|
||||
|
||||
d = debugfs_create_file("log", S_IFREG | S_IRUSR,
|
||||
bat_priv->debug_dir, bat_priv,
|
||||
&batadv_log_fops);
|
||||
if (!d)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void batadv_debug_log_cleanup(struct batadv_priv *bat_priv)
|
||||
{
|
||||
kfree(bat_priv->debug_log);
|
||||
bat_priv->debug_log = NULL;
|
||||
}
|
||||
#else /* CONFIG_BATMAN_ADV_DEBUG */
|
||||
static int batadv_debug_log_setup(struct batadv_priv *bat_priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void batadv_debug_log_cleanup(struct batadv_priv *bat_priv)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int batadv_algorithms_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, batadv_algo_seq_print_text, NULL);
|
||||
}
|
||||
|
||||
static int batadv_originators_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct net_device *net_dev = (struct net_device *)inode->i_private;
|
||||
|
||||
return single_open(file, batadv_orig_seq_print_text, net_dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_originators_hardif_open - handles debugfs output for the
|
||||
* originator table of an hard interface
|
||||
* @inode: inode pointer to debugfs file
|
||||
* @file: pointer to the seq_file
|
||||
*/
|
||||
static int batadv_originators_hardif_open(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
struct net_device *net_dev = (struct net_device *)inode->i_private;
|
||||
|
||||
return single_open(file, batadv_orig_hardif_seq_print_text, net_dev);
|
||||
}
|
||||
|
||||
static int batadv_gateways_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct net_device *net_dev = (struct net_device *)inode->i_private;
|
||||
|
||||
return single_open(file, batadv_gw_client_seq_print_text, net_dev);
|
||||
}
|
||||
|
||||
static int batadv_transtable_global_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct net_device *net_dev = (struct net_device *)inode->i_private;
|
||||
|
||||
return single_open(file, batadv_tt_global_seq_print_text, net_dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BATMAN_ADV_BLA
|
||||
static int batadv_bla_claim_table_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct net_device *net_dev = (struct net_device *)inode->i_private;
|
||||
|
||||
return single_open(file, batadv_bla_claim_table_seq_print_text,
|
||||
net_dev);
|
||||
}
|
||||
|
||||
static int batadv_bla_backbone_table_open(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
struct net_device *net_dev = (struct net_device *)inode->i_private;
|
||||
|
||||
return single_open(file, batadv_bla_backbone_table_seq_print_text,
|
||||
net_dev);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BATMAN_ADV_DAT
|
||||
/**
|
||||
* batadv_dat_cache_open - Prepare file handler for reads from dat_chache
|
||||
* @inode: inode which was opened
|
||||
* @file: file handle to be initialized
|
||||
*/
|
||||
static int batadv_dat_cache_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct net_device *net_dev = (struct net_device *)inode->i_private;
|
||||
|
||||
return single_open(file, batadv_dat_cache_seq_print_text, net_dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int batadv_transtable_local_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct net_device *net_dev = (struct net_device *)inode->i_private;
|
||||
|
||||
return single_open(file, batadv_tt_local_seq_print_text, net_dev);
|
||||
}
|
||||
|
||||
struct batadv_debuginfo {
|
||||
struct attribute attr;
|
||||
const struct file_operations fops;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BATMAN_ADV_NC
|
||||
static int batadv_nc_nodes_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct net_device *net_dev = (struct net_device *)inode->i_private;
|
||||
|
||||
return single_open(file, batadv_nc_nodes_seq_print_text, net_dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define BATADV_DEBUGINFO(_name, _mode, _open) \
|
||||
struct batadv_debuginfo batadv_debuginfo_##_name = { \
|
||||
.attr = { .name = __stringify(_name), \
|
||||
.mode = _mode, }, \
|
||||
.fops = { .owner = THIS_MODULE, \
|
||||
.open = _open, \
|
||||
.read = seq_read, \
|
||||
.llseek = seq_lseek, \
|
||||
.release = single_release, \
|
||||
} \
|
||||
}
|
||||
|
||||
/* the following attributes are general and therefore they will be directly
|
||||
* placed in the BATADV_DEBUGFS_SUBDIR subdirectory of debugfs
|
||||
*/
|
||||
static BATADV_DEBUGINFO(routing_algos, S_IRUGO, batadv_algorithms_open);
|
||||
|
||||
static struct batadv_debuginfo *batadv_general_debuginfos[] = {
|
||||
&batadv_debuginfo_routing_algos,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* The following attributes are per soft interface */
|
||||
static BATADV_DEBUGINFO(originators, S_IRUGO, batadv_originators_open);
|
||||
static BATADV_DEBUGINFO(gateways, S_IRUGO, batadv_gateways_open);
|
||||
static BATADV_DEBUGINFO(transtable_global, S_IRUGO,
|
||||
batadv_transtable_global_open);
|
||||
#ifdef CONFIG_BATMAN_ADV_BLA
|
||||
static BATADV_DEBUGINFO(bla_claim_table, S_IRUGO, batadv_bla_claim_table_open);
|
||||
static BATADV_DEBUGINFO(bla_backbone_table, S_IRUGO,
|
||||
batadv_bla_backbone_table_open);
|
||||
#endif
|
||||
#ifdef CONFIG_BATMAN_ADV_DAT
|
||||
static BATADV_DEBUGINFO(dat_cache, S_IRUGO, batadv_dat_cache_open);
|
||||
#endif
|
||||
static BATADV_DEBUGINFO(transtable_local, S_IRUGO,
|
||||
batadv_transtable_local_open);
|
||||
#ifdef CONFIG_BATMAN_ADV_NC
|
||||
static BATADV_DEBUGINFO(nc_nodes, S_IRUGO, batadv_nc_nodes_open);
|
||||
#endif
|
||||
|
||||
static struct batadv_debuginfo *batadv_mesh_debuginfos[] = {
|
||||
&batadv_debuginfo_originators,
|
||||
&batadv_debuginfo_gateways,
|
||||
&batadv_debuginfo_transtable_global,
|
||||
#ifdef CONFIG_BATMAN_ADV_BLA
|
||||
&batadv_debuginfo_bla_claim_table,
|
||||
&batadv_debuginfo_bla_backbone_table,
|
||||
#endif
|
||||
#ifdef CONFIG_BATMAN_ADV_DAT
|
||||
&batadv_debuginfo_dat_cache,
|
||||
#endif
|
||||
&batadv_debuginfo_transtable_local,
|
||||
#ifdef CONFIG_BATMAN_ADV_NC
|
||||
&batadv_debuginfo_nc_nodes,
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
||||
#define BATADV_HARDIF_DEBUGINFO(_name, _mode, _open) \
|
||||
struct batadv_debuginfo batadv_hardif_debuginfo_##_name = { \
|
||||
.attr = { \
|
||||
.name = __stringify(_name), \
|
||||
.mode = _mode, \
|
||||
}, \
|
||||
.fops = { \
|
||||
.owner = THIS_MODULE, \
|
||||
.open = _open, \
|
||||
.read = seq_read, \
|
||||
.llseek = seq_lseek, \
|
||||
.release = single_release, \
|
||||
}, \
|
||||
}
|
||||
static BATADV_HARDIF_DEBUGINFO(originators, S_IRUGO,
|
||||
batadv_originators_hardif_open);
|
||||
|
||||
static struct batadv_debuginfo *batadv_hardif_debuginfos[] = {
|
||||
&batadv_hardif_debuginfo_originators,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void batadv_debugfs_init(void)
|
||||
{
|
||||
struct batadv_debuginfo **bat_debug;
|
||||
struct dentry *file;
|
||||
|
||||
batadv_debugfs = debugfs_create_dir(BATADV_DEBUGFS_SUBDIR, NULL);
|
||||
if (batadv_debugfs == ERR_PTR(-ENODEV))
|
||||
batadv_debugfs = NULL;
|
||||
|
||||
if (!batadv_debugfs)
|
||||
goto err;
|
||||
|
||||
for (bat_debug = batadv_general_debuginfos; *bat_debug; ++bat_debug) {
|
||||
file = debugfs_create_file(((*bat_debug)->attr).name,
|
||||
S_IFREG | ((*bat_debug)->attr).mode,
|
||||
batadv_debugfs, NULL,
|
||||
&(*bat_debug)->fops);
|
||||
if (!file) {
|
||||
pr_err("Can't add general debugfs file: %s\n",
|
||||
((*bat_debug)->attr).name);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
err:
|
||||
debugfs_remove_recursive(batadv_debugfs);
|
||||
batadv_debugfs = NULL;
|
||||
}
|
||||
|
||||
void batadv_debugfs_destroy(void)
|
||||
{
|
||||
debugfs_remove_recursive(batadv_debugfs);
|
||||
batadv_debugfs = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_debugfs_add_hardif - creates the base directory for a hard interface
|
||||
* in debugfs.
|
||||
* @hard_iface: hard interface which should be added.
|
||||
*/
|
||||
int batadv_debugfs_add_hardif(struct batadv_hard_iface *hard_iface)
|
||||
{
|
||||
struct batadv_debuginfo **bat_debug;
|
||||
struct dentry *file;
|
||||
|
||||
if (!batadv_debugfs)
|
||||
goto out;
|
||||
|
||||
hard_iface->debug_dir = debugfs_create_dir(hard_iface->net_dev->name,
|
||||
batadv_debugfs);
|
||||
if (!hard_iface->debug_dir)
|
||||
goto out;
|
||||
|
||||
for (bat_debug = batadv_hardif_debuginfos; *bat_debug; ++bat_debug) {
|
||||
file = debugfs_create_file(((*bat_debug)->attr).name,
|
||||
S_IFREG | ((*bat_debug)->attr).mode,
|
||||
hard_iface->debug_dir,
|
||||
hard_iface->net_dev,
|
||||
&(*bat_debug)->fops);
|
||||
if (!file)
|
||||
goto rem_attr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
rem_attr:
|
||||
debugfs_remove_recursive(hard_iface->debug_dir);
|
||||
hard_iface->debug_dir = NULL;
|
||||
out:
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
return -ENOMEM;
|
||||
#else
|
||||
return 0;
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_debugfs_del_hardif - delete the base directory for a hard interface
|
||||
* in debugfs.
|
||||
* @hard_iface: hard interface which is deleted.
|
||||
*/
|
||||
void batadv_debugfs_del_hardif(struct batadv_hard_iface *hard_iface)
|
||||
{
|
||||
if (batadv_debugfs) {
|
||||
debugfs_remove_recursive(hard_iface->debug_dir);
|
||||
hard_iface->debug_dir = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int batadv_debugfs_add_meshif(struct net_device *dev)
|
||||
{
|
||||
struct batadv_priv *bat_priv = netdev_priv(dev);
|
||||
struct batadv_debuginfo **bat_debug;
|
||||
struct dentry *file;
|
||||
|
||||
if (!batadv_debugfs)
|
||||
goto out;
|
||||
|
||||
bat_priv->debug_dir = debugfs_create_dir(dev->name, batadv_debugfs);
|
||||
if (!bat_priv->debug_dir)
|
||||
goto out;
|
||||
|
||||
if (batadv_socket_setup(bat_priv) < 0)
|
||||
goto rem_attr;
|
||||
|
||||
if (batadv_debug_log_setup(bat_priv) < 0)
|
||||
goto rem_attr;
|
||||
|
||||
for (bat_debug = batadv_mesh_debuginfos; *bat_debug; ++bat_debug) {
|
||||
file = debugfs_create_file(((*bat_debug)->attr).name,
|
||||
S_IFREG | ((*bat_debug)->attr).mode,
|
||||
bat_priv->debug_dir,
|
||||
dev, &(*bat_debug)->fops);
|
||||
if (!file) {
|
||||
batadv_err(dev, "Can't add debugfs file: %s/%s\n",
|
||||
dev->name, ((*bat_debug)->attr).name);
|
||||
goto rem_attr;
|
||||
}
|
||||
}
|
||||
|
||||
if (batadv_nc_init_debugfs(bat_priv) < 0)
|
||||
goto rem_attr;
|
||||
|
||||
return 0;
|
||||
rem_attr:
|
||||
debugfs_remove_recursive(bat_priv->debug_dir);
|
||||
bat_priv->debug_dir = NULL;
|
||||
out:
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
return -ENOMEM;
|
||||
#else
|
||||
return 0;
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
}
|
||||
|
||||
void batadv_debugfs_del_meshif(struct net_device *dev)
|
||||
{
|
||||
struct batadv_priv *bat_priv = netdev_priv(dev);
|
||||
|
||||
batadv_debug_log_cleanup(bat_priv);
|
||||
|
||||
if (batadv_debugfs) {
|
||||
debugfs_remove_recursive(bat_priv->debug_dir);
|
||||
bat_priv->debug_dir = NULL;
|
||||
}
|
||||
}
|
30
net/batman-adv/debugfs.h
Normal file
30
net/batman-adv/debugfs.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/* Copyright (C) 2010-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_DEBUGFS_H_
|
||||
#define _NET_BATMAN_ADV_DEBUGFS_H_
|
||||
|
||||
#define BATADV_DEBUGFS_SUBDIR "batman_adv"
|
||||
|
||||
void batadv_debugfs_init(void);
|
||||
void batadv_debugfs_destroy(void);
|
||||
int batadv_debugfs_add_meshif(struct net_device *dev);
|
||||
void batadv_debugfs_del_meshif(struct net_device *dev);
|
||||
int batadv_debugfs_add_hardif(struct batadv_hard_iface *hard_iface);
|
||||
void batadv_debugfs_del_hardif(struct batadv_hard_iface *hard_iface);
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_DEBUGFS_H_ */
|
1203
net/batman-adv/distributed-arp-table.c
Normal file
1203
net/batman-adv/distributed-arp-table.c
Normal file
File diff suppressed because it is too large
Load diff
173
net/batman-adv/distributed-arp-table.h
Normal file
173
net/batman-adv/distributed-arp-table.h
Normal file
|
@ -0,0 +1,173 @@
|
|||
/* Copyright (C) 2011-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Antonio Quartulli
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_DISTRIBUTED_ARP_TABLE_H_
|
||||
#define _NET_BATMAN_ADV_DISTRIBUTED_ARP_TABLE_H_
|
||||
|
||||
#ifdef CONFIG_BATMAN_ADV_DAT
|
||||
|
||||
#include "types.h"
|
||||
#include "originator.h"
|
||||
|
||||
#include <linux/if_arp.h>
|
||||
|
||||
/**
|
||||
* BATADV_DAT_ADDR_MAX - maximum address value in the DHT space
|
||||
*/
|
||||
#define BATADV_DAT_ADDR_MAX ((batadv_dat_addr_t)~(batadv_dat_addr_t)0)
|
||||
|
||||
void batadv_dat_status_update(struct net_device *net_dev);
|
||||
bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb);
|
||||
bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb, int hdr_size);
|
||||
void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb);
|
||||
bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb, int hdr_size);
|
||||
bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
|
||||
struct batadv_forw_packet *forw_packet);
|
||||
|
||||
/**
|
||||
* batadv_dat_init_orig_node_addr - assign a DAT address to the orig_node
|
||||
* @orig_node: the node to assign the DAT address to
|
||||
*/
|
||||
static inline void
|
||||
batadv_dat_init_orig_node_addr(struct batadv_orig_node *orig_node)
|
||||
{
|
||||
uint32_t addr;
|
||||
|
||||
addr = batadv_choose_orig(orig_node->orig, BATADV_DAT_ADDR_MAX);
|
||||
orig_node->dat_addr = (batadv_dat_addr_t)addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_dat_init_own_addr - assign a DAT address to the node itself
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @primary_if: a pointer to the primary interface
|
||||
*/
|
||||
static inline void
|
||||
batadv_dat_init_own_addr(struct batadv_priv *bat_priv,
|
||||
struct batadv_hard_iface *primary_if)
|
||||
{
|
||||
uint32_t addr;
|
||||
|
||||
addr = batadv_choose_orig(primary_if->net_dev->dev_addr,
|
||||
BATADV_DAT_ADDR_MAX);
|
||||
|
||||
bat_priv->dat.addr = (batadv_dat_addr_t)addr;
|
||||
}
|
||||
|
||||
int batadv_dat_init(struct batadv_priv *bat_priv);
|
||||
void batadv_dat_free(struct batadv_priv *bat_priv);
|
||||
int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset);
|
||||
|
||||
/**
|
||||
* batadv_dat_inc_counter - increment the correct DAT packet counter
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @subtype: the 4addr subtype of the packet to be counted
|
||||
*
|
||||
* Updates the ethtool statistics for the received packet if it is a DAT subtype
|
||||
*/
|
||||
static inline void batadv_dat_inc_counter(struct batadv_priv *bat_priv,
|
||||
uint8_t subtype)
|
||||
{
|
||||
switch (subtype) {
|
||||
case BATADV_P_DAT_DHT_GET:
|
||||
batadv_inc_counter(bat_priv,
|
||||
BATADV_CNT_DAT_GET_RX);
|
||||
break;
|
||||
case BATADV_P_DAT_DHT_PUT:
|
||||
batadv_inc_counter(bat_priv,
|
||||
BATADV_CNT_DAT_PUT_RX);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline void batadv_dat_status_update(struct net_device *net_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool
|
||||
batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb, int hdr_size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb, int hdr_size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
|
||||
struct batadv_forw_packet *forw_packet)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void
|
||||
batadv_dat_init_orig_node_addr(struct batadv_orig_node *orig_node)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void batadv_dat_init_own_addr(struct batadv_priv *bat_priv,
|
||||
struct batadv_hard_iface *iface)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void batadv_arp_change_timeout(struct net_device *soft_iface,
|
||||
const char *name)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int batadv_dat_init(struct batadv_priv *bat_priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void batadv_dat_free(struct batadv_priv *bat_priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void batadv_dat_inc_counter(struct batadv_priv *bat_priv,
|
||||
uint8_t subtype)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BATMAN_ADV_DAT */
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_DISTRIBUTED_ARP_TABLE_H_ */
|
498
net/batman-adv/fragmentation.c
Normal file
498
net/batman-adv/fragmentation.c
Normal file
|
@ -0,0 +1,498 @@
|
|||
/* Copyright (C) 2013-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Martin Hundebøll <martin@hundeboll.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "fragmentation.h"
|
||||
#include "send.h"
|
||||
#include "originator.h"
|
||||
#include "routing.h"
|
||||
#include "hard-interface.h"
|
||||
#include "soft-interface.h"
|
||||
|
||||
|
||||
/**
|
||||
* batadv_frag_clear_chain - delete entries in the fragment buffer chain
|
||||
* @head: head of chain with entries.
|
||||
*
|
||||
* Free fragments in the passed hlist. Should be called with appropriate lock.
|
||||
*/
|
||||
static void batadv_frag_clear_chain(struct hlist_head *head)
|
||||
{
|
||||
struct batadv_frag_list_entry *entry;
|
||||
struct hlist_node *node;
|
||||
|
||||
hlist_for_each_entry_safe(entry, node, head, list) {
|
||||
hlist_del(&entry->list);
|
||||
kfree_skb(entry->skb);
|
||||
kfree(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_frag_purge_orig - free fragments associated to an orig
|
||||
* @orig_node: originator to free fragments from
|
||||
* @check_cb: optional function to tell if an entry should be purged
|
||||
*/
|
||||
void batadv_frag_purge_orig(struct batadv_orig_node *orig_node,
|
||||
bool (*check_cb)(struct batadv_frag_table_entry *))
|
||||
{
|
||||
struct batadv_frag_table_entry *chain;
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) {
|
||||
chain = &orig_node->fragments[i];
|
||||
spin_lock_bh(&orig_node->fragments[i].lock);
|
||||
|
||||
if (!check_cb || check_cb(chain)) {
|
||||
batadv_frag_clear_chain(&orig_node->fragments[i].head);
|
||||
orig_node->fragments[i].size = 0;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&orig_node->fragments[i].lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_frag_size_limit - maximum possible size of packet to be fragmented
|
||||
*
|
||||
* Returns the maximum size of payload that can be fragmented.
|
||||
*/
|
||||
static int batadv_frag_size_limit(void)
|
||||
{
|
||||
int limit = BATADV_FRAG_MAX_FRAG_SIZE;
|
||||
|
||||
limit -= sizeof(struct batadv_frag_packet);
|
||||
limit *= BATADV_FRAG_MAX_FRAGMENTS;
|
||||
|
||||
return limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_frag_init_chain - check and prepare fragment chain for new fragment
|
||||
* @chain: chain in fragments table to init
|
||||
* @seqno: sequence number of the received fragment
|
||||
*
|
||||
* Make chain ready for a fragment with sequence number "seqno". Delete existing
|
||||
* entries if they have an "old" sequence number.
|
||||
*
|
||||
* Caller must hold chain->lock.
|
||||
*
|
||||
* Returns true if chain is empty and caller can just insert the new fragment
|
||||
* without searching for the right position.
|
||||
*/
|
||||
static bool batadv_frag_init_chain(struct batadv_frag_table_entry *chain,
|
||||
uint16_t seqno)
|
||||
{
|
||||
if (chain->seqno == seqno)
|
||||
return false;
|
||||
|
||||
if (!hlist_empty(&chain->head))
|
||||
batadv_frag_clear_chain(&chain->head);
|
||||
|
||||
chain->size = 0;
|
||||
chain->seqno = seqno;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_frag_insert_packet - insert a fragment into a fragment chain
|
||||
* @orig_node: originator that the fragment was received from
|
||||
* @skb: skb to insert
|
||||
* @chain_out: list head to attach complete chains of fragments to
|
||||
*
|
||||
* Insert a new fragment into the reverse ordered chain in the right table
|
||||
* entry. The hash table entry is cleared if "old" fragments exist in it.
|
||||
*
|
||||
* Returns true if skb is buffered, false on error. If the chain has all the
|
||||
* fragments needed to merge the packet, the chain is moved to the passed head
|
||||
* to avoid locking the chain in the table.
|
||||
*/
|
||||
static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node,
|
||||
struct sk_buff *skb,
|
||||
struct hlist_head *chain_out)
|
||||
{
|
||||
struct batadv_frag_table_entry *chain;
|
||||
struct batadv_frag_list_entry *frag_entry_new = NULL, *frag_entry_curr;
|
||||
struct batadv_frag_list_entry *frag_entry_last = NULL;
|
||||
struct batadv_frag_packet *frag_packet;
|
||||
uint8_t bucket;
|
||||
uint16_t seqno, hdr_size = sizeof(struct batadv_frag_packet);
|
||||
bool ret = false;
|
||||
|
||||
/* Linearize packet to avoid linearizing 16 packets in a row when doing
|
||||
* the later merge. Non-linear merge should be added to remove this
|
||||
* linearization.
|
||||
*/
|
||||
if (skb_linearize(skb) < 0)
|
||||
goto err;
|
||||
|
||||
frag_packet = (struct batadv_frag_packet *)skb->data;
|
||||
seqno = ntohs(frag_packet->seqno);
|
||||
bucket = seqno % BATADV_FRAG_BUFFER_COUNT;
|
||||
|
||||
frag_entry_new = kmalloc(sizeof(*frag_entry_new), GFP_ATOMIC);
|
||||
if (!frag_entry_new)
|
||||
goto err;
|
||||
|
||||
frag_entry_new->skb = skb;
|
||||
frag_entry_new->no = frag_packet->no;
|
||||
|
||||
/* Select entry in the "chain table" and delete any prior fragments
|
||||
* with another sequence number. batadv_frag_init_chain() returns true,
|
||||
* if the list is empty at return.
|
||||
*/
|
||||
chain = &orig_node->fragments[bucket];
|
||||
spin_lock_bh(&chain->lock);
|
||||
if (batadv_frag_init_chain(chain, seqno)) {
|
||||
hlist_add_head(&frag_entry_new->list, &chain->head);
|
||||
chain->size = skb->len - hdr_size;
|
||||
chain->timestamp = jiffies;
|
||||
ret = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Find the position for the new fragment. */
|
||||
hlist_for_each_entry(frag_entry_curr, &chain->head, list) {
|
||||
/* Drop packet if fragment already exists. */
|
||||
if (frag_entry_curr->no == frag_entry_new->no)
|
||||
goto err_unlock;
|
||||
|
||||
/* Order fragments from highest to lowest. */
|
||||
if (frag_entry_curr->no < frag_entry_new->no) {
|
||||
hlist_add_before(&frag_entry_new->list,
|
||||
&frag_entry_curr->list);
|
||||
chain->size += skb->len - hdr_size;
|
||||
chain->timestamp = jiffies;
|
||||
ret = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* store current entry because it could be the last in list */
|
||||
frag_entry_last = frag_entry_curr;
|
||||
}
|
||||
|
||||
/* Reached the end of the list, so insert after 'frag_entry_last'. */
|
||||
if (likely(frag_entry_last)) {
|
||||
hlist_add_behind(&frag_entry_new->list, &frag_entry_last->list);
|
||||
chain->size += skb->len - hdr_size;
|
||||
chain->timestamp = jiffies;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
out:
|
||||
if (chain->size > batadv_frag_size_limit() ||
|
||||
ntohs(frag_packet->total_size) > batadv_frag_size_limit()) {
|
||||
/* Clear chain if total size of either the list or the packet
|
||||
* exceeds the maximum size of one merged packet.
|
||||
*/
|
||||
batadv_frag_clear_chain(&chain->head);
|
||||
chain->size = 0;
|
||||
} else if (ntohs(frag_packet->total_size) == chain->size) {
|
||||
/* All fragments received. Hand over chain to caller. */
|
||||
hlist_move_list(&chain->head, chain_out);
|
||||
chain->size = 0;
|
||||
}
|
||||
|
||||
err_unlock:
|
||||
spin_unlock_bh(&chain->lock);
|
||||
|
||||
err:
|
||||
if (!ret)
|
||||
kfree(frag_entry_new);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_frag_merge_packets - merge a chain of fragments
|
||||
* @chain: head of chain with fragments
|
||||
* @skb: packet with total size of skb after merging
|
||||
*
|
||||
* Expand the first skb in the chain and copy the content of the remaining
|
||||
* skb's into the expanded one. After doing so, clear the chain.
|
||||
*
|
||||
* Returns the merged skb or NULL on error.
|
||||
*/
|
||||
static struct sk_buff *
|
||||
batadv_frag_merge_packets(struct hlist_head *chain, struct sk_buff *skb)
|
||||
{
|
||||
struct batadv_frag_packet *packet;
|
||||
struct batadv_frag_list_entry *entry;
|
||||
struct sk_buff *skb_out = NULL;
|
||||
int size, hdr_size = sizeof(struct batadv_frag_packet);
|
||||
|
||||
/* Make sure incoming skb has non-bogus data. */
|
||||
packet = (struct batadv_frag_packet *)skb->data;
|
||||
size = ntohs(packet->total_size);
|
||||
if (size > batadv_frag_size_limit())
|
||||
goto free;
|
||||
|
||||
/* Remove first entry, as this is the destination for the rest of the
|
||||
* fragments.
|
||||
*/
|
||||
entry = hlist_entry(chain->first, struct batadv_frag_list_entry, list);
|
||||
hlist_del(&entry->list);
|
||||
skb_out = entry->skb;
|
||||
kfree(entry);
|
||||
|
||||
/* Make room for the rest of the fragments. */
|
||||
if (pskb_expand_head(skb_out, 0, size - skb_out->len, GFP_ATOMIC) < 0) {
|
||||
kfree_skb(skb_out);
|
||||
skb_out = NULL;
|
||||
goto free;
|
||||
}
|
||||
|
||||
/* Move the existing MAC header to just before the payload. (Override
|
||||
* the fragment header.)
|
||||
*/
|
||||
skb_pull_rcsum(skb_out, hdr_size);
|
||||
memmove(skb_out->data - ETH_HLEN, skb_mac_header(skb_out), ETH_HLEN);
|
||||
skb_set_mac_header(skb_out, -ETH_HLEN);
|
||||
skb_reset_network_header(skb_out);
|
||||
skb_reset_transport_header(skb_out);
|
||||
|
||||
/* Copy the payload of the each fragment into the last skb */
|
||||
hlist_for_each_entry(entry, chain, list) {
|
||||
size = entry->skb->len - hdr_size;
|
||||
memcpy(skb_put(skb_out, size), entry->skb->data + hdr_size,
|
||||
size);
|
||||
}
|
||||
|
||||
free:
|
||||
/* Locking is not needed, because 'chain' is not part of any orig. */
|
||||
batadv_frag_clear_chain(chain);
|
||||
return skb_out;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_frag_skb_buffer - buffer fragment for later merge
|
||||
* @skb: skb to buffer
|
||||
* @orig_node_src: originator that the skb is received from
|
||||
*
|
||||
* Add fragment to buffer and merge fragments if possible.
|
||||
*
|
||||
* There are three possible outcomes: 1) Packet is merged: Return true and
|
||||
* set *skb to merged packet; 2) Packet is buffered: Return true and set *skb
|
||||
* to NULL; 3) Error: Return false and leave skb as is.
|
||||
*/
|
||||
bool batadv_frag_skb_buffer(struct sk_buff **skb,
|
||||
struct batadv_orig_node *orig_node_src)
|
||||
{
|
||||
struct sk_buff *skb_out = NULL;
|
||||
struct hlist_head head = HLIST_HEAD_INIT;
|
||||
bool ret = false;
|
||||
|
||||
/* Add packet to buffer and table entry if merge is possible. */
|
||||
if (!batadv_frag_insert_packet(orig_node_src, *skb, &head))
|
||||
goto out_err;
|
||||
|
||||
/* Leave if more fragments are needed to merge. */
|
||||
if (hlist_empty(&head))
|
||||
goto out;
|
||||
|
||||
skb_out = batadv_frag_merge_packets(&head, *skb);
|
||||
if (!skb_out)
|
||||
goto out_err;
|
||||
|
||||
out:
|
||||
*skb = skb_out;
|
||||
ret = true;
|
||||
out_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_frag_skb_fwd - forward fragments that would exceed MTU when merged
|
||||
* @skb: skb to forward
|
||||
* @recv_if: interface that the skb is received on
|
||||
* @orig_node_src: originator that the skb is received from
|
||||
*
|
||||
* Look up the next-hop of the fragments payload and check if the merged packet
|
||||
* will exceed the MTU towards the next-hop. If so, the fragment is forwarded
|
||||
* without merging it.
|
||||
*
|
||||
* Returns true if the fragment is consumed/forwarded, false otherwise.
|
||||
*/
|
||||
bool batadv_frag_skb_fwd(struct sk_buff *skb,
|
||||
struct batadv_hard_iface *recv_if,
|
||||
struct batadv_orig_node *orig_node_src)
|
||||
{
|
||||
struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
|
||||
struct batadv_orig_node *orig_node_dst = NULL;
|
||||
struct batadv_neigh_node *neigh_node = NULL;
|
||||
struct batadv_frag_packet *packet;
|
||||
uint16_t total_size;
|
||||
bool ret = false;
|
||||
|
||||
packet = (struct batadv_frag_packet *)skb->data;
|
||||
orig_node_dst = batadv_orig_hash_find(bat_priv, packet->dest);
|
||||
if (!orig_node_dst)
|
||||
goto out;
|
||||
|
||||
neigh_node = batadv_find_router(bat_priv, orig_node_dst, recv_if);
|
||||
if (!neigh_node)
|
||||
goto out;
|
||||
|
||||
/* Forward the fragment, if the merged packet would be too big to
|
||||
* be assembled.
|
||||
*/
|
||||
total_size = ntohs(packet->total_size);
|
||||
if (total_size > neigh_node->if_incoming->net_dev->mtu) {
|
||||
batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD);
|
||||
batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES,
|
||||
skb->len + ETH_HLEN);
|
||||
|
||||
packet->ttl--;
|
||||
batadv_send_skb_packet(skb, neigh_node->if_incoming,
|
||||
neigh_node->addr);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
out:
|
||||
if (orig_node_dst)
|
||||
batadv_orig_node_free_ref(orig_node_dst);
|
||||
if (neigh_node)
|
||||
batadv_neigh_node_free_ref(neigh_node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_frag_create - create a fragment from skb
|
||||
* @skb: skb to create fragment from
|
||||
* @frag_head: header to use in new fragment
|
||||
* @mtu: size of new fragment
|
||||
*
|
||||
* Split the passed skb into two fragments: A new one with size matching the
|
||||
* passed mtu and the old one with the rest. The new skb contains data from the
|
||||
* tail of the old skb.
|
||||
*
|
||||
* Returns the new fragment, NULL on error.
|
||||
*/
|
||||
static struct sk_buff *batadv_frag_create(struct sk_buff *skb,
|
||||
struct batadv_frag_packet *frag_head,
|
||||
unsigned int mtu)
|
||||
{
|
||||
struct sk_buff *skb_fragment;
|
||||
unsigned header_size = sizeof(*frag_head);
|
||||
unsigned fragment_size = mtu - header_size;
|
||||
|
||||
skb_fragment = netdev_alloc_skb(NULL, mtu + ETH_HLEN);
|
||||
if (!skb_fragment)
|
||||
goto err;
|
||||
|
||||
skb->priority = TC_PRIO_CONTROL;
|
||||
|
||||
/* Eat the last mtu-bytes of the skb */
|
||||
skb_reserve(skb_fragment, header_size + ETH_HLEN);
|
||||
skb_split(skb, skb_fragment, skb->len - fragment_size);
|
||||
|
||||
/* Add the header */
|
||||
skb_push(skb_fragment, header_size);
|
||||
memcpy(skb_fragment->data, frag_head, header_size);
|
||||
|
||||
err:
|
||||
return skb_fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_frag_send_packet - create up to 16 fragments from the passed skb
|
||||
* @skb: skb to create fragments from
|
||||
* @orig_node: final destination of the created fragments
|
||||
* @neigh_node: next-hop of the created fragments
|
||||
*
|
||||
* Returns true on success, false otherwise.
|
||||
*/
|
||||
bool batadv_frag_send_packet(struct sk_buff *skb,
|
||||
struct batadv_orig_node *orig_node,
|
||||
struct batadv_neigh_node *neigh_node)
|
||||
{
|
||||
struct batadv_priv *bat_priv;
|
||||
struct batadv_hard_iface *primary_if = NULL;
|
||||
struct batadv_frag_packet frag_header;
|
||||
struct sk_buff *skb_fragment;
|
||||
unsigned mtu = neigh_node->if_incoming->net_dev->mtu;
|
||||
unsigned header_size = sizeof(frag_header);
|
||||
unsigned max_fragment_size, max_packet_size;
|
||||
bool ret = false;
|
||||
|
||||
/* To avoid merge and refragmentation at next-hops we never send
|
||||
* fragments larger than BATADV_FRAG_MAX_FRAG_SIZE
|
||||
*/
|
||||
mtu = min_t(unsigned, mtu, BATADV_FRAG_MAX_FRAG_SIZE);
|
||||
max_fragment_size = mtu - header_size;
|
||||
max_packet_size = max_fragment_size * BATADV_FRAG_MAX_FRAGMENTS;
|
||||
|
||||
/* Don't even try to fragment, if we need more than 16 fragments */
|
||||
if (skb->len > max_packet_size)
|
||||
goto out_err;
|
||||
|
||||
bat_priv = orig_node->bat_priv;
|
||||
primary_if = batadv_primary_if_get_selected(bat_priv);
|
||||
if (!primary_if)
|
||||
goto out_err;
|
||||
|
||||
/* Create one header to be copied to all fragments */
|
||||
frag_header.packet_type = BATADV_UNICAST_FRAG;
|
||||
frag_header.version = BATADV_COMPAT_VERSION;
|
||||
frag_header.ttl = BATADV_TTL;
|
||||
frag_header.seqno = htons(atomic_inc_return(&bat_priv->frag_seqno));
|
||||
frag_header.reserved = 0;
|
||||
frag_header.no = 0;
|
||||
frag_header.total_size = htons(skb->len);
|
||||
ether_addr_copy(frag_header.orig, primary_if->net_dev->dev_addr);
|
||||
ether_addr_copy(frag_header.dest, orig_node->orig);
|
||||
|
||||
/* Eat and send fragments from the tail of skb */
|
||||
while (skb->len > max_fragment_size) {
|
||||
skb_fragment = batadv_frag_create(skb, &frag_header, mtu);
|
||||
if (!skb_fragment)
|
||||
goto out_err;
|
||||
|
||||
batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX);
|
||||
batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES,
|
||||
skb_fragment->len + ETH_HLEN);
|
||||
batadv_send_skb_packet(skb_fragment, neigh_node->if_incoming,
|
||||
neigh_node->addr);
|
||||
frag_header.no++;
|
||||
|
||||
/* The initial check in this function should cover this case */
|
||||
if (frag_header.no == BATADV_FRAG_MAX_FRAGMENTS - 1)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* Make room for the fragment header. */
|
||||
if (batadv_skb_head_push(skb, header_size) < 0 ||
|
||||
pskb_expand_head(skb, header_size + ETH_HLEN, 0, GFP_ATOMIC) < 0)
|
||||
goto out_err;
|
||||
|
||||
memcpy(skb->data, &frag_header, header_size);
|
||||
|
||||
/* Send the last fragment */
|
||||
batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX);
|
||||
batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES,
|
||||
skb->len + ETH_HLEN);
|
||||
batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
|
||||
|
||||
ret = true;
|
||||
|
||||
out_err:
|
||||
if (primary_if)
|
||||
batadv_hardif_free_ref(primary_if);
|
||||
|
||||
return ret;
|
||||
}
|
48
net/batman-adv/fragmentation.h
Normal file
48
net/batman-adv/fragmentation.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/* Copyright (C) 2013-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Martin Hundebøll <martin@hundeboll.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_FRAGMENTATION_H_
|
||||
#define _NET_BATMAN_ADV_FRAGMENTATION_H_
|
||||
|
||||
void batadv_frag_purge_orig(struct batadv_orig_node *orig,
|
||||
bool (*check_cb)(struct batadv_frag_table_entry *));
|
||||
bool batadv_frag_skb_fwd(struct sk_buff *skb,
|
||||
struct batadv_hard_iface *recv_if,
|
||||
struct batadv_orig_node *orig_node_src);
|
||||
bool batadv_frag_skb_buffer(struct sk_buff **skb,
|
||||
struct batadv_orig_node *orig_node);
|
||||
bool batadv_frag_send_packet(struct sk_buff *skb,
|
||||
struct batadv_orig_node *orig_node,
|
||||
struct batadv_neigh_node *neigh_node);
|
||||
|
||||
/**
|
||||
* batadv_frag_check_entry - check if a list of fragments has timed out
|
||||
* @frags_entry: table entry to check
|
||||
*
|
||||
* Returns true if the frags entry has timed out, false otherwise.
|
||||
*/
|
||||
static inline bool
|
||||
batadv_frag_check_entry(struct batadv_frag_table_entry *frags_entry)
|
||||
{
|
||||
if (!hlist_empty(&frags_entry->head) &&
|
||||
batadv_has_timed_out(frags_entry->timestamp, BATADV_FRAG_TIMEOUT))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_FRAGMENTATION_H_ */
|
879
net/batman-adv/gateway_client.c
Normal file
879
net/batman-adv/gateway_client.c
Normal file
|
@ -0,0 +1,879 @@
|
|||
/* Copyright (C) 2009-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "sysfs.h"
|
||||
#include "gateway_client.h"
|
||||
#include "gateway_common.h"
|
||||
#include "hard-interface.h"
|
||||
#include "originator.h"
|
||||
#include "translation-table.h"
|
||||
#include "routing.h"
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/if_vlan.h>
|
||||
|
||||
/* These are the offsets of the "hw type" and "hw address length" in the dhcp
|
||||
* packet starting at the beginning of the dhcp header
|
||||
*/
|
||||
#define BATADV_DHCP_HTYPE_OFFSET 1
|
||||
#define BATADV_DHCP_HLEN_OFFSET 2
|
||||
/* Value of htype representing Ethernet */
|
||||
#define BATADV_DHCP_HTYPE_ETHERNET 0x01
|
||||
/* This is the offset of the "chaddr" field in the dhcp packet starting at the
|
||||
* beginning of the dhcp header
|
||||
*/
|
||||
#define BATADV_DHCP_CHADDR_OFFSET 28
|
||||
|
||||
static void batadv_gw_node_free_ref(struct batadv_gw_node *gw_node)
|
||||
{
|
||||
if (atomic_dec_and_test(&gw_node->refcount)) {
|
||||
batadv_orig_node_free_ref(gw_node->orig_node);
|
||||
kfree_rcu(gw_node, rcu);
|
||||
}
|
||||
}
|
||||
|
||||
static struct batadv_gw_node *
|
||||
batadv_gw_get_selected_gw_node(struct batadv_priv *bat_priv)
|
||||
{
|
||||
struct batadv_gw_node *gw_node;
|
||||
|
||||
rcu_read_lock();
|
||||
gw_node = rcu_dereference(bat_priv->gw.curr_gw);
|
||||
if (!gw_node)
|
||||
goto out;
|
||||
|
||||
if (!atomic_inc_not_zero(&gw_node->refcount))
|
||||
gw_node = NULL;
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
return gw_node;
|
||||
}
|
||||
|
||||
struct batadv_orig_node *
|
||||
batadv_gw_get_selected_orig(struct batadv_priv *bat_priv)
|
||||
{
|
||||
struct batadv_gw_node *gw_node;
|
||||
struct batadv_orig_node *orig_node = NULL;
|
||||
|
||||
gw_node = batadv_gw_get_selected_gw_node(bat_priv);
|
||||
if (!gw_node)
|
||||
goto out;
|
||||
|
||||
rcu_read_lock();
|
||||
orig_node = gw_node->orig_node;
|
||||
if (!orig_node)
|
||||
goto unlock;
|
||||
|
||||
if (!atomic_inc_not_zero(&orig_node->refcount))
|
||||
orig_node = NULL;
|
||||
|
||||
unlock:
|
||||
rcu_read_unlock();
|
||||
out:
|
||||
if (gw_node)
|
||||
batadv_gw_node_free_ref(gw_node);
|
||||
return orig_node;
|
||||
}
|
||||
|
||||
static void batadv_gw_select(struct batadv_priv *bat_priv,
|
||||
struct batadv_gw_node *new_gw_node)
|
||||
{
|
||||
struct batadv_gw_node *curr_gw_node;
|
||||
|
||||
spin_lock_bh(&bat_priv->gw.list_lock);
|
||||
|
||||
if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount))
|
||||
new_gw_node = NULL;
|
||||
|
||||
curr_gw_node = rcu_dereference_protected(bat_priv->gw.curr_gw, 1);
|
||||
rcu_assign_pointer(bat_priv->gw.curr_gw, new_gw_node);
|
||||
|
||||
if (curr_gw_node)
|
||||
batadv_gw_node_free_ref(curr_gw_node);
|
||||
|
||||
spin_unlock_bh(&bat_priv->gw.list_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_gw_reselect - force a gateway reselection
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
*
|
||||
* Set a flag to remind the GW component to perform a new gateway reselection.
|
||||
* However this function does not ensure that the current gateway is going to be
|
||||
* deselected. The reselection mechanism may elect the same gateway once again.
|
||||
*
|
||||
* This means that invoking batadv_gw_reselect() does not guarantee a gateway
|
||||
* change and therefore a uevent is not necessarily expected.
|
||||
*/
|
||||
void batadv_gw_reselect(struct batadv_priv *bat_priv)
|
||||
{
|
||||
atomic_set(&bat_priv->gw.reselect, 1);
|
||||
}
|
||||
|
||||
static struct batadv_gw_node *
|
||||
batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
|
||||
{
|
||||
struct batadv_neigh_node *router;
|
||||
struct batadv_neigh_ifinfo *router_ifinfo;
|
||||
struct batadv_gw_node *gw_node, *curr_gw = NULL;
|
||||
uint32_t max_gw_factor = 0, tmp_gw_factor = 0;
|
||||
uint32_t gw_divisor;
|
||||
uint8_t max_tq = 0;
|
||||
uint8_t tq_avg;
|
||||
struct batadv_orig_node *orig_node;
|
||||
|
||||
gw_divisor = BATADV_TQ_LOCAL_WINDOW_SIZE * BATADV_TQ_LOCAL_WINDOW_SIZE;
|
||||
gw_divisor *= 64;
|
||||
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) {
|
||||
if (gw_node->deleted)
|
||||
continue;
|
||||
|
||||
orig_node = gw_node->orig_node;
|
||||
router = batadv_orig_router_get(orig_node, BATADV_IF_DEFAULT);
|
||||
if (!router)
|
||||
continue;
|
||||
|
||||
router_ifinfo = batadv_neigh_ifinfo_get(router,
|
||||
BATADV_IF_DEFAULT);
|
||||
if (!router_ifinfo)
|
||||
goto next;
|
||||
|
||||
if (!atomic_inc_not_zero(&gw_node->refcount))
|
||||
goto next;
|
||||
|
||||
tq_avg = router_ifinfo->bat_iv.tq_avg;
|
||||
|
||||
switch (atomic_read(&bat_priv->gw_sel_class)) {
|
||||
case 1: /* fast connection */
|
||||
tmp_gw_factor = tq_avg * tq_avg;
|
||||
tmp_gw_factor *= gw_node->bandwidth_down;
|
||||
tmp_gw_factor *= 100 * 100;
|
||||
tmp_gw_factor /= gw_divisor;
|
||||
|
||||
if ((tmp_gw_factor > max_gw_factor) ||
|
||||
((tmp_gw_factor == max_gw_factor) &&
|
||||
(tq_avg > max_tq))) {
|
||||
if (curr_gw)
|
||||
batadv_gw_node_free_ref(curr_gw);
|
||||
curr_gw = gw_node;
|
||||
atomic_inc(&curr_gw->refcount);
|
||||
}
|
||||
break;
|
||||
|
||||
default: /* 2: stable connection (use best statistic)
|
||||
* 3: fast-switch (use best statistic but change as
|
||||
* soon as a better gateway appears)
|
||||
* XX: late-switch (use best statistic but change as
|
||||
* soon as a better gateway appears which has
|
||||
* $routing_class more tq points)
|
||||
*/
|
||||
if (tq_avg > max_tq) {
|
||||
if (curr_gw)
|
||||
batadv_gw_node_free_ref(curr_gw);
|
||||
curr_gw = gw_node;
|
||||
atomic_inc(&curr_gw->refcount);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (tq_avg > max_tq)
|
||||
max_tq = tq_avg;
|
||||
|
||||
if (tmp_gw_factor > max_gw_factor)
|
||||
max_gw_factor = tmp_gw_factor;
|
||||
|
||||
batadv_gw_node_free_ref(gw_node);
|
||||
|
||||
next:
|
||||
batadv_neigh_node_free_ref(router);
|
||||
if (router_ifinfo)
|
||||
batadv_neigh_ifinfo_free_ref(router_ifinfo);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return curr_gw;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_gw_check_client_stop - check if client mode has been switched off
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
*
|
||||
* This function assumes the caller has checked that the gw state *is actually
|
||||
* changing*. This function is not supposed to be called when there is no state
|
||||
* change.
|
||||
*/
|
||||
void batadv_gw_check_client_stop(struct batadv_priv *bat_priv)
|
||||
{
|
||||
struct batadv_gw_node *curr_gw;
|
||||
|
||||
if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_CLIENT)
|
||||
return;
|
||||
|
||||
curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
|
||||
if (!curr_gw)
|
||||
return;
|
||||
|
||||
/* deselect the current gateway so that next time that client mode is
|
||||
* enabled a proper GW_ADD event can be sent
|
||||
*/
|
||||
batadv_gw_select(bat_priv, NULL);
|
||||
|
||||
/* if batman-adv is switching the gw client mode off and a gateway was
|
||||
* already selected, send a DEL uevent
|
||||
*/
|
||||
batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_DEL, NULL);
|
||||
|
||||
batadv_gw_node_free_ref(curr_gw);
|
||||
}
|
||||
|
||||
void batadv_gw_election(struct batadv_priv *bat_priv)
|
||||
{
|
||||
struct batadv_gw_node *curr_gw = NULL, *next_gw = NULL;
|
||||
struct batadv_neigh_node *router = NULL;
|
||||
struct batadv_neigh_ifinfo *router_ifinfo = NULL;
|
||||
char gw_addr[18] = { '\0' };
|
||||
|
||||
if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_CLIENT)
|
||||
goto out;
|
||||
|
||||
curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
|
||||
|
||||
if (!batadv_atomic_dec_not_zero(&bat_priv->gw.reselect) && curr_gw)
|
||||
goto out;
|
||||
|
||||
next_gw = batadv_gw_get_best_gw_node(bat_priv);
|
||||
|
||||
if (curr_gw == next_gw)
|
||||
goto out;
|
||||
|
||||
if (next_gw) {
|
||||
sprintf(gw_addr, "%pM", next_gw->orig_node->orig);
|
||||
|
||||
router = batadv_orig_router_get(next_gw->orig_node,
|
||||
BATADV_IF_DEFAULT);
|
||||
if (!router) {
|
||||
batadv_gw_reselect(bat_priv);
|
||||
goto out;
|
||||
}
|
||||
|
||||
router_ifinfo = batadv_neigh_ifinfo_get(router,
|
||||
BATADV_IF_DEFAULT);
|
||||
if (!router_ifinfo) {
|
||||
batadv_gw_reselect(bat_priv);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if ((curr_gw) && (!next_gw)) {
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"Removing selected gateway - no gateway in range\n");
|
||||
batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_DEL,
|
||||
NULL);
|
||||
} else if ((!curr_gw) && (next_gw)) {
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"Adding route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n",
|
||||
next_gw->orig_node->orig,
|
||||
next_gw->bandwidth_down / 10,
|
||||
next_gw->bandwidth_down % 10,
|
||||
next_gw->bandwidth_up / 10,
|
||||
next_gw->bandwidth_up % 10,
|
||||
router_ifinfo->bat_iv.tq_avg);
|
||||
batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_ADD,
|
||||
gw_addr);
|
||||
} else {
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"Changing route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n",
|
||||
next_gw->orig_node->orig,
|
||||
next_gw->bandwidth_down / 10,
|
||||
next_gw->bandwidth_down % 10,
|
||||
next_gw->bandwidth_up / 10,
|
||||
next_gw->bandwidth_up % 10,
|
||||
router_ifinfo->bat_iv.tq_avg);
|
||||
batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_CHANGE,
|
||||
gw_addr);
|
||||
}
|
||||
|
||||
batadv_gw_select(bat_priv, next_gw);
|
||||
|
||||
out:
|
||||
if (curr_gw)
|
||||
batadv_gw_node_free_ref(curr_gw);
|
||||
if (next_gw)
|
||||
batadv_gw_node_free_ref(next_gw);
|
||||
if (router)
|
||||
batadv_neigh_node_free_ref(router);
|
||||
if (router_ifinfo)
|
||||
batadv_neigh_ifinfo_free_ref(router_ifinfo);
|
||||
}
|
||||
|
||||
void batadv_gw_check_election(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node)
|
||||
{
|
||||
struct batadv_neigh_ifinfo *router_orig_tq = NULL;
|
||||
struct batadv_neigh_ifinfo *router_gw_tq = NULL;
|
||||
struct batadv_orig_node *curr_gw_orig;
|
||||
struct batadv_neigh_node *router_gw = NULL, *router_orig = NULL;
|
||||
uint8_t gw_tq_avg, orig_tq_avg;
|
||||
|
||||
curr_gw_orig = batadv_gw_get_selected_orig(bat_priv);
|
||||
if (!curr_gw_orig)
|
||||
goto reselect;
|
||||
|
||||
router_gw = batadv_orig_router_get(curr_gw_orig, BATADV_IF_DEFAULT);
|
||||
if (!router_gw)
|
||||
goto reselect;
|
||||
|
||||
router_gw_tq = batadv_neigh_ifinfo_get(router_gw,
|
||||
BATADV_IF_DEFAULT);
|
||||
if (!router_gw_tq)
|
||||
goto reselect;
|
||||
|
||||
/* this node already is the gateway */
|
||||
if (curr_gw_orig == orig_node)
|
||||
goto out;
|
||||
|
||||
router_orig = batadv_orig_router_get(orig_node, BATADV_IF_DEFAULT);
|
||||
if (!router_orig)
|
||||
goto out;
|
||||
|
||||
router_orig_tq = batadv_neigh_ifinfo_get(router_orig,
|
||||
BATADV_IF_DEFAULT);
|
||||
if (!router_orig_tq)
|
||||
goto out;
|
||||
|
||||
gw_tq_avg = router_gw_tq->bat_iv.tq_avg;
|
||||
orig_tq_avg = router_orig_tq->bat_iv.tq_avg;
|
||||
|
||||
/* the TQ value has to be better */
|
||||
if (orig_tq_avg < gw_tq_avg)
|
||||
goto out;
|
||||
|
||||
/* if the routing class is greater than 3 the value tells us how much
|
||||
* greater the TQ value of the new gateway must be
|
||||
*/
|
||||
if ((atomic_read(&bat_priv->gw_sel_class) > 3) &&
|
||||
(orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw_sel_class)))
|
||||
goto out;
|
||||
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"Restarting gateway selection: better gateway found (tq curr: %i, tq new: %i)\n",
|
||||
gw_tq_avg, orig_tq_avg);
|
||||
|
||||
reselect:
|
||||
batadv_gw_reselect(bat_priv);
|
||||
out:
|
||||
if (curr_gw_orig)
|
||||
batadv_orig_node_free_ref(curr_gw_orig);
|
||||
if (router_gw)
|
||||
batadv_neigh_node_free_ref(router_gw);
|
||||
if (router_orig)
|
||||
batadv_neigh_node_free_ref(router_orig);
|
||||
if (router_gw_tq)
|
||||
batadv_neigh_ifinfo_free_ref(router_gw_tq);
|
||||
if (router_orig_tq)
|
||||
batadv_neigh_ifinfo_free_ref(router_orig_tq);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_gw_node_add - add gateway node to list of available gateways
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @orig_node: originator announcing gateway capabilities
|
||||
* @gateway: announced bandwidth information
|
||||
*/
|
||||
static void batadv_gw_node_add(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node,
|
||||
struct batadv_tvlv_gateway_data *gateway)
|
||||
{
|
||||
struct batadv_gw_node *gw_node;
|
||||
|
||||
if (gateway->bandwidth_down == 0)
|
||||
return;
|
||||
|
||||
if (!atomic_inc_not_zero(&orig_node->refcount))
|
||||
return;
|
||||
|
||||
gw_node = kzalloc(sizeof(*gw_node), GFP_ATOMIC);
|
||||
if (!gw_node) {
|
||||
batadv_orig_node_free_ref(orig_node);
|
||||
return;
|
||||
}
|
||||
|
||||
INIT_HLIST_NODE(&gw_node->list);
|
||||
gw_node->orig_node = orig_node;
|
||||
atomic_set(&gw_node->refcount, 1);
|
||||
|
||||
spin_lock_bh(&bat_priv->gw.list_lock);
|
||||
hlist_add_head_rcu(&gw_node->list, &bat_priv->gw.list);
|
||||
spin_unlock_bh(&bat_priv->gw.list_lock);
|
||||
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"Found new gateway %pM -> gw bandwidth: %u.%u/%u.%u MBit\n",
|
||||
orig_node->orig,
|
||||
ntohl(gateway->bandwidth_down) / 10,
|
||||
ntohl(gateway->bandwidth_down) % 10,
|
||||
ntohl(gateway->bandwidth_up) / 10,
|
||||
ntohl(gateway->bandwidth_up) % 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_gw_node_get - retrieve gateway node from list of available gateways
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @orig_node: originator announcing gateway capabilities
|
||||
*
|
||||
* Returns gateway node if found or NULL otherwise.
|
||||
*/
|
||||
static struct batadv_gw_node *
|
||||
batadv_gw_node_get(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node)
|
||||
{
|
||||
struct batadv_gw_node *gw_node_tmp, *gw_node = NULL;
|
||||
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(gw_node_tmp, &bat_priv->gw.list, list) {
|
||||
if (gw_node_tmp->orig_node != orig_node)
|
||||
continue;
|
||||
|
||||
if (gw_node_tmp->deleted)
|
||||
continue;
|
||||
|
||||
if (!atomic_inc_not_zero(&gw_node_tmp->refcount))
|
||||
continue;
|
||||
|
||||
gw_node = gw_node_tmp;
|
||||
break;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return gw_node;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_gw_node_update - update list of available gateways with changed
|
||||
* bandwidth information
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @orig_node: originator announcing gateway capabilities
|
||||
* @gateway: announced bandwidth information
|
||||
*/
|
||||
void batadv_gw_node_update(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node,
|
||||
struct batadv_tvlv_gateway_data *gateway)
|
||||
{
|
||||
struct batadv_gw_node *gw_node, *curr_gw = NULL;
|
||||
|
||||
gw_node = batadv_gw_node_get(bat_priv, orig_node);
|
||||
if (!gw_node) {
|
||||
batadv_gw_node_add(bat_priv, orig_node, gateway);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((gw_node->bandwidth_down == ntohl(gateway->bandwidth_down)) &&
|
||||
(gw_node->bandwidth_up == ntohl(gateway->bandwidth_up)))
|
||||
goto out;
|
||||
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"Gateway bandwidth of originator %pM changed from %u.%u/%u.%u MBit to %u.%u/%u.%u MBit\n",
|
||||
orig_node->orig,
|
||||
gw_node->bandwidth_down / 10,
|
||||
gw_node->bandwidth_down % 10,
|
||||
gw_node->bandwidth_up / 10,
|
||||
gw_node->bandwidth_up % 10,
|
||||
ntohl(gateway->bandwidth_down) / 10,
|
||||
ntohl(gateway->bandwidth_down) % 10,
|
||||
ntohl(gateway->bandwidth_up) / 10,
|
||||
ntohl(gateway->bandwidth_up) % 10);
|
||||
|
||||
gw_node->bandwidth_down = ntohl(gateway->bandwidth_down);
|
||||
gw_node->bandwidth_up = ntohl(gateway->bandwidth_up);
|
||||
|
||||
gw_node->deleted = 0;
|
||||
if (ntohl(gateway->bandwidth_down) == 0) {
|
||||
gw_node->deleted = jiffies;
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"Gateway %pM removed from gateway list\n",
|
||||
orig_node->orig);
|
||||
|
||||
/* Note: We don't need a NULL check here, since curr_gw never
|
||||
* gets dereferenced.
|
||||
*/
|
||||
curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
|
||||
if (gw_node == curr_gw)
|
||||
batadv_gw_reselect(bat_priv);
|
||||
}
|
||||
|
||||
out:
|
||||
if (curr_gw)
|
||||
batadv_gw_node_free_ref(curr_gw);
|
||||
if (gw_node)
|
||||
batadv_gw_node_free_ref(gw_node);
|
||||
}
|
||||
|
||||
void batadv_gw_node_delete(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node)
|
||||
{
|
||||
struct batadv_tvlv_gateway_data gateway;
|
||||
|
||||
gateway.bandwidth_down = 0;
|
||||
gateway.bandwidth_up = 0;
|
||||
|
||||
batadv_gw_node_update(bat_priv, orig_node, &gateway);
|
||||
}
|
||||
|
||||
void batadv_gw_node_purge(struct batadv_priv *bat_priv)
|
||||
{
|
||||
struct batadv_gw_node *gw_node, *curr_gw;
|
||||
struct hlist_node *node_tmp;
|
||||
unsigned long timeout = msecs_to_jiffies(2 * BATADV_PURGE_TIMEOUT);
|
||||
int do_reselect = 0;
|
||||
|
||||
curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
|
||||
|
||||
spin_lock_bh(&bat_priv->gw.list_lock);
|
||||
|
||||
hlist_for_each_entry_safe(gw_node, node_tmp,
|
||||
&bat_priv->gw.list, list) {
|
||||
if (((!gw_node->deleted) ||
|
||||
(time_before(jiffies, gw_node->deleted + timeout))) &&
|
||||
atomic_read(&bat_priv->mesh_state) == BATADV_MESH_ACTIVE)
|
||||
continue;
|
||||
|
||||
if (curr_gw == gw_node)
|
||||
do_reselect = 1;
|
||||
|
||||
hlist_del_rcu(&gw_node->list);
|
||||
batadv_gw_node_free_ref(gw_node);
|
||||
}
|
||||
|
||||
spin_unlock_bh(&bat_priv->gw.list_lock);
|
||||
|
||||
/* gw_reselect() needs to acquire the gw_list_lock */
|
||||
if (do_reselect)
|
||||
batadv_gw_reselect(bat_priv);
|
||||
|
||||
if (curr_gw)
|
||||
batadv_gw_node_free_ref(curr_gw);
|
||||
}
|
||||
|
||||
/* fails if orig_node has no router */
|
||||
static int batadv_write_buffer_text(struct batadv_priv *bat_priv,
|
||||
struct seq_file *seq,
|
||||
const struct batadv_gw_node *gw_node)
|
||||
{
|
||||
struct batadv_gw_node *curr_gw;
|
||||
struct batadv_neigh_node *router;
|
||||
struct batadv_neigh_ifinfo *router_ifinfo = NULL;
|
||||
int ret = -1;
|
||||
|
||||
router = batadv_orig_router_get(gw_node->orig_node, BATADV_IF_DEFAULT);
|
||||
if (!router)
|
||||
goto out;
|
||||
|
||||
router_ifinfo = batadv_neigh_ifinfo_get(router, BATADV_IF_DEFAULT);
|
||||
if (!router_ifinfo)
|
||||
goto out;
|
||||
|
||||
curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
|
||||
|
||||
ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %u.%u/%u.%u MBit\n",
|
||||
(curr_gw == gw_node ? "=>" : " "),
|
||||
gw_node->orig_node->orig,
|
||||
router_ifinfo->bat_iv.tq_avg, router->addr,
|
||||
router->if_incoming->net_dev->name,
|
||||
gw_node->bandwidth_down / 10,
|
||||
gw_node->bandwidth_down % 10,
|
||||
gw_node->bandwidth_up / 10,
|
||||
gw_node->bandwidth_up % 10);
|
||||
|
||||
if (curr_gw)
|
||||
batadv_gw_node_free_ref(curr_gw);
|
||||
out:
|
||||
if (router_ifinfo)
|
||||
batadv_neigh_ifinfo_free_ref(router_ifinfo);
|
||||
if (router)
|
||||
batadv_neigh_node_free_ref(router);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int batadv_gw_client_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_gw_node *gw_node;
|
||||
int gw_count = 0;
|
||||
|
||||
primary_if = batadv_seq_print_text_primary_if_get(seq);
|
||||
if (!primary_if)
|
||||
goto out;
|
||||
|
||||
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", "#", BATADV_TQ_MAX_VALUE, "Nexthop", "outgoingIF",
|
||||
BATADV_SOURCE_VERSION, primary_if->net_dev->name,
|
||||
primary_if->net_dev->dev_addr, net_dev->name);
|
||||
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) {
|
||||
if (gw_node->deleted)
|
||||
continue;
|
||||
|
||||
/* fails if orig_node has no router */
|
||||
if (batadv_write_buffer_text(bat_priv, seq, gw_node) < 0)
|
||||
continue;
|
||||
|
||||
gw_count++;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (gw_count == 0)
|
||||
seq_puts(seq, "No gateways in range ...\n");
|
||||
|
||||
out:
|
||||
if (primary_if)
|
||||
batadv_hardif_free_ref(primary_if);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_gw_dhcp_recipient_get - check if a packet is a DHCP message
|
||||
* @skb: the packet to check
|
||||
* @header_len: a pointer to the batman-adv header size
|
||||
* @chaddr: buffer where the client address will be stored. Valid
|
||||
* only if the function returns BATADV_DHCP_TO_CLIENT
|
||||
*
|
||||
* Returns:
|
||||
* - BATADV_DHCP_NO if the packet is not a dhcp message or if there was an error
|
||||
* while parsing it
|
||||
* - BATADV_DHCP_TO_SERVER if this is a message going to the DHCP server
|
||||
* - BATADV_DHCP_TO_CLIENT if this is a message going to a DHCP client
|
||||
*
|
||||
* This function may re-allocate the data buffer of the skb passed as argument.
|
||||
*/
|
||||
enum batadv_dhcp_recipient
|
||||
batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len,
|
||||
uint8_t *chaddr)
|
||||
{
|
||||
enum batadv_dhcp_recipient ret = BATADV_DHCP_NO;
|
||||
struct ethhdr *ethhdr;
|
||||
struct iphdr *iphdr;
|
||||
struct ipv6hdr *ipv6hdr;
|
||||
struct udphdr *udphdr;
|
||||
struct vlan_ethhdr *vhdr;
|
||||
int chaddr_offset;
|
||||
__be16 proto;
|
||||
uint8_t *p;
|
||||
|
||||
/* check for ethernet header */
|
||||
if (!pskb_may_pull(skb, *header_len + ETH_HLEN))
|
||||
return BATADV_DHCP_NO;
|
||||
|
||||
ethhdr = eth_hdr(skb);
|
||||
proto = ethhdr->h_proto;
|
||||
*header_len += ETH_HLEN;
|
||||
|
||||
/* check for initial vlan header */
|
||||
if (proto == htons(ETH_P_8021Q)) {
|
||||
if (!pskb_may_pull(skb, *header_len + VLAN_HLEN))
|
||||
return BATADV_DHCP_NO;
|
||||
|
||||
vhdr = vlan_eth_hdr(skb);
|
||||
proto = vhdr->h_vlan_encapsulated_proto;
|
||||
*header_len += VLAN_HLEN;
|
||||
}
|
||||
|
||||
/* check for ip header */
|
||||
switch (proto) {
|
||||
case htons(ETH_P_IP):
|
||||
if (!pskb_may_pull(skb, *header_len + sizeof(*iphdr)))
|
||||
return BATADV_DHCP_NO;
|
||||
|
||||
iphdr = (struct iphdr *)(skb->data + *header_len);
|
||||
*header_len += iphdr->ihl * 4;
|
||||
|
||||
/* check for udp header */
|
||||
if (iphdr->protocol != IPPROTO_UDP)
|
||||
return BATADV_DHCP_NO;
|
||||
|
||||
break;
|
||||
case htons(ETH_P_IPV6):
|
||||
if (!pskb_may_pull(skb, *header_len + sizeof(*ipv6hdr)))
|
||||
return BATADV_DHCP_NO;
|
||||
|
||||
ipv6hdr = (struct ipv6hdr *)(skb->data + *header_len);
|
||||
*header_len += sizeof(*ipv6hdr);
|
||||
|
||||
/* check for udp header */
|
||||
if (ipv6hdr->nexthdr != IPPROTO_UDP)
|
||||
return BATADV_DHCP_NO;
|
||||
|
||||
break;
|
||||
default:
|
||||
return BATADV_DHCP_NO;
|
||||
}
|
||||
|
||||
if (!pskb_may_pull(skb, *header_len + sizeof(*udphdr)))
|
||||
return BATADV_DHCP_NO;
|
||||
|
||||
/* skb->data might have been reallocated by pskb_may_pull() */
|
||||
ethhdr = eth_hdr(skb);
|
||||
if (ntohs(ethhdr->h_proto) == ETH_P_8021Q)
|
||||
ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN);
|
||||
|
||||
udphdr = (struct udphdr *)(skb->data + *header_len);
|
||||
*header_len += sizeof(*udphdr);
|
||||
|
||||
/* check for bootp port */
|
||||
switch (proto) {
|
||||
case htons(ETH_P_IP):
|
||||
if (udphdr->dest == htons(67))
|
||||
ret = BATADV_DHCP_TO_SERVER;
|
||||
else if (udphdr->source == htons(67))
|
||||
ret = BATADV_DHCP_TO_CLIENT;
|
||||
break;
|
||||
case htons(ETH_P_IPV6):
|
||||
if (udphdr->dest == htons(547))
|
||||
ret = BATADV_DHCP_TO_SERVER;
|
||||
else if (udphdr->source == htons(547))
|
||||
ret = BATADV_DHCP_TO_CLIENT;
|
||||
break;
|
||||
}
|
||||
|
||||
chaddr_offset = *header_len + BATADV_DHCP_CHADDR_OFFSET;
|
||||
/* store the client address if the message is going to a client */
|
||||
if (ret == BATADV_DHCP_TO_CLIENT &&
|
||||
pskb_may_pull(skb, chaddr_offset + ETH_ALEN)) {
|
||||
/* check if the DHCP packet carries an Ethernet DHCP */
|
||||
p = skb->data + *header_len + BATADV_DHCP_HTYPE_OFFSET;
|
||||
if (*p != BATADV_DHCP_HTYPE_ETHERNET)
|
||||
return BATADV_DHCP_NO;
|
||||
|
||||
/* check if the DHCP packet carries a valid Ethernet address */
|
||||
p = skb->data + *header_len + BATADV_DHCP_HLEN_OFFSET;
|
||||
if (*p != ETH_ALEN)
|
||||
return BATADV_DHCP_NO;
|
||||
|
||||
ether_addr_copy(chaddr, skb->data + chaddr_offset);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* batadv_gw_out_of_range - check if the dhcp request destination is the best gw
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @skb: the outgoing packet
|
||||
*
|
||||
* Check if the skb is a DHCP request and if it is sent to the current best GW
|
||||
* server. Due to topology changes it may be the case that the GW server
|
||||
* previously selected is not the best one anymore.
|
||||
*
|
||||
* Returns true if the packet destination is unicast and it is not the best gw,
|
||||
* false otherwise.
|
||||
*
|
||||
* This call might reallocate skb data.
|
||||
* Must be invoked only when the DHCP packet is going TO a DHCP SERVER.
|
||||
*/
|
||||
bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct batadv_neigh_node *neigh_curr = NULL, *neigh_old = NULL;
|
||||
struct batadv_orig_node *orig_dst_node = NULL;
|
||||
struct batadv_gw_node *gw_node = NULL, *curr_gw = NULL;
|
||||
struct batadv_neigh_ifinfo *curr_ifinfo, *old_ifinfo;
|
||||
struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
|
||||
bool out_of_range = false;
|
||||
uint8_t curr_tq_avg;
|
||||
unsigned short vid;
|
||||
|
||||
vid = batadv_get_vid(skb, 0);
|
||||
|
||||
orig_dst_node = batadv_transtable_search(bat_priv, ethhdr->h_source,
|
||||
ethhdr->h_dest, vid);
|
||||
if (!orig_dst_node)
|
||||
goto out;
|
||||
|
||||
gw_node = batadv_gw_node_get(bat_priv, orig_dst_node);
|
||||
if (!gw_node)
|
||||
goto out;
|
||||
|
||||
switch (atomic_read(&bat_priv->gw_mode)) {
|
||||
case BATADV_GW_MODE_SERVER:
|
||||
/* If we are a GW then we are our best GW. We can artificially
|
||||
* set the tq towards ourself as the maximum value
|
||||
*/
|
||||
curr_tq_avg = BATADV_TQ_MAX_VALUE;
|
||||
break;
|
||||
case BATADV_GW_MODE_CLIENT:
|
||||
curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
|
||||
if (!curr_gw)
|
||||
goto out;
|
||||
|
||||
/* packet is going to our gateway */
|
||||
if (curr_gw->orig_node == orig_dst_node)
|
||||
goto out;
|
||||
|
||||
/* If the dhcp packet has been sent to a different gw,
|
||||
* we have to evaluate whether the old gw is still
|
||||
* reliable enough
|
||||
*/
|
||||
neigh_curr = batadv_find_router(bat_priv, curr_gw->orig_node,
|
||||
NULL);
|
||||
if (!neigh_curr)
|
||||
goto out;
|
||||
|
||||
curr_ifinfo = batadv_neigh_ifinfo_get(neigh_curr,
|
||||
BATADV_IF_DEFAULT);
|
||||
if (!curr_ifinfo)
|
||||
goto out;
|
||||
|
||||
curr_tq_avg = curr_ifinfo->bat_iv.tq_avg;
|
||||
batadv_neigh_ifinfo_free_ref(curr_ifinfo);
|
||||
|
||||
break;
|
||||
case BATADV_GW_MODE_OFF:
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
|
||||
neigh_old = batadv_find_router(bat_priv, orig_dst_node, NULL);
|
||||
if (!neigh_old)
|
||||
goto out;
|
||||
|
||||
old_ifinfo = batadv_neigh_ifinfo_get(neigh_old, BATADV_IF_DEFAULT);
|
||||
if (!old_ifinfo)
|
||||
goto out;
|
||||
|
||||
if ((curr_tq_avg - old_ifinfo->bat_iv.tq_avg) > BATADV_GW_THRESHOLD)
|
||||
out_of_range = true;
|
||||
batadv_neigh_ifinfo_free_ref(old_ifinfo);
|
||||
|
||||
out:
|
||||
if (orig_dst_node)
|
||||
batadv_orig_node_free_ref(orig_dst_node);
|
||||
if (curr_gw)
|
||||
batadv_gw_node_free_ref(curr_gw);
|
||||
if (gw_node)
|
||||
batadv_gw_node_free_ref(gw_node);
|
||||
if (neigh_old)
|
||||
batadv_neigh_node_free_ref(neigh_old);
|
||||
if (neigh_curr)
|
||||
batadv_neigh_node_free_ref(neigh_curr);
|
||||
return out_of_range;
|
||||
}
|
40
net/batman-adv/gateway_client.h
Normal file
40
net/batman-adv/gateway_client.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/* Copyright (C) 2009-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_GATEWAY_CLIENT_H_
|
||||
#define _NET_BATMAN_ADV_GATEWAY_CLIENT_H_
|
||||
|
||||
void batadv_gw_check_client_stop(struct batadv_priv *bat_priv);
|
||||
void batadv_gw_reselect(struct batadv_priv *bat_priv);
|
||||
void batadv_gw_election(struct batadv_priv *bat_priv);
|
||||
struct batadv_orig_node *
|
||||
batadv_gw_get_selected_orig(struct batadv_priv *bat_priv);
|
||||
void batadv_gw_check_election(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node);
|
||||
void batadv_gw_node_update(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node,
|
||||
struct batadv_tvlv_gateway_data *gateway);
|
||||
void batadv_gw_node_delete(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node);
|
||||
void batadv_gw_node_purge(struct batadv_priv *bat_priv);
|
||||
int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset);
|
||||
bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, struct sk_buff *skb);
|
||||
enum batadv_dhcp_recipient
|
||||
batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len,
|
||||
uint8_t *chaddr);
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */
|
241
net/batman-adv/gateway_common.c
Normal file
241
net/batman-adv/gateway_common.c
Normal file
|
@ -0,0 +1,241 @@
|
|||
/* Copyright (C) 2009-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "gateway_common.h"
|
||||
#include "gateway_client.h"
|
||||
|
||||
/**
|
||||
* batadv_parse_gw_bandwidth - parse supplied string buffer to extract download
|
||||
* and upload bandwidth information
|
||||
* @net_dev: the soft interface net device
|
||||
* @buff: string buffer to parse
|
||||
* @down: pointer holding the returned download bandwidth information
|
||||
* @up: pointer holding the returned upload bandwidth information
|
||||
*
|
||||
* Returns false on parse error and true otherwise.
|
||||
*/
|
||||
static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
|
||||
uint32_t *down, uint32_t *up)
|
||||
{
|
||||
enum batadv_bandwidth_units bw_unit_type = BATADV_BW_UNIT_KBIT;
|
||||
char *slash_ptr, *tmp_ptr;
|
||||
long ldown, lup;
|
||||
int ret;
|
||||
|
||||
slash_ptr = strchr(buff, '/');
|
||||
if (slash_ptr)
|
||||
*slash_ptr = 0;
|
||||
|
||||
if (strlen(buff) > 4) {
|
||||
tmp_ptr = buff + strlen(buff) - 4;
|
||||
|
||||
if (strncasecmp(tmp_ptr, "mbit", 4) == 0)
|
||||
bw_unit_type = BATADV_BW_UNIT_MBIT;
|
||||
|
||||
if ((strncasecmp(tmp_ptr, "kbit", 4) == 0) ||
|
||||
(bw_unit_type == BATADV_BW_UNIT_MBIT))
|
||||
*tmp_ptr = '\0';
|
||||
}
|
||||
|
||||
ret = kstrtol(buff, 10, &ldown);
|
||||
if (ret) {
|
||||
batadv_err(net_dev,
|
||||
"Download speed of gateway mode invalid: %s\n",
|
||||
buff);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (bw_unit_type) {
|
||||
case BATADV_BW_UNIT_MBIT:
|
||||
*down = ldown * 10;
|
||||
break;
|
||||
case BATADV_BW_UNIT_KBIT:
|
||||
default:
|
||||
*down = ldown / 100;
|
||||
break;
|
||||
}
|
||||
|
||||
/* we also got some upload info */
|
||||
if (slash_ptr) {
|
||||
bw_unit_type = BATADV_BW_UNIT_KBIT;
|
||||
|
||||
if (strlen(slash_ptr + 1) > 4) {
|
||||
tmp_ptr = slash_ptr + 1 - 4 + strlen(slash_ptr + 1);
|
||||
|
||||
if (strncasecmp(tmp_ptr, "mbit", 4) == 0)
|
||||
bw_unit_type = BATADV_BW_UNIT_MBIT;
|
||||
|
||||
if ((strncasecmp(tmp_ptr, "kbit", 4) == 0) ||
|
||||
(bw_unit_type == BATADV_BW_UNIT_MBIT))
|
||||
*tmp_ptr = '\0';
|
||||
}
|
||||
|
||||
ret = kstrtol(slash_ptr + 1, 10, &lup);
|
||||
if (ret) {
|
||||
batadv_err(net_dev,
|
||||
"Upload speed of gateway mode invalid: %s\n",
|
||||
slash_ptr + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (bw_unit_type) {
|
||||
case BATADV_BW_UNIT_MBIT:
|
||||
*up = lup * 10;
|
||||
break;
|
||||
case BATADV_BW_UNIT_KBIT:
|
||||
default:
|
||||
*up = lup / 100;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_gw_tvlv_container_update - update the gw tvlv container after gateway
|
||||
* setting change
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
*/
|
||||
void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv)
|
||||
{
|
||||
struct batadv_tvlv_gateway_data gw;
|
||||
uint32_t down, up;
|
||||
char gw_mode;
|
||||
|
||||
gw_mode = atomic_read(&bat_priv->gw_mode);
|
||||
|
||||
switch (gw_mode) {
|
||||
case BATADV_GW_MODE_OFF:
|
||||
case BATADV_GW_MODE_CLIENT:
|
||||
batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1);
|
||||
break;
|
||||
case BATADV_GW_MODE_SERVER:
|
||||
down = atomic_read(&bat_priv->gw.bandwidth_down);
|
||||
up = atomic_read(&bat_priv->gw.bandwidth_up);
|
||||
gw.bandwidth_down = htonl(down);
|
||||
gw.bandwidth_up = htonl(up);
|
||||
batadv_tvlv_container_register(bat_priv, BATADV_TVLV_GW, 1,
|
||||
&gw, sizeof(gw));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff,
|
||||
size_t count)
|
||||
{
|
||||
struct batadv_priv *bat_priv = netdev_priv(net_dev);
|
||||
uint32_t down_curr, up_curr, down_new = 0, up_new = 0;
|
||||
bool ret;
|
||||
|
||||
down_curr = (unsigned int)atomic_read(&bat_priv->gw.bandwidth_down);
|
||||
up_curr = (unsigned int)atomic_read(&bat_priv->gw.bandwidth_up);
|
||||
|
||||
ret = batadv_parse_gw_bandwidth(net_dev, buff, &down_new, &up_new);
|
||||
if (!ret)
|
||||
goto end;
|
||||
|
||||
if (!down_new)
|
||||
down_new = 1;
|
||||
|
||||
if (!up_new)
|
||||
up_new = down_new / 5;
|
||||
|
||||
if (!up_new)
|
||||
up_new = 1;
|
||||
|
||||
if ((down_curr == down_new) && (up_curr == up_new))
|
||||
return count;
|
||||
|
||||
batadv_gw_reselect(bat_priv);
|
||||
batadv_info(net_dev,
|
||||
"Changing gateway bandwidth from: '%u.%u/%u.%u MBit' to: '%u.%u/%u.%u MBit'\n",
|
||||
down_curr / 10, down_curr % 10, up_curr / 10, up_curr % 10,
|
||||
down_new / 10, down_new % 10, up_new / 10, up_new % 10);
|
||||
|
||||
atomic_set(&bat_priv->gw.bandwidth_down, down_new);
|
||||
atomic_set(&bat_priv->gw.bandwidth_up, up_new);
|
||||
batadv_gw_tvlv_container_update(bat_priv);
|
||||
|
||||
end:
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_gw_tvlv_ogm_handler_v1 - process incoming gateway tvlv container
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @orig: the orig_node of the ogm
|
||||
* @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
|
||||
* @tvlv_value: tvlv buffer containing the gateway data
|
||||
* @tvlv_value_len: tvlv buffer length
|
||||
*/
|
||||
static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig,
|
||||
uint8_t flags,
|
||||
void *tvlv_value,
|
||||
uint16_t tvlv_value_len)
|
||||
{
|
||||
struct batadv_tvlv_gateway_data gateway, *gateway_ptr;
|
||||
|
||||
/* only fetch the tvlv value if the handler wasn't called via the
|
||||
* CIFNOTFND flag and if there is data to fetch
|
||||
*/
|
||||
if ((flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) ||
|
||||
(tvlv_value_len < sizeof(gateway))) {
|
||||
gateway.bandwidth_down = 0;
|
||||
gateway.bandwidth_up = 0;
|
||||
} else {
|
||||
gateway_ptr = tvlv_value;
|
||||
gateway.bandwidth_down = gateway_ptr->bandwidth_down;
|
||||
gateway.bandwidth_up = gateway_ptr->bandwidth_up;
|
||||
if ((gateway.bandwidth_down == 0) ||
|
||||
(gateway.bandwidth_up == 0)) {
|
||||
gateway.bandwidth_down = 0;
|
||||
gateway.bandwidth_up = 0;
|
||||
}
|
||||
}
|
||||
|
||||
batadv_gw_node_update(bat_priv, orig, &gateway);
|
||||
|
||||
/* restart gateway selection if fast or late switching was enabled */
|
||||
if ((gateway.bandwidth_down != 0) &&
|
||||
(atomic_read(&bat_priv->gw_mode) == BATADV_GW_MODE_CLIENT) &&
|
||||
(atomic_read(&bat_priv->gw_sel_class) > 2))
|
||||
batadv_gw_check_election(bat_priv, orig);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_gw_init - initialise the gateway handling internals
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
*/
|
||||
void batadv_gw_init(struct batadv_priv *bat_priv)
|
||||
{
|
||||
batadv_tvlv_handler_register(bat_priv, batadv_gw_tvlv_ogm_handler_v1,
|
||||
NULL, BATADV_TVLV_GW, 1,
|
||||
BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_gw_free - free the gateway handling internals
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
*/
|
||||
void batadv_gw_free(struct batadv_priv *bat_priv)
|
||||
{
|
||||
batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1);
|
||||
batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_GW, 1);
|
||||
}
|
47
net/batman-adv/gateway_common.h
Normal file
47
net/batman-adv/gateway_common.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/* Copyright (C) 2009-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_GATEWAY_COMMON_H_
|
||||
#define _NET_BATMAN_ADV_GATEWAY_COMMON_H_
|
||||
|
||||
enum batadv_gw_modes {
|
||||
BATADV_GW_MODE_OFF,
|
||||
BATADV_GW_MODE_CLIENT,
|
||||
BATADV_GW_MODE_SERVER,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum batadv_bandwidth_units - bandwidth unit types
|
||||
* @BATADV_BW_UNIT_KBIT: unit type kbit
|
||||
* @BATADV_BW_UNIT_MBIT: unit type mbit
|
||||
*/
|
||||
enum batadv_bandwidth_units {
|
||||
BATADV_BW_UNIT_KBIT,
|
||||
BATADV_BW_UNIT_MBIT,
|
||||
};
|
||||
|
||||
#define BATADV_GW_MODE_OFF_NAME "off"
|
||||
#define BATADV_GW_MODE_CLIENT_NAME "client"
|
||||
#define BATADV_GW_MODE_SERVER_NAME "server"
|
||||
|
||||
ssize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff,
|
||||
size_t count);
|
||||
void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv);
|
||||
void batadv_gw_init(struct batadv_priv *bat_priv);
|
||||
void batadv_gw_free(struct batadv_priv *bat_priv);
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_GATEWAY_COMMON_H_ */
|
707
net/batman-adv/hard-interface.c
Normal file
707
net/batman-adv/hard-interface.c
Normal file
|
@ -0,0 +1,707 @@
|
|||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "distributed-arp-table.h"
|
||||
#include "hard-interface.h"
|
||||
#include "soft-interface.h"
|
||||
#include "send.h"
|
||||
#include "translation-table.h"
|
||||
#include "routing.h"
|
||||
#include "sysfs.h"
|
||||
#include "debugfs.h"
|
||||
#include "originator.h"
|
||||
#include "hash.h"
|
||||
#include "bridge_loop_avoidance.h"
|
||||
#include "gateway_client.h"
|
||||
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/if_ether.h>
|
||||
|
||||
void batadv_hardif_free_rcu(struct rcu_head *rcu)
|
||||
{
|
||||
struct batadv_hard_iface *hard_iface;
|
||||
|
||||
hard_iface = container_of(rcu, struct batadv_hard_iface, rcu);
|
||||
dev_put(hard_iface->net_dev);
|
||||
kfree(hard_iface);
|
||||
}
|
||||
|
||||
struct batadv_hard_iface *
|
||||
batadv_hardif_get_by_netdev(const struct net_device *net_dev)
|
||||
{
|
||||
struct batadv_hard_iface *hard_iface;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
|
||||
if (hard_iface->net_dev == net_dev &&
|
||||
atomic_inc_not_zero(&hard_iface->refcount))
|
||||
goto out;
|
||||
}
|
||||
|
||||
hard_iface = NULL;
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
return hard_iface;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_is_on_batman_iface - check if a device is a batman iface descendant
|
||||
* @net_dev: the device to check
|
||||
*
|
||||
* If the user creates any virtual device on top of a batman-adv interface, it
|
||||
* is important to prevent this new interface to be used to create a new mesh
|
||||
* network (this behaviour would lead to a batman-over-batman configuration).
|
||||
* This function recursively checks all the fathers of the device passed as
|
||||
* argument looking for a batman-adv soft interface.
|
||||
*
|
||||
* Returns true if the device is descendant of a batman-adv mesh interface (or
|
||||
* if it is a batman-adv interface itself), false otherwise
|
||||
*/
|
||||
static bool batadv_is_on_batman_iface(const struct net_device *net_dev)
|
||||
{
|
||||
struct net_device *parent_dev;
|
||||
bool ret;
|
||||
|
||||
/* check if this is a batman-adv mesh interface */
|
||||
if (batadv_softif_is_valid(net_dev))
|
||||
return true;
|
||||
|
||||
/* no more parents..stop recursion */
|
||||
if (net_dev->iflink == 0 || net_dev->iflink == net_dev->ifindex)
|
||||
return false;
|
||||
|
||||
/* recurse over the parent device */
|
||||
parent_dev = __dev_get_by_index(&init_net, net_dev->iflink);
|
||||
/* if we got a NULL parent_dev there is something broken.. */
|
||||
if (WARN(!parent_dev, "Cannot find parent device"))
|
||||
return false;
|
||||
|
||||
ret = batadv_is_on_batman_iface(parent_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int batadv_is_valid_iface(const struct net_device *net_dev)
|
||||
{
|
||||
if (net_dev->flags & IFF_LOOPBACK)
|
||||
return 0;
|
||||
|
||||
if (net_dev->type != ARPHRD_ETHER)
|
||||
return 0;
|
||||
|
||||
if (net_dev->addr_len != ETH_ALEN)
|
||||
return 0;
|
||||
|
||||
/* no batman over batman */
|
||||
if (batadv_is_on_batman_iface(net_dev))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_is_wifi_netdev - check if the given net_device struct is a wifi
|
||||
* interface
|
||||
* @net_device: the device to check
|
||||
*
|
||||
* Returns true if the net device is a 802.11 wireless device, false otherwise.
|
||||
*/
|
||||
bool batadv_is_wifi_netdev(struct net_device *net_device)
|
||||
{
|
||||
if (!net_device)
|
||||
return false;
|
||||
|
||||
#ifdef CONFIG_WIRELESS_EXT
|
||||
/* pre-cfg80211 drivers have to implement WEXT, so it is possible to
|
||||
* check for wireless_handlers != NULL
|
||||
*/
|
||||
if (net_device->wireless_handlers)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
/* cfg80211 drivers have to set ieee80211_ptr */
|
||||
if (net_device->ieee80211_ptr)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct batadv_hard_iface *
|
||||
batadv_hardif_get_active(const struct net_device *soft_iface)
|
||||
{
|
||||
struct batadv_hard_iface *hard_iface;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
|
||||
if (hard_iface->soft_iface != soft_iface)
|
||||
continue;
|
||||
|
||||
if (hard_iface->if_status == BATADV_IF_ACTIVE &&
|
||||
atomic_inc_not_zero(&hard_iface->refcount))
|
||||
goto out;
|
||||
}
|
||||
|
||||
hard_iface = NULL;
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
return hard_iface;
|
||||
}
|
||||
|
||||
static void batadv_primary_if_update_addr(struct batadv_priv *bat_priv,
|
||||
struct batadv_hard_iface *oldif)
|
||||
{
|
||||
struct batadv_hard_iface *primary_if;
|
||||
|
||||
primary_if = batadv_primary_if_get_selected(bat_priv);
|
||||
if (!primary_if)
|
||||
goto out;
|
||||
|
||||
batadv_dat_init_own_addr(bat_priv, primary_if);
|
||||
batadv_bla_update_orig_address(bat_priv, primary_if, oldif);
|
||||
out:
|
||||
if (primary_if)
|
||||
batadv_hardif_free_ref(primary_if);
|
||||
}
|
||||
|
||||
static void batadv_primary_if_select(struct batadv_priv *bat_priv,
|
||||
struct batadv_hard_iface *new_hard_iface)
|
||||
{
|
||||
struct batadv_hard_iface *curr_hard_iface;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (new_hard_iface && !atomic_inc_not_zero(&new_hard_iface->refcount))
|
||||
new_hard_iface = NULL;
|
||||
|
||||
curr_hard_iface = rcu_dereference_protected(bat_priv->primary_if, 1);
|
||||
rcu_assign_pointer(bat_priv->primary_if, new_hard_iface);
|
||||
|
||||
if (!new_hard_iface)
|
||||
goto out;
|
||||
|
||||
bat_priv->bat_algo_ops->bat_primary_iface_set(new_hard_iface);
|
||||
batadv_primary_if_update_addr(bat_priv, curr_hard_iface);
|
||||
|
||||
out:
|
||||
if (curr_hard_iface)
|
||||
batadv_hardif_free_ref(curr_hard_iface);
|
||||
}
|
||||
|
||||
static bool
|
||||
batadv_hardif_is_iface_up(const struct batadv_hard_iface *hard_iface)
|
||||
{
|
||||
if (hard_iface->net_dev->flags & IFF_UP)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void batadv_check_known_mac_addr(const struct net_device *net_dev)
|
||||
{
|
||||
const struct batadv_hard_iface *hard_iface;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
|
||||
if ((hard_iface->if_status != BATADV_IF_ACTIVE) &&
|
||||
(hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED))
|
||||
continue;
|
||||
|
||||
if (hard_iface->net_dev == net_dev)
|
||||
continue;
|
||||
|
||||
if (!batadv_compare_eth(hard_iface->net_dev->dev_addr,
|
||||
net_dev->dev_addr))
|
||||
continue;
|
||||
|
||||
pr_warn("The newly added mac address (%pM) already exists on: %s\n",
|
||||
net_dev->dev_addr, hard_iface->net_dev->name);
|
||||
pr_warn("It is strongly recommended to keep mac addresses unique to avoid problems!\n");
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
int batadv_hardif_min_mtu(struct net_device *soft_iface)
|
||||
{
|
||||
struct batadv_priv *bat_priv = netdev_priv(soft_iface);
|
||||
const struct batadv_hard_iface *hard_iface;
|
||||
int min_mtu = INT_MAX;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
|
||||
if ((hard_iface->if_status != BATADV_IF_ACTIVE) &&
|
||||
(hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED))
|
||||
continue;
|
||||
|
||||
if (hard_iface->soft_iface != soft_iface)
|
||||
continue;
|
||||
|
||||
min_mtu = min_t(int, hard_iface->net_dev->mtu, min_mtu);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (atomic_read(&bat_priv->fragmentation) == 0)
|
||||
goto out;
|
||||
|
||||
/* with fragmentation enabled the maximum size of internally generated
|
||||
* packets such as translation table exchanges or tvlv containers, etc
|
||||
* has to be calculated
|
||||
*/
|
||||
min_mtu = min_t(int, min_mtu, BATADV_FRAG_MAX_FRAG_SIZE);
|
||||
min_mtu -= sizeof(struct batadv_frag_packet);
|
||||
min_mtu *= BATADV_FRAG_MAX_FRAGMENTS;
|
||||
|
||||
out:
|
||||
/* report to the other components the maximum amount of bytes that
|
||||
* batman-adv can send over the wire (without considering the payload
|
||||
* overhead). For example, this value is used by TT to compute the
|
||||
* maximum local table table size
|
||||
*/
|
||||
atomic_set(&bat_priv->packet_size_max, min_mtu);
|
||||
|
||||
/* the real soft-interface MTU is computed by removing the payload
|
||||
* overhead from the maximum amount of bytes that was just computed.
|
||||
*
|
||||
* However batman-adv does not support MTUs bigger than ETH_DATA_LEN
|
||||
*/
|
||||
return min_t(int, min_mtu - batadv_max_header_len(), ETH_DATA_LEN);
|
||||
}
|
||||
|
||||
/* adjusts the MTU if a new interface with a smaller MTU appeared. */
|
||||
void batadv_update_min_mtu(struct net_device *soft_iface)
|
||||
{
|
||||
soft_iface->mtu = batadv_hardif_min_mtu(soft_iface);
|
||||
|
||||
/* Check if the local translate table should be cleaned up to match a
|
||||
* new (and smaller) MTU.
|
||||
*/
|
||||
batadv_tt_local_resize_to_mtu(soft_iface);
|
||||
}
|
||||
|
||||
static void
|
||||
batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface)
|
||||
{
|
||||
struct batadv_priv *bat_priv;
|
||||
struct batadv_hard_iface *primary_if = NULL;
|
||||
|
||||
if (hard_iface->if_status != BATADV_IF_INACTIVE)
|
||||
goto out;
|
||||
|
||||
bat_priv = netdev_priv(hard_iface->soft_iface);
|
||||
|
||||
bat_priv->bat_algo_ops->bat_iface_update_mac(hard_iface);
|
||||
hard_iface->if_status = BATADV_IF_TO_BE_ACTIVATED;
|
||||
|
||||
/* the first active interface becomes our primary interface or
|
||||
* the next active interface after the old primary interface was removed
|
||||
*/
|
||||
primary_if = batadv_primary_if_get_selected(bat_priv);
|
||||
if (!primary_if)
|
||||
batadv_primary_if_select(bat_priv, hard_iface);
|
||||
|
||||
batadv_info(hard_iface->soft_iface, "Interface activated: %s\n",
|
||||
hard_iface->net_dev->name);
|
||||
|
||||
batadv_update_min_mtu(hard_iface->soft_iface);
|
||||
|
||||
out:
|
||||
if (primary_if)
|
||||
batadv_hardif_free_ref(primary_if);
|
||||
}
|
||||
|
||||
static void
|
||||
batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface)
|
||||
{
|
||||
if ((hard_iface->if_status != BATADV_IF_ACTIVE) &&
|
||||
(hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED))
|
||||
return;
|
||||
|
||||
hard_iface->if_status = BATADV_IF_INACTIVE;
|
||||
|
||||
batadv_info(hard_iface->soft_iface, "Interface deactivated: %s\n",
|
||||
hard_iface->net_dev->name);
|
||||
|
||||
batadv_update_min_mtu(hard_iface->soft_iface);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_master_del_slave - remove hard_iface from the current master interface
|
||||
* @slave: the interface enslaved in another master
|
||||
* @master: the master from which slave has to be removed
|
||||
*
|
||||
* Invoke ndo_del_slave on master passing slave as argument. In this way slave
|
||||
* is free'd and master can correctly change its internal state.
|
||||
* Return 0 on success, a negative value representing the error otherwise
|
||||
*/
|
||||
static int batadv_master_del_slave(struct batadv_hard_iface *slave,
|
||||
struct net_device *master)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!master)
|
||||
return 0;
|
||||
|
||||
ret = -EBUSY;
|
||||
if (master->netdev_ops->ndo_del_slave)
|
||||
ret = master->netdev_ops->ndo_del_slave(master, slave->net_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
|
||||
const char *iface_name)
|
||||
{
|
||||
struct batadv_priv *bat_priv;
|
||||
struct net_device *soft_iface, *master;
|
||||
__be16 ethertype = htons(ETH_P_BATMAN);
|
||||
int max_header_len = batadv_max_header_len();
|
||||
int ret;
|
||||
|
||||
if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
|
||||
goto out;
|
||||
|
||||
if (!atomic_inc_not_zero(&hard_iface->refcount))
|
||||
goto out;
|
||||
|
||||
soft_iface = dev_get_by_name(&init_net, iface_name);
|
||||
|
||||
if (!soft_iface) {
|
||||
soft_iface = batadv_softif_create(iface_name);
|
||||
|
||||
if (!soft_iface) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* dev_get_by_name() increases the reference counter for us */
|
||||
dev_hold(soft_iface);
|
||||
}
|
||||
|
||||
if (!batadv_softif_is_valid(soft_iface)) {
|
||||
pr_err("Can't create batman mesh interface %s: already exists as regular interface\n",
|
||||
soft_iface->name);
|
||||
ret = -EINVAL;
|
||||
goto err_dev;
|
||||
}
|
||||
|
||||
/* check if the interface is enslaved in another virtual one and
|
||||
* in that case unlink it first
|
||||
*/
|
||||
master = netdev_master_upper_dev_get(hard_iface->net_dev);
|
||||
ret = batadv_master_del_slave(hard_iface, master);
|
||||
if (ret)
|
||||
goto err_dev;
|
||||
|
||||
hard_iface->soft_iface = soft_iface;
|
||||
bat_priv = netdev_priv(hard_iface->soft_iface);
|
||||
|
||||
ret = netdev_master_upper_dev_link(hard_iface->net_dev, soft_iface);
|
||||
if (ret)
|
||||
goto err_dev;
|
||||
|
||||
ret = bat_priv->bat_algo_ops->bat_iface_enable(hard_iface);
|
||||
if (ret < 0)
|
||||
goto err_upper;
|
||||
|
||||
hard_iface->if_num = bat_priv->num_ifaces;
|
||||
bat_priv->num_ifaces++;
|
||||
hard_iface->if_status = BATADV_IF_INACTIVE;
|
||||
ret = batadv_orig_hash_add_if(hard_iface, bat_priv->num_ifaces);
|
||||
if (ret < 0) {
|
||||
bat_priv->bat_algo_ops->bat_iface_disable(hard_iface);
|
||||
bat_priv->num_ifaces--;
|
||||
hard_iface->if_status = BATADV_IF_NOT_IN_USE;
|
||||
goto err_upper;
|
||||
}
|
||||
|
||||
hard_iface->batman_adv_ptype.type = ethertype;
|
||||
hard_iface->batman_adv_ptype.func = batadv_batman_skb_recv;
|
||||
hard_iface->batman_adv_ptype.dev = hard_iface->net_dev;
|
||||
dev_add_pack(&hard_iface->batman_adv_ptype);
|
||||
|
||||
batadv_info(hard_iface->soft_iface, "Adding interface: %s\n",
|
||||
hard_iface->net_dev->name);
|
||||
|
||||
if (atomic_read(&bat_priv->fragmentation) &&
|
||||
hard_iface->net_dev->mtu < ETH_DATA_LEN + max_header_len)
|
||||
batadv_info(hard_iface->soft_iface,
|
||||
"The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. Packets going over this interface will be fragmented on layer2 which could impact the performance. Setting the MTU to %i would solve the problem.\n",
|
||||
hard_iface->net_dev->name, hard_iface->net_dev->mtu,
|
||||
ETH_DATA_LEN + max_header_len);
|
||||
|
||||
if (!atomic_read(&bat_priv->fragmentation) &&
|
||||
hard_iface->net_dev->mtu < ETH_DATA_LEN + max_header_len)
|
||||
batadv_info(hard_iface->soft_iface,
|
||||
"The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. If you experience problems getting traffic through try increasing the MTU to %i.\n",
|
||||
hard_iface->net_dev->name, hard_iface->net_dev->mtu,
|
||||
ETH_DATA_LEN + max_header_len);
|
||||
|
||||
if (batadv_hardif_is_iface_up(hard_iface))
|
||||
batadv_hardif_activate_interface(hard_iface);
|
||||
else
|
||||
batadv_err(hard_iface->soft_iface,
|
||||
"Not using interface %s (retrying later): interface not active\n",
|
||||
hard_iface->net_dev->name);
|
||||
|
||||
/* begin scheduling originator messages on that interface */
|
||||
batadv_schedule_bat_ogm(hard_iface);
|
||||
|
||||
out:
|
||||
return 0;
|
||||
|
||||
err_upper:
|
||||
netdev_upper_dev_unlink(hard_iface->net_dev, soft_iface);
|
||||
err_dev:
|
||||
hard_iface->soft_iface = NULL;
|
||||
dev_put(soft_iface);
|
||||
err:
|
||||
batadv_hardif_free_ref(hard_iface);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface,
|
||||
enum batadv_hard_if_cleanup autodel)
|
||||
{
|
||||
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
|
||||
struct batadv_hard_iface *primary_if = NULL;
|
||||
|
||||
if (hard_iface->if_status == BATADV_IF_ACTIVE)
|
||||
batadv_hardif_deactivate_interface(hard_iface);
|
||||
|
||||
if (hard_iface->if_status != BATADV_IF_INACTIVE)
|
||||
goto out;
|
||||
|
||||
batadv_info(hard_iface->soft_iface, "Removing interface: %s\n",
|
||||
hard_iface->net_dev->name);
|
||||
dev_remove_pack(&hard_iface->batman_adv_ptype);
|
||||
|
||||
bat_priv->num_ifaces--;
|
||||
batadv_orig_hash_del_if(hard_iface, bat_priv->num_ifaces);
|
||||
|
||||
primary_if = batadv_primary_if_get_selected(bat_priv);
|
||||
if (hard_iface == primary_if) {
|
||||
struct batadv_hard_iface *new_if;
|
||||
|
||||
new_if = batadv_hardif_get_active(hard_iface->soft_iface);
|
||||
batadv_primary_if_select(bat_priv, new_if);
|
||||
|
||||
if (new_if)
|
||||
batadv_hardif_free_ref(new_if);
|
||||
}
|
||||
|
||||
bat_priv->bat_algo_ops->bat_iface_disable(hard_iface);
|
||||
hard_iface->if_status = BATADV_IF_NOT_IN_USE;
|
||||
|
||||
/* delete all references to this hard_iface */
|
||||
batadv_purge_orig_ref(bat_priv);
|
||||
batadv_purge_outstanding_packets(bat_priv, hard_iface);
|
||||
dev_put(hard_iface->soft_iface);
|
||||
|
||||
/* nobody uses this interface anymore */
|
||||
if (!bat_priv->num_ifaces) {
|
||||
batadv_gw_check_client_stop(bat_priv);
|
||||
|
||||
if (autodel == BATADV_IF_CLEANUP_AUTO)
|
||||
batadv_softif_destroy_sysfs(hard_iface->soft_iface);
|
||||
}
|
||||
|
||||
netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->soft_iface);
|
||||
hard_iface->soft_iface = NULL;
|
||||
batadv_hardif_free_ref(hard_iface);
|
||||
|
||||
out:
|
||||
if (primary_if)
|
||||
batadv_hardif_free_ref(primary_if);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_hardif_remove_interface_finish - cleans up the remains of a hardif
|
||||
* @work: work queue item
|
||||
*
|
||||
* Free the parts of the hard interface which can not be removed under
|
||||
* rtnl lock (to prevent deadlock situations).
|
||||
*/
|
||||
static void batadv_hardif_remove_interface_finish(struct work_struct *work)
|
||||
{
|
||||
struct batadv_hard_iface *hard_iface;
|
||||
|
||||
hard_iface = container_of(work, struct batadv_hard_iface,
|
||||
cleanup_work);
|
||||
|
||||
batadv_debugfs_del_hardif(hard_iface);
|
||||
batadv_sysfs_del_hardif(&hard_iface->hardif_obj);
|
||||
batadv_hardif_free_ref(hard_iface);
|
||||
}
|
||||
|
||||
static struct batadv_hard_iface *
|
||||
batadv_hardif_add_interface(struct net_device *net_dev)
|
||||
{
|
||||
struct batadv_hard_iface *hard_iface;
|
||||
int ret;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
ret = batadv_is_valid_iface(net_dev);
|
||||
if (ret != 1)
|
||||
goto out;
|
||||
|
||||
dev_hold(net_dev);
|
||||
|
||||
hard_iface = kzalloc(sizeof(*hard_iface), GFP_ATOMIC);
|
||||
if (!hard_iface)
|
||||
goto release_dev;
|
||||
|
||||
ret = batadv_sysfs_add_hardif(&hard_iface->hardif_obj, net_dev);
|
||||
if (ret)
|
||||
goto free_if;
|
||||
|
||||
hard_iface->if_num = -1;
|
||||
hard_iface->net_dev = net_dev;
|
||||
hard_iface->soft_iface = NULL;
|
||||
hard_iface->if_status = BATADV_IF_NOT_IN_USE;
|
||||
|
||||
ret = batadv_debugfs_add_hardif(hard_iface);
|
||||
if (ret)
|
||||
goto free_sysfs;
|
||||
|
||||
INIT_LIST_HEAD(&hard_iface->list);
|
||||
INIT_WORK(&hard_iface->cleanup_work,
|
||||
batadv_hardif_remove_interface_finish);
|
||||
|
||||
hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
|
||||
if (batadv_is_wifi_netdev(net_dev))
|
||||
hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
|
||||
|
||||
/* extra reference for return */
|
||||
atomic_set(&hard_iface->refcount, 2);
|
||||
|
||||
batadv_check_known_mac_addr(hard_iface->net_dev);
|
||||
list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
|
||||
|
||||
return hard_iface;
|
||||
|
||||
free_sysfs:
|
||||
batadv_sysfs_del_hardif(&hard_iface->hardif_obj);
|
||||
free_if:
|
||||
kfree(hard_iface);
|
||||
release_dev:
|
||||
dev_put(net_dev);
|
||||
out:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)
|
||||
{
|
||||
ASSERT_RTNL();
|
||||
|
||||
/* first deactivate interface */
|
||||
if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
|
||||
batadv_hardif_disable_interface(hard_iface,
|
||||
BATADV_IF_CLEANUP_AUTO);
|
||||
|
||||
if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
|
||||
return;
|
||||
|
||||
hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
|
||||
queue_work(batadv_event_workqueue, &hard_iface->cleanup_work);
|
||||
}
|
||||
|
||||
void batadv_hardif_remove_interfaces(void)
|
||||
{
|
||||
struct batadv_hard_iface *hard_iface, *hard_iface_tmp;
|
||||
|
||||
rtnl_lock();
|
||||
list_for_each_entry_safe(hard_iface, hard_iface_tmp,
|
||||
&batadv_hardif_list, list) {
|
||||
list_del_rcu(&hard_iface->list);
|
||||
batadv_hardif_remove_interface(hard_iface);
|
||||
}
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
static int batadv_hard_if_event(struct notifier_block *this,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct net_device *net_dev = netdev_notifier_info_to_dev(ptr);
|
||||
struct batadv_hard_iface *hard_iface;
|
||||
struct batadv_hard_iface *primary_if = NULL;
|
||||
struct batadv_priv *bat_priv;
|
||||
|
||||
if (batadv_softif_is_valid(net_dev) && event == NETDEV_REGISTER) {
|
||||
batadv_sysfs_add_meshif(net_dev);
|
||||
bat_priv = netdev_priv(net_dev);
|
||||
batadv_softif_create_vlan(bat_priv, BATADV_NO_FLAGS);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
hard_iface = batadv_hardif_get_by_netdev(net_dev);
|
||||
if (!hard_iface && event == NETDEV_REGISTER)
|
||||
hard_iface = batadv_hardif_add_interface(net_dev);
|
||||
|
||||
if (!hard_iface)
|
||||
goto out;
|
||||
|
||||
switch (event) {
|
||||
case NETDEV_UP:
|
||||
batadv_hardif_activate_interface(hard_iface);
|
||||
break;
|
||||
case NETDEV_GOING_DOWN:
|
||||
case NETDEV_DOWN:
|
||||
batadv_hardif_deactivate_interface(hard_iface);
|
||||
break;
|
||||
case NETDEV_UNREGISTER:
|
||||
list_del_rcu(&hard_iface->list);
|
||||
|
||||
batadv_hardif_remove_interface(hard_iface);
|
||||
break;
|
||||
case NETDEV_CHANGEMTU:
|
||||
if (hard_iface->soft_iface)
|
||||
batadv_update_min_mtu(hard_iface->soft_iface);
|
||||
break;
|
||||
case NETDEV_CHANGEADDR:
|
||||
if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
|
||||
goto hardif_put;
|
||||
|
||||
batadv_check_known_mac_addr(hard_iface->net_dev);
|
||||
|
||||
bat_priv = netdev_priv(hard_iface->soft_iface);
|
||||
bat_priv->bat_algo_ops->bat_iface_update_mac(hard_iface);
|
||||
|
||||
primary_if = batadv_primary_if_get_selected(bat_priv);
|
||||
if (!primary_if)
|
||||
goto hardif_put;
|
||||
|
||||
if (hard_iface == primary_if)
|
||||
batadv_primary_if_update_addr(bat_priv, NULL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
hardif_put:
|
||||
batadv_hardif_free_ref(hard_iface);
|
||||
out:
|
||||
if (primary_if)
|
||||
batadv_hardif_free_ref(primary_if);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
struct notifier_block batadv_hard_if_notifier = {
|
||||
.notifier_call = batadv_hard_if_event,
|
||||
};
|
97
net/batman-adv/hard-interface.h
Normal file
97
net/batman-adv/hard-interface.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_HARD_INTERFACE_H_
|
||||
#define _NET_BATMAN_ADV_HARD_INTERFACE_H_
|
||||
|
||||
enum batadv_hard_if_state {
|
||||
BATADV_IF_NOT_IN_USE,
|
||||
BATADV_IF_TO_BE_REMOVED,
|
||||
BATADV_IF_INACTIVE,
|
||||
BATADV_IF_ACTIVE,
|
||||
BATADV_IF_TO_BE_ACTIVATED,
|
||||
BATADV_IF_I_WANT_YOU,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum batadv_hard_if_cleanup - Cleanup modi for soft_iface after slave removal
|
||||
* @BATADV_IF_CLEANUP_KEEP: Don't automatically delete soft-interface
|
||||
* @BATADV_IF_CLEANUP_AUTO: Delete soft-interface after last slave was removed
|
||||
*/
|
||||
enum batadv_hard_if_cleanup {
|
||||
BATADV_IF_CLEANUP_KEEP,
|
||||
BATADV_IF_CLEANUP_AUTO,
|
||||
};
|
||||
|
||||
extern struct notifier_block batadv_hard_if_notifier;
|
||||
|
||||
bool batadv_is_wifi_netdev(struct net_device *net_device);
|
||||
bool batadv_is_wifi_iface(int ifindex);
|
||||
struct batadv_hard_iface*
|
||||
batadv_hardif_get_by_netdev(const struct net_device *net_dev);
|
||||
int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
|
||||
const char *iface_name);
|
||||
void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface,
|
||||
enum batadv_hard_if_cleanup autodel);
|
||||
void batadv_hardif_remove_interfaces(void);
|
||||
int batadv_hardif_min_mtu(struct net_device *soft_iface);
|
||||
void batadv_update_min_mtu(struct net_device *soft_iface);
|
||||
void batadv_hardif_free_rcu(struct rcu_head *rcu);
|
||||
|
||||
/**
|
||||
* batadv_hardif_free_ref - decrement the hard interface refcounter and
|
||||
* possibly free it
|
||||
* @hard_iface: the hard interface to free
|
||||
*/
|
||||
static inline void
|
||||
batadv_hardif_free_ref(struct batadv_hard_iface *hard_iface)
|
||||
{
|
||||
if (atomic_dec_and_test(&hard_iface->refcount))
|
||||
call_rcu(&hard_iface->rcu, batadv_hardif_free_rcu);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_hardif_free_ref_now - decrement the hard interface refcounter and
|
||||
* possibly free it (without rcu callback)
|
||||
* @hard_iface: the hard interface to free
|
||||
*/
|
||||
static inline void
|
||||
batadv_hardif_free_ref_now(struct batadv_hard_iface *hard_iface)
|
||||
{
|
||||
if (atomic_dec_and_test(&hard_iface->refcount))
|
||||
batadv_hardif_free_rcu(&hard_iface->rcu);
|
||||
}
|
||||
|
||||
static inline struct batadv_hard_iface *
|
||||
batadv_primary_if_get_selected(struct batadv_priv *bat_priv)
|
||||
{
|
||||
struct batadv_hard_iface *hard_iface;
|
||||
|
||||
rcu_read_lock();
|
||||
hard_iface = rcu_dereference(bat_priv->primary_if);
|
||||
if (!hard_iface)
|
||||
goto out;
|
||||
|
||||
if (!atomic_inc_not_zero(&hard_iface->refcount))
|
||||
hard_iface = NULL;
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
return hard_iface;
|
||||
}
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_HARD_INTERFACE_H_ */
|
76
net/batman-adv/hash.c
Normal file
76
net/batman-adv/hash.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* Copyright (C) 2006-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Simon Wunderlich, Marek Lindner
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "hash.h"
|
||||
|
||||
/* clears the hash */
|
||||
static void batadv_hash_init(struct batadv_hashtable *hash)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < hash->size; i++) {
|
||||
INIT_HLIST_HEAD(&hash->table[i]);
|
||||
spin_lock_init(&hash->list_locks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* free only the hashtable and the hash itself. */
|
||||
void batadv_hash_destroy(struct batadv_hashtable *hash)
|
||||
{
|
||||
kfree(hash->list_locks);
|
||||
kfree(hash->table);
|
||||
kfree(hash);
|
||||
}
|
||||
|
||||
/* allocates and clears the hash */
|
||||
struct batadv_hashtable *batadv_hash_new(uint32_t size)
|
||||
{
|
||||
struct batadv_hashtable *hash;
|
||||
|
||||
hash = kmalloc(sizeof(*hash), GFP_ATOMIC);
|
||||
if (!hash)
|
||||
return NULL;
|
||||
|
||||
hash->table = kmalloc_array(size, sizeof(*hash->table), GFP_ATOMIC);
|
||||
if (!hash->table)
|
||||
goto free_hash;
|
||||
|
||||
hash->list_locks = kmalloc_array(size, sizeof(*hash->list_locks),
|
||||
GFP_ATOMIC);
|
||||
if (!hash->list_locks)
|
||||
goto free_table;
|
||||
|
||||
hash->size = size;
|
||||
batadv_hash_init(hash);
|
||||
return hash;
|
||||
|
||||
free_table:
|
||||
kfree(hash->table);
|
||||
free_hash:
|
||||
kfree(hash);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void batadv_hash_set_lock_class(struct batadv_hashtable *hash,
|
||||
struct lock_class_key *key)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < hash->size; i++)
|
||||
lockdep_set_class(&hash->list_locks[i], key);
|
||||
}
|
187
net/batman-adv/hash.h
Normal file
187
net/batman-adv/hash.h
Normal file
|
@ -0,0 +1,187 @@
|
|||
/* Copyright (C) 2006-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Simon Wunderlich, Marek Lindner
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_HASH_H_
|
||||
#define _NET_BATMAN_ADV_HASH_H_
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
/* callback to a compare function. should compare 2 element datas for their
|
||||
* keys, return 0 if same and not 0 if not same
|
||||
*/
|
||||
typedef int (*batadv_hashdata_compare_cb)(const struct hlist_node *,
|
||||
const void *);
|
||||
|
||||
/* the hashfunction, should return an index
|
||||
* based on the key in the data of the first
|
||||
* argument and the size the second
|
||||
*/
|
||||
typedef uint32_t (*batadv_hashdata_choose_cb)(const void *, uint32_t);
|
||||
typedef void (*batadv_hashdata_free_cb)(struct hlist_node *, void *);
|
||||
|
||||
struct batadv_hashtable {
|
||||
struct hlist_head *table; /* the hashtable itself with the buckets */
|
||||
spinlock_t *list_locks; /* spinlock for each hash list entry */
|
||||
uint32_t size; /* size of hashtable */
|
||||
};
|
||||
|
||||
/* allocates and clears the hash */
|
||||
struct batadv_hashtable *batadv_hash_new(uint32_t size);
|
||||
|
||||
/* set class key for all locks */
|
||||
void batadv_hash_set_lock_class(struct batadv_hashtable *hash,
|
||||
struct lock_class_key *key);
|
||||
|
||||
/* free only the hashtable and the hash itself. */
|
||||
void batadv_hash_destroy(struct batadv_hashtable *hash);
|
||||
|
||||
/* remove the hash structure. if hashdata_free_cb != NULL, this function will be
|
||||
* called to remove the elements inside of the hash. if you don't remove the
|
||||
* elements, memory might be leaked.
|
||||
*/
|
||||
static inline void batadv_hash_delete(struct batadv_hashtable *hash,
|
||||
batadv_hashdata_free_cb free_cb,
|
||||
void *arg)
|
||||
{
|
||||
struct hlist_head *head;
|
||||
struct hlist_node *node, *node_tmp;
|
||||
spinlock_t *list_lock; /* spinlock to protect write access */
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < hash->size; i++) {
|
||||
head = &hash->table[i];
|
||||
list_lock = &hash->list_locks[i];
|
||||
|
||||
spin_lock_bh(list_lock);
|
||||
hlist_for_each_safe(node, node_tmp, head) {
|
||||
hlist_del_rcu(node);
|
||||
|
||||
if (free_cb)
|
||||
free_cb(node, arg);
|
||||
}
|
||||
spin_unlock_bh(list_lock);
|
||||
}
|
||||
|
||||
batadv_hash_destroy(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_hash_bytes - hash some bytes and add them to the previous hash
|
||||
* @hash: previous hash value
|
||||
* @data: data to be hashed
|
||||
* @size: number of bytes to be hashed
|
||||
*
|
||||
* Returns the new hash value.
|
||||
*/
|
||||
static inline uint32_t batadv_hash_bytes(uint32_t hash, const void *data,
|
||||
uint32_t size)
|
||||
{
|
||||
const unsigned char *key = data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
hash += key[i];
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_hash_add - adds data to the hashtable
|
||||
* @hash: storage hash table
|
||||
* @compare: callback to determine if 2 hash elements are identical
|
||||
* @choose: callback calculating the hash index
|
||||
* @data: data passed to the aforementioned callbacks as argument
|
||||
* @data_node: to be added element
|
||||
*
|
||||
* Returns 0 on success, 1 if the element already is in the hash
|
||||
* and -1 on error.
|
||||
*/
|
||||
static inline int batadv_hash_add(struct batadv_hashtable *hash,
|
||||
batadv_hashdata_compare_cb compare,
|
||||
batadv_hashdata_choose_cb choose,
|
||||
const void *data,
|
||||
struct hlist_node *data_node)
|
||||
{
|
||||
uint32_t index;
|
||||
int ret = -1;
|
||||
struct hlist_head *head;
|
||||
struct hlist_node *node;
|
||||
spinlock_t *list_lock; /* spinlock to protect write access */
|
||||
|
||||
if (!hash)
|
||||
goto out;
|
||||
|
||||
index = choose(data, hash->size);
|
||||
head = &hash->table[index];
|
||||
list_lock = &hash->list_locks[index];
|
||||
|
||||
spin_lock_bh(list_lock);
|
||||
|
||||
hlist_for_each(node, head) {
|
||||
if (!compare(node, data))
|
||||
continue;
|
||||
|
||||
ret = 1;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* no duplicate found in list, add new element */
|
||||
hlist_add_head_rcu(data_node, head);
|
||||
|
||||
ret = 0;
|
||||
|
||||
unlock:
|
||||
spin_unlock_bh(list_lock);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* removes data from hash, if found. returns pointer do data on success, so you
|
||||
* can remove the used structure yourself, or NULL on error . data could be the
|
||||
* structure you use with just the key filled, we just need the key for
|
||||
* comparing.
|
||||
*/
|
||||
static inline void *batadv_hash_remove(struct batadv_hashtable *hash,
|
||||
batadv_hashdata_compare_cb compare,
|
||||
batadv_hashdata_choose_cb choose,
|
||||
void *data)
|
||||
{
|
||||
uint32_t index;
|
||||
struct hlist_node *node;
|
||||
struct hlist_head *head;
|
||||
void *data_save = NULL;
|
||||
|
||||
index = choose(data, hash->size);
|
||||
head = &hash->table[index];
|
||||
|
||||
spin_lock_bh(&hash->list_locks[index]);
|
||||
hlist_for_each(node, head) {
|
||||
if (!compare(node, data))
|
||||
continue;
|
||||
|
||||
data_save = node;
|
||||
hlist_del_rcu(node);
|
||||
break;
|
||||
}
|
||||
spin_unlock_bh(&hash->list_locks[index]);
|
||||
|
||||
return data_save;
|
||||
}
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_HASH_H_ */
|
385
net/batman-adv/icmp_socket.c
Normal file
385
net/batman-adv/icmp_socket.c
Normal file
|
@ -0,0 +1,385 @@
|
|||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/slab.h>
|
||||
#include "icmp_socket.h"
|
||||
#include "send.h"
|
||||
#include "hash.h"
|
||||
#include "originator.h"
|
||||
#include "hard-interface.h"
|
||||
|
||||
static struct batadv_socket_client *batadv_socket_client_hash[256];
|
||||
|
||||
static void batadv_socket_add_packet(struct batadv_socket_client *socket_client,
|
||||
struct batadv_icmp_header *icmph,
|
||||
size_t icmp_len);
|
||||
|
||||
void batadv_socket_init(void)
|
||||
{
|
||||
memset(batadv_socket_client_hash, 0, sizeof(batadv_socket_client_hash));
|
||||
}
|
||||
|
||||
static int batadv_socket_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
unsigned int i;
|
||||
struct batadv_socket_client *socket_client;
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return -EBUSY;
|
||||
|
||||
nonseekable_open(inode, file);
|
||||
|
||||
socket_client = kmalloc(sizeof(*socket_client), GFP_KERNEL);
|
||||
if (!socket_client) {
|
||||
module_put(THIS_MODULE);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(batadv_socket_client_hash); i++) {
|
||||
if (!batadv_socket_client_hash[i]) {
|
||||
batadv_socket_client_hash[i] = socket_client;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(batadv_socket_client_hash)) {
|
||||
pr_err("Error - can't add another packet client: maximum number of clients reached\n");
|
||||
kfree(socket_client);
|
||||
module_put(THIS_MODULE);
|
||||
return -EXFULL;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&socket_client->queue_list);
|
||||
socket_client->queue_len = 0;
|
||||
socket_client->index = i;
|
||||
socket_client->bat_priv = inode->i_private;
|
||||
spin_lock_init(&socket_client->lock);
|
||||
init_waitqueue_head(&socket_client->queue_wait);
|
||||
|
||||
file->private_data = socket_client;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int batadv_socket_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct batadv_socket_client *socket_client = file->private_data;
|
||||
struct batadv_socket_packet *socket_packet;
|
||||
struct list_head *list_pos, *list_pos_tmp;
|
||||
|
||||
spin_lock_bh(&socket_client->lock);
|
||||
|
||||
/* for all packets in the queue ... */
|
||||
list_for_each_safe(list_pos, list_pos_tmp, &socket_client->queue_list) {
|
||||
socket_packet = list_entry(list_pos,
|
||||
struct batadv_socket_packet, list);
|
||||
|
||||
list_del(list_pos);
|
||||
kfree(socket_packet);
|
||||
}
|
||||
|
||||
batadv_socket_client_hash[socket_client->index] = NULL;
|
||||
spin_unlock_bh(&socket_client->lock);
|
||||
|
||||
kfree(socket_client);
|
||||
module_put(THIS_MODULE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t batadv_socket_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct batadv_socket_client *socket_client = file->private_data;
|
||||
struct batadv_socket_packet *socket_packet;
|
||||
size_t packet_len;
|
||||
int error;
|
||||
|
||||
if ((file->f_flags & O_NONBLOCK) && (socket_client->queue_len == 0))
|
||||
return -EAGAIN;
|
||||
|
||||
if ((!buf) || (count < sizeof(struct batadv_icmp_packet)))
|
||||
return -EINVAL;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, buf, count))
|
||||
return -EFAULT;
|
||||
|
||||
error = wait_event_interruptible(socket_client->queue_wait,
|
||||
socket_client->queue_len);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
spin_lock_bh(&socket_client->lock);
|
||||
|
||||
socket_packet = list_first_entry(&socket_client->queue_list,
|
||||
struct batadv_socket_packet, list);
|
||||
list_del(&socket_packet->list);
|
||||
socket_client->queue_len--;
|
||||
|
||||
spin_unlock_bh(&socket_client->lock);
|
||||
|
||||
packet_len = min(count, socket_packet->icmp_len);
|
||||
error = copy_to_user(buf, &socket_packet->icmp_packet, packet_len);
|
||||
|
||||
kfree(socket_packet);
|
||||
|
||||
if (error)
|
||||
return -EFAULT;
|
||||
|
||||
return packet_len;
|
||||
}
|
||||
|
||||
static ssize_t batadv_socket_write(struct file *file, const char __user *buff,
|
||||
size_t len, loff_t *off)
|
||||
{
|
||||
struct batadv_socket_client *socket_client = file->private_data;
|
||||
struct batadv_priv *bat_priv = socket_client->bat_priv;
|
||||
struct batadv_hard_iface *primary_if = NULL;
|
||||
struct sk_buff *skb;
|
||||
struct batadv_icmp_packet_rr *icmp_packet_rr;
|
||||
struct batadv_icmp_header *icmp_header;
|
||||
struct batadv_orig_node *orig_node = NULL;
|
||||
struct batadv_neigh_node *neigh_node = NULL;
|
||||
size_t packet_len = sizeof(struct batadv_icmp_packet);
|
||||
uint8_t *addr;
|
||||
|
||||
if (len < sizeof(struct batadv_icmp_header)) {
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"Error - can't send packet from char device: invalid packet size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
primary_if = batadv_primary_if_get_selected(bat_priv);
|
||||
|
||||
if (!primary_if) {
|
||||
len = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (len >= BATADV_ICMP_MAX_PACKET_SIZE)
|
||||
packet_len = BATADV_ICMP_MAX_PACKET_SIZE;
|
||||
else
|
||||
packet_len = len;
|
||||
|
||||
skb = netdev_alloc_skb_ip_align(NULL, packet_len + ETH_HLEN);
|
||||
if (!skb) {
|
||||
len = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
skb->priority = TC_PRIO_CONTROL;
|
||||
skb_reserve(skb, ETH_HLEN);
|
||||
icmp_header = (struct batadv_icmp_header *)skb_put(skb, packet_len);
|
||||
|
||||
if (copy_from_user(icmp_header, buff, packet_len)) {
|
||||
len = -EFAULT;
|
||||
goto free_skb;
|
||||
}
|
||||
|
||||
if (icmp_header->packet_type != BATADV_ICMP) {
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"Error - can't send packet from char device: got bogus packet type (expected: BAT_ICMP)\n");
|
||||
len = -EINVAL;
|
||||
goto free_skb;
|
||||
}
|
||||
|
||||
switch (icmp_header->msg_type) {
|
||||
case BATADV_ECHO_REQUEST:
|
||||
if (len < sizeof(struct batadv_icmp_packet)) {
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"Error - can't send packet from char device: invalid packet size\n");
|
||||
len = -EINVAL;
|
||||
goto free_skb;
|
||||
}
|
||||
|
||||
if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
|
||||
goto dst_unreach;
|
||||
|
||||
orig_node = batadv_orig_hash_find(bat_priv, icmp_header->dst);
|
||||
if (!orig_node)
|
||||
goto dst_unreach;
|
||||
|
||||
neigh_node = batadv_orig_router_get(orig_node,
|
||||
BATADV_IF_DEFAULT);
|
||||
if (!neigh_node)
|
||||
goto dst_unreach;
|
||||
|
||||
if (!neigh_node->if_incoming)
|
||||
goto dst_unreach;
|
||||
|
||||
if (neigh_node->if_incoming->if_status != BATADV_IF_ACTIVE)
|
||||
goto dst_unreach;
|
||||
|
||||
icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmp_header;
|
||||
if (packet_len == sizeof(*icmp_packet_rr)) {
|
||||
addr = neigh_node->if_incoming->net_dev->dev_addr;
|
||||
ether_addr_copy(icmp_packet_rr->rr[0], addr);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"Error - can't send packet from char device: got unknown message type\n");
|
||||
len = -EINVAL;
|
||||
goto free_skb;
|
||||
}
|
||||
|
||||
icmp_header->uid = socket_client->index;
|
||||
|
||||
if (icmp_header->version != BATADV_COMPAT_VERSION) {
|
||||
icmp_header->msg_type = BATADV_PARAMETER_PROBLEM;
|
||||
icmp_header->version = BATADV_COMPAT_VERSION;
|
||||
batadv_socket_add_packet(socket_client, icmp_header,
|
||||
packet_len);
|
||||
goto free_skb;
|
||||
}
|
||||
|
||||
ether_addr_copy(icmp_header->orig, primary_if->net_dev->dev_addr);
|
||||
|
||||
batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
|
||||
goto out;
|
||||
|
||||
dst_unreach:
|
||||
icmp_header->msg_type = BATADV_DESTINATION_UNREACHABLE;
|
||||
batadv_socket_add_packet(socket_client, icmp_header, packet_len);
|
||||
free_skb:
|
||||
kfree_skb(skb);
|
||||
out:
|
||||
if (primary_if)
|
||||
batadv_hardif_free_ref(primary_if);
|
||||
if (neigh_node)
|
||||
batadv_neigh_node_free_ref(neigh_node);
|
||||
if (orig_node)
|
||||
batadv_orig_node_free_ref(orig_node);
|
||||
return len;
|
||||
}
|
||||
|
||||
static unsigned int batadv_socket_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct batadv_socket_client *socket_client = file->private_data;
|
||||
|
||||
poll_wait(file, &socket_client->queue_wait, wait);
|
||||
|
||||
if (socket_client->queue_len > 0)
|
||||
return POLLIN | POLLRDNORM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations batadv_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = batadv_socket_open,
|
||||
.release = batadv_socket_release,
|
||||
.read = batadv_socket_read,
|
||||
.write = batadv_socket_write,
|
||||
.poll = batadv_socket_poll,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
int batadv_socket_setup(struct batadv_priv *bat_priv)
|
||||
{
|
||||
struct dentry *d;
|
||||
|
||||
if (!bat_priv->debug_dir)
|
||||
goto err;
|
||||
|
||||
d = debugfs_create_file(BATADV_ICMP_SOCKET, S_IFREG | S_IWUSR | S_IRUSR,
|
||||
bat_priv->debug_dir, bat_priv, &batadv_fops);
|
||||
if (!d)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_socket_receive_packet - schedule an icmp packet to be sent to userspace
|
||||
* on an icmp socket.
|
||||
* @socket_client: the socket this packet belongs to
|
||||
* @icmph: pointer to the header of the icmp packet
|
||||
* @icmp_len: total length of the icmp packet
|
||||
*/
|
||||
static void batadv_socket_add_packet(struct batadv_socket_client *socket_client,
|
||||
struct batadv_icmp_header *icmph,
|
||||
size_t icmp_len)
|
||||
{
|
||||
struct batadv_socket_packet *socket_packet;
|
||||
size_t len;
|
||||
|
||||
socket_packet = kmalloc(sizeof(*socket_packet), GFP_ATOMIC);
|
||||
|
||||
if (!socket_packet)
|
||||
return;
|
||||
|
||||
len = icmp_len;
|
||||
/* check the maximum length before filling the buffer */
|
||||
if (len > sizeof(socket_packet->icmp_packet))
|
||||
len = sizeof(socket_packet->icmp_packet);
|
||||
|
||||
INIT_LIST_HEAD(&socket_packet->list);
|
||||
memcpy(&socket_packet->icmp_packet, icmph, len);
|
||||
socket_packet->icmp_len = len;
|
||||
|
||||
spin_lock_bh(&socket_client->lock);
|
||||
|
||||
/* while waiting for the lock the socket_client could have been
|
||||
* deleted
|
||||
*/
|
||||
if (!batadv_socket_client_hash[icmph->uid]) {
|
||||
spin_unlock_bh(&socket_client->lock);
|
||||
kfree(socket_packet);
|
||||
return;
|
||||
}
|
||||
|
||||
list_add_tail(&socket_packet->list, &socket_client->queue_list);
|
||||
socket_client->queue_len++;
|
||||
|
||||
if (socket_client->queue_len > 100) {
|
||||
socket_packet = list_first_entry(&socket_client->queue_list,
|
||||
struct batadv_socket_packet,
|
||||
list);
|
||||
|
||||
list_del(&socket_packet->list);
|
||||
kfree(socket_packet);
|
||||
socket_client->queue_len--;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&socket_client->lock);
|
||||
|
||||
wake_up(&socket_client->queue_wait);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_socket_receive_packet - schedule an icmp packet to be received
|
||||
* locally and sent to userspace.
|
||||
* @icmph: pointer to the header of the icmp packet
|
||||
* @icmp_len: total length of the icmp packet
|
||||
*/
|
||||
void batadv_socket_receive_packet(struct batadv_icmp_header *icmph,
|
||||
size_t icmp_len)
|
||||
{
|
||||
struct batadv_socket_client *hash;
|
||||
|
||||
hash = batadv_socket_client_hash[icmph->uid];
|
||||
if (hash)
|
||||
batadv_socket_add_packet(hash, icmph, icmp_len);
|
||||
}
|
28
net/batman-adv/icmp_socket.h
Normal file
28
net/batman-adv/icmp_socket.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_ICMP_SOCKET_H_
|
||||
#define _NET_BATMAN_ADV_ICMP_SOCKET_H_
|
||||
|
||||
#define BATADV_ICMP_SOCKET "socket"
|
||||
|
||||
void batadv_socket_init(void);
|
||||
int batadv_socket_setup(struct batadv_priv *bat_priv);
|
||||
void batadv_socket_receive_packet(struct batadv_icmp_header *icmph,
|
||||
size_t icmp_len);
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_ICMP_SOCKET_H_ */
|
1263
net/batman-adv/main.c
Normal file
1263
net/batman-adv/main.c
Normal file
File diff suppressed because it is too large
Load diff
390
net/batman-adv/main.h
Normal file
390
net/batman-adv/main.h
Normal file
|
@ -0,0 +1,390 @@
|
|||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_MAIN_H_
|
||||
#define _NET_BATMAN_ADV_MAIN_H_
|
||||
|
||||
#define BATADV_DRIVER_AUTHOR "Marek Lindner <mareklindner@neomailbox.ch>, " \
|
||||
"Simon Wunderlich <sw@simonwunderlich.de>"
|
||||
#define BATADV_DRIVER_DESC "B.A.T.M.A.N. advanced"
|
||||
#define BATADV_DRIVER_DEVICE "batman-adv"
|
||||
|
||||
#ifndef BATADV_SOURCE_VERSION
|
||||
#define BATADV_SOURCE_VERSION "2014.4.0"
|
||||
#endif
|
||||
|
||||
/* B.A.T.M.A.N. parameters */
|
||||
|
||||
#define BATADV_TQ_MAX_VALUE 255
|
||||
#define BATADV_JITTER 20
|
||||
|
||||
/* Time To Live of broadcast messages */
|
||||
#define BATADV_TTL 50
|
||||
|
||||
/* purge originators after time in seconds if no valid packet comes in
|
||||
* -> TODO: check influence on BATADV_TQ_LOCAL_WINDOW_SIZE
|
||||
*/
|
||||
#define BATADV_PURGE_TIMEOUT 200000 /* 200 seconds */
|
||||
#define BATADV_TT_LOCAL_TIMEOUT 600000 /* in milliseconds */
|
||||
#define BATADV_TT_CLIENT_ROAM_TIMEOUT 600000 /* in milliseconds */
|
||||
#define BATADV_TT_CLIENT_TEMP_TIMEOUT 600000 /* in milliseconds */
|
||||
#define BATADV_TT_WORK_PERIOD 5000 /* 5 seconds */
|
||||
#define BATADV_ORIG_WORK_PERIOD 1000 /* 1 second */
|
||||
#define BATADV_DAT_ENTRY_TIMEOUT (5*60000) /* 5 mins in milliseconds */
|
||||
/* sliding packet range of received originator messages in sequence numbers
|
||||
* (should be a multiple of our word size)
|
||||
*/
|
||||
#define BATADV_TQ_LOCAL_WINDOW_SIZE 64
|
||||
/* milliseconds we have to keep pending tt_req */
|
||||
#define BATADV_TT_REQUEST_TIMEOUT 3000
|
||||
|
||||
#define BATADV_TQ_GLOBAL_WINDOW_SIZE 5
|
||||
#define BATADV_TQ_LOCAL_BIDRECT_SEND_MINIMUM 1
|
||||
#define BATADV_TQ_LOCAL_BIDRECT_RECV_MINIMUM 1
|
||||
#define BATADV_TQ_TOTAL_BIDRECT_LIMIT 1
|
||||
|
||||
/* number of OGMs sent with the last tt diff */
|
||||
#define BATADV_TT_OGM_APPEND_MAX 3
|
||||
|
||||
/* Time in which a client can roam at most ROAMING_MAX_COUNT times in
|
||||
* milliseconds
|
||||
*/
|
||||
#define BATADV_ROAMING_MAX_TIME 20000
|
||||
#define BATADV_ROAMING_MAX_COUNT 5
|
||||
|
||||
#define BATADV_NO_FLAGS 0
|
||||
|
||||
#define BATADV_NULL_IFINDEX 0 /* dummy ifindex used to avoid iface checks */
|
||||
|
||||
#define BATADV_NO_MARK 0
|
||||
|
||||
/* default interface for multi interface operation. The default interface is
|
||||
* used for communication which originated locally (i.e. is not forwarded)
|
||||
* or where special forwarding is not desired/necessary.
|
||||
*/
|
||||
#define BATADV_IF_DEFAULT ((struct batadv_hard_iface *)NULL)
|
||||
|
||||
#define BATADV_NUM_WORDS BITS_TO_LONGS(BATADV_TQ_LOCAL_WINDOW_SIZE)
|
||||
|
||||
#define BATADV_LOG_BUF_LEN 8192 /* has to be a power of 2 */
|
||||
|
||||
/* 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
|
||||
|
||||
/* msecs after which an ARP_REQUEST is sent in broadcast as fallback */
|
||||
#define ARP_REQ_DELAY 250
|
||||
/* numbers of originator to contact for any PUT/GET DHT operation */
|
||||
#define BATADV_DAT_CANDIDATES_NUM 3
|
||||
|
||||
/**
|
||||
* BATADV_TQ_SIMILARITY_THRESHOLD - TQ points that a secondary metric can differ
|
||||
* at most from the primary one in order to be still considered acceptable
|
||||
*/
|
||||
#define BATADV_TQ_SIMILARITY_THRESHOLD 50
|
||||
|
||||
/* how much worse secondary interfaces may be to be considered as bonding
|
||||
* candidates
|
||||
*/
|
||||
#define BATADV_BONDING_TQ_THRESHOLD 50
|
||||
|
||||
/* should not be bigger than 512 bytes or change the size of
|
||||
* forw_packet->direct_link_flags
|
||||
*/
|
||||
#define BATADV_MAX_AGGREGATION_BYTES 512
|
||||
#define BATADV_MAX_AGGREGATION_MS 100
|
||||
|
||||
#define BATADV_BLA_PERIOD_LENGTH 10000 /* 10 seconds */
|
||||
#define BATADV_BLA_BACKBONE_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 3)
|
||||
#define BATADV_BLA_CLAIM_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 10)
|
||||
#define BATADV_BLA_WAIT_PERIODS 3
|
||||
|
||||
#define BATADV_DUPLIST_SIZE 16
|
||||
#define BATADV_DUPLIST_TIMEOUT 500 /* 500 ms */
|
||||
/* don't reset again within 30 seconds */
|
||||
#define BATADV_RESET_PROTECTION_MS 30000
|
||||
#define BATADV_EXPECTED_SEQNO_RANGE 65536
|
||||
|
||||
#define BATADV_NC_NODE_TIMEOUT 10000 /* Milliseconds */
|
||||
|
||||
enum batadv_mesh_state {
|
||||
BATADV_MESH_INACTIVE,
|
||||
BATADV_MESH_ACTIVE,
|
||||
BATADV_MESH_DEACTIVATING,
|
||||
};
|
||||
|
||||
#define BATADV_BCAST_QUEUE_LEN 256
|
||||
#define BATADV_BATMAN_QUEUE_LEN 256
|
||||
|
||||
enum batadv_uev_action {
|
||||
BATADV_UEV_ADD = 0,
|
||||
BATADV_UEV_DEL,
|
||||
BATADV_UEV_CHANGE,
|
||||
};
|
||||
|
||||
enum batadv_uev_type {
|
||||
BATADV_UEV_GW = 0,
|
||||
};
|
||||
|
||||
#define BATADV_GW_THRESHOLD 50
|
||||
|
||||
/* Number of fragment chains for each orig_node */
|
||||
#define BATADV_FRAG_BUFFER_COUNT 8
|
||||
/* Maximum number of fragments for one packet */
|
||||
#define BATADV_FRAG_MAX_FRAGMENTS 16
|
||||
/* Maxumim size of each fragment */
|
||||
#define BATADV_FRAG_MAX_FRAG_SIZE 1400
|
||||
/* Time to keep fragments while waiting for rest of the fragments */
|
||||
#define BATADV_FRAG_TIMEOUT 10000
|
||||
|
||||
#define BATADV_DAT_CANDIDATE_NOT_FOUND 0
|
||||
#define BATADV_DAT_CANDIDATE_ORIG 1
|
||||
|
||||
/* Debug Messages */
|
||||
#ifdef pr_fmt
|
||||
#undef pr_fmt
|
||||
#endif
|
||||
/* Append 'batman-adv: ' before kernel messages */
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
/* Kernel headers */
|
||||
|
||||
#include <linux/mutex.h> /* mutex */
|
||||
#include <linux/module.h> /* needed by all modules */
|
||||
#include <linux/netdevice.h> /* netdevice */
|
||||
#include <linux/etherdevice.h> /* ethernet address classification */
|
||||
#include <linux/if_ether.h> /* ethernet header */
|
||||
#include <linux/poll.h> /* poll_table */
|
||||
#include <linux/kthread.h> /* kernel threads */
|
||||
#include <linux/pkt_sched.h> /* schedule types */
|
||||
#include <linux/workqueue.h> /* workqueue */
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/sock.h> /* struct sock */
|
||||
#include <net/addrconf.h> /* ipv6 address stuff */
|
||||
#include <linux/ip.h>
|
||||
#include <net/rtnetlink.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/if_vlan.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define BATADV_PRINT_VID(vid) (vid & BATADV_VLAN_HAS_TAG ? \
|
||||
(int)(vid & VLAN_VID_MASK) : -1)
|
||||
|
||||
extern char batadv_routing_algo[];
|
||||
extern struct list_head batadv_hardif_list;
|
||||
|
||||
extern unsigned char batadv_broadcast_addr[];
|
||||
extern struct workqueue_struct *batadv_event_workqueue;
|
||||
|
||||
int batadv_mesh_init(struct net_device *soft_iface);
|
||||
void batadv_mesh_free(struct net_device *soft_iface);
|
||||
int batadv_is_my_mac(struct batadv_priv *bat_priv, const uint8_t *addr);
|
||||
struct batadv_hard_iface *
|
||||
batadv_seq_print_text_primary_if_get(struct seq_file *seq);
|
||||
int batadv_max_header_len(void);
|
||||
void batadv_skb_set_priority(struct sk_buff *skb, int offset);
|
||||
int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *ptype,
|
||||
struct net_device *orig_dev);
|
||||
int
|
||||
batadv_recv_handler_register(uint8_t packet_type,
|
||||
int (*recv_handler)(struct sk_buff *,
|
||||
struct batadv_hard_iface *));
|
||||
void batadv_recv_handler_unregister(uint8_t packet_type);
|
||||
int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops);
|
||||
int batadv_algo_select(struct batadv_priv *bat_priv, char *name);
|
||||
int batadv_algo_seq_print_text(struct seq_file *seq, void *offset);
|
||||
__be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr);
|
||||
|
||||
/**
|
||||
* enum batadv_dbg_level - available log levels
|
||||
* @BATADV_DBG_BATMAN: OGM and TQ computations related messages
|
||||
* @BATADV_DBG_ROUTES: route added / changed / deleted
|
||||
* @BATADV_DBG_TT: translation table messages
|
||||
* @BATADV_DBG_BLA: bridge loop avoidance messages
|
||||
* @BATADV_DBG_DAT: ARP snooping and DAT related messages
|
||||
* @BATADV_DBG_NC: network coding related messages
|
||||
* @BATADV_DBG_ALL: the union of all the above log levels
|
||||
*/
|
||||
enum batadv_dbg_level {
|
||||
BATADV_DBG_BATMAN = BIT(0),
|
||||
BATADV_DBG_ROUTES = BIT(1),
|
||||
BATADV_DBG_TT = BIT(2),
|
||||
BATADV_DBG_BLA = BIT(3),
|
||||
BATADV_DBG_DAT = BIT(4),
|
||||
BATADV_DBG_NC = BIT(5),
|
||||
BATADV_DBG_ALL = 63,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BATMAN_ADV_DEBUG
|
||||
int batadv_debug_log(struct batadv_priv *bat_priv, const char *fmt, ...)
|
||||
__printf(2, 3);
|
||||
|
||||
/* possibly ratelimited debug output */
|
||||
#define _batadv_dbg(type, bat_priv, ratelimited, fmt, arg...) \
|
||||
do { \
|
||||
if (atomic_read(&bat_priv->log_level) & type && \
|
||||
(!ratelimited || net_ratelimit())) \
|
||||
batadv_debug_log(bat_priv, fmt, ## arg);\
|
||||
} \
|
||||
while (0)
|
||||
#else /* !CONFIG_BATMAN_ADV_DEBUG */
|
||||
__printf(4, 5)
|
||||
static inline void _batadv_dbg(int type __always_unused,
|
||||
struct batadv_priv *bat_priv __always_unused,
|
||||
int ratelimited __always_unused,
|
||||
const char *fmt __always_unused, ...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#define batadv_dbg(type, bat_priv, arg...) \
|
||||
_batadv_dbg(type, bat_priv, 0, ## arg)
|
||||
#define batadv_dbg_ratelimited(type, bat_priv, arg...) \
|
||||
_batadv_dbg(type, bat_priv, 1, ## arg)
|
||||
|
||||
#define batadv_info(net_dev, fmt, arg...) \
|
||||
do { \
|
||||
struct net_device *_netdev = (net_dev); \
|
||||
struct batadv_priv *_batpriv = netdev_priv(_netdev); \
|
||||
batadv_dbg(BATADV_DBG_ALL, _batpriv, fmt, ## arg); \
|
||||
pr_info("%s: " fmt, _netdev->name, ## arg); \
|
||||
} while (0)
|
||||
#define batadv_err(net_dev, fmt, arg...) \
|
||||
do { \
|
||||
struct net_device *_netdev = (net_dev); \
|
||||
struct batadv_priv *_batpriv = netdev_priv(_netdev); \
|
||||
batadv_dbg(BATADV_DBG_ALL, _batpriv, fmt, ## arg); \
|
||||
pr_err("%s: " fmt, _netdev->name, ## arg); \
|
||||
} while (0)
|
||||
|
||||
/* returns 1 if they are the same ethernet addr
|
||||
*
|
||||
* note: can't use ether_addr_equal() as it requires aligned memory
|
||||
*/
|
||||
static inline int batadv_compare_eth(const void *data1, const void *data2)
|
||||
{
|
||||
return ether_addr_equal_unaligned(data1, data2);
|
||||
}
|
||||
|
||||
/**
|
||||
* has_timed_out - compares current time (jiffies) and timestamp + timeout
|
||||
* @timestamp: base value to compare with (in jiffies)
|
||||
* @timeout: added to base value before comparing (in milliseconds)
|
||||
*
|
||||
* Returns true if current time is after timestamp + timeout
|
||||
*/
|
||||
static inline bool batadv_has_timed_out(unsigned long timestamp,
|
||||
unsigned int timeout)
|
||||
{
|
||||
return time_is_before_jiffies(timestamp + msecs_to_jiffies(timeout));
|
||||
}
|
||||
|
||||
#define batadv_atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0)
|
||||
|
||||
/* Returns the smallest signed integer in two's complement with the sizeof x */
|
||||
#define batadv_smallest_signed_int(x) (1u << (7u + 8u * (sizeof(x) - 1u)))
|
||||
|
||||
/* Checks if a sequence number x is a predecessor/successor of y.
|
||||
* they handle overflows/underflows and can correctly check for a
|
||||
* predecessor/successor unless the variable sequence number has grown by
|
||||
* more then 2**(bitwidth(x)-1)-1.
|
||||
* This means that for a uint8_t with the maximum value 255, it would think:
|
||||
* - when adding nothing - it is neither a predecessor nor a successor
|
||||
* - before adding more than 127 to the starting value - it is a predecessor,
|
||||
* - when adding 128 - it is neither a predecessor nor a successor,
|
||||
* - after adding more than 127 to the starting value - it is a successor
|
||||
*/
|
||||
#define batadv_seq_before(x, y) ({typeof(x) _d1 = (x); \
|
||||
typeof(y) _d2 = (y); \
|
||||
typeof(x) _dummy = (_d1 - _d2); \
|
||||
(void) (&_d1 == &_d2); \
|
||||
_dummy > batadv_smallest_signed_int(_dummy); })
|
||||
#define batadv_seq_after(x, y) batadv_seq_before(y, x)
|
||||
|
||||
/* Stop preemption on local cpu while incrementing the counter */
|
||||
static inline void batadv_add_counter(struct batadv_priv *bat_priv, size_t idx,
|
||||
size_t count)
|
||||
{
|
||||
this_cpu_add(bat_priv->bat_counters[idx], count);
|
||||
}
|
||||
|
||||
#define batadv_inc_counter(b, i) batadv_add_counter(b, i, 1)
|
||||
|
||||
/* Sum and return the cpu-local counters for index 'idx' */
|
||||
static inline uint64_t batadv_sum_counter(struct batadv_priv *bat_priv,
|
||||
size_t idx)
|
||||
{
|
||||
uint64_t *counters, sum = 0;
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
counters = per_cpu_ptr(bat_priv->bat_counters, cpu);
|
||||
sum += counters[idx];
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
/* Define a macro to reach the control buffer of the skb. The members of the
|
||||
* control buffer are defined in struct batadv_skb_cb in types.h.
|
||||
* The macro is inspired by the similar macro TCP_SKB_CB() in tcp.h.
|
||||
*/
|
||||
#define BATADV_SKB_CB(__skb) ((struct batadv_skb_cb *)&((__skb)->cb[0]))
|
||||
|
||||
void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
|
||||
uint8_t type, uint8_t version,
|
||||
void *tvlv_value, uint16_t tvlv_value_len);
|
||||
uint16_t batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
|
||||
unsigned char **packet_buff,
|
||||
int *packet_buff_len,
|
||||
int packet_min_len);
|
||||
void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
|
||||
struct batadv_ogm_packet *batadv_ogm_packet,
|
||||
struct batadv_orig_node *orig_node);
|
||||
void batadv_tvlv_container_unregister(struct batadv_priv *bat_priv,
|
||||
uint8_t type, uint8_t version);
|
||||
|
||||
void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
|
||||
void (*optr)(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig,
|
||||
uint8_t flags,
|
||||
void *tvlv_value,
|
||||
uint16_t tvlv_value_len),
|
||||
int (*uptr)(struct batadv_priv *bat_priv,
|
||||
uint8_t *src, uint8_t *dst,
|
||||
void *tvlv_value,
|
||||
uint16_t tvlv_value_len),
|
||||
uint8_t type, uint8_t version, uint8_t flags);
|
||||
void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv,
|
||||
uint8_t type, uint8_t version);
|
||||
int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
|
||||
bool ogm_source,
|
||||
struct batadv_orig_node *orig_node,
|
||||
uint8_t *src, uint8_t *dst,
|
||||
void *tvlv_buff, uint16_t tvlv_buff_len);
|
||||
void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, uint8_t *src,
|
||||
uint8_t *dst, uint8_t type, uint8_t version,
|
||||
void *tvlv_value, uint16_t tvlv_value_len);
|
||||
unsigned short batadv_get_vid(struct sk_buff *skb, size_t header_len);
|
||||
bool batadv_vlan_ap_isola_get(struct batadv_priv *bat_priv, unsigned short vid);
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_MAIN_H_ */
|
747
net/batman-adv/multicast.c
Normal file
747
net/batman-adv/multicast.c
Normal file
|
@ -0,0 +1,747 @@
|
|||
/* Copyright (C) 2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Linus Lüssing
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "multicast.h"
|
||||
#include "originator.h"
|
||||
#include "hard-interface.h"
|
||||
#include "translation-table.h"
|
||||
|
||||
/**
|
||||
* batadv_mcast_mla_softif_get - get softif multicast listeners
|
||||
* @dev: the device to collect multicast addresses from
|
||||
* @mcast_list: a list to put found addresses into
|
||||
*
|
||||
* Collect multicast addresses of the local multicast listeners
|
||||
* on the given soft interface, dev, in the given mcast_list.
|
||||
*
|
||||
* Returns -ENOMEM on memory allocation error or the number of
|
||||
* items added to the mcast_list otherwise.
|
||||
*/
|
||||
static int batadv_mcast_mla_softif_get(struct net_device *dev,
|
||||
struct hlist_head *mcast_list)
|
||||
{
|
||||
struct netdev_hw_addr *mc_list_entry;
|
||||
struct batadv_hw_addr *new;
|
||||
int ret = 0;
|
||||
|
||||
netif_addr_lock_bh(dev);
|
||||
netdev_for_each_mc_addr(mc_list_entry, dev) {
|
||||
new = kmalloc(sizeof(*new), GFP_ATOMIC);
|
||||
if (!new) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
ether_addr_copy(new->addr, mc_list_entry->addr);
|
||||
hlist_add_head(&new->list, mcast_list);
|
||||
ret++;
|
||||
}
|
||||
netif_addr_unlock_bh(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_mla_is_duplicate - check whether an address is in a list
|
||||
* @mcast_addr: the multicast address to check
|
||||
* @mcast_list: the list with multicast addresses to search in
|
||||
*
|
||||
* Returns true if the given address is already in the given list.
|
||||
* Otherwise returns false.
|
||||
*/
|
||||
static bool batadv_mcast_mla_is_duplicate(uint8_t *mcast_addr,
|
||||
struct hlist_head *mcast_list)
|
||||
{
|
||||
struct batadv_hw_addr *mcast_entry;
|
||||
|
||||
hlist_for_each_entry(mcast_entry, mcast_list, list)
|
||||
if (batadv_compare_eth(mcast_entry->addr, mcast_addr))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_mla_list_free - free a list of multicast addresses
|
||||
* @mcast_list: the list to free
|
||||
*
|
||||
* Removes and frees all items in the given mcast_list.
|
||||
*/
|
||||
static void batadv_mcast_mla_list_free(struct hlist_head *mcast_list)
|
||||
{
|
||||
struct batadv_hw_addr *mcast_entry;
|
||||
struct hlist_node *tmp;
|
||||
|
||||
hlist_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) {
|
||||
hlist_del(&mcast_entry->list);
|
||||
kfree(mcast_entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_mla_tt_retract - clean up multicast listener announcements
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @mcast_list: a list of addresses which should _not_ be removed
|
||||
*
|
||||
* Retracts the announcement of any multicast listener from the
|
||||
* translation table except the ones listed in the given mcast_list.
|
||||
*
|
||||
* If mcast_list is NULL then all are retracted.
|
||||
*/
|
||||
static void batadv_mcast_mla_tt_retract(struct batadv_priv *bat_priv,
|
||||
struct hlist_head *mcast_list)
|
||||
{
|
||||
struct batadv_hw_addr *mcast_entry;
|
||||
struct hlist_node *tmp;
|
||||
|
||||
hlist_for_each_entry_safe(mcast_entry, tmp, &bat_priv->mcast.mla_list,
|
||||
list) {
|
||||
if (mcast_list &&
|
||||
batadv_mcast_mla_is_duplicate(mcast_entry->addr,
|
||||
mcast_list))
|
||||
continue;
|
||||
|
||||
batadv_tt_local_remove(bat_priv, mcast_entry->addr,
|
||||
BATADV_NO_FLAGS,
|
||||
"mcast TT outdated", false);
|
||||
|
||||
hlist_del(&mcast_entry->list);
|
||||
kfree(mcast_entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_mla_tt_add - add multicast listener announcements
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @mcast_list: a list of addresses which are going to get added
|
||||
*
|
||||
* Adds multicast listener announcements from the given mcast_list to the
|
||||
* translation table if they have not been added yet.
|
||||
*/
|
||||
static void batadv_mcast_mla_tt_add(struct batadv_priv *bat_priv,
|
||||
struct hlist_head *mcast_list)
|
||||
{
|
||||
struct batadv_hw_addr *mcast_entry;
|
||||
struct hlist_node *tmp;
|
||||
|
||||
if (!mcast_list)
|
||||
return;
|
||||
|
||||
hlist_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) {
|
||||
if (batadv_mcast_mla_is_duplicate(mcast_entry->addr,
|
||||
&bat_priv->mcast.mla_list))
|
||||
continue;
|
||||
|
||||
if (!batadv_tt_local_add(bat_priv->soft_iface,
|
||||
mcast_entry->addr, BATADV_NO_FLAGS,
|
||||
BATADV_NULL_IFINDEX, BATADV_NO_MARK))
|
||||
continue;
|
||||
|
||||
hlist_del(&mcast_entry->list);
|
||||
hlist_add_head(&mcast_entry->list, &bat_priv->mcast.mla_list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_has_bridge - check whether the soft-iface is bridged
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
*
|
||||
* Checks whether there is a bridge on top of our soft interface. Returns
|
||||
* true if so, false otherwise.
|
||||
*/
|
||||
static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv)
|
||||
{
|
||||
struct net_device *upper = bat_priv->soft_iface;
|
||||
|
||||
rcu_read_lock();
|
||||
do {
|
||||
upper = netdev_master_upper_dev_get_rcu(upper);
|
||||
} while (upper && !(upper->priv_flags & IFF_EBRIDGE));
|
||||
rcu_read_unlock();
|
||||
|
||||
return upper;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_mla_tvlv_update - update multicast tvlv
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
*
|
||||
* Updates the own multicast tvlv with our current multicast related settings,
|
||||
* capabilities and inabilities.
|
||||
*
|
||||
* Returns true if the tvlv container is registered afterwards. Otherwise
|
||||
* returns false.
|
||||
*/
|
||||
static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv)
|
||||
{
|
||||
struct batadv_tvlv_mcast_data mcast_data;
|
||||
|
||||
mcast_data.flags = BATADV_NO_FLAGS;
|
||||
memset(mcast_data.reserved, 0, sizeof(mcast_data.reserved));
|
||||
|
||||
/* Avoid attaching MLAs, if there is a bridge on top of our soft
|
||||
* interface, we don't support that yet (TODO)
|
||||
*/
|
||||
if (batadv_mcast_has_bridge(bat_priv)) {
|
||||
if (bat_priv->mcast.enabled) {
|
||||
batadv_tvlv_container_unregister(bat_priv,
|
||||
BATADV_TVLV_MCAST, 1);
|
||||
bat_priv->mcast.enabled = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bat_priv->mcast.enabled ||
|
||||
mcast_data.flags != bat_priv->mcast.flags) {
|
||||
batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 1,
|
||||
&mcast_data, sizeof(mcast_data));
|
||||
bat_priv->mcast.flags = mcast_data.flags;
|
||||
bat_priv->mcast.enabled = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_mla_update - update the own MLAs
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
*
|
||||
* Updates the own multicast listener announcements in the translation
|
||||
* table as well as the own, announced multicast tvlv container.
|
||||
*/
|
||||
void batadv_mcast_mla_update(struct batadv_priv *bat_priv)
|
||||
{
|
||||
struct net_device *soft_iface = bat_priv->soft_iface;
|
||||
struct hlist_head mcast_list = HLIST_HEAD_INIT;
|
||||
int ret;
|
||||
|
||||
if (!batadv_mcast_mla_tvlv_update(bat_priv))
|
||||
goto update;
|
||||
|
||||
ret = batadv_mcast_mla_softif_get(soft_iface, &mcast_list);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
update:
|
||||
batadv_mcast_mla_tt_retract(bat_priv, &mcast_list);
|
||||
batadv_mcast_mla_tt_add(bat_priv, &mcast_list);
|
||||
|
||||
out:
|
||||
batadv_mcast_mla_list_free(&mcast_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_forw_mode_check_ipv4 - check for optimized forwarding potential
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @skb: the IPv4 packet to check
|
||||
* @is_unsnoopable: stores whether the destination is snoopable
|
||||
*
|
||||
* Checks whether the given IPv4 packet has the potential to be forwarded with a
|
||||
* mode more optimal than classic flooding.
|
||||
*
|
||||
* If so then returns 0. Otherwise -EINVAL is returned or -ENOMEM in case of
|
||||
* memory allocation failure.
|
||||
*/
|
||||
static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb,
|
||||
bool *is_unsnoopable)
|
||||
{
|
||||
struct iphdr *iphdr;
|
||||
|
||||
/* We might fail due to out-of-memory -> drop it */
|
||||
if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*iphdr)))
|
||||
return -ENOMEM;
|
||||
|
||||
iphdr = ip_hdr(skb);
|
||||
|
||||
/* TODO: Implement Multicast Router Discovery (RFC4286),
|
||||
* then allow scope > link local, too
|
||||
*/
|
||||
if (!ipv4_is_local_multicast(iphdr->daddr))
|
||||
return -EINVAL;
|
||||
|
||||
/* link-local multicast listeners behind a bridge are
|
||||
* not snoopable (see RFC4541, section 2.1.2.2)
|
||||
*/
|
||||
*is_unsnoopable = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_forw_mode_check_ipv6 - check for optimized forwarding potential
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @skb: the IPv6 packet to check
|
||||
* @is_unsnoopable: stores whether the destination is snoopable
|
||||
*
|
||||
* Checks whether the given IPv6 packet has the potential to be forwarded with a
|
||||
* mode more optimal than classic flooding.
|
||||
*
|
||||
* If so then returns 0. Otherwise -EINVAL is returned or -ENOMEM if we are out
|
||||
* of memory.
|
||||
*/
|
||||
static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb,
|
||||
bool *is_unsnoopable)
|
||||
{
|
||||
struct ipv6hdr *ip6hdr;
|
||||
|
||||
/* We might fail due to out-of-memory -> drop it */
|
||||
if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*ip6hdr)))
|
||||
return -ENOMEM;
|
||||
|
||||
ip6hdr = ipv6_hdr(skb);
|
||||
|
||||
/* TODO: Implement Multicast Router Discovery (RFC4286),
|
||||
* then allow scope > link local, too
|
||||
*/
|
||||
if (IPV6_ADDR_MC_SCOPE(&ip6hdr->daddr) != IPV6_ADDR_SCOPE_LINKLOCAL)
|
||||
return -EINVAL;
|
||||
|
||||
/* link-local-all-nodes multicast listeners behind a bridge are
|
||||
* not snoopable (see RFC4541, section 3, paragraph 3)
|
||||
*/
|
||||
if (ipv6_addr_is_ll_all_nodes(&ip6hdr->daddr))
|
||||
*is_unsnoopable = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_forw_mode_check - check for optimized forwarding potential
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @skb: the multicast frame to check
|
||||
* @is_unsnoopable: stores whether the destination is snoopable
|
||||
*
|
||||
* Checks whether the given multicast ethernet frame has the potential to be
|
||||
* forwarded with a mode more optimal than classic flooding.
|
||||
*
|
||||
* If so then returns 0. Otherwise -EINVAL is returned or -ENOMEM if we are out
|
||||
* of memory.
|
||||
*/
|
||||
static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb,
|
||||
bool *is_unsnoopable)
|
||||
{
|
||||
struct ethhdr *ethhdr = eth_hdr(skb);
|
||||
|
||||
if (!atomic_read(&bat_priv->multicast_mode))
|
||||
return -EINVAL;
|
||||
|
||||
if (atomic_read(&bat_priv->mcast.num_disabled))
|
||||
return -EINVAL;
|
||||
|
||||
switch (ntohs(ethhdr->h_proto)) {
|
||||
case ETH_P_IP:
|
||||
return batadv_mcast_forw_mode_check_ipv4(bat_priv, skb,
|
||||
is_unsnoopable);
|
||||
case ETH_P_IPV6:
|
||||
return batadv_mcast_forw_mode_check_ipv6(bat_priv, skb,
|
||||
is_unsnoopable);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_want_all_ip_count - count nodes with unspecific mcast interest
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @ethhdr: ethernet header of a packet
|
||||
*
|
||||
* Returns the number of nodes which want all IPv4 multicast traffic if the
|
||||
* given ethhdr is from an IPv4 packet or the number of nodes which want all
|
||||
* IPv6 traffic if it matches an IPv6 packet.
|
||||
*/
|
||||
static int batadv_mcast_forw_want_all_ip_count(struct batadv_priv *bat_priv,
|
||||
struct ethhdr *ethhdr)
|
||||
{
|
||||
switch (ntohs(ethhdr->h_proto)) {
|
||||
case ETH_P_IP:
|
||||
return atomic_read(&bat_priv->mcast.num_want_all_ipv4);
|
||||
case ETH_P_IPV6:
|
||||
return atomic_read(&bat_priv->mcast.num_want_all_ipv6);
|
||||
default:
|
||||
/* we shouldn't be here... */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_forw_tt_node_get - get a multicast tt node
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @ethhdr: the ether header containing the multicast destination
|
||||
*
|
||||
* Returns an orig_node matching the multicast address provided by ethhdr
|
||||
* via a translation table lookup. This increases the returned nodes refcount.
|
||||
*/
|
||||
static struct batadv_orig_node *
|
||||
batadv_mcast_forw_tt_node_get(struct batadv_priv *bat_priv,
|
||||
struct ethhdr *ethhdr)
|
||||
{
|
||||
return batadv_transtable_search(bat_priv, ethhdr->h_source,
|
||||
ethhdr->h_dest, BATADV_NO_FLAGS);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_want_forw_ipv4_node_get - get a node with an ipv4 flag
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
*
|
||||
* Returns an orig_node which has the BATADV_MCAST_WANT_ALL_IPV4 flag set and
|
||||
* increases its refcount.
|
||||
*/
|
||||
static struct batadv_orig_node *
|
||||
batadv_mcast_forw_ipv4_node_get(struct batadv_priv *bat_priv)
|
||||
{
|
||||
struct batadv_orig_node *tmp_orig_node, *orig_node = NULL;
|
||||
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(tmp_orig_node,
|
||||
&bat_priv->mcast.want_all_ipv4_list,
|
||||
mcast_want_all_ipv4_node) {
|
||||
if (!atomic_inc_not_zero(&tmp_orig_node->refcount))
|
||||
continue;
|
||||
|
||||
orig_node = tmp_orig_node;
|
||||
break;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return orig_node;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_want_forw_ipv6_node_get - get a node with an ipv6 flag
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
*
|
||||
* Returns an orig_node which has the BATADV_MCAST_WANT_ALL_IPV6 flag set
|
||||
* and increases its refcount.
|
||||
*/
|
||||
static struct batadv_orig_node *
|
||||
batadv_mcast_forw_ipv6_node_get(struct batadv_priv *bat_priv)
|
||||
{
|
||||
struct batadv_orig_node *tmp_orig_node, *orig_node = NULL;
|
||||
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(tmp_orig_node,
|
||||
&bat_priv->mcast.want_all_ipv6_list,
|
||||
mcast_want_all_ipv6_node) {
|
||||
if (!atomic_inc_not_zero(&tmp_orig_node->refcount))
|
||||
continue;
|
||||
|
||||
orig_node = tmp_orig_node;
|
||||
break;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return orig_node;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_want_forw_ip_node_get - get a node with an ipv4/ipv6 flag
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @ethhdr: an ethernet header to determine the protocol family from
|
||||
*
|
||||
* Returns an orig_node which has the BATADV_MCAST_WANT_ALL_IPV4 or
|
||||
* BATADV_MCAST_WANT_ALL_IPV6 flag, depending on the provided ethhdr, set and
|
||||
* increases its refcount.
|
||||
*/
|
||||
static struct batadv_orig_node *
|
||||
batadv_mcast_forw_ip_node_get(struct batadv_priv *bat_priv,
|
||||
struct ethhdr *ethhdr)
|
||||
{
|
||||
switch (ntohs(ethhdr->h_proto)) {
|
||||
case ETH_P_IP:
|
||||
return batadv_mcast_forw_ipv4_node_get(bat_priv);
|
||||
case ETH_P_IPV6:
|
||||
return batadv_mcast_forw_ipv6_node_get(bat_priv);
|
||||
default:
|
||||
/* we shouldn't be here... */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_want_forw_unsnoop_node_get - get a node with an unsnoopable flag
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
*
|
||||
* Returns an orig_node which has the BATADV_MCAST_WANT_ALL_UNSNOOPABLES flag
|
||||
* set and increases its refcount.
|
||||
*/
|
||||
static struct batadv_orig_node *
|
||||
batadv_mcast_forw_unsnoop_node_get(struct batadv_priv *bat_priv)
|
||||
{
|
||||
struct batadv_orig_node *tmp_orig_node, *orig_node = NULL;
|
||||
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(tmp_orig_node,
|
||||
&bat_priv->mcast.want_all_unsnoopables_list,
|
||||
mcast_want_all_unsnoopables_node) {
|
||||
if (!atomic_inc_not_zero(&tmp_orig_node->refcount))
|
||||
continue;
|
||||
|
||||
orig_node = tmp_orig_node;
|
||||
break;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return orig_node;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_forw_mode - check on how to forward a multicast packet
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @skb: The multicast packet to check
|
||||
* @orig: an originator to be set to forward the skb to
|
||||
*
|
||||
* Returns the forwarding mode as enum batadv_forw_mode and in case of
|
||||
* BATADV_FORW_SINGLE set the orig to the single originator the skb
|
||||
* should be forwarded to.
|
||||
*/
|
||||
enum batadv_forw_mode
|
||||
batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
|
||||
struct batadv_orig_node **orig)
|
||||
{
|
||||
int ret, tt_count, ip_count, unsnoop_count, total_count;
|
||||
bool is_unsnoopable = false;
|
||||
struct ethhdr *ethhdr;
|
||||
|
||||
ret = batadv_mcast_forw_mode_check(bat_priv, skb, &is_unsnoopable);
|
||||
if (ret == -ENOMEM)
|
||||
return BATADV_FORW_NONE;
|
||||
else if (ret < 0)
|
||||
return BATADV_FORW_ALL;
|
||||
|
||||
ethhdr = eth_hdr(skb);
|
||||
|
||||
tt_count = batadv_tt_global_hash_count(bat_priv, ethhdr->h_dest,
|
||||
BATADV_NO_FLAGS);
|
||||
ip_count = batadv_mcast_forw_want_all_ip_count(bat_priv, ethhdr);
|
||||
unsnoop_count = !is_unsnoopable ? 0 :
|
||||
atomic_read(&bat_priv->mcast.num_want_all_unsnoopables);
|
||||
|
||||
total_count = tt_count + ip_count + unsnoop_count;
|
||||
|
||||
switch (total_count) {
|
||||
case 1:
|
||||
if (tt_count)
|
||||
*orig = batadv_mcast_forw_tt_node_get(bat_priv, ethhdr);
|
||||
else if (ip_count)
|
||||
*orig = batadv_mcast_forw_ip_node_get(bat_priv, ethhdr);
|
||||
else if (unsnoop_count)
|
||||
*orig = batadv_mcast_forw_unsnoop_node_get(bat_priv);
|
||||
|
||||
if (*orig)
|
||||
return BATADV_FORW_SINGLE;
|
||||
|
||||
/* fall through */
|
||||
case 0:
|
||||
return BATADV_FORW_NONE;
|
||||
default:
|
||||
return BATADV_FORW_ALL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_want_unsnoop_update - update unsnoop counter and list
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @orig: the orig_node which multicast state might have changed of
|
||||
* @mcast_flags: flags indicating the new multicast state
|
||||
*
|
||||
* If the BATADV_MCAST_WANT_ALL_UNSNOOPABLES flag of this originator,
|
||||
* orig, has toggled then this method updates counter and list accordingly.
|
||||
*/
|
||||
static void batadv_mcast_want_unsnoop_update(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig,
|
||||
uint8_t mcast_flags)
|
||||
{
|
||||
/* switched from flag unset to set */
|
||||
if (mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES &&
|
||||
!(orig->mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES)) {
|
||||
atomic_inc(&bat_priv->mcast.num_want_all_unsnoopables);
|
||||
|
||||
spin_lock_bh(&bat_priv->mcast.want_lists_lock);
|
||||
hlist_add_head_rcu(&orig->mcast_want_all_unsnoopables_node,
|
||||
&bat_priv->mcast.want_all_unsnoopables_list);
|
||||
spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
|
||||
/* switched from flag set to unset */
|
||||
} else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) &&
|
||||
orig->mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) {
|
||||
atomic_dec(&bat_priv->mcast.num_want_all_unsnoopables);
|
||||
|
||||
spin_lock_bh(&bat_priv->mcast.want_lists_lock);
|
||||
hlist_del_rcu(&orig->mcast_want_all_unsnoopables_node);
|
||||
spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_want_ipv4_update - update want-all-ipv4 counter and list
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @orig: the orig_node which multicast state might have changed of
|
||||
* @mcast_flags: flags indicating the new multicast state
|
||||
*
|
||||
* If the BATADV_MCAST_WANT_ALL_IPV4 flag of this originator, orig, has
|
||||
* toggled then this method updates counter and list accordingly.
|
||||
*/
|
||||
static void batadv_mcast_want_ipv4_update(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig,
|
||||
uint8_t mcast_flags)
|
||||
{
|
||||
/* switched from flag unset to set */
|
||||
if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV4 &&
|
||||
!(orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV4)) {
|
||||
atomic_inc(&bat_priv->mcast.num_want_all_ipv4);
|
||||
|
||||
spin_lock_bh(&bat_priv->mcast.want_lists_lock);
|
||||
hlist_add_head_rcu(&orig->mcast_want_all_ipv4_node,
|
||||
&bat_priv->mcast.want_all_ipv4_list);
|
||||
spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
|
||||
/* switched from flag set to unset */
|
||||
} else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_IPV4) &&
|
||||
orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV4) {
|
||||
atomic_dec(&bat_priv->mcast.num_want_all_ipv4);
|
||||
|
||||
spin_lock_bh(&bat_priv->mcast.want_lists_lock);
|
||||
hlist_del_rcu(&orig->mcast_want_all_ipv4_node);
|
||||
spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_want_ipv6_update - update want-all-ipv6 counter and list
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @orig: the orig_node which multicast state might have changed of
|
||||
* @mcast_flags: flags indicating the new multicast state
|
||||
*
|
||||
* If the BATADV_MCAST_WANT_ALL_IPV6 flag of this originator, orig, has
|
||||
* toggled then this method updates counter and list accordingly.
|
||||
*/
|
||||
static void batadv_mcast_want_ipv6_update(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig,
|
||||
uint8_t mcast_flags)
|
||||
{
|
||||
/* switched from flag unset to set */
|
||||
if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV6 &&
|
||||
!(orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV6)) {
|
||||
atomic_inc(&bat_priv->mcast.num_want_all_ipv6);
|
||||
|
||||
spin_lock_bh(&bat_priv->mcast.want_lists_lock);
|
||||
hlist_add_head_rcu(&orig->mcast_want_all_ipv6_node,
|
||||
&bat_priv->mcast.want_all_ipv6_list);
|
||||
spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
|
||||
/* switched from flag set to unset */
|
||||
} else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_IPV6) &&
|
||||
orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV6) {
|
||||
atomic_dec(&bat_priv->mcast.num_want_all_ipv6);
|
||||
|
||||
spin_lock_bh(&bat_priv->mcast.want_lists_lock);
|
||||
hlist_del_rcu(&orig->mcast_want_all_ipv6_node);
|
||||
spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_tvlv_ogm_handler_v1 - process incoming multicast tvlv container
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @orig: the orig_node of the ogm
|
||||
* @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
|
||||
* @tvlv_value: tvlv buffer containing the multicast data
|
||||
* @tvlv_value_len: tvlv buffer length
|
||||
*/
|
||||
static void batadv_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig,
|
||||
uint8_t flags,
|
||||
void *tvlv_value,
|
||||
uint16_t tvlv_value_len)
|
||||
{
|
||||
bool orig_mcast_enabled = !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
|
||||
uint8_t mcast_flags = BATADV_NO_FLAGS;
|
||||
bool orig_initialized;
|
||||
|
||||
orig_initialized = orig->capa_initialized & BATADV_ORIG_CAPA_HAS_MCAST;
|
||||
|
||||
/* If mcast support is turned on decrease the disabled mcast node
|
||||
* counter only if we had increased it for this node before. If this
|
||||
* is a completely new orig_node no need to decrease the counter.
|
||||
*/
|
||||
if (orig_mcast_enabled &&
|
||||
!(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST)) {
|
||||
if (orig_initialized)
|
||||
atomic_dec(&bat_priv->mcast.num_disabled);
|
||||
orig->capabilities |= BATADV_ORIG_CAPA_HAS_MCAST;
|
||||
/* If mcast support is being switched off increase the disabled
|
||||
* mcast node counter.
|
||||
*/
|
||||
} else if (!orig_mcast_enabled &&
|
||||
orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST) {
|
||||
atomic_inc(&bat_priv->mcast.num_disabled);
|
||||
orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_MCAST;
|
||||
}
|
||||
|
||||
orig->capa_initialized |= BATADV_ORIG_CAPA_HAS_MCAST;
|
||||
|
||||
if (orig_mcast_enabled && tvlv_value &&
|
||||
(tvlv_value_len >= sizeof(mcast_flags)))
|
||||
mcast_flags = *(uint8_t *)tvlv_value;
|
||||
|
||||
batadv_mcast_want_unsnoop_update(bat_priv, orig, mcast_flags);
|
||||
batadv_mcast_want_ipv4_update(bat_priv, orig, mcast_flags);
|
||||
batadv_mcast_want_ipv6_update(bat_priv, orig, mcast_flags);
|
||||
|
||||
orig->mcast_flags = mcast_flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_init - initialize the multicast optimizations structures
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
*/
|
||||
void batadv_mcast_init(struct batadv_priv *bat_priv)
|
||||
{
|
||||
batadv_tvlv_handler_register(bat_priv, batadv_mcast_tvlv_ogm_handler_v1,
|
||||
NULL, BATADV_TVLV_MCAST, 1,
|
||||
BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_free - free the multicast optimizations structures
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
*/
|
||||
void batadv_mcast_free(struct batadv_priv *bat_priv)
|
||||
{
|
||||
batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_MCAST, 1);
|
||||
batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_MCAST, 1);
|
||||
|
||||
batadv_mcast_mla_tt_retract(bat_priv, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_mcast_purge_orig - reset originator global mcast state modifications
|
||||
* @orig: the originator which is going to get purged
|
||||
*/
|
||||
void batadv_mcast_purge_orig(struct batadv_orig_node *orig)
|
||||
{
|
||||
struct batadv_priv *bat_priv = orig->bat_priv;
|
||||
|
||||
if (!(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST))
|
||||
atomic_dec(&bat_priv->mcast.num_disabled);
|
||||
|
||||
batadv_mcast_want_unsnoop_update(bat_priv, orig, BATADV_NO_FLAGS);
|
||||
batadv_mcast_want_ipv4_update(bat_priv, orig, BATADV_NO_FLAGS);
|
||||
batadv_mcast_want_ipv6_update(bat_priv, orig, BATADV_NO_FLAGS);
|
||||
}
|
80
net/batman-adv/multicast.h
Normal file
80
net/batman-adv/multicast.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/* Copyright (C) 2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Linus Lüssing
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_MULTICAST_H_
|
||||
#define _NET_BATMAN_ADV_MULTICAST_H_
|
||||
|
||||
/**
|
||||
* batadv_forw_mode - the way a packet should be forwarded as
|
||||
* @BATADV_FORW_ALL: forward the packet to all nodes (currently via classic
|
||||
* flooding)
|
||||
* @BATADV_FORW_SINGLE: forward the packet to a single node (currently via the
|
||||
* BATMAN unicast routing protocol)
|
||||
* @BATADV_FORW_NONE: don't forward, drop it
|
||||
*/
|
||||
enum batadv_forw_mode {
|
||||
BATADV_FORW_ALL,
|
||||
BATADV_FORW_SINGLE,
|
||||
BATADV_FORW_NONE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BATMAN_ADV_MCAST
|
||||
|
||||
void batadv_mcast_mla_update(struct batadv_priv *bat_priv);
|
||||
|
||||
enum batadv_forw_mode
|
||||
batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
|
||||
struct batadv_orig_node **mcast_single_orig);
|
||||
|
||||
void batadv_mcast_init(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);
|
||||
|
||||
#else
|
||||
|
||||
static inline void batadv_mcast_mla_update(struct batadv_priv *bat_priv)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline enum batadv_forw_mode
|
||||
batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
|
||||
struct batadv_orig_node **mcast_single_orig)
|
||||
{
|
||||
return BATADV_FORW_ALL;
|
||||
}
|
||||
|
||||
static inline int batadv_mcast_init(struct batadv_priv *bat_priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void batadv_mcast_free(struct batadv_priv *bat_priv)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BATMAN_ADV_MCAST */
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_MULTICAST_H_ */
|
1937
net/batman-adv/network-coding.c
Normal file
1937
net/batman-adv/network-coding.c
Normal file
File diff suppressed because it is too large
Load diff
123
net/batman-adv/network-coding.h
Normal file
123
net/batman-adv/network-coding.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
/* Copyright (C) 2012-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Martin Hundebøll, Jeppe Ledet-Pedersen
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_NETWORK_CODING_H_
|
||||
#define _NET_BATMAN_ADV_NETWORK_CODING_H_
|
||||
|
||||
#ifdef CONFIG_BATMAN_ADV_NC
|
||||
|
||||
void batadv_nc_status_update(struct net_device *net_dev);
|
||||
int batadv_nc_init(void);
|
||||
int batadv_nc_mesh_init(struct batadv_priv *bat_priv);
|
||||
void batadv_nc_mesh_free(struct batadv_priv *bat_priv);
|
||||
void batadv_nc_update_nc_node(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node,
|
||||
struct batadv_orig_node *orig_neigh_node,
|
||||
struct batadv_ogm_packet *ogm_packet,
|
||||
int is_single_hop_neigh);
|
||||
void batadv_nc_purge_orig(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node,
|
||||
bool (*to_purge)(struct batadv_priv *,
|
||||
struct batadv_nc_node *));
|
||||
void batadv_nc_init_bat_priv(struct batadv_priv *bat_priv);
|
||||
void batadv_nc_init_orig(struct batadv_orig_node *orig_node);
|
||||
bool batadv_nc_skb_forward(struct sk_buff *skb,
|
||||
struct batadv_neigh_node *neigh_node);
|
||||
void batadv_nc_skb_store_for_decoding(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb);
|
||||
void batadv_nc_skb_store_sniffed_unicast(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb);
|
||||
int batadv_nc_nodes_seq_print_text(struct seq_file *seq, void *offset);
|
||||
int batadv_nc_init_debugfs(struct batadv_priv *bat_priv);
|
||||
|
||||
#else /* ifdef CONFIG_BATMAN_ADV_NC */
|
||||
|
||||
static inline void batadv_nc_status_update(struct net_device *net_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int batadv_nc_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int batadv_nc_mesh_init(struct batadv_priv *bat_priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void batadv_nc_mesh_free(struct batadv_priv *bat_priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void
|
||||
batadv_nc_update_nc_node(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node,
|
||||
struct batadv_orig_node *orig_neigh_node,
|
||||
struct batadv_ogm_packet *ogm_packet,
|
||||
int is_single_hop_neigh)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void
|
||||
batadv_nc_purge_orig(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node,
|
||||
bool (*to_purge)(struct batadv_priv *,
|
||||
struct batadv_nc_node *))
|
||||
{
|
||||
}
|
||||
|
||||
static inline void batadv_nc_init_bat_priv(struct batadv_priv *bat_priv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void batadv_nc_init_orig(struct batadv_orig_node *orig_node)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool batadv_nc_skb_forward(struct sk_buff *skb,
|
||||
struct batadv_neigh_node *neigh_node)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void
|
||||
batadv_nc_skb_store_for_decoding(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void
|
||||
batadv_nc_skb_store_sniffed_unicast(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int batadv_nc_nodes_seq_print_text(struct seq_file *seq,
|
||||
void *offset)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int batadv_nc_init_debugfs(struct batadv_priv *bat_priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* ifdef CONFIG_BATMAN_ADV_NC */
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_NETWORK_CODING_H_ */
|
1177
net/batman-adv/originator.c
Normal file
1177
net/batman-adv/originator.c
Normal file
File diff suppressed because it is too large
Load diff
126
net/batman-adv/originator.h
Normal file
126
net/batman-adv/originator.h
Normal file
|
@ -0,0 +1,126 @@
|
|||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_ORIGINATOR_H_
|
||||
#define _NET_BATMAN_ADV_ORIGINATOR_H_
|
||||
|
||||
#include "hash.h"
|
||||
|
||||
int batadv_compare_orig(const struct hlist_node *node, const void *data2);
|
||||
int batadv_originator_init(struct batadv_priv *bat_priv);
|
||||
void batadv_originator_free(struct batadv_priv *bat_priv);
|
||||
void batadv_purge_orig_ref(struct batadv_priv *bat_priv);
|
||||
void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node);
|
||||
void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node);
|
||||
struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
|
||||
const uint8_t *addr);
|
||||
struct batadv_neigh_node *
|
||||
batadv_neigh_node_get(const struct batadv_orig_node *orig_node,
|
||||
const struct batadv_hard_iface *hard_iface,
|
||||
const uint8_t *addr);
|
||||
struct batadv_neigh_node *
|
||||
batadv_neigh_node_new(struct batadv_hard_iface *hard_iface,
|
||||
const uint8_t *neigh_addr,
|
||||
struct batadv_orig_node *orig_node);
|
||||
void batadv_neigh_node_free_ref(struct batadv_neigh_node *neigh_node);
|
||||
struct batadv_neigh_node *
|
||||
batadv_orig_router_get(struct batadv_orig_node *orig_node,
|
||||
const struct batadv_hard_iface *if_outgoing);
|
||||
struct batadv_neigh_ifinfo *
|
||||
batadv_neigh_ifinfo_new(struct batadv_neigh_node *neigh,
|
||||
struct batadv_hard_iface *if_outgoing);
|
||||
struct batadv_neigh_ifinfo *
|
||||
batadv_neigh_ifinfo_get(struct batadv_neigh_node *neigh,
|
||||
struct batadv_hard_iface *if_outgoing);
|
||||
void batadv_neigh_ifinfo_free_ref(struct batadv_neigh_ifinfo *neigh_ifinfo);
|
||||
|
||||
struct batadv_orig_ifinfo *
|
||||
batadv_orig_ifinfo_get(struct batadv_orig_node *orig_node,
|
||||
struct batadv_hard_iface *if_outgoing);
|
||||
struct batadv_orig_ifinfo *
|
||||
batadv_orig_ifinfo_new(struct batadv_orig_node *orig_node,
|
||||
struct batadv_hard_iface *if_outgoing);
|
||||
void batadv_orig_ifinfo_free_ref(struct batadv_orig_ifinfo *orig_ifinfo);
|
||||
|
||||
int batadv_orig_seq_print_text(struct seq_file *seq, void *offset);
|
||||
int batadv_orig_hardif_seq_print_text(struct seq_file *seq, void *offset);
|
||||
int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface,
|
||||
int max_if_num);
|
||||
int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface,
|
||||
int max_if_num);
|
||||
struct batadv_orig_node_vlan *
|
||||
batadv_orig_node_vlan_new(struct batadv_orig_node *orig_node,
|
||||
unsigned short vid);
|
||||
struct batadv_orig_node_vlan *
|
||||
batadv_orig_node_vlan_get(struct batadv_orig_node *orig_node,
|
||||
unsigned short vid);
|
||||
void batadv_orig_node_vlan_free_ref(struct batadv_orig_node_vlan *orig_vlan);
|
||||
|
||||
|
||||
/* hashfunction to choose an entry in a hash table of given size
|
||||
* hash algorithm from http://en.wikipedia.org/wiki/Hash_table
|
||||
*/
|
||||
static inline uint32_t batadv_choose_orig(const void *data, uint32_t size)
|
||||
{
|
||||
const unsigned char *key = data;
|
||||
uint32_t hash = 0;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
hash += key[i];
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
hash += (hash << 15);
|
||||
|
||||
return hash % size;
|
||||
}
|
||||
|
||||
static inline struct batadv_orig_node *
|
||||
batadv_orig_hash_find(struct batadv_priv *bat_priv, const void *data)
|
||||
{
|
||||
struct batadv_hashtable *hash = bat_priv->orig_hash;
|
||||
struct hlist_head *head;
|
||||
struct batadv_orig_node *orig_node, *orig_node_tmp = NULL;
|
||||
int index;
|
||||
|
||||
if (!hash)
|
||||
return NULL;
|
||||
|
||||
index = batadv_choose_orig(data, hash->size);
|
||||
head = &hash->table[index];
|
||||
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
|
||||
if (!batadv_compare_eth(orig_node, data))
|
||||
continue;
|
||||
|
||||
if (!atomic_inc_not_zero(&orig_node->refcount))
|
||||
continue;
|
||||
|
||||
orig_node_tmp = orig_node;
|
||||
break;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return orig_node_tmp;
|
||||
}
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_ORIGINATOR_H_ */
|
552
net/batman-adv/packet.h
Normal file
552
net/batman-adv/packet.h
Normal file
|
@ -0,0 +1,552 @@
|
|||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_PACKET_H_
|
||||
#define _NET_BATMAN_ADV_PACKET_H_
|
||||
|
||||
/**
|
||||
* enum batadv_packettype - types for batman-adv encapsulated packets
|
||||
* @BATADV_IV_OGM: originator messages for B.A.T.M.A.N. IV
|
||||
* @BATADV_BCAST: broadcast packets carrying broadcast payload
|
||||
* @BATADV_CODED: network coded packets
|
||||
*
|
||||
* @BATADV_UNICAST: unicast packets carrying unicast payload traffic
|
||||
* @BATADV_UNICAST_FRAG: unicast packets carrying a fragment of the original
|
||||
* payload packet
|
||||
* @BATADV_UNICAST_4ADDR: unicast packet including the originator address of
|
||||
* the sender
|
||||
* @BATADV_ICMP: unicast packet like IP ICMP used for ping or traceroute
|
||||
* @BATADV_UNICAST_TVLV: unicast packet carrying TVLV containers
|
||||
*/
|
||||
enum batadv_packettype {
|
||||
/* 0x00 - 0x3f: local packets or special rules for handling */
|
||||
BATADV_IV_OGM = 0x00,
|
||||
BATADV_BCAST = 0x01,
|
||||
BATADV_CODED = 0x02,
|
||||
/* 0x40 - 0x7f: unicast */
|
||||
#define BATADV_UNICAST_MIN 0x40
|
||||
BATADV_UNICAST = 0x40,
|
||||
BATADV_UNICAST_FRAG = 0x41,
|
||||
BATADV_UNICAST_4ADDR = 0x42,
|
||||
BATADV_ICMP = 0x43,
|
||||
BATADV_UNICAST_TVLV = 0x44,
|
||||
#define BATADV_UNICAST_MAX 0x7f
|
||||
/* 0x80 - 0xff: reserved */
|
||||
};
|
||||
|
||||
/**
|
||||
* enum batadv_subtype - packet subtype for unicast4addr
|
||||
* @BATADV_P_DATA: user payload
|
||||
* @BATADV_P_DAT_DHT_GET: DHT request message
|
||||
* @BATADV_P_DAT_DHT_PUT: DHT store message
|
||||
* @BATADV_P_DAT_CACHE_REPLY: ARP reply generated by DAT
|
||||
*/
|
||||
enum batadv_subtype {
|
||||
BATADV_P_DATA = 0x01,
|
||||
BATADV_P_DAT_DHT_GET = 0x02,
|
||||
BATADV_P_DAT_DHT_PUT = 0x03,
|
||||
BATADV_P_DAT_CACHE_REPLY = 0x04,
|
||||
};
|
||||
|
||||
/* this file is included by batctl which needs these defines */
|
||||
#define BATADV_COMPAT_VERSION 15
|
||||
|
||||
/**
|
||||
* enum batadv_iv_flags - flags used in B.A.T.M.A.N. IV OGM packets
|
||||
* @BATADV_NOT_BEST_NEXT_HOP: flag is set when ogm packet is forwarded and was
|
||||
* previously received from someone else than the best neighbor.
|
||||
* @BATADV_PRIMARIES_FIRST_HOP: flag is set when the primary interface address
|
||||
* is used, and the packet travels its first hop.
|
||||
* @BATADV_DIRECTLINK: flag is for the first hop or if rebroadcasted from a
|
||||
* one hop neighbor on the interface where it was originally received.
|
||||
*/
|
||||
enum batadv_iv_flags {
|
||||
BATADV_NOT_BEST_NEXT_HOP = BIT(0),
|
||||
BATADV_PRIMARIES_FIRST_HOP = BIT(1),
|
||||
BATADV_DIRECTLINK = BIT(2),
|
||||
};
|
||||
|
||||
/* ICMP message types */
|
||||
enum batadv_icmp_packettype {
|
||||
BATADV_ECHO_REPLY = 0,
|
||||
BATADV_DESTINATION_UNREACHABLE = 3,
|
||||
BATADV_ECHO_REQUEST = 8,
|
||||
BATADV_TTL_EXCEEDED = 11,
|
||||
BATADV_PARAMETER_PROBLEM = 12,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum batadv_mcast_flags - flags for multicast capabilities and settings
|
||||
* @BATADV_MCAST_WANT_ALL_UNSNOOPABLES: we want all packets destined for
|
||||
* 224.0.0.0/24 or ff02::1
|
||||
* @BATADV_MCAST_WANT_ALL_IPV4: we want all IPv4 multicast packets
|
||||
* @BATADV_MCAST_WANT_ALL_IPV6: we want all IPv6 multicast packets
|
||||
*/
|
||||
enum batadv_mcast_flags {
|
||||
BATADV_MCAST_WANT_ALL_UNSNOOPABLES = BIT(0),
|
||||
BATADV_MCAST_WANT_ALL_IPV4 = BIT(1),
|
||||
BATADV_MCAST_WANT_ALL_IPV6 = BIT(2),
|
||||
};
|
||||
|
||||
/* tt data subtypes */
|
||||
#define BATADV_TT_DATA_TYPE_MASK 0x0F
|
||||
|
||||
/**
|
||||
* enum batadv_tt_data_flags - flags for tt data tvlv
|
||||
* @BATADV_TT_OGM_DIFF: TT diff propagated through OGM
|
||||
* @BATADV_TT_REQUEST: TT request message
|
||||
* @BATADV_TT_RESPONSE: TT response message
|
||||
* @BATADV_TT_FULL_TABLE: contains full table to replace existing table
|
||||
*/
|
||||
enum batadv_tt_data_flags {
|
||||
BATADV_TT_OGM_DIFF = BIT(0),
|
||||
BATADV_TT_REQUEST = BIT(1),
|
||||
BATADV_TT_RESPONSE = BIT(2),
|
||||
BATADV_TT_FULL_TABLE = BIT(4),
|
||||
};
|
||||
|
||||
/**
|
||||
* enum batadv_tt_client_flags - TT client specific flags
|
||||
* @BATADV_TT_CLIENT_DEL: the client has to be deleted from the table
|
||||
* @BATADV_TT_CLIENT_ROAM: the client roamed to/from another node and the new
|
||||
* update telling its new real location has not been received/sent yet
|
||||
* @BATADV_TT_CLIENT_WIFI: this client is connected through a wifi interface.
|
||||
* This information is used by the "AP Isolation" feature
|
||||
* @BATADV_TT_CLIENT_ISOLA: this client is considered "isolated". This
|
||||
* information is used by the Extended Isolation feature
|
||||
* @BATADV_TT_CLIENT_NOPURGE: this client should never be removed from the table
|
||||
* @BATADV_TT_CLIENT_NEW: this client has been added to the local table but has
|
||||
* not been announced yet
|
||||
* @BATADV_TT_CLIENT_PENDING: this client is marked for removal but it is kept
|
||||
* in the table for one more originator interval for consistency purposes
|
||||
* @BATADV_TT_CLIENT_TEMP: this global client has been detected to be part of
|
||||
* the network but no nnode has already announced it
|
||||
*
|
||||
* Bits from 0 to 7 are called _remote flags_ because they are sent on the wire.
|
||||
* Bits from 8 to 15 are called _local flags_ because they are used for local
|
||||
* computations only.
|
||||
*
|
||||
* Bits from 4 to 7 - a subset of remote flags - are ensured to be in sync with
|
||||
* the other nodes in the network. To achieve this goal these flags are included
|
||||
* in the TT CRC computation.
|
||||
*/
|
||||
enum batadv_tt_client_flags {
|
||||
BATADV_TT_CLIENT_DEL = BIT(0),
|
||||
BATADV_TT_CLIENT_ROAM = BIT(1),
|
||||
BATADV_TT_CLIENT_WIFI = BIT(4),
|
||||
BATADV_TT_CLIENT_ISOLA = BIT(5),
|
||||
BATADV_TT_CLIENT_NOPURGE = BIT(8),
|
||||
BATADV_TT_CLIENT_NEW = BIT(9),
|
||||
BATADV_TT_CLIENT_PENDING = BIT(10),
|
||||
BATADV_TT_CLIENT_TEMP = BIT(11),
|
||||
};
|
||||
|
||||
/**
|
||||
* batadv_vlan_flags - flags for the four MSB of any vlan ID field
|
||||
* @BATADV_VLAN_HAS_TAG: whether the field contains a valid vlan tag or not
|
||||
*/
|
||||
enum batadv_vlan_flags {
|
||||
BATADV_VLAN_HAS_TAG = BIT(15),
|
||||
};
|
||||
|
||||
/* claim frame types for the bridge loop avoidance */
|
||||
enum batadv_bla_claimframe {
|
||||
BATADV_CLAIM_TYPE_CLAIM = 0x00,
|
||||
BATADV_CLAIM_TYPE_UNCLAIM = 0x01,
|
||||
BATADV_CLAIM_TYPE_ANNOUNCE = 0x02,
|
||||
BATADV_CLAIM_TYPE_REQUEST = 0x03,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum batadv_tvlv_type - tvlv type definitions
|
||||
* @BATADV_TVLV_GW: gateway tvlv
|
||||
* @BATADV_TVLV_DAT: distributed arp table tvlv
|
||||
* @BATADV_TVLV_NC: network coding tvlv
|
||||
* @BATADV_TVLV_TT: translation table tvlv
|
||||
* @BATADV_TVLV_ROAM: roaming advertisement tvlv
|
||||
* @BATADV_TVLV_MCAST: multicast capability tvlv
|
||||
*/
|
||||
enum batadv_tvlv_type {
|
||||
BATADV_TVLV_GW = 0x01,
|
||||
BATADV_TVLV_DAT = 0x02,
|
||||
BATADV_TVLV_NC = 0x03,
|
||||
BATADV_TVLV_TT = 0x04,
|
||||
BATADV_TVLV_ROAM = 0x05,
|
||||
BATADV_TVLV_MCAST = 0x06,
|
||||
};
|
||||
|
||||
#pragma pack(2)
|
||||
/* the destination hardware field in the ARP frame is used to
|
||||
* transport the claim type and the group id
|
||||
*/
|
||||
struct batadv_bla_claim_dst {
|
||||
uint8_t magic[3]; /* FF:43:05 */
|
||||
uint8_t type; /* bla_claimframe */
|
||||
__be16 group; /* group id */
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
/**
|
||||
* struct batadv_ogm_packet - ogm (routing protocol) packet
|
||||
* @packet_type: batman-adv packet type, part of the general header
|
||||
* @version: batman-adv protocol version, part of the genereal header
|
||||
* @ttl: time to live for this packet, part of the genereal header
|
||||
* @flags: contains routing relevant flags - see enum batadv_iv_flags
|
||||
* @tvlv_len: length of tvlv data following the ogm header
|
||||
*/
|
||||
struct batadv_ogm_packet {
|
||||
uint8_t packet_type;
|
||||
uint8_t version;
|
||||
uint8_t ttl;
|
||||
uint8_t flags;
|
||||
__be32 seqno;
|
||||
uint8_t orig[ETH_ALEN];
|
||||
uint8_t prev_sender[ETH_ALEN];
|
||||
uint8_t reserved;
|
||||
uint8_t tq;
|
||||
__be16 tvlv_len;
|
||||
/* __packed is not needed as the struct size is divisible by 4,
|
||||
* and the largest data type in this struct has a size of 4.
|
||||
*/
|
||||
};
|
||||
|
||||
#define BATADV_OGM_HLEN sizeof(struct batadv_ogm_packet)
|
||||
|
||||
/**
|
||||
* batadv_icmp_header - common members among all the ICMP packets
|
||||
* @packet_type: batman-adv packet type, part of the general header
|
||||
* @version: batman-adv protocol version, part of the genereal header
|
||||
* @ttl: time to live for this packet, part of the genereal header
|
||||
* @msg_type: ICMP packet type
|
||||
* @dst: address of the destination node
|
||||
* @orig: address of the source node
|
||||
* @uid: local ICMP socket identifier
|
||||
* @align: not used - useful for alignment purposes only
|
||||
*
|
||||
* This structure is used for ICMP packets parsing only and it is never sent
|
||||
* over the wire. The alignment field at the end is there to ensure that
|
||||
* members are padded the same way as they are in real packets.
|
||||
*/
|
||||
struct batadv_icmp_header {
|
||||
uint8_t packet_type;
|
||||
uint8_t version;
|
||||
uint8_t ttl;
|
||||
uint8_t msg_type; /* see ICMP message types above */
|
||||
uint8_t dst[ETH_ALEN];
|
||||
uint8_t orig[ETH_ALEN];
|
||||
uint8_t uid;
|
||||
uint8_t align[3];
|
||||
};
|
||||
|
||||
/**
|
||||
* batadv_icmp_packet - ICMP packet
|
||||
* @packet_type: batman-adv packet type, part of the general header
|
||||
* @version: batman-adv protocol version, part of the genereal header
|
||||
* @ttl: time to live for this packet, part of the genereal header
|
||||
* @msg_type: ICMP packet type
|
||||
* @dst: address of the destination node
|
||||
* @orig: address of the source node
|
||||
* @uid: local ICMP socket identifier
|
||||
* @reserved: not used - useful for alignment
|
||||
* @seqno: ICMP sequence number
|
||||
*/
|
||||
struct batadv_icmp_packet {
|
||||
uint8_t packet_type;
|
||||
uint8_t version;
|
||||
uint8_t ttl;
|
||||
uint8_t msg_type; /* see ICMP message types above */
|
||||
uint8_t dst[ETH_ALEN];
|
||||
uint8_t orig[ETH_ALEN];
|
||||
uint8_t uid;
|
||||
uint8_t reserved;
|
||||
__be16 seqno;
|
||||
};
|
||||
|
||||
#define BATADV_RR_LEN 16
|
||||
|
||||
/**
|
||||
* batadv_icmp_packet_rr - ICMP RouteRecord packet
|
||||
* @packet_type: batman-adv packet type, part of the general header
|
||||
* @version: batman-adv protocol version, part of the genereal header
|
||||
* @ttl: time to live for this packet, part of the genereal header
|
||||
* @msg_type: ICMP packet type
|
||||
* @dst: address of the destination node
|
||||
* @orig: address of the source node
|
||||
* @uid: local ICMP socket identifier
|
||||
* @rr_cur: number of entries the rr array
|
||||
* @seqno: ICMP sequence number
|
||||
* @rr: route record array
|
||||
*/
|
||||
struct batadv_icmp_packet_rr {
|
||||
uint8_t packet_type;
|
||||
uint8_t version;
|
||||
uint8_t ttl;
|
||||
uint8_t msg_type; /* see ICMP message types above */
|
||||
uint8_t dst[ETH_ALEN];
|
||||
uint8_t orig[ETH_ALEN];
|
||||
uint8_t uid;
|
||||
uint8_t rr_cur;
|
||||
__be16 seqno;
|
||||
uint8_t rr[BATADV_RR_LEN][ETH_ALEN];
|
||||
};
|
||||
|
||||
#define BATADV_ICMP_MAX_PACKET_SIZE sizeof(struct batadv_icmp_packet_rr)
|
||||
|
||||
/* All packet headers in front of an ethernet header have to be completely
|
||||
* divisible by 2 but not by 4 to make the payload after the ethernet
|
||||
* header again 4 bytes boundary aligned.
|
||||
*
|
||||
* A packing of 2 is necessary to avoid extra padding at the end of the struct
|
||||
* caused by a structure member which is larger than two bytes. Otherwise
|
||||
* the structure would not fulfill the previously mentioned rule to avoid the
|
||||
* misalignment of the payload after the ethernet header. It may also lead to
|
||||
* leakage of information when the padding it not initialized before sending.
|
||||
*/
|
||||
#pragma pack(2)
|
||||
|
||||
/**
|
||||
* struct batadv_unicast_packet - unicast packet for network payload
|
||||
* @packet_type: batman-adv packet type, part of the general header
|
||||
* @version: batman-adv protocol version, part of the genereal header
|
||||
* @ttl: time to live for this packet, part of the genereal header
|
||||
* @ttvn: translation table version number
|
||||
* @dest: originator destination of the unicast packet
|
||||
*/
|
||||
struct batadv_unicast_packet {
|
||||
uint8_t packet_type;
|
||||
uint8_t version;
|
||||
uint8_t ttl;
|
||||
uint8_t ttvn; /* destination translation table version number */
|
||||
uint8_t dest[ETH_ALEN];
|
||||
/* "4 bytes boundary + 2 bytes" long to make the payload after the
|
||||
* following ethernet header again 4 bytes boundary aligned
|
||||
*/
|
||||
};
|
||||
|
||||
/**
|
||||
* struct batadv_unicast_4addr_packet - extended unicast packet
|
||||
* @u: common unicast packet header
|
||||
* @src: address of the source
|
||||
* @subtype: packet subtype
|
||||
*/
|
||||
struct batadv_unicast_4addr_packet {
|
||||
struct batadv_unicast_packet u;
|
||||
uint8_t src[ETH_ALEN];
|
||||
uint8_t subtype;
|
||||
uint8_t reserved;
|
||||
/* "4 bytes boundary + 2 bytes" long to make the payload after the
|
||||
* following ethernet header again 4 bytes boundary aligned
|
||||
*/
|
||||
};
|
||||
|
||||
/**
|
||||
* struct batadv_frag_packet - fragmented packet
|
||||
* @packet_type: batman-adv packet type, part of the general header
|
||||
* @version: batman-adv protocol version, part of the genereal header
|
||||
* @ttl: time to live for this packet, part of the genereal header
|
||||
* @dest: final destination used when routing fragments
|
||||
* @orig: originator of the fragment used when merging the packet
|
||||
* @no: fragment number within this sequence
|
||||
* @reserved: reserved byte for alignment
|
||||
* @seqno: sequence identification
|
||||
* @total_size: size of the merged packet
|
||||
*/
|
||||
struct batadv_frag_packet {
|
||||
uint8_t packet_type;
|
||||
uint8_t version; /* batman version field */
|
||||
uint8_t ttl;
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
uint8_t no:4;
|
||||
uint8_t reserved:4;
|
||||
#elif defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
uint8_t reserved:4;
|
||||
uint8_t no:4;
|
||||
#else
|
||||
#error "unknown bitfield endianess"
|
||||
#endif
|
||||
uint8_t dest[ETH_ALEN];
|
||||
uint8_t orig[ETH_ALEN];
|
||||
__be16 seqno;
|
||||
__be16 total_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct batadv_bcast_packet - broadcast packet for network payload
|
||||
* @packet_type: batman-adv packet type, part of the general header
|
||||
* @version: batman-adv protocol version, part of the genereal header
|
||||
* @ttl: time to live for this packet, part of the genereal header
|
||||
* @reserved: reserved byte for alignment
|
||||
* @seqno: sequence identification
|
||||
* @orig: originator of the broadcast packet
|
||||
*/
|
||||
struct batadv_bcast_packet {
|
||||
uint8_t packet_type;
|
||||
uint8_t version; /* batman version field */
|
||||
uint8_t ttl;
|
||||
uint8_t reserved;
|
||||
__be32 seqno;
|
||||
uint8_t orig[ETH_ALEN];
|
||||
/* "4 bytes boundary + 2 bytes" long to make the payload after the
|
||||
* following ethernet header again 4 bytes boundary aligned
|
||||
*/
|
||||
};
|
||||
|
||||
/**
|
||||
* struct batadv_coded_packet - network coded packet
|
||||
* @packet_type: batman-adv packet type, part of the general header
|
||||
* @version: batman-adv protocol version, part of the genereal header
|
||||
* @ttl: time to live for this packet, part of the genereal header
|
||||
* @reserved: Align following fields to 2-byte boundaries
|
||||
* @first_source: original source of first included packet
|
||||
* @first_orig_dest: original destinal of first included packet
|
||||
* @first_crc: checksum of first included packet
|
||||
* @first_ttvn: tt-version number of first included packet
|
||||
* @second_ttl: ttl of second packet
|
||||
* @second_dest: second receiver of this coded packet
|
||||
* @second_source: original source of second included packet
|
||||
* @second_orig_dest: original destination of second included packet
|
||||
* @second_crc: checksum of second included packet
|
||||
* @second_ttvn: tt version number of second included packet
|
||||
* @coded_len: length of network coded part of the payload
|
||||
*/
|
||||
struct batadv_coded_packet {
|
||||
uint8_t packet_type;
|
||||
uint8_t version; /* batman version field */
|
||||
uint8_t ttl;
|
||||
uint8_t first_ttvn;
|
||||
/* uint8_t first_dest[ETH_ALEN]; - saved in mac header destination */
|
||||
uint8_t first_source[ETH_ALEN];
|
||||
uint8_t first_orig_dest[ETH_ALEN];
|
||||
__be32 first_crc;
|
||||
uint8_t second_ttl;
|
||||
uint8_t second_ttvn;
|
||||
uint8_t second_dest[ETH_ALEN];
|
||||
uint8_t second_source[ETH_ALEN];
|
||||
uint8_t second_orig_dest[ETH_ALEN];
|
||||
__be32 second_crc;
|
||||
__be16 coded_len;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
/**
|
||||
* struct batadv_unicast_tvlv - generic unicast packet with tvlv payload
|
||||
* @packet_type: batman-adv packet type, part of the general header
|
||||
* @version: batman-adv protocol version, part of the genereal header
|
||||
* @ttl: time to live for this packet, part of the genereal header
|
||||
* @reserved: reserved field (for packet alignment)
|
||||
* @src: address of the source
|
||||
* @dst: address of the destination
|
||||
* @tvlv_len: length of tvlv data following the unicast tvlv header
|
||||
* @align: 2 bytes to align the header to a 4 byte boundry
|
||||
*/
|
||||
struct batadv_unicast_tvlv_packet {
|
||||
uint8_t packet_type;
|
||||
uint8_t version; /* batman version field */
|
||||
uint8_t ttl;
|
||||
uint8_t reserved;
|
||||
uint8_t dst[ETH_ALEN];
|
||||
uint8_t src[ETH_ALEN];
|
||||
__be16 tvlv_len;
|
||||
uint16_t align;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct batadv_tvlv_hdr - base tvlv header struct
|
||||
* @type: tvlv container type (see batadv_tvlv_type)
|
||||
* @version: tvlv container version
|
||||
* @len: tvlv container length
|
||||
*/
|
||||
struct batadv_tvlv_hdr {
|
||||
uint8_t type;
|
||||
uint8_t version;
|
||||
__be16 len;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct batadv_tvlv_gateway_data - gateway data propagated through gw tvlv
|
||||
* container
|
||||
* @bandwidth_down: advertised uplink download bandwidth
|
||||
* @bandwidth_up: advertised uplink upload bandwidth
|
||||
*/
|
||||
struct batadv_tvlv_gateway_data {
|
||||
__be32 bandwidth_down;
|
||||
__be32 bandwidth_up;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct batadv_tvlv_tt_data - tt data propagated through the tt tvlv container
|
||||
* @flags: translation table flags (see batadv_tt_data_flags)
|
||||
* @ttvn: translation table version number
|
||||
* @vlan_num: number of announced VLANs. In the TVLV this struct is followed by
|
||||
* one batadv_tvlv_tt_vlan_data object per announced vlan
|
||||
*/
|
||||
struct batadv_tvlv_tt_data {
|
||||
uint8_t flags;
|
||||
uint8_t ttvn;
|
||||
__be16 num_vlan;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct batadv_tvlv_tt_vlan_data - vlan specific tt data propagated through
|
||||
* the tt tvlv container
|
||||
* @crc: crc32 checksum of the entries belonging to this vlan
|
||||
* @vid: vlan identifier
|
||||
* @reserved: unused, useful for alignment purposes
|
||||
*/
|
||||
struct batadv_tvlv_tt_vlan_data {
|
||||
__be32 crc;
|
||||
__be16 vid;
|
||||
uint16_t reserved;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct batadv_tvlv_tt_change - translation table diff data
|
||||
* @flags: status indicators concerning the non-mesh client (see
|
||||
* batadv_tt_client_flags)
|
||||
* @reserved: reserved field - useful for alignment purposes only
|
||||
* @addr: mac address of non-mesh client that triggered this tt change
|
||||
* @vid: VLAN identifier
|
||||
*/
|
||||
struct batadv_tvlv_tt_change {
|
||||
uint8_t flags;
|
||||
uint8_t reserved[3];
|
||||
uint8_t addr[ETH_ALEN];
|
||||
__be16 vid;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct batadv_tvlv_roam_adv - roaming advertisement
|
||||
* @client: mac address of roaming client
|
||||
* @vid: VLAN identifier
|
||||
*/
|
||||
struct batadv_tvlv_roam_adv {
|
||||
uint8_t client[ETH_ALEN];
|
||||
__be16 vid;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct batadv_tvlv_mcast_data - payload of a multicast tvlv
|
||||
* @flags: multicast flags announced by the orig node
|
||||
* @reserved: reserved field
|
||||
*/
|
||||
struct batadv_tvlv_mcast_data {
|
||||
uint8_t flags;
|
||||
uint8_t reserved[3];
|
||||
};
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_PACKET_H_ */
|
1085
net/batman-adv/routing.c
Normal file
1085
net/batman-adv/routing.c
Normal file
File diff suppressed because it is too large
Load diff
51
net/batman-adv/routing.h
Normal file
51
net/batman-adv/routing.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_ROUTING_H_
|
||||
#define _NET_BATMAN_ADV_ROUTING_H_
|
||||
|
||||
bool batadv_check_management_packet(struct sk_buff *skb,
|
||||
struct batadv_hard_iface *hard_iface,
|
||||
int header_len);
|
||||
void batadv_update_route(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node,
|
||||
struct batadv_hard_iface *recv_if,
|
||||
struct batadv_neigh_node *neigh_node);
|
||||
int batadv_recv_icmp_packet(struct sk_buff *skb,
|
||||
struct batadv_hard_iface *recv_if);
|
||||
int batadv_recv_unicast_packet(struct sk_buff *skb,
|
||||
struct batadv_hard_iface *recv_if);
|
||||
int batadv_recv_frag_packet(struct sk_buff *skb,
|
||||
struct batadv_hard_iface *iface);
|
||||
int batadv_recv_bcast_packet(struct sk_buff *skb,
|
||||
struct batadv_hard_iface *recv_if);
|
||||
int batadv_recv_tt_query(struct sk_buff *skb,
|
||||
struct batadv_hard_iface *recv_if);
|
||||
int batadv_recv_roam_adv(struct sk_buff *skb,
|
||||
struct batadv_hard_iface *recv_if);
|
||||
int batadv_recv_unicast_tvlv(struct sk_buff *skb,
|
||||
struct batadv_hard_iface *recv_if);
|
||||
int batadv_recv_unhandled_unicast_packet(struct sk_buff *skb,
|
||||
struct batadv_hard_iface *recv_if);
|
||||
struct batadv_neigh_node *
|
||||
batadv_find_router(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node,
|
||||
struct batadv_hard_iface *recv_if);
|
||||
int batadv_window_protected(struct batadv_priv *bat_priv, int32_t seq_num_diff,
|
||||
unsigned long *last_reset);
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_ROUTING_H_ */
|
645
net/batman-adv/send.c
Normal file
645
net/batman-adv/send.c
Normal file
|
@ -0,0 +1,645 @@
|
|||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "distributed-arp-table.h"
|
||||
#include "send.h"
|
||||
#include "routing.h"
|
||||
#include "translation-table.h"
|
||||
#include "soft-interface.h"
|
||||
#include "hard-interface.h"
|
||||
#include "gateway_common.h"
|
||||
#include "gateway_client.h"
|
||||
#include "originator.h"
|
||||
#include "network-coding.h"
|
||||
#include "fragmentation.h"
|
||||
#include "multicast.h"
|
||||
|
||||
static void batadv_send_outstanding_bcast_packet(struct work_struct *work);
|
||||
|
||||
/* send out an already prepared packet to the given address via the
|
||||
* specified batman interface
|
||||
*/
|
||||
int batadv_send_skb_packet(struct sk_buff *skb,
|
||||
struct batadv_hard_iface *hard_iface,
|
||||
const uint8_t *dst_addr)
|
||||
{
|
||||
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
|
||||
struct ethhdr *ethhdr;
|
||||
|
||||
if (hard_iface->if_status != BATADV_IF_ACTIVE)
|
||||
goto send_skb_err;
|
||||
|
||||
if (unlikely(!hard_iface->net_dev))
|
||||
goto send_skb_err;
|
||||
|
||||
if (!(hard_iface->net_dev->flags & IFF_UP)) {
|
||||
pr_warn("Interface %s is not up - can't send packet via that interface!\n",
|
||||
hard_iface->net_dev->name);
|
||||
goto send_skb_err;
|
||||
}
|
||||
|
||||
/* push to the ethernet header. */
|
||||
if (batadv_skb_head_push(skb, ETH_HLEN) < 0)
|
||||
goto send_skb_err;
|
||||
|
||||
skb_reset_mac_header(skb);
|
||||
|
||||
ethhdr = eth_hdr(skb);
|
||||
ether_addr_copy(ethhdr->h_source, hard_iface->net_dev->dev_addr);
|
||||
ether_addr_copy(ethhdr->h_dest, dst_addr);
|
||||
ethhdr->h_proto = htons(ETH_P_BATMAN);
|
||||
|
||||
skb_set_network_header(skb, ETH_HLEN);
|
||||
skb->protocol = htons(ETH_P_BATMAN);
|
||||
|
||||
skb->dev = hard_iface->net_dev;
|
||||
|
||||
/* Save a clone of the skb to use when decoding coded packets */
|
||||
batadv_nc_skb_store_for_decoding(bat_priv, skb);
|
||||
|
||||
/* dev_queue_xmit() returns a negative result on error. However on
|
||||
* congestion and traffic shaping, it drops and returns NET_XMIT_DROP
|
||||
* (which is > 0). This will not be treated as an error.
|
||||
*/
|
||||
return dev_queue_xmit(skb);
|
||||
send_skb_err:
|
||||
kfree_skb(skb);
|
||||
return NET_XMIT_DROP;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_send_skb_to_orig - Lookup next-hop and transmit skb.
|
||||
* @skb: Packet to be transmitted.
|
||||
* @orig_node: Final destination of the packet.
|
||||
* @recv_if: Interface used when receiving the packet (can be NULL).
|
||||
*
|
||||
* Looks up the best next-hop towards the passed originator and passes the
|
||||
* skb on for preparation of MAC header. If the packet originated from this
|
||||
* host, NULL can be passed as recv_if and no interface alternating is
|
||||
* attempted.
|
||||
*
|
||||
* Returns NET_XMIT_SUCCESS on success, NET_XMIT_DROP on failure, or
|
||||
* NET_XMIT_POLICED if the skb is buffered for later transmit.
|
||||
*/
|
||||
int batadv_send_skb_to_orig(struct sk_buff *skb,
|
||||
struct batadv_orig_node *orig_node,
|
||||
struct batadv_hard_iface *recv_if)
|
||||
{
|
||||
struct batadv_priv *bat_priv = orig_node->bat_priv;
|
||||
struct batadv_neigh_node *neigh_node;
|
||||
int ret = NET_XMIT_DROP;
|
||||
|
||||
/* batadv_find_router() increases neigh_nodes refcount if found. */
|
||||
neigh_node = batadv_find_router(bat_priv, orig_node, recv_if);
|
||||
if (!neigh_node)
|
||||
goto out;
|
||||
|
||||
/* Check if the skb is too large to send in one piece and fragment
|
||||
* it if needed.
|
||||
*/
|
||||
if (atomic_read(&bat_priv->fragmentation) &&
|
||||
skb->len > neigh_node->if_incoming->net_dev->mtu) {
|
||||
/* Fragment and send packet. */
|
||||
if (batadv_frag_send_packet(skb, orig_node, neigh_node))
|
||||
ret = NET_XMIT_SUCCESS;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* try to network code the packet, if it is received on an interface
|
||||
* (i.e. being forwarded). If the packet originates from this node or if
|
||||
* network coding fails, then send the packet as usual.
|
||||
*/
|
||||
if (recv_if && batadv_nc_skb_forward(skb, neigh_node)) {
|
||||
ret = NET_XMIT_POLICED;
|
||||
} else {
|
||||
batadv_send_skb_packet(skb, neigh_node->if_incoming,
|
||||
neigh_node->addr);
|
||||
ret = NET_XMIT_SUCCESS;
|
||||
}
|
||||
|
||||
out:
|
||||
if (neigh_node)
|
||||
batadv_neigh_node_free_ref(neigh_node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_send_skb_push_fill_unicast - extend the buffer and initialize the
|
||||
* common fields for unicast packets
|
||||
* @skb: the skb carrying the unicast header to initialize
|
||||
* @hdr_size: amount of bytes to push at the beginning of the skb
|
||||
* @orig_node: the destination node
|
||||
*
|
||||
* Returns false if the buffer extension was not possible or true otherwise.
|
||||
*/
|
||||
static bool
|
||||
batadv_send_skb_push_fill_unicast(struct sk_buff *skb, int hdr_size,
|
||||
struct batadv_orig_node *orig_node)
|
||||
{
|
||||
struct batadv_unicast_packet *unicast_packet;
|
||||
uint8_t ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn);
|
||||
|
||||
if (batadv_skb_head_push(skb, hdr_size) < 0)
|
||||
return false;
|
||||
|
||||
unicast_packet = (struct batadv_unicast_packet *)skb->data;
|
||||
unicast_packet->version = BATADV_COMPAT_VERSION;
|
||||
/* batman packet type: unicast */
|
||||
unicast_packet->packet_type = BATADV_UNICAST;
|
||||
/* set unicast ttl */
|
||||
unicast_packet->ttl = BATADV_TTL;
|
||||
/* copy the destination for faster routing */
|
||||
ether_addr_copy(unicast_packet->dest, orig_node->orig);
|
||||
/* set the destination tt version number */
|
||||
unicast_packet->ttvn = ttvn;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_send_skb_prepare_unicast - encapsulate an skb with a unicast header
|
||||
* @skb: the skb containing the payload to encapsulate
|
||||
* @orig_node: the destination node
|
||||
*
|
||||
* Returns false if the payload could not be encapsulated or true otherwise.
|
||||
*/
|
||||
static bool batadv_send_skb_prepare_unicast(struct sk_buff *skb,
|
||||
struct batadv_orig_node *orig_node)
|
||||
{
|
||||
size_t uni_size = sizeof(struct batadv_unicast_packet);
|
||||
|
||||
return batadv_send_skb_push_fill_unicast(skb, uni_size, orig_node);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_send_skb_prepare_unicast_4addr - encapsulate an skb with a
|
||||
* unicast 4addr header
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @skb: the skb containing the payload to encapsulate
|
||||
* @orig_node: the destination node
|
||||
* @packet_subtype: the unicast 4addr packet subtype to use
|
||||
*
|
||||
* Returns false if the payload could not be encapsulated or true otherwise.
|
||||
*/
|
||||
bool batadv_send_skb_prepare_unicast_4addr(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb,
|
||||
struct batadv_orig_node *orig,
|
||||
int packet_subtype)
|
||||
{
|
||||
struct batadv_hard_iface *primary_if;
|
||||
struct batadv_unicast_4addr_packet *uc_4addr_packet;
|
||||
bool ret = false;
|
||||
|
||||
primary_if = batadv_primary_if_get_selected(bat_priv);
|
||||
if (!primary_if)
|
||||
goto out;
|
||||
|
||||
/* Pull the header space and fill the unicast_packet substructure.
|
||||
* We can do that because the first member of the uc_4addr_packet
|
||||
* is of type struct unicast_packet
|
||||
*/
|
||||
if (!batadv_send_skb_push_fill_unicast(skb, sizeof(*uc_4addr_packet),
|
||||
orig))
|
||||
goto out;
|
||||
|
||||
uc_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data;
|
||||
uc_4addr_packet->u.packet_type = BATADV_UNICAST_4ADDR;
|
||||
ether_addr_copy(uc_4addr_packet->src, primary_if->net_dev->dev_addr);
|
||||
uc_4addr_packet->subtype = packet_subtype;
|
||||
uc_4addr_packet->reserved = 0;
|
||||
|
||||
ret = true;
|
||||
out:
|
||||
if (primary_if)
|
||||
batadv_hardif_free_ref(primary_if);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_send_skb_unicast - encapsulate and send an skb via unicast
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @skb: payload to send
|
||||
* @packet_type: the batman unicast packet type to use
|
||||
* @packet_subtype: the unicast 4addr packet subtype (only relevant for unicast
|
||||
* 4addr packets)
|
||||
* @orig_node: the originator to send the packet to
|
||||
* @vid: the vid to be used to search the translation table
|
||||
*
|
||||
* Wrap the given skb into a batman-adv unicast or unicast-4addr header
|
||||
* depending on whether BATADV_UNICAST or BATADV_UNICAST_4ADDR was supplied
|
||||
* as packet_type. Then send this frame to the given orig_node and release a
|
||||
* reference to this orig_node.
|
||||
*
|
||||
* Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
|
||||
*/
|
||||
int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb, int packet_type,
|
||||
int packet_subtype,
|
||||
struct batadv_orig_node *orig_node,
|
||||
unsigned short vid)
|
||||
{
|
||||
struct ethhdr *ethhdr;
|
||||
struct batadv_unicast_packet *unicast_packet;
|
||||
int ret = NET_XMIT_DROP;
|
||||
|
||||
if (!orig_node)
|
||||
goto out;
|
||||
|
||||
switch (packet_type) {
|
||||
case BATADV_UNICAST:
|
||||
if (!batadv_send_skb_prepare_unicast(skb, orig_node))
|
||||
goto out;
|
||||
break;
|
||||
case BATADV_UNICAST_4ADDR:
|
||||
if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, skb,
|
||||
orig_node,
|
||||
packet_subtype))
|
||||
goto out;
|
||||
break;
|
||||
default:
|
||||
/* this function supports UNICAST and UNICAST_4ADDR only. It
|
||||
* should never be invoked with any other packet type
|
||||
*/
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* skb->data might have been reallocated by
|
||||
* batadv_send_skb_prepare_unicast{,_4addr}()
|
||||
*/
|
||||
ethhdr = eth_hdr(skb);
|
||||
unicast_packet = (struct batadv_unicast_packet *)skb->data;
|
||||
|
||||
/* inform the destination node that we are still missing a correct route
|
||||
* for this client. The destination will receive this packet and will
|
||||
* try to reroute it because the ttvn contained in the header is less
|
||||
* than the current one
|
||||
*/
|
||||
if (batadv_tt_global_client_is_roaming(bat_priv, ethhdr->h_dest, vid))
|
||||
unicast_packet->ttvn = unicast_packet->ttvn - 1;
|
||||
|
||||
if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
|
||||
ret = NET_XMIT_SUCCESS;
|
||||
|
||||
out:
|
||||
if (orig_node)
|
||||
batadv_orig_node_free_ref(orig_node);
|
||||
if (ret == NET_XMIT_DROP)
|
||||
kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_send_skb_via_tt_generic - send an skb via TT lookup
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @skb: payload to send
|
||||
* @packet_type: the batman unicast packet type to use
|
||||
* @packet_subtype: the unicast 4addr packet subtype (only relevant for unicast
|
||||
* 4addr packets)
|
||||
* @dst_hint: can be used to override the destination contained in the skb
|
||||
* @vid: the vid to be used to search the translation table
|
||||
*
|
||||
* Look up the recipient node for the destination address in the ethernet
|
||||
* header via the translation table. Wrap the given skb into a batman-adv
|
||||
* unicast or unicast-4addr header depending on whether BATADV_UNICAST or
|
||||
* BATADV_UNICAST_4ADDR was supplied as packet_type. Then send this frame
|
||||
* to the according destination node.
|
||||
*
|
||||
* Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
|
||||
*/
|
||||
int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb, int packet_type,
|
||||
int packet_subtype, uint8_t *dst_hint,
|
||||
unsigned short vid)
|
||||
{
|
||||
struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
|
||||
struct batadv_orig_node *orig_node;
|
||||
uint8_t *src, *dst;
|
||||
|
||||
src = ethhdr->h_source;
|
||||
dst = ethhdr->h_dest;
|
||||
|
||||
/* if we got an hint! let's send the packet to this client (if any) */
|
||||
if (dst_hint) {
|
||||
src = NULL;
|
||||
dst = dst_hint;
|
||||
}
|
||||
orig_node = batadv_transtable_search(bat_priv, src, dst, vid);
|
||||
|
||||
return batadv_send_skb_unicast(bat_priv, skb, packet_type,
|
||||
packet_subtype, orig_node, vid);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_send_skb_via_gw - send an skb via gateway lookup
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @skb: payload to send
|
||||
* @vid: the vid to be used to search the translation table
|
||||
*
|
||||
* Look up the currently selected gateway. Wrap the given skb into a batman-adv
|
||||
* unicast header and send this frame to this gateway node.
|
||||
*
|
||||
* Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
|
||||
*/
|
||||
int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb,
|
||||
unsigned short vid)
|
||||
{
|
||||
struct batadv_orig_node *orig_node;
|
||||
|
||||
orig_node = batadv_gw_get_selected_orig(bat_priv);
|
||||
return batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST, 0,
|
||||
orig_node, vid);
|
||||
}
|
||||
|
||||
void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (forw_packet->skb)
|
||||
kfree_skb(forw_packet->skb);
|
||||
if (forw_packet->if_incoming)
|
||||
batadv_hardif_free_ref(forw_packet->if_incoming);
|
||||
if (forw_packet->if_outgoing)
|
||||
batadv_hardif_free_ref(forw_packet->if_outgoing);
|
||||
kfree(forw_packet);
|
||||
}
|
||||
|
||||
static void
|
||||
_batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
|
||||
struct batadv_forw_packet *forw_packet,
|
||||
unsigned long send_time)
|
||||
{
|
||||
/* add new packet to packet list */
|
||||
spin_lock_bh(&bat_priv->forw_bcast_list_lock);
|
||||
hlist_add_head(&forw_packet->list, &bat_priv->forw_bcast_list);
|
||||
spin_unlock_bh(&bat_priv->forw_bcast_list_lock);
|
||||
|
||||
/* start timer for this packet */
|
||||
queue_delayed_work(batadv_event_workqueue, &forw_packet->delayed_work,
|
||||
send_time);
|
||||
}
|
||||
|
||||
/* add a broadcast packet to the queue and setup timers. broadcast packets
|
||||
* are sent multiple times to increase probability for being received.
|
||||
*
|
||||
* This function returns NETDEV_TX_OK on success and NETDEV_TX_BUSY on
|
||||
* errors.
|
||||
*
|
||||
* The skb is not consumed, so the caller should make sure that the
|
||||
* skb is freed.
|
||||
*/
|
||||
int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
|
||||
const struct sk_buff *skb,
|
||||
unsigned long delay)
|
||||
{
|
||||
struct batadv_hard_iface *primary_if = NULL;
|
||||
struct batadv_forw_packet *forw_packet;
|
||||
struct batadv_bcast_packet *bcast_packet;
|
||||
struct sk_buff *newskb;
|
||||
|
||||
if (!batadv_atomic_dec_not_zero(&bat_priv->bcast_queue_left)) {
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"bcast packet queue full\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
primary_if = batadv_primary_if_get_selected(bat_priv);
|
||||
if (!primary_if)
|
||||
goto out_and_inc;
|
||||
|
||||
forw_packet = kmalloc(sizeof(*forw_packet), GFP_ATOMIC);
|
||||
|
||||
if (!forw_packet)
|
||||
goto out_and_inc;
|
||||
|
||||
newskb = skb_copy(skb, GFP_ATOMIC);
|
||||
if (!newskb)
|
||||
goto 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--;
|
||||
|
||||
skb_reset_mac_header(newskb);
|
||||
|
||||
forw_packet->skb = newskb;
|
||||
forw_packet->if_incoming = primary_if;
|
||||
forw_packet->if_outgoing = NULL;
|
||||
|
||||
/* how often did we send the bcast packet ? */
|
||||
forw_packet->num_packets = 0;
|
||||
|
||||
INIT_DELAYED_WORK(&forw_packet->delayed_work,
|
||||
batadv_send_outstanding_bcast_packet);
|
||||
|
||||
_batadv_add_bcast_packet_to_list(bat_priv, forw_packet, delay);
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
packet_free:
|
||||
kfree(forw_packet);
|
||||
out_and_inc:
|
||||
atomic_inc(&bat_priv->bcast_queue_left);
|
||||
out:
|
||||
if (primary_if)
|
||||
batadv_hardif_free_ref(primary_if);
|
||||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
|
||||
static void batadv_send_outstanding_bcast_packet(struct work_struct *work)
|
||||
{
|
||||
struct batadv_hard_iface *hard_iface;
|
||||
struct delayed_work *delayed_work;
|
||||
struct batadv_forw_packet *forw_packet;
|
||||
struct sk_buff *skb1;
|
||||
struct net_device *soft_iface;
|
||||
struct batadv_priv *bat_priv;
|
||||
|
||||
delayed_work = container_of(work, struct 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);
|
||||
|
||||
spin_lock_bh(&bat_priv->forw_bcast_list_lock);
|
||||
hlist_del(&forw_packet->list);
|
||||
spin_unlock_bh(&bat_priv->forw_bcast_list_lock);
|
||||
|
||||
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
|
||||
goto out;
|
||||
|
||||
if (batadv_dat_drop_broadcast_packet(bat_priv, forw_packet))
|
||||
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;
|
||||
|
||||
if (forw_packet->num_packets >= hard_iface->num_bcasts)
|
||||
continue;
|
||||
|
||||
/* send a copy of the saved skb */
|
||||
skb1 = skb_clone(forw_packet->skb, GFP_ATOMIC);
|
||||
if (skb1)
|
||||
batadv_send_skb_packet(skb1, hard_iface,
|
||||
batadv_broadcast_addr);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
forw_packet->num_packets++;
|
||||
|
||||
/* if we still have some more bcasts to send */
|
||||
if (forw_packet->num_packets < BATADV_NUM_BCASTS_MAX) {
|
||||
_batadv_add_bcast_packet_to_list(bat_priv, forw_packet,
|
||||
msecs_to_jiffies(5));
|
||||
return;
|
||||
}
|
||||
|
||||
out:
|
||||
batadv_forw_packet_free(forw_packet);
|
||||
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 = container_of(work, struct 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
|
||||
batadv_purge_outstanding_packets(struct batadv_priv *bat_priv,
|
||||
const struct batadv_hard_iface *hard_iface)
|
||||
{
|
||||
struct batadv_forw_packet *forw_packet;
|
||||
struct hlist_node *safe_tmp_node;
|
||||
bool pending;
|
||||
|
||||
if (hard_iface)
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"purge_outstanding_packets(): %s\n",
|
||||
hard_iface->net_dev->name);
|
||||
else
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"purge_outstanding_packets()\n");
|
||||
|
||||
/* free bcast list */
|
||||
spin_lock_bh(&bat_priv->forw_bcast_list_lock);
|
||||
hlist_for_each_entry_safe(forw_packet, safe_tmp_node,
|
||||
&bat_priv->forw_bcast_list, list) {
|
||||
/* if purge_outstanding_packets() was called with an argument
|
||||
* we delete only packets belonging to the given interface
|
||||
*/
|
||||
if ((hard_iface) &&
|
||||
(forw_packet->if_incoming != hard_iface))
|
||||
continue;
|
||||
|
||||
spin_unlock_bh(&bat_priv->forw_bcast_list_lock);
|
||||
|
||||
/* batadv_send_outstanding_bcast_packet() will lock the list to
|
||||
* delete the item from the list
|
||||
*/
|
||||
pending = cancel_delayed_work_sync(&forw_packet->delayed_work);
|
||||
spin_lock_bh(&bat_priv->forw_bcast_list_lock);
|
||||
|
||||
if (pending) {
|
||||
hlist_del(&forw_packet->list);
|
||||
batadv_forw_packet_free(forw_packet);
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&bat_priv->forw_bcast_list_lock);
|
||||
|
||||
/* free batman packet list */
|
||||
spin_lock_bh(&bat_priv->forw_bat_list_lock);
|
||||
hlist_for_each_entry_safe(forw_packet, safe_tmp_node,
|
||||
&bat_priv->forw_bat_list, list) {
|
||||
/* if purge_outstanding_packets() was called with an argument
|
||||
* we delete only packets belonging to the given interface
|
||||
*/
|
||||
if ((hard_iface) &&
|
||||
(forw_packet->if_incoming != hard_iface) &&
|
||||
(forw_packet->if_outgoing != hard_iface))
|
||||
continue;
|
||||
|
||||
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
|
||||
|
||||
/* send_outstanding_bat_packet() will lock the list to
|
||||
* delete the item from the list
|
||||
*/
|
||||
pending = cancel_delayed_work_sync(&forw_packet->delayed_work);
|
||||
spin_lock_bh(&bat_priv->forw_bat_list_lock);
|
||||
|
||||
if (pending) {
|
||||
hlist_del(&forw_packet->list);
|
||||
batadv_forw_packet_free(forw_packet);
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
|
||||
}
|
98
net/batman-adv/send.h
Normal file
98
net/batman-adv/send.h
Normal file
|
@ -0,0 +1,98 @@
|
|||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_SEND_H_
|
||||
#define _NET_BATMAN_ADV_SEND_H_
|
||||
|
||||
int batadv_send_skb_packet(struct sk_buff *skb,
|
||||
struct batadv_hard_iface *hard_iface,
|
||||
const uint8_t *dst_addr);
|
||||
int batadv_send_skb_to_orig(struct sk_buff *skb,
|
||||
struct batadv_orig_node *orig_node,
|
||||
struct batadv_hard_iface *recv_if);
|
||||
void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface);
|
||||
int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
|
||||
const struct sk_buff *skb,
|
||||
unsigned long delay);
|
||||
void batadv_send_outstanding_bat_ogm_packet(struct work_struct *work);
|
||||
void
|
||||
batadv_purge_outstanding_packets(struct batadv_priv *bat_priv,
|
||||
const struct batadv_hard_iface *hard_iface);
|
||||
bool batadv_send_skb_prepare_unicast_4addr(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb,
|
||||
struct batadv_orig_node *orig_node,
|
||||
int packet_subtype);
|
||||
int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb, int packet_type,
|
||||
int packet_subtype,
|
||||
struct batadv_orig_node *orig_node,
|
||||
unsigned short vid);
|
||||
int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb, int packet_type,
|
||||
int packet_subtype, uint8_t *dst_hint,
|
||||
unsigned short vid);
|
||||
int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb,
|
||||
unsigned short vid);
|
||||
|
||||
/**
|
||||
* batadv_send_skb_via_tt - send an skb via TT lookup
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @skb: the payload to send
|
||||
* @dst_hint: can be used to override the destination contained in the skb
|
||||
* @vid: the vid to be used to search the translation table
|
||||
*
|
||||
* Look up the recipient node for the destination address in the ethernet
|
||||
* header via the translation table. Wrap the given skb into a batman-adv
|
||||
* unicast header. Then send this frame to the according destination node.
|
||||
*
|
||||
* Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
|
||||
*/
|
||||
static inline int batadv_send_skb_via_tt(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb, uint8_t *dst_hint,
|
||||
unsigned short vid)
|
||||
{
|
||||
return batadv_send_skb_via_tt_generic(bat_priv, skb, BATADV_UNICAST, 0,
|
||||
dst_hint, vid);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_send_skb_via_tt_4addr - send an skb via TT lookup
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @skb: the payload to send
|
||||
* @packet_subtype: the unicast 4addr packet subtype to use
|
||||
* @dst_hint: can be used to override the destination contained in the skb
|
||||
* @vid: the vid to be used to search the translation table
|
||||
*
|
||||
* Look up the recipient node for the destination address in the ethernet
|
||||
* header via the translation table. Wrap the given skb into a batman-adv
|
||||
* unicast-4addr header. Then send this frame to the according destination
|
||||
* node.
|
||||
*
|
||||
* Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
|
||||
*/
|
||||
static inline int batadv_send_skb_via_tt_4addr(struct batadv_priv *bat_priv,
|
||||
struct sk_buff *skb,
|
||||
int packet_subtype,
|
||||
uint8_t *dst_hint,
|
||||
unsigned short vid)
|
||||
{
|
||||
return batadv_send_skb_via_tt_generic(bat_priv, skb,
|
||||
BATADV_UNICAST_4ADDR,
|
||||
packet_subtype, dst_hint, vid);
|
||||
}
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_SEND_H_ */
|
1108
net/batman-adv/soft-interface.c
Normal file
1108
net/batman-adv/soft-interface.c
Normal file
File diff suppressed because it is too large
Load diff
34
net/batman-adv/soft-interface.h
Normal file
34
net/batman-adv/soft-interface.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_SOFT_INTERFACE_H_
|
||||
#define _NET_BATMAN_ADV_SOFT_INTERFACE_H_
|
||||
|
||||
int batadv_skb_head_push(struct sk_buff *skb, unsigned int len);
|
||||
void batadv_interface_rx(struct net_device *soft_iface,
|
||||
struct sk_buff *skb, struct batadv_hard_iface *recv_if,
|
||||
int hdr_size, struct batadv_orig_node *orig_node);
|
||||
struct net_device *batadv_softif_create(const char *name);
|
||||
void batadv_softif_destroy_sysfs(struct net_device *soft_iface);
|
||||
int batadv_softif_is_valid(const struct net_device *net_dev);
|
||||
extern struct rtnl_link_ops batadv_link_ops;
|
||||
int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid);
|
||||
void batadv_softif_vlan_free_ref(struct batadv_softif_vlan *softif_vlan);
|
||||
struct batadv_softif_vlan *batadv_softif_vlan_get(struct batadv_priv *bat_priv,
|
||||
unsigned short vid);
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_SOFT_INTERFACE_H_ */
|
936
net/batman-adv/sysfs.c
Normal file
936
net/batman-adv/sysfs.c
Normal file
|
@ -0,0 +1,936 @@
|
|||
/* Copyright (C) 2010-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "sysfs.h"
|
||||
#include "translation-table.h"
|
||||
#include "distributed-arp-table.h"
|
||||
#include "network-coding.h"
|
||||
#include "originator.h"
|
||||
#include "hard-interface.h"
|
||||
#include "soft-interface.h"
|
||||
#include "gateway_common.h"
|
||||
#include "gateway_client.h"
|
||||
|
||||
static struct net_device *batadv_kobj_to_netdev(struct kobject *obj)
|
||||
{
|
||||
struct device *dev = container_of(obj->parent, struct device, kobj);
|
||||
|
||||
return to_net_dev(dev);
|
||||
}
|
||||
|
||||
static struct batadv_priv *batadv_kobj_to_batpriv(struct kobject *obj)
|
||||
{
|
||||
struct net_device *net_dev = batadv_kobj_to_netdev(obj);
|
||||
|
||||
return netdev_priv(net_dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_vlan_kobj_to_batpriv - convert a vlan kobj in the associated batpriv
|
||||
* @obj: kobject to covert
|
||||
*
|
||||
* Returns the associated batadv_priv struct.
|
||||
*/
|
||||
static struct batadv_priv *batadv_vlan_kobj_to_batpriv(struct kobject *obj)
|
||||
{
|
||||
/* VLAN specific attributes are located in the root sysfs folder if they
|
||||
* refer to the untagged VLAN..
|
||||
*/
|
||||
if (!strcmp(BATADV_SYSFS_IF_MESH_SUBDIR, obj->name))
|
||||
return batadv_kobj_to_batpriv(obj);
|
||||
|
||||
/* ..while the attributes for the tagged vlans are located in
|
||||
* the in the corresponding "vlan%VID" subfolder
|
||||
*/
|
||||
return batadv_kobj_to_batpriv(obj->parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_kobj_to_vlan - convert a kobj in the associated softif_vlan struct
|
||||
* @obj: kobject to covert
|
||||
*
|
||||
* Returns the associated softif_vlan struct if found, NULL otherwise.
|
||||
*/
|
||||
static struct batadv_softif_vlan *
|
||||
batadv_kobj_to_vlan(struct batadv_priv *bat_priv, struct kobject *obj)
|
||||
{
|
||||
struct batadv_softif_vlan *vlan_tmp, *vlan = NULL;
|
||||
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(vlan_tmp, &bat_priv->softif_vlan_list, list) {
|
||||
if (vlan_tmp->kobj != obj)
|
||||
continue;
|
||||
|
||||
if (!atomic_inc_not_zero(&vlan_tmp->refcount))
|
||||
continue;
|
||||
|
||||
vlan = vlan_tmp;
|
||||
break;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return vlan;
|
||||
}
|
||||
|
||||
#define BATADV_UEV_TYPE_VAR "BATTYPE="
|
||||
#define BATADV_UEV_ACTION_VAR "BATACTION="
|
||||
#define BATADV_UEV_DATA_VAR "BATDATA="
|
||||
|
||||
static char *batadv_uev_action_str[] = {
|
||||
"add",
|
||||
"del",
|
||||
"change"
|
||||
};
|
||||
|
||||
static char *batadv_uev_type_str[] = {
|
||||
"gw"
|
||||
};
|
||||
|
||||
/* Use this, if you have customized show and store functions for vlan attrs */
|
||||
#define BATADV_ATTR_VLAN(_name, _mode, _show, _store) \
|
||||
struct batadv_attribute batadv_attr_vlan_##_name = { \
|
||||
.attr = {.name = __stringify(_name), \
|
||||
.mode = _mode }, \
|
||||
.show = _show, \
|
||||
.store = _store, \
|
||||
}
|
||||
|
||||
/* Use this, if you have customized show and store functions */
|
||||
#define BATADV_ATTR(_name, _mode, _show, _store) \
|
||||
struct batadv_attribute batadv_attr_##_name = { \
|
||||
.attr = {.name = __stringify(_name), \
|
||||
.mode = _mode }, \
|
||||
.show = _show, \
|
||||
.store = _store, \
|
||||
}
|
||||
|
||||
#define BATADV_ATTR_SIF_STORE_BOOL(_name, _post_func) \
|
||||
ssize_t batadv_store_##_name(struct kobject *kobj, \
|
||||
struct attribute *attr, char *buff, \
|
||||
size_t count) \
|
||||
{ \
|
||||
struct net_device *net_dev = batadv_kobj_to_netdev(kobj); \
|
||||
struct batadv_priv *bat_priv = netdev_priv(net_dev); \
|
||||
\
|
||||
return __batadv_store_bool_attr(buff, count, _post_func, attr, \
|
||||
&bat_priv->_name, net_dev); \
|
||||
}
|
||||
|
||||
#define BATADV_ATTR_SIF_SHOW_BOOL(_name) \
|
||||
ssize_t batadv_show_##_name(struct kobject *kobj, \
|
||||
struct attribute *attr, char *buff) \
|
||||
{ \
|
||||
struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); \
|
||||
\
|
||||
return sprintf(buff, "%s\n", \
|
||||
atomic_read(&bat_priv->_name) == 0 ? \
|
||||
"disabled" : "enabled"); \
|
||||
} \
|
||||
|
||||
/* Use this, if you are going to turn a [name] in the soft-interface
|
||||
* (bat_priv) on or off
|
||||
*/
|
||||
#define BATADV_ATTR_SIF_BOOL(_name, _mode, _post_func) \
|
||||
static BATADV_ATTR_SIF_STORE_BOOL(_name, _post_func) \
|
||||
static BATADV_ATTR_SIF_SHOW_BOOL(_name) \
|
||||
static BATADV_ATTR(_name, _mode, batadv_show_##_name, \
|
||||
batadv_store_##_name)
|
||||
|
||||
|
||||
#define BATADV_ATTR_SIF_STORE_UINT(_name, _min, _max, _post_func) \
|
||||
ssize_t batadv_store_##_name(struct kobject *kobj, \
|
||||
struct attribute *attr, char *buff, \
|
||||
size_t count) \
|
||||
{ \
|
||||
struct net_device *net_dev = batadv_kobj_to_netdev(kobj); \
|
||||
struct batadv_priv *bat_priv = netdev_priv(net_dev); \
|
||||
\
|
||||
return __batadv_store_uint_attr(buff, count, _min, _max, \
|
||||
_post_func, attr, \
|
||||
&bat_priv->_name, net_dev); \
|
||||
}
|
||||
|
||||
#define BATADV_ATTR_SIF_SHOW_UINT(_name) \
|
||||
ssize_t batadv_show_##_name(struct kobject *kobj, \
|
||||
struct attribute *attr, char *buff) \
|
||||
{ \
|
||||
struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); \
|
||||
\
|
||||
return sprintf(buff, "%i\n", atomic_read(&bat_priv->_name)); \
|
||||
} \
|
||||
|
||||
/* Use this, if you are going to set [name] in the soft-interface
|
||||
* (bat_priv) to an unsigned integer value
|
||||
*/
|
||||
#define BATADV_ATTR_SIF_UINT(_name, _mode, _min, _max, _post_func) \
|
||||
static BATADV_ATTR_SIF_STORE_UINT(_name, _min, _max, _post_func)\
|
||||
static BATADV_ATTR_SIF_SHOW_UINT(_name) \
|
||||
static BATADV_ATTR(_name, _mode, batadv_show_##_name, \
|
||||
batadv_store_##_name)
|
||||
|
||||
#define BATADV_ATTR_VLAN_STORE_BOOL(_name, _post_func) \
|
||||
ssize_t batadv_store_vlan_##_name(struct kobject *kobj, \
|
||||
struct attribute *attr, char *buff, \
|
||||
size_t count) \
|
||||
{ \
|
||||
struct batadv_priv *bat_priv = batadv_vlan_kobj_to_batpriv(kobj);\
|
||||
struct batadv_softif_vlan *vlan = batadv_kobj_to_vlan(bat_priv, \
|
||||
kobj); \
|
||||
size_t res = __batadv_store_bool_attr(buff, count, _post_func, \
|
||||
attr, &vlan->_name, \
|
||||
bat_priv->soft_iface); \
|
||||
\
|
||||
batadv_softif_vlan_free_ref(vlan); \
|
||||
return res; \
|
||||
}
|
||||
|
||||
#define BATADV_ATTR_VLAN_SHOW_BOOL(_name) \
|
||||
ssize_t batadv_show_vlan_##_name(struct kobject *kobj, \
|
||||
struct attribute *attr, char *buff) \
|
||||
{ \
|
||||
struct batadv_priv *bat_priv = batadv_vlan_kobj_to_batpriv(kobj);\
|
||||
struct batadv_softif_vlan *vlan = batadv_kobj_to_vlan(bat_priv, \
|
||||
kobj); \
|
||||
size_t res = sprintf(buff, "%s\n", \
|
||||
atomic_read(&vlan->_name) == 0 ? \
|
||||
"disabled" : "enabled"); \
|
||||
\
|
||||
batadv_softif_vlan_free_ref(vlan); \
|
||||
return res; \
|
||||
}
|
||||
|
||||
/* Use this, if you are going to turn a [name] in the vlan struct on or off */
|
||||
#define BATADV_ATTR_VLAN_BOOL(_name, _mode, _post_func) \
|
||||
static BATADV_ATTR_VLAN_STORE_BOOL(_name, _post_func) \
|
||||
static BATADV_ATTR_VLAN_SHOW_BOOL(_name) \
|
||||
static BATADV_ATTR_VLAN(_name, _mode, batadv_show_vlan_##_name, \
|
||||
batadv_store_vlan_##_name)
|
||||
|
||||
static int batadv_store_bool_attr(char *buff, size_t count,
|
||||
struct net_device *net_dev,
|
||||
const char *attr_name, atomic_t *attr)
|
||||
{
|
||||
int enabled = -1;
|
||||
|
||||
if (buff[count - 1] == '\n')
|
||||
buff[count - 1] = '\0';
|
||||
|
||||
if ((strncmp(buff, "1", 2) == 0) ||
|
||||
(strncmp(buff, "enable", 7) == 0) ||
|
||||
(strncmp(buff, "enabled", 8) == 0))
|
||||
enabled = 1;
|
||||
|
||||
if ((strncmp(buff, "0", 2) == 0) ||
|
||||
(strncmp(buff, "disable", 8) == 0) ||
|
||||
(strncmp(buff, "disabled", 9) == 0))
|
||||
enabled = 0;
|
||||
|
||||
if (enabled < 0) {
|
||||
batadv_info(net_dev, "%s: Invalid parameter received: %s\n",
|
||||
attr_name, buff);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (atomic_read(attr) == enabled)
|
||||
return count;
|
||||
|
||||
batadv_info(net_dev, "%s: Changing from: %s to: %s\n", attr_name,
|
||||
atomic_read(attr) == 1 ? "enabled" : "disabled",
|
||||
enabled == 1 ? "enabled" : "disabled");
|
||||
|
||||
atomic_set(attr, (unsigned int)enabled);
|
||||
return count;
|
||||
}
|
||||
|
||||
static inline ssize_t
|
||||
__batadv_store_bool_attr(char *buff, size_t count,
|
||||
void (*post_func)(struct net_device *),
|
||||
struct attribute *attr,
|
||||
atomic_t *attr_store, struct net_device *net_dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = batadv_store_bool_attr(buff, count, net_dev, attr->name,
|
||||
attr_store);
|
||||
if (post_func && ret)
|
||||
post_func(net_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int batadv_store_uint_attr(const char *buff, size_t count,
|
||||
struct net_device *net_dev,
|
||||
const char *attr_name,
|
||||
unsigned int min, unsigned int max,
|
||||
atomic_t *attr)
|
||||
{
|
||||
unsigned long uint_val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buff, 10, &uint_val);
|
||||
if (ret) {
|
||||
batadv_info(net_dev, "%s: Invalid parameter received: %s\n",
|
||||
attr_name, buff);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (uint_val < min) {
|
||||
batadv_info(net_dev, "%s: Value is too small: %lu min: %u\n",
|
||||
attr_name, uint_val, min);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (uint_val > max) {
|
||||
batadv_info(net_dev, "%s: Value is too big: %lu max: %u\n",
|
||||
attr_name, uint_val, max);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (atomic_read(attr) == uint_val)
|
||||
return count;
|
||||
|
||||
batadv_info(net_dev, "%s: Changing from: %i to: %lu\n",
|
||||
attr_name, atomic_read(attr), uint_val);
|
||||
|
||||
atomic_set(attr, uint_val);
|
||||
return count;
|
||||
}
|
||||
|
||||
static inline ssize_t
|
||||
__batadv_store_uint_attr(const char *buff, size_t count,
|
||||
int min, int max,
|
||||
void (*post_func)(struct net_device *),
|
||||
const struct attribute *attr,
|
||||
atomic_t *attr_store, struct net_device *net_dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = batadv_store_uint_attr(buff, count, net_dev, attr->name, min, max,
|
||||
attr_store);
|
||||
if (post_func && ret)
|
||||
post_func(net_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t batadv_show_bat_algo(struct kobject *kobj,
|
||||
struct attribute *attr, char *buff)
|
||||
{
|
||||
struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
|
||||
|
||||
return sprintf(buff, "%s\n", bat_priv->bat_algo_ops->name);
|
||||
}
|
||||
|
||||
static void batadv_post_gw_reselect(struct net_device *net_dev)
|
||||
{
|
||||
struct batadv_priv *bat_priv = netdev_priv(net_dev);
|
||||
|
||||
batadv_gw_reselect(bat_priv);
|
||||
}
|
||||
|
||||
static ssize_t batadv_show_gw_mode(struct kobject *kobj, struct attribute *attr,
|
||||
char *buff)
|
||||
{
|
||||
struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
|
||||
int bytes_written;
|
||||
|
||||
switch (atomic_read(&bat_priv->gw_mode)) {
|
||||
case BATADV_GW_MODE_CLIENT:
|
||||
bytes_written = sprintf(buff, "%s\n",
|
||||
BATADV_GW_MODE_CLIENT_NAME);
|
||||
break;
|
||||
case BATADV_GW_MODE_SERVER:
|
||||
bytes_written = sprintf(buff, "%s\n",
|
||||
BATADV_GW_MODE_SERVER_NAME);
|
||||
break;
|
||||
default:
|
||||
bytes_written = sprintf(buff, "%s\n",
|
||||
BATADV_GW_MODE_OFF_NAME);
|
||||
break;
|
||||
}
|
||||
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
static ssize_t batadv_store_gw_mode(struct kobject *kobj,
|
||||
struct attribute *attr, char *buff,
|
||||
size_t count)
|
||||
{
|
||||
struct net_device *net_dev = batadv_kobj_to_netdev(kobj);
|
||||
struct batadv_priv *bat_priv = netdev_priv(net_dev);
|
||||
char *curr_gw_mode_str;
|
||||
int gw_mode_tmp = -1;
|
||||
|
||||
if (buff[count - 1] == '\n')
|
||||
buff[count - 1] = '\0';
|
||||
|
||||
if (strncmp(buff, BATADV_GW_MODE_OFF_NAME,
|
||||
strlen(BATADV_GW_MODE_OFF_NAME)) == 0)
|
||||
gw_mode_tmp = BATADV_GW_MODE_OFF;
|
||||
|
||||
if (strncmp(buff, BATADV_GW_MODE_CLIENT_NAME,
|
||||
strlen(BATADV_GW_MODE_CLIENT_NAME)) == 0)
|
||||
gw_mode_tmp = BATADV_GW_MODE_CLIENT;
|
||||
|
||||
if (strncmp(buff, BATADV_GW_MODE_SERVER_NAME,
|
||||
strlen(BATADV_GW_MODE_SERVER_NAME)) == 0)
|
||||
gw_mode_tmp = BATADV_GW_MODE_SERVER;
|
||||
|
||||
if (gw_mode_tmp < 0) {
|
||||
batadv_info(net_dev,
|
||||
"Invalid parameter for 'gw mode' setting received: %s\n",
|
||||
buff);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (atomic_read(&bat_priv->gw_mode) == gw_mode_tmp)
|
||||
return count;
|
||||
|
||||
switch (atomic_read(&bat_priv->gw_mode)) {
|
||||
case BATADV_GW_MODE_CLIENT:
|
||||
curr_gw_mode_str = BATADV_GW_MODE_CLIENT_NAME;
|
||||
break;
|
||||
case BATADV_GW_MODE_SERVER:
|
||||
curr_gw_mode_str = BATADV_GW_MODE_SERVER_NAME;
|
||||
break;
|
||||
default:
|
||||
curr_gw_mode_str = BATADV_GW_MODE_OFF_NAME;
|
||||
break;
|
||||
}
|
||||
|
||||
batadv_info(net_dev, "Changing gw mode from: %s to: %s\n",
|
||||
curr_gw_mode_str, buff);
|
||||
|
||||
/* Invoking batadv_gw_reselect() is not enough to really de-select the
|
||||
* current GW. It will only instruct the gateway client code to perform
|
||||
* a re-election the next time that this is needed.
|
||||
*
|
||||
* When gw client mode is being switched off the current GW must be
|
||||
* de-selected explicitly otherwise no GW_ADD uevent is thrown on
|
||||
* client mode re-activation. This is operation is performed in
|
||||
* batadv_gw_check_client_stop().
|
||||
*/
|
||||
batadv_gw_reselect(bat_priv);
|
||||
/* always call batadv_gw_check_client_stop() before changing the gateway
|
||||
* state
|
||||
*/
|
||||
batadv_gw_check_client_stop(bat_priv);
|
||||
atomic_set(&bat_priv->gw_mode, (unsigned int)gw_mode_tmp);
|
||||
batadv_gw_tvlv_container_update(bat_priv);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t batadv_show_gw_bwidth(struct kobject *kobj,
|
||||
struct attribute *attr, char *buff)
|
||||
{
|
||||
struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
|
||||
uint32_t down, up;
|
||||
|
||||
down = atomic_read(&bat_priv->gw.bandwidth_down);
|
||||
up = atomic_read(&bat_priv->gw.bandwidth_up);
|
||||
|
||||
return sprintf(buff, "%u.%u/%u.%u MBit\n", down / 10,
|
||||
down % 10, up / 10, up % 10);
|
||||
}
|
||||
|
||||
static ssize_t batadv_store_gw_bwidth(struct kobject *kobj,
|
||||
struct attribute *attr, char *buff,
|
||||
size_t count)
|
||||
{
|
||||
struct net_device *net_dev = batadv_kobj_to_netdev(kobj);
|
||||
|
||||
if (buff[count - 1] == '\n')
|
||||
buff[count - 1] = '\0';
|
||||
|
||||
return batadv_gw_bandwidth_set(net_dev, buff, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_show_isolation_mark - print the current isolation mark/mask
|
||||
* @kobj: kobject representing the private mesh sysfs directory
|
||||
* @attr: the batman-adv attribute the user is interacting with
|
||||
* @buff: the buffer that will contain the data to send back to the user
|
||||
*
|
||||
* Returns the number of bytes written into 'buff' on success or a negative
|
||||
* error code in case of failure
|
||||
*/
|
||||
static ssize_t batadv_show_isolation_mark(struct kobject *kobj,
|
||||
struct attribute *attr, char *buff)
|
||||
{
|
||||
struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
|
||||
|
||||
return sprintf(buff, "%#.8x/%#.8x\n", bat_priv->isolation_mark,
|
||||
bat_priv->isolation_mark_mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_store_isolation_mark - parse and store the isolation mark/mask entered
|
||||
* by the user
|
||||
* @kobj: kobject representing the private mesh sysfs directory
|
||||
* @attr: the batman-adv attribute the user is interacting with
|
||||
* @buff: the buffer containing the user data
|
||||
* @count: number of bytes in the buffer
|
||||
*
|
||||
* Returns 'count' on success or a negative error code in case of failure
|
||||
*/
|
||||
static ssize_t batadv_store_isolation_mark(struct kobject *kobj,
|
||||
struct attribute *attr, char *buff,
|
||||
size_t count)
|
||||
{
|
||||
struct net_device *net_dev = batadv_kobj_to_netdev(kobj);
|
||||
struct batadv_priv *bat_priv = netdev_priv(net_dev);
|
||||
uint32_t mark, mask;
|
||||
char *mask_ptr;
|
||||
|
||||
/* parse the mask if it has been specified, otherwise assume the mask is
|
||||
* the biggest possible
|
||||
*/
|
||||
mask = 0xFFFFFFFF;
|
||||
mask_ptr = strchr(buff, '/');
|
||||
if (mask_ptr) {
|
||||
*mask_ptr = '\0';
|
||||
mask_ptr++;
|
||||
|
||||
/* the mask must be entered in hex base as it is going to be a
|
||||
* bitmask and not a prefix length
|
||||
*/
|
||||
if (kstrtou32(mask_ptr, 16, &mask) < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* the mark can be entered in any base */
|
||||
if (kstrtou32(buff, 0, &mark) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
bat_priv->isolation_mark_mask = mask;
|
||||
/* erase bits not covered by the mask */
|
||||
bat_priv->isolation_mark = mark & bat_priv->isolation_mark_mask;
|
||||
|
||||
batadv_info(net_dev,
|
||||
"New skb mark for extended isolation: %#.8x/%#.8x\n",
|
||||
bat_priv->isolation_mark, bat_priv->isolation_mark_mask);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
BATADV_ATTR_SIF_BOOL(aggregated_ogms, S_IRUGO | S_IWUSR, NULL);
|
||||
BATADV_ATTR_SIF_BOOL(bonding, S_IRUGO | S_IWUSR, NULL);
|
||||
#ifdef CONFIG_BATMAN_ADV_BLA
|
||||
BATADV_ATTR_SIF_BOOL(bridge_loop_avoidance, S_IRUGO | S_IWUSR, NULL);
|
||||
#endif
|
||||
#ifdef CONFIG_BATMAN_ADV_DAT
|
||||
BATADV_ATTR_SIF_BOOL(distributed_arp_table, S_IRUGO | S_IWUSR,
|
||||
batadv_dat_status_update);
|
||||
#endif
|
||||
BATADV_ATTR_SIF_BOOL(fragmentation, S_IRUGO | S_IWUSR, batadv_update_min_mtu);
|
||||
static BATADV_ATTR(routing_algo, S_IRUGO, batadv_show_bat_algo, NULL);
|
||||
static BATADV_ATTR(gw_mode, S_IRUGO | S_IWUSR, batadv_show_gw_mode,
|
||||
batadv_store_gw_mode);
|
||||
BATADV_ATTR_SIF_UINT(orig_interval, S_IRUGO | S_IWUSR, 2 * BATADV_JITTER,
|
||||
INT_MAX, NULL);
|
||||
BATADV_ATTR_SIF_UINT(hop_penalty, S_IRUGO | S_IWUSR, 0, BATADV_TQ_MAX_VALUE,
|
||||
NULL);
|
||||
BATADV_ATTR_SIF_UINT(gw_sel_class, S_IRUGO | S_IWUSR, 1, BATADV_TQ_MAX_VALUE,
|
||||
batadv_post_gw_reselect);
|
||||
static BATADV_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, batadv_show_gw_bwidth,
|
||||
batadv_store_gw_bwidth);
|
||||
#ifdef CONFIG_BATMAN_ADV_MCAST
|
||||
BATADV_ATTR_SIF_BOOL(multicast_mode, S_IRUGO | S_IWUSR, NULL);
|
||||
#endif
|
||||
#ifdef CONFIG_BATMAN_ADV_DEBUG
|
||||
BATADV_ATTR_SIF_UINT(log_level, S_IRUGO | S_IWUSR, 0, BATADV_DBG_ALL, NULL);
|
||||
#endif
|
||||
#ifdef CONFIG_BATMAN_ADV_NC
|
||||
BATADV_ATTR_SIF_BOOL(network_coding, S_IRUGO | S_IWUSR,
|
||||
batadv_nc_status_update);
|
||||
#endif
|
||||
static BATADV_ATTR(isolation_mark, S_IRUGO | S_IWUSR,
|
||||
batadv_show_isolation_mark, batadv_store_isolation_mark);
|
||||
|
||||
static struct batadv_attribute *batadv_mesh_attrs[] = {
|
||||
&batadv_attr_aggregated_ogms,
|
||||
&batadv_attr_bonding,
|
||||
#ifdef CONFIG_BATMAN_ADV_BLA
|
||||
&batadv_attr_bridge_loop_avoidance,
|
||||
#endif
|
||||
#ifdef CONFIG_BATMAN_ADV_DAT
|
||||
&batadv_attr_distributed_arp_table,
|
||||
#endif
|
||||
#ifdef CONFIG_BATMAN_ADV_MCAST
|
||||
&batadv_attr_multicast_mode,
|
||||
#endif
|
||||
&batadv_attr_fragmentation,
|
||||
&batadv_attr_routing_algo,
|
||||
&batadv_attr_gw_mode,
|
||||
&batadv_attr_orig_interval,
|
||||
&batadv_attr_hop_penalty,
|
||||
&batadv_attr_gw_sel_class,
|
||||
&batadv_attr_gw_bandwidth,
|
||||
#ifdef CONFIG_BATMAN_ADV_DEBUG
|
||||
&batadv_attr_log_level,
|
||||
#endif
|
||||
#ifdef CONFIG_BATMAN_ADV_NC
|
||||
&batadv_attr_network_coding,
|
||||
#endif
|
||||
&batadv_attr_isolation_mark,
|
||||
NULL,
|
||||
};
|
||||
|
||||
BATADV_ATTR_VLAN_BOOL(ap_isolation, S_IRUGO | S_IWUSR, NULL);
|
||||
|
||||
/**
|
||||
* batadv_vlan_attrs - array of vlan specific sysfs attributes
|
||||
*/
|
||||
static struct batadv_attribute *batadv_vlan_attrs[] = {
|
||||
&batadv_attr_vlan_ap_isolation,
|
||||
NULL,
|
||||
};
|
||||
|
||||
int batadv_sysfs_add_meshif(struct net_device *dev)
|
||||
{
|
||||
struct kobject *batif_kobject = &dev->dev.kobj;
|
||||
struct batadv_priv *bat_priv = netdev_priv(dev);
|
||||
struct batadv_attribute **bat_attr;
|
||||
int err;
|
||||
|
||||
bat_priv->mesh_obj = kobject_create_and_add(BATADV_SYSFS_IF_MESH_SUBDIR,
|
||||
batif_kobject);
|
||||
if (!bat_priv->mesh_obj) {
|
||||
batadv_err(dev, "Can't add sysfs directory: %s/%s\n", dev->name,
|
||||
BATADV_SYSFS_IF_MESH_SUBDIR);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (bat_attr = batadv_mesh_attrs; *bat_attr; ++bat_attr) {
|
||||
err = sysfs_create_file(bat_priv->mesh_obj,
|
||||
&((*bat_attr)->attr));
|
||||
if (err) {
|
||||
batadv_err(dev, "Can't add sysfs file: %s/%s/%s\n",
|
||||
dev->name, BATADV_SYSFS_IF_MESH_SUBDIR,
|
||||
((*bat_attr)->attr).name);
|
||||
goto rem_attr;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
rem_attr:
|
||||
for (bat_attr = batadv_mesh_attrs; *bat_attr; ++bat_attr)
|
||||
sysfs_remove_file(bat_priv->mesh_obj, &((*bat_attr)->attr));
|
||||
|
||||
kobject_put(bat_priv->mesh_obj);
|
||||
bat_priv->mesh_obj = NULL;
|
||||
out:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void batadv_sysfs_del_meshif(struct net_device *dev)
|
||||
{
|
||||
struct batadv_priv *bat_priv = netdev_priv(dev);
|
||||
struct batadv_attribute **bat_attr;
|
||||
|
||||
for (bat_attr = batadv_mesh_attrs; *bat_attr; ++bat_attr)
|
||||
sysfs_remove_file(bat_priv->mesh_obj, &((*bat_attr)->attr));
|
||||
|
||||
kobject_put(bat_priv->mesh_obj);
|
||||
bat_priv->mesh_obj = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_sysfs_add_vlan - add all the needed sysfs objects for the new vlan
|
||||
* @dev: netdev of the mesh interface
|
||||
* @vlan: private data of the newly added VLAN interface
|
||||
*
|
||||
* Returns 0 on success and -ENOMEM if any of the structure allocations fails.
|
||||
*/
|
||||
int batadv_sysfs_add_vlan(struct net_device *dev,
|
||||
struct batadv_softif_vlan *vlan)
|
||||
{
|
||||
char vlan_subdir[sizeof(BATADV_SYSFS_VLAN_SUBDIR_PREFIX) + 5];
|
||||
struct batadv_priv *bat_priv = netdev_priv(dev);
|
||||
struct batadv_attribute **bat_attr;
|
||||
int err;
|
||||
|
||||
if (vlan->vid & BATADV_VLAN_HAS_TAG) {
|
||||
sprintf(vlan_subdir, BATADV_SYSFS_VLAN_SUBDIR_PREFIX "%hu",
|
||||
vlan->vid & VLAN_VID_MASK);
|
||||
|
||||
vlan->kobj = kobject_create_and_add(vlan_subdir,
|
||||
bat_priv->mesh_obj);
|
||||
if (!vlan->kobj) {
|
||||
batadv_err(dev, "Can't add sysfs directory: %s/%s\n",
|
||||
dev->name, vlan_subdir);
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
/* the untagged LAN uses the root folder to store its "VLAN
|
||||
* specific attributes"
|
||||
*/
|
||||
vlan->kobj = bat_priv->mesh_obj;
|
||||
kobject_get(bat_priv->mesh_obj);
|
||||
}
|
||||
|
||||
for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr) {
|
||||
err = sysfs_create_file(vlan->kobj,
|
||||
&((*bat_attr)->attr));
|
||||
if (err) {
|
||||
batadv_err(dev, "Can't add sysfs file: %s/%s/%s\n",
|
||||
dev->name, vlan_subdir,
|
||||
((*bat_attr)->attr).name);
|
||||
goto rem_attr;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
rem_attr:
|
||||
for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr)
|
||||
sysfs_remove_file(vlan->kobj, &((*bat_attr)->attr));
|
||||
|
||||
kobject_put(vlan->kobj);
|
||||
vlan->kobj = NULL;
|
||||
out:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_sysfs_del_vlan - remove all the sysfs objects for a given VLAN
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @vlan: the private data of the VLAN to destroy
|
||||
*/
|
||||
void batadv_sysfs_del_vlan(struct batadv_priv *bat_priv,
|
||||
struct batadv_softif_vlan *vlan)
|
||||
{
|
||||
struct batadv_attribute **bat_attr;
|
||||
|
||||
for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr)
|
||||
sysfs_remove_file(vlan->kobj, &((*bat_attr)->attr));
|
||||
|
||||
kobject_put(vlan->kobj);
|
||||
vlan->kobj = NULL;
|
||||
}
|
||||
|
||||
static ssize_t batadv_show_mesh_iface(struct kobject *kobj,
|
||||
struct attribute *attr, char *buff)
|
||||
{
|
||||
struct net_device *net_dev = batadv_kobj_to_netdev(kobj);
|
||||
struct batadv_hard_iface *hard_iface;
|
||||
ssize_t length;
|
||||
const char *ifname;
|
||||
|
||||
hard_iface = batadv_hardif_get_by_netdev(net_dev);
|
||||
if (!hard_iface)
|
||||
return 0;
|
||||
|
||||
if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
|
||||
ifname = "none";
|
||||
else
|
||||
ifname = hard_iface->soft_iface->name;
|
||||
|
||||
length = sprintf(buff, "%s\n", ifname);
|
||||
|
||||
batadv_hardif_free_ref(hard_iface);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static ssize_t batadv_store_mesh_iface(struct kobject *kobj,
|
||||
struct attribute *attr, char *buff,
|
||||
size_t count)
|
||||
{
|
||||
struct net_device *net_dev = batadv_kobj_to_netdev(kobj);
|
||||
struct batadv_hard_iface *hard_iface;
|
||||
int status_tmp = -1;
|
||||
int ret = count;
|
||||
|
||||
hard_iface = batadv_hardif_get_by_netdev(net_dev);
|
||||
if (!hard_iface)
|
||||
return count;
|
||||
|
||||
if (buff[count - 1] == '\n')
|
||||
buff[count - 1] = '\0';
|
||||
|
||||
if (strlen(buff) >= IFNAMSIZ) {
|
||||
pr_err("Invalid parameter for 'mesh_iface' setting received: interface name too long '%s'\n",
|
||||
buff);
|
||||
batadv_hardif_free_ref(hard_iface);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (strncmp(buff, "none", 4) == 0)
|
||||
status_tmp = BATADV_IF_NOT_IN_USE;
|
||||
else
|
||||
status_tmp = BATADV_IF_I_WANT_YOU;
|
||||
|
||||
if (hard_iface->if_status == status_tmp)
|
||||
goto out;
|
||||
|
||||
if ((hard_iface->soft_iface) &&
|
||||
(strncmp(hard_iface->soft_iface->name, buff, IFNAMSIZ) == 0))
|
||||
goto out;
|
||||
|
||||
rtnl_lock();
|
||||
|
||||
if (status_tmp == BATADV_IF_NOT_IN_USE) {
|
||||
batadv_hardif_disable_interface(hard_iface,
|
||||
BATADV_IF_CLEANUP_AUTO);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* if the interface already is in use */
|
||||
if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
|
||||
batadv_hardif_disable_interface(hard_iface,
|
||||
BATADV_IF_CLEANUP_AUTO);
|
||||
|
||||
ret = batadv_hardif_enable_interface(hard_iface, buff);
|
||||
|
||||
unlock:
|
||||
rtnl_unlock();
|
||||
out:
|
||||
batadv_hardif_free_ref(hard_iface);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t batadv_show_iface_status(struct kobject *kobj,
|
||||
struct attribute *attr, char *buff)
|
||||
{
|
||||
struct net_device *net_dev = batadv_kobj_to_netdev(kobj);
|
||||
struct batadv_hard_iface *hard_iface;
|
||||
ssize_t length;
|
||||
|
||||
hard_iface = batadv_hardif_get_by_netdev(net_dev);
|
||||
if (!hard_iface)
|
||||
return 0;
|
||||
|
||||
switch (hard_iface->if_status) {
|
||||
case BATADV_IF_TO_BE_REMOVED:
|
||||
length = sprintf(buff, "disabling\n");
|
||||
break;
|
||||
case BATADV_IF_INACTIVE:
|
||||
length = sprintf(buff, "inactive\n");
|
||||
break;
|
||||
case BATADV_IF_ACTIVE:
|
||||
length = sprintf(buff, "active\n");
|
||||
break;
|
||||
case BATADV_IF_TO_BE_ACTIVATED:
|
||||
length = sprintf(buff, "enabling\n");
|
||||
break;
|
||||
case BATADV_IF_NOT_IN_USE:
|
||||
default:
|
||||
length = sprintf(buff, "not in use\n");
|
||||
break;
|
||||
}
|
||||
|
||||
batadv_hardif_free_ref(hard_iface);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static BATADV_ATTR(mesh_iface, S_IRUGO | S_IWUSR, batadv_show_mesh_iface,
|
||||
batadv_store_mesh_iface);
|
||||
static BATADV_ATTR(iface_status, S_IRUGO, batadv_show_iface_status, NULL);
|
||||
|
||||
static struct batadv_attribute *batadv_batman_attrs[] = {
|
||||
&batadv_attr_mesh_iface,
|
||||
&batadv_attr_iface_status,
|
||||
NULL,
|
||||
};
|
||||
|
||||
int batadv_sysfs_add_hardif(struct kobject **hardif_obj, struct net_device *dev)
|
||||
{
|
||||
struct kobject *hardif_kobject = &dev->dev.kobj;
|
||||
struct batadv_attribute **bat_attr;
|
||||
int err;
|
||||
|
||||
*hardif_obj = kobject_create_and_add(BATADV_SYSFS_IF_BAT_SUBDIR,
|
||||
hardif_kobject);
|
||||
|
||||
if (!*hardif_obj) {
|
||||
batadv_err(dev, "Can't add sysfs directory: %s/%s\n", dev->name,
|
||||
BATADV_SYSFS_IF_BAT_SUBDIR);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (bat_attr = batadv_batman_attrs; *bat_attr; ++bat_attr) {
|
||||
err = sysfs_create_file(*hardif_obj, &((*bat_attr)->attr));
|
||||
if (err) {
|
||||
batadv_err(dev, "Can't add sysfs file: %s/%s/%s\n",
|
||||
dev->name, BATADV_SYSFS_IF_BAT_SUBDIR,
|
||||
((*bat_attr)->attr).name);
|
||||
goto rem_attr;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
rem_attr:
|
||||
for (bat_attr = batadv_batman_attrs; *bat_attr; ++bat_attr)
|
||||
sysfs_remove_file(*hardif_obj, &((*bat_attr)->attr));
|
||||
out:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void batadv_sysfs_del_hardif(struct kobject **hardif_obj)
|
||||
{
|
||||
kobject_put(*hardif_obj);
|
||||
*hardif_obj = NULL;
|
||||
}
|
||||
|
||||
int batadv_throw_uevent(struct batadv_priv *bat_priv, enum batadv_uev_type type,
|
||||
enum batadv_uev_action action, const char *data)
|
||||
{
|
||||
int ret = -ENOMEM;
|
||||
struct kobject *bat_kobj;
|
||||
char *uevent_env[4] = { NULL, NULL, NULL, NULL };
|
||||
|
||||
bat_kobj = &bat_priv->soft_iface->dev.kobj;
|
||||
|
||||
uevent_env[0] = kasprintf(GFP_ATOMIC,
|
||||
"%s%s", BATADV_UEV_TYPE_VAR,
|
||||
batadv_uev_type_str[type]);
|
||||
if (!uevent_env[0])
|
||||
goto out;
|
||||
|
||||
uevent_env[1] = kasprintf(GFP_ATOMIC,
|
||||
"%s%s", BATADV_UEV_ACTION_VAR,
|
||||
batadv_uev_action_str[action]);
|
||||
if (!uevent_env[1])
|
||||
goto out;
|
||||
|
||||
/* If the event is DEL, ignore the data field */
|
||||
if (action != BATADV_UEV_DEL) {
|
||||
uevent_env[2] = kasprintf(GFP_ATOMIC,
|
||||
"%s%s", BATADV_UEV_DATA_VAR, data);
|
||||
if (!uevent_env[2])
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = kobject_uevent_env(bat_kobj, KOBJ_CHANGE, uevent_env);
|
||||
out:
|
||||
kfree(uevent_env[0]);
|
||||
kfree(uevent_env[1]);
|
||||
kfree(uevent_env[2]);
|
||||
|
||||
if (ret)
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"Impossible to send uevent for (%s,%s,%s) event (err: %d)\n",
|
||||
batadv_uev_type_str[type],
|
||||
batadv_uev_action_str[action],
|
||||
(action == BATADV_UEV_DEL ? "NULL" : data), ret);
|
||||
return ret;
|
||||
}
|
50
net/batman-adv/sysfs.h
Normal file
50
net/batman-adv/sysfs.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/* Copyright (C) 2010-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_SYSFS_H_
|
||||
#define _NET_BATMAN_ADV_SYSFS_H_
|
||||
|
||||
#define BATADV_SYSFS_IF_MESH_SUBDIR "mesh"
|
||||
#define BATADV_SYSFS_IF_BAT_SUBDIR "batman_adv"
|
||||
/**
|
||||
* BATADV_SYSFS_VLAN_SUBDIR_PREFIX - prefix of the subfolder that will be
|
||||
* created in the sysfs hierarchy for each VLAN interface. The subfolder will
|
||||
* be named "BATADV_SYSFS_VLAN_SUBDIR_PREFIX%vid".
|
||||
*/
|
||||
#define BATADV_SYSFS_VLAN_SUBDIR_PREFIX "vlan"
|
||||
|
||||
struct batadv_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct kobject *kobj, struct attribute *attr,
|
||||
char *buf);
|
||||
ssize_t (*store)(struct kobject *kobj, struct attribute *attr,
|
||||
char *buf, size_t count);
|
||||
};
|
||||
|
||||
int batadv_sysfs_add_meshif(struct net_device *dev);
|
||||
void batadv_sysfs_del_meshif(struct net_device *dev);
|
||||
int batadv_sysfs_add_hardif(struct kobject **hardif_obj,
|
||||
struct net_device *dev);
|
||||
void batadv_sysfs_del_hardif(struct kobject **hardif_obj);
|
||||
int batadv_sysfs_add_vlan(struct net_device *dev,
|
||||
struct batadv_softif_vlan *vlan);
|
||||
void batadv_sysfs_del_vlan(struct batadv_priv *bat_priv,
|
||||
struct batadv_softif_vlan *vlan);
|
||||
int batadv_throw_uevent(struct batadv_priv *bat_priv, enum batadv_uev_type type,
|
||||
enum batadv_uev_action action, const char *data);
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_SYSFS_H_ */
|
3712
net/batman-adv/translation-table.c
Normal file
3712
net/batman-adv/translation-table.c
Normal file
File diff suppressed because it is too large
Load diff
56
net/batman-adv/translation-table.h
Normal file
56
net/batman-adv/translation-table.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich, Antonio Quartulli
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_TRANSLATION_TABLE_H_
|
||||
#define _NET_BATMAN_ADV_TRANSLATION_TABLE_H_
|
||||
|
||||
int batadv_tt_init(struct batadv_priv *bat_priv);
|
||||
bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
|
||||
unsigned short vid, int ifindex, uint32_t mark);
|
||||
uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv,
|
||||
const uint8_t *addr, unsigned short vid,
|
||||
const char *message, bool roaming);
|
||||
int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset);
|
||||
int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset);
|
||||
void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node,
|
||||
int32_t match_vid, const char *message);
|
||||
int batadv_tt_global_hash_count(struct batadv_priv *bat_priv,
|
||||
const uint8_t *addr, unsigned short vid);
|
||||
struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
|
||||
const uint8_t *src,
|
||||
const uint8_t *addr,
|
||||
unsigned short vid);
|
||||
void batadv_tt_free(struct batadv_priv *bat_priv);
|
||||
bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr,
|
||||
unsigned short vid);
|
||||
bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, uint8_t *src,
|
||||
uint8_t *dst, unsigned short vid);
|
||||
void batadv_tt_local_commit_changes(struct batadv_priv *bat_priv);
|
||||
bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv,
|
||||
uint8_t *addr, unsigned short vid);
|
||||
bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv,
|
||||
uint8_t *addr, unsigned short vid);
|
||||
void batadv_tt_local_resize_to_mtu(struct net_device *soft_iface);
|
||||
bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node,
|
||||
const unsigned char *addr,
|
||||
unsigned short vid);
|
||||
bool batadv_tt_global_is_isolated(struct batadv_priv *bat_priv,
|
||||
const uint8_t *addr, unsigned short vid);
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ */
|
1259
net/batman-adv/types.h
Normal file
1259
net/batman-adv/types.h
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue