Included changes:

- update emails for A. Quartulli and M. Lindner in MAINTAINERS
 - switch to the next on-the-wire protocol version
 - introduce the T(ype) V(ersion) L(ength) V(alue) framework
 - adjust the existing components to make them use the new TVLV code
 - make the TT component use CRC32 instead of CRC16
 - totally remove the VIS functionality (has been moved to userspace)
 - reorder packet types and flags
 - add static checks on packet format
 - remove __packed from batadv_ogm_packet
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABCAAGBQJSVa1BAAoJEADl0hg6qKeOiZkP/iD8Gehkb+jII3J1AE4+TdaW
 X5XzB40A6bRr/RhiqqFXA6FhjLPCwPlaYwo+FL9QC9fIbV6Drofd8B4Vxvp8daQI
 A5ZTzA7GFJhUkzM4vvw5eyF5848OYGITAJh5kSkiYiFSC6x9X9UFrG1EBBX7ikGd
 7MXCjNa5GXaO7BsSnSbGuuMYw6CuZxwwxUZ3F/fG031yTlRqz88TiBpOGCZXSM5J
 eYOf68byDPPsRvj/RgpmYTt7MzWnAgp9gJotp5Owuqpb3kvZC6SkA8uj4O2f29rn
 nmQ+1q5onjHpfqukezPOIx9HEKo2XCq3qbFiW74dyNi+EQlYJ6RAGyRBkTTl4hse
 cTOeWENANoFco/SPiOi1PjGrytuflAiIegwkVugAvnmYlbsVIi6MXV+BGo/ARybv
 RwPBFULCaxO5HNkYz6elcu8cwFY9s3PROEdMVpRj+iNTeQq3ZQHj+QfPsHdedkAz
 mkHqiZQo/pgBeWd+KiN98YJtQ4nPV1Qi+yGiayc3hmsk5fIABaOHxP3NV0NrTeOO
 PYKWFcKfdLgtvrfcOXbcWpPz0d8SUkyF8zjlzIscXlpJVWK1sOw8oEER3IrDgzDy
 PmzHkEL+qfDh4I5hEPOIEXxzPw/E4b+G3WI34bUN/4chGSmJdo6yyaC6XcXe7r9g
 AsxmABE84uT2npNSOSkN
 =YQD5
 -----END PGP SIGNATURE-----

Merge tag 'batman-adv-for-davem' of git://git.open-mesh.org/linux-merge

Included changes:
- update emails for A. Quartulli and M. Lindner in MAINTAINERS
- switch to the next on-the-wire protocol version
- introduce the T(ype) V(ersion) L(ength) V(alue) framework
- adjust the existing components to make them use the new TVLV code
- make the TT component use CRC32 instead of CRC16
- totally remove the VIS functionality (has been moved to userspace)
- reorder packet types and flags
- add static checks on packet format
- remove __packed from batadv_ogm_packet
This commit is contained in:
David S. Miller 2013-10-11 16:28:55 -04:00
commit 29b67c39dc
29 changed files with 1969 additions and 2134 deletions

View File

@ -88,14 +88,3 @@ Contact: Marek Lindner <lindner_marek@yahoo.de>
Description:
Defines the routing procotol this mesh instance
uses to find the optimal paths through the mesh.
What: /sys/class/net/<mesh_iface>/mesh/vis_mode
Date: May 2010
Contact: Marek Lindner <lindner_marek@yahoo.de>
Description:
Each batman node only maintains information about its
own local neighborhood, therefore generating graphs
showing the topology of the entire mesh is not easily
feasible without having a central instance to collect
the local topologies from all nodes. This file allows
to activate the collecting (server) mode.

View File

@ -69,8 +69,7 @@ folder:
# aggregated_ogms gw_bandwidth log_level
# ap_isolation gw_mode orig_interval
# bonding gw_sel_class routing_algo
# bridge_loop_avoidance hop_penalty vis_mode
# fragmentation
# bridge_loop_avoidance hop_penalty fragmentation
There is a special folder for debugging information:
@ -78,7 +77,7 @@ There is a special folder for debugging information:
# ls /sys/kernel/debug/batman_adv/bat0/
# bla_backbone_table log transtable_global
# bla_claim_table originators transtable_local
# gateways socket vis_data
# gateways socket
Some of the files contain all sort of status information regard-
ing the mesh network. For example, you can view the table of
@ -127,51 +126,6 @@ ously assigned to interfaces now used by batman advanced, e.g.
# ifconfig eth0 0.0.0.0
VISUALIZATION
-------------
If you want topology visualization, at least one mesh node must
be configured as VIS-server:
# echo "server" > /sys/class/net/bat0/mesh/vis_mode
Each node is either configured as "server" or as "client" (de-
fault: "client"). Clients send their topology data to the server
next to them, and server synchronize with other servers. If there
is no server configured (default) within the mesh, no topology
information will be transmitted. With these "synchronizing
servers", there can be 1 or more vis servers sharing the same (or
at least very similar) data.
When configured as server, you can get a topology snapshot of
your mesh:
# cat /sys/kernel/debug/batman_adv/bat0/vis_data
This raw output is intended to be easily parsable and convertable
with other tools. Have a look at the batctl README if you want a
vis output in dot or json format for instance and how those out-
puts could then be visualised in an image.
The raw format consists of comma separated values per entry where
each entry is giving information about a certain source inter-
face. Each entry can/has to have the following values:
-> "mac" - mac address of an originator's source interface
(each line begins with it)
-> "TQ mac value" - src mac's link quality towards mac address
of a neighbor originator's interface which
is being used for routing
-> "TT mac" - TT announced by source mac
-> "PRIMARY" - this is a primary interface
-> "SEC mac" - secondary mac address of source
(requires preceding PRIMARY)
The TQ value has a range from 4 to 255 with 255 being the best.
The TT entries are showing which hosts are connected to the mesh
via bat0 or being bridged into the mesh network. The PRIMARY/SEC
values are only applied on primary interfaces
LOGGING/DEBUGGING
-----------------

View File

@ -1652,9 +1652,9 @@ F: drivers/video/backlight/
F: include/linux/backlight.h
BATMAN ADVANCED
M: Marek Lindner <lindner_marek@yahoo.de>
M: Marek Lindner <mareklindner@neomailbox.ch>
M: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
M: Antonio Quartulli <ordex@autistici.org>
M: Antonio Quartulli <antonio@meshcoding.com>
L: b.a.t.m.a.n@lists.open-mesh.org
W: http://www.open-mesh.org/
S: Maintained

View File

@ -38,4 +38,3 @@ batman-adv-y += soft-interface.o
batman-adv-y += sysfs.o
batman-adv-y += translation-table.o
batman-adv-y += unicast.o
batman-adv-y += vis.o

View File

@ -135,9 +135,8 @@ static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
batadv_ogm_packet->header.version = BATADV_COMPAT_VERSION;
batadv_ogm_packet->header.ttl = 2;
batadv_ogm_packet->flags = BATADV_NO_FLAGS;
batadv_ogm_packet->reserved = 0;
batadv_ogm_packet->tq = BATADV_TQ_MAX_VALUE;
batadv_ogm_packet->tt_num_changes = 0;
batadv_ogm_packet->ttvn = 0;
res = 0;
@ -207,12 +206,12 @@ static uint8_t batadv_hop_penalty(uint8_t tq,
/* is there another aggregated packet here? */
static int batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len,
int tt_num_changes)
__be16 tvlv_len)
{
int next_buff_pos = 0;
next_buff_pos += buff_pos + BATADV_OGM_HLEN;
next_buff_pos += batadv_tt_len(tt_num_changes);
next_buff_pos += ntohs(tvlv_len);
return (next_buff_pos <= packet_len) &&
(next_buff_pos <= BATADV_MAX_AGGREGATION_BYTES);
@ -240,7 +239,7 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
/* adjust all flags and log packets */
while (batadv_iv_ogm_aggr_packet(buff_pos, forw_packet->packet_len,
batadv_ogm_packet->tt_num_changes)) {
batadv_ogm_packet->tvlv_len)) {
/* we might have aggregated direct link packets with an
* ordinary base packet
*/
@ -256,18 +255,18 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
fwd_str = "Sending own";
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"%s %spacket (originator %pM, seqno %u, TQ %d, TTL %d, IDF %s, ttvn %d) on interface %s [%pM]\n",
"%s %spacket (originator %pM, seqno %u, TQ %d, TTL %d, IDF %s) on interface %s [%pM]\n",
fwd_str, (packet_num > 0 ? "aggregated " : ""),
batadv_ogm_packet->orig,
ntohl(batadv_ogm_packet->seqno),
batadv_ogm_packet->tq, batadv_ogm_packet->header.ttl,
(batadv_ogm_packet->flags & BATADV_DIRECTLINK ?
"on" : "off"),
batadv_ogm_packet->ttvn, hard_iface->net_dev->name,
hard_iface->net_dev->name,
hard_iface->net_dev->dev_addr);
buff_pos += BATADV_OGM_HLEN;
buff_pos += batadv_tt_len(batadv_ogm_packet->tt_num_changes);
buff_pos += ntohs(batadv_ogm_packet->tvlv_len);
packet_num++;
packet_pos = forw_packet->skb->data + buff_pos;
batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos;
@ -601,7 +600,7 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
struct batadv_hard_iface *if_incoming)
{
struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
uint8_t tt_num_changes;
uint16_t tvlv_len;
if (batadv_ogm_packet->header.ttl <= 1) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "ttl exceeded\n");
@ -621,7 +620,7 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
return;
}
tt_num_changes = batadv_ogm_packet->tt_num_changes;
tvlv_len = ntohs(batadv_ogm_packet->tvlv_len);
batadv_ogm_packet->header.ttl--;
memcpy(batadv_ogm_packet->prev_sender, ethhdr->h_source, ETH_ALEN);
@ -642,7 +641,7 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
batadv_ogm_packet->flags &= ~BATADV_DIRECTLINK;
batadv_iv_ogm_queue_add(bat_priv, (unsigned char *)batadv_ogm_packet,
BATADV_OGM_HLEN + batadv_tt_len(tt_num_changes),
BATADV_OGM_HLEN + tvlv_len,
if_incoming, 0, batadv_iv_ogm_fwd_send_time());
}
@ -688,43 +687,29 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
struct batadv_ogm_packet *batadv_ogm_packet;
struct batadv_hard_iface *primary_if;
int *ogm_buff_len = &hard_iface->bat_iv.ogm_buff_len;
int vis_server, tt_num_changes = 0;
uint32_t seqno;
uint8_t bandwidth;
uint16_t tvlv_len = 0;
vis_server = atomic_read(&bat_priv->vis_mode);
primary_if = batadv_primary_if_get_selected(bat_priv);
if (hard_iface == primary_if)
tt_num_changes = batadv_tt_append_diff(bat_priv, ogm_buff,
ogm_buff_len,
BATADV_OGM_HLEN);
if (hard_iface == primary_if) {
/* tt changes have to be committed before the tvlv data is
* appended as it may alter the tt tvlv container
*/
batadv_tt_local_commit_changes(bat_priv);
tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
ogm_buff_len,
BATADV_OGM_HLEN);
}
batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff);
batadv_ogm_packet->tvlv_len = htons(tvlv_len);
/* change sequence number to network order */
seqno = (uint32_t)atomic_read(&hard_iface->bat_iv.ogm_seqno);
batadv_ogm_packet->seqno = htonl(seqno);
atomic_inc(&hard_iface->bat_iv.ogm_seqno);
batadv_ogm_packet->ttvn = atomic_read(&bat_priv->tt.vn);
batadv_ogm_packet->tt_crc = htons(bat_priv->tt.local_crc);
if (tt_num_changes >= 0)
batadv_ogm_packet->tt_num_changes = tt_num_changes;
if (vis_server == BATADV_VIS_TYPE_SERVER_SYNC)
batadv_ogm_packet->flags |= BATADV_VIS_SERVER;
else
batadv_ogm_packet->flags &= ~BATADV_VIS_SERVER;
if (hard_iface == primary_if &&
atomic_read(&bat_priv->gw_mode) == BATADV_GW_MODE_SERVER) {
bandwidth = (uint8_t)atomic_read(&bat_priv->gw_bandwidth);
batadv_ogm_packet->gw_flags = bandwidth;
} else {
batadv_ogm_packet->gw_flags = BATADV_NO_FLAGS;
}
batadv_iv_ogm_slide_own_bcast_window(hard_iface);
batadv_iv_ogm_queue_add(bat_priv, hard_iface->bat_iv.ogm_buff,
hard_iface->bat_iv.ogm_buff_len, hard_iface, 1,
@ -798,7 +783,6 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
rcu_read_unlock();
orig_node->flags = batadv_ogm_packet->flags;
neigh_node->last_seen = jiffies;
spin_lock_bh(&neigh_node->lq_update_lock);
@ -820,11 +804,11 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
*/
router = batadv_orig_node_get_router(orig_node);
if (router == neigh_node)
goto update_tt;
goto out;
/* if this neighbor does not offer a better TQ we won't consider it */
if (router && (router->tq_avg > neigh_node->tq_avg))
goto update_tt;
goto out;
/* if the TQ is the same and the link not more symmetric we
* won't consider it either
@ -843,35 +827,10 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);
if (sum_orig >= sum_neigh)
goto update_tt;
goto out;
}
batadv_update_route(bat_priv, orig_node, neigh_node);
update_tt:
/* I have to check for transtable changes only if the OGM has been
* sent through a primary interface
*/
if (((batadv_ogm_packet->orig != ethhdr->h_source) &&
(batadv_ogm_packet->header.ttl > 2)) ||
(batadv_ogm_packet->flags & BATADV_PRIMARIES_FIRST_HOP))
batadv_tt_update_orig(bat_priv, orig_node, tt_buff,
batadv_ogm_packet->tt_num_changes,
batadv_ogm_packet->ttvn,
ntohs(batadv_ogm_packet->tt_crc));
if (orig_node->gw_flags != batadv_ogm_packet->gw_flags)
batadv_gw_node_update(bat_priv, orig_node,
batadv_ogm_packet->gw_flags);
orig_node->gw_flags = batadv_ogm_packet->gw_flags;
/* restart gateway selection if fast or late switching was enabled */
if ((orig_node->gw_flags) &&
(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_node);
goto out;
unlock:
@ -1122,13 +1081,11 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
is_single_hop_neigh = true;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %u, ttvn %u, crc %#.4x, changes %u, tq %d, TTL %d, V %d, IDF %d)\n",
"Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %u, tq %d, TTL %d, V %d, IDF %d)\n",
ethhdr->h_source, if_incoming->net_dev->name,
if_incoming->net_dev->dev_addr, batadv_ogm_packet->orig,
batadv_ogm_packet->prev_sender,
ntohl(batadv_ogm_packet->seqno), batadv_ogm_packet->ttvn,
ntohs(batadv_ogm_packet->tt_crc),
batadv_ogm_packet->tt_num_changes, batadv_ogm_packet->tq,
ntohl(batadv_ogm_packet->seqno), batadv_ogm_packet->tq,
batadv_ogm_packet->header.ttl,
batadv_ogm_packet->header.version, has_directlink_flag);
@ -1254,6 +1211,8 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
goto out;
}
batadv_tvlv_ogm_receive(bat_priv, batadv_ogm_packet, orig_node);
/* if sender is a direct neighbor the sender mac equals
* originator mac
*/
@ -1350,9 +1309,9 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb,
struct batadv_ogm_packet *batadv_ogm_packet;
struct ethhdr *ethhdr;
int buff_pos = 0, packet_len;
unsigned char *tt_buff, *packet_buff;
bool ret;
unsigned char *tvlv_buff, *packet_buff;
uint8_t *packet_pos;
bool ret;
ret = batadv_check_management_packet(skb, if_incoming, BATADV_OGM_HLEN);
if (!ret)
@ -1375,14 +1334,14 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb,
/* unpack the aggregated packets and process them one by one */
while (batadv_iv_ogm_aggr_packet(buff_pos, packet_len,
batadv_ogm_packet->tt_num_changes)) {
tt_buff = packet_buff + buff_pos + BATADV_OGM_HLEN;
batadv_ogm_packet->tvlv_len)) {
tvlv_buff = packet_buff + buff_pos + BATADV_OGM_HLEN;
batadv_iv_ogm_process(ethhdr, batadv_ogm_packet, tt_buff,
if_incoming);
batadv_iv_ogm_process(ethhdr, batadv_ogm_packet,
tvlv_buff, if_incoming);
buff_pos += BATADV_OGM_HLEN;
buff_pos += batadv_tt_len(batadv_ogm_packet->tt_num_changes);
buff_pos += ntohs(batadv_ogm_packet->tvlv_len);
packet_pos = packet_buff + buff_pos;
batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos;

View File

@ -28,7 +28,6 @@
#include "gateway_common.h"
#include "gateway_client.h"
#include "soft-interface.h"
#include "vis.h"
#include "icmp_socket.h"
#include "bridge_loop_avoidance.h"
#include "distributed-arp-table.h"
@ -300,12 +299,6 @@ static int batadv_transtable_local_open(struct inode *inode, struct file *file)
return single_open(file, batadv_tt_local_seq_print_text, net_dev);
}
static int batadv_vis_data_open(struct inode *inode, struct file *file)
{
struct net_device *net_dev = (struct net_device *)inode->i_private;
return single_open(file, batadv_vis_seq_print_text, net_dev);
}
struct batadv_debuginfo {
struct attribute attr;
const struct file_operations fops;
@ -356,7 +349,6 @@ static BATADV_DEBUGINFO(dat_cache, S_IRUGO, batadv_dat_cache_open);
#endif
static BATADV_DEBUGINFO(transtable_local, S_IRUGO,
batadv_transtable_local_open);
static BATADV_DEBUGINFO(vis_data, S_IRUGO, batadv_vis_data_open);
#ifdef CONFIG_BATMAN_ADV_NC
static BATADV_DEBUGINFO(nc_nodes, S_IRUGO, batadv_nc_nodes_open);
#endif
@ -373,7 +365,6 @@ static struct batadv_debuginfo *batadv_mesh_debuginfos[] = {
&batadv_debuginfo_dat_cache,
#endif
&batadv_debuginfo_transtable_local,
&batadv_debuginfo_vis_data,
#ifdef CONFIG_BATMAN_ADV_NC
&batadv_debuginfo_nc_nodes,
#endif

View File

@ -419,6 +419,10 @@ static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res,
bool ret = false;
int j;
/* check if orig node candidate is running DAT */
if (!(candidate->capabilities & BATADV_ORIG_CAPA_HAS_DAT))
goto out;
/* Check if this node has already been selected... */
for (j = 0; j < select; j++)
if (res[j].orig_node == candidate)
@ -625,6 +629,59 @@ out:
return ret;
}
/**
* batadv_dat_tvlv_container_update - update the dat tvlv container after dat
* setting change
* @bat_priv: the bat priv with all the soft interface information
*/
static void batadv_dat_tvlv_container_update(struct batadv_priv *bat_priv)
{
char dat_mode;
dat_mode = atomic_read(&bat_priv->distributed_arp_table);
switch (dat_mode) {
case 0:
batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_DAT, 1);
break;
case 1:
batadv_tvlv_container_register(bat_priv, BATADV_TVLV_DAT, 1,
NULL, 0);
break;
}
}
/**
* batadv_dat_status_update - update the dat tvlv container after dat
* setting change
* @net_dev: the soft interface net device
*/
void batadv_dat_status_update(struct net_device *net_dev)
{
struct batadv_priv *bat_priv = netdev_priv(net_dev);
batadv_dat_tvlv_container_update(bat_priv);
}
/**
* batadv_gw_tvlv_ogm_handler_v1 - process incoming dat 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_dat_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)
{
if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)
orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_DAT;
else
orig->capabilities |= BATADV_ORIG_CAPA_HAS_DAT;
}
/**
* batadv_dat_hash_free - free the local DAT hash table
* @bat_priv: the bat priv with all the soft interface information
@ -657,6 +714,10 @@ int batadv_dat_init(struct batadv_priv *bat_priv)
batadv_dat_start_timer(bat_priv);
batadv_tvlv_handler_register(bat_priv, batadv_dat_tvlv_ogm_handler_v1,
NULL, BATADV_TVLV_DAT, 1,
BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
batadv_dat_tvlv_container_update(bat_priv);
return 0;
}
@ -666,6 +727,9 @@ int batadv_dat_init(struct batadv_priv *bat_priv)
*/
void batadv_dat_free(struct batadv_priv *bat_priv)
{
batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_DAT, 1);
batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_DAT, 1);
cancel_delayed_work_sync(&bat_priv->dat.work);
batadv_dat_hash_free(bat_priv);

