/** * enum batadv_dup_status - duplicate status
*/ enum batadv_dup_status { /** @BATADV_NO_DUP: the packet is no duplicate */
BATADV_NO_DUP = 0,
/** * @BATADV_ORIG_DUP: OGM is a duplicate in the originator (but not for * the neighbor)
*/
BATADV_ORIG_DUP,
/** @BATADV_NEIGH_DUP: OGM is a duplicate for the neighbor */
BATADV_NEIGH_DUP,
/** * @BATADV_PROTECTED: originator is currently protected (after reboot)
*/
BATADV_PROTECTED,
};
/** * batadv_ring_buffer_set() - update the ring buffer with the given value * @lq_recv: pointer to the ring buffer * @lq_index: index to store the value at * @value: value to store in the ring buffer
*/ staticvoid batadv_ring_buffer_set(u8 lq_recv[], u8 *lq_index, u8 value)
{
lq_recv[*lq_index] = value;
*lq_index = (*lq_index + 1) % BATADV_TQ_GLOBAL_WINDOW_SIZE;
}
/** * batadv_ring_buffer_avg() - compute the average of all non-zero values stored * in the given ring buffer * @lq_recv: pointer to the ring buffer * * Return: computed average value.
*/ static u8 batadv_ring_buffer_avg(const u8 lq_recv[])
{ const u8 *ptr;
u16 count = 0;
u16 i = 0;
u16 sum = 0;
ptr = lq_recv;
while (i < BATADV_TQ_GLOBAL_WINDOW_SIZE) { if (*ptr != 0) {
count++;
sum += *ptr;
}
i++;
ptr++;
}
if (count == 0) return 0;
return (u8)(sum / count);
}
/** * batadv_iv_ogm_orig_get() - retrieve or create (if does not exist) an * originator * @bat_priv: the bat priv with all the mesh interface information * @addr: mac address of the originator * * Return: the originator object corresponding to the passed mac address or NULL * on failure. * If the object does not exist, it is created and initialised.
*/ staticstruct batadv_orig_node *
batadv_iv_ogm_orig_get(struct batadv_priv *bat_priv, const u8 *addr)
{ struct batadv_orig_node *orig_node; int hash_added;
orig_node = batadv_orig_hash_find(bat_priv, addr); if (orig_node) return orig_node;
orig_node = batadv_orig_node_new(bat_priv, addr); if (!orig_node) return NULL;
/* when do we schedule our own ogm to be sent */ staticunsignedlong
batadv_iv_ogm_emit_send_time(conststruct batadv_priv *bat_priv)
{ unsignedint msecs;
/* when do we schedule a ogm packet to be sent */ staticunsignedlong batadv_iv_ogm_fwd_send_time(void)
{ return jiffies + msecs_to_jiffies(get_random_u32_below(BATADV_JITTER / 2));
}
/* apply hop penalty for a normal link */ static u8 batadv_hop_penalty(u8 tq, conststruct batadv_priv *bat_priv)
{ int hop_penalty = atomic_read(&bat_priv->hop_penalty); int new_tq;
/** * batadv_iv_ogm_aggr_packet() - checks if there is another OGM attached * @buff_pos: current position in the skb * @packet_len: total length of the skb * @ogm_packet: potential OGM in buffer * * Return: true if there is enough space for another OGM, false otherwise.
*/ staticbool
batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len, conststruct batadv_ogm_packet *ogm_packet)
{ int next_buff_pos = 0;
/* check if there is enough space for the header */
next_buff_pos += buff_pos + sizeof(*ogm_packet); if (next_buff_pos > packet_len) returnfalse;
/* check if there is enough space for the optional TVLV */
next_buff_pos += ntohs(ogm_packet->tvlv_len);
return next_buff_pos <= packet_len;
}
/* send a batman ogm to a given interface */ staticvoid batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet, struct batadv_hard_iface *hard_iface)
{ struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface); constchar *fwd_str;
u8 packet_num;
s16 buff_pos; struct batadv_ogm_packet *batadv_ogm_packet; struct sk_buff *skb;
u8 *packet_pos;
if (hard_iface->if_status != BATADV_IF_ACTIVE) return;
/* adjust all flags and log packets */ while (batadv_iv_ogm_aggr_packet(buff_pos, forw_packet->packet_len,
batadv_ogm_packet)) { /* we might have aggregated direct link packets with an * ordinary base packet
*/ if (test_bit(packet_num, forw_packet->direct_link_flags) &&
forw_packet->if_incoming == hard_iface)
batadv_ogm_packet->flags |= BATADV_DIRECTLINK; else
batadv_ogm_packet->flags &= ~BATADV_DIRECTLINK;
/* create clone because function is called more than once */
skb = skb_clone(forw_packet->skb, GFP_ATOMIC); if (skb) {
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_broadcast_skb(skb, hard_iface);
}
}
/* send a batman ogm packet */ staticvoid batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet)
{ struct net_device *mesh_iface;
if (!forw_packet->if_incoming) {
pr_err("Error - can't forward packet: incoming iface not specified\n"); return;
}
if (forw_packet->if_outgoing->mesh_iface != mesh_iface) {
pr_warn("%s: mesh interface switch for queued OGM\n", __func__); return;
}
if (forw_packet->if_incoming->if_status != BATADV_IF_ACTIVE) return;
/* only for one specific outgoing interface */
batadv_iv_ogm_send_to_if(forw_packet, forw_packet->if_outgoing);
}
/** * batadv_iv_ogm_can_aggregate() - find out if an OGM can be aggregated on an * existing forward packet * @new_bat_ogm_packet: OGM packet to be aggregated * @bat_priv: the bat priv with all the mesh interface information * @packet_len: (total) length of the OGM * @send_time: timestamp (jiffies) when the packet is to be sent * @directlink: true if this is a direct link packet * @if_incoming: interface where the packet was received * @if_outgoing: interface for which the retransmission should be considered * @forw_packet: the forwarded packet which should be checked * * Return: true if new_packet can be aggregated with forw_packet
*/ staticbool
batadv_iv_ogm_can_aggregate(conststruct batadv_ogm_packet *new_bat_ogm_packet, struct batadv_priv *bat_priv, int packet_len, unsignedlong send_time, bool directlink, conststruct batadv_hard_iface *if_incoming, conststruct batadv_hard_iface *if_outgoing, conststruct batadv_forw_packet *forw_packet)
{ struct batadv_ogm_packet *batadv_ogm_packet; unsignedint aggregated_bytes = forw_packet->packet_len + packet_len; struct batadv_hard_iface *primary_if = NULL;
u8 packet_num = forw_packet->num_packets; bool res = false; unsignedlong aggregation_end_time; unsignedint max_bytes;
/* we can aggregate the current packet to this aggregated packet * if: * * - the send time is within our MAX_AGGREGATION_MS time * - the resulting packet won't be bigger than * MAX_AGGREGATION_BYTES and MTU of the outgoing interface * - the number of packets is lower than MAX_AGGREGATION_PACKETS * otherwise aggregation is not possible
*/ if (!time_before(send_time, forw_packet->send_time) ||
!time_after_eq(aggregation_end_time, forw_packet->send_time)) returnfalse;
if (aggregated_bytes > max_bytes) returnfalse;
if (packet_num >= BATADV_MAX_AGGREGATION_PACKETS) returnfalse;
/* packet is not leaving on the same interface. */ if (forw_packet->if_outgoing != if_outgoing) returnfalse;
/* check aggregation compatibility * -> direct link packets are broadcasted on * their interface only * -> aggregate packet if the current packet is * a "global" packet as well as the base * packet
*/
primary_if = batadv_primary_if_get_selected(bat_priv); if (!primary_if) returnfalse;
/* packets without direct link flag and high TTL * are flooded through the net
*/ if (!directlink &&
!(batadv_ogm_packet->flags & BATADV_DIRECTLINK) &&
batadv_ogm_packet->ttl != 1 &&
/* own packets originating non-primary * interfaces leave only that interface
*/
(!forw_packet->own ||
forw_packet->if_incoming == primary_if)) {
res = true; goto out;
}
/* if the incoming packet is sent via this one * interface only - we still can aggregate
*/ if (directlink &&
new_bat_ogm_packet->ttl == 1 &&
forw_packet->if_incoming == if_incoming &&
/* packets from direct neighbors or * own secondary interface packets * (= secondary interface packets in general)
*/
(batadv_ogm_packet->flags & BATADV_DIRECTLINK ||
(forw_packet->own &&
forw_packet->if_incoming != primary_if))) {
res = true; goto out;
}
out:
batadv_hardif_put(primary_if); return res;
}
/** * batadv_iv_ogm_aggregate_new() - create a new aggregated packet and add this * packet to it. * @packet_buff: pointer to the OGM * @packet_len: (total) length of the OGM * @send_time: timestamp (jiffies) when the packet is to be sent * @direct_link: whether this OGM has direct link status * @if_incoming: interface where the packet was received * @if_outgoing: interface for which the retransmission should be considered * @own_packet: true if it is a self-generated ogm
*/ staticvoid batadv_iv_ogm_aggregate_new(constunsignedchar *packet_buff, int packet_len, unsignedlong send_time, bool direct_link, struct batadv_hard_iface *if_incoming, struct batadv_hard_iface *if_outgoing, int own_packet)
{ struct batadv_priv *bat_priv = netdev_priv(if_incoming->mesh_iface); struct batadv_forw_packet *forw_packet_aggr; struct sk_buff *skb; unsignedchar *skb_buff; unsignedint skb_size;
atomic_t *queue_left = own_packet ? NULL : &bat_priv->batman_queue_left;
/* aggregate a new packet into the existing ogm packet */ staticvoid batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr, constunsignedchar *packet_buff, int packet_len, bool direct_link)
{
skb_put_data(forw_packet_aggr->skb, packet_buff, packet_len);
forw_packet_aggr->packet_len += packet_len;
/* save packet direct link flag status */ if (direct_link)
set_bit(forw_packet_aggr->num_packets,
forw_packet_aggr->direct_link_flags);
forw_packet_aggr->num_packets++;
}
/** * batadv_iv_ogm_queue_add() - queue up an OGM for transmission * @bat_priv: the bat priv with all the mesh interface information * @packet_buff: pointer to the OGM * @packet_len: (total) length of the OGM * @if_incoming: interface where the packet was received * @if_outgoing: interface for which the retransmission should be considered * @own_packet: true if it is a self-generated ogm * @send_time: timestamp (jiffies) when the packet is to be sent
*/ staticvoid batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, unsignedchar *packet_buff, int packet_len, struct batadv_hard_iface *if_incoming, struct batadv_hard_iface *if_outgoing, int own_packet, unsignedlong send_time)
{ /* _aggr -> pointer to the packet we want to aggregate with * _pos -> pointer to the position in the queue
*/ struct batadv_forw_packet *forw_packet_aggr = NULL; struct batadv_forw_packet *forw_packet_pos = NULL; struct batadv_ogm_packet *batadv_ogm_packet; bool direct_link; unsignedlong max_aggregation_jiffies;
/* find position for the packet in the forward queue */
spin_lock_bh(&bat_priv->forw_bat_list_lock); /* own packets are not to be aggregated */ if (atomic_read(&bat_priv->aggregated_ogms) && !own_packet) {
hlist_for_each_entry(forw_packet_pos,
&bat_priv->forw_bat_list, list) { if (batadv_iv_ogm_can_aggregate(batadv_ogm_packet,
bat_priv, packet_len,
send_time, direct_link,
if_incoming,
if_outgoing,
forw_packet_pos)) {
forw_packet_aggr = forw_packet_pos; break;
}
}
}
/* nothing to aggregate with - either aggregation disabled or no * suitable aggregation packet found
*/ if (!forw_packet_aggr) { /* the following section can run without the lock */
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
/* if we could not aggregate this packet with one of the others * we hold it back for a while, so that it might be aggregated * later on
*/ if (!own_packet && atomic_read(&bat_priv->aggregated_ogms))
send_time += max_aggregation_jiffies;
if (!is_from_best_next_hop) { /* Mark the forwarded packet when it is not coming from our * best next hop. We still need to forward the packet for our * neighbor link quality detection to work in case the packet * originated from a single hop neighbor. Otherwise we can * simply drop the ogm.
*/ if (is_single_hop_neigh)
batadv_ogm_packet->flags |= BATADV_NOT_BEST_NEXT_HOP; else return;
}
/** * batadv_iv_ogm_slide_own_bcast_window() - bitshift own OGM broadcast windows * for the given interface * @hard_iface: the interface for which the windows have to be shifted
*/ staticvoid
batadv_iv_ogm_slide_own_bcast_window(struct batadv_hard_iface *hard_iface)
{ struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface); struct batadv_hashtable *hash = bat_priv->orig_hash; struct hlist_head *head; struct batadv_orig_node *orig_node; struct batadv_orig_ifinfo *orig_ifinfo; unsignedlong *word;
u32 i;
u8 *w;
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
/* interface already disabled by batadv_iv_ogm_iface_disable */ if (!*ogm_buff) return;
/* the interface gets activated here to avoid race conditions between * the moment of activating the interface in * hardif_activate_interface() where the originator mac is set and * outdated packets (especially uninitialized mac addresses) in the * packet queue
*/ if (hard_iface->if_status == BATADV_IF_TO_BE_ACTIVATED)
hard_iface->if_status = BATADV_IF_ACTIVE;
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);
}
/* change sequence number to network order */
seqno = (u32)atomic_read(&hard_iface->bat_iv.ogm_seqno);
batadv_ogm_packet->seqno = htonl(seqno);
atomic_inc(&hard_iface->bat_iv.ogm_seqno);
if (hard_iface != primary_if) { /* OGMs from secondary interfaces are only scheduled on their * respective interfaces.
*/
batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
hard_iface, hard_iface, 1, send_time); goto out;
}
/* OGMs from primary interfaces are scheduled on all * interfaces.
*/
rcu_read_lock();
netdev_for_each_lower_private_rcu(hard_iface->mesh_iface, tmp_hard_iface, iter) { if (!kref_get_unless_zero(&tmp_hard_iface->refcount)) continue;
/** * batadv_iv_orig_ifinfo_sum() - Get bcast_own sum for originator over interface * @orig_node: originator which reproadcasted the OGMs directly * @if_outgoing: interface which transmitted the original OGM and received the * direct rebroadcast * * Return: Number of replied (rebroadcasted) OGMs which were transmitted by * an originator and directly (without intermediate hop) received by a specific * interface
*/ static u8 batadv_iv_orig_ifinfo_sum(struct batadv_orig_node *orig_node, struct batadv_hard_iface *if_outgoing)
{ struct batadv_orig_ifinfo *orig_ifinfo;
u8 sum;
orig_ifinfo = batadv_orig_ifinfo_get(orig_node, if_outgoing); if (!orig_ifinfo) return 0;
spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
sum = orig_ifinfo->bat_iv.bcast_own_sum;
spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock);
batadv_orig_ifinfo_put(orig_ifinfo);
return sum;
}
/** * batadv_iv_ogm_orig_update() - use OGM to update corresponding data in an * originator * @bat_priv: the bat priv with all the mesh interface information * @orig_node: the orig node who originally emitted the ogm packet * @orig_ifinfo: ifinfo for the outgoing interface of the orig_node * @ethhdr: Ethernet header of the OGM * @batadv_ogm_packet: the ogm packet * @if_incoming: interface where the packet was received * @if_outgoing: interface for which the retransmission should be considered * @dup_status: the duplicate status of this ogm packet.
*/ staticvoid
batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, struct batadv_orig_ifinfo *orig_ifinfo, conststruct ethhdr *ethhdr, conststruct batadv_ogm_packet *batadv_ogm_packet, struct batadv_hard_iface *if_incoming, struct batadv_hard_iface *if_outgoing, enum batadv_dup_status dup_status)
{ struct batadv_neigh_ifinfo *neigh_ifinfo = NULL; struct batadv_neigh_ifinfo *router_ifinfo = NULL; struct batadv_neigh_node *neigh_node = NULL; struct batadv_neigh_node *tmp_neigh_node = NULL; struct batadv_neigh_node *router = NULL;
u8 sum_orig, sum_neigh;
u8 *neigh_addr;
u8 tq_avg;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "%s(): Searching and updating originator entry of received packet\n",
__func__);
rcu_read_lock();
hlist_for_each_entry_rcu(tmp_neigh_node,
&orig_node->neigh_list, list) {
neigh_addr = tmp_neigh_node->addr; if (batadv_compare_eth(neigh_addr, ethhdr->h_source) &&
tmp_neigh_node->if_incoming == if_incoming &&
kref_get_unless_zero(&tmp_neigh_node->refcount)) { if (WARN(neigh_node, "too many matching neigh_nodes"))
batadv_neigh_node_put(neigh_node);
neigh_node = tmp_neigh_node; continue;
}
if (dup_status != BATADV_NO_DUP) continue;
/* only update the entry for this outgoing interface */
neigh_ifinfo = batadv_neigh_ifinfo_get(tmp_neigh_node,
if_outgoing); if (!neigh_ifinfo) continue;
/* if this neighbor already is our next hop there is nothing * to change
*/
router = batadv_orig_router_get(orig_node, if_outgoing); if (router == neigh_node) goto out;
if (router) {
router_ifinfo = batadv_neigh_ifinfo_get(router, if_outgoing); if (!router_ifinfo) goto out;
/* if this neighbor does not offer a better TQ we won't * consider it
*/ if (router_ifinfo->bat_iv.tq_avg > neigh_ifinfo->bat_iv.tq_avg) goto out;
}
/* if the TQ is the same and the link not more symmetric we * won't consider it either
*/ if (router_ifinfo &&
neigh_ifinfo->bat_iv.tq_avg == router_ifinfo->bat_iv.tq_avg) {
sum_orig = batadv_iv_orig_ifinfo_sum(router->orig_node,
router->if_incoming);
sum_neigh = batadv_iv_orig_ifinfo_sum(neigh_node->orig_node,
neigh_node->if_incoming); if (sum_orig >= sum_neigh) goto out;
}
/** * batadv_iv_ogm_calc_tq() - calculate tq for current received ogm packet * @orig_node: the orig node who originally emitted the ogm packet * @orig_neigh_node: the orig node struct of the neighbor who sent the packet * @batadv_ogm_packet: the ogm packet * @if_incoming: interface where the packet was received * @if_outgoing: interface for which the retransmission should be considered * * Return: true if the link can be considered bidirectional, false otherwise
*/ staticbool batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node, struct batadv_orig_node *orig_neigh_node, struct batadv_ogm_packet *batadv_ogm_packet, struct batadv_hard_iface *if_incoming, struct batadv_hard_iface *if_outgoing)
{ struct batadv_priv *bat_priv = netdev_priv(if_incoming->mesh_iface); struct batadv_neigh_node *neigh_node = NULL, *tmp_neigh_node; struct batadv_neigh_ifinfo *neigh_ifinfo;
u8 total_count;
u8 orig_eq_count, neigh_rq_count, neigh_rq_inv, tq_own; unsignedint tq_iface_hop_penalty = BATADV_TQ_MAX_VALUE; unsignedint neigh_rq_inv_cube, neigh_rq_max_cube; unsignedint tq_asym_penalty, inv_asym_penalty; unsignedint combined_tq; bool ret = false;
/* find corresponding one hop neighbor */
rcu_read_lock();
hlist_for_each_entry_rcu(tmp_neigh_node,
&orig_neigh_node->neigh_list, list) { if (!batadv_compare_eth(tmp_neigh_node->addr,
orig_neigh_node->orig)) continue;
if (tmp_neigh_node->if_incoming != if_incoming) continue;
if (!kref_get_unless_zero(&tmp_neigh_node->refcount)) continue;
if (!neigh_node)
neigh_node = batadv_iv_ogm_neigh_new(if_incoming,
orig_neigh_node->orig,
orig_neigh_node,
orig_neigh_node);
if (!neigh_node) goto out;
/* if orig_node is direct neighbor update neigh_node last_seen */ if (orig_node == orig_neigh_node)
neigh_node->last_seen = jiffies;
orig_node->last_seen = jiffies;
/* find packet count of corresponding one hop neighbor */
orig_eq_count = batadv_iv_orig_ifinfo_sum(orig_neigh_node, if_incoming);
neigh_ifinfo = batadv_neigh_ifinfo_new(neigh_node, if_outgoing); if (neigh_ifinfo) {
neigh_rq_count = neigh_ifinfo->bat_iv.real_packet_count;
batadv_neigh_ifinfo_put(neigh_ifinfo);
} else {
neigh_rq_count = 0;
}
/* pay attention to not get a value bigger than 100 % */ if (orig_eq_count > neigh_rq_count)
total_count = neigh_rq_count; else
total_count = orig_eq_count;
/* if we have too few packets (too less data) we set tq_own to zero * if we receive too few packets it is not considered bidirectional
*/ if (total_count < BATADV_TQ_LOCAL_BIDRECT_SEND_MINIMUM ||
neigh_rq_count < BATADV_TQ_LOCAL_BIDRECT_RECV_MINIMUM)
tq_own = 0; else /* neigh_node->real_packet_count is never zero as we * only purge old information when getting new * information
*/
tq_own = (BATADV_TQ_MAX_VALUE * total_count) / neigh_rq_count;
/* 1 - ((1-x) ** 3), normalized to TQ_MAX_VALUE this does * affect the nearly-symmetric links only a little, but * punishes asymmetric links more. This will give a value * between 0 and TQ_MAX_VALUE
*/
neigh_rq_inv = BATADV_TQ_LOCAL_WINDOW_SIZE - neigh_rq_count;
neigh_rq_inv_cube = neigh_rq_inv * neigh_rq_inv * neigh_rq_inv;
neigh_rq_max_cube = BATADV_TQ_LOCAL_WINDOW_SIZE *
BATADV_TQ_LOCAL_WINDOW_SIZE *
BATADV_TQ_LOCAL_WINDOW_SIZE;
inv_asym_penalty = BATADV_TQ_MAX_VALUE * neigh_rq_inv_cube;
inv_asym_penalty /= neigh_rq_max_cube;
tq_asym_penalty = BATADV_TQ_MAX_VALUE - inv_asym_penalty;
tq_iface_hop_penalty -= atomic_read(&if_incoming->hop_penalty);
/* penalize if the OGM is forwarded on the same interface. WiFi * interfaces and other half duplex devices suffer from throughput * drops as they can't send and receive at the same time.
*/ if (if_outgoing && if_incoming == if_outgoing &&
batadv_is_wifi_hardif(if_outgoing))
tq_iface_hop_penalty = batadv_hop_penalty(tq_iface_hop_penalty,
bat_priv);
/* if link has the minimum required transmission quality * consider it bidirectional
*/ if (batadv_ogm_packet->tq >= BATADV_TQ_TOTAL_BIDRECT_LIMIT)
ret = true;
/** * batadv_iv_ogm_update_seqnos() - process a batman packet for all interfaces, * adjust the sequence number and find out whether it is a duplicate * @ethhdr: ethernet header of the packet * @batadv_ogm_packet: OGM packet to be considered * @if_incoming: interface on which the OGM packet was received * @if_outgoing: interface for which the retransmission should be considered * * Return: duplicate status as enum batadv_dup_status
*/ staticenum batadv_dup_status
batadv_iv_ogm_update_seqnos(conststruct ethhdr *ethhdr, conststruct batadv_ogm_packet *batadv_ogm_packet, conststruct batadv_hard_iface *if_incoming, struct batadv_hard_iface *if_outgoing)
{ struct batadv_priv *bat_priv = netdev_priv(if_incoming->mesh_iface); struct batadv_orig_node *orig_node; struct batadv_orig_ifinfo *orig_ifinfo = NULL; struct batadv_neigh_node *neigh_node; struct batadv_neigh_ifinfo *neigh_ifinfo; bool is_dup;
s32 seq_diff; bool need_update = false; int set_mark; enum batadv_dup_status ret = BATADV_NO_DUP;
u32 seqno = ntohl(batadv_ogm_packet->seqno);
u8 *neigh_addr;
u8 packet_count; unsignedlong *bitmap;
orig_node = batadv_iv_ogm_orig_get(bat_priv, batadv_ogm_packet->orig); if (!orig_node) return BATADV_NO_DUP;
/* signalize caller that the packet is to be dropped. */ if (!hlist_empty(&orig_node->neigh_list) &&
batadv_window_protected(bat_priv, seq_diff,
BATADV_TQ_LOCAL_WINDOW_SIZE,
&orig_ifinfo->batman_seqno_reset, NULL)) {
ret = BATADV_PROTECTED; goto out;
}
if (batadv_compare_eth(neigh_addr, ethhdr->h_source) &&
neigh_node->if_incoming == if_incoming) {
set_mark = 1; if (is_dup)
ret = BATADV_NEIGH_DUP;
} else {
set_mark = 0; if (is_dup && ret != BATADV_NEIGH_DUP)
ret = BATADV_ORIG_DUP;
}
/* if the window moved, set the update flag. */
bitmap = neigh_ifinfo->bat_iv.real_bits;
need_update |= batadv_bit_get_packet(bat_priv, bitmap,
seq_diff, set_mark);
/** * batadv_iv_ogm_process_per_outif() - process a batman iv OGM for an outgoing * interface * @skb: the skb containing the OGM * @ogm_offset: offset from skb->data to start of ogm header * @orig_node: the (cached) orig node for the originator of this OGM * @if_incoming: the interface where this packet was received * @if_outgoing: the interface for which the packet should be considered
*/ staticvoid
batadv_iv_ogm_process_per_outif(conststruct sk_buff *skb, int ogm_offset, struct batadv_orig_node *orig_node, struct batadv_hard_iface *if_incoming, struct batadv_hard_iface *if_outgoing)
{ struct batadv_priv *bat_priv = netdev_priv(if_incoming->mesh_iface); struct batadv_hardif_neigh_node *hardif_neigh = NULL; struct batadv_neigh_node *router = NULL; struct batadv_neigh_node *router_router = NULL; struct batadv_orig_node *orig_neigh_node; struct batadv_orig_ifinfo *orig_ifinfo; struct batadv_neigh_node *orig_neigh_router = NULL; struct batadv_neigh_ifinfo *router_ifinfo = NULL; struct batadv_ogm_packet *ogm_packet; enum batadv_dup_status dup_status; bool is_from_best_next_hop = false; bool is_single_hop_neigh = false; bool sameseq, similar_ttl; struct sk_buff *skb_priv; struct ethhdr *ethhdr;
u8 *prev_sender; bool is_bidirect;
/* create a private copy of the skb, as some functions change tq value * and/or flags.
*/
skb_priv = skb_copy(skb, GFP_ATOMIC); if (!skb_priv) return;
prev_sender = ogm_packet->prev_sender; /* avoid temporary routing loops */ if (router && router_router &&
(batadv_compare_eth(router->addr, prev_sender)) &&
!(batadv_compare_eth(ogm_packet->orig, prev_sender)) &&
(batadv_compare_eth(router->addr, router_router->addr))) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Drop packet: ignoring all rebroadcast packets that may make me loop (sender: %pM)\n",
ethhdr->h_source); goto out;
}
if (if_outgoing == BATADV_IF_DEFAULT)
batadv_tvlv_ogm_receive(bat_priv, ogm_packet, orig_node);
/* if sender is a direct neighbor the sender mac equals * originator mac
*/ if (is_single_hop_neigh)
orig_neigh_node = orig_node; else
orig_neigh_node = batadv_iv_ogm_orig_get(bat_priv,
ethhdr->h_source);
if (!orig_neigh_node) goto out;
/* Update nc_nodes of the originator */
batadv_nc_update_nc_node(bat_priv, orig_node, orig_neigh_node,
ogm_packet, is_single_hop_neigh);
/* drop packet if sender is not a direct neighbor and if we * don't route towards it
*/ if (!is_single_hop_neigh && !orig_neigh_router) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Drop packet: OGM via unknown neighbor!\n"); goto out_neigh;
}
/* update ranking if it is not a duplicate or has the same * seqno and similar ttl as the non-duplicate
*/
orig_ifinfo = batadv_orig_ifinfo_new(orig_node, if_outgoing); if (!orig_ifinfo) goto out_neigh;
/* only forward for specific interface, not for the default one. */ if (if_outgoing == BATADV_IF_DEFAULT) goto out_neigh;
/* is single hop (direct) neighbor */ if (is_single_hop_neigh) { /* OGMs from secondary interfaces should only scheduled once * per interface where it has been received, not multiple times
*/ if (ogm_packet->ttl <= 2 &&
if_incoming != if_outgoing) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Drop packet: OGM from secondary interface and wrong outgoing interface\n"); goto out_neigh;
} /* mark direct link on incoming interface */
batadv_iv_ogm_forward(orig_node, ethhdr, ogm_packet,
is_single_hop_neigh,
is_from_best_next_hop, if_incoming,
if_outgoing);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Forwarding packet: rebroadcast neighbor packet with direct link flag\n"); goto out_neigh;
}
/* multihop originator */ if (!is_bidirect) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Drop packet: not received via bidirectional link\n"); goto out_neigh;
}
/** * batadv_iv_ogm_process_reply() - Check OGM for direct reply and process it * @ogm_packet: rebroadcast OGM packet to process * @if_incoming: the interface where this packet was received * @orig_node: originator which reproadcasted the OGMs * @if_incoming_seqno: OGM sequence number when rebroadcast was received
*/ staticvoid batadv_iv_ogm_process_reply(struct batadv_ogm_packet *ogm_packet, struct batadv_hard_iface *if_incoming, struct batadv_orig_node *orig_node,
u32 if_incoming_seqno)
{ struct batadv_orig_ifinfo *orig_ifinfo;
s32 bit_pos;
u8 *weight;
/* neighbor has to indicate direct link and it has to * come via the corresponding interface
*/ if (!(ogm_packet->flags & BATADV_DIRECTLINK)) return;
if (!batadv_compare_eth(if_incoming->net_dev->dev_addr,
ogm_packet->orig)) return;
orig_ifinfo = batadv_orig_ifinfo_get(orig_node, if_incoming); if (!orig_ifinfo) return;
/* Silently drop when the batman packet is actually not a * correct packet. * * This might happen if a packet is padded (e.g. Ethernet has a * minimum frame length of 64 byte) and the aggregation interprets * it as an additional length. * * TODO: A more sane solution would be to have a bit in the * batadv_ogm_packet to detect whether the packet is the last * packet in an aggregation. Here we expect that the padding * is always zero (or not 0x01)
*/ if (ogm_packet->packet_type != BATADV_IV_OGM) return;
/* could be changed by schedule_own_packet() */
if_incoming_seqno = atomic_read(&if_incoming->bat_iv.ogm_seqno);
if (is_my_oldorig) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Drop packet: ignoring all rebroadcast echos (sender: %pM)\n",
ethhdr->h_source); return;
}
if (ogm_packet->flags & BATADV_NOT_BEST_NEXT_HOP) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Drop packet: ignoring all packets not forwarded from the best next hop (sender: %pM)\n",
ethhdr->h_source); return;
}
orig_node = batadv_iv_ogm_orig_get(bat_priv, ogm_packet->orig); if (!orig_node) return;
/* we have to have at least one packet in the queue to determine the * queues wake up time unless we are shutting down. * * only re-schedule if this is the "original" copy, e.g. the OGM of the * primary interface should only be rescheduled once per period, but * this function will be called for the forw_packet instances of the * other secondary interfaces as well.
*/ if (forw_packet->own &&
forw_packet->if_incoming == forw_packet->if_outgoing)
batadv_iv_ogm_schedule(forw_packet->if_incoming);
out: /* do we get something for free()? */ if (batadv_forw_packet_steal(forw_packet,
&bat_priv->forw_bat_list_lock))
batadv_forw_packet_free(forw_packet, dropped);
}
staticint batadv_iv_ogm_receive(struct sk_buff *skb, struct batadv_hard_iface *if_incoming)
{ struct batadv_priv *bat_priv = netdev_priv(if_incoming->mesh_iface); struct batadv_ogm_packet *ogm_packet;
u8 *packet_pos; int ogm_offset; bool res; int ret = NET_RX_DROP;
res = batadv_check_management_packet(skb, if_incoming, BATADV_OGM_HLEN); if (!res) goto free_skb;
/* did we receive a B.A.T.M.A.N. IV OGM packet on an interface * that does not have B.A.T.M.A.N. IV enabled ?
*/ if (bat_priv->algo_ops->iface.enable != batadv_iv_ogm_iface_enable) goto free_skb;
/* unpack the aggregated packets and process them one by one */ while (batadv_iv_ogm_aggr_packet(ogm_offset, skb_headlen(skb),
ogm_packet)) {
batadv_iv_ogm_process(skb, ogm_offset, if_incoming);
free_skb: if (ret == NET_RX_SUCCESS)
consume_skb(skb); else
kfree_skb(skb);
return ret;
}
/** * batadv_iv_ogm_neigh_get_tq_avg() - Get the TQ average for a neighbour on a * given outgoing interface. * @neigh_node: Neighbour of interest * @if_outgoing: Outgoing interface of interest * @tq_avg: Pointer of where to store the TQ average * * Return: False if no average TQ available, otherwise true.
*/ staticbool
batadv_iv_ogm_neigh_get_tq_avg(struct batadv_neigh_node *neigh_node, struct batadv_hard_iface *if_outgoing,
u8 *tq_avg)
{ struct batadv_neigh_ifinfo *n_ifinfo;
n_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing); if (!n_ifinfo) returnfalse;
/** * batadv_iv_ogm_orig_dump_subentry() - Dump an originator subentry into a * message * @msg: Netlink message to dump into * @portid: Port making netlink request * @seq: Sequence number of netlink message * @bat_priv: The bat priv with all the mesh interface information * @if_outgoing: Limit dump to entries with this outgoing interface * @orig_node: Originator to dump * @neigh_node: Single hops neighbour * @best: Is the best originator * * Return: Error code, or 0 on success
*/ staticint
batadv_iv_ogm_orig_dump_subentry(struct sk_buff *msg, u32 portid, u32 seq, struct batadv_priv *bat_priv, struct batadv_hard_iface *if_outgoing, struct batadv_orig_node *orig_node, struct batadv_neigh_node *neigh_node, bool best)
{ void *hdr;
u8 tq_avg; unsignedint last_seen_msecs;
/** * batadv_iv_ogm_orig_dump_entry() - Dump an originator entry into a message * @msg: Netlink message to dump into * @portid: Port making netlink request * @seq: Sequence number of netlink message * @bat_priv: The bat priv with all the mesh interface information * @if_outgoing: Limit dump to entries with this outgoing interface * @orig_node: Originator to dump * @sub_s: Number of sub entries to skip * * This function assumes the caller holds rcu_read_lock(). * * Return: Error code, or 0 on success
*/ staticint
batadv_iv_ogm_orig_dump_entry(struct sk_buff *msg, u32 portid, u32 seq, struct batadv_priv *bat_priv, struct batadv_hard_iface *if_outgoing, struct batadv_orig_node *orig_node, int *sub_s)
{ struct batadv_neigh_node *neigh_node_best; struct batadv_neigh_node *neigh_node; int sub = 0; bool best;
u8 tq_avg_best;
neigh_node_best = batadv_orig_router_get(orig_node, if_outgoing); if (!neigh_node_best) goto out;
if (!batadv_iv_ogm_neigh_get_tq_avg(neigh_node_best, if_outgoing,
&tq_avg_best)) goto out;
if (tq_avg_best == 0) goto out;
hlist_for_each_entry_rcu(neigh_node, &orig_node->neigh_list, list) { if (sub++ < *sub_s) continue;
/** * batadv_iv_ogm_orig_dump_bucket() - Dump an originator bucket into a * message * @msg: Netlink message to dump into * @portid: Port making netlink request * @seq: Sequence number of netlink message * @bat_priv: The bat priv with all the mesh interface information * @if_outgoing: Limit dump to entries with this outgoing interface * @head: Bucket to be dumped * @idx_s: Number of entries to be skipped * @sub: Number of sub entries to be skipped * * Return: Error code, or 0 on success
*/ staticint
batadv_iv_ogm_orig_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq, struct batadv_priv *bat_priv, struct batadv_hard_iface *if_outgoing, struct hlist_head *head, int *idx_s, int *sub)
{ struct batadv_orig_node *orig_node; int idx = 0;
rcu_read_lock();
hlist_for_each_entry_rcu(orig_node, head, hash_entry) { if (idx++ < *idx_s) continue;
/** * batadv_iv_ogm_orig_dump() - Dump the originators into a message * @msg: Netlink message to dump into * @cb: Control block containing additional options * @bat_priv: The bat priv with all the mesh interface information * @if_outgoing: Limit dump to entries with this outgoing interface
*/ staticvoid
batadv_iv_ogm_orig_dump(struct sk_buff *msg, struct netlink_callback *cb, struct batadv_priv *bat_priv, struct batadv_hard_iface *if_outgoing)
{ struct batadv_hashtable *hash = bat_priv->orig_hash; struct hlist_head *head; int bucket = cb->args[0]; int idx = cb->args[1]; int sub = cb->args[2]; int portid = NETLINK_CB(cb->skb).portid;
while (bucket < hash->size) {
head = &hash->table[bucket];
if (batadv_iv_ogm_orig_dump_bucket(msg, portid,
cb->nlh->nlmsg_seq,
bat_priv, if_outgoing, head,
&idx, &sub)) break;
/** * batadv_iv_ogm_neigh_diff() - calculate tq difference of two neighbors * @neigh1: the first neighbor object of the comparison * @if_outgoing1: outgoing interface for the first neighbor * @neigh2: the second neighbor object of the comparison * @if_outgoing2: outgoing interface for the second neighbor * @diff: pointer to integer receiving the calculated difference * * The content of *@diff is only valid when this function returns true. * It is less, equal to or greater than 0 if the metric via neigh1 is lower, * the same as or higher than the metric via neigh2 * * Return: true when the difference could be calculated, false otherwise
*/ staticbool batadv_iv_ogm_neigh_diff(struct batadv_neigh_node *neigh1, struct batadv_hard_iface *if_outgoing1, struct batadv_neigh_node *neigh2, struct batadv_hard_iface *if_outgoing2, int *diff)
{ struct batadv_neigh_ifinfo *neigh1_ifinfo, *neigh2_ifinfo;
u8 tq1, tq2; bool ret = true;
/** * batadv_iv_ogm_neigh_dump_neigh() - Dump a neighbour into a netlink message * @msg: Netlink message to dump into * @portid: Port making netlink request * @seq: Sequence number of netlink message * @hardif_neigh: Neighbour to be dumped * * Return: Error code, or 0 on success
*/ staticint
batadv_iv_ogm_neigh_dump_neigh(struct sk_buff *msg, u32 portid, u32 seq, struct batadv_hardif_neigh_node *hardif_neigh)
{ void *hdr; unsignedint last_seen_msecs;
/** * batadv_iv_ogm_neigh_dump_hardif() - Dump the neighbours of a hard interface * into a message * @msg: Netlink message to dump into * @portid: Port making netlink request * @seq: Sequence number of netlink message * @bat_priv: The bat priv with all the mesh interface information * @hard_iface: Hard interface to dump the neighbours for * @idx_s: Number of entries to skip * * This function assumes the caller holds rcu_read_lock(). * * Return: Error code, or 0 on success
*/ staticint
batadv_iv_ogm_neigh_dump_hardif(struct sk_buff *msg, u32 portid, u32 seq, struct batadv_priv *bat_priv, struct batadv_hard_iface *hard_iface, int *idx_s)
{ struct batadv_hardif_neigh_node *hardif_neigh; int idx = 0;
hlist_for_each_entry_rcu(hardif_neigh,
&hard_iface->neigh_list, list) { if (idx++ < *idx_s) continue;
/** * batadv_iv_ogm_neigh_dump() - Dump the neighbours into a message * @msg: Netlink message to dump into * @cb: Control block containing additional options * @bat_priv: The bat priv with all the mesh interface information * @single_hardif: Limit dump to this hard interface
*/ staticvoid
batadv_iv_ogm_neigh_dump(struct sk_buff *msg, struct netlink_callback *cb, struct batadv_priv *bat_priv, struct batadv_hard_iface *single_hardif)
{ struct batadv_hard_iface *hard_iface; struct list_head *iter; int i_hardif = 0; int i_hardif_s = cb->args[0]; int idx = cb->args[1]; int portid = NETLINK_CB(cb->skb).portid;
rcu_read_lock(); if (single_hardif) { if (i_hardif_s == 0) { if (batadv_iv_ogm_neigh_dump_hardif(msg, portid,
cb->nlh->nlmsg_seq,
bat_priv,
single_hardif,
&idx) == 0)
i_hardif++;
}
} else {
netdev_for_each_lower_private_rcu(bat_priv->mesh_iface, hard_iface, iter) { if (i_hardif++ < i_hardif_s) continue;
/** * batadv_iv_ogm_neigh_cmp() - compare the metrics of two neighbors * @neigh1: the first neighbor object of the comparison * @if_outgoing1: outgoing interface for the first neighbor * @neigh2: the second neighbor object of the comparison * @if_outgoing2: outgoing interface for the second neighbor * * Return: a value less, equal to or greater than 0 if the metric via neigh1 is * lower, the same as or higher than the metric via neigh2
*/
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.23 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.