280 lines
8.2 KiB
C
280 lines
8.2 KiB
C
|
/* Copyright (C) 2013-2016 B.A.T.M.A.N. contributors:
|
||
|
*
|
||
|
* Antonio Quartulli
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of version 2 of the GNU General Public
|
||
|
* License as published by the Free Software Foundation.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful, but
|
||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "bat_v_ogm.h"
|
||
|
#include "main.h"
|
||
|
|
||
|
#include <linux/atomic.h>
|
||
|
#include <linux/byteorder/generic.h>
|
||
|
#include <linux/errno.h>
|
||
|
#include <linux/etherdevice.h>
|
||
|
#include <linux/fs.h>
|
||
|
#include <linux/if_ether.h>
|
||
|
#include <linux/jiffies.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/netdevice.h>
|
||
|
#include <linux/random.h>
|
||
|
#include <linux/rculist.h>
|
||
|
#include <linux/rcupdate.h>
|
||
|
#include <linux/skbuff.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/stddef.h>
|
||
|
#include <linux/string.h>
|
||
|
#include <linux/types.h>
|
||
|
#include <linux/workqueue.h>
|
||
|
|
||
|
#include "hard-interface.h"
|
||
|
#include "packet.h"
|
||
|
#include "routing.h"
|
||
|
#include "send.h"
|
||
|
#include "translation-table.h"
|
||
|
|
||
|
/**
|
||
|
* batadv_v_ogm_start_timer - restart the OGM sending timer
|
||
|
* @bat_priv: the bat priv with all the soft interface information
|
||
|
*/
|
||
|
static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv)
|
||
|
{
|
||
|
unsigned long msecs;
|
||
|
/* this function may be invoked in different contexts (ogm rescheduling
|
||
|
* or hard_iface activation), but the work timer should not be reset
|
||
|
*/
|
||
|
if (delayed_work_pending(&bat_priv->bat_v.ogm_wq))
|
||
|
return;
|
||
|
|
||
|
msecs = atomic_read(&bat_priv->orig_interval) - BATADV_JITTER;
|
||
|
msecs += prandom_u32() % (2 * BATADV_JITTER);
|
||
|
queue_delayed_work(batadv_event_workqueue, &bat_priv->bat_v.ogm_wq,
|
||
|
msecs_to_jiffies(msecs));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* batadv_v_ogm_send_to_if - send a batman ogm using a given interface
|
||
|
* @skb: the OGM to send
|
||
|
* @hard_iface: the interface to use to send the OGM
|
||
|
*/
|
||
|
static void batadv_v_ogm_send_to_if(struct sk_buff *skb,
|
||
|
struct batadv_hard_iface *hard_iface)
|
||
|
{
|
||
|
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
|
||
|
|
||
|
if (hard_iface->if_status != BATADV_IF_ACTIVE)
|
||
|
return;
|
||
|
|
||
|
batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_TX);
|
||
|
batadv_add_counter(bat_priv, BATADV_CNT_MGMT_TX_BYTES,
|
||
|
skb->len + ETH_HLEN);
|
||
|
|
||
|
batadv_send_skb_packet(skb, hard_iface, batadv_broadcast_addr);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* batadv_v_ogm_send - periodic worker broadcasting the own OGM
|
||
|
* @work: work queue item
|
||
|
*/
|
||
|
static void batadv_v_ogm_send(struct work_struct *work)
|
||
|
{
|
||
|
struct batadv_hard_iface *hard_iface;
|
||
|
struct batadv_priv_bat_v *bat_v;
|
||
|
struct batadv_priv *bat_priv;
|
||
|
struct batadv_ogm2_packet *ogm_packet;
|
||
|
struct sk_buff *skb, *skb_tmp;
|
||
|
unsigned char *ogm_buff, *pkt_buff;
|
||
|
int ogm_buff_len;
|
||
|
u16 tvlv_len = 0;
|
||
|
|
||
|
bat_v = container_of(work, struct batadv_priv_bat_v, ogm_wq.work);
|
||
|
bat_priv = container_of(bat_v, struct batadv_priv, bat_v);
|
||
|
|
||
|
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
|
||
|
goto out;
|
||
|
|
||
|
ogm_buff = bat_priv->bat_v.ogm_buff;
|
||
|
ogm_buff_len = bat_priv->bat_v.ogm_buff_len;
|
||
|
/* 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_OGM2_HLEN);
|
||
|
|
||
|
bat_priv->bat_v.ogm_buff = ogm_buff;
|
||
|
bat_priv->bat_v.ogm_buff_len = ogm_buff_len;
|
||
|
|
||
|
skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + ogm_buff_len);
|
||
|
if (!skb)
|
||
|
goto reschedule;
|
||
|
|
||
|
skb_reserve(skb, ETH_HLEN);
|
||
|
pkt_buff = skb_put(skb, ogm_buff_len);
|
||
|
memcpy(pkt_buff, ogm_buff, ogm_buff_len);
|
||
|
|
||
|
ogm_packet = (struct batadv_ogm2_packet *)skb->data;
|
||
|
ogm_packet->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno));
|
||
|
atomic_inc(&bat_priv->bat_v.ogm_seqno);
|
||
|
ogm_packet->tvlv_len = htons(tvlv_len);
|
||
|
|
||
|
/* broadcast on every interface */
|
||
|
rcu_read_lock();
|
||
|
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
|
||
|
if (hard_iface->soft_iface != bat_priv->soft_iface)
|
||
|
continue;
|
||
|
|
||
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||
|
"Sending own OGM2 packet (originator %pM, seqno %u, throughput %u, TTL %d) on interface %s [%pM]\n",
|
||
|
ogm_packet->orig, ntohl(ogm_packet->seqno),
|
||
|
ntohl(ogm_packet->throughput), ogm_packet->ttl,
|
||
|
hard_iface->net_dev->name,
|
||
|
hard_iface->net_dev->dev_addr);
|
||
|
|
||
|
/* this skb gets consumed by batadv_v_ogm_send_to_if() */
|
||
|
skb_tmp = skb_clone(skb, GFP_ATOMIC);
|
||
|
if (!skb_tmp)
|
||
|
break;
|
||
|
|
||
|
batadv_v_ogm_send_to_if(skb_tmp, hard_iface);
|
||
|
}
|
||
|
rcu_read_unlock();
|
||
|
|
||
|
consume_skb(skb);
|
||
|
|
||
|
reschedule:
|
||
|
batadv_v_ogm_start_timer(bat_priv);
|
||
|
out:
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* batadv_v_ogm_iface_enable - prepare an interface for B.A.T.M.A.N. V
|
||
|
* @hard_iface: the interface to prepare
|
||
|
*
|
||
|
* Takes care of scheduling own OGM sending routine for this interface.
|
||
|
*
|
||
|
* Return: 0 on success or a negative error code otherwise
|
||
|
*/
|
||
|
int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
|
||
|
{
|
||
|
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
|
||
|
|
||
|
batadv_v_ogm_start_timer(bat_priv);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* batadv_v_ogm_primary_iface_set - set a new primary interface
|
||
|
* @primary_iface: the new primary interface
|
||
|
*/
|
||
|
void batadv_v_ogm_primary_iface_set(struct batadv_hard_iface *primary_iface)
|
||
|
{
|
||
|
struct batadv_priv *bat_priv = netdev_priv(primary_iface->soft_iface);
|
||
|
struct batadv_ogm2_packet *ogm_packet;
|
||
|
|
||
|
if (!bat_priv->bat_v.ogm_buff)
|
||
|
return;
|
||
|
|
||
|
ogm_packet = (struct batadv_ogm2_packet *)bat_priv->bat_v.ogm_buff;
|
||
|
ether_addr_copy(ogm_packet->orig, primary_iface->net_dev->dev_addr);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* batadv_v_ogm_packet_recv - OGM2 receiving handler
|
||
|
* @skb: the received OGM
|
||
|
* @if_incoming: the interface where this OGM has been received
|
||
|
*
|
||
|
* Return: NET_RX_SUCCESS and consume the skb on success or returns NET_RX_DROP
|
||
|
* (without freeing the skb) on failure
|
||
|
*/
|
||
|
int batadv_v_ogm_packet_recv(struct sk_buff *skb,
|
||
|
struct batadv_hard_iface *if_incoming)
|
||
|
{
|
||
|
struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
|
||
|
struct batadv_ogm2_packet *ogm_packet;
|
||
|
struct ethhdr *ethhdr = eth_hdr(skb);
|
||
|
|
||
|
/* did we receive a OGM2 packet on an interface that does not have
|
||
|
* B.A.T.M.A.N. V enabled ?
|
||
|
*/
|
||
|
if (strcmp(bat_priv->bat_algo_ops->name, "BATMAN_V") != 0)
|
||
|
return NET_RX_DROP;
|
||
|
|
||
|
if (!batadv_check_management_packet(skb, if_incoming, BATADV_OGM2_HLEN))
|
||
|
return NET_RX_DROP;
|
||
|
|
||
|
if (batadv_is_my_mac(bat_priv, ethhdr->h_source))
|
||
|
return NET_RX_DROP;
|
||
|
|
||
|
ogm_packet = (struct batadv_ogm2_packet *)skb->data;
|
||
|
|
||
|
if (batadv_is_my_mac(bat_priv, ogm_packet->orig))
|
||
|
return NET_RX_DROP;
|
||
|
|
||
|
batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_RX);
|
||
|
batadv_add_counter(bat_priv, BATADV_CNT_MGMT_RX_BYTES,
|
||
|
skb->len + ETH_HLEN);
|
||
|
|
||
|
consume_skb(skb);
|
||
|
return NET_RX_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* batadv_v_ogm_init - initialise the OGM2 engine
|
||
|
* @bat_priv: the bat priv with all the soft interface information
|
||
|
*
|
||
|
* Return: 0 on success or a negative error code in case of failure
|
||
|
*/
|
||
|
int batadv_v_ogm_init(struct batadv_priv *bat_priv)
|
||
|
{
|
||
|
struct batadv_ogm2_packet *ogm_packet;
|
||
|
unsigned char *ogm_buff;
|
||
|
u32 random_seqno;
|
||
|
|
||
|
bat_priv->bat_v.ogm_buff_len = BATADV_OGM2_HLEN;
|
||
|
ogm_buff = kzalloc(bat_priv->bat_v.ogm_buff_len, GFP_ATOMIC);
|
||
|
if (!ogm_buff)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
bat_priv->bat_v.ogm_buff = ogm_buff;
|
||
|
ogm_packet = (struct batadv_ogm2_packet *)ogm_buff;
|
||
|
ogm_packet->packet_type = BATADV_OGM2;
|
||
|
ogm_packet->version = BATADV_COMPAT_VERSION;
|
||
|
ogm_packet->ttl = BATADV_TTL;
|
||
|
ogm_packet->flags = BATADV_NO_FLAGS;
|
||
|
ogm_packet->throughput = htonl(BATADV_THROUGHPUT_MAX_VALUE);
|
||
|
|
||
|
/* randomize initial seqno to avoid collision */
|
||
|
get_random_bytes(&random_seqno, sizeof(random_seqno));
|
||
|
atomic_set(&bat_priv->bat_v.ogm_seqno, random_seqno);
|
||
|
INIT_DELAYED_WORK(&bat_priv->bat_v.ogm_wq, batadv_v_ogm_send);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* batadv_v_ogm_free - free OGM private resources
|
||
|
* @bat_priv: the bat priv with all the soft interface information
|
||
|
*/
|
||
|
void batadv_v_ogm_free(struct batadv_priv *bat_priv)
|
||
|
{
|
||
|
cancel_delayed_work_sync(&bat_priv->bat_v.ogm_wq);
|
||
|
|
||
|
kfree(bat_priv->bat_v.ogm_buff);
|
||
|
bat_priv->bat_v.ogm_buff = NULL;
|
||
|
bat_priv->bat_v.ogm_buff_len = 0;
|
||
|
}
|