View File

@ -29,6 +29,7 @@
#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,
@ -98,6 +99,10 @@ static inline void batadv_dat_inc_counter(struct batadv_priv *bat_priv,
#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)

View File

@ -118,7 +118,6 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
uint32_t max_gw_factor = 0, tmp_gw_factor = 0;
uint32_t gw_divisor;
uint8_t max_tq = 0;
int down, up;
uint8_t tq_avg;
struct batadv_orig_node *orig_node;
@ -142,10 +141,9 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
switch (atomic_read(&bat_priv->gw_sel_class)) {
case 1: /* fast connection */
batadv_gw_bandwidth_to_kbit(orig_node->gw_flags,
&down, &up);
tmp_gw_factor = tq_avg * tq_avg * down * 100 * 100;
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) ||
@ -258,16 +256,22 @@ void batadv_gw_election(struct batadv_priv *bat_priv)
NULL);
} else if ((!curr_gw) && (next_gw)) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Adding route to gateway %pM (gw_flags: %i, tq: %i)\n",
"Adding route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n",
next_gw->orig_node->orig,
next_gw->orig_node->gw_flags, router->tq_avg);
next_gw->bandwidth_down / 10,
next_gw->bandwidth_down % 10,
next_gw->bandwidth_up / 10,
next_gw->bandwidth_up % 10, router->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 (gw_flags: %i, tq: %i)\n",
"Changing route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n",
next_gw->orig_node->orig,
next_gw->orig_node->gw_flags, router->tq_avg);
next_gw->bandwidth_down / 10,
next_gw->bandwidth_down % 10,
next_gw->bandwidth_up / 10,
next_gw->bandwidth_up % 10, router->tq_avg);
batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_CHANGE,
gw_addr);
}
@ -337,12 +341,20 @@ out:
return;
}
/**
* 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,
uint8_t new_gwflags)
struct batadv_tvlv_gateway_data *gateway)
{
struct batadv_gw_node *gw_node;
int down, up;
if (gateway->bandwidth_down == 0)
return;
gw_node = kzalloc(sizeof(*gw_node), GFP_ATOMIC);
if (!gw_node)
@ -356,73 +368,116 @@ static void batadv_gw_node_add(struct batadv_priv *bat_priv,
hlist_add_head_rcu(&gw_node->list, &bat_priv->gw.list);
spin_unlock_bh(&bat_priv->gw.list_lock);
batadv_gw_bandwidth_to_kbit(new_gwflags, &down, &up);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Found new gateway %pM -> gw_class: %i - %i%s/%i%s\n",
orig_node->orig, new_gwflags,
(down > 2048 ? down / 1024 : down),
(down > 2048 ? "MBit" : "KBit"),
(up > 2048 ? up / 1024 : up),
(up > 2048 ? "MBit" : "KBit"));
"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);
}
void batadv_gw_node_update(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
uint8_t new_gwflags)
/**
* 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, *curr_gw;
/* Note: We don't need a NULL check here, since curr_gw never gets
* dereferenced. If curr_gw is NULL we also should not exit as we may
* have this gateway in our list (duplication check!) even though we
* have no currently selected gateway.
*/
curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
struct batadv_gw_node *gw_node_tmp, *gw_node = NULL;
rcu_read_lock();
hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) {
if (gw_node->orig_node != orig_node)
hlist_for_each_entry_rcu(gw_node_tmp, &bat_priv->gw.list, list) {
if (gw_node_tmp->orig_node != orig_node)
continue;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Gateway class of originator %pM changed from %i to %i\n",
orig_node->orig, gw_node->orig_node->gw_flags,
new_gwflags);
if (gw_node_tmp->deleted)
continue;
gw_node->deleted = 0;
if (!atomic_inc_not_zero(&gw_node_tmp->refcount))
continue;
if (new_gwflags == BATADV_NO_FLAGS) {
gw_node->deleted = jiffies;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Gateway %pM removed from gateway list\n",
orig_node->orig);
if (gw_node == curr_gw)
goto deselect;
}
goto unlock;
gw_node = gw_node_tmp;
break;
}
if (new_gwflags == BATADV_NO_FLAGS)
goto unlock;
batadv_gw_node_add(bat_priv, orig_node, new_gwflags);
goto unlock;
deselect:
batadv_gw_deselect(bat_priv);
unlock:
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_deselect(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)
{
batadv_gw_node_update(bat_priv, orig_node, 0);
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)
@ -467,9 +522,7 @@ static int batadv_write_buffer_text(struct batadv_priv *bat_priv,
{
struct batadv_gw_node *curr_gw;
struct batadv_neigh_node *router;
int down, up, ret = -1;
batadv_gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up);
int ret = -1;
router = batadv_orig_node_get_router(gw_node->orig_node);
if (!router)
@ -477,16 +530,15 @@ static int batadv_write_buffer_text(struct batadv_priv *bat_priv,
curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n",
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->tq_avg, router->addr,
router->if_incoming->net_dev->name,
gw_node->orig_node->gw_flags,
(down > 2048 ? down / 1024 : down),
(down > 2048 ? "MBit" : "KBit"),
(up > 2048 ? up / 1024 : up),
(up > 2048 ? "MBit" : "KBit"));
gw_node->bandwidth_down / 10,
gw_node->bandwidth_down % 10,
gw_node->bandwidth_up / 10,
gw_node->bandwidth_up % 10);
batadv_neigh_node_free_ref(router);
if (curr_gw)
@ -508,7 +560,7 @@ int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset)
goto out;
seq_printf(seq,
" %-12s (%s/%i) %17s [%10s]: gw_class ... [B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
" %-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);
@ -675,7 +727,7 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
{
struct batadv_neigh_node *neigh_curr = NULL, *neigh_old = NULL;
struct batadv_orig_node *orig_dst_node = NULL;
struct batadv_gw_node *curr_gw = NULL;
struct batadv_gw_node *gw_node = NULL, *curr_gw = NULL;
struct ethhdr *ethhdr;
bool ret, out_of_range = false;
unsigned int header_len = 0;
@ -691,7 +743,8 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
if (!orig_dst_node)
goto out;
if (!orig_dst_node->gw_flags)
gw_node = batadv_gw_node_get(bat_priv, orig_dst_node);
if (!gw_node->bandwidth_down == 0)
goto out;
ret = batadv_is_type_dhcprequest(skb, header_len);
@ -742,6 +795,8 @@ out:
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)

View File

@ -29,7 +29,7 @@ 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,
uint8_t new_gwflags);
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);

View File

@ -21,64 +21,23 @@
#include "gateway_common.h"
#include "gateway_client.h"
/* calculates the gateway class from kbit */
static void batadv_kbit_to_gw_bandwidth(int down, int up, long *gw_srv_class)
{
int mdown = 0, tdown, tup, difference;
uint8_t sbit, part;
*gw_srv_class = 0;
difference = 0x0FFFFFFF;
/* test all downspeeds */
for (sbit = 0; sbit < 2; sbit++) {
for (part = 0; part < 16; part++) {
tdown = 32 * (sbit + 2) * (1 << part);
if (abs(tdown - down) < difference) {
*gw_srv_class = (sbit << 7) + (part << 3);
difference = abs(tdown - down);
mdown = tdown;
}
}
}
/* test all upspeeds */
difference = 0x0FFFFFFF;
for (part = 0; part < 8; part++) {
tup = ((part + 1) * (mdown)) / 8;
if (abs(tup - up) < difference) {
*gw_srv_class = (*gw_srv_class & 0xF8) | part;
difference = abs(tup - up);
}
}
}
/* returns the up and downspeeds in kbit, calculated from the class */
void batadv_gw_bandwidth_to_kbit(uint8_t gw_srv_class, int *down, int *up)
{
int sbit = (gw_srv_class & 0x80) >> 7;
int dpart = (gw_srv_class & 0x78) >> 3;
int upart = (gw_srv_class & 0x07);
if (!gw_srv_class) {
*down = 0;
*up = 0;
return;
}
*down = 32 * (sbit + 2) * (1 << dpart);
*up = ((upart + 1) * (*down)) / 8;
}
/**
* 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,
int *up, int *down)
uint32_t *down, uint32_t *up)
{
int ret, multi = 1;
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)
@ -88,10 +47,10 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
tmp_ptr = buff + strlen(buff) - 4;
if (strnicmp(tmp_ptr, "mbit", 4) == 0)
multi = 1024;
bw_unit_type = BATADV_BW_UNIT_MBIT;
if ((strnicmp(tmp_ptr, "kbit", 4) == 0) ||
(multi > 1))
(bw_unit_type == BATADV_BW_UNIT_MBIT))
*tmp_ptr = '\0';
}
@ -103,20 +62,28 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
return false;
}
*down = ldown * multi;
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) {
multi = 1;
bw_unit_type = BATADV_BW_UNIT_KBIT;
if (strlen(slash_ptr + 1) > 4) {
tmp_ptr = slash_ptr + 1 - 4 + strlen(slash_ptr + 1);
if (strnicmp(tmp_ptr, "mbit", 4) == 0)
multi = 1024;
bw_unit_type = BATADV_BW_UNIT_MBIT;
if ((strnicmp(tmp_ptr, "kbit", 4) == 0) ||
(multi > 1))
(bw_unit_type == BATADV_BW_UNIT_MBIT))
*tmp_ptr = '\0';
}
@ -128,52 +95,149 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
return false;
}
*up = lup * multi;
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);
long gw_bandwidth_tmp = 0;
int up = 0, down = 0;
uint32_t down_curr, up_curr, down_new = 0, up_new = 0;
bool ret;
ret = batadv_parse_gw_bandwidth(net_dev, buff, &up, &down);
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) || (down < 256))
down = 2000;
if (!down_new)
down_new = 1;
if (!up)
up = down / 5;
if (!up_new)
up_new = down_new / 5;
batadv_kbit_to_gw_bandwidth(down, up, &gw_bandwidth_tmp);
if (!up_new)
up_new = 1;
/* the gw bandwidth we guessed above might not match the given
* speeds, hence we need to calculate it back to show the number
* that is going to be propagated
*/
batadv_gw_bandwidth_to_kbit((uint8_t)gw_bandwidth_tmp, &down, &up);
if (atomic_read(&bat_priv->gw_bandwidth) == gw_bandwidth_tmp)
if ((down_curr == down_new) && (up_curr == up_new))
return count;
batadv_gw_deselect(bat_priv);
batadv_info(net_dev,
"Changing gateway bandwidth from: '%i' to: '%ld' (propagating: %d%s/%d%s)\n",
atomic_read(&bat_priv->gw_bandwidth), gw_bandwidth_tmp,
(down > 2048 ? down / 1024 : down),
(down > 2048 ? "MBit" : "KBit"),
(up > 2048 ? up / 1024 : up),
(up > 2048 ? "MBit" : "KBit"));
"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, gw_bandwidth_tmp);
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);
}

