diff options
Diffstat (limited to 'net/batman-adv')
41 files changed, 2107 insertions, 1087 deletions
diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile index 4f4aabbd8eab..42df18f877e9 100644 --- a/net/batman-adv/Makefile +++ b/net/batman-adv/Makefile @@ -1,5 +1,5 @@ # -# Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +# Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: # # Marek Lindner, Simon Wunderlich # @@ -13,9 +13,7 @@ # 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 +# along with this program; if not, see <http://www.gnu.org/licenses/>. # obj-$(CONFIG_BATMAN_ADV) += batman-adv.o diff --git a/net/batman-adv/bat_algo.h b/net/batman-adv/bat_algo.h index a4808c29ea3d..4e49666f8c65 100644 --- a/net/batman-adv/bat_algo.h +++ b/net/batman-adv/bat_algo.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2014 B.A.T.M.A.N. contributors: * * Marek Lindner * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_BAT_ALGO_H_ diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index b9c8a6eedf45..512159bf607f 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" @@ -274,7 +272,14 @@ batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface, if (!neigh_node) goto out; - spin_lock_init(&neigh_node->bat_iv.lq_update_lock); + if (!atomic_inc_not_zero(&hard_iface->refcount)) { + kfree(neigh_node); + neigh_node = NULL; + goto out; + } + + neigh_node->orig_node = orig_neigh; + neigh_node->if_incoming = hard_iface; batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Creating new neighbor %pM for orig_node %pM on interface %s\n", @@ -461,17 +466,9 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet, /* send a batman ogm packet */ static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet) { - struct batadv_hard_iface *hard_iface; struct net_device *soft_iface; struct batadv_priv *bat_priv; struct batadv_hard_iface *primary_if = NULL; - struct batadv_ogm_packet *batadv_ogm_packet; - unsigned char directlink; - uint8_t *packet_pos; - - packet_pos = forw_packet->skb->data; - batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos; - directlink = (batadv_ogm_packet->flags & BATADV_DIRECTLINK ? 1 : 0); if (!forw_packet->if_incoming) { pr_err("Error - can't forward packet: incoming iface not specified\n"); @@ -481,59 +478,48 @@ static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet) soft_iface = forw_packet->if_incoming->soft_iface; bat_priv = netdev_priv(soft_iface); - if (forw_packet->if_incoming->if_status != BATADV_IF_ACTIVE) + if (WARN_ON(!forw_packet->if_outgoing)) goto out; - primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) + if (WARN_ON(forw_packet->if_outgoing->soft_iface != soft_iface)) goto out; - /* multihomed peer assumed - * non-primary OGMs are only broadcasted on their interface - */ - if ((directlink && (batadv_ogm_packet->ttl == 1)) || - (forw_packet->own && (forw_packet->if_incoming != primary_if))) { - /* FIXME: what about aggregated packets ? */ - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "%s packet (originator %pM, seqno %u, TTL %d) on interface %s [%pM]\n", - (forw_packet->own ? "Sending own" : "Forwarding"), - batadv_ogm_packet->orig, - ntohl(batadv_ogm_packet->seqno), - batadv_ogm_packet->ttl, - forw_packet->if_incoming->net_dev->name, - forw_packet->if_incoming->net_dev->dev_addr); - - /* skb is only used once and than forw_packet is free'd */ - batadv_send_skb_packet(forw_packet->skb, - forw_packet->if_incoming, - batadv_broadcast_addr); - forw_packet->skb = NULL; - + if (forw_packet->if_incoming->if_status != BATADV_IF_ACTIVE) goto out; - } - /* broadcast on every interface */ - rcu_read_lock(); - list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { - if (hard_iface->soft_iface != soft_iface) - continue; + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; - batadv_iv_ogm_send_to_if(forw_packet, hard_iface); - } - rcu_read_unlock(); + /* only for one specific outgoing interface */ + batadv_iv_ogm_send_to_if(forw_packet, forw_packet->if_outgoing); out: if (primary_if) batadv_hardif_free_ref(primary_if); } -/* return true if new_packet can be aggregated with forw_packet */ +/** + * 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 soft interface information + * @packet_len: (total) length of the OGM + * @send_time: timestamp (jiffies) when the packet is to be sent + * @direktlink: 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 + * + * Returns true if new_packet can be aggregated with forw_packet + */ static bool batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet, struct batadv_priv *bat_priv, int packet_len, unsigned long send_time, bool directlink, const struct batadv_hard_iface *if_incoming, + const struct batadv_hard_iface *if_outgoing, const struct batadv_forw_packet *forw_packet) { struct batadv_ogm_packet *batadv_ogm_packet; @@ -567,6 +553,10 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet, if (!primary_if) goto out; + /* packet is not leaving on the same interface. */ + if (forw_packet->if_outgoing != if_outgoing) + goto out; + /* packets without direct link flag and high TTL * are flooded through the net */ @@ -608,11 +598,22 @@ out: return res; } -/* create a new aggregated packet and add this packet to it */ +/** + * 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 + */ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, int packet_len, unsigned long 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->soft_iface); @@ -623,6 +624,9 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, if (!atomic_inc_not_zero(&if_incoming->refcount)) return; + if (!atomic_inc_not_zero(&if_outgoing->refcount)) + goto out_free_incoming; + /* own packet should always be scheduled */ if (!own_packet) { if (!batadv_atomic_dec_not_zero(&bat_priv->batman_queue_left)) { @@ -663,6 +667,7 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, forw_packet_aggr->own = own_packet; forw_packet_aggr->if_incoming = if_incoming; + forw_packet_aggr->if_outgoing = if_outgoing; forw_packet_aggr->num_packets = 0; forw_packet_aggr->direct_link_flags = BATADV_NO_FLAGS; forw_packet_aggr->send_time = send_time; @@ -685,6 +690,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, return; out: + batadv_hardif_free_ref(if_outgoing); +out_free_incoming: batadv_hardif_free_ref(if_incoming); } @@ -708,10 +715,21 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr, } } +/** + * batadv_iv_ogm_queue_add - queue up an OGM for transmission + * @bat_priv: the bat priv with all the soft 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 + */ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, unsigned char *packet_buff, int packet_len, struct batadv_hard_iface *if_incoming, + struct batadv_hard_iface *if_outgoing, int own_packet, unsigned long send_time) { /* _aggr -> pointer to the packet we want to aggregate with @@ -737,6 +755,7 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, bat_priv, packet_len, send_time, direct_link, if_incoming, + if_outgoing, forw_packet_pos)) { forw_packet_aggr = forw_packet_pos; break; @@ -760,7 +779,8 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, batadv_iv_ogm_aggregate_new(packet_buff, packet_len, send_time, direct_link, - if_incoming, own_packet); + if_incoming, if_outgoing, + own_packet); } else { batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff, packet_len, direct_link); @@ -773,7 +793,8 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node, struct batadv_ogm_packet *batadv_ogm_packet, bool is_single_hop_neigh, bool is_from_best_next_hop, - struct batadv_hard_iface *if_incoming) + struct batadv_hard_iface *if_incoming, + struct batadv_hard_iface *if_outgoing) { struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); uint16_t tvlv_len; @@ -818,7 +839,8 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node, batadv_iv_ogm_queue_add(bat_priv, (unsigned char *)batadv_ogm_packet, BATADV_OGM_HLEN + tvlv_len, - if_incoming, 0, batadv_iv_ogm_fwd_send_time()); + if_incoming, if_outgoing, 0, + batadv_iv_ogm_fwd_send_time()); } /** @@ -863,10 +885,11 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); unsigned char **ogm_buff = &hard_iface->bat_iv.ogm_buff; struct batadv_ogm_packet *batadv_ogm_packet; - struct batadv_hard_iface *primary_if; + struct batadv_hard_iface *primary_if, *tmp_hard_iface; int *ogm_buff_len = &hard_iface->bat_iv.ogm_buff_len; uint32_t seqno; uint16_t tvlv_len = 0; + unsigned long send_time; primary_if = batadv_primary_if_get_selected(bat_priv); @@ -889,23 +912,60 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) atomic_inc(&hard_iface->bat_iv.ogm_seqno); 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, - batadv_iv_ogm_emit_send_time(bat_priv)); + send_time = batadv_iv_ogm_emit_send_time(bat_priv); + + 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(); + list_for_each_entry_rcu(tmp_hard_iface, &batadv_hardif_list, list) { + if (tmp_hard_iface->soft_iface != hard_iface->soft_iface) + continue; + batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, + *ogm_buff_len, hard_iface, + tmp_hard_iface, 1, send_time); + } + rcu_read_unlock(); + +out: if (primary_if) batadv_hardif_free_ref(primary_if); } +/** + * batadv_iv_ogm_orig_update - use OGM to update corresponding data in an + * originator + * @bat_priv: the bat priv with all the soft 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. + */ static void batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, + struct batadv_orig_ifinfo *orig_ifinfo, const struct ethhdr *ethhdr, const struct batadv_ogm_packet *batadv_ogm_packet, struct batadv_hard_iface *if_incoming, - const unsigned char *tt_buff, + 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, *tmp_neigh_node = NULL; struct batadv_neigh_node *router = NULL; struct batadv_orig_node *orig_node_tmp; @@ -933,12 +993,21 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, if (dup_status != BATADV_NO_DUP) continue; - spin_lock_bh(&tmp_neigh_node->bat_iv.lq_update_lock); - batadv_ring_buffer_set(tmp_neigh_node->bat_iv.tq_recv, - &tmp_neigh_node->bat_iv.tq_index, 0); - tq_avg = batadv_ring_buffer_avg(tmp_neigh_node->bat_iv.tq_recv); - tmp_neigh_node->bat_iv.tq_avg = tq_avg; - spin_unlock_bh(&tmp_neigh_node->bat_iv.lq_update_lock); + /* only update the entry for this outgoing interface */ + neigh_ifinfo = batadv_neigh_ifinfo_get(tmp_neigh_node, + if_outgoing); + if (!neigh_ifinfo) + continue; + + spin_lock_bh(&tmp_neigh_node->ifinfo_lock); + batadv_ring_buffer_set(neigh_ifinfo->bat_iv.tq_recv, + &neigh_ifinfo->bat_iv.tq_index, 0); + tq_avg = batadv_ring_buffer_avg(neigh_ifinfo->bat_iv.tq_recv); + neigh_ifinfo->bat_iv.tq_avg = tq_avg; + spin_unlock_bh(&tmp_neigh_node->ifinfo_lock); + + batadv_neigh_ifinfo_free_ref(neigh_ifinfo); + neigh_ifinfo = NULL; } if (!neigh_node) { @@ -960,39 +1029,49 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, "Updating existing last-hop neighbor of originator\n"); rcu_read_unlock(); + neigh_ifinfo = batadv_neigh_ifinfo_new(neigh_node, if_outgoing); + if (!neigh_ifinfo) + goto out; neigh_node->last_seen = jiffies; - spin_lock_bh(&neigh_node->bat_iv.lq_update_lock); - batadv_ring_buffer_set(neigh_node->bat_iv.tq_recv, - &neigh_node->bat_iv.tq_index, + spin_lock_bh(&neigh_node->ifinfo_lock); + batadv_ring_buffer_set(neigh_ifinfo->bat_iv.tq_recv, + &neigh_ifinfo->bat_iv.tq_index, batadv_ogm_packet->tq); - tq_avg = batadv_ring_buffer_avg(neigh_node->bat_iv.tq_recv); - neigh_node->bat_iv.tq_avg = tq_avg; - spin_unlock_bh(&neigh_node->bat_iv.lq_update_lock); + tq_avg = batadv_ring_buffer_avg(neigh_ifinfo->bat_iv.tq_recv); + neigh_ifinfo->bat_iv.tq_avg = tq_avg; + spin_unlock_bh(&neigh_node->ifinfo_lock); if (dup_status == BATADV_NO_DUP) { - orig_node->last_ttl = batadv_ogm_packet->ttl; - neigh_node->last_ttl = batadv_ogm_packet->ttl; + orig_ifinfo->last_ttl = batadv_ogm_packet->ttl; + neigh_ifinfo->last_ttl = batadv_ogm_packet->ttl; } - batadv_bonding_candidate_add(bat_priv, orig_node, neigh_node); - /* if this neighbor already is our next hop there is nothing * to change */ - router = batadv_orig_node_get_router(orig_node); + router = batadv_orig_router_get(orig_node, if_outgoing); if (router == neigh_node) goto out; - /* if this neighbor does not offer a better TQ we won't consider it */ - if (router && (router->bat_iv.tq_avg > neigh_node->bat_iv.tq_avg)) - 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 && (neigh_node->bat_iv.tq_avg == router->bat_iv.tq_avg)) { + if (router_ifinfo && + (neigh_ifinfo->bat_iv.tq_avg == router_ifinfo->bat_iv.tq_avg)) { orig_node_tmp = router->orig_node; spin_lock_bh(&orig_node_tmp->bat_iv.ogm_cnt_lock); if_num = router->if_incoming->if_num; @@ -1009,7 +1088,7 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, goto out; } - batadv_update_route(bat_priv, orig_node, neigh_node); + batadv_update_route(bat_priv, orig_node, if_outgoing, neigh_node); goto out; unlock: @@ -1019,20 +1098,37 @@ out: batadv_neigh_node_free_ref(neigh_node); if (router) batadv_neigh_node_free_ref(router); + if (neigh_ifinfo) + batadv_neigh_ifinfo_free_ref(neigh_ifinfo); + if (router_ifinfo) + batadv_neigh_ifinfo_free_ref(router_ifinfo); } +/** + * 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 + * + * Returns 1 if the link can be considered bidirectional, 0 otherwise + */ static int 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_incoming, + struct batadv_hard_iface *if_outgoing) { struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct batadv_neigh_node *neigh_node = NULL, *tmp_neigh_node; + struct batadv_neigh_ifinfo *neigh_ifinfo; uint8_t total_count; uint8_t orig_eq_count, neigh_rq_count, neigh_rq_inv, tq_own; unsigned int neigh_rq_inv_cube, neigh_rq_max_cube; int tq_asym_penalty, inv_asym_penalty, if_num, ret = 0; unsigned int combined_tq; + int tq_iface_penalty; /* find corresponding one hop neighbor */ rcu_read_lock(); @@ -1072,7 +1168,13 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node, spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock); if_num = if_incoming->if_num; orig_eq_count = orig_neigh_node->bat_iv.bcast_own_sum[if_num]; - neigh_rq_count = neigh_node->bat_iv.real_packet_count; + 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_free_ref(neigh_ifinfo); + } else { + neigh_rq_count = 0; + } spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock); /* pay attention to not get a value bigger than 100 % */ @@ -1108,15 +1210,31 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node, inv_asym_penalty /= neigh_rq_max_cube; tq_asym_penalty = BATADV_TQ_MAX_VALUE - inv_asym_penalty; - combined_tq = batadv_ogm_packet->tq * tq_own * tq_asym_penalty; - combined_tq /= BATADV_TQ_MAX_VALUE * BATADV_TQ_MAX_VALUE; + /* 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. + */ + tq_iface_penalty = BATADV_TQ_MAX_VALUE; + if (if_outgoing && (if_incoming == if_outgoing) && + batadv_is_wifi_netdev(if_outgoing->net_dev)) + tq_iface_penalty = batadv_hop_penalty(BATADV_TQ_MAX_VALUE, + bat_priv); + + combined_tq = batadv_ogm_packet->tq * + tq_own * + tq_asym_penalty * + tq_iface_penalty; + combined_tq /= BATADV_TQ_MAX_VALUE * + BATADV_TQ_MAX_VALUE * + BATADV_TQ_MAX_VALUE; batadv_ogm_packet->tq = combined_tq; batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "bidirectional: orig = %-15pM neigh = %-15pM => own_bcast = %2i, real recv = %2i, local tq: %3i, asym_penalty: %3i, total tq: %3i\n", + "bidirectional: orig = %-15pM neigh = %-15pM => own_bcast = %2i, real recv = %2i, local tq: %3i, asym_penalty: %3i, iface_penalty: %3i, total tq: %3i, if_incoming = %s, if_outgoing = %s\n", orig_node->orig, orig_neigh_node->orig, total_count, - neigh_rq_count, tq_own, - tq_asym_penalty, batadv_ogm_packet->tq); + neigh_rq_count, tq_own, tq_asym_penalty, tq_iface_penalty, + batadv_ogm_packet->tq, if_incoming->net_dev->name, + if_outgoing ? if_outgoing->net_dev->name : "DEFAULT"); /* if link has the minimum required transmission quality * consider it bidirectional @@ -1136,17 +1254,21 @@ out: * @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 * * Returns duplicate status as enum batadv_dup_status */ static enum batadv_dup_status batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, const struct batadv_ogm_packet *batadv_ogm_packet, - const struct batadv_hard_iface *if_incoming) + const struct batadv_hard_iface *if_incoming, + struct batadv_hard_iface *if_outgoing) { struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct batadv_orig_node *orig_node; - struct batadv_neigh_node *tmp_neigh_node; + struct batadv_orig_ifinfo *orig_ifinfo = NULL; + struct batadv_neigh_node *neigh_node; + struct batadv_neigh_ifinfo *neigh_ifinfo; int is_dup; int32_t seq_diff; int need_update = 0; @@ -1161,27 +1283,37 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, if (!orig_node) return BATADV_NO_DUP; + orig_ifinfo = batadv_orig_ifinfo_new(orig_node, if_outgoing); + if (WARN_ON(!orig_ifinfo)) { + batadv_orig_node_free_ref(orig_node); + return 0; + } + spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock); - seq_diff = seqno - orig_node->last_real_seqno; + seq_diff = seqno - orig_ifinfo->last_real_seqno; /* signalize caller that the packet is to be dropped. */ if (!hlist_empty(&orig_node->neigh_list) && batadv_window_protected(bat_priv, seq_diff, - &orig_node->batman_seqno_reset)) { + &orig_ifinfo->batman_seqno_reset)) { ret = BATADV_PROTECTED; goto out; } rcu_read_lock(); - hlist_for_each_entry_rcu(tmp_neigh_node, - &orig_node->neigh_list, list) { - neigh_addr = tmp_neigh_node->addr; - is_dup = batadv_test_bit(tmp_neigh_node->bat_iv.real_bits, - orig_node->last_real_seqno, + hlist_for_each_entry_rcu(neigh_node, &orig_node->neigh_list, list) { + neigh_ifinfo = batadv_neigh_ifinfo_new(neigh_node, + if_outgoing); + if (!neigh_ifinfo) + continue; + + neigh_addr = neigh_node->addr; + is_dup = batadv_test_bit(neigh_ifinfo->bat_iv.real_bits, + orig_ifinfo->last_real_seqno, seqno); if (batadv_compare_eth(neigh_addr, ethhdr->h_source) && - tmp_neigh_node->if_incoming == if_incoming) { + neigh_node->if_incoming == if_incoming) { set_mark = 1; if (is_dup) ret = BATADV_NEIGH_DUP; @@ -1192,173 +1324,78 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, } /* if the window moved, set the update flag. */ - bitmap = tmp_neigh_node->bat_iv.real_bits; + bitmap = neigh_ifinfo->bat_iv.real_bits; need_update |= batadv_bit_get_packet(bat_priv, bitmap, seq_diff, set_mark); - packet_count = bitmap_weight(tmp_neigh_node->bat_iv.real_bits, + packet_count = bitmap_weight(bitmap, BATADV_TQ_LOCAL_WINDOW_SIZE); - tmp_neigh_node->bat_iv.real_packet_count = packet_count; + neigh_ifinfo->bat_iv.real_packet_count = packet_count; + batadv_neigh_ifinfo_free_ref(neigh_ifinfo); } rcu_read_unlock(); if (need_update) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "updating last_seqno: old %u, new %u\n", - orig_node->last_real_seqno, seqno); - orig_node->last_real_seqno = seqno; + "%s updating last_seqno: old %u, new %u\n", + if_outgoing ? if_outgoing->net_dev->name : "DEFAULT", + orig_ifinfo->last_real_seqno, seqno); + orig_ifinfo->last_real_seqno = seqno; } out: spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock); batadv_orig_node_free_ref(orig_node); + if (orig_ifinfo) + batadv_orig_ifinfo_free_ref(orig_ifinfo); return ret; } -static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, - struct batadv_ogm_packet *batadv_ogm_packet, - const unsigned char *tt_buff, - struct batadv_hard_iface *if_incoming) + +/** + * batadv_iv_ogm_process_per_outif - process a batman iv OGM for an outgoing if + * @skb: the skb containing the OGM + * @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 + */ +static void +batadv_iv_ogm_process_per_outif(const struct 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->soft_iface); - struct batadv_hard_iface *hard_iface; - struct batadv_orig_node *orig_neigh_node, *orig_node, *orig_node_tmp; struct batadv_neigh_node *router = NULL, *router_router = NULL; + struct batadv_orig_node *orig_neigh_node; + struct batadv_orig_ifinfo *orig_ifinfo; struct batadv_neigh_node *orig_neigh_router = NULL; - int has_directlink_flag; - int is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0; - int is_bidirect; - bool is_single_hop_neigh = false; - bool is_from_best_next_hop = false; - int sameseq, similar_ttl; + struct batadv_neigh_ifinfo *router_ifinfo = NULL; + struct batadv_ogm_packet *ogm_packet; enum batadv_dup_status dup_status; - uint32_t if_incoming_seqno; + 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; uint8_t *prev_sender; + int is_bidirect; - /* 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) + /* create a private copy of the skb, as some functions change tq value + * and/or flags. */ - if (batadv_ogm_packet->packet_type != BATADV_IV_OGM) + skb_priv = skb_copy(skb, GFP_ATOMIC); + if (!skb_priv) return; - /* could be changed by schedule_own_packet() */ - if_incoming_seqno = atomic_read(&if_incoming->bat_iv.ogm_seqno); - - if (batadv_ogm_packet->flags & BATADV_DIRECTLINK) - has_directlink_flag = 1; - else - has_directlink_flag = 0; + ethhdr = eth_hdr(skb_priv); + ogm_packet = (struct batadv_ogm_packet *)(skb_priv->data + ogm_offset); - if (batadv_compare_eth(ethhdr->h_source, batadv_ogm_packet->orig)) + dup_status = batadv_iv_ogm_update_seqnos(ethhdr, ogm_packet, + if_incoming, if_outgoing); + if (batadv_compare_eth(ethhdr->h_source, ogm_packet->orig)) 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, 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->tq, - batadv_ogm_packet->ttl, - batadv_ogm_packet->version, has_directlink_flag); - - rcu_read_lock(); - list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { - if (hard_iface->if_status != BATADV_IF_ACTIVE) - continue; - - if (hard_iface->soft_iface != if_incoming->soft_iface) - continue; - - if (batadv_compare_eth(ethhdr->h_source, - hard_iface->net_dev->dev_addr)) - is_my_addr = 1; - - if (batadv_compare_eth(batadv_ogm_packet->orig, - hard_iface->net_dev->dev_addr)) - is_my_orig = 1; - - if (batadv_compare_eth(batadv_ogm_packet->prev_sender, - hard_iface->net_dev->dev_addr)) - is_my_oldorig = 1; - } - rcu_read_unlock(); - - if (is_my_addr) { - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Drop packet: received my own broadcast (sender: %pM)\n", - ethhdr->h_source); - return; - } - - if (is_my_orig) { - unsigned long *word; - int offset; - int32_t bit_pos; - int16_t if_num; - uint8_t *weight; - - orig_neigh_node = batadv_iv_ogm_orig_get(bat_priv, - ethhdr->h_source); - if (!orig_neigh_node) - return; - - /* neighbor has to indicate direct link and it has to - * come via the corresponding interface - * save packet seqno for bidirectional check - */ - if (has_directlink_flag && - batadv_compare_eth(if_incoming->net_dev->dev_addr, - batadv_ogm_packet->orig)) { - if_num = if_incoming->if_num; - offset = if_num * BATADV_NUM_WORDS; - - spin_lock_bh(&orig_neigh_node->bat_iv.ogm_cnt_lock); - word = &(orig_neigh_node->bat_iv.bcast_own[offset]); - bit_pos = if_incoming_seqno - 2; - bit_pos -= ntohl(batadv_ogm_packet->seqno); - batadv_set_bit(word, bit_pos); - weight = &orig_neigh_node->bat_iv.bcast_own_sum[if_num]; - *weight = bitmap_weight(word, - BATADV_TQ_LOCAL_WINDOW_SIZE); - spin_unlock_bh(&orig_neigh_node->bat_iv.ogm_cnt_lock); - } - - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Drop packet: originator packet from myself (via neighbor)\n"); - batadv_orig_node_free_ref(orig_neigh_node); - return; - } - - 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 (batadv_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, batadv_ogm_packet->orig); - if (!orig_node) - return; - - dup_status = batadv_iv_ogm_update_seqnos(ethhdr, batadv_ogm_packet, - if_incoming); - if (dup_status == BATADV_PROTECTED) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Drop packet: packet within seqno protection time (sender: %pM)\n", @@ -1366,27 +1403,28 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, goto out; } - if (batadv_ogm_packet->tq == 0) { + if (ogm_packet->tq == 0) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Drop packet: originator packet with tq equal 0\n"); goto out; } - router = batadv_orig_node_get_router(orig_node); + router = batadv_orig_router_get(orig_node, if_outgoing); if (router) { - orig_node_tmp = router->orig_node; - router_router = batadv_orig_node_get_router(orig_node_tmp); + router_router = batadv_orig_router_get(router->orig_node, + if_outgoing); + router_ifinfo = batadv_neigh_ifinfo_get(router, if_outgoing); } - if ((router && router->bat_iv.tq_avg != 0) && + if ((router_ifinfo && router_ifinfo->bat_iv.tq_avg != 0) && (batadv_compare_eth(router->addr, ethhdr->h_source))) is_from_best_next_hop = true; - prev_sender = batadv_ogm_packet->prev_sender; + prev_sender = ogm_packet->prev_sender; /* avoid temporary routing loops */ if (router && router_router && (batadv_compare_eth(router->addr, prev_sender)) && - !(batadv_compare_eth(batadv_ogm_packet->orig, 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", @@ -1394,7 +1432,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 (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 @@ -1410,9 +1449,10 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, /* Update nc_nodes of the originator */ batadv_nc_update_nc_node(bat_priv, orig_node, orig_neigh_node, - batadv_ogm_packet, is_single_hop_neigh); + ogm_packet, is_single_hop_neigh); - orig_neigh_router = batadv_orig_node_get_router(orig_neigh_node); + orig_neigh_router = batadv_orig_router_get(orig_neigh_node, + if_outgoing); /* drop packet if sender is not a direct neighbor and if we * don't route towards it @@ -1424,28 +1464,48 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, } is_bidirect = batadv_iv_ogm_calc_tq(orig_node, orig_neigh_node, - batadv_ogm_packet, if_incoming); - - batadv_bonding_save_primary(orig_node, orig_neigh_node, - batadv_ogm_packet); + ogm_packet, if_incoming, + if_outgoing); /* update ranking if it is not a duplicate or has the same * seqno and similar ttl as the non-duplicate */ - sameseq = orig_node->last_real_seqno == ntohl(batadv_ogm_packet->seqno); - similar_ttl = orig_node->last_ttl - 3 <= batadv_ogm_packet->ttl; + orig_ifinfo = batadv_orig_ifinfo_new(orig_node, if_outgoing); + if (!orig_ifinfo) + goto out_neigh; + + sameseq = orig_ifinfo->last_real_seqno == ntohl(ogm_packet->seqno); + similar_ttl = (orig_ifinfo->last_ttl - 3) <= ogm_packet->ttl; + if (is_bidirect && ((dup_status == BATADV_NO_DUP) || - (sameseq && similar_ttl))) - batadv_iv_ogm_orig_update(bat_priv, orig_node, ethhdr, - batadv_ogm_packet, if_incoming, - tt_buff, dup_status); + (sameseq && similar_ttl))) { + batadv_iv_ogm_orig_update(bat_priv, orig_node, + orig_ifinfo, ethhdr, + ogm_packet, if_incoming, + if_outgoing, dup_status); + } + batadv_orig_ifinfo_free_ref(orig_ifinfo); + + /* 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, batadv_ogm_packet, + batadv_iv_ogm_forward(orig_node, ethhdr, ogm_packet, is_single_hop_neigh, - is_from_best_next_hop, if_incoming); + 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"); @@ -1467,9 +1527,9 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Forwarding packet: rebroadcast originator packet\n"); - batadv_iv_ogm_forward(orig_node, ethhdr, batadv_ogm_packet, + batadv_iv_ogm_forward(orig_node, ethhdr, ogm_packet, is_single_hop_neigh, is_from_best_next_hop, - if_incoming); + if_incoming, if_outgoing); out_neigh: if ((orig_neigh_node) && (!is_single_hop_neigh)) @@ -1482,6 +1542,165 @@ out: if (orig_neigh_router) batadv_neigh_node_free_ref(orig_neigh_router); + kfree_skb(skb_priv); +} + +/** + * batadv_iv_ogm_process - process an incoming batman iv OGM + * @skb: the skb containing the OGM + * @ogm_offset: offset to the OGM which should be processed (for aggregates) + * @if_incoming: the interface where this packet was receved + */ +static void batadv_iv_ogm_process(const struct sk_buff *skb, int ogm_offset, + struct batadv_hard_iface *if_incoming) +{ + struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); + struct batadv_orig_node *orig_neigh_node, *orig_node; + struct batadv_hard_iface *hard_iface; + struct batadv_ogm_packet *ogm_packet; + uint32_t if_incoming_seqno; + bool has_directlink_flag; + struct ethhdr *ethhdr; + bool is_my_oldorig = false; + bool is_my_addr = false; + bool is_my_orig = false; + + ogm_packet = (struct batadv_ogm_packet *)(skb->data + ogm_offset); + ethhdr = eth_hdr(skb); + + /* 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 (ogm_packet->flags & BATADV_DIRECTLINK) + has_directlink_flag = true; + else + has_directlink_flag = false; + + 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, tq %d, TTL %d, V %d, IDF %d)\n", + ethhdr->h_source, if_incoming->net_dev->name, + if_incoming->net_dev->dev_addr, ogm_packet->orig, + ogm_packet->prev_sender, ntohl(ogm_packet->seqno), + ogm_packet->tq, ogm_packet->ttl, + ogm_packet->version, has_directlink_flag); + + rcu_read_lock(); + list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { + if (hard_iface->if_status != BATADV_IF_ACTIVE) + continue; + + if (hard_iface->soft_iface != if_incoming->soft_iface) + continue; + + if (batadv_compare_eth(ethhdr->h_source, + hard_iface->net_dev->dev_addr)) + is_my_addr = true; + + if (batadv_compare_eth(ogm_packet->orig, + hard_iface->net_dev->dev_addr)) + is_my_orig = true; + + if (batadv_compare_eth(ogm_packet->prev_sender, + hard_iface->net_dev->dev_addr)) + is_my_oldorig = true; + } + rcu_read_unlock(); + + if (is_my_addr) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Drop packet: received my own broadcast (sender: %pM)\n", + ethhdr->h_source); + return; + } + + if (is_my_orig) { + unsigned long *word; + int offset; + int32_t bit_pos; + int16_t if_num; + uint8_t *weight; + + orig_neigh_node = batadv_iv_ogm_orig_get(bat_priv, + ethhdr->h_source); + if (!orig_neigh_node) + return; + + /* neighbor has to indicate direct link and it has to + * come via the corresponding interface + * save packet seqno for bidirectional check + */ + if (has_directlink_flag && + batadv_compare_eth(if_incoming->net_dev->dev_addr, + ogm_packet->orig)) { + if_num = if_incoming->if_num; + offset = if_num * BATADV_NUM_WORDS; + + spin_lock_bh(&orig_neigh_node->bat_iv.ogm_cnt_lock); + word = &(orig_neigh_node->bat_iv.bcast_own[offset]); + bit_pos = if_incoming_seqno - 2; + bit_pos -= ntohl(ogm_packet->seqno); + batadv_set_bit(word, bit_pos); + weight = &orig_neigh_node->bat_iv.bcast_own_sum[if_num]; + *weight = bitmap_weight(word, + BATADV_TQ_LOCAL_WINDOW_SIZE); + spin_unlock_bh(&orig_neigh_node->bat_iv.ogm_cnt_lock); + } + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Drop packet: originator packet from myself (via neighbor)\n"); + batadv_orig_node_free_ref(orig_neigh_node); + return; + } + + 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; + + batadv_iv_ogm_process_per_outif(skb, ogm_offset, orig_node, + if_incoming, BATADV_IF_DEFAULT); + + rcu_read_lock(); + list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { + if (hard_iface->if_status != BATADV_IF_ACTIVE) + continue; + + if (hard_iface->soft_iface != bat_priv->soft_iface) + continue; + + batadv_iv_ogm_process_per_outif(skb, ogm_offset, orig_node, + if_incoming, hard_iface); + } + rcu_read_unlock(); + batadv_orig_node_free_ref(orig_node); } @@ -1489,11 +1708,9 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb, struct batadv_hard_iface *if_incoming) { struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); - struct batadv_ogm_packet *batadv_ogm_packet; - struct ethhdr *ethhdr; - int buff_pos = 0, packet_len; - unsigned char *tvlv_buff, *packet_buff; + struct batadv_ogm_packet *ogm_packet; uint8_t *packet_pos; + int ogm_offset; bool ret; ret = batadv_check_management_packet(skb, if_incoming, BATADV_OGM_HLEN); @@ -1510,24 +1727,19 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb, batadv_add_counter(bat_priv, BATADV_CNT_MGMT_RX_BYTES, skb->len + ETH_HLEN); - packet_len = skb_headlen(skb); - ethhdr = eth_hdr(skb); - packet_buff = skb->data; - batadv_ogm_packet = (struct batadv_ogm_packet *)packet_buff; + ogm_offset = 0; + ogm_packet = (struct batadv_ogm_packet *)skb->data; /* unpack the aggregated packets and process them one by one */ - while (batadv_iv_ogm_aggr_packet(buff_pos, packet_len, - batadv_ogm_packet->tvlv_len)) { - tvlv_buff = packet_buff + buff_pos + BATADV_OGM_HLEN; - - batadv_iv_ogm_process(ethhdr, batadv_ogm_packet, - tvlv_buff, if_incoming); + while (batadv_iv_ogm_aggr_packet(ogm_offset, skb_headlen(skb), + ogm_packet->tvlv_len)) { + batadv_iv_ogm_process(skb, ogm_offset, if_incoming); - buff_pos += BATADV_OGM_HLEN; - buff_pos += ntohs(batadv_ogm_packet->tvlv_len); + ogm_offset += BATADV_OGM_HLEN; + ogm_offset += ntohs(ogm_packet->tvlv_len); - packet_pos = packet_buff + buff_pos; - batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos; + packet_pos = skb->data + ogm_offset; + ogm_packet = (struct batadv_ogm_packet *)packet_pos; } kfree_skb(skb); @@ -1535,17 +1747,49 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb, } /** + * batadv_iv_ogm_orig_print_neigh - print neighbors for the originator table + * @orig_node: the orig_node for which the neighbors are printed + * @if_outgoing: outgoing interface for these entries + * @seq: debugfs table seq_file struct + * + * Must be called while holding an rcu lock. + */ +static void +batadv_iv_ogm_orig_print_neigh(struct batadv_orig_node *orig_node, + struct batadv_hard_iface *if_outgoing, + struct seq_file *seq) +{ + struct batadv_neigh_node *neigh_node; + struct batadv_neigh_ifinfo *n_ifinfo; + + hlist_for_each_entry_rcu(neigh_node, &orig_node->neigh_list, list) { + n_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing); + if (!n_ifinfo) + continue; + + seq_printf(seq, " %pM (%3i)", + neigh_node->addr, + n_ifinfo->bat_iv.tq_avg); + + batadv_neigh_ifinfo_free_ref(n_ifinfo); + } +} + +/** * batadv_iv_ogm_orig_print - print the originator table * @bat_priv: the bat priv with all the soft interface information * @seq: debugfs table seq_file struct + * @if_outgoing: the outgoing interface for which this should be printed */ static void batadv_iv_ogm_orig_print(struct batadv_priv *bat_priv, - struct seq_file *seq) + struct seq_file *seq, + struct batadv_hard_iface *if_outgoing) { - struct batadv_neigh_node *neigh_node, *neigh_node_tmp; + struct batadv_neigh_node *neigh_node; struct batadv_hashtable *hash = bat_priv->orig_hash; int last_seen_msecs, last_seen_secs; struct batadv_orig_node *orig_node; + struct batadv_neigh_ifinfo *n_ifinfo; unsigned long last_seen_jiffies; struct hlist_head *head; int batman_count = 0; @@ -1560,11 +1804,17 @@ static void batadv_iv_ogm_orig_print(struct batadv_priv *bat_priv, rcu_read_lock(); hlist_for_each_entry_rcu(orig_node, head, hash_entry) { - neigh_node = batadv_orig_node_get_router(orig_node); + neigh_node = batadv_orig_router_get(orig_node, + if_outgoing); if (!neigh_node) continue; - if (neigh_node->bat_iv.tq_avg == 0) + n_ifinfo = batadv_neigh_ifinfo_get(neigh_node, + if_outgoing); + if (!n_ifinfo) + goto next; + + if (n_ifinfo->bat_iv.tq_avg == 0) goto next; last_seen_jiffies = jiffies - orig_node->last_seen; @@ -1574,22 +1824,19 @@ static void batadv_iv_ogm_orig_print(struct batadv_priv *bat_priv, seq_printf(seq, "%pM %4i.%03is (%3i) %pM [%10s]:", orig_node->orig, last_seen_secs, - last_seen_msecs, neigh_node->bat_iv.tq_avg, + last_seen_msecs, n_ifinfo->bat_iv.tq_avg, neigh_node->addr, neigh_node->if_incoming->net_dev->name); - hlist_for_each_entry_rcu(neigh_node_tmp, - &orig_node->neigh_list, list) { - seq_printf(seq, " %pM (%3i)", - neigh_node_tmp->addr, - neigh_node_tmp->bat_iv.tq_avg); - } - + batadv_iv_ogm_orig_print_neigh(orig_node, if_outgoing, + seq); seq_puts(seq, "\n"); batman_count++; next: batadv_neigh_node_free_ref(neigh_node); + if (n_ifinfo) + batadv_neigh_ifinfo_free_ref(n_ifinfo); } rcu_read_unlock(); } @@ -1601,37 +1848,84 @@ next: /** * 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 * * Returns 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 */ static int batadv_iv_ogm_neigh_cmp(struct batadv_neigh_node *neigh1, - struct batadv_neigh_node *neigh2) + struct batadv_hard_iface *if_outgoing1, + struct batadv_neigh_node *neigh2, + struct batadv_hard_iface *if_outgoing2) { + struct batadv_neigh_ifinfo *neigh1_ifinfo, *neigh2_ifinfo; uint8_t tq1, tq2; + int diff; + + neigh1_ifinfo = batadv_neigh_ifinfo_get(neigh1, if_outgoing1); + neigh2_ifinfo = batadv_neigh_ifinfo_get(neigh2, if_outgoing2); + + if (!neigh1_ifinfo || !neigh2_ifinfo) { + diff = 0; + goto out; + } + + tq1 = neigh1_ifinfo->bat_iv.tq_avg; + tq2 = neigh2_ifinfo->bat_iv.tq_avg; + diff = tq1 - tq2; - tq1 = neigh1->bat_iv.tq_avg; - tq2 = neigh2->bat_iv.tq_avg; +out: + if (neigh1_ifinfo) + batadv_neigh_ifinfo_free_ref(neigh1_ifinfo); + if (neigh2_ifinfo) + batadv_neigh_ifinfo_free_ref(neigh2_ifinfo); - return tq1 - tq2; + return diff; } /** * batadv_iv_ogm_neigh_is_eob - check if neigh1 is equally good or better than * neigh2 from the metric prospective * @neigh1: the first neighbor object of the comparison + * @if_outgoing: outgoing interface for the first neighbor * @neigh2: the second neighbor object of the comparison - * - * Returns true if the metric via neigh1 is equally good or better than the - * metric via neigh2, false otherwise. + * @if_outgoing2: outgoing interface for the second neighbor + + * Returns true if the metric via neigh1 is equally good or better than + * the metric via neigh2, false otherwise. */ -static bool batadv_iv_ogm_neigh_is_eob(struct batadv_neigh_node *neigh1, - struct batadv_neigh_node *neigh2) +static bool +batadv_iv_ogm_neigh_is_eob(struct batadv_neigh_node *neigh1, + struct batadv_hard_iface *if_outgoing1, + struct batadv_neigh_node *neigh2, + struct batadv_hard_iface *if_outgoing2) { - int diff = batadv_iv_ogm_neigh_cmp(neigh1, neigh2); + struct batadv_neigh_ifinfo *neigh1_ifinfo, *neigh2_ifinfo; + uint8_t tq1, tq2; + bool ret; - return diff > -BATADV_TQ_SIMILARITY_THRESHOLD; + neigh1_ifinfo = batadv_neigh_ifinfo_get(neigh1, if_outgoing1); + neigh2_ifinfo = batadv_neigh_ifinfo_get(neigh2, if_outgoing2); + + /* we can't say that the metric is better */ + if (!neigh1_ifinfo || !neigh2_ifinfo) { + ret = false; + goto out; + } + + tq1 = neigh1_ifinfo->bat_iv.tq_avg; + tq2 = neigh2_ifinfo->bat_iv.tq_avg; + ret = (tq1 - tq2) > -BATADV_TQ_SIMILARITY_THRESHOLD; + +out: + if (neigh1_ifinfo) + batadv_neigh_ifinfo_free_ref(neigh1_ifinfo); + if (neigh2_ifinfo) + batadv_neigh_ifinfo_free_ref(neigh2_ifinfo); + + return ret; } static struct batadv_algo_ops batadv_batman_iv __read_mostly = { diff --git a/net/batman-adv/bitarray.c b/net/batman-adv/bitarray.c index 973982414d58..9586750022f5 100644 --- a/net/batman-adv/bitarray.c +++ b/net/batman-adv/bitarray.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2006-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2006-2014 B.A.T.M.A.N. contributors: * * Simon Wunderlich, Marek Lindner * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" diff --git a/net/batman-adv/bitarray.h b/net/batman-adv/bitarray.h index a81b9322e382..cc2407351d36 100644 --- a/net/batman-adv/bitarray.h +++ b/net/batman-adv/bitarray.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2006-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2006-2014 B.A.T.M.A.N. contributors: * * Simon Wunderlich, Marek Lindner * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_BITARRAY_H_ diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 28eb5e6d0a02..05f0712be5e7 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2011-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2014 B.A.T.M.A.N. contributors: * * Simon Wunderlich * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" @@ -132,7 +130,9 @@ static void batadv_claim_free_ref(struct batadv_bla_claim *claim) call_rcu(&claim->rcu, batadv_claim_free_rcu); } -/* @bat_priv: the bat priv with all the soft interface information +/** + * batadv_claim_hash_find + * @bat_priv: the bat priv with all the soft interface information * @data: search data (may be local/static data) * * looks for a claim in the hash, and returns it if found @@ -451,7 +451,9 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv, batadv_backbone_gw_free_ref(backbone_gw); } -/* @bat_priv: the bat priv with all the soft interface information +/** + * batadv_bla_answer_request - answer a bla request by sending own claims + * @bat_priv: the bat priv with all the soft interface information * @vid: the vid where the request came on * * Repeat all of our own claims, and finally send an ANNOUNCE frame @@ -497,7 +499,9 @@ static void batadv_bla_answer_request(struct batadv_priv *bat_priv, batadv_backbone_gw_free_ref(backbone_gw); } -/* @backbone_gw: the backbone gateway from whom we are out of sync +/** + * batadv_bla_send_request - send a request to repeat claims + * @backbone_gw: the backbone gateway from whom we are out of sync * * When the crc is wrong, ask the backbone gateway for a full table update. * After the request, it will repeat all of his own claims and finally @@ -522,7 +526,9 @@ static void batadv_bla_send_request(struct batadv_bla_backbone_gw *backbone_gw) } } -/* @bat_priv: the bat priv with all the soft interface information +/** + * batadv_bla_send_announce + * @bat_priv: the bat priv with all the soft interface information * @backbone_gw: our backbone gateway which should be announced * * This function sends an announcement. It is called from multiple @@ -846,7 +852,9 @@ static int batadv_check_claim_group(struct batadv_priv *bat_priv, } -/* @bat_priv: the bat priv with all the soft interface information +/** + * batadv_bla_process_claim + * @bat_priv: the bat priv with all the soft interface information * @skb: the frame to be checked * * Check if this is a claim frame, and process it accordingly. @@ -1313,7 +1321,9 @@ out: -/* @bat_priv: the bat priv with all the soft interface information +/** + * batadv_bla_is_backbone_gw_orig + * @bat_priv: the bat priv with all the soft interface information * @orig: originator mac address * @vid: VLAN identifier * diff --git a/net/batman-adv/bridge_loop_avoidance.h b/net/batman-adv/bridge_loop_avoidance.h index da173e760e77..43c985d92c3e 100644 --- a/net/batman-adv/bridge_loop_avoidance.h +++ b/net/batman-adv/bridge_loop_avoidance.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2014 B.A.T.M.A.N. contributors: * * Simon Wunderlich * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_BLA_H_ diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c index 049a7a2ac5b6..b758881be108 100644 --- a/net/batman-adv/debugfs.c +++ b/net/batman-adv/debugfs.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2010-2014 B.A.T.M.A.N. contributors: * * Marek Lindner * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" @@ -250,6 +248,19 @@ static int batadv_originators_open(struct inode *inode, struct file *file) return single_open(file, batadv_orig_seq_print_text, net_dev); } +/** + * batadv_originators_hardif_open - handles debugfs output for the + * originator table of an hard interface + * @inode: inode pointer to debugfs file + * @file: pointer to the seq_file + */ +static int batadv_originators_hardif_open(struct inode *inode, + struct file *file) +{ + struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, batadv_orig_hardif_seq_print_text, net_dev); +} + static int batadv_gateways_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; @@ -371,6 +382,28 @@ static struct batadv_debuginfo *batadv_mesh_debuginfos[] = { NULL, }; +#define BATADV_HARDIF_DEBUGINFO(_name, _mode, _open) \ +struct batadv_debuginfo batadv_hardif_debuginfo_##_name = { \ + .attr = { \ + .name = __stringify(_name), \ + .mode = _mode, \ + }, \ + .fops = { \ + .owner = THIS_MODULE, \ + .open = _open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ + }, \ +}; +static BATADV_HARDIF_DEBUGINFO(originators, S_IRUGO, + batadv_originators_hardif_open); + +static struct batadv_debuginfo *batadv_hardif_debuginfos[] = { + &batadv_hardif_debuginfo_originators, + NULL, +}; + void batadv_debugfs_init(void) { struct batadv_debuginfo **bat_debug; @@ -398,6 +431,7 @@ void batadv_debugfs_init(void) return; err: debugfs_remove_recursive(batadv_debugfs); + batadv_debugfs = NULL; } void batadv_debugfs_destroy(void) @@ -406,6 +440,59 @@ void batadv_debugfs_destroy(void) batadv_debugfs = NULL; } +/** + * batadv_debugfs_add_hardif - creates the base directory for a hard interface + * in debugfs. + * @hard_iface: hard interface which should be added. + */ +int batadv_debugfs_add_hardif(struct batadv_hard_iface *hard_iface) +{ + struct batadv_debuginfo **bat_debug; + struct dentry *file; + + if (!batadv_debugfs) + goto out; + + hard_iface->debug_dir = debugfs_create_dir(hard_iface->net_dev->name, + batadv_debugfs); + if (!hard_iface->debug_dir) + goto out; + + for (bat_debug = batadv_hardif_debuginfos; *bat_debug; ++bat_debug) { + file = debugfs_create_file(((*bat_debug)->attr).name, + S_IFREG | ((*bat_debug)->attr).mode, + hard_iface->debug_dir, + hard_iface->net_dev, + &(*bat_debug)->fops); + if (!file) + goto rem_attr; + } + + return 0; +rem_attr: + debugfs_remove_recursive(hard_iface->debug_dir); + hard_iface->debug_dir = NULL; +out: +#ifdef CONFIG_DEBUG_FS + return -ENOMEM; +#else + return 0; +#endif /* CONFIG_DEBUG_FS */ +} + +/** + * batadv_debugfs_del_hardif - delete the base directory for a hard interface + * in debugfs. + * @hard_iface: hard interface which is deleted. + */ +void batadv_debugfs_del_hardif(struct batadv_hard_iface *hard_iface) +{ + if (batadv_debugfs) { + debugfs_remove_recursive(hard_iface->debug_dir); + hard_iface->debug_dir = NULL; + } +} + int batadv_debugfs_add_meshif(struct net_device *dev) { struct batadv_priv *bat_priv = netdev_priv(dev); diff --git a/net/batman-adv/debugfs.h b/net/batman-adv/debugfs.h index f8c3849edff4..37c4d6ddd04d 100644 --- a/net/batman-adv/debugfs.h +++ b/net/batman-adv/debugfs.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2010-2014 B.A.T.M.A.N. contributors: * * Marek Lindner * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_DEBUGFS_H_ @@ -26,5 +24,7 @@ void batadv_debugfs_init(void); void batadv_debugfs_destroy(void); int batadv_debugfs_add_meshif(struct net_device *dev); void batadv_debugfs_del_meshif(struct net_device *dev); +int batadv_debugfs_add_hardif(struct batadv_hard_iface *hard_iface); +void batadv_debugfs_del_hardif(struct batadv_hard_iface *hard_iface); #endif /* _NET_BATMAN_ADV_DEBUGFS_H_ */ diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index b316a4cb6f14..edee50411892 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2011-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2014 B.A.T.M.A.N. contributors: * * Antonio Quartulli * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/if_ether.h> @@ -141,7 +139,7 @@ static int batadv_compare_dat(const struct hlist_node *node, const void *data2) const void *data1 = container_of(node, struct batadv_dat_entry, hash_entry); - return (memcmp(data1, data2, sizeof(__be32)) == 0 ? 1 : 0); + return memcmp(data1, data2, sizeof(__be32)) == 0 ? 1 : 0; } /** @@ -591,7 +589,8 @@ static bool batadv_dat_send_data(struct batadv_priv *bat_priv, if (cand[i].type == BATADV_DAT_CANDIDATE_NOT_FOUND) continue; - neigh_node = batadv_orig_node_get_router(cand[i].orig_node); + neigh_node = batadv_orig_router_get(cand[i].orig_node, + BATADV_IF_DEFAULT); if (!neigh_node) goto free_orig; @@ -1039,9 +1038,9 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, if (hdr_size == sizeof(struct batadv_unicast_4addr_packet)) err = batadv_send_skb_via_tt_4addr(bat_priv, skb_new, BATADV_P_DAT_CACHE_REPLY, - vid); + NULL, vid); else - err = batadv_send_skb_via_tt(bat_priv, skb_new, vid); + err = batadv_send_skb_via_tt(bat_priv, skb_new, NULL, vid); if (err != NET_XMIT_DROP) { batadv_inc_counter(bat_priv, BATADV_CNT_DAT_CACHED_REPLY_TX); diff --git a/net/batman-adv/distributed-arp-table.h b/net/batman-adv/distributed-arp-table.h index 60d853beb8d8..ac9be9b67a25 100644 --- a/net/batman-adv/distributed-arp-table.h +++ b/net/batman-adv/distributed-arp-table.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2014 B.A.T.M.A.N. contributors: * * Antonio Quartulli * @@ -12,13 +12,11 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ -#ifndef _NET_BATMAN_ADV_ARP_H_ -#define _NET_BATMAN_ADV_ARP_H_ +#ifndef _NET_BATMAN_ADV_DISTRIBUTED_ARP_TABLE_H_ +#define _NET_BATMAN_ADV_DISTRIBUTED_ARP_TABLE_H_ #ifdef CONFIG_BATMAN_ADV_DAT @@ -169,4 +167,4 @@ static inline void batadv_dat_inc_counter(struct batadv_priv *bat_priv, #endif /* CONFIG_BATMAN_ADV_DAT */ -#endif /* _NET_BATMAN_ADV_ARP_H_ */ +#endif /* _NET_BATMAN_ADV_DISTRIBUTED_ARP_TABLE_H_ */ diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 6ddb6145ffb5..88df9b1d552d 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2013-2014 B.A.T.M.A.N. contributors: * * Martin Hundebøll <martin@hundeboll.net> * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h index ca029e2676e7..5d7a0e66a22b 100644 --- a/net/batman-adv/fragmentation.h +++ b/net/batman-adv/fragmentation.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2013-2014 B.A.T.M.A.N. contributors: * * Martin Hundebøll <martin@hundeboll.net> * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_FRAGMENTATION_H_ diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 2449afaa7638..55cf2260d295 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2009-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2009-2014 B.A.T.M.A.N. contributors: * * Marek Lindner * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" @@ -30,11 +28,17 @@ #include <linux/udp.h> #include <linux/if_vlan.h> -/* This is the offset of the options field in a dhcp packet starting at - * the beginning of the dhcp header +/* These are the offsets of the "hw type" and "hw address length" in the dhcp + * packet starting at the beginning of the dhcp header */ -#define BATADV_DHCP_OPTIONS_OFFSET 240 -#define BATADV_DHCP_REQUEST 3 +#define BATADV_DHCP_HTYPE_OFFSET 1 +#define BATADV_DHCP_HLEN_OFFSET 2 +/* Value of htype representing Ethernet */ +#define BATADV_DHCP_HTYPE_ETHERNET 0x01 +/* This is the offset of the "chaddr" field in the dhcp packet starting at the + * beginning of the dhcp header + */ +#define BATADV_DHCP_CHADDR_OFFSET 28 static void batadv_gw_node_free_ref(struct batadv_gw_node *gw_node) { @@ -105,7 +109,18 @@ static void batadv_gw_select(struct batadv_priv *bat_priv, spin_unlock_bh(&bat_priv->gw.list_lock); } -void batadv_gw_deselect(struct batadv_priv *bat_priv) +/** + * batadv_gw_reselect - force a gateway reselection + * @bat_priv: the bat priv with all the soft interface information + * + * Set a flag to remind the GW component to perform a new gateway reselection. + * However this function does not ensure that the current gateway is going to be + * deselected. The reselection mechanism may elect the same gateway once again. + * + * This means that invoking batadv_gw_reselect() does not guarantee a gateway + * change and therefore a uevent is not necessarily expected. + */ +void batadv_gw_reselect(struct batadv_priv *bat_priv) { atomic_set(&bat_priv->gw.reselect, 1); } @@ -114,6 +129,7 @@ static struct batadv_gw_node * batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv) { struct batadv_neigh_node *router; + struct batadv_neigh_ifinfo *router_ifinfo; struct batadv_gw_node *gw_node, *curr_gw = NULL; uint32_t max_gw_factor = 0, tmp_gw_factor = 0; uint32_t gw_divisor; @@ -130,14 +146,19 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv) continue; orig_node = gw_node->orig_node; - router = batadv_orig_node_get_router(orig_node); + router = batadv_orig_router_get(orig_node, BATADV_IF_DEFAULT); if (!router) continue; + router_ifinfo = batadv_neigh_ifinfo_get(router, + BATADV_IF_DEFAULT); + if (!router_ifinfo) + goto next; + if (!atomic_inc_not_zero(&gw_node->refcount)) goto next; - tq_avg = router->bat_iv.tq_avg; + tq_avg = router_ifinfo->bat_iv.tq_avg; switch (atomic_read(&bat_priv->gw_sel_class)) { case 1: /* fast connection */ @@ -182,6 +203,8 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv) next: batadv_neigh_node_free_ref(router); + if (router_ifinfo) + batadv_neigh_ifinfo_free_ref(router_ifinfo); } rcu_read_unlock(); @@ -207,6 +230,11 @@ void batadv_gw_check_client_stop(struct batadv_priv *bat_priv) if (!curr_gw) return; + /* deselect the current gateway so that next time that client mode is + * enabled a proper GW_ADD event can be sent + */ + batadv_gw_select(bat_priv, NULL); + /* if batman-adv is switching the gw client mode off and a gateway was * already selected, send a DEL uevent */ @@ -219,6 +247,7 @@ void batadv_gw_election(struct batadv_priv *bat_priv) { struct batadv_gw_node *curr_gw = NULL, *next_gw = NULL; struct batadv_neigh_node *router = NULL; + struct batadv_neigh_ifinfo *router_ifinfo = NULL; char gw_addr[18] = { '\0' }; if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_CLIENT) @@ -237,9 +266,17 @@ void batadv_gw_election(struct batadv_priv *bat_priv) if (next_gw) { sprintf(gw_addr, "%pM", next_gw->orig_node->orig); - router = batadv_orig_node_get_router(next_gw->orig_node); + router = batadv_orig_router_get(next_gw->orig_node, + BATADV_IF_DEFAULT); if (!router) { - batadv_gw_deselect(bat_priv); + batadv_gw_reselect(bat_priv); + goto out; + } + + router_ifinfo = batadv_neigh_ifinfo_get(router, + BATADV_IF_DEFAULT); + if (!router_ifinfo) { + batadv_gw_reselect(bat_priv); goto out; } } @@ -256,7 +293,8 @@ void batadv_gw_election(struct batadv_priv *bat_priv) next_gw->bandwidth_down / 10, next_gw->bandwidth_down % 10, next_gw->bandwidth_up / 10, - next_gw->bandwidth_up % 10, router->bat_iv.tq_avg); + next_gw->bandwidth_up % 10, + router_ifinfo->bat_iv.tq_avg); batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_ADD, gw_addr); } else { @@ -266,7 +304,8 @@ void batadv_gw_election(struct batadv_priv *bat_priv) next_gw->bandwidth_down / 10, next_gw->bandwidth_down % 10, next_gw->bandwidth_up / 10, - next_gw->bandwidth_up % 10, router->bat_iv.tq_avg); + next_gw->bandwidth_up % 10, + router_ifinfo->bat_iv.tq_avg); batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_CHANGE, gw_addr); } @@ -280,33 +319,47 @@ out: batadv_gw_node_free_ref(next_gw); if (router) batadv_neigh_node_free_ref(router); + if (router_ifinfo) + batadv_neigh_ifinfo_free_ref(router_ifinfo); } void batadv_gw_check_election(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node) { + struct batadv_neigh_ifinfo *router_orig_tq = NULL; + struct batadv_neigh_ifinfo *router_gw_tq = NULL; struct batadv_orig_node *curr_gw_orig; struct batadv_neigh_node *router_gw = NULL, *router_orig = NULL; uint8_t gw_tq_avg, orig_tq_avg; curr_gw_orig = batadv_gw_get_selected_orig(bat_priv); if (!curr_gw_orig) - goto deselect; + goto reselect; - router_gw = batadv_orig_node_get_router(curr_gw_orig); + router_gw = batadv_orig_router_get(curr_gw_orig, BATADV_IF_DEFAULT); if (!router_gw) - goto deselect; + goto reselect; + + router_gw_tq = batadv_neigh_ifinfo_get(router_gw, + BATADV_IF_DEFAULT); + if (!router_gw_tq) + goto reselect; /* this node already is the gateway */ if (curr_gw_orig == orig_node) goto out; - router_orig = batadv_orig_node_get_router(orig_node); + router_orig = batadv_orig_router_get(orig_node, BATADV_IF_DEFAULT); if (!router_orig) goto out; - gw_tq_avg = router_gw->bat_iv.tq_avg; - orig_tq_avg = router_orig->bat_iv.tq_avg; + router_orig_tq = batadv_neigh_ifinfo_get(router_orig, + BATADV_IF_DEFAULT); + if (!router_orig_tq) + goto out; + + gw_tq_avg = router_gw_tq->bat_iv.tq_avg; + orig_tq_avg = router_orig_tq->bat_iv.tq_avg; /* the TQ value has to be better */ if (orig_tq_avg < gw_tq_avg) @@ -323,8 +376,8 @@ void batadv_gw_check_election(struct batadv_priv *bat_priv, "Restarting gateway selection: better gateway found (tq curr: %i, tq new: %i)\n", gw_tq_avg, orig_tq_avg); -deselect: - batadv_gw_deselect(bat_priv); +reselect: + batadv_gw_reselect(bat_priv); out: if (curr_gw_orig) batadv_orig_node_free_ref(curr_gw_orig); @@ -332,6 +385,10 @@ out: batadv_neigh_node_free_ref(router_gw); if (router_orig) batadv_neigh_node_free_ref(router_orig); + if (router_gw_tq) + batadv_neigh_ifinfo_free_ref(router_gw_tq); + if (router_orig_tq) + batadv_neigh_ifinfo_free_ref(router_orig_tq); return; } @@ -454,7 +511,7 @@ void batadv_gw_node_update(struct batadv_priv *bat_priv, */ curr_gw = batadv_gw_get_selected_gw_node(bat_priv); if (gw_node == curr_gw) - batadv_gw_deselect(bat_priv); + batadv_gw_reselect(bat_priv); } out: @@ -480,7 +537,7 @@ void batadv_gw_node_purge(struct batadv_priv *bat_priv) struct batadv_gw_node *gw_node, *curr_gw; struct hlist_node *node_tmp; unsigned long timeout = msecs_to_jiffies(2 * BATADV_PURGE_TIMEOUT); - int do_deselect = 0; + int do_reselect = 0; curr_gw = batadv_gw_get_selected_gw_node(bat_priv); @@ -494,7 +551,7 @@ void batadv_gw_node_purge(struct batadv_priv *bat_priv) continue; if (curr_gw == gw_node) - do_deselect = 1; + do_reselect = 1; hlist_del_rcu(&gw_node->list); batadv_gw_node_free_ref(gw_node); @@ -502,9 +559,9 @@ void batadv_gw_node_purge(struct batadv_priv *bat_priv) spin_unlock_bh(&bat_priv->gw.list_lock); - /* gw_deselect() needs to acquire the gw_list_lock */ - if (do_deselect) - batadv_gw_deselect(bat_priv); + /* gw_reselect() needs to acquire the gw_list_lock */ + if (do_reselect) + batadv_gw_reselect(bat_priv); if (curr_gw) batadv_gw_node_free_ref(curr_gw); @@ -517,28 +574,36 @@ static int batadv_write_buffer_text(struct batadv_priv *bat_priv, { struct batadv_gw_node *curr_gw; struct batadv_neigh_node *router; + struct batadv_neigh_ifinfo *router_ifinfo = NULL; int ret = -1; - router = batadv_orig_node_get_router(gw_node->orig_node); + router = batadv_orig_router_get(gw_node->orig_node, BATADV_IF_DEFAULT); if (!router) goto out; + router_ifinfo = batadv_neigh_ifinfo_get(router, BATADV_IF_DEFAULT); + if (!router_ifinfo) + goto out; + curr_gw = batadv_gw_get_selected_gw_node(bat_priv); ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %u.%u/%u.%u MBit\n", (curr_gw == gw_node ? "=>" : " "), gw_node->orig_node->orig, - router->bat_iv.tq_avg, router->addr, + router_ifinfo->bat_iv.tq_avg, router->addr, router->if_incoming->net_dev->name, gw_node->bandwidth_down / 10, gw_node->bandwidth_down % 10, gw_node->bandwidth_up / 10, gw_node->bandwidth_up % 10); - batadv_neigh_node_free_ref(router); if (curr_gw) batadv_gw_node_free_ref(curr_gw); out: + if (router_ifinfo) + batadv_neigh_ifinfo_free_ref(router_ifinfo); + if (router) + batadv_neigh_node_free_ref(router); return ret; } @@ -582,80 +647,39 @@ out: return 0; } -/* this call might reallocate skb data */ -static bool batadv_is_type_dhcprequest(struct sk_buff *skb, int header_len) -{ - int ret = false; - unsigned char *p; - int pkt_len; - - if (skb_linearize(skb) < 0) - goto out; - - pkt_len = skb_headlen(skb); - - if (pkt_len < header_len + BATADV_DHCP_OPTIONS_OFFSET + 1) - goto out; - - p = skb->data + header_len + BATADV_DHCP_OPTIONS_OFFSET; - pkt_len -= header_len + BATADV_DHCP_OPTIONS_OFFSET + 1; - - /* Access the dhcp option lists. Each entry is made up by: - * - octet 1: option type - * - octet 2: option data len (only if type != 255 and 0) - * - octet 3: option data - */ - while (*p != 255 && !ret) { - /* p now points to the first octet: option type */ - if (*p == 53) { - /* type 53 is the message type option. - * Jump the len octet and go to the data octet - */ - if (pkt_len < 2) - goto out; - p += 2; - - /* check if the message type is what we need */ - if (*p == BATADV_DHCP_REQUEST) - ret = true; - break; - } else if (*p == 0) { - /* option type 0 (padding), just go forward */ - if (pkt_len < 1) - goto out; - pkt_len--; - p++; - } else { - /* This is any other option. So we get the length... */ - if (pkt_len < 1) - goto out; - pkt_len--; - p++; - - /* ...and then we jump over the data */ - if (pkt_len < 1 + (*p)) - goto out; - pkt_len -= 1 + (*p); - p += 1 + (*p); - } - } -out: - return ret; -} - -/* this call might reallocate skb data */ -bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) +/** + * batadv_gw_dhcp_recipient_get - check if a packet is a DHCP message + * @skb: the packet to check + * @header_len: a pointer to the batman-adv header size + * @chaddr: buffer where the client address will be stored. Valid + * only if the function returns BATADV_DHCP_TO_CLIENT + * + * Returns: + * - BATADV_DHCP_NO if the packet is not a dhcp message or if there was an error + * while parsing it + * - BATADV_DHCP_TO_SERVER if this is a message going to the DHCP server + * - BATADV_DHCP_TO_CLIENT if this is a message going to a DHCP client + * + * This function may re-allocate the data buffer of the skb passed as argument. + */ +enum batadv_dhcp_recipient +batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len, + uint8_t *chaddr) { + enum batadv_dhcp_recipient ret = BATADV_DHCP_NO; struct ethhdr *ethhdr; struct iphdr *iphdr; struct ipv6hdr *ipv6hdr; struct udphdr *udphdr; struct vlan_ethhdr *vhdr; + int chaddr_offset; __be16 proto; + uint8_t *p; /* check for ethernet header */ if (!pskb_may_pull(skb, *header_len + ETH_HLEN)) - return false; + return BATADV_DHCP_NO; + ethhdr = (struct ethhdr *)skb->data; proto = ethhdr->h_proto; *header_len += ETH_HLEN; @@ -663,7 +687,7 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) /* check for initial vlan header */ if (proto == htons(ETH_P_8021Q)) { if (!pskb_may_pull(skb, *header_len + VLAN_HLEN)) - return false; + return BATADV_DHCP_NO; vhdr = (struct vlan_ethhdr *)skb->data; proto = vhdr->h_vlan_encapsulated_proto; @@ -674,32 +698,34 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) switch (proto) { case htons(ETH_P_IP): if (!pskb_may_pull(skb, *header_len + sizeof(*iphdr))) - return false; + return BATADV_DHCP_NO; + iphdr = (struct iphdr *)(skb->data + *header_len); *header_len += iphdr->ihl * 4; /* check for udp header */ if (iphdr->protocol != IPPROTO_UDP) - return false; + return BATADV_DHCP_NO; break; case htons(ETH_P_IPV6): if (!pskb_may_pull(skb, *header_len + sizeof(*ipv6hdr))) - return false; + return BATADV_DHCP_NO; + ipv6hdr = (struct ipv6hdr *)(skb->data + *header_len); *header_len += sizeof(*ipv6hdr); /* check for udp header */ if (ipv6hdr->nexthdr != IPPROTO_UDP) - return false; + return BATADV_DHCP_NO; break; default: - return false; + return BATADV_DHCP_NO; } if (!pskb_may_pull(skb, *header_len + sizeof(*udphdr))) - return false; + return BATADV_DHCP_NO; /* skb->data might have been reallocated by pskb_may_pull() */ ethhdr = (struct ethhdr *)skb->data; @@ -710,17 +736,40 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) *header_len += sizeof(*udphdr); /* check for bootp port */ - if ((proto == htons(ETH_P_IP)) && - (udphdr->dest != htons(67))) - return false; + switch (proto) { + case htons(ETH_P_IP): + if (udphdr->dest == htons(67)) + ret = BATADV_DHCP_TO_SERVER; + else if (udphdr->source == htons(67)) + ret = BATADV_DHCP_TO_CLIENT; + break; + case htons(ETH_P_IPV6): + if (udphdr->dest == htons(547)) + ret = BATADV_DHCP_TO_SERVER; + else if (udphdr->source == htons(547)) + ret = BATADV_DHCP_TO_CLIENT; + break; + } - if ((proto == htons(ETH_P_IPV6)) && - (udphdr->dest != htons(547))) - return false; + chaddr_offset = *header_len + BATADV_DHCP_CHADDR_OFFSET; + /* store the client address if the message is going to a client */ + if (ret == BATADV_DHCP_TO_CLIENT && + pskb_may_pull(skb, chaddr_offset + ETH_ALEN)) { + /* check if the DHCP packet carries an Ethernet DHCP */ + p = skb->data + *header_len + BATADV_DHCP_HTYPE_OFFSET; + if (*p != BATADV_DHCP_HTYPE_ETHERNET) + return BATADV_DHCP_NO; + + /* check if the DHCP packet carries a valid Ethernet address */ + p = skb->data + *header_len + BATADV_DHCP_HLEN_OFFSET; + if (*p != ETH_ALEN) + return BATADV_DHCP_NO; + + memcpy(chaddr, skb->data + chaddr_offset, ETH_ALEN); + } - return true; + return ret; } - /** * batadv_gw_out_of_range - check if the dhcp request destination is the best gw * @bat_priv: the bat priv with all the soft interface information @@ -734,6 +783,7 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) * false otherwise. * * This call might reallocate skb data. + * Must be invoked only when the DHCP packet is going TO a DHCP SERVER. */ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, struct sk_buff *skb) @@ -741,19 +791,14 @@ 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 *gw_node = NULL, *curr_gw = NULL; - struct ethhdr *ethhdr; - bool ret, out_of_range = false; - unsigned int header_len = 0; + struct batadv_neigh_ifinfo *curr_ifinfo, *old_ifinfo; + struct ethhdr *ethhdr = (struct ethhdr *)skb->data; + bool out_of_range = false; uint8_t curr_tq_avg; unsigned short vid; vid = batadv_get_vid(skb, 0); - ret = batadv_gw_is_dhcp_target(skb, &header_len); - if (!ret) - goto out; - - ethhdr = (struct ethhdr *)skb->data; orig_dst_node = batadv_transtable_search(bat_priv, ethhdr->h_source, ethhdr->h_dest, vid); if (!orig_dst_node) @@ -763,10 +808,6 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, if (!gw_node->bandwidth_down == 0) goto out; - ret = batadv_is_type_dhcprequest(skb, header_len); - if (!ret) - goto out; - switch (atomic_read(&bat_priv->gw_mode)) { case BATADV_GW_MODE_SERVER: /* If we are a GW then we are our best GW. We can artificially @@ -792,7 +833,14 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, if (!neigh_curr) goto out; - curr_tq_avg = neigh_curr->bat_iv.tq_avg; + curr_ifinfo = batadv_neigh_ifinfo_get(neigh_curr, + BATADV_IF_DEFAULT); + if (!curr_ifinfo) + goto out; + + curr_tq_avg = curr_ifinfo->bat_iv.tq_avg; + batadv_neigh_ifinfo_free_ref(curr_ifinfo); + break; case BATADV_GW_MODE_OFF: default: @@ -803,8 +851,13 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, if (!neigh_old) goto out; - if (curr_tq_avg - neigh_old->bat_iv.tq_avg > BATADV_GW_THRESHOLD) + old_ifinfo = batadv_neigh_ifinfo_get(neigh_old, BATADV_IF_DEFAULT); + if (!old_ifinfo) + goto out; + + if ((curr_tq_avg - old_ifinfo->bat_iv.tq_avg) > BATADV_GW_THRESHOLD) out_of_range = true; + batadv_neigh_ifinfo_free_ref(old_ifinfo); out: if (orig_dst_node) diff --git a/net/batman-adv/gateway_client.h b/net/batman-adv/gateway_client.h index d95c2d23195e..7ee53bb7d50f 100644 --- a/net/batman-adv/gateway_client.h +++ b/net/batman-adv/gateway_client.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2009-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2009-2014 B.A.T.M.A.N. contributors: * * Marek Lindner * @@ -12,16 +12,14 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ #define _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ void batadv_gw_check_client_stop(struct batadv_priv *bat_priv); -void batadv_gw_deselect(struct batadv_priv *bat_priv); +void batadv_gw_reselect(struct batadv_priv *bat_priv); void batadv_gw_election(struct batadv_priv *bat_priv); struct batadv_orig_node * batadv_gw_get_selected_orig(struct batadv_priv *bat_priv); @@ -34,7 +32,9 @@ void batadv_gw_node_delete(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node); void batadv_gw_node_purge(struct batadv_priv *bat_priv); int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset); -bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len); bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, struct sk_buff *skb); +enum batadv_dhcp_recipient +batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len, + uint8_t *chaddr); #endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */ diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c index b211b0f9cb78..6f5e621f220a 100644 --- a/net/batman-adv/gateway_common.c +++ b/net/batman-adv/gateway_common.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2009-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2009-2014 B.A.T.M.A.N. contributors: * * Marek Lindner * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" @@ -164,7 +162,7 @@ ssize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff, if ((down_curr == down_new) && (up_curr == up_new)) return count; - batadv_gw_deselect(bat_priv); + batadv_gw_reselect(bat_priv); batadv_info(net_dev, "Changing gateway bandwidth from: '%u.%u/%u.%u MBit' to: '%u.%u/%u.%u MBit'\n", down_curr / 10, down_curr % 10, up_curr / 10, up_curr % 10, diff --git a/net/batman-adv/gateway_common.h b/net/batman-adv/gateway_common.h index 56384a4cd18c..aa5116561947 100644 --- a/net/batman-adv/gateway_common.h +++ b/net/batman-adv/gateway_common.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2009-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2009-2014 B.A.T.M.A.N. contributors: * * Marek Lindner * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_GATEWAY_COMMON_H_ diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index 57c2a19dcb5c..3d417d3641c6 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" @@ -25,6 +23,7 @@ #include "translation-table.h" #include "routing.h" #include "sysfs.h" +#include "debugfs.h" #include "originator.h" #include "hash.h" #include "bridge_loop_avoidance.h" @@ -88,15 +87,13 @@ static bool batadv_is_on_batman_iface(const struct net_device *net_dev) return false; /* recurse over the parent device */ - parent_dev = dev_get_by_index(&init_net, net_dev->iflink); + parent_dev = __dev_get_by_index(&init_net, net_dev->iflink); /* if we got a NULL parent_dev there is something broken.. */ if (WARN(!parent_dev, "Cannot find parent device")) return false; ret = batadv_is_on_batman_iface(parent_dev); - if (parent_dev) - dev_put(parent_dev); return ret; } @@ -541,6 +538,7 @@ static void batadv_hardif_remove_interface_finish(struct work_struct *work) hard_iface = container_of(work, struct batadv_hard_iface, cleanup_work); + batadv_debugfs_del_hardif(hard_iface); batadv_sysfs_del_hardif(&hard_iface->hardif_obj); batadv_hardif_free_ref(hard_iface); } @@ -571,6 +569,11 @@ batadv_hardif_add_interface(struct net_device *net_dev) hard_iface->net_dev = net_dev; hard_iface->soft_iface = NULL; hard_iface->if_status = BATADV_IF_NOT_IN_USE; + + ret = batadv_debugfs_add_hardif(hard_iface); + if (ret) + goto free_sysfs; + INIT_LIST_HEAD(&hard_iface->list); INIT_WORK(&hard_iface->cleanup_work, batadv_hardif_remove_interface_finish); @@ -587,6 +590,8 @@ batadv_hardif_add_interface(struct net_device *net_dev) return hard_iface; +free_sysfs: + batadv_sysfs_del_hardif(&hard_iface->hardif_obj); free_if: kfree(hard_iface); release_dev: diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index df4c8bd45c40..1918cd50b62e 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_HARD_INTERFACE_H_ @@ -42,6 +40,7 @@ enum batadv_hard_if_cleanup { extern struct notifier_block batadv_hard_if_notifier; bool batadv_is_wifi_netdev(struct net_device *net_device); +bool batadv_is_wifi_iface(int ifindex); struct batadv_hard_iface* batadv_hardif_get_by_netdev(const struct net_device *net_dev); int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, @@ -53,6 +52,11 @@ int batadv_hardif_min_mtu(struct net_device *soft_iface); void batadv_update_min_mtu(struct net_device *soft_iface); void batadv_hardif_free_rcu(struct rcu_head *rcu); +/** + * batadv_hardif_free_ref - decrement the hard interface refcounter and + * possibly free it + * @hard_iface: the hard interface to free + */ static inline void batadv_hardif_free_ref(struct batadv_hard_iface *hard_iface) { @@ -60,6 +64,18 @@ batadv_hardif_free_ref(struct batadv_hard_iface *hard_iface) call_rcu(&hard_iface->rcu, batadv_hardif_free_rcu); } +/** + * batadv_hardif_free_ref_now - decrement the hard interface refcounter and + * possibly free it (without rcu callback) + * @hard_iface: the hard interface to free + */ +static inline void +batadv_hardif_free_ref_now(struct batadv_hard_iface *hard_iface) +{ + if (atomic_dec_and_test(&hard_iface->refcount)) + batadv_hardif_free_rcu(&hard_iface->rcu); +} + static inline struct batadv_hard_iface * batadv_primary_if_get_selected(struct batadv_priv *bat_priv) { diff --git a/net/batman-adv/hash.c b/net/batman-adv/hash.c index 7198dafd3bf3..63bdf7e94f1e 100644 --- a/net/batman-adv/hash.c +++ b/net/batman-adv/hash.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2006-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2006-2014 B.A.T.M.A.N. contributors: * * Simon Wunderlich, Marek Lindner * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" diff --git a/net/batman-adv/hash.h b/net/batman-adv/hash.h index 1b4da72f2093..539fc1266793 100644 --- a/net/batman-adv/hash.h +++ b/net/batman-adv/hash.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2006-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2006-2014 B.A.T.M.A.N. contributors: * * Simon Wunderlich, Marek Lindner * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_HASH_H_ diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c index 130cc3217e2b..abb9d6e0388b 100644 --- a/net/batman-adv/icmp_socket.c +++ b/net/batman-adv/icmp_socket.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" @@ -217,7 +215,8 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff, if (!orig_node) goto dst_unreach; - neigh_node = batadv_orig_node_get_router(orig_node); + neigh_node = batadv_orig_router_get(orig_node, + BATADV_IF_DEFAULT); if (!neigh_node) goto dst_unreach; diff --git a/net/batman-adv/icmp_socket.h b/net/batman-adv/icmp_socket.h index 6665080dff79..0c33950aa4aa 100644 --- a/net/batman-adv/icmp_socket.h +++ b/net/batman-adv/icmp_socket.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_ICMP_SOCKET_H_ diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index faba0f61ad53..66ae135b9f27 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/crc32c.h> @@ -421,13 +419,23 @@ static void batadv_recv_handler_init(void) 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_tvlv_packet, dst) != 4); - BUILD_BUG_ON(offsetof(struct batadv_frag_packet, dest) != 4); - BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, dst) != 4); - BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, dst) != 4); + /* compile time checks for sizes */ + BUILD_BUG_ON(sizeof(struct batadv_bla_claim_dst) != 6); + BUILD_BUG_ON(sizeof(struct batadv_ogm_packet) != 24); + BUILD_BUG_ON(sizeof(struct batadv_icmp_header) != 20); + BUILD_BUG_ON(sizeof(struct batadv_icmp_packet) != 20); + BUILD_BUG_ON(sizeof(struct batadv_icmp_packet_rr) != 116); + BUILD_BUG_ON(sizeof(struct batadv_unicast_packet) != 10); + BUILD_BUG_ON(sizeof(struct batadv_unicast_4addr_packet) != 18); + BUILD_BUG_ON(sizeof(struct batadv_frag_packet) != 20); + BUILD_BUG_ON(sizeof(struct batadv_bcast_packet) != 14); + BUILD_BUG_ON(sizeof(struct batadv_coded_packet) != 46); + BUILD_BUG_ON(sizeof(struct batadv_unicast_tvlv_packet) != 20); + BUILD_BUG_ON(sizeof(struct batadv_tvlv_hdr) != 4); + BUILD_BUG_ON(sizeof(struct batadv_tvlv_gateway_data) != 8); + BUILD_BUG_ON(sizeof(struct batadv_tvlv_tt_vlan_data) != 8); + BUILD_BUG_ON(sizeof(struct batadv_tvlv_tt_change) != 12); + BUILD_BUG_ON(sizeof(struct batadv_tvlv_roam_adv) != 8); /* broadcast packet */ batadv_rx_handler[BATADV_BCAST] = batadv_recv_bcast_packet; @@ -1173,6 +1181,32 @@ unsigned short batadv_get_vid(struct sk_buff *skb, size_t header_len) return vid; } +/** + * batadv_vlan_ap_isola_get - return the AP isolation status for the given vlan + * @bat_priv: the bat priv with all the soft interface information + * @vid: the VLAN identifier for which the AP isolation attributed as to be + * looked up + * + * Returns true if AP isolation is on for the VLAN idenfied by vid, false + * otherwise + */ +bool batadv_vlan_ap_isola_get(struct batadv_priv *bat_priv, unsigned short vid) +{ + bool ap_isolation_enabled = false; + struct batadv_softif_vlan *vlan; + + /* if the AP isolation is requested on a VLAN, then check for its + * setting in the proper VLAN private data structure + */ + vlan = batadv_softif_vlan_get(bat_priv, vid); + if (vlan) { + ap_isolation_enabled = atomic_read(&vlan->ap_isolation); + batadv_softif_vlan_free_ref(vlan); + } + + return ap_isolation_enabled; +} + static int batadv_param_set_ra(const char *val, const struct kernel_param *kp) { struct batadv_algo_ops *bat_algo_ops; diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index f94f287b8670..9374f1a51348 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_MAIN_H_ @@ -26,7 +24,7 @@ #define BATADV_DRIVER_DEVICE "batman-adv" #ifndef BATADV_SOURCE_VERSION -#define BATADV_SOURCE_VERSION "2013.5.0" +#define BATADV_SOURCE_VERSION "2014.1.0" #endif /* B.A.T.M.A.N. parameters */ @@ -72,6 +70,14 @@ #define BATADV_NULL_IFINDEX 0 /* dummy ifindex used to avoid iface checks */ +#define BATADV_NO_MARK 0 + +/* default interface for multi interface operation. The default interface is + * used for communication which originated locally (i.e. is not forwarded) + * or where special forwarding is not desired/necessary. + */ +#define BATADV_IF_DEFAULT ((struct batadv_hard_iface *)NULL) + #define BATADV_NUM_WORDS BITS_TO_LONGS(BATADV_TQ_LOCAL_WINDOW_SIZE) #define BATADV_LOG_BUF_LEN 8192 /* has to be a power of 2 */ @@ -266,7 +272,7 @@ static inline void batadv_dbg(int type __always_unused, */ static inline int batadv_compare_eth(const void *data1, const void *data2) { - return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); + return ether_addr_equal_unaligned(data1, data2); } /** @@ -369,5 +375,6 @@ void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, uint8_t *src, uint8_t *dst, uint8_t type, uint8_t version, void *tvlv_value, uint16_t tvlv_value_len); unsigned short batadv_get_vid(struct sk_buff *skb, size_t header_len); +bool batadv_vlan_ap_isola_get(struct batadv_priv *bat_priv, unsigned short vid); #endif /* _NET_BATMAN_ADV_MAIN_H_ */ diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c index 511d7e1eea38..f1b604d88dc3 100644 --- a/net/batman-adv/network-coding.c +++ b/net/batman-adv/network-coding.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2012-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2012-2014 B.A.T.M.A.N. contributors: * * Martin Hundebøll, Jeppe Ledet-Pedersen * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/debugfs.h> @@ -720,9 +718,21 @@ static bool batadv_can_nc_with_orig(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, struct batadv_ogm_packet *ogm_packet) { - if (orig_node->last_real_seqno != ntohl(ogm_packet->seqno)) + struct batadv_orig_ifinfo *orig_ifinfo; + uint32_t last_real_seqno; + uint8_t last_ttl; + + orig_ifinfo = batadv_orig_ifinfo_get(orig_node, BATADV_IF_DEFAULT); + if (!orig_ifinfo) return false; - if (orig_node->last_ttl != ogm_packet->ttl + 1) + + last_ttl = orig_ifinfo->last_ttl; + last_real_seqno = orig_ifinfo->last_real_seqno; + batadv_orig_ifinfo_free_ref(orig_ifinfo); + + if (last_real_seqno != ntohl(ogm_packet->seqno)) + return false; + if (last_ttl != ogm_packet->ttl + 1) return false; if (!batadv_compare_eth(ogm_packet->orig, ogm_packet->prev_sender)) return false; @@ -1010,6 +1020,8 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv, struct batadv_coded_packet *coded_packet; struct batadv_neigh_node *neigh_tmp, *router_neigh; struct batadv_neigh_node *router_coding = NULL; + struct batadv_neigh_ifinfo *router_neigh_ifinfo = NULL; + struct batadv_neigh_ifinfo *router_coding_ifinfo = NULL; uint8_t *first_source, *first_dest, *second_source, *second_dest; __be32 packet_id1, packet_id2; size_t count; @@ -1019,19 +1031,34 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv, int coded_size = sizeof(*coded_packet); int header_add = coded_size - unicast_size; - router_neigh = batadv_orig_node_get_router(neigh_node->orig_node); + /* TODO: do we need to consider the outgoing interface for + * coded packets? + */ + router_neigh = batadv_orig_router_get(neigh_node->orig_node, + BATADV_IF_DEFAULT); if (!router_neigh) goto out; + router_neigh_ifinfo = batadv_neigh_ifinfo_get(router_neigh, + BATADV_IF_DEFAULT); + if (!router_neigh_ifinfo) + goto out; + neigh_tmp = nc_packet->neigh_node; - router_coding = batadv_orig_node_get_router(neigh_tmp->orig_node); + router_coding = batadv_orig_router_get(neigh_tmp->orig_node, + BATADV_IF_DEFAULT); if (!router_coding) goto out; - tq_tmp = batadv_nc_random_weight_tq(router_neigh->bat_iv.tq_avg); - tq_weighted_neigh = tq_tmp; - tq_tmp = batadv_nc_random_weight_tq(router_coding->bat_iv.tq_avg); - tq_weighted_coding = tq_tmp; + router_coding_ifinfo = batadv_neigh_ifinfo_get(router_coding, + BATADV_IF_DEFAULT); + if (!router_coding_ifinfo) + goto out; + + tq_tmp = router_neigh_ifinfo->bat_iv.tq_avg; + tq_weighted_neigh = batadv_nc_random_weight_tq(tq_tmp); + tq_tmp = router_coding_ifinfo->bat_iv.tq_avg; + tq_weighted_coding = batadv_nc_random_weight_tq(tq_tmp); /* Select one destination for the MAC-header dst-field based on * weighted TQ-values. @@ -1155,6 +1182,10 @@ out: batadv_neigh_node_free_ref(router_neigh); if (router_coding) batadv_neigh_node_free_ref(router_coding); + if (router_neigh_ifinfo) + batadv_neigh_ifinfo_free_ref(router_neigh_ifinfo); + if (router_coding_ifinfo) + batadv_neigh_ifinfo_free_ref(router_coding_ifinfo); return res; } diff --git a/net/batman-adv/network-coding.h b/net/batman-adv/network-coding.h index d4fd315b5261..358c0d686ab0 100644 --- a/net/batman-adv/network-coding.h +++ b/net/batman-adv/network-coding.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2012-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2012-2014 B.A.T.M.A.N. contributors: * * Martin Hundebøll, Jeppe Ledet-Pedersen * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_NETWORK_CODING_H_ @@ -64,7 +62,6 @@ static inline int batadv_nc_mesh_init(struct batadv_priv *bat_priv) static inline void batadv_nc_mesh_free(struct batadv_priv *bat_priv) { - return; } static inline void @@ -74,7 +71,6 @@ batadv_nc_update_nc_node(struct batadv_priv *bat_priv, struct batadv_ogm_packet *ogm_packet, int is_single_hop_neigh) { - return; } static inline void @@ -83,17 +79,14 @@ batadv_nc_purge_orig(struct batadv_priv *bat_priv, bool (*to_purge)(struct batadv_priv *, struct batadv_nc_node *)) { - return; } static inline void batadv_nc_init_bat_priv(struct batadv_priv *bat_priv) { - return; } static inline void batadv_nc_init_orig(struct batadv_orig_node *orig_node) { - return; } static inline bool batadv_nc_skb_forward(struct sk_buff *skb, @@ -106,14 +99,12 @@ static inline void batadv_nc_skb_store_for_decoding(struct batadv_priv *bat_priv, struct sk_buff *skb) { - return; } static inline void batadv_nc_skb_store_sniffed_unicast(struct batadv_priv *bat_priv, struct sk_buff *skb) { - return; } static inline int batadv_nc_nodes_seq_print_text(struct seq_file *seq, diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 8ab14340d10f..6df12a2e3605 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2009-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2009-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" @@ -41,7 +39,7 @@ int batadv_compare_orig(const struct hlist_node *node, const void *data2) const void *data1 = container_of(node, struct batadv_orig_node, hash_entry); - return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); + return batadv_compare_eth(data1, data2); } /** @@ -150,20 +148,114 @@ err: return -ENOMEM; } +/** + * batadv_neigh_ifinfo_free_rcu - free the neigh_ifinfo object + * @rcu: rcu pointer of the neigh_ifinfo object + */ +static void batadv_neigh_ifinfo_free_rcu(struct rcu_head *rcu) +{ + struct batadv_neigh_ifinfo *neigh_ifinfo; + + neigh_ifinfo = container_of(rcu, struct batadv_neigh_ifinfo, rcu); + + if (neigh_ifinfo->if_outgoing != BATADV_IF_DEFAULT) + batadv_hardif_free_ref_now(neigh_ifinfo->if_outgoing); + + kfree(neigh_ifinfo); +} + +/** + * batadv_neigh_ifinfo_free_now - decrement the refcounter and possibly free + * the neigh_ifinfo (without rcu callback) + * @neigh_ifinfo: the neigh_ifinfo object to release + */ +static void +batadv_neigh_ifinfo_free_ref_now(struct batadv_neigh_ifinfo *neigh_ifinfo) +{ + if (atomic_dec_and_test(&neigh_ifinfo->refcount)) + batadv_neigh_ifinfo_free_rcu(&neigh_ifinfo->rcu); +} + +/** + * batadv_neigh_ifinfo_free_ref - decrement the refcounter and possibly free + * the neigh_ifinfo + * @neigh_ifinfo: the neigh_ifinfo object to release + */ +void batadv_neigh_ifinfo_free_ref(struct batadv_neigh_ifinfo *neigh_ifinfo) +{ + if (atomic_dec_and_test(&neigh_ifinfo->refcount)) + call_rcu(&neigh_ifinfo->rcu, batadv_neigh_ifinfo_free_rcu); +} + +/** + * batadv_neigh_node_free_rcu - free the neigh_node + * @rcu: rcu pointer of the neigh_node + */ +static void batadv_neigh_node_free_rcu(struct rcu_head *rcu) +{ + struct hlist_node *node_tmp; + struct batadv_neigh_node *neigh_node; + struct batadv_neigh_ifinfo *neigh_ifinfo; + + neigh_node = container_of(rcu, struct batadv_neigh_node, rcu); + + hlist_for_each_entry_safe(neigh_ifinfo, node_tmp, + &neigh_node->ifinfo_list, list) { + batadv_neigh_ifinfo_free_ref_now(neigh_ifinfo); + } + batadv_hardif_free_ref_now(neigh_node->if_incoming); + + kfree(neigh_node); +} + +/** + * batadv_neigh_node_free_ref_now - decrement the neighbors refcounter + * and possibly free it (without rcu callback) + * @neigh_node: neigh neighbor to free + */ +static void +batadv_neigh_node_free_ref_now(struct batadv_neigh_node *neigh_node) +{ + if (atomic_dec_and_test(&neigh_node->refcount)) + batadv_neigh_node_free_rcu(&neigh_node->rcu); +} + +/** + * batadv_neigh_node_free_ref - decrement the neighbors refcounter + * and possibly free it + * @neigh_node: neigh neighbor to free + */ void batadv_neigh_node_free_ref(struct batadv_neigh_node *neigh_node) { if (atomic_dec_and_test(&neigh_node->refcount)) - kfree_rcu(neigh_node, rcu); + call_rcu(&neigh_node->rcu, batadv_neigh_node_free_rcu); } -/* increases the refcounter of a found router */ +/** + * batadv_orig_node_get_router - router to the originator depending on iface + * @orig_node: the orig node for the router + * @if_outgoing: the interface where the payload packet has been received or + * the OGM should be sent to + * + * Returns the neighbor which should be router for this orig_node/iface. + * + * The object is returned with refcounter increased by 1. + */ struct batadv_neigh_node * -batadv_orig_node_get_router(struct batadv_orig_node *orig_node) +batadv_orig_router_get(struct batadv_orig_node *orig_node, + const struct batadv_hard_iface *if_outgoing) { - struct batadv_neigh_node *router; + struct batadv_orig_ifinfo *orig_ifinfo; + struct batadv_neigh_node *router = NULL; rcu_read_lock(); - router = rcu_dereference(orig_node->router); + hlist_for_each_entry_rcu(orig_ifinfo, &orig_node->ifinfo_list, list) { + if (orig_ifinfo->if_outgoing != if_outgoing) + continue; + + router = rcu_dereference(orig_ifinfo->router); + break; + } if (router && !atomic_inc_not_zero(&router->refcount)) router = NULL; @@ -173,6 +265,164 @@ batadv_orig_node_get_router(struct batadv_orig_node *orig_node) } /** + * batadv_orig_ifinfo_get - find the ifinfo from an orig_node + * @orig_node: the orig node to be queried + * @if_outgoing: the interface for which the ifinfo should be acquired + * + * Returns the requested orig_ifinfo or NULL if not found. + * + * The object is returned with refcounter increased by 1. + */ +struct batadv_orig_ifinfo * +batadv_orig_ifinfo_get(struct batadv_orig_node *orig_node, + struct batadv_hard_iface *if_outgoing) +{ + struct batadv_orig_ifinfo *tmp, *orig_ifinfo = NULL; + + rcu_read_lock(); + hlist_for_each_entry_rcu(tmp, &orig_node->ifinfo_list, + list) { + if (tmp->if_outgoing != if_outgoing) + continue; + + if (!atomic_inc_not_zero(&tmp->refcount)) + continue; + + orig_ifinfo = tmp; + break; + } + rcu_read_unlock(); + + return orig_ifinfo; +} + +/** + * batadv_orig_ifinfo_new - search and possibly create an orig_ifinfo object + * @orig_node: the orig node to be queried + * @if_outgoing: the interface for which the ifinfo should be acquired + * + * Returns NULL in case of failure or the orig_ifinfo object for the if_outgoing + * interface otherwise. The object is created and added to the list + * if it does not exist. + * + * The object is returned with refcounter increased by 1. + */ +struct batadv_orig_ifinfo * +batadv_orig_ifinfo_new(struct batadv_orig_node *orig_node, + struct batadv_hard_iface *if_outgoing) +{ + struct batadv_orig_ifinfo *orig_ifinfo = NULL; + unsigned long reset_time; + + spin_lock_bh(&orig_node->neigh_list_lock); + + orig_ifinfo = batadv_orig_ifinfo_get(orig_node, if_outgoing); + if (orig_ifinfo) + goto out; + + orig_ifinfo = kzalloc(sizeof(*orig_ifinfo), GFP_ATOMIC); + if (!orig_ifinfo) + goto out; + + if (if_outgoing != BATADV_IF_DEFAULT && + !atomic_inc_not_zero(&if_outgoing->refcount)) { + kfree(orig_ifinfo); + orig_ifinfo = NULL; + goto out; + } + + reset_time = jiffies - 1; + reset_time -= msecs_to_jiffies(BATADV_RESET_PROTECTION_MS); + orig_ifinfo->batman_seqno_reset = reset_time; + orig_ifinfo->if_outgoing = if_outgoing; + INIT_HLIST_NODE(&orig_ifinfo->list); + atomic_set(&orig_ifinfo->refcount, 2); + hlist_add_head_rcu(&orig_ifinfo->list, + &orig_node->ifinfo_list); +out: + spin_unlock_bh(&orig_node->neigh_list_lock); + return orig_ifinfo; +} + +/** + * batadv_neigh_ifinfo_get - find the ifinfo from an neigh_node + * @neigh_node: the neigh node to be queried + * @if_outgoing: the interface for which the ifinfo should be acquired + * + * The object is returned with refcounter increased by 1. + * + * Returns the requested neigh_ifinfo or NULL if not found + */ +struct batadv_neigh_ifinfo * +batadv_neigh_ifinfo_get(struct batadv_neigh_node *neigh, + struct batadv_hard_iface *if_outgoing) +{ + struct batadv_neigh_ifinfo *neigh_ifinfo = NULL, + *tmp_neigh_ifinfo; + + rcu_read_lock(); + hlist_for_each_entry_rcu(tmp_neigh_ifinfo, &neigh->ifinfo_list, + list) { + if (tmp_neigh_ifinfo->if_outgoing != if_outgoing) + continue; + + if (!atomic_inc_not_zero(&tmp_neigh_ifinfo->refcount)) + continue; + + neigh_ifinfo = tmp_neigh_ifinfo; + break; + } + rcu_read_unlock(); + + return neigh_ifinfo; +} + +/** + * batadv_neigh_ifinfo_new - search and possibly create an neigh_ifinfo object + * @neigh_node: the neigh node to be queried + * @if_outgoing: the interface for which the ifinfo should be acquired + * + * Returns NULL in case of failure or the neigh_ifinfo object for the + * if_outgoing interface otherwise. The object is created and added to the list + * if it does not exist. + * + * The object is returned with refcounter increased by 1. + */ +struct batadv_neigh_ifinfo * +batadv_neigh_ifinfo_new(struct batadv_neigh_node *neigh, + struct batadv_hard_iface *if_outgoing) +{ + struct batadv_neigh_ifinfo *neigh_ifinfo; + + spin_lock_bh(&neigh->ifinfo_lock); + + neigh_ifinfo = batadv_neigh_ifinfo_get(neigh, if_outgoing); + if (neigh_ifinfo) + goto out; + + neigh_ifinfo = kzalloc(sizeof(*neigh_ifinfo), GFP_ATOMIC); + if (!neigh_ifinfo) + goto out; + + if (if_outgoing && !atomic_inc_not_zero(&if_outgoing->refcount)) { + kfree(neigh_ifinfo); + neigh_ifinfo = NULL; + goto out; + } + + INIT_HLIST_NODE(&neigh_ifinfo->list); + atomic_set(&neigh_ifinfo->refcount, 2); + neigh_ifinfo->if_outgoing = if_outgoing; + + hlist_add_head_rcu(&neigh_ifinfo->list, &neigh->ifinfo_list); + +out: + spin_unlock_bh(&neigh->ifinfo_lock); + + return neigh_ifinfo; +} + +/** * batadv_neigh_node_new - create and init a new neigh_node object * @hard_iface: the interface where the neighbour is connected to * @neigh_addr: the mac address of the neighbour interface @@ -193,13 +443,13 @@ batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, goto out; INIT_HLIST_NODE(&neigh_node->list); + INIT_HLIST_HEAD(&neigh_node->ifinfo_list); + spin_lock_init(&neigh_node->ifinfo_lock); memcpy(neigh_node->addr, neigh_addr, ETH_ALEN); neigh_node->if_incoming = hard_iface; neigh_node->orig_node = orig_node; - INIT_LIST_HEAD(&neigh_node->bonding_list); - /* extra reference for return */ atomic_set(&neigh_node->refcount, 2); @@ -207,30 +457,68 @@ out: return neigh_node; } +/** + * batadv_orig_ifinfo_free_rcu - free the orig_ifinfo object + * @rcu: rcu pointer of the orig_ifinfo object + */ +static void batadv_orig_ifinfo_free_rcu(struct rcu_head *rcu) +{ + struct batadv_orig_ifinfo *orig_ifinfo; + + orig_ifinfo = container_of(rcu, struct batadv_orig_ifinfo, rcu); + + if (orig_ifinfo->if_outgoing != BATADV_IF_DEFAULT) + batadv_hardif_free_ref_now(orig_ifinfo->if_outgoing); + + kfree(orig_ifinfo); +} + +/** + * batadv_orig_ifinfo_free_ref - decrement the refcounter and possibly free + * the orig_ifinfo (without rcu callback) + * @orig_ifinfo: the orig_ifinfo object to release + */ +static void +batadv_orig_ifinfo_free_ref_now(struct batadv_orig_ifinfo *orig_ifinfo) +{ + if (atomic_dec_and_test(&orig_ifinfo->refcount)) + batadv_orig_ifinfo_free_rcu(&orig_ifinfo->rcu); +} + +/** + * batadv_orig_ifinfo_free_ref - decrement the refcounter and possibly free + * the orig_ifinfo + * @orig_ifinfo: the orig_ifinfo object to release + */ +void batadv_orig_ifinfo_free_ref(struct batadv_orig_ifinfo *orig_ifinfo) +{ + if (atomic_dec_and_test(&orig_ifinfo->refcount)) + call_rcu(&orig_ifinfo->rcu, batadv_orig_ifinfo_free_rcu); +} + static void batadv_orig_node_free_rcu(struct rcu_head *rcu) { struct hlist_node *node_tmp; - struct batadv_neigh_node *neigh_node, *tmp_neigh_node; + struct batadv_neigh_node *neigh_node; struct batadv_orig_node *orig_node; + struct batadv_orig_ifinfo *orig_ifinfo; orig_node = container_of(rcu, struct batadv_orig_node, rcu); spin_lock_bh(&orig_node->neigh_list_lock); - /* for all bonding members ... */ - list_for_each_entry_safe(neigh_node, tmp_neigh_node, - &orig_node->bond_list, bonding_list) { - list_del_rcu(&neigh_node->bonding_list); - batadv_neigh_node_free_ref(neigh_node); - } - /* for all neighbors towards this originator ... */ hlist_for_each_entry_safe(neigh_node, node_tmp, &orig_node->neigh_list, list) { hlist_del_rcu(&neigh_node->list); - batadv_neigh_node_free_ref(neigh_node); + batadv_neigh_node_free_ref_now(neigh_node); } + hlist_for_each_entry_safe(orig_ifinfo, node_tmp, + &orig_node->ifinfo_list, list) { + hlist_del_rcu(&orig_ifinfo->list); + batadv_orig_ifinfo_free_ref_now(orig_ifinfo); + } spin_unlock_bh(&orig_node->neigh_list_lock); /* Free nc_nodes */ @@ -327,8 +615,8 @@ struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv, return NULL; INIT_HLIST_HEAD(&orig_node->neigh_list); - INIT_LIST_HEAD(&orig_node->bond_list); INIT_LIST_HEAD(&orig_node->vlan_list); + INIT_HLIST_HEAD(&orig_node->ifinfo_list); spin_lock_init(&orig_node->bcast_seqno_lock); spin_lock_init(&orig_node->neigh_list_lock); spin_lock_init(&orig_node->tt_buff_lock); @@ -344,15 +632,11 @@ struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv, orig_node->bat_priv = bat_priv; memcpy(orig_node->orig, addr, ETH_ALEN); batadv_dat_init_orig_node_addr(orig_node); - orig_node->router = NULL; atomic_set(&orig_node->last_ttvn, 0); orig_node->tt_buff = NULL; orig_node->tt_buff_len = 0; reset_time = jiffies - 1 - msecs_to_jiffies(BATADV_RESET_PROTECTION_MS); orig_node->bcast_seqno_reset = reset_time; - orig_node->batman_seqno_reset = reset_time; - - atomic_set(&orig_node->bond_candidates, 0); /* create a vlan object for the "untagged" LAN */ vlan = batadv_orig_node_vlan_new(orig_node, BATADV_NO_FLAGS); @@ -376,20 +660,76 @@ free_orig_node: return NULL; } +/** + * batadv_purge_orig_ifinfo - purge obsolete ifinfo entries from originator + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: orig node which is to be checked + * + * Returns true if any ifinfo entry was purged, false otherwise. + */ +static bool +batadv_purge_orig_ifinfo(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node) +{ + struct batadv_orig_ifinfo *orig_ifinfo; + struct batadv_hard_iface *if_outgoing; + struct hlist_node *node_tmp; + bool ifinfo_purged = false; + + spin_lock_bh(&orig_node->neigh_list_lock); + + /* for all ifinfo objects for this originator */ + hlist_for_each_entry_safe(orig_ifinfo, node_tmp, + &orig_node->ifinfo_list, list) { + if_outgoing = orig_ifinfo->if_outgoing; + + /* always keep the default interface */ + if (if_outgoing == BATADV_IF_DEFAULT) + continue; + + /* don't purge if the interface is not (going) down */ + if ((if_outgoing->if_status != BATADV_IF_INACTIVE) && + (if_outgoing->if_status != BATADV_IF_NOT_IN_USE) && + (if_outgoing->if_status != BATADV_IF_TO_BE_REMOVED)) + continue; + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "router/ifinfo purge: originator %pM, iface: %s\n", + orig_node->orig, if_outgoing->net_dev->name); + + ifinfo_purged = true; + + hlist_del_rcu(&orig_ifinfo->list); + batadv_orig_ifinfo_free_ref(orig_ifinfo); + if (orig_node->last_bonding_candidate == orig_ifinfo) { + orig_node->last_bonding_candidate = NULL; + batadv_orig_ifinfo_free_ref(orig_ifinfo); + } + } + + spin_unlock_bh(&orig_node->neigh_list_lock); + + return ifinfo_purged; +} + + +/** + * batadv_purge_orig_neighbors - purges neighbors from originator + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: orig node which is to be checked + * + * Returns true if any neighbor was purged, false otherwise + */ static bool batadv_purge_orig_neighbors(struct batadv_priv *bat_priv, - struct batadv_orig_node *orig_node, - struct batadv_neigh_node **best_neigh) + struct batadv_orig_node *orig_node) { - struct batadv_algo_ops *bao = bat_priv->bat_algo_ops; struct hlist_node *node_tmp; struct batadv_neigh_node *neigh_node; bool neigh_purged = false; unsigned long last_seen; struct batadv_hard_iface *if_incoming; - *best_neigh = NULL; - spin_lock_bh(&orig_node->neigh_list_lock); /* for all neighbors towards this originator ... */ @@ -418,15 +758,7 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv, neigh_purged = true; hlist_del_rcu(&neigh_node->list); - batadv_bonding_candidate_del(orig_node, neigh_node); batadv_neigh_node_free_ref(neigh_node); - } else { - /* store the best_neighbour if this is the first - * iteration or if a better neighbor has been found - */ - if (!*best_neigh || - bao->bat_neigh_cmp(neigh_node, *best_neigh) > 0) - *best_neigh = neigh_node; } } @@ -434,10 +766,57 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv, return neigh_purged; } +/** + * batadv_find_best_neighbor - finds the best neighbor after purging + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: orig node which is to be checked + * @if_outgoing: the interface for which the metric should be compared + * + * Returns the current best neighbor, with refcount increased. + */ +static struct batadv_neigh_node * +batadv_find_best_neighbor(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node, + struct batadv_hard_iface *if_outgoing) +{ + struct batadv_neigh_node *best = NULL, *neigh; + struct batadv_algo_ops *bao = bat_priv->bat_algo_ops; + + rcu_read_lock(); + hlist_for_each_entry_rcu(neigh, &orig_node->neigh_list, list) { + if (best && (bao->bat_neigh_cmp(neigh, if_outgoing, + best, if_outgoing) <= 0)) + continue; + + if (!atomic_inc_not_zero(&neigh->refcount)) + continue; + + if (best) + batadv_neigh_node_free_ref(best); + + best = neigh; + } + rcu_read_unlock(); + + return best; +} + +/** + * batadv_purge_orig_node - purges obsolete information from an orig_node + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: orig node which is to be checked + * + * This function checks if the orig_node or substructures of it have become + * obsolete, and purges this information if that's the case. + * + * Returns true if the orig_node is to be removed, false otherwise. + */ static bool batadv_purge_orig_node(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node) { struct batadv_neigh_node *best_neigh_node; + struct batadv_hard_iface *hard_iface; + bool changed; if (batadv_has_timed_out(orig_node->last_seen, 2 * BATADV_PURGE_TIMEOUT)) { @@ -446,12 +825,39 @@ static bool batadv_purge_orig_node(struct batadv_priv *bat_priv, orig_node->orig, jiffies_to_msecs(orig_node->last_seen)); return true; - } else { - if (batadv_purge_orig_neighbors(bat_priv, orig_node, - &best_neigh_node)) - batadv_update_route(bat_priv, orig_node, - best_neigh_node); } + changed = batadv_purge_orig_ifinfo(bat_priv, orig_node); + changed = changed || batadv_purge_orig_neighbors(bat_priv, orig_node); + + if (!changed) + return false; + + /* first for NULL ... */ + best_neigh_node = batadv_find_best_neighbor(bat_priv, orig_node, + BATADV_IF_DEFAULT); + batadv_update_route(bat_priv, orig_node, BATADV_IF_DEFAULT, + best_neigh_node); + if (best_neigh_node) + batadv_neigh_node_free_ref(best_neigh_node); + + /* ... then for all other interfaces. */ + rcu_read_lock(); + list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { + if (hard_iface->if_status != BATADV_IF_ACTIVE) + continue; + + if (hard_iface->soft_iface != bat_priv->soft_iface) + continue; + + best_neigh_node = batadv_find_best_neighbor(bat_priv, + orig_node, + hard_iface); + batadv_update_route(bat_priv, orig_node, hard_iface, + best_neigh_node); + if (best_neigh_node) + batadv_neigh_node_free_ref(best_neigh_node); + } + rcu_read_unlock(); return false; } @@ -534,8 +940,54 @@ int batadv_orig_seq_print_text(struct seq_file *seq, void *offset) return 0; } - bat_priv->bat_algo_ops->bat_orig_print(bat_priv, seq); + bat_priv->bat_algo_ops->bat_orig_print(bat_priv, seq, + BATADV_IF_DEFAULT); + + return 0; +} + +/** + * batadv_orig_hardif_seq_print_text - writes originator infos for a specific + * outgoing interface + * @seq: debugfs table seq_file struct + * @offset: not used + * + * Returns 0 + */ +int batadv_orig_hardif_seq_print_text(struct seq_file *seq, void *offset) +{ + struct net_device *net_dev = (struct net_device *)seq->private; + struct batadv_hard_iface *hard_iface; + struct batadv_priv *bat_priv; + + hard_iface = batadv_hardif_get_by_netdev(net_dev); + + if (!hard_iface || !hard_iface->soft_iface) { + seq_puts(seq, "Interface not known to B.A.T.M.A.N.\n"); + goto out; + } + + bat_priv = netdev_priv(hard_iface->soft_iface); + if (!bat_priv->bat_algo_ops->bat_orig_print) { + seq_puts(seq, + "No printing function for this routing protocol\n"); + goto out; + } + + if (hard_iface->if_status != BATADV_IF_ACTIVE) { + seq_puts(seq, "Interface not active\n"); + goto out; + } + seq_printf(seq, "[B.A.T.M.A.N. adv %s, IF/MAC: %s/%pM (%s %s)]\n", + BATADV_SOURCE_VERSION, hard_iface->net_dev->name, + hard_iface->net_dev->dev_addr, + hard_iface->soft_iface->name, bat_priv->bat_algo_ops->name); + + bat_priv->bat_algo_ops->bat_orig_print(bat_priv, seq, hard_iface); + +out: + batadv_hardif_free_ref(hard_iface); return 0; } diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h index 6f77d808a916..37be290f63f6 100644 --- a/net/batman-adv/originator.h +++ b/net/batman-adv/originator.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_ORIGINATOR_H_ @@ -36,8 +34,26 @@ batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, struct batadv_orig_node *orig_node); void batadv_neigh_node_free_ref(struct batadv_neigh_node *neigh_node); struct batadv_neigh_node * -batadv_orig_node_get_router(struct batadv_orig_node *orig_node); +batadv_orig_router_get(struct batadv_orig_node *orig_node, + const struct batadv_hard_iface *if_outgoing); +struct batadv_neigh_ifinfo * +batadv_neigh_ifinfo_new(struct batadv_neigh_node *neigh, + struct batadv_hard_iface *if_outgoing); +struct batadv_neigh_ifinfo * +batadv_neigh_ifinfo_get(struct batadv_neigh_node *neigh, + struct batadv_hard_iface *if_outgoing); +void batadv_neigh_ifinfo_free_ref(struct batadv_neigh_ifinfo *neigh_ifinfo); + +struct batadv_orig_ifinfo * +batadv_orig_ifinfo_get(struct batadv_orig_node *orig_node, + struct batadv_hard_iface *if_outgoing); +struct batadv_orig_ifinfo * +batadv_orig_ifinfo_new(struct batadv_orig_node *orig_node, + struct batadv_hard_iface *if_outgoing); +void batadv_orig_ifinfo_free_ref(struct batadv_orig_ifinfo *orig_ifinfo); + int batadv_orig_seq_print_text(struct seq_file *seq, void *offset); +int batadv_orig_hardif_seq_print_text(struct seq_file *seq, void *offset); int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface, int max_if_num); int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface, diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h index 2dd8f2422550..0a381d1174c1 100644 --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_PACKET_H_ @@ -117,6 +115,7 @@ enum batadv_tt_client_flags { BATADV_TT_CLIENT_DEL = BIT(0), BATADV_TT_CLIENT_ROAM = BIT(1), BATADV_TT_CLIENT_WIFI = BIT(4), + BATADV_TT_CLIENT_ISOLA = BIT(5), BATADV_TT_CLIENT_NOPURGE = BIT(8), BATADV_TT_CLIENT_NEW = BIT(9), BATADV_TT_CLIENT_PENDING = BIT(10), diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 46278bfb8fdb..1ed9f7c9ecea 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" @@ -35,13 +33,32 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); +/** + * _batadv_update_route - set the router for this originator + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: orig node which is to be configured + * @recv_if: the receive interface for which this route is set + * @neigh_node: neighbor which should be the next router + * + * This function does not perform any error checks + */ static void _batadv_update_route(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, + struct batadv_hard_iface *recv_if, struct batadv_neigh_node *neigh_node) { + struct batadv_orig_ifinfo *orig_ifinfo; struct batadv_neigh_node *curr_router; - curr_router = batadv_orig_node_get_router(orig_node); + orig_ifinfo = batadv_orig_ifinfo_get(orig_node, recv_if); + if (!orig_ifinfo) + return; + + rcu_read_lock(); + curr_router = rcu_dereference(orig_ifinfo->router); + if (curr_router && !atomic_inc_not_zero(&curr_router->refcount)) + curr_router = NULL; + rcu_read_unlock(); /* route deleted */ if ((curr_router) && (!neigh_node)) { @@ -71,16 +88,25 @@ static void _batadv_update_route(struct batadv_priv *bat_priv, neigh_node = NULL; spin_lock_bh(&orig_node->neigh_list_lock); - rcu_assign_pointer(orig_node->router, neigh_node); + rcu_assign_pointer(orig_ifinfo->router, neigh_node); spin_unlock_bh(&orig_node->neigh_list_lock); + batadv_orig_ifinfo_free_ref(orig_ifinfo); /* decrease refcount of previous best neighbor */ if (curr_router) batadv_neigh_node_free_ref(curr_router); } +/** + * batadv_update_route - set the router for this originator + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: orig node which is to be configured + * @recv_if: the receive interface for which this route is set + * @neigh_node: neighbor which should be the next router + */ void batadv_update_route(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, + struct batadv_hard_iface *recv_if, struct batadv_neigh_node *neigh_node) { struct batadv_neigh_node *router = NULL; @@ -88,125 +114,16 @@ void batadv_update_route(struct batadv_priv *bat_priv, if (!orig_node) goto out; - router = batadv_orig_node_get_router(orig_node); + router = batadv_orig_router_get(orig_node, recv_if); if (router != neigh_node) - _batadv_update_route(bat_priv, orig_node, neigh_node); + _batadv_update_route(bat_priv, orig_node, recv_if, neigh_node); out: if (router) batadv_neigh_node_free_ref(router); } -/* caller must hold the neigh_list_lock */ -void batadv_bonding_candidate_del(struct batadv_orig_node *orig_node, - struct batadv_neigh_node *neigh_node) -{ - /* this neighbor is not part of our candidate list */ - if (list_empty(&neigh_node->bonding_list)) - goto out; - - list_del_rcu(&neigh_node->bonding_list); - INIT_LIST_HEAD(&neigh_node->bonding_list); - batadv_neigh_node_free_ref(neigh_node); - atomic_dec(&orig_node->bond_candidates); - -out: - return; -} - -/** - * batadv_bonding_candidate_add - consider a new link for bonding mode towards - * the given originator - * @bat_priv: the bat priv with all the soft interface information - * @orig_node: the target node - * @neigh_node: the neighbor representing the new link to consider for bonding - * mode - */ -void batadv_bonding_candidate_add(struct batadv_priv *bat_priv, - struct batadv_orig_node *orig_node, - struct batadv_neigh_node *neigh_node) -{ - struct batadv_algo_ops *bao = bat_priv->bat_algo_ops; - struct batadv_neigh_node *tmp_neigh_node, *router = NULL; - uint8_t interference_candidate = 0; - - spin_lock_bh(&orig_node->neigh_list_lock); - - /* only consider if it has the same primary address ... */ - if (!batadv_compare_eth(orig_node->orig, - neigh_node->orig_node->primary_addr)) - goto candidate_del; - - router = batadv_orig_node_get_router(orig_node); - if (!router) - goto candidate_del; - - - /* ... and is good enough to be considered */ - if (bao->bat_neigh_is_equiv_or_better(neigh_node, router)) - goto candidate_del; - - /* check if we have another candidate with the same mac address or - * interface. If we do, we won't select this candidate because of - * possible interference. - */ - hlist_for_each_entry_rcu(tmp_neigh_node, - &orig_node->neigh_list, list) { - if (tmp_neigh_node == neigh_node) - continue; - - /* we only care if the other candidate is even - * considered as candidate. - */ - if (list_empty(&tmp_neigh_node->bonding_list)) - continue; - - if ((neigh_node->if_incoming == tmp_neigh_node->if_incoming) || - (batadv_compare_eth(neigh_node->addr, - tmp_neigh_node->addr))) { - interference_candidate = 1; - break; - } - } - - /* don't care further if it is an interference candidate */ - if (interference_candidate) - goto candidate_del; - - /* this neighbor already is part of our candidate list */ - if (!list_empty(&neigh_node->bonding_list)) - goto out; - - if (!atomic_inc_not_zero(&neigh_node->refcount)) - goto out; - - list_add_rcu(&neigh_node->bonding_list, &orig_node->bond_list); - atomic_inc(&orig_node->bond_candidates); - goto out; - -candidate_del: - batadv_bonding_candidate_del(orig_node, neigh_node); - -out: - spin_unlock_bh(&orig_node->neigh_list_lock); - - if (router) - batadv_neigh_node_free_ref(router); -} - -/* copy primary address for bonding */ -void -batadv_bonding_save_primary(const struct batadv_orig_node *orig_node, - struct batadv_orig_node *orig_neigh_node, - const struct batadv_ogm_packet *batman_ogm_packet) -{ - if (!(batman_ogm_packet->flags & BATADV_PRIMARIES_FIRST_HOP)) - return; - - memcpy(orig_neigh_node->primary_addr, orig_node->orig, ETH_ALEN); -} - /* checks whether the host restarted and is in the protection time. * returns: * 0 if the packet is to be accepted @@ -461,114 +378,6 @@ out: return ret; } -/* In the bonding case, send the packets in a round - * robin fashion over the remaining interfaces. - * - * This method rotates the bonding list and increases the - * returned router's refcount. - */ -static struct batadv_neigh_node * -batadv_find_bond_router(struct batadv_orig_node *primary_orig, - const struct batadv_hard_iface *recv_if) -{ - struct batadv_neigh_node *tmp_neigh_node; - struct batadv_neigh_node *router = NULL, *first_candidate = NULL; - - rcu_read_lock(); - list_for_each_entry_rcu(tmp_neigh_node, &primary_orig->bond_list, - bonding_list) { - if (!first_candidate) - first_candidate = tmp_neigh_node; - - /* recv_if == NULL on the first node. */ - if (tmp_neigh_node->if_incoming == recv_if) - continue; - - if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) - continue; - - router = tmp_neigh_node; - break; - } - - /* use the first candidate if nothing was found. */ - if (!router && first_candidate && - atomic_inc_not_zero(&first_candidate->refcount)) - router = first_candidate; - - if (!router) - goto out; - - /* selected should point to the next element - * after the current router - */ - spin_lock_bh(&primary_orig->neigh_list_lock); - /* this is a list_move(), which unfortunately - * does not exist as rcu version - */ - list_del_rcu(&primary_orig->bond_list); - list_add_rcu(&primary_orig->bond_list, - &router->bonding_list); - spin_unlock_bh(&primary_orig->neigh_list_lock); - -out: - rcu_read_unlock(); - return router; -} - -/** - * batadv_find_ifalter_router - find the best of the remaining candidates which - * are not using this interface - * @bat_priv: the bat priv with all the soft interface information - * @primary_orig: the destination - * @recv_if: the interface that the router returned by this function has to not - * use - * - * Returns the best candidate towards primary_orig that is not using recv_if. - * Increases the returned neighbor's refcount - */ -static struct batadv_neigh_node * -batadv_find_ifalter_router(struct batadv_priv *bat_priv, - struct batadv_orig_node *primary_orig, - const struct batadv_hard_iface *recv_if) -{ - struct batadv_neigh_node *router = NULL, *first_candidate = NULL; - struct batadv_algo_ops *bao = bat_priv->bat_algo_ops; - struct batadv_neigh_node *tmp_neigh_node; - - rcu_read_lock(); - list_for_each_entry_rcu(tmp_neigh_node, &primary_orig->bond_list, - bonding_list) { - if (!first_candidate) - first_candidate = tmp_neigh_node; - - /* recv_if == NULL on the first node. */ - if (tmp_neigh_node->if_incoming == recv_if) - continue; - - if (router && bao->bat_neigh_cmp(tmp_neigh_node, router)) - continue; - - if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) - continue; - - /* decrement refcount of previously selected router */ - if (router) - batadv_neigh_node_free_ref(router); - - /* we found a better router (or at least one valid router) */ - router = tmp_neigh_node; - } - - /* use the first candidate if nothing was found. */ - if (!router && first_candidate && - atomic_inc_not_zero(&first_candidate->refcount)) - router = first_candidate; - - rcu_read_unlock(); - return router; -} - /** * batadv_check_unicast_packet - Check for malformed unicast packets * @bat_priv: the bat priv with all the soft interface information @@ -606,95 +415,141 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv, return 0; } -/* find a suitable router for this originator, and use - * bonding if possible. increases the found neighbors - * refcount. +/** + * batadv_find_router - find a suitable router for this originator + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: the destination node + * @recv_if: pointer to interface this packet was received on + * + * Returns the router which should be used for this orig_node on + * this interface, or NULL if not available. */ struct batadv_neigh_node * batadv_find_router(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, - const struct batadv_hard_iface *recv_if) + struct batadv_hard_iface *recv_if) { - struct batadv_orig_node *primary_orig_node; - struct batadv_orig_node *router_orig; - struct batadv_neigh_node *router; - static uint8_t zero_mac[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; - int bonding_enabled; - uint8_t *primary_addr; + struct batadv_algo_ops *bao = bat_priv->bat_algo_ops; + struct batadv_neigh_node *first_candidate_router = NULL; + struct batadv_neigh_node *next_candidate_router = NULL; + struct batadv_neigh_node *router, *cand_router = NULL; + struct batadv_neigh_node *last_cand_router = NULL; + struct batadv_orig_ifinfo *cand, *first_candidate = NULL; + struct batadv_orig_ifinfo *next_candidate = NULL; + struct batadv_orig_ifinfo *last_candidate; + bool last_candidate_found = false; if (!orig_node) return NULL; - router = batadv_orig_node_get_router(orig_node); - if (!router) - goto err; + router = batadv_orig_router_get(orig_node, recv_if); - /* without bonding, the first node should - * always choose the default router. + /* only consider bonding for recv_if == BATADV_IF_DEFAULT (first hop) + * and if activated. + */ + if (recv_if == BATADV_IF_DEFAULT || !atomic_read(&bat_priv->bonding) || + !router) + return router; + + /* bonding: loop through the list of possible routers found + * for the various outgoing interfaces and find a candidate after + * the last chosen bonding candidate (next_candidate). If no such + * router is found, use the first candidate found (the previously + * chosen bonding candidate might have been the last one in the list). + * If this can't be found either, return the previously choosen + * router - obviously there are no other candidates. */ - bonding_enabled = atomic_read(&bat_priv->bonding); - rcu_read_lock(); - /* select default router to output */ - router_orig = router->orig_node; - if (!router_orig) - goto err_unlock; + last_candidate = orig_node->last_bonding_candidate; + if (last_candidate) + last_cand_router = rcu_dereference(last_candidate->router); - if ((!recv_if) && (!bonding_enabled)) - goto return_router; + hlist_for_each_entry_rcu(cand, &orig_node->ifinfo_list, list) { + /* acquire some structures and references ... */ + if (!atomic_inc_not_zero(&cand->refcount)) + continue; - primary_addr = router_orig->primary_addr; + cand_router = rcu_dereference(cand->router); + if (!cand_router) + goto next; - /* if we have something in the primary_addr, we can search - * for a potential bonding candidate. - */ - if (batadv_compare_eth(primary_addr, zero_mac)) - goto return_router; + if (!atomic_inc_not_zero(&cand_router->refcount)) { + cand_router = NULL; + goto next; + } - /* find the orig_node which has the primary interface. might - * even be the same as our router_orig in many cases - */ - if (batadv_compare_eth(primary_addr, router_orig->orig)) { - primary_orig_node = router_orig; - } else { - primary_orig_node = batadv_orig_hash_find(bat_priv, - primary_addr); - if (!primary_orig_node) - goto return_router; + /* alternative candidate should be good enough to be + * considered + */ + if (!bao->bat_neigh_is_equiv_or_better(cand_router, + cand->if_outgoing, + router, recv_if)) + goto next; + + /* don't use the same router twice */ + if (last_cand_router == cand_router) + goto next; + + /* mark the first possible candidate */ + if (!first_candidate) { + atomic_inc(&cand_router->refcount); + atomic_inc(&cand->refcount); + first_candidate = cand; + first_candidate_router = cand_router; + } + + /* check if the loop has already passed the previously selected + * candidate ... this function should select the next candidate + * AFTER the previously used bonding candidate. + */ + if (!last_candidate || last_candidate_found) { + next_candidate = cand; + next_candidate_router = cand_router; + break; + } - batadv_orig_node_free_ref(primary_orig_node); + if (last_candidate == cand) + last_candidate_found = true; +next: + /* free references */ + if (cand_router) { + batadv_neigh_node_free_ref(cand_router); + cand_router = NULL; + } + batadv_orig_ifinfo_free_ref(cand); } + rcu_read_unlock(); - /* with less than 2 candidates, we can't do any - * bonding and prefer the original router. - */ - if (atomic_read(&primary_orig_node->bond_candidates) < 2) - goto return_router; + /* last_bonding_candidate is reset below, remove the old reference. */ + if (orig_node->last_bonding_candidate) + batadv_orig_ifinfo_free_ref(orig_node->last_bonding_candidate); - /* all nodes between should choose a candidate which - * is is not on the interface where the packet came - * in. + /* After finding candidates, handle the three cases: + * 1) there is a next candidate, use that + * 2) there is no next candidate, use the first of the list + * 3) there is no candidate at all, return the default router */ - batadv_neigh_node_free_ref(router); + if (next_candidate) { + batadv_neigh_node_free_ref(router); - if (bonding_enabled) - router = batadv_find_bond_router(primary_orig_node, recv_if); - else - router = batadv_find_ifalter_router(bat_priv, primary_orig_node, - recv_if); + /* remove references to first candidate, we don't need it. */ + if (first_candidate) { + batadv_neigh_node_free_ref(first_candidate_router); + batadv_orig_ifinfo_free_ref(first_candidate); + } + router = next_candidate_router; + orig_node->last_bonding_candidate = next_candidate; + } else if (first_candidate) { + batadv_neigh_node_free_ref(router); -return_router: - if (router && router->if_incoming->if_status != BATADV_IF_ACTIVE) - goto err_unlock; + /* refcounting has already been done in the loop above. */ + router = first_candidate_router; + orig_node->last_bonding_candidate = first_candidate; + } else { + orig_node->last_bonding_candidate = NULL; + } - rcu_read_unlock(); return router; -err_unlock: - rcu_read_unlock(); -err: - if (router) - batadv_neigh_node_free_ref(router); - return NULL; } static int batadv_route_unicast_packet(struct sk_buff *skb, @@ -1135,6 +990,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, int hdr_size = sizeof(*bcast_packet); int ret = NET_RX_DROP; int32_t seq_diff; + uint32_t seqno; /* drop packet if it has not necessary minimum size */ if (unlikely(!pskb_may_pull(skb, hdr_size))) @@ -1170,12 +1026,13 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, spin_lock_bh(&orig_node->bcast_seqno_lock); + seqno = ntohl(bcast_packet->seqno); /* check whether the packet is a duplicate */ if (batadv_test_bit(orig_node->bcast_bits, orig_node->last_bcast_seqno, - ntohl(bcast_packet->seqno))) + seqno)) goto spin_unlock; - seq_diff = ntohl(bcast_packet->seqno) - orig_node->last_bcast_seqno; + seq_diff = seqno - orig_node->last_bcast_seqno; /* check whether the packet is old and the host just restarted. */ if (batadv_window_protected(bat_priv, seq_diff, @@ -1186,7 +1043,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, * if required. */ if (batadv_bit_get_packet(bat_priv, orig_node->bcast_bits, seq_diff, 1)) - orig_node->last_bcast_seqno = ntohl(bcast_packet->seqno); + orig_node->last_bcast_seqno = seqno; spin_unlock_bh(&orig_node->bcast_seqno_lock); diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h index 19544ddb81b5..557d3d12a9ab 100644 --- a/net/batman-adv/routing.h +++ b/net/batman-adv/routing.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_ROUTING_H_ @@ -25,6 +23,7 @@ bool batadv_check_management_packet(struct sk_buff *skb, int header_len); void batadv_update_route(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, + struct batadv_hard_iface *recv_if, struct batadv_neigh_node *neigh_node); int batadv_recv_icmp_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); @@ -45,16 +44,7 @@ int batadv_recv_unhandled_unicast_packet(struct sk_buff *skb, struct batadv_neigh_node * batadv_find_router(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, - const struct batadv_hard_iface *recv_if); -void batadv_bonding_candidate_del(struct batadv_orig_node *orig_node, - struct batadv_neigh_node *neigh_node); -void batadv_bonding_candidate_add(struct batadv_priv *bat_priv, - struct batadv_orig_node *orig_node, - struct batadv_neigh_node *neigh_node); -void batadv_bonding_save_primary(const struct batadv_orig_node *orig_node, - struct batadv_orig_node *orig_neigh_node, - const struct batadv_ogm_packet - *batman_ogm_packet); + struct batadv_hard_iface *recv_if); int batadv_window_protected(struct batadv_priv *bat_priv, int32_t seq_num_diff, unsigned long *last_reset); diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index fba4dcfcfac2..579f5f00a385 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" @@ -321,13 +319,23 @@ out: */ int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv, struct sk_buff *skb, int packet_type, - int packet_subtype, unsigned short vid) + int packet_subtype, uint8_t *dst_hint, + unsigned short vid) { struct ethhdr *ethhdr = (struct ethhdr *)skb->data; struct batadv_orig_node *orig_node; + uint8_t *src, *dst; + + src = ethhdr->h_source; + dst = ethhdr->h_dest; + + /* if we got an hint! let's send the packet to this client (if any) */ + if (dst_hint) { + src = NULL; + dst = dst_hint; + } + orig_node = batadv_transtable_search(bat_priv, src, dst, vid); - orig_node = batadv_transtable_search(bat_priv, ethhdr->h_source, - ethhdr->h_dest, vid); return batadv_send_skb_unicast(bat_priv, skb, packet_type, packet_subtype, orig_node, vid); } @@ -379,6 +387,8 @@ static void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet) kfree_skb(forw_packet->skb); if (forw_packet->if_incoming) batadv_hardif_free_ref(forw_packet->if_incoming); + if (forw_packet->if_outgoing) + batadv_hardif_free_ref(forw_packet->if_outgoing); kfree(forw_packet); } @@ -442,6 +452,7 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, forw_packet->skb = newskb; forw_packet->if_incoming = primary_if; + forw_packet->if_outgoing = NULL; /* how often did we send the bcast packet ? */ forw_packet->num_packets = 0; @@ -537,11 +548,16 @@ void batadv_send_outstanding_bat_ogm_packet(struct work_struct *work) bat_priv->bat_algo_ops->bat_ogm_emit(forw_packet); - /* we have to have at least one packet in the queue - * to determine the queues wake up time unless we are - * shutting down + /* 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) + if (forw_packet->own && + forw_packet->if_incoming == forw_packet->if_outgoing) batadv_schedule_bat_ogm(forw_packet->if_incoming); out: @@ -602,7 +618,8 @@ batadv_purge_outstanding_packets(struct batadv_priv *bat_priv, * we delete only packets belonging to the given interface */ if ((hard_iface) && - (forw_packet->if_incoming != hard_iface)) + (forw_packet->if_incoming != hard_iface) && + (forw_packet->if_outgoing != hard_iface)) continue; spin_unlock_bh(&bat_priv->forw_bat_list_lock); diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h index aa2e2537a739..aaddaa9661ce 100644 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_SEND_H_ @@ -40,7 +38,8 @@ bool batadv_send_skb_prepare_unicast_4addr(struct batadv_priv *bat_priv, int packet_subtype); int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv, struct sk_buff *skb, int packet_type, - int packet_subtype, unsigned short vid); + int packet_subtype, uint8_t *dst_hint, + unsigned short vid); int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb, unsigned short vid); @@ -57,11 +56,11 @@ int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb, * Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise. */ static inline int batadv_send_skb_via_tt(struct batadv_priv *bat_priv, - struct sk_buff *skb, + struct sk_buff *skb, uint8_t *dst_hint, unsigned short vid) { return batadv_send_skb_via_tt_generic(bat_priv, skb, BATADV_UNICAST, 0, - vid); + dst_hint, vid); } /** @@ -81,11 +80,12 @@ static inline int batadv_send_skb_via_tt(struct batadv_priv *bat_priv, static inline int batadv_send_skb_via_tt_4addr(struct batadv_priv *bat_priv, struct sk_buff *skb, int packet_subtype, + uint8_t *dst_hint, unsigned short vid) { return batadv_send_skb_via_tt_generic(bat_priv, skb, BATADV_UNICAST_4ADDR, - packet_subtype, vid); + packet_subtype, dst_hint, vid); } #endif /* _NET_BATMAN_ADV_SEND_H_ */ diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index a8f99d1486c0..f82c267e1886 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" @@ -121,7 +119,7 @@ static int batadv_interface_set_mac_addr(struct net_device *dev, void *p) batadv_tt_local_remove(bat_priv, old_addr, BATADV_NO_FLAGS, "mac address changed", false); batadv_tt_local_add(dev, addr->sa_data, BATADV_NO_FLAGS, - BATADV_NULL_IFINDEX); + BATADV_NULL_IFINDEX, BATADV_NO_MARK); } return 0; @@ -162,6 +160,8 @@ static int batadv_interface_tx(struct sk_buff *skb, 0x00, 0x00}; static const uint8_t ectp_addr[ETH_ALEN] = {0xCF, 0x00, 0x00, 0x00, 0x00, 0x00}; + enum batadv_dhcp_recipient dhcp_rcp = BATADV_DHCP_NO; + uint8_t *dst_hint = NULL, chaddr[ETH_ALEN]; struct vlan_ethhdr *vhdr; unsigned int header_len = 0; int data_len = skb->len, ret; @@ -169,6 +169,7 @@ static int batadv_interface_tx(struct sk_buff *skb, bool do_bcast = false, client_added; unsigned short vid; uint32_t seqno; + int gw_mode; if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto dropped; @@ -198,7 +199,8 @@ static int batadv_interface_tx(struct sk_buff *skb, /* Register the client MAC in the transtable */ if (!is_multicast_ether_addr(ethhdr->h_source)) { client_added = batadv_tt_local_add(soft_iface, ethhdr->h_source, - vid, skb->skb_iif); + vid, skb->skb_iif, + skb->mark); if (!client_added) goto dropped; } @@ -215,36 +217,39 @@ static int batadv_interface_tx(struct sk_buff *skb, if (batadv_compare_eth(ethhdr->h_dest, ectp_addr)) goto dropped; + gw_mode = atomic_read(&bat_priv->gw_mode); if (is_multicast_ether_addr(ethhdr->h_dest)) { - do_bcast = true; - - switch (atomic_read(&bat_priv->gw_mode)) { - case BATADV_GW_MODE_SERVER: - /* gateway servers should not send dhcp - * requests into the mesh - */ - ret = batadv_gw_is_dhcp_target(skb, &header_len); - if (ret) - goto dropped; - break; - case BATADV_GW_MODE_CLIENT: - /* gateway clients should send dhcp requests - * via unicast to their gateway - */ - ret = batadv_gw_is_dhcp_target(skb, &header_len); - if (ret) - do_bcast = false; - break; - case BATADV_GW_MODE_OFF: - default: - break; + /* if gw mode is off, broadcast every packet */ + if (gw_mode == BATADV_GW_MODE_OFF) { + do_bcast = true; + goto send; } - /* reminder: ethhdr might have become unusable from here on - * (batadv_gw_is_dhcp_target() might have reallocated skb data) + dhcp_rcp = batadv_gw_dhcp_recipient_get(skb, &header_len, + chaddr); + /* skb->data may have been modified by + * batadv_gw_dhcp_recipient_get() */ + ethhdr = (struct ethhdr *)skb->data; + /* if gw_mode is on, broadcast any non-DHCP message. + * All the DHCP packets are going to be sent as unicast + */ + if (dhcp_rcp == BATADV_DHCP_NO) { + do_bcast = true; + goto send; + } + + if (dhcp_rcp == BATADV_DHCP_TO_CLIENT) + dst_hint = chaddr; + else if ((gw_mode == BATADV_GW_MODE_SERVER) && + (dhcp_rcp == BATADV_DHCP_TO_SERVER)) + /* gateways should not forward any DHCP message if + * directed to a DHCP server + */ + goto dropped; } +send: batadv_skb_set_priority(skb, 0); /* ethernet packet should be broadcasted */ @@ -290,22 +295,22 @@ static int batadv_interface_tx(struct sk_buff *skb, /* unicast packet */ } else { - if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_OFF) { + /* DHCP packets going to a server will use the GW feature */ + if (dhcp_rcp == BATADV_DHCP_TO_SERVER) { ret = batadv_gw_out_of_range(bat_priv, skb); if (ret) goto dropped; - } - - if (batadv_dat_snoop_outgoing_arp_request(bat_priv, skb)) - goto dropped; - - batadv_dat_snoop_outgoing_arp_reply(bat_priv, skb); - - if (is_multicast_ether_addr(ethhdr->h_dest)) ret = batadv_send_skb_via_gw(bat_priv, skb, vid); - else - ret = batadv_send_skb_via_tt(bat_priv, skb, vid); + } else { + if (batadv_dat_snoop_outgoing_arp_request(bat_priv, + skb)) + goto dropped; + batadv_dat_snoop_outgoing_arp_reply(bat_priv, skb); + + ret = batadv_send_skb_via_tt(bat_priv, skb, dst_hint, + vid); + } if (ret == NET_XMIT_DROP) goto dropped_freed; } @@ -394,9 +399,23 @@ void batadv_interface_rx(struct net_device *soft_iface, batadv_tt_add_temporary_global_entry(bat_priv, orig_node, ethhdr->h_source, vid); - if (batadv_is_ap_isolated(bat_priv, ethhdr->h_source, ethhdr->h_dest, - vid)) + if (is_multicast_ether_addr(ethhdr->h_dest)) { + /* set the mark on broadcast packets if AP isolation is ON and + * the packet is coming from an "isolated" client + */ + if (batadv_vlan_ap_isola_get(bat_priv, vid) && + batadv_tt_global_is_isolated(bat_priv, ethhdr->h_source, + vid)) { + /* save bits in skb->mark not covered by the mask and + * apply the mark on the rest + */ + skb->mark &= ~bat_priv->isolation_mark_mask; + skb->mark |= bat_priv->isolation_mark; + } + } else if (batadv_is_ap_isolated(bat_priv, ethhdr->h_source, + ethhdr->h_dest, vid)) { goto dropped; + } netif_rx(skb); goto out; @@ -485,7 +504,7 @@ int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid) */ batadv_tt_local_add(bat_priv->soft_iface, bat_priv->soft_iface->dev_addr, vid, - BATADV_NULL_IFINDEX); + BATADV_NULL_IFINDEX, BATADV_NO_MARK); spin_lock_bh(&bat_priv->softif_vlan_list_lock); hlist_add_head_rcu(&vlan->list, &bat_priv->softif_vlan_list); @@ -678,7 +697,7 @@ static int batadv_softif_init_late(struct net_device *dev) 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); + atomic_set(&bat_priv->hop_penalty, 15); #ifdef CONFIG_BATMAN_ADV_DEBUG atomic_set(&bat_priv->log_level, 0); #endif @@ -697,6 +716,8 @@ static int batadv_softif_init_late(struct net_device *dev) #endif bat_priv->tt.last_changeset = NULL; bat_priv->tt.last_changeset_len = 0; + bat_priv->isolation_mark = 0; + bat_priv->isolation_mark_mask = 0; /* randomize initial seqno to avoid collision */ get_random_bytes(&random_seqno, sizeof(random_seqno)); diff --git a/net/batman-adv/soft-interface.h b/net/batman-adv/soft-interface.h index 06fc91ff5a02..dbab22fd89a5 100644 --- a/net/batman-adv/soft-interface.h +++ b/net/batman-adv/soft-interface.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_SOFT_INTERFACE_H_ diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c index 6335433310af..e456bf6bb284 100644 --- a/net/batman-adv/sysfs.c +++ b/net/batman-adv/sysfs.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2010-2014 B.A.T.M.A.N. contributors: * * Marek Lindner * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" @@ -329,10 +327,10 @@ static ssize_t batadv_show_bat_algo(struct kobject *kobj, return sprintf(buff, "%s\n", bat_priv->bat_algo_ops->name); } -static void batadv_post_gw_deselect(struct net_device *net_dev) +static void batadv_post_gw_reselect(struct net_device *net_dev) { struct batadv_priv *bat_priv = netdev_priv(net_dev); - batadv_gw_deselect(bat_priv); + batadv_gw_reselect(bat_priv); } static ssize_t batadv_show_gw_mode(struct kobject *kobj, struct attribute *attr, @@ -408,7 +406,16 @@ static ssize_t batadv_store_gw_mode(struct kobject *kobj, batadv_info(net_dev, "Changing gw mode from: %s to: %s\n", curr_gw_mode_str, buff); - batadv_gw_deselect(bat_priv); + /* Invoking batadv_gw_reselect() is not enough to really de-select the + * current GW. It will only instruct the gateway client code to perform + * a re-election the next time that this is needed. + * + * When gw client mode is being switched off the current GW must be + * de-selected explicitly otherwise no GW_ADD uevent is thrown on + * client mode re-activation. This is operation is performed in + * batadv_gw_check_client_stop(). + */ + batadv_gw_reselect(bat_priv); /* always call batadv_gw_check_client_stop() before changing the gateway * state */ @@ -443,6 +450,74 @@ static ssize_t batadv_store_gw_bwidth(struct kobject *kobj, return batadv_gw_bandwidth_set(net_dev, buff, count); } +/** + * batadv_show_isolation_mark - print the current isolation mark/mask + * @kobj: kobject representing the private mesh sysfs directory + * @attr: the batman-adv attribute the user is interacting with + * @buff: the buffer that will contain the data to send back to the user + * + * Returns the number of bytes written into 'buff' on success or a negative + * error code in case of failure + */ +static ssize_t batadv_show_isolation_mark(struct kobject *kobj, + struct attribute *attr, char *buff) +{ + struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); + + return sprintf(buff, "%#.8x/%#.8x\n", bat_priv->isolation_mark, + bat_priv->isolation_mark_mask); +} + +/** + * batadv_store_isolation_mark - parse and store the isolation mark/mask entered + * by the user + * @kobj: kobject representing the private mesh sysfs directory + * @attr: the batman-adv attribute the user is interacting with + * @buff: the buffer containing the user data + * @count: number of bytes in the buffer + * + * Returns 'count' on success or a negative error code in case of failure + */ +static ssize_t batadv_store_isolation_mark(struct kobject *kobj, + struct attribute *attr, char *buff, + size_t count) +{ + struct net_device *net_dev = batadv_kobj_to_netdev(kobj); + struct batadv_priv *bat_priv = netdev_priv(net_dev); + uint32_t mark, mask; + char *mask_ptr; + + /* parse the mask if it has been specified, otherwise assume the mask is + * the biggest possible + */ + mask = 0xFFFFFFFF; + mask_ptr = strchr(buff, '/'); + if (mask_ptr) { + *mask_ptr = '\0'; + mask_ptr++; + + /* the mask must be entered in hex base as it is going to be a + * bitmask and not a prefix length + */ + if (kstrtou32(mask_ptr, 16, &mask) < 0) + return -EINVAL; + } + + /* the mark can be entered in any base */ + if (kstrtou32(buff, 0, &mark) < 0) + return -EINVAL; + + bat_priv->isolation_mark_mask = mask; + /* erase bits not covered by the mask */ + bat_priv->isolation_mark = mark & bat_priv->isolation_mark_mask; + + batadv_info(net_dev, + "New skb mark for extended isolation: %#.8x/%#.8x\n", + bat_priv->isolation_mark, bat_priv->isolation_mark_mask); + + return count; +} + BATADV_ATTR_SIF_BOOL(aggregated_ogms, S_IRUGO | S_IWUSR, NULL); BATADV_ATTR_SIF_BOOL(bonding, S_IRUGO | S_IWUSR, NULL); #ifdef CONFIG_BATMAN_ADV_BLA @@ -461,7 +536,7 @@ BATADV_ATTR_SIF_UINT(orig_interval, S_IRUGO | S_IWUSR, 2 * BATADV_JITTER, BATADV_ATTR_SIF_UINT(hop_penalty, S_IRUGO | S_IWUSR, 0, BATADV_TQ_MAX_VALUE, NULL); BATADV_ATTR_SIF_UINT(gw_sel_class, S_IRUGO | S_IWUSR, 1, BATADV_TQ_MAX_VALUE, - batadv_post_gw_deselect); + batadv_post_gw_reselect); static BATADV_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, batadv_show_gw_bwidth, batadv_store_gw_bwidth); #ifdef CONFIG_BATMAN_ADV_DEBUG @@ -471,6 +546,8 @@ BATADV_ATTR_SIF_UINT(log_level, S_IRUGO | S_IWUSR, 0, BATADV_DBG_ALL, NULL); BATADV_ATTR_SIF_BOOL(network_coding, S_IRUGO | S_IWUSR, batadv_nc_status_update); #endif +static BATADV_ATTR(isolation_mark, S_IRUGO | S_IWUSR, + batadv_show_isolation_mark, batadv_store_isolation_mark); static struct batadv_attribute *batadv_mesh_attrs[] = { &batadv_attr_aggregated_ogms, @@ -494,6 +571,7 @@ static struct batadv_attribute *batadv_mesh_attrs[] = { #ifdef CONFIG_BATMAN_ADV_NC &batadv_attr_network_coding, #endif + &batadv_attr_isolation_mark, NULL, }; diff --git a/net/batman-adv/sysfs.h b/net/batman-adv/sysfs.h index c7d725de50ad..b715b60db7cd 100644 --- a/net/batman-adv/sysfs.h +++ b/net/batman-adv/sysfs.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2010-2014 B.A.T.M.A.N. contributors: * * Marek Lindner * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_SYSFS_H_ diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index ff625fedbc5e..b6071f675a3e 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich, Antonio Quartulli * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" @@ -51,7 +49,7 @@ static int batadv_compare_tt(const struct hlist_node *node, const void *data2) const void *data1 = container_of(node, struct batadv_tt_common_entry, hash_entry); - return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); + return batadv_compare_eth(data1, data2); } /** @@ -476,11 +474,13 @@ static void batadv_tt_global_free(struct batadv_priv *bat_priv, * @vid: VLAN identifier * @ifindex: index of the interface where the client is connected to (useful to * identify wireless clients) + * @mark: the value contained in the skb->mark field of the received packet (if + * any) * * Returns true if the client was successfully added, false otherwise. */ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, - unsigned short vid, int ifindex) + unsigned short vid, int ifindex, uint32_t mark) { struct batadv_priv *bat_priv = netdev_priv(soft_iface); struct batadv_tt_local_entry *tt_local; @@ -491,6 +491,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, int hash_added, table_size, packet_size_max; bool ret = false, roamed_back = false; uint8_t remote_flags; + uint32_t match_mark; if (ifindex != BATADV_NULL_IFINDEX) in_dev = dev_get_by_index(&init_net, ifindex); @@ -615,6 +616,17 @@ check_roaming: else tt_local->common.flags &= ~BATADV_TT_CLIENT_WIFI; + /* check the mark in the skb: if it's equal to the configured + * isolation_mark, it means the packet is coming from an isolated + * non-mesh client + */ + match_mark = (mark & bat_priv->isolation_mark_mask); + if (bat_priv->isolation_mark_mask && + match_mark == bat_priv->isolation_mark) + tt_local->common.flags |= BATADV_TT_CLIENT_ISOLA; + else + tt_local->common.flags &= ~BATADV_TT_CLIENT_ISOLA; + /* if any "dynamic" flag has been modified, resend an ADD event for this * entry so that all the nodes can get the new flags */ @@ -875,7 +887,7 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) seq_printf(seq, "Locally retrieved addresses (from %s) announced via TT (TTVN: %u):\n", net_dev->name, (uint8_t)atomic_read(&bat_priv->tt.vn)); - seq_printf(seq, " %-13s %s %-7s %-9s (%-10s)\n", "Client", "VID", + seq_printf(seq, " %-13s %s %-8s %-9s (%-10s)\n", "Client", "VID", "Flags", "Last seen", "CRC"); for (i = 0; i < hash->size; i++) { @@ -903,7 +915,7 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) } seq_printf(seq, - " * %pM %4i [%c%c%c%c%c] %3u.%03u (%#.8x)\n", + " * %pM %4i [%c%c%c%c%c%c] %3u.%03u (%#.8x)\n", tt_common_entry->addr, BATADV_PRINT_VID(tt_common_entry->vid), (tt_common_entry->flags & @@ -915,6 +927,8 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) BATADV_TT_CLIENT_PENDING ? 'X' : '.'), (tt_common_entry->flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'), + (tt_common_entry->flags & + BATADV_TT_CLIENT_ISOLA ? 'I' : '.'), no_purge ? 0 : last_seen_secs, no_purge ? 0 : last_seen_msecs, vlan->tt.crc); @@ -1368,7 +1382,8 @@ out: return ret; } -/* batadv_transtable_best_orig - Get best originator list entry from tt entry +/** + * batadv_transtable_best_orig - Get best originator list entry from tt entry * @bat_priv: the bat priv with all the soft interface information * @tt_global_entry: global translation table entry to be analyzed * @@ -1386,12 +1401,14 @@ batadv_transtable_best_orig(struct batadv_priv *bat_priv, head = &tt_global_entry->orig_list; hlist_for_each_entry_rcu(orig_entry, head, list) { - router = batadv_orig_node_get_router(orig_entry->orig_node); + router = batadv_orig_router_get(orig_entry->orig_node, + BATADV_IF_DEFAULT); if (!router) continue; if (best_router && - bao->bat_neigh_cmp(router, best_router) <= 0) { + bao->bat_neigh_cmp(router, BATADV_IF_DEFAULT, + best_router, BATADV_IF_DEFAULT) <= 0) { batadv_neigh_node_free_ref(router); continue; } @@ -1410,8 +1427,9 @@ batadv_transtable_best_orig(struct batadv_priv *bat_priv, return best_entry; } -/* batadv_tt_global_print_entry - print all orig nodes who announce the address - * for this global entry +/** + * batadv_tt_global_print_entry - print all orig nodes who announce the address + * for this global entry * @bat_priv: the bat priv with all the soft interface information * @tt_global_entry: global translation table entry to be printed * @seq: debugfs table seq_file struct @@ -1447,13 +1465,14 @@ batadv_tt_global_print_entry(struct batadv_priv *bat_priv, last_ttvn = atomic_read(&best_entry->orig_node->last_ttvn); seq_printf(seq, - " %c %pM %4i (%3u) via %pM (%3u) (%#.8x) [%c%c%c]\n", + " %c %pM %4i (%3u) via %pM (%3u) (%#.8x) [%c%c%c%c]\n", '*', tt_global_entry->common.addr, BATADV_PRINT_VID(tt_global_entry->common.vid), best_entry->ttvn, best_entry->orig_node->orig, last_ttvn, vlan->tt.crc, (flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'), (flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'), + (flags & BATADV_TT_CLIENT_ISOLA ? 'I' : '.'), (flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.')); batadv_orig_node_vlan_free_ref(vlan); @@ -1478,13 +1497,14 @@ print_list: last_ttvn = atomic_read(&orig_entry->orig_node->last_ttvn); seq_printf(seq, - " %c %pM %4d (%3u) via %pM (%3u) (%#.8x) [%c%c%c]\n", + " %c %pM %4d (%3u) via %pM (%3u) (%#.8x) [%c%c%c%c]\n", '+', tt_global_entry->common.addr, BATADV_PRINT_VID(tt_global_entry->common.vid), orig_entry->ttvn, orig_entry->orig_node->orig, last_ttvn, vlan->tt.crc, (flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'), (flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'), + (flags & BATADV_TT_CLIENT_ISOLA ? 'I' : '.'), (flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.')); batadv_orig_node_vlan_free_ref(vlan); @@ -1853,6 +1873,11 @@ _batadv_is_ap_isolated(struct batadv_tt_local_entry *tt_local_entry, tt_global_entry->common.flags & BATADV_TT_CLIENT_WIFI) ret = true; + /* check if the two clients are marked as isolated */ + if (tt_local_entry->common.flags & BATADV_TT_CLIENT_ISOLA && + tt_global_entry->common.flags & BATADV_TT_CLIENT_ISOLA) + ret = true; + return ret; } @@ -1879,19 +1904,8 @@ struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv, struct batadv_tt_global_entry *tt_global_entry = NULL; struct batadv_orig_node *orig_node = NULL; struct batadv_tt_orig_list_entry *best_entry; - bool ap_isolation_enabled = false; - struct batadv_softif_vlan *vlan; - /* if the AP isolation is requested on a VLAN, then check for its - * setting in the proper VLAN private data structure - */ - vlan = batadv_softif_vlan_get(bat_priv, vid); - if (vlan) { - ap_isolation_enabled = atomic_read(&vlan->ap_isolation); - batadv_softif_vlan_free_ref(vlan); - } - - if (src && ap_isolation_enabled) { + if (src && batadv_vlan_ap_isola_get(bat_priv, vid)) { tt_local_entry = batadv_tt_local_hash_find(bat_priv, src, vid); if (!tt_local_entry || (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING)) @@ -3567,3 +3581,29 @@ int batadv_tt_init(struct batadv_priv *bat_priv) return 1; } + +/** + * batadv_tt_global_is_isolated - check if a client is marked as isolated + * @bat_priv: the bat priv with all the soft interface information + * @addr: the mac address of the client + * @vid: the identifier of the VLAN where this client is connected + * + * Returns true if the client is marked with the TT_CLIENT_ISOLA flag, false + * otherwise + */ +bool batadv_tt_global_is_isolated(struct batadv_priv *bat_priv, + const uint8_t *addr, unsigned short vid) +{ + struct batadv_tt_global_entry *tt; + bool ret; + + tt = batadv_tt_global_hash_find(bat_priv, addr, vid); + if (!tt) + return false; + + ret = tt->common.flags & BATADV_TT_CLIENT_ISOLA; + + batadv_tt_global_entry_free_ref(tt); + + return ret; +} diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h index 026b1ffa6746..20a1d7861ded 100644 --- a/net/batman-adv/translation-table.h +++ b/net/batman-adv/translation-table.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich, Antonio Quartulli * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ @@ -22,7 +20,7 @@ int batadv_tt_init(struct batadv_priv *bat_priv); bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, - unsigned short vid, int ifindex); + unsigned short vid, int ifindex, uint32_t mark); uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv, const uint8_t *addr, unsigned short vid, const char *message, bool roaming); @@ -50,5 +48,7 @@ bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, const unsigned char *addr, unsigned short vid); +bool batadv_tt_global_is_isolated(struct batadv_priv *bat_priv, + const uint8_t *addr, unsigned short vid); #endif /* _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ */ diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 91dd369b0ff2..78370ab31f9c 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * @@ -12,9 +12,7 @@ * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_TYPES_H_ @@ -36,6 +34,18 @@ #endif /* CONFIG_BATMAN_ADV_DAT */ /** + * enum batadv_dhcp_recipient - dhcp destination + * @BATADV_DHCP_NO: packet is not a dhcp message + * @BATADV_DHCP_TO_SERVER: dhcp message is directed to a server + * @BATADV_DHCP_TO_CLIENT: dhcp message is directed to a client + */ +enum batadv_dhcp_recipient { + BATADV_DHCP_NO = 0, + BATADV_DHCP_TO_SERVER, + BATADV_DHCP_TO_CLIENT, +}; + +/** * BATADV_TT_REMOTE_MASK - bitmask selecting the flags that are sent over the * wire only */ @@ -74,6 +84,7 @@ struct batadv_hard_iface_bat_iv { * @rcu: struct used for freeing in an RCU-safe manner * @bat_iv: BATMAN IV specific per hard interface data * @cleanup_work: work queue callback item for hard interface deinit + * @debug_dir: dentry for nc subdir in batman-adv directory in debugfs */ struct batadv_hard_iface { struct list_head list; @@ -88,6 +99,29 @@ struct batadv_hard_iface { struct rcu_head rcu; struct batadv_hard_iface_bat_iv bat_iv; struct work_struct cleanup_work; + struct dentry *debug_dir; +}; + +/** + * struct batadv_orig_ifinfo - originator info per outgoing interface + * @list: list node for orig_node::ifinfo_list + * @if_outgoing: pointer to outgoing hard interface + * @router: router that should be used to reach this originator + * @last_real_seqno: last and best known sequence number + * @last_ttl: ttl of last received packet + * @batman_seqno_reset: time when the batman seqno window was reset + * @refcount: number of contexts the object is used + * @rcu: struct used for freeing in an RCU-safe manner + */ +struct batadv_orig_ifinfo { + struct hlist_node list; + struct batadv_hard_iface *if_outgoing; + struct batadv_neigh_node __rcu *router; /* rcu protected pointer */ + uint32_t last_real_seqno; + uint8_t last_ttl; + unsigned long batman_seqno_reset; + atomic_t refcount; + struct rcu_head rcu; }; /** @@ -165,11 +199,11 @@ struct batadv_orig_bat_iv { * struct batadv_orig_node - structure for orig_list maintaining nodes of mesh * @orig: originator ethernet address * @primary_addr: hosts primary interface address - * @router: router that should be used to reach this originator + * @ifinfo_list: list for routers per outgoing interface + * @last_bonding_candidate: pointer to last ifinfo of last used router * @batadv_dat_addr_t: address of the orig node in the distributed hash * @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 * @capabilities: announced capabilities of this originator * @last_ttvn: last seen translation table version number * @tt_buff: last tt changeset this node received from the orig node @@ -182,19 +216,15 @@ struct batadv_orig_bat_iv { * made up by two operations (data structure update and metdata -CRC/TTVN- * recalculation) and they have to be executed atomically in order to avoid * another thread to read the table/metadata between those. - * @last_real_seqno: last and best known sequence number - * @last_ttl: ttl of last received packet * @bcast_bits: bitfield containing the info which payload broadcast originated * from this orig node this host already has seen (relative to * last_bcast_seqno) * @last_bcast_seqno: last broadcast sequence number received by this host * @neigh_list: list of potential next hop neighbor towards this orig node - * @neigh_list_lock: lock protecting neigh_list, router and bonding_list + * @neigh_list_lock: lock protecting neigh_list and router * @hash_entry: hlist node for batadv_priv::orig_hash * @bat_priv: pointer to soft_iface this orig node belongs to * @bcast_seqno_lock: lock protecting bcast_bits & last_bcast_seqno - * @bond_candidates: how many candidates are available - * @bond_list: list of bonding candidates * @refcount: number of contexts the object is used * @rcu: struct used for freeing in an RCU-safe manner * @in_coding_list: list of nodes this orig can hear @@ -210,13 +240,13 @@ struct batadv_orig_bat_iv { struct batadv_orig_node { uint8_t orig[ETH_ALEN]; uint8_t primary_addr[ETH_ALEN]; - struct batadv_neigh_node __rcu *router; /* rcu protected pointer */ + struct hlist_head ifinfo_list; + struct batadv_orig_ifinfo *last_bonding_candidate; #ifdef CONFIG_BATMAN_ADV_DAT batadv_dat_addr_t dat_addr; #endif unsigned long last_seen; unsigned long bcast_seqno_reset; - unsigned long batman_seqno_reset; uint8_t capabilities; atomic_t last_ttvn; unsigned char *tt_buff; @@ -225,19 +255,15 @@ struct batadv_orig_node { bool tt_initialised; /* prevents from changing the table while reading it */ spinlock_t tt_lock; - uint32_t last_real_seqno; - uint8_t last_ttl; DECLARE_BITMAP(bcast_bits, BATADV_TQ_LOCAL_WINDOW_SIZE); uint32_t last_bcast_seqno; struct hlist_head neigh_list; - /* neigh_list_lock protects: neigh_list, router & bonding_list */ + /* neigh_list_lock protects: neigh_list and router */ spinlock_t neigh_list_lock; struct hlist_node hash_entry; struct batadv_priv *bat_priv; /* bcast_seqno_lock protects: bcast_bits & last_bcast_seqno */ spinlock_t bcast_seqno_lock; - atomic_t bond_candidates; - struct list_head bond_list; atomic_t refcount; struct rcu_head rcu; #ifdef CONFIG_BATMAN_ADV_NC @@ -283,49 +309,64 @@ struct batadv_gw_node { }; /** - * struct batadv_neigh_bat_iv - B.A.T.M.A.N. IV specific structure for single - * hop neighbors + * struct batadv_neigh_node - structure for single hops neighbors + * @list: list node for batadv_orig_node::neigh_list + * @orig_node: pointer to corresponding orig_node + * @addr: the MAC address of the neighboring interface + * @ifinfo_list: list for routing metrics per outgoing interface + * @ifinfo_lock: lock protecting private ifinfo members and list + * @if_incoming: pointer to incoming hard interface + * @last_seen: when last packet via this neighbor was received + * @last_ttl: last received ttl from this neigh node + * @rcu: struct used for freeing in an RCU-safe manner + * @bat_iv: B.A.T.M.A.N. IV private structure + */ +struct batadv_neigh_node { + struct hlist_node list; + struct batadv_orig_node *orig_node; + uint8_t addr[ETH_ALEN]; + struct hlist_head ifinfo_list; + spinlock_t ifinfo_lock; /* protects ifinfo_list and its members */ + struct batadv_hard_iface *if_incoming; + unsigned long last_seen; + atomic_t refcount; + struct rcu_head rcu; +}; + +/** + * struct batadv_neigh_node_bat_iv - neighbor information per outgoing + * interface for BATMAN IV * @tq_recv: ring buffer of received TQ values from this neigh node * @tq_index: ring buffer index * @tq_avg: averaged tq of all tq values in the ring buffer (tq_recv) * @real_bits: bitfield containing the number of OGMs received from this neigh * node (relative to orig_node->last_real_seqno) * @real_packet_count: counted result of real_bits - * @lq_update_lock: lock protecting tq_recv & tq_index */ -struct batadv_neigh_bat_iv { +struct batadv_neigh_ifinfo_bat_iv { uint8_t tq_recv[BATADV_TQ_GLOBAL_WINDOW_SIZE]; uint8_t tq_index; uint8_t tq_avg; DECLARE_BITMAP(real_bits, BATADV_TQ_LOCAL_WINDOW_SIZE); uint8_t real_packet_count; - spinlock_t lq_update_lock; /* protects tq_recv & tq_index */ }; /** - * struct batadv_neigh_node - structure for single hops neighbors - * @list: list node for batadv_orig_node::neigh_list - * @orig_node: pointer to corresponding orig_node - * @addr: the MAC address of the neighboring interface - * @if_incoming: pointer to incoming hard interface - * @last_seen: when last packet via this neighbor was received + * struct batadv_neigh_ifinfo - neighbor information per outgoing interface + * @list: list node for batadv_neigh_node::ifinfo_list + * @if_outgoing: pointer to outgoing hard interface + * @bat_iv: B.A.T.M.A.N. IV private structure * @last_ttl: last received ttl from this neigh node - * @bonding_list: list node for batadv_orig_node::bond_list * @refcount: number of contexts the object is used - * @rcu: struct used for freeing in an RCU-safe manner - * @bat_iv: B.A.T.M.A.N. IV private structure + * @rcu: struct used for freeing in a RCU-safe manner */ -struct batadv_neigh_node { +struct batadv_neigh_ifinfo { struct hlist_node list; - struct batadv_orig_node *orig_node; - uint8_t addr[ETH_ALEN]; - struct batadv_hard_iface *if_incoming; - unsigned long last_seen; + struct batadv_hard_iface *if_outgoing; + struct batadv_neigh_ifinfo_bat_iv bat_iv; uint8_t last_ttl; - struct list_head bonding_list; atomic_t refcount; struct rcu_head rcu; - struct batadv_neigh_bat_iv bat_iv; }; /** @@ -687,6 +728,8 @@ struct batadv_priv { #ifdef CONFIG_BATMAN_ADV_DEBUG atomic_t log_level; #endif + uint32_t isolation_mark; + uint32_t isolation_mark_mask; atomic_t bcast_seqno; atomic_t bcast_queue_left; atomic_t batman_queue_left; @@ -981,8 +1024,10 @@ struct batadv_skb_cb { * @direct_link_flags: direct link flags for aggregated OGM packets * @num_packets: counter for bcast packet retransmission * @delayed_work: work queue callback item for packet sending - * @if_incoming: pointer incoming hard-iface or primary iface if locally - * generated packet + * @if_incoming: pointer to incoming hard-iface or primary iface if + * locally generated packet + * @if_outgoing: packet where the packet should be sent to, or NULL if + * unspecified */ struct batadv_forw_packet { struct hlist_node list; @@ -994,6 +1039,7 @@ struct batadv_forw_packet { uint8_t num_packets; struct delayed_work delayed_work; struct batadv_hard_iface *if_incoming; + struct batadv_hard_iface *if_outgoing; }; /** @@ -1007,9 +1053,11 @@ struct batadv_forw_packet { * @bat_primary_iface_set: called when primary interface is selected / changed * @bat_ogm_schedule: prepare a new outgoing OGM for the send queue * @bat_ogm_emit: send scheduled OGM - * @bat_neigh_cmp: compare the metrics of two neighbors - * @bat_neigh_is_equiv_or_better: check if neigh1 is equally good or - * better than neigh2 from the metric prospective + * @bat_neigh_cmp: compare the metrics of two neighbors for their respective + * outgoing interfaces + * @bat_neigh_is_equiv_or_better: check if neigh1 is equally good or better + * than neigh2 for their respective outgoing interface from the metric + * prospective * @bat_orig_print: print the originator table (optional) * @bat_orig_free: free the resources allocated by the routing algorithm for an * orig_node object @@ -1028,11 +1076,17 @@ struct batadv_algo_ops { void (*bat_ogm_schedule)(struct batadv_hard_iface *hard_iface); void (*bat_ogm_emit)(struct batadv_forw_packet *forw_packet); int (*bat_neigh_cmp)(struct batadv_neigh_node *neigh1, - struct batadv_neigh_node *neigh2); - bool (*bat_neigh_is_equiv_or_better)(struct batadv_neigh_node *neigh1, - struct batadv_neigh_node *neigh2); + struct batadv_hard_iface *if_outgoing1, + struct batadv_neigh_node *neigh2, + struct batadv_hard_iface *if_outgoing2); + bool (*bat_neigh_is_equiv_or_better) + (struct batadv_neigh_node *neigh1, + struct batadv_hard_iface *if_outgoing1, + struct batadv_neigh_node *neigh2, + struct batadv_hard_iface *if_outgoing2); /* orig_node handling API */ - void (*bat_orig_print)(struct batadv_priv *priv, struct seq_file *seq); + void (*bat_orig_print)(struct batadv_priv *priv, struct seq_file *seq, + struct batadv_hard_iface *hard_iface); void (*bat_orig_free)(struct batadv_orig_node *orig_node); int (*bat_orig_add_if)(struct batadv_orig_node *orig_node, int max_if_num); |