View File

@ -26,12 +26,24 @@ enum batadv_gw_modes {
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"
void batadv_gw_bandwidth_to_kbit(uint8_t gw_class, int *down, int *up);
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_ */

View File

@ -194,22 +194,13 @@ out:
static void batadv_primary_if_update_addr(struct batadv_priv *bat_priv,
struct batadv_hard_iface *oldif)
{
struct batadv_vis_packet *vis_packet;
struct batadv_hard_iface *primary_if;
struct sk_buff *skb;
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if)
goto out;
batadv_dat_init_own_addr(bat_priv, primary_if);
skb = bat_priv->vis.my_info->skb_packet;
vis_packet = (struct batadv_vis_packet *)skb->data;
memcpy(vis_packet->vis_orig, primary_if->net_dev->dev_addr, ETH_ALEN);
memcpy(vis_packet->sender_orig,
primary_if->net_dev->dev_addr, ETH_ALEN);
batadv_bla_update_orig_address(bat_priv, primary_if, oldif);
out:
if (primary_if)

View File

@ -36,7 +36,8 @@
#include "gateway_client.h"
#include "bridge_loop_avoidance.h"
#include "distributed-arp-table.h"
#include "vis.h"
#include "unicast.h"
#include "gateway_common.h"
#include "hash.h"
#include "bat_algo.h"
#include "network-coding.h"
@ -110,8 +111,8 @@ int batadv_mesh_init(struct net_device *soft_iface)
spin_lock_init(&bat_priv->tt.roam_list_lock);
spin_lock_init(&bat_priv->tt.last_changeset_lock);
spin_lock_init(&bat_priv->gw.list_lock);
spin_lock_init(&bat_priv->vis.hash_lock);
spin_lock_init(&bat_priv->vis.list_lock);
spin_lock_init(&bat_priv->tvlv.container_list_lock);
spin_lock_init(&bat_priv->tvlv.handler_list_lock);
INIT_HLIST_HEAD(&bat_priv->forw_bat_list);
INIT_HLIST_HEAD(&bat_priv->forw_bcast_list);
@ -119,6 +120,8 @@ int batadv_mesh_init(struct net_device *soft_iface)
INIT_LIST_HEAD(&bat_priv->tt.changes_list);
INIT_LIST_HEAD(&bat_priv->tt.req_list);
INIT_LIST_HEAD(&bat_priv->tt.roam_list);
INIT_HLIST_HEAD(&bat_priv->tvlv.container_list);
INIT_HLIST_HEAD(&bat_priv->tvlv.handler_list);
ret = batadv_originator_init(bat_priv);
if (ret < 0)
@ -131,10 +134,6 @@ int batadv_mesh_init(struct net_device *soft_iface)
batadv_tt_local_add(soft_iface, soft_iface->dev_addr,
BATADV_NULL_IFINDEX);
ret = batadv_vis_init(bat_priv);
if (ret < 0)
goto err;
ret = batadv_bla_init(bat_priv);
if (ret < 0)
goto err;
@ -147,6 +146,8 @@ int batadv_mesh_init(struct net_device *soft_iface)
if (ret < 0)
goto err;
batadv_gw_init(bat_priv);
atomic_set(&bat_priv->gw.reselect, 0);
atomic_set(&bat_priv->mesh_state, BATADV_MESH_ACTIVE);
@ -165,8 +166,6 @@ void batadv_mesh_free(struct net_device *soft_iface)
batadv_purge_outstanding_packets(bat_priv, NULL);
batadv_vis_quit(bat_priv);
batadv_gw_node_purge(bat_priv);
batadv_nc_mesh_free(bat_priv);
batadv_dat_free(bat_priv);
@ -185,6 +184,8 @@ void batadv_mesh_free(struct net_device *soft_iface)
*/
batadv_originator_free(bat_priv);
batadv_gw_free(bat_priv);
free_percpu(bat_priv->bat_counters);
bat_priv->bat_counters = NULL;
@ -392,22 +393,31 @@ static void batadv_recv_handler_init(void)
for (i = 0; i < ARRAY_SIZE(batadv_rx_handler); i++)
batadv_rx_handler[i] = batadv_recv_unhandled_packet;
/* batman icmp packet */
batadv_rx_handler[BATADV_ICMP] = batadv_recv_icmp_packet;
for (i = BATADV_UNICAST_MIN; i <= BATADV_UNICAST_MAX; i++)
batadv_rx_handler[i] = batadv_recv_unhandled_unicast_packet;
/* compile time checks for struct member offsets */
BUILD_BUG_ON(offsetof(struct batadv_unicast_4addr_packet, src) != 10);
BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, dest) != 4);
BUILD_BUG_ON(offsetof(struct batadv_unicast_frag_packet, dest) != 4);
BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, dst) != 4);
BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, dst) != 4);
BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, dst) != 4);
/* broadcast packet */
batadv_rx_handler[BATADV_BCAST] = batadv_recv_bcast_packet;
/* unicast packets ... */
/* unicast with 4 addresses packet */
batadv_rx_handler[BATADV_UNICAST_4ADDR] = batadv_recv_unicast_packet;
/* unicast packet */
batadv_rx_handler[BATADV_UNICAST] = batadv_recv_unicast_packet;
/* fragmented unicast packet */
batadv_rx_handler[BATADV_UNICAST_FRAG] = batadv_recv_ucast_frag_packet;
/* broadcast packet */
batadv_rx_handler[BATADV_BCAST] = batadv_recv_bcast_packet;
/* vis packet */
batadv_rx_handler[BATADV_VIS] = batadv_recv_vis_packet;
/* Translation table query (request or response) */
batadv_rx_handler[BATADV_TT_QUERY] = batadv_recv_tt_query;
/* Roaming advertisement */
batadv_rx_handler[BATADV_ROAM_ADV] = batadv_recv_roam_adv;
/* unicast tvlv packet */
batadv_rx_handler[BATADV_UNICAST_TVLV] = batadv_recv_unicast_tvlv;
/* batman icmp packet */
batadv_rx_handler[BATADV_ICMP] = batadv_recv_icmp_packet;
}
int
@ -415,7 +425,12 @@ batadv_recv_handler_register(uint8_t packet_type,
int (*recv_handler)(struct sk_buff *,
struct batadv_hard_iface *))
{
if (batadv_rx_handler[packet_type] != &batadv_recv_unhandled_packet)
int (*curr)(struct sk_buff *,
struct batadv_hard_iface *);
curr = batadv_rx_handler[packet_type];
if ((curr != batadv_recv_unhandled_packet) &&
(curr != batadv_recv_unhandled_unicast_packet))
return -EBUSY;
batadv_rx_handler[packet_type] = recv_handler;
@ -536,6 +551,574 @@ __be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr)
return htonl(crc);
}
/**
* batadv_tvlv_handler_free_ref - decrement the tvlv handler refcounter and
* possibly free it
* @tvlv_handler: the tvlv handler to free
*/
static void
batadv_tvlv_handler_free_ref(struct batadv_tvlv_handler *tvlv_handler)
{
if (atomic_dec_and_test(&tvlv_handler->refcount))
kfree_rcu(tvlv_handler, rcu);
}
/**
* batadv_tvlv_handler_get - retrieve tvlv handler from the tvlv handler list
* based on the provided type and version (both need to match)
* @bat_priv: the bat priv with all the soft interface information
* @type: tvlv handler type to look for
* @version: tvlv handler version to look for
*
* Returns tvlv handler if found or NULL otherwise.
*/
static struct batadv_tvlv_handler
*batadv_tvlv_handler_get(struct batadv_priv *bat_priv,
uint8_t type, uint8_t version)
{
struct batadv_tvlv_handler *tvlv_handler_tmp, *tvlv_handler = NULL;
rcu_read_lock();
hlist_for_each_entry_rcu(tvlv_handler_tmp,
&bat_priv->tvlv.handler_list, list) {
if (tvlv_handler_tmp->type != type)
continue;
if (tvlv_handler_tmp->version != version)
continue;
if (!atomic_inc_not_zero(&tvlv_handler_tmp->refcount))
continue;
tvlv_handler = tvlv_handler_tmp;
break;
}
rcu_read_unlock();
return tvlv_handler;
}
/**
* batadv_tvlv_container_free_ref - decrement the tvlv container refcounter and
* possibly free it
* @tvlv_handler: the tvlv container to free
*/
static void batadv_tvlv_container_free_ref(struct batadv_tvlv_container *tvlv)
{
if (atomic_dec_and_test(&tvlv->refcount))
kfree(tvlv);
}
/**
* batadv_tvlv_container_get - retrieve tvlv container from the tvlv container
* list based on the provided type and version (both need to match)
* @bat_priv: the bat priv with all the soft interface information
* @type: tvlv container type to look for
* @version: tvlv container version to look for
*
* Has to be called with the appropriate locks being acquired
* (tvlv.container_list_lock).
*
* Returns tvlv container if found or NULL otherwise.
*/
static struct batadv_tvlv_container
*batadv_tvlv_container_get(struct batadv_priv *bat_priv,
uint8_t type, uint8_t version)
{
struct batadv_tvlv_container *tvlv_tmp, *tvlv = NULL;
hlist_for_each_entry(tvlv_tmp, &bat_priv->tvlv.container_list, list) {
if (tvlv_tmp->tvlv_hdr.type != type)
continue;
if (tvlv_tmp->tvlv_hdr.version != version)
continue;
if (!atomic_inc_not_zero(&tvlv_tmp->refcount))
continue;
tvlv = tvlv_tmp;
break;
}
return tvlv;
}
/**
* batadv_tvlv_container_list_size - calculate the size of the tvlv container
* list entries
* @bat_priv: the bat priv with all the soft interface information
*
* Has to be called with the appropriate locks being acquired
* (tvlv.container_list_lock).
*
* Returns size of all currently registered tvlv containers in bytes.
*/
static uint16_t batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
{
struct batadv_tvlv_container *tvlv;
uint16_t tvlv_len = 0;
hlist_for_each_entry(tvlv, &bat_priv->tvlv.container_list, list) {
tvlv_len += sizeof(struct batadv_tvlv_hdr);
tvlv_len += ntohs(tvlv->tvlv_hdr.len);
}
return tvlv_len;
}
/**
* batadv_tvlv_container_remove - remove tvlv container from the tvlv container
* list
* @tvlv: the to be removed tvlv container
*
* Has to be called with the appropriate locks being acquired
* (tvlv.container_list_lock).
*/
static void batadv_tvlv_container_remove(struct batadv_tvlv_container *tvlv)
{
if (!tvlv)
return;
hlist_del(&tvlv->list);
/* first call to decrement the counter, second call to free */
batadv_tvlv_container_free_ref(tvlv);
batadv_tvlv_container_free_ref(tvlv);
}
/**
* batadv_tvlv_container_unregister - unregister tvlv container based on the
* provided type and version (both need to match)
* @bat_priv: the bat priv with all the soft interface information
* @type: tvlv container type to unregister
* @version: tvlv container type to unregister
*/
void batadv_tvlv_container_unregister(struct batadv_priv *bat_priv,
uint8_t type, uint8_t version)
{
struct batadv_tvlv_container *tvlv;
spin_lock_bh(&bat_priv->tvlv.container_list_lock);
tvlv = batadv_tvlv_container_get(bat_priv, type, version);
batadv_tvlv_container_remove(tvlv);
spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
}
/**
* batadv_tvlv_container_register - register tvlv type, version and content
* to be propagated with each (primary interface) OGM
* @bat_priv: the bat priv with all the soft interface information
* @type: tvlv container type
* @version: tvlv container version
* @tvlv_value: tvlv container content
* @tvlv_value_len: tvlv container content length
*
* If a container of the same type and version was already registered the new
* content is going to replace the old one.
*/
void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
uint8_t type, uint8_t version,
void *tvlv_value, uint16_t tvlv_value_len)
{
struct batadv_tvlv_container *tvlv_old, *tvlv_new;
if (!tvlv_value)
tvlv_value_len = 0;
tvlv_new = kzalloc(sizeof(*tvlv_new) + tvlv_value_len, GFP_ATOMIC);
if (!tvlv_new)
return;
tvlv_new->tvlv_hdr.version = version;
tvlv_new->tvlv_hdr.type = type;
tvlv_new->tvlv_hdr.len = htons(tvlv_value_len);
memcpy(tvlv_new + 1, tvlv_value, ntohs(tvlv_new->tvlv_hdr.len));
INIT_HLIST_NODE(&tvlv_new->list);
atomic_set(&tvlv_new->refcount, 1);
spin_lock_bh(&bat_priv->tvlv.container_list_lock);
tvlv_old = batadv_tvlv_container_get(bat_priv, type, version);
batadv_tvlv_container_remove(tvlv_old);
hlist_add_head(&tvlv_new->list, &bat_priv->tvlv.container_list);
spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
}
/**
* batadv_tvlv_realloc_packet_buff - reallocate packet buffer to accomodate
* requested packet size
* @packet_buff: packet buffer
* @packet_buff_len: packet buffer size
* @packet_min_len: requested packet minimum size
* @additional_packet_len: requested additional packet size on top of minimum
* size
*
* Returns true of the packet buffer could be changed to the requested size,
* false otherwise.
*/
static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff,
int *packet_buff_len,
int min_packet_len,
int additional_packet_len)
{
unsigned char *new_buff;
new_buff = kmalloc(min_packet_len + additional_packet_len, GFP_ATOMIC);
/* keep old buffer if kmalloc should fail */
if (new_buff) {
memcpy(new_buff, *packet_buff, min_packet_len);
kfree(*packet_buff);
*packet_buff = new_buff;
*packet_buff_len = min_packet_len + additional_packet_len;
return true;
}
return false;
}
/**
* batadv_tvlv_container_ogm_append - append tvlv container content to given
* OGM packet buffer
* @bat_priv: the bat priv with all the soft interface information
* @packet_buff: ogm packet buffer
* @packet_buff_len: ogm packet buffer size including ogm header and tvlv
* content
* @packet_min_len: ogm header size to be preserved for the OGM itself
*
* The ogm packet might be enlarged or shrunk depending on the current size
* and the size of the to-be-appended tvlv containers.
*
* Returns size of all appended tvlv containers in bytes.
*/
uint16_t batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
unsigned char **packet_buff,
int *packet_buff_len,
int packet_min_len)
{
struct batadv_tvlv_container *tvlv;
struct batadv_tvlv_hdr *tvlv_hdr;
uint16_t tvlv_value_len;
void *tvlv_value;
bool ret;
spin_lock_bh(&bat_priv->tvlv.container_list_lock);
tvlv_value_len = batadv_tvlv_container_list_size(bat_priv);
ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
packet_min_len, tvlv_value_len);
if (!ret)
goto end;
if (!tvlv_value_len)
goto end;
tvlv_value = (*packet_buff) + packet_min_len;
hlist_for_each_entry(tvlv, &bat_priv->tvlv.container_list, list) {
tvlv_hdr = tvlv_value;
tvlv_hdr->type = tvlv->tvlv_hdr.type;
tvlv_hdr->version = tvlv->tvlv_hdr.version;
tvlv_hdr->len = tvlv->tvlv_hdr.len;
tvlv_value = tvlv_hdr + 1;
memcpy(tvlv_value, tvlv + 1, ntohs(tvlv->tvlv_hdr.len));
tvlv_value = (uint8_t *)tvlv_value + ntohs(tvlv->tvlv_hdr.len);
}
end:
spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
return tvlv_value_len;
}
/**
* batadv_tvlv_call_handler - parse the given tvlv buffer to call the
* appropriate handlers
* @bat_priv: the bat priv with all the soft interface information
* @tvlv_handler: tvlv callback function handling the tvlv content
* @ogm_source: flag indicating wether the tvlv is an ogm or a unicast packet
* @orig_node: orig node emitting the ogm packet
* @src: source mac address of the unicast packet
* @dst: destination mac address of the unicast packet
* @tvlv_value: tvlv content
* @tvlv_value_len: tvlv content length
*
* Returns success if handler was not found or the return value of the handler
* callback.
*/
static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
struct batadv_tvlv_handler *tvlv_handler,
bool ogm_source,
struct batadv_orig_node *orig_node,
uint8_t *src, uint8_t *dst,
void *tvlv_value, uint16_t tvlv_value_len)
{
if (!tvlv_handler)
return NET_RX_SUCCESS;
if (ogm_source) {
if (!tvlv_handler->ogm_handler)
return NET_RX_SUCCESS;
if (!orig_node)
return NET_RX_SUCCESS;
tvlv_handler->ogm_handler(bat_priv, orig_node,
BATADV_NO_FLAGS,
tvlv_value, tvlv_value_len);
tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED;
} else {
if (!src)
return NET_RX_SUCCESS;
if (!dst)
return NET_RX_SUCCESS;
if (!tvlv_handler->unicast_handler)
return NET_RX_SUCCESS;
return tvlv_handler->unicast_handler(bat_priv, src,
dst, tvlv_value,
tvlv_value_len);
}
return NET_RX_SUCCESS;
}
/**
* batadv_tvlv_containers_process - parse the given tvlv buffer to call the
* appropriate handlers
* @bat_priv: the bat priv with all the soft interface information
* @ogm_source: flag indicating wether the tvlv is an ogm or a unicast packet
* @orig_node: orig node emitting the ogm packet
* @src: source mac address of the unicast packet
* @dst: destination mac address of the unicast packet
* @tvlv_value: tvlv content
* @tvlv_value_len: tvlv content length
*
* Returns success when processing an OGM or the return value of all called
* handler callbacks.
*/
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_value, uint16_t tvlv_value_len)
{
struct batadv_tvlv_handler *tvlv_handler;
struct batadv_tvlv_hdr *tvlv_hdr;
uint16_t tvlv_value_cont_len;
uint8_t cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
int ret = NET_RX_SUCCESS;
while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
tvlv_hdr = tvlv_value;
tvlv_value_cont_len = ntohs(tvlv_hdr->len);
tvlv_value = tvlv_hdr + 1;
tvlv_value_len -= sizeof(*tvlv_hdr);
if (tvlv_value_cont_len > tvlv_value_len)
break;
tvlv_handler = batadv_tvlv_handler_get(bat_priv,
tvlv_hdr->type,
tvlv_hdr->version);
ret |= batadv_tvlv_call_handler(bat_priv, tvlv_handler,
ogm_source, orig_node,
src, dst, tvlv_value,
tvlv_value_cont_len);
if (tvlv_handler)
batadv_tvlv_handler_free_ref(tvlv_handler);
tvlv_value = (uint8_t *)tvlv_value + tvlv_value_cont_len;
tvlv_value_len -= tvlv_value_cont_len;
}
if (!ogm_source)
return ret;
rcu_read_lock();
hlist_for_each_entry_rcu(tvlv_handler,
&bat_priv->tvlv.handler_list, list) {
if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) &&
!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED))
tvlv_handler->ogm_handler(bat_priv, orig_node,
cifnotfound, NULL, 0);
tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED;
}
rcu_read_unlock();
return NET_RX_SUCCESS;
}
/**
* batadv_tvlv_ogm_receive - process an incoming ogm and call the appropriate
* handlers
* @bat_priv: the bat priv with all the soft interface information
* @batadv_ogm_packet: ogm packet containing the tvlv containers
* @orig_node: orig node emitting the ogm packet
*/
void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
struct batadv_ogm_packet *batadv_ogm_packet,
struct batadv_orig_node *orig_node)
{
void *tvlv_value;
uint16_t tvlv_value_len;
if (!batadv_ogm_packet)
return;
tvlv_value_len = ntohs(batadv_ogm_packet->tvlv_len);
if (!tvlv_value_len)
return;
tvlv_value = batadv_ogm_packet + 1;
batadv_tvlv_containers_process(bat_priv, true, orig_node, NULL, NULL,
tvlv_value, tvlv_value_len);
}
/**
* batadv_tvlv_handler_register - register tvlv handler based on the provided
* type and version (both need to match) for ogm tvlv payload and/or unicast
* payload
* @bat_priv: the bat priv with all the soft interface information
* @optr: ogm tvlv handler callback function. This function receives the orig
* node, flags and the tvlv content as argument to process.
* @uptr: unicast tvlv handler callback function. This function receives the
* source & destination of the unicast packet as well as the tvlv content
* to process.
* @type: tvlv handler type to be registered
* @version: tvlv handler version to be registered
* @flags: flags to enable or disable TVLV API behavior
*/
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)
{
struct batadv_tvlv_handler *tvlv_handler;
tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version);
if (tvlv_handler) {
batadv_tvlv_handler_free_ref(tvlv_handler);
return;
}
tvlv_handler = kzalloc(sizeof(*tvlv_handler), GFP_ATOMIC);
if (!tvlv_handler)
return;
tvlv_handler->ogm_handler = optr;
tvlv_handler->unicast_handler = uptr;
tvlv_handler->type = type;
tvlv_handler->version = version;
tvlv_handler->flags = flags;
atomic_set(&tvlv_handler->refcount, 1);
INIT_HLIST_NODE(&tvlv_handler->list);
spin_lock_bh(&bat_priv->tvlv.handler_list_lock);
hlist_add_head_rcu(&tvlv_handler->list, &bat_priv->tvlv.handler_list);
spin_unlock_bh(&bat_priv->tvlv.handler_list_lock);
}
/**
* batadv_tvlv_handler_unregister - unregister tvlv handler based on the
* provided type and version (both need to match)
* @bat_priv: the bat priv with all the soft interface information
* @type: tvlv handler type to be unregistered
* @version: tvlv handler version to be unregistered
*/
void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv,
uint8_t type, uint8_t version)
{
struct batadv_tvlv_handler *tvlv_handler;
tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version);
if (!tvlv_handler)
return;
batadv_tvlv_handler_free_ref(tvlv_handler);
spin_lock_bh(&bat_priv->tvlv.handler_list_lock);
hlist_del_rcu(&tvlv_handler->list);
spin_unlock_bh(&bat_priv->tvlv.handler_list_lock);
batadv_tvlv_handler_free_ref(tvlv_handler);
}
/**
* batadv_tvlv_unicast_send - send a unicast packet with tvlv payload to the
* specified host
* @bat_priv: the bat priv with all the soft interface information
* @src: source mac address of the unicast packet
* @dst: destination mac address of the unicast packet
* @type: tvlv type
* @version: tvlv version
* @tvlv_value: tvlv content
* @tvlv_value_len: tvlv content length
*/
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)
{
struct batadv_unicast_tvlv_packet *unicast_tvlv_packet;
struct batadv_tvlv_hdr *tvlv_hdr;
struct batadv_orig_node *orig_node;
struct sk_buff *skb = NULL;
unsigned char *tvlv_buff;
unsigned int tvlv_len;
ssize_t hdr_len = sizeof(*unicast_tvlv_packet);
bool ret = false;
orig_node = batadv_orig_hash_find(bat_priv, dst);
if (!orig_node)
goto out;
tvlv_len = sizeof(*tvlv_hdr) + tvlv_value_len;
skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + hdr_len + tvlv_len);
if (!skb)
goto out;
skb->priority = TC_PRIO_CONTROL;
skb_reserve(skb, ETH_HLEN);
tvlv_buff = skb_put(skb, sizeof(*unicast_tvlv_packet) + tvlv_len);
unicast_tvlv_packet = (struct batadv_unicast_tvlv_packet *)tvlv_buff;
unicast_tvlv_packet->header.packet_type = BATADV_UNICAST_TVLV;
unicast_tvlv_packet->header.version = BATADV_COMPAT_VERSION;
unicast_tvlv_packet->header.ttl = BATADV_TTL;
unicast_tvlv_packet->reserved = 0;
unicast_tvlv_packet->tvlv_len = htons(tvlv_len);
unicast_tvlv_packet->align = 0;
memcpy(unicast_tvlv_packet->src, src, ETH_ALEN);
memcpy(unicast_tvlv_packet->dst, dst, ETH_ALEN);
tvlv_buff = (unsigned char *)(unicast_tvlv_packet + 1);
tvlv_hdr = (struct batadv_tvlv_hdr *)tvlv_buff;
tvlv_hdr->version = version;
tvlv_hdr->type = type;
tvlv_hdr->len = htons(tvlv_value_len);
tvlv_buff += sizeof(*tvlv_hdr);
memcpy(tvlv_buff, tvlv_value, tvlv_value_len);
if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
ret = true;
out:
if (skb && !ret)
kfree_skb(skb);
if (orig_node)
batadv_orig_node_free_ref(orig_node);
}
static int batadv_param_set_ra(const char *val, const struct kernel_param *kp)
{
struct batadv_algo_ops *bat_algo_ops;

View File

@ -86,8 +86,6 @@
/* numbers of originator to contact for any PUT/GET DHT operation */
#define BATADV_DAT_CANDIDATES_NUM 3
#define BATADV_VIS_INTERVAL 5000 /* 5 seconds */
/* how much worse secondary interfaces may be to be considered as bonding
* candidates
*/
@ -326,4 +324,39 @@ static inline uint64_t batadv_sum_counter(struct batadv_priv *bat_priv,
*/
#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);
#endif /* _NET_BATMAN_ADV_MAIN_H_ */

View File

@ -58,6 +58,59 @@ static void batadv_nc_start_timer(struct batadv_priv *bat_priv)
msecs_to_jiffies(10));
}
/**
* batadv_nc_tvlv_container_update - update the network coding tvlv container
* after network coding setting change
* @bat_priv: the bat priv with all the soft interface information
*/
static void batadv_nc_tvlv_container_update(struct batadv_priv *bat_priv)
{
char nc_mode;
nc_mode = atomic_read(&bat_priv->network_coding);
switch (nc_mode) {
case 0:
batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_NC, 1);
break;
case 1:
batadv_tvlv_container_register(bat_priv, BATADV_TVLV_NC, 1,
NULL, 0);
break;
}
}
/**
* batadv_nc_status_update - update the network coding tvlv container after
* network coding setting change
* @net_dev: the soft interface net device
*/
void batadv_nc_status_update(struct net_device *net_dev)
{
struct batadv_priv *bat_priv = netdev_priv(net_dev);
batadv_nc_tvlv_container_update(bat_priv);
}
/**
* batadv_nc_tvlv_ogm_handler_v1 - process incoming nc 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_nc_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)
{
if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)
orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_NC;
else
orig->capabilities |= BATADV_ORIG_CAPA_HAS_NC;
}
/**
* batadv_nc_mesh_init - initialise coding hash table and start house keeping
* @bat_priv: the bat priv with all the soft interface information
@ -87,6 +140,10 @@ int batadv_nc_mesh_init(struct batadv_priv *bat_priv)
INIT_DELAYED_WORK(&bat_priv->nc.work, batadv_nc_worker);
batadv_nc_start_timer(bat_priv);
batadv_tvlv_handler_register(bat_priv, batadv_nc_tvlv_ogm_handler_v1,
NULL, BATADV_TVLV_NC, 1,
BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
batadv_nc_tvlv_container_update(bat_priv);
return 0;
err:
@ -802,6 +859,10 @@ void batadv_nc_update_nc_node(struct batadv_priv *bat_priv,
if (!atomic_read(&bat_priv->network_coding))
goto out;
/* check if orig node is network coding enabled */
if (!(orig_node->capabilities & BATADV_ORIG_CAPA_HAS_NC))
goto out;
/* accept ogms from 'good' neighbors and single hop neighbors */
if (!batadv_can_nc_with_orig(bat_priv, orig_node, ogm_packet) &&
!is_single_hop_neigh)
@ -1735,6 +1796,8 @@ free_nc_packet:
*/
void batadv_nc_mesh_free(struct batadv_priv *bat_priv)
{
batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_NC, 1);
batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_NC, 1);
cancel_delayed_work_sync(&bat_priv->nc.work);
batadv_nc_purge_paths(bat_priv, bat_priv->nc.coding_hash, NULL);

View File

@ -22,6 +22,7 @@
#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);
@ -47,6 +48,10 @@ 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;

View File

@ -388,9 +388,7 @@ static void _batadv_purge_orig(struct batadv_priv *bat_priv)
hlist_for_each_entry_safe(orig_node, node_tmp,
head, hash_entry) {
if (batadv_purge_orig_node(bat_priv, orig_node)) {
if (orig_node->gw_flags)
batadv_gw_node_delete(bat_priv,
orig_node);
batadv_gw_node_delete(bat_priv, orig_node);
hlist_del_rcu(&orig_node->hash_entry);
batadv_orig_node_free_ref(orig_node);
continue;

View File

@ -20,17 +20,34 @@
#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 {
BATADV_IV_OGM = 0x01,
BATADV_ICMP = 0x02,
BATADV_UNICAST = 0x03,
BATADV_BCAST = 0x04,
BATADV_VIS = 0x05,
BATADV_UNICAST_FRAG = 0x06,
BATADV_TT_QUERY = 0x07,
BATADV_ROAM_ADV = 0x08,
BATADV_UNICAST_4ADDR = 0x09,
BATADV_CODED = 0x0a,
/* 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 */
};
/**
@ -48,13 +65,21 @@ enum batadv_subtype {
};
/* this file is included by batctl which needs these defines */
#define BATADV_COMPAT_VERSION 14
#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(3),
BATADV_PRIMARIES_FIRST_HOP = BIT(4),
BATADV_VIS_SERVER = BIT(5),
BATADV_DIRECTLINK = BIT(6),
BATADV_NOT_BEST_NEXT_HOP = BIT(0),
BATADV_PRIMARIES_FIRST_HOP = BIT(1),
BATADV_DIRECTLINK = BIT(2),
};
/* ICMP message types */
@ -66,29 +91,27 @@ enum batadv_icmp_packettype {
BATADV_PARAMETER_PROBLEM = 12,
};
/* vis defines */
enum batadv_vis_packettype {
BATADV_VIS_TYPE_SERVER_SYNC = 0,
BATADV_VIS_TYPE_CLIENT_UPDATE = 1,
};
/* fragmentation defines */
enum batadv_unicast_frag_flags {
BATADV_UNI_FRAG_HEAD = BIT(0),
BATADV_UNI_FRAG_LARGETAIL = BIT(1),
};
/* TT_QUERY subtypes */
#define BATADV_TT_QUERY_TYPE_MASK 0x3
/* tt data subtypes */
#define BATADV_TT_DATA_TYPE_MASK 0x0F
enum batadv_tt_query_packettype {
BATADV_TT_REQUEST = 0,
BATADV_TT_RESPONSE = 1,
};
/* TT_QUERY flags */
enum batadv_tt_query_flags {
BATADV_TT_FULL_TABLE = BIT(2),
/**
* 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),
};
/* BATADV_TT_CLIENT flags.
@ -99,10 +122,10 @@ enum batadv_tt_client_flags {
BATADV_TT_CLIENT_DEL = BIT(0),
BATADV_TT_CLIENT_ROAM = BIT(1),
BATADV_TT_CLIENT_WIFI = BIT(2),
BATADV_TT_CLIENT_TEMP = BIT(3),
BATADV_TT_CLIENT_NOPURGE = BIT(8),
BATADV_TT_CLIENT_NEW = BIT(9),
BATADV_TT_CLIENT_PENDING = BIT(10),
BATADV_TT_CLIENT_TEMP = BIT(11),
};
/* claim frame types for the bridge loop avoidance */
@ -113,6 +136,22 @@ enum batadv_bla_claimframe {
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
*/
enum batadv_tvlv_type {
BATADV_TVLV_GW = 0x01,
BATADV_TVLV_DAT = 0x02,
BATADV_TVLV_NC = 0x03,
BATADV_TVLV_TT = 0x04,
BATADV_TVLV_ROAM = 0x05,
};
/* the destination hardware field in the ARP frame is used to
* transport the claim type and the group id
*/
@ -131,18 +170,25 @@ struct batadv_header {
*/
};
/**
* struct batadv_ogm_packet - ogm (routing protocol) packet
* @header: common batman packet 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 {
struct batadv_header header;
uint8_t flags; /* 0x40: DIRECTLINK flag, 0x20 VIS_SERVER flag... */
uint8_t flags;
__be32 seqno;
uint8_t orig[ETH_ALEN];
uint8_t prev_sender[ETH_ALEN];
uint8_t gw_flags; /* flags related to gateway class */
uint8_t reserved;
uint8_t tq;
uint8_t tt_num_changes;
uint8_t ttvn; /* translation table version number */
__be16 tt_crc;
} __packed;
__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)
@ -231,54 +277,6 @@ struct batadv_bcast_packet {
#pragma pack()
struct batadv_vis_packet {
struct batadv_header header;
uint8_t vis_type; /* which type of vis-participant sent this? */
__be32 seqno; /* sequence number */
uint8_t entries; /* number of entries behind this struct */
uint8_t reserved;
uint8_t vis_orig[ETH_ALEN]; /* originator reporting its neighbors */
uint8_t target_orig[ETH_ALEN]; /* who should receive this packet */
uint8_t sender_orig[ETH_ALEN]; /* who sent or forwarded this packet */
};
struct batadv_tt_query_packet {
struct batadv_header header;
/* the flag field is a combination of:
* - TT_REQUEST or TT_RESPONSE
* - TT_FULL_TABLE
*/
uint8_t flags;
uint8_t dst[ETH_ALEN];
uint8_t src[ETH_ALEN];
/* the ttvn field is:
* if TT_REQUEST: ttvn that triggered the
* request
* if TT_RESPONSE: new ttvn for the src
* orig_node
*/
uint8_t ttvn;
/* tt_data field is:
* if TT_REQUEST: crc associated with the
* ttvn
* if TT_RESPONSE: table_size
*/
__be16 tt_data;
} __packed;
struct batadv_roam_adv_packet {
struct batadv_header header;
uint8_t reserved;
uint8_t dst[ETH_ALEN];
uint8_t src[ETH_ALEN];
uint8_t client[ETH_ALEN];
} __packed;
struct batadv_tt_change {
uint8_t flags;
uint8_t addr[ETH_ALEN];
} __packed;
/**
* struct batadv_coded_packet - network coded packet
* @header: common batman packet header and ttl of first included packet
@ -311,4 +309,82 @@ struct batadv_coded_packet {
__be16 coded_len;
};
/**
* struct batadv_unicast_tvlv - generic unicast packet with tvlv payload
* @header: common batman packet 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 {
struct batadv_header header;
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
* @reserved: field reserved for future use
* @crc: crc32 checksum of the local translation table
*/
struct batadv_tvlv_tt_data {
uint8_t flags;
uint8_t ttvn;
uint16_t reserved;
__be32 crc;
};
/**
* 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
* @addr: mac address of non-mesh client that triggered this tt change
*/
struct batadv_tvlv_tt_change {
uint8_t flags;
uint8_t reserved;
uint8_t addr[ETH_ALEN];
};
/**
* struct batadv_tvlv_roam_adv - roaming advertisement
* @client: mac address of roaming client
* @reserved: field reserved for future use
*/
struct batadv_tvlv_roam_adv {
uint8_t client[ETH_ALEN];
uint16_t reserved;
};
#endif /* _NET_BATMAN_ADV_PACKET_H_ */

View File

@ -25,7 +25,6 @@
#include "icmp_socket.h"
#include "translation-table.h"
#include "originator.h"
#include "vis.h"
#include "unicast.h"
#include "bridge_loop_avoidance.h"
#include "distributed-arp-table.h"
@ -557,126 +556,6 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv,
return 0;
}
int batadv_recv_tt_query(struct sk_buff *skb, struct batadv_hard_iface *recv_if)
{
struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
struct batadv_tt_query_packet *tt_query;
uint16_t tt_size;
int hdr_size = sizeof(*tt_query);
char tt_flag;
size_t packet_size;
if (batadv_check_unicast_packet(bat_priv, skb, hdr_size) < 0)
return NET_RX_DROP;
/* I could need to modify it */
if (skb_cow(skb, sizeof(struct batadv_tt_query_packet)) < 0)
goto out;
tt_query = (struct batadv_tt_query_packet *)skb->data;
switch (tt_query->flags & BATADV_TT_QUERY_TYPE_MASK) {
case BATADV_TT_REQUEST:
batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_RX);
/* If we cannot provide an answer the tt_request is
* forwarded
*/
if (!batadv_send_tt_response(bat_priv, tt_query)) {
if (tt_query->flags & BATADV_TT_FULL_TABLE)
tt_flag = 'F';
else
tt_flag = '.';
batadv_dbg(BATADV_DBG_TT, bat_priv,
"Routing TT_REQUEST to %pM [%c]\n",
tt_query->dst,
tt_flag);
return batadv_route_unicast_packet(skb, recv_if);
}
break;
case BATADV_TT_RESPONSE:
batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_RX);
if (batadv_is_my_mac(bat_priv, tt_query->dst)) {
/* packet needs to be linearized to access the TT
* changes
*/
if (skb_linearize(skb) < 0)
goto out;
/* skb_linearize() possibly changed skb->data */
tt_query = (struct batadv_tt_query_packet *)skb->data;
tt_size = batadv_tt_len(ntohs(tt_query->tt_data));
/* Ensure we have all the claimed data */
packet_size = sizeof(struct batadv_tt_query_packet);
packet_size += tt_size;
if (unlikely(skb_headlen(skb) < packet_size))
goto out;
batadv_handle_tt_response(bat_priv, tt_query);
} else {
if (tt_query->flags & BATADV_TT_FULL_TABLE)
tt_flag = 'F';
else
tt_flag = '.';
batadv_dbg(BATADV_DBG_TT, bat_priv,
"Routing TT_RESPONSE to %pM [%c]\n",
tt_query->dst,
tt_flag);
return batadv_route_unicast_packet(skb, recv_if);
}
break;
}
out:
/* returning NET_RX_DROP will make the caller function kfree the skb */
return NET_RX_DROP;
}
int batadv_recv_roam_adv(struct sk_buff *skb, struct batadv_hard_iface *recv_if)
{
struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
struct batadv_roam_adv_packet *roam_adv_packet;
struct batadv_orig_node *orig_node;
if (batadv_check_unicast_packet(bat_priv, skb,
sizeof(*roam_adv_packet)) < 0)
goto out;
batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_RX);
roam_adv_packet = (struct batadv_roam_adv_packet *)skb->data;
if (!batadv_is_my_mac(bat_priv, roam_adv_packet->dst))
return batadv_route_unicast_packet(skb, recv_if);
/* check if it is a backbone gateway. we don't accept
* roaming advertisement from it, as it has the same
* entries as we have.
*/
if (batadv_bla_is_backbone_gw_orig(bat_priv, roam_adv_packet->src))
goto out;
orig_node = batadv_orig_hash_find(bat_priv, roam_adv_packet->src);
if (!orig_node)
goto out;
batadv_dbg(BATADV_DBG_TT, bat_priv,
"Received ROAMING_ADV from %pM (client %pM)\n",
roam_adv_packet->src, roam_adv_packet->client);
batadv_tt_global_add(bat_priv, orig_node, roam_adv_packet->client,
BATADV_TT_CLIENT_ROAM,
atomic_read(&orig_node->last_ttvn) + 1);
batadv_orig_node_free_ref(orig_node);
out:
/* returning NET_RX_DROP will make the caller function kfree the skb */
return NET_RX_DROP;
}
/* find a suitable router for this originator, and use
* bonding if possible. increases the found neighbors
* refcount.
@ -1032,6 +911,34 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
return 1;
}
/**
* batadv_recv_unhandled_unicast_packet - receive and process packets which
* are in the unicast number space but not yet known to the implementation
* @skb: unicast tvlv packet to process
* @recv_if: pointer to interface this packet was received on
*
* Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
* otherwise.
*/
int batadv_recv_unhandled_unicast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if)
{
struct batadv_unicast_packet *unicast_packet;
struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
int check, hdr_size = sizeof(*unicast_packet);
check = batadv_check_unicast_packet(bat_priv, skb, hdr_size);
if (check < 0)
return NET_RX_DROP;
/* we don't know about this type, drop it. */
unicast_packet = (struct batadv_unicast_packet *)skb->data;
if (batadv_is_my_mac(bat_priv, unicast_packet->dest))
return NET_RX_DROP;
return batadv_route_unicast_packet(skb, recv_if);
}
int batadv_recv_unicast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if)
{
@ -1139,6 +1046,54 @@ rx_success:
return batadv_route_unicast_packet(skb, recv_if);
}
/**
* batadv_recv_unicast_tvlv - receive and process unicast tvlv packets
* @skb: unicast tvlv packet to process
* @recv_if: pointer to interface this packet was received on
* @dst_addr: the payload destination
*
* Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
* otherwise.
*/
int batadv_recv_unicast_tvlv(struct sk_buff *skb,
struct batadv_hard_iface *recv_if)
{
struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
struct batadv_unicast_tvlv_packet *unicast_tvlv_packet;
unsigned char *tvlv_buff;
uint16_t tvlv_buff_len;
int hdr_size = sizeof(*unicast_tvlv_packet);
int ret = NET_RX_DROP;
if (batadv_check_unicast_packet(bat_priv, skb, hdr_size) < 0)
return NET_RX_DROP;
/* the header is likely to be modified while forwarding */
if (skb_cow(skb, hdr_size) < 0)
return NET_RX_DROP;
/* packet needs to be linearized to access the tvlv content */
if (skb_linearize(skb) < 0)
return NET_RX_DROP;
unicast_tvlv_packet = (struct batadv_unicast_tvlv_packet *)skb->data;
tvlv_buff = (unsigned char *)(skb->data + hdr_size);
tvlv_buff_len = ntohs(unicast_tvlv_packet->tvlv_len);
if (tvlv_buff_len > skb->len - hdr_size)
return NET_RX_DROP;
ret = batadv_tvlv_containers_process(bat_priv, false, NULL,
unicast_tvlv_packet->src,
unicast_tvlv_packet->dst,
tvlv_buff, tvlv_buff_len);
if (ret != NET_RX_SUCCESS)
ret = batadv_route_unicast_packet(skb, recv_if);
return ret;
}
int batadv_recv_bcast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if)
@ -1240,53 +1195,3 @@ out:
batadv_orig_node_free_ref(orig_node);
return ret;
}
int batadv_recv_vis_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if)
{
struct batadv_vis_packet *vis_packet;
struct ethhdr *ethhdr;
struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
int hdr_size = sizeof(*vis_packet);
/* keep skb linear */
if (skb_linearize(skb) < 0)
return NET_RX_DROP;
if (unlikely(!pskb_may_pull(skb, hdr_size)))
return NET_RX_DROP;
vis_packet = (struct batadv_vis_packet *)skb->data;
ethhdr = eth_hdr(skb);
/* not for me */
if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest))
return NET_RX_DROP;
/* ignore own packets */
if (batadv_is_my_mac(bat_priv, vis_packet->vis_orig))
return NET_RX_DROP;
if (batadv_is_my_mac(bat_priv, vis_packet->sender_orig))
return NET_RX_DROP;
switch (vis_packet->vis_type) {
case BATADV_VIS_TYPE_SERVER_SYNC:
batadv_receive_server_sync_packet(bat_priv, vis_packet,
skb_headlen(skb));
break;
case BATADV_VIS_TYPE_CLIENT_UPDATE:
batadv_receive_client_update_packet(bat_priv, vis_packet,
skb_headlen(skb));
break;
default: /* ignore unknown packet */
break;
}
/* We take a copy of the data in the packet, so we should
* always free the skbuf.
*/
return NET_RX_DROP;
}

View File

@ -34,12 +34,14 @@ int batadv_recv_ucast_frag_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
int batadv_recv_bcast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
int batadv_recv_vis_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,

View File

@ -24,7 +24,6 @@
#include "translation-table.h"
#include "soft-interface.h"
#include "hard-interface.h"
#include "vis.h"
#include "gateway_common.h"
#include "originator.h"
#include "network-coding.h"

View File

@ -469,10 +469,10 @@ static int batadv_softif_init_late(struct net_device *dev)
atomic_set(&bat_priv->distributed_arp_table, 1);
#endif
atomic_set(&bat_priv->ap_isolation, 0);
atomic_set(&bat_priv->vis_mode, BATADV_VIS_TYPE_CLIENT_UPDATE);
atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF);
atomic_set(&bat_priv->gw_sel_class, 20);
atomic_set(&bat_priv->gw_bandwidth, 41);
atomic_set(&bat_priv->gw.bandwidth_down, 100);
atomic_set(&bat_priv->gw.bandwidth_up, 20);
atomic_set(&bat_priv->orig_interval, 1000);
atomic_set(&bat_priv->hop_penalty, 30);
#ifdef CONFIG_BATMAN_ADV_DEBUG

View File

@ -21,11 +21,11 @@
#include "sysfs.h"
#include "translation-table.h"
#include "distributed-arp-table.h"
#include "network-coding.h"
#include "originator.h"
#include "hard-interface.h"
#include "gateway_common.h"
#include "gateway_client.h"
#include "vis.h"
static struct net_device *batadv_kobj_to_netdev(struct kobject *obj)
{
@ -230,74 +230,6 @@ __batadv_store_uint_attr(const char *buff, size_t count,
return ret;
}
static ssize_t batadv_show_vis_mode(struct kobject *kobj,
struct attribute *attr, char *buff)
{
struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
int vis_mode = atomic_read(&bat_priv->vis_mode);
const char *mode;
if (vis_mode == BATADV_VIS_TYPE_CLIENT_UPDATE)
mode = "client";
else
mode = "server";
return sprintf(buff, "%s\n", mode);
}
static ssize_t batadv_store_vis_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);
unsigned long val;
int ret, vis_mode_tmp = -1;
const char *old_mode, *new_mode;
ret = kstrtoul(buff, 10, &val);
if (((count == 2) && (!ret) &&
(val == BATADV_VIS_TYPE_CLIENT_UPDATE)) ||
(strncmp(buff, "client", 6) == 0) ||
(strncmp(buff, "off", 3) == 0))
vis_mode_tmp = BATADV_VIS_TYPE_CLIENT_UPDATE;
if (((count == 2) && (!ret) &&
(val == BATADV_VIS_TYPE_SERVER_SYNC)) ||
(strncmp(buff, "server", 6) == 0))
vis_mode_tmp = BATADV_VIS_TYPE_SERVER_SYNC;
if (vis_mode_tmp < 0) {
if (buff[count - 1] == '\n')
buff[count - 1] = '\0';
batadv_info(net_dev,
"Invalid parameter for 'vis mode' setting received: %s\n",
buff);
return -EINVAL;
}
if (atomic_read(&bat_priv->vis_mode) == vis_mode_tmp)
return count;
if (atomic_read(&bat_priv->vis_mode) == BATADV_VIS_TYPE_CLIENT_UPDATE)
old_mode = "client";
else
old_mode = "server";
if (vis_mode_tmp == BATADV_VIS_TYPE_CLIENT_UPDATE)
new_mode = "client";
else
new_mode = "server";
batadv_info(net_dev, "Changing vis mode from: %s to: %s\n", old_mode,
new_mode);
atomic_set(&bat_priv->vis_mode, (unsigned int)vis_mode_tmp);
return count;
}
static ssize_t batadv_show_bat_algo(struct kobject *kobj,
struct attribute *attr, char *buff)
{
@ -390,6 +322,7 @@ static ssize_t batadv_store_gw_mode(struct kobject *kobj,
*/
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;
}
@ -397,15 +330,13 @@ 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);
int down, up;
int gw_bandwidth = atomic_read(&bat_priv->gw_bandwidth);
uint32_t down, up;
batadv_gw_bandwidth_to_kbit(gw_bandwidth, &down, &up);
return sprintf(buff, "%i%s/%i%s\n",
(down > 2048 ? down / 1024 : down),
(down > 2048 ? "MBit" : "KBit"),
(up > 2048 ? up / 1024 : up),
(up > 2048 ? "MBit" : "KBit"));
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,
@ -426,12 +357,11 @@ BATADV_ATTR_SIF_BOOL(bonding, S_IRUGO | S_IWUSR, NULL);
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, NULL);
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);
BATADV_ATTR_SIF_BOOL(ap_isolation, S_IRUGO | S_IWUSR, NULL);
static BATADV_ATTR(vis_mode, S_IRUGO | S_IWUSR, batadv_show_vis_mode,
batadv_store_vis_mode);
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);
@ -447,7 +377,8 @@ static BATADV_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, batadv_show_gw_bwidth,
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, NULL);
BATADV_ATTR_SIF_BOOL(network_coding, S_IRUGO | S_IWUSR,
batadv_nc_status_update);
#endif
static struct batadv_attribute *batadv_mesh_attrs[] = {
@ -461,7 +392,6 @@ static struct batadv_attribute *batadv_mesh_attrs[] = {
#endif
&batadv_attr_fragmentation,
&batadv_attr_ap_isolation,
&batadv_attr_vis_mode,
&batadv_attr_routing_algo,
&batadv_attr_gw_mode,
&batadv_attr_orig_interval,

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,6 @@
#ifndef _NET_BATMAN_ADV_TRANSLATION_TABLE_H_
#define _NET_BATMAN_ADV_TRANSLATION_TABLE_H_
int batadv_tt_len(int changes_num);
int batadv_tt_init(struct batadv_priv *bat_priv);
void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
int ifindex);
@ -43,20 +42,10 @@ struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
const uint8_t *src,
const uint8_t *addr);
void batadv_tt_free(struct batadv_priv *bat_priv);
bool batadv_send_tt_response(struct batadv_priv *bat_priv,
struct batadv_tt_query_packet *tt_request);
bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr);
void batadv_handle_tt_response(struct batadv_priv *bat_priv,
struct batadv_tt_query_packet *tt_response);
bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, uint8_t *src,
uint8_t *dst);
void batadv_tt_update_orig(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
const unsigned char *tt_buff, uint8_t tt_num_changes,
uint8_t ttvn, uint16_t tt_crc);
int batadv_tt_append_diff(struct batadv_priv *bat_priv,
unsigned char **packet_buff, int *packet_buff_len,
int packet_min_len);
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);
bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv,

View File

@ -99,8 +99,7 @@ struct batadv_hard_iface {
* @last_seen: time when last packet from this node was received
* @bcast_seqno_reset: time when the broadcast seqno window was reset
* @batman_seqno_reset: time when the batman seqno window was reset
* @gw_flags: flags related to gateway class
* @flags: for now only VIS_SERVER flag
* @capabilities: announced capabilities of this originator
* @last_ttvn: last seen translation table version number
* @tt_crc: CRC of the translation table
* @tt_buff: last tt changeset this node received from the orig node
@ -147,10 +146,9 @@ struct batadv_orig_node {
unsigned long last_seen;
unsigned long bcast_seqno_reset;
unsigned long batman_seqno_reset;
uint8_t gw_flags;
uint8_t flags;
uint8_t capabilities;
atomic_t last_ttvn;
uint16_t tt_crc;
uint32_t tt_crc;
unsigned char *tt_buff;
int16_t tt_buff_len;
spinlock_t tt_buff_lock; /* protects tt_buff & tt_buff_len */
@ -185,10 +183,22 @@ struct batadv_orig_node {
#endif
};
/**
* enum batadv_orig_capabilities - orig node capabilities
* @BATADV_ORIG_CAPA_HAS_DAT: orig node has distributed arp table enabled
* @BATADV_ORIG_CAPA_HAS_NC: orig node has network coding enabled
*/
enum batadv_orig_capabilities {
BATADV_ORIG_CAPA_HAS_DAT = BIT(0),
BATADV_ORIG_CAPA_HAS_NC = BIT(1),
};
/**
* struct batadv_gw_node - structure for orig nodes announcing gw capabilities
* @list: list node for batadv_priv_gw::list
* @orig_node: pointer to corresponding orig node
* @bandwidth_down: advertised uplink download bandwidth
* @bandwidth_up: advertised uplink upload bandwidth
* @deleted: this struct is scheduled for deletion
* @refcount: number of contexts the object is used
* @rcu: struct used for freeing in an RCU-safe manner
@ -196,6 +206,8 @@ struct batadv_orig_node {
struct batadv_gw_node {
struct hlist_node list;
struct batadv_orig_node *orig_node;
uint32_t bandwidth_down;
uint32_t bandwidth_up;
unsigned long deleted;
atomic_t refcount;
struct rcu_head rcu;
@ -363,7 +375,7 @@ struct batadv_priv_tt {
spinlock_t req_list_lock; /* protects req_list */
spinlock_t roam_list_lock; /* protects roam_list */
atomic_t local_entry_num;
uint16_t local_crc;
uint32_t local_crc;
unsigned char *last_changeset;
int16_t last_changeset_len;
/* protects last_changeset & last_changeset_len */
@ -420,31 +432,31 @@ struct batadv_priv_debug_log {
* @list: list of available gateway nodes
* @list_lock: lock protecting gw_list & curr_gw
* @curr_gw: pointer to currently selected gateway node
* @bandwidth_down: advertised uplink download bandwidth (if gw_mode server)
* @bandwidth_up: advertised uplink upload bandwidth (if gw_mode server)
* @reselect: bool indicating a gateway re-selection is in progress
*/
struct batadv_priv_gw {
struct hlist_head list;
spinlock_t list_lock; /* protects gw_list & curr_gw */
struct batadv_gw_node __rcu *curr_gw; /* rcu protected pointer */
atomic_t bandwidth_down;
atomic_t bandwidth_up;
atomic_t reselect;
};
/**
* struct batadv_priv_vis - per mesh interface vis data
* @send_list: list of batadv_vis_info packets to sent
* @hash: hash table containing vis data from other nodes in the network
* @hash_lock: lock protecting the hash table
* @list_lock: lock protecting my_info::recv_list
* @work: work queue callback item for vis packet sending
* @my_info: holds this node's vis data sent on a regular basis
* struct batadv_priv_tvlv - per mesh interface tvlv data
* @container_list: list of registered tvlv containers to be sent with each OGM
* @handler_list: list of the various tvlv content handlers
* @container_list_lock: protects tvlv container list access
* @handler_list_lock: protects handler list access
*/
struct batadv_priv_vis {
struct list_head send_list;
struct batadv_hashtable *hash;
spinlock_t hash_lock; /* protects hash */
spinlock_t list_lock; /* protects my_info::recv_list */
struct delayed_work work;
struct batadv_vis_info *my_info;
struct batadv_priv_tvlv {
struct hlist_head container_list;
struct hlist_head handler_list;
spinlock_t container_list_lock; /* protects container_list */
spinlock_t handler_list_lock; /* protects handler_list */
};
/**
@ -504,10 +516,8 @@ struct batadv_priv_nc {
* enabled
* @distributed_arp_table: bool indicating whether distributed ARP table is
* enabled
* @vis_mode: vis operation: client or server (see batadv_vis_packettype)
* @gw_mode: gateway operation: off, client or server (see batadv_gw_modes)
* @gw_sel_class: gateway selection class (applies if gw_mode client)
* @gw_bandwidth: gateway announced bandwidth (applies if gw_mode server)
* @orig_interval: OGM broadcast interval in milliseconds
* @hop_penalty: penalty which will be applied to an OGM's tq-field on every hop
* @log_level: configured log level (see batadv_dbg_level)
@ -531,7 +541,7 @@ struct batadv_priv_nc {
* @debug_log: holding debug logging relevant data
* @gw: gateway data
* @tt: translation table data
* @vis: vis data
* @tvlv: type-version-length-value data
* @dat: distributed arp table data
* @network_coding: bool indicating whether network coding is enabled
* @batadv_priv_nc: network coding data
@ -551,10 +561,8 @@ struct batadv_priv {
#ifdef CONFIG_BATMAN_ADV_DAT
atomic_t distributed_arp_table;
#endif
atomic_t vis_mode;
atomic_t gw_mode;
atomic_t gw_sel_class;
atomic_t gw_bandwidth;
atomic_t orig_interval;
atomic_t hop_penalty;
#ifdef CONFIG_BATMAN_ADV_DEBUG
@ -583,7 +591,7 @@ struct batadv_priv {
#endif
struct batadv_priv_gw gw;
struct batadv_priv_tt tt;
struct batadv_priv_vis vis;
struct batadv_priv_tvlv tvlv;
#ifdef CONFIG_BATMAN_ADV_DAT
struct batadv_priv_dat dat;
#endif
@ -740,7 +748,7 @@ struct batadv_tt_orig_list_entry {
*/
struct batadv_tt_change_node {
struct list_head list;
struct batadv_tt_change change;
struct batadv_tvlv_tt_change change;
};
/**
@ -877,66 +885,6 @@ struct batadv_frag_packet_list_entry {
struct sk_buff *skb;
};
/**
* struct batadv_vis_info - local data for vis information
* @first_seen: timestamp used for purging stale vis info entries
* @recv_list: List of server-neighbors we have received this packet from. This
* packet should not be re-forward to them again. List elements are struct
* batadv_vis_recvlist_node
* @send_list: list of packets to be forwarded
* @refcount: number of contexts the object is used
* @hash_entry: hlist node for batadv_priv_vis::hash
* @bat_priv: pointer to soft_iface this orig node belongs to
* @skb_packet: contains the vis packet
*/
struct batadv_vis_info {
unsigned long first_seen;
struct list_head recv_list;
struct list_head send_list;
struct kref refcount;
struct hlist_node hash_entry;
struct batadv_priv *bat_priv;
struct sk_buff *skb_packet;
} __packed;
/**
* struct batadv_vis_info_entry - contains link information for vis
* @src: source MAC of the link, all zero for local TT entry
* @dst: destination MAC of the link, client mac address for local TT entry
* @quality: transmission quality of the link, or 0 for local TT entry
*/
struct batadv_vis_info_entry {
uint8_t src[ETH_ALEN];
uint8_t dest[ETH_ALEN];
uint8_t quality;
} __packed;
/**
* struct batadv_vis_recvlist_node - list entry for batadv_vis_info::recv_list
* @list: list node for batadv_vis_info::recv_list
* @mac: MAC address of the originator from where the vis_info was received
*/
struct batadv_vis_recvlist_node {
struct list_head list;
uint8_t mac[ETH_ALEN];
};
/**
* struct batadv_vis_if_list_entry - auxiliary data for vis data generation
* @addr: MAC address of the interface
* @primary: true if this interface is the primary interface
* @list: list node the interface list
*
* While scanning for vis-entries of a particular vis-originator
* this list collects its interfaces to create a subgraph/cluster
* out of them later
*/
struct batadv_vis_if_list_entry {
uint8_t addr[ETH_ALEN];
bool primary;
struct hlist_node list;
};
/**
* struct batadv_algo_ops - mesh algorithm callbacks
* @list: list node for the batadv_algo_list
@ -992,4 +940,60 @@ struct batadv_dat_candidate {
struct batadv_orig_node *orig_node;
};
/**
* struct batadv_tvlv_container - container for tvlv appended to OGMs
* @list: hlist node for batadv_priv_tvlv::container_list
* @tvlv_hdr: tvlv header information needed to construct the tvlv
* @value_len: length of the buffer following this struct which contains
* the actual tvlv payload
* @refcount: number of contexts the object is used
*/
struct batadv_tvlv_container {
struct hlist_node list;
struct batadv_tvlv_hdr tvlv_hdr;
atomic_t refcount;
};
/**
* struct batadv_tvlv_handler - handler for specific tvlv type and version
* @list: hlist node for batadv_priv_tvlv::handler_list
* @ogm_handler: handler callback which is given the tvlv payload to process on
* incoming OGM packets
* @unicast_handler: handler callback which is given the tvlv payload to process
* on incoming unicast tvlv packets
* @type: tvlv type this handler feels responsible for
* @version: tvlv version this handler feels responsible for
* @flags: tvlv handler flags
* @refcount: number of contexts the object is used
* @rcu: struct used for freeing in an RCU-safe manner
*/
struct batadv_tvlv_handler {
struct hlist_node list;
void (*ogm_handler)(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig,
uint8_t flags,
void *tvlv_value, uint16_t tvlv_value_len);
int (*unicast_handler)(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;
atomic_t refcount;
struct rcu_head rcu;
};
/**
* enum batadv_tvlv_handler_flags - tvlv handler flags definitions
* @BATADV_TVLV_HANDLER_OGM_CIFNOTFND: tvlv ogm processing function will call
* this handler even if its type was not found (with no data)
* @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the API marks
* a handler as being called, so it won't be called if the
* BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set
*/
enum batadv_tvlv_handler_flags {
BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1),
BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2),
};
#endif /* _NET_BATMAN_ADV_TYPES_H_ */

View File

@ -1,938 +0,0 @@
/* Copyright (C) 2008-2013 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA
*/
#include "main.h"
#include "send.h"
#include "translation-table.h"
#include "vis.h"
#include "soft-interface.h"
#include "hard-interface.h"
#include "hash.h"
#include "originator.h"
#define BATADV_MAX_VIS_PACKET_SIZE 1000
/* hash class keys */
static struct lock_class_key batadv_vis_hash_lock_class_key;
/* free the info */
static void batadv_free_info(struct kref *ref)
{
struct batadv_vis_info *info;
struct batadv_priv *bat_priv;
struct batadv_vis_recvlist_node *entry, *tmp;
info = container_of(ref, struct batadv_vis_info, refcount);
bat_priv = info->bat_priv;
list_del_init(&info->send_list);
spin_lock_bh(&bat_priv->vis.list_lock);
list_for_each_entry_safe(entry, tmp, &info->recv_list, list) {
list_del(&entry->list);
kfree(entry);
}
spin_unlock_bh(&bat_priv->vis.list_lock);
kfree_skb(info->skb_packet);
kfree(info);
}
/* Compare two vis packets, used by the hashing algorithm */
static int batadv_vis_info_cmp(const struct hlist_node *node, const void *data2)
{
const struct batadv_vis_info *d1, *d2;
const struct batadv_vis_packet *p1, *p2;
d1 = container_of(node, struct batadv_vis_info, hash_entry);
d2 = data2;
p1 = (struct batadv_vis_packet *)d1->skb_packet->data;
p2 = (struct batadv_vis_packet *)d2->skb_packet->data;
return batadv_compare_eth(p1->vis_orig, p2->vis_orig);
}
/* hash function to choose an entry in a hash table of given size
* hash algorithm from http://en.wikipedia.org/wiki/Hash_table
*/
static uint32_t batadv_vis_info_choose(const void *data, uint32_t size)
{
const struct batadv_vis_info *vis_info = data;
const struct batadv_vis_packet *packet;
const unsigned char *key;
uint32_t hash = 0;
size_t i;
packet = (struct batadv_vis_packet *)vis_info->skb_packet->data;
key = packet->vis_orig;
for (i = 0; i < ETH_ALEN; i++) {
hash += key[i];
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash % size;
}
static struct batadv_vis_info *
batadv_vis_hash_find(struct batadv_priv *bat_priv, const void *data)
{
struct batadv_hashtable *hash = bat_priv->vis.hash;
struct hlist_head *head;
struct batadv_vis_info *vis_info, *vis_info_tmp = NULL;
uint32_t index;
if (!hash)
return NULL;
index = batadv_vis_info_choose(data, hash->size);
head = &hash->table[index];
rcu_read_lock();
hlist_for_each_entry_rcu(vis_info, head, hash_entry) {
if (!batadv_vis_info_cmp(&vis_info->hash_entry, data))
continue;
vis_info_tmp = vis_info;
break;
}
rcu_read_unlock();
return vis_info_tmp;
}
/* insert interface to the list of interfaces of one originator, if it
* does not already exist in the list
*/
static void batadv_vis_data_insert_interface(const uint8_t *interface,
struct hlist_head *if_list,
bool primary)
{
struct batadv_vis_if_list_entry *entry;
hlist_for_each_entry(entry, if_list, list) {
if (batadv_compare_eth(entry->addr, interface))
return;
}
/* it's a new address, add it to the list */
entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
if (!entry)
return;
memcpy(entry->addr, interface, ETH_ALEN);
entry->primary = primary;
hlist_add_head(&entry->list, if_list);
}
static void batadv_vis_data_read_prim_sec(struct seq_file *seq,
const struct hlist_head *if_list)
{
struct batadv_vis_if_list_entry *entry;
hlist_for_each_entry(entry, if_list, list) {
if (entry->primary)
seq_puts(seq, "PRIMARY, ");
else
seq_printf(seq, "SEC %pM, ", entry->addr);
}
}
/* read an entry */
static ssize_t
batadv_vis_data_read_entry(struct seq_file *seq,
const struct batadv_vis_info_entry *entry,
const uint8_t *src, bool primary)
{
if (primary && entry->quality == 0)
return seq_printf(seq, "TT %pM, ", entry->dest);
else if (batadv_compare_eth(entry->src, src))
return seq_printf(seq, "TQ %pM %d, ", entry->dest,
entry->quality);
return 0;
}
static void
batadv_vis_data_insert_interfaces(struct hlist_head *list,
struct batadv_vis_packet *packet,
struct batadv_vis_info_entry *entries)
{
int i;
for (i = 0; i < packet->entries; i++) {
if (entries[i].quality == 0)
continue;
if (batadv_compare_eth(entries[i].src, packet->vis_orig))
continue;
batadv_vis_data_insert_interface(entries[i].src, list, false);
}
}
static void batadv_vis_data_read_entries(struct seq_file *seq,
struct hlist_head *list,
struct batadv_vis_packet *packet,
struct batadv_vis_info_entry *entries)
{
int i;
struct batadv_vis_if_list_entry *entry;
hlist_for_each_entry(entry, list, list) {
seq_printf(seq, "%pM,", entry->addr);
for (i = 0; i < packet->entries; i++)
batadv_vis_data_read_entry(seq, &entries[i],
entry->addr, entry->primary);
/* add primary/secondary records */
if (batadv_compare_eth(entry->addr, packet->vis_orig))
batadv_vis_data_read_prim_sec(seq, list);
seq_puts(seq, "\n");
}
}
static void batadv_vis_seq_print_text_bucket(struct seq_file *seq,
const struct hlist_head *head)
{
struct batadv_vis_info *info;
struct batadv_vis_packet *packet;
uint8_t *entries_pos;
struct batadv_vis_info_entry *entries;
struct batadv_vis_if_list_entry *entry;
struct hlist_node *n;
HLIST_HEAD(vis_if_list);
hlist_for_each_entry_rcu(info, head, hash_entry) {
packet = (struct batadv_vis_packet *)info->skb_packet->data;
entries_pos = (uint8_t *)packet + sizeof(*packet);
entries = (struct batadv_vis_info_entry *)entries_pos;
batadv_vis_data_insert_interface(packet->vis_orig, &vis_if_list,
true);
batadv_vis_data_insert_interfaces(&vis_if_list, packet,
entries);
batadv_vis_data_read_entries(seq, &vis_if_list, packet,
entries);
hlist_for_each_entry_safe(entry, n, &vis_if_list, list) {
hlist_del(&entry->list);
kfree(entry);
}
}
}
int batadv_vis_seq_print_text(struct seq_file *seq, void *offset)
{
struct batadv_hard_iface *primary_if;
struct hlist_head *head;
struct net_device *net_dev = (struct net_device *)seq->private;
struct batadv_priv *bat_priv = netdev_priv(net_dev);
struct batadv_hashtable *hash = bat_priv->vis.hash;
uint32_t i;
int ret = 0;
int vis_server = atomic_read(&bat_priv->vis_mode);
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if)
goto out;
if (vis_server == BATADV_VIS_TYPE_CLIENT_UPDATE)
goto out;
spin_lock_bh(&bat_priv->vis.hash_lock);
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
batadv_vis_seq_print_text_bucket(seq, head);
}
spin_unlock_bh(&bat_priv->vis.hash_lock);
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
return ret;
}
/* add the info packet to the send list, if it was not
* already linked in.
*/
static void batadv_send_list_add(struct batadv_priv *bat_priv,
struct batadv_vis_info *info)
{
if (list_empty(&info->send_list)) {
kref_get(&info->refcount);
list_add_tail(&info->send_list, &bat_priv->vis.send_list);
}
}
/* delete the info packet from the send list, if it was
* linked in.
*/
static void batadv_send_list_del(struct batadv_vis_info *info)
{
if (!list_empty(&info->send_list)) {
list_del_init(&info->send_list);
kref_put(&info->refcount, batadv_free_info);
}
}
/* tries to add one entry to the receive list. */
static void batadv_recv_list_add(struct batadv_priv *bat_priv,
struct list_head *recv_list, const char *mac)
{
struct batadv_vis_recvlist_node *entry;
entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
if (!entry)
return;
memcpy(entry->mac, mac, ETH_ALEN);
spin_lock_bh(&bat_priv->vis.list_lock);
list_add_tail(&entry->list, recv_list);
spin_unlock_bh(&bat_priv->vis.list_lock);
}
/* returns 1 if this mac is in the recv_list */
static int batadv_recv_list_is_in(struct batadv_priv *bat_priv,
const struct list_head *recv_list,
const char *mac)
{
const struct batadv_vis_recvlist_node *entry;
spin_lock_bh(&bat_priv->vis.list_lock);
list_for_each_entry(entry, recv_list, list) {
if (batadv_compare_eth(entry->mac, mac)) {
spin_unlock_bh(&bat_priv->vis.list_lock);
return 1;
}
}
spin_unlock_bh(&bat_priv->vis.list_lock);
return 0;
}
/* try to add the packet to the vis_hash. return NULL if invalid (e.g. too old,
* broken.. ). vis hash must be locked outside. is_new is set when the packet
* is newer than old entries in the hash.
*/
static struct batadv_vis_info *
batadv_add_packet(struct batadv_priv *bat_priv,
struct batadv_vis_packet *vis_packet, int vis_info_len,
int *is_new, int make_broadcast)
{
struct batadv_vis_info *info, *old_info;
struct batadv_vis_packet *search_packet, *old_packet;
struct batadv_vis_info search_elem;
struct batadv_vis_packet *packet;
struct sk_buff *tmp_skb;
int hash_added;
size_t len;
size_t max_entries;
*is_new = 0;
/* sanity check */
if (!bat_priv->vis.hash)
return NULL;
/* see if the packet is already in vis_hash */
search_elem.skb_packet = dev_alloc_skb(sizeof(*search_packet));
if (!search_elem.skb_packet)
return NULL;
len = sizeof(*search_packet);
tmp_skb = search_elem.skb_packet;
search_packet = (struct batadv_vis_packet *)skb_put(tmp_skb, len);
memcpy(search_packet->vis_orig, vis_packet->vis_orig, ETH_ALEN);
old_info = batadv_vis_hash_find(bat_priv, &search_elem);
kfree_skb(search_elem.skb_packet);
if (old_info) {
tmp_skb = old_info->skb_packet;
old_packet = (struct batadv_vis_packet *)tmp_skb->data;
if (!batadv_seq_after(ntohl(vis_packet->seqno),
ntohl(old_packet->seqno))) {
if (old_packet->seqno == vis_packet->seqno) {
batadv_recv_list_add(bat_priv,
&old_info->recv_list,
vis_packet->sender_orig);
return old_info;
} else {
/* newer packet is already in hash. */
return NULL;
}
}
/* remove old entry */
batadv_hash_remove(bat_priv->vis.hash, batadv_vis_info_cmp,
batadv_vis_info_choose, old_info);
batadv_send_list_del(old_info);
kref_put(&old_info->refcount, batadv_free_info);
}
info = kmalloc(sizeof(*info), GFP_ATOMIC);
if (!info)
return NULL;
len = sizeof(*packet) + vis_info_len;
info->skb_packet = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
if (!info->skb_packet) {
kfree(info);
return NULL;
}
info->skb_packet->priority = TC_PRIO_CONTROL;
skb_reserve(info->skb_packet, ETH_HLEN);
packet = (struct batadv_vis_packet *)skb_put(info->skb_packet, len);
kref_init(&info->refcount);
INIT_LIST_HEAD(&info->send_list);
INIT_LIST_HEAD(&info->recv_list);
info->first_seen = jiffies;
info->bat_priv = bat_priv;
memcpy(packet, vis_packet, len);
/* initialize and add new packet. */
*is_new = 1;
/* Make it a broadcast packet, if required */
if (make_broadcast)
memcpy(packet->target_orig, batadv_broadcast_addr, ETH_ALEN);
/* repair if entries is longer than packet. */
max_entries = vis_info_len / sizeof(struct batadv_vis_info_entry);
if (packet->entries > max_entries)
packet->entries = max_entries;
batadv_recv_list_add(bat_priv, &info->recv_list, packet->sender_orig);
/* try to add it */
hash_added = batadv_hash_add(bat_priv->vis.hash, batadv_vis_info_cmp,
batadv_vis_info_choose, info,
&info->hash_entry);
if (hash_added != 0) {
/* did not work (for some reason) */
kref_put(&info->refcount, batadv_free_info);
info = NULL;
}
return info;
}
/* handle the server sync packet, forward if needed. */
void batadv_receive_server_sync_packet(struct batadv_priv *bat_priv,
struct batadv_vis_packet *vis_packet,
int vis_info_len)
{
struct batadv_vis_info *info;
int is_new, make_broadcast;
int vis_server = atomic_read(&bat_priv->vis_mode);
make_broadcast = (vis_server == BATADV_VIS_TYPE_SERVER_SYNC);
spin_lock_bh(&bat_priv->vis.hash_lock);
info = batadv_add_packet(bat_priv, vis_packet, vis_info_len,
&is_new, make_broadcast);
if (!info)
goto end;
/* only if we are server ourselves and packet is newer than the one in
* hash.
*/
if (vis_server == BATADV_VIS_TYPE_SERVER_SYNC && is_new)
batadv_send_list_add(bat_priv, info);
end:
spin_unlock_bh(&bat_priv->vis.hash_lock);
}
/* handle an incoming client update packet and schedule forward if needed. */
void batadv_receive_client_update_packet(struct batadv_priv *bat_priv,
struct batadv_vis_packet *vis_packet,
int vis_info_len)
{
struct batadv_vis_info *info;
struct batadv_vis_packet *packet;
int is_new;
int vis_server = atomic_read(&bat_priv->vis_mode);
int are_target = 0;
/* clients shall not broadcast. */
if (is_broadcast_ether_addr(vis_packet->target_orig))
return;
/* Are we the target for this VIS packet? */
if (vis_server == BATADV_VIS_TYPE_SERVER_SYNC &&
batadv_is_my_mac(bat_priv, vis_packet->target_orig))
are_target = 1;
spin_lock_bh(&bat_priv->vis.hash_lock);
info = batadv_add_packet(bat_priv, vis_packet, vis_info_len,
&is_new, are_target);
if (!info)
goto end;
/* note that outdated packets will be dropped at this point. */
packet = (struct batadv_vis_packet *)info->skb_packet->data;
/* send only if we're the target server or ... */
if (are_target && is_new) {
packet->vis_type = BATADV_VIS_TYPE_SERVER_SYNC; /* upgrade! */
batadv_send_list_add(bat_priv, info);
/* ... we're not the recipient (and thus need to forward). */
} else if (!batadv_is_my_mac(bat_priv, packet->target_orig)) {
batadv_send_list_add(bat_priv, info);
}
end:
spin_unlock_bh(&bat_priv->vis.hash_lock);
}
/* Walk the originators and find the VIS server with the best tq. Set the packet
* address to its address and return the best_tq.
*
* Must be called with the originator hash locked
*/
static int batadv_find_best_vis_server(struct batadv_priv *bat_priv,
struct batadv_vis_info *info)
{
struct batadv_hashtable *hash = bat_priv->orig_hash;
struct batadv_neigh_node *router;
struct hlist_head *head;
struct batadv_orig_node *orig_node;
struct batadv_vis_packet *packet;
int best_tq = -1;
uint32_t i;
packet = (struct batadv_vis_packet *)info->skb_packet->data;
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
rcu_read_lock();
hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
router = batadv_orig_node_get_router(orig_node);
if (!router)
continue;
if ((orig_node->flags & BATADV_VIS_SERVER) &&
(router->tq_avg > best_tq)) {
best_tq = router->tq_avg;
memcpy(packet->target_orig, orig_node->orig,
ETH_ALEN);
}
batadv_neigh_node_free_ref(router);
}
rcu_read_unlock();
}
return best_tq;
}
/* Return true if the vis packet is full. */
static bool batadv_vis_packet_full(const struct batadv_vis_info *info)
{
const struct batadv_vis_packet *packet;
size_t num;
packet = (struct batadv_vis_packet *)info->skb_packet->data;
num = BATADV_MAX_VIS_PACKET_SIZE / sizeof(struct batadv_vis_info_entry);
if (num < packet->entries + 1)
return true;
return false;
}
/* generates a packet of own vis data,
* returns 0 on success, -1 if no packet could be generated
*/
static int batadv_generate_vis_packet(struct batadv_priv *bat_priv)
{
struct batadv_hashtable *hash = bat_priv->orig_hash;
struct hlist_head *head;
struct batadv_orig_node *orig_node;
struct batadv_neigh_node *router;
struct batadv_vis_info *info = bat_priv->vis.my_info;
struct batadv_vis_packet *packet;
struct batadv_vis_info_entry *entry;
struct batadv_tt_common_entry *tt_common_entry;
uint8_t *packet_pos;
int best_tq = -1;
uint32_t i;
info->first_seen = jiffies;
packet = (struct batadv_vis_packet *)info->skb_packet->data;
packet->vis_type = atomic_read(&bat_priv->vis_mode);
memcpy(packet->target_orig, batadv_broadcast_addr, ETH_ALEN);
packet->header.ttl = BATADV_TTL;
packet->seqno = htonl(ntohl(packet->seqno) + 1);
packet->entries = 0;
packet->reserved = 0;
skb_trim(info->skb_packet, sizeof(*packet));
if (packet->vis_type == BATADV_VIS_TYPE_CLIENT_UPDATE) {
best_tq = batadv_find_best_vis_server(bat_priv, info);
if (best_tq < 0)
return best_tq;
}
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
rcu_read_lock();
hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
router = batadv_orig_node_get_router(orig_node);
if (!router)
continue;
if (!batadv_compare_eth(router->addr, orig_node->orig))
goto next;
if (router->if_incoming->if_status != BATADV_IF_ACTIVE)
goto next;
if (router->tq_avg < 1)
goto next;
/* fill one entry into buffer. */
packet_pos = skb_put(info->skb_packet, sizeof(*entry));
entry = (struct batadv_vis_info_entry *)packet_pos;
memcpy(entry->src,
router->if_incoming->net_dev->dev_addr,
ETH_ALEN);
memcpy(entry->dest, orig_node->orig, ETH_ALEN);
entry->quality = router->tq_avg;
packet->entries++;
next:
batadv_neigh_node_free_ref(router);
if (batadv_vis_packet_full(info))
goto unlock;
}
rcu_read_unlock();
}
hash = bat_priv->tt.local_hash;
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
rcu_read_lock();
hlist_for_each_entry_rcu(tt_common_entry, head,
hash_entry) {
packet_pos = skb_put(info->skb_packet, sizeof(*entry));
entry = (struct batadv_vis_info_entry *)packet_pos;
memset(entry->src, 0, ETH_ALEN);
memcpy(entry->dest, tt_common_entry->addr, ETH_ALEN);
entry->quality = 0; /* 0 means TT */
packet->entries++;
if (batadv_vis_packet_full(info))
goto unlock;
}
rcu_read_unlock();
}
return 0;
unlock:
rcu_read_unlock();
return 0;
}
/* free old vis packets. Must be called with this vis_hash_lock
* held
*/
static void batadv_purge_vis_packets(struct batadv_priv *bat_priv)
{
uint32_t i;
struct batadv_hashtable *hash = bat_priv->vis.hash;
struct hlist_node *node_tmp;
struct hlist_head *head;
struct batadv_vis_info *info;
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
hlist_for_each_entry_safe(info, node_tmp,
head, hash_entry) {
/* never purge own data. */
if (info == bat_priv->vis.my_info)
continue;
if (batadv_has_timed_out(info->first_seen,
BATADV_VIS_TIMEOUT)) {
hlist_del(&info->hash_entry);
batadv_send_list_del(info);
kref_put(&info->refcount, batadv_free_info);
}
}
}
}
static void batadv_broadcast_vis_packet(struct batadv_priv *bat_priv,
struct batadv_vis_info *info)
{
struct batadv_hashtable *hash = bat_priv->orig_hash;
struct hlist_head *head;
struct batadv_orig_node *orig_node;
struct batadv_vis_packet *packet;
struct sk_buff *skb;
uint32_t i, res;
packet = (struct batadv_vis_packet *)info->skb_packet->data;
/* send to all routers in range. */
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
rcu_read_lock();
hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
/* if it's a vis server and reachable, send it. */
if (!(orig_node->flags & BATADV_VIS_SERVER))
continue;
/* don't send it if we already received the packet from
* this node.
*/
if (batadv_recv_list_is_in(bat_priv, &info->recv_list,
orig_node->orig))
continue;
memcpy(packet->target_orig, orig_node->orig, ETH_ALEN);
skb = skb_clone(info->skb_packet, GFP_ATOMIC);
if (!skb)
continue;
res = batadv_send_skb_to_orig(skb, orig_node, NULL);
if (res == NET_XMIT_DROP)
kfree_skb(skb);
}
rcu_read_unlock();
}
}
static void batadv_unicast_vis_packet(struct batadv_priv *bat_priv,
struct batadv_vis_info *info)
{
struct batadv_orig_node *orig_node;
struct sk_buff *skb;
struct batadv_vis_packet *packet;
packet = (struct batadv_vis_packet *)info->skb_packet->data;
orig_node = batadv_orig_hash_find(bat_priv, packet->target_orig);
if (!orig_node)
goto out;
skb = skb_clone(info->skb_packet, GFP_ATOMIC);
if (!skb)
goto out;
if (batadv_send_skb_to_orig(skb, orig_node, NULL) == NET_XMIT_DROP)
kfree_skb(skb);
out:
if (orig_node)
batadv_orig_node_free_ref(orig_node);
}
/* only send one vis packet. called from batadv_send_vis_packets() */
static void batadv_send_vis_packet(struct batadv_priv *bat_priv,
struct batadv_vis_info *info)
{
struct batadv_hard_iface *primary_if;
struct batadv_vis_packet *packet;
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if)
goto out;
packet = (struct batadv_vis_packet *)info->skb_packet->data;
if (packet->header.ttl < 2) {
pr_debug("Error - can't send vis packet: ttl exceeded\n");
goto out;
}
memcpy(packet->sender_orig, primary_if->net_dev->dev_addr, ETH_ALEN);
packet->header.ttl--;
if (is_broadcast_ether_addr(packet->target_orig))
batadv_broadcast_vis_packet(bat_priv, info);
else
batadv_unicast_vis_packet(bat_priv, info);
packet->header.ttl++; /* restore TTL */
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
}
/* called from timer; send (and maybe generate) vis packet. */
static void batadv_send_vis_packets(struct work_struct *work)
{
struct delayed_work *delayed_work;
struct batadv_priv *bat_priv;
struct batadv_priv_vis *priv_vis;
struct batadv_vis_info *info;
delayed_work = container_of(work, struct delayed_work, work);
priv_vis = container_of(delayed_work, struct batadv_priv_vis, work);
bat_priv = container_of(priv_vis, struct batadv_priv, vis);
spin_lock_bh(&bat_priv->vis.hash_lock);
batadv_purge_vis_packets(bat_priv);
if (batadv_generate_vis_packet(bat_priv) == 0) {
/* schedule if generation was successful */
batadv_send_list_add(bat_priv, bat_priv->vis.my_info);
}
while (!list_empty(&bat_priv->vis.send_list)) {
info = list_first_entry(&bat_priv->vis.send_list,
typeof(*info), send_list);
kref_get(&info->refcount);
spin_unlock_bh(&bat_priv->vis.hash_lock);
batadv_send_vis_packet(bat_priv, info);
spin_lock_bh(&bat_priv->vis.hash_lock);
batadv_send_list_del(info);
kref_put(&info->refcount, batadv_free_info);
}
spin_unlock_bh(&bat_priv->vis.hash_lock);
queue_delayed_work(batadv_event_workqueue, &bat_priv->vis.work,
msecs_to_jiffies(BATADV_VIS_INTERVAL));
}
/* init the vis server. this may only be called when if_list is already
* initialized (e.g. bat0 is initialized, interfaces have been added)
*/
int batadv_vis_init(struct batadv_priv *bat_priv)
{
struct batadv_vis_packet *packet;
int hash_added;
unsigned int len;
unsigned long first_seen;
struct sk_buff *tmp_skb;
if (bat_priv->vis.hash)
return 0;
spin_lock_bh(&bat_priv->vis.hash_lock);
bat_priv->vis.hash = batadv_hash_new(256);
if (!bat_priv->vis.hash) {
pr_err("Can't initialize vis_hash\n");
goto err;
}
batadv_hash_set_lock_class(bat_priv->vis.hash,
&batadv_vis_hash_lock_class_key);
bat_priv->vis.my_info = kmalloc(BATADV_MAX_VIS_PACKET_SIZE, GFP_ATOMIC);
if (!bat_priv->vis.my_info)
goto err;
len = sizeof(*packet) + BATADV_MAX_VIS_PACKET_SIZE + ETH_HLEN;
bat_priv->vis.my_info->skb_packet = netdev_alloc_skb_ip_align(NULL,
len);
if (!bat_priv->vis.my_info->skb_packet)
goto free_info;
bat_priv->vis.my_info->skb_packet->priority = TC_PRIO_CONTROL;
skb_reserve(bat_priv->vis.my_info->skb_packet, ETH_HLEN);
tmp_skb = bat_priv->vis.my_info->skb_packet;
packet = (struct batadv_vis_packet *)skb_put(tmp_skb, sizeof(*packet));
/* prefill the vis info */
first_seen = jiffies - msecs_to_jiffies(BATADV_VIS_INTERVAL);
bat_priv->vis.my_info->first_seen = first_seen;
INIT_LIST_HEAD(&bat_priv->vis.my_info->recv_list);
INIT_LIST_HEAD(&bat_priv->vis.my_info->send_list);
kref_init(&bat_priv->vis.my_info->refcount);
bat_priv->vis.my_info->bat_priv = bat_priv;
packet->header.version = BATADV_COMPAT_VERSION;
packet->header.packet_type = BATADV_VIS;
packet->header.ttl = BATADV_TTL;
packet->seqno = 0;
packet->reserved = 0;
packet->entries = 0;
INIT_LIST_HEAD(&bat_priv->vis.send_list);
hash_added = batadv_hash_add(bat_priv->vis.hash, batadv_vis_info_cmp,
batadv_vis_info_choose,
bat_priv->vis.my_info,
&bat_priv->vis.my_info->hash_entry);
if (hash_added != 0) {
pr_err("Can't add own vis packet into hash\n");
/* not in hash, need to remove it manually. */
kref_put(&bat_priv->vis.my_info->refcount, batadv_free_info);
goto err;
}
spin_unlock_bh(&bat_priv->vis.hash_lock);
INIT_DELAYED_WORK(&bat_priv->vis.work, batadv_send_vis_packets);
queue_delayed_work(batadv_event_workqueue, &bat_priv->vis.work,
msecs_to_jiffies(BATADV_VIS_INTERVAL));
return 0;
free_info:
kfree(bat_priv->vis.my_info);
bat_priv->vis.my_info = NULL;
err:
spin_unlock_bh(&bat_priv->vis.hash_lock);
batadv_vis_quit(bat_priv);
return -ENOMEM;
}
/* Decrease the reference count on a hash item info */
static void batadv_free_info_ref(struct hlist_node *node, void *arg)
{
struct batadv_vis_info *info;
info = container_of(node, struct batadv_vis_info, hash_entry);
batadv_send_list_del(info);
kref_put(&info->refcount, batadv_free_info);
}
/* shutdown vis-server */
void batadv_vis_quit(struct batadv_priv *bat_priv)
{
if (!bat_priv->vis.hash)
return;
cancel_delayed_work_sync(&bat_priv->vis.work);
spin_lock_bh(&bat_priv->vis.hash_lock);
/* properly remove, kill timers ... */
batadv_hash_delete(bat_priv->vis.hash, batadv_free_info_ref, NULL);
bat_priv->vis.hash = NULL;
bat_priv->vis.my_info = NULL;
spin_unlock_bh(&bat_priv->vis.hash_lock);
}

View File

@ -1,36 +0,0 @@
/* Copyright (C) 2008-2013 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA
*/
#ifndef _NET_BATMAN_ADV_VIS_H_
#define _NET_BATMAN_ADV_VIS_H_
/* timeout of vis packets in milliseconds */
#define BATADV_VIS_TIMEOUT 200000
int batadv_vis_seq_print_text(struct seq_file *seq, void *offset);
void batadv_receive_server_sync_packet(struct batadv_priv *bat_priv,
struct batadv_vis_packet *vis_packet,
int vis_info_len);
void batadv_receive_client_update_packet(struct batadv_priv *bat_priv,
struct batadv_vis_packet *vis_packet,
int vis_info_len);
int batadv_vis_init(struct batadv_priv *bat_priv);
void batadv_vis_quit(struct batadv_priv *bat_priv);
#endif /* _NET_BATMAN_ADV_VIS_H_ */