diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 89b499f7a37d..85e99506a53f 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1410,7 +1410,7 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, return BATADV_NO_DUP; orig_ifinfo = batadv_orig_ifinfo_new(orig_node, if_outgoing); - if (WARN_ON(!orig_ifinfo)) { + if (!orig_ifinfo) { batadv_orig_node_put(orig_node); return 0; } @@ -1909,6 +1909,11 @@ static void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work) goto out; } + /* Mark aggregate as full before forcing emit. */ + spin_lock_bh(&bat_priv->forw_bat_list_lock); + forw_packet->num_packets = BATADV_MAX_AGGREGATION_PACKETS; + spin_unlock_bh(&bat_priv->forw_bat_list_lock); + batadv_iv_ogm_emit(forw_packet); /* we have to have at least one packet in the queue to determine the diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 74050bffedbc..0260ed7e828b 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -653,6 +653,7 @@ static void batadv_choose_next_candidate(struct batadv_priv *bat_priv, * NOT_FOUND */ cands[select].type = BATADV_DAT_CANDIDATE_NOT_FOUND; + cands[select].orig_node = NULL; /* iterate over the originator list and find the node with the closest * dat_address which has not been selected yet diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 6c54a8d4be3e..3b9fa861be7b 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -295,6 +295,11 @@ void batadv_mesh_free(struct net_device *mesh_iface) WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); + /* the originator purge worker accesses (among others) the TT tables + * and must therefore be stopped before any of them are freed + */ + disable_delayed_work_sync(&bat_priv->orig_work); + batadv_purge_outstanding_packets(bat_priv, NULL); batadv_tp_stop_all(bat_priv); @@ -484,8 +489,10 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev, * shut down otherwise the packet may trigger de-reference errors * further down in the receive path. */ - if (!kref_get_unless_zero(&hard_iface->refcount)) - goto err_out; + if (!kref_get_unless_zero(&hard_iface->refcount)) { + kfree_skb(skb); + return NET_RX_DROP; + } skb = skb_share_check(skb, GFP_ATOMIC); @@ -541,7 +548,6 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev, kfree_skb(skb); err_put: batadv_hardif_put(hard_iface); -err_out: return NET_RX_DROP; } diff --git a/net/batman-adv/multicast_forw.c b/net/batman-adv/multicast_forw.c index ac97cd8b81dc..9ffed1f237b6 100644 --- a/net/batman-adv/multicast_forw.c +++ b/net/batman-adv/multicast_forw.c @@ -950,8 +950,11 @@ static int batadv_mcast_forw_packet(struct batadv_priv *bat_priv, dest = (u8 *)mcast_tracker + sizeof(*mcast_tracker); - /* check if full tracker tvlv is within skb length */ + /* check if full tracker tvlv, including padding, is within skb length */ tvlv_len = sizeof(*mcast_tracker) + ETH_ALEN * num_dests; + if (!(num_dests % 2)) + tvlv_len += 2; + if (tvlv_len > skb_network_header_len(skb)) return -EINVAL; diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 022eb13e8ec4..56feb02d2b7e 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -221,7 +221,7 @@ static void batadv_tp_batctl_notify(enum batadv_tp_meter_reason reason, unsigned long start_time, u64 total_sent, u32 cookie) { - u32 total_bytes; + u64 total_bytes; u32 test_time; u8 result; diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 3349376a9087..46304069b3b7 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -26,8 +26,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -253,7 +255,8 @@ static int batadv_tt_len(int changes_num) * * Return: local translation table size in bytes. */ -static int batadv_tt_local_transmit_size(u16 num_vlan, u16 num_entries) +static int batadv_tt_local_transmit_size(unsigned int num_vlan, + unsigned int num_entries) { int hdr_size; @@ -266,6 +269,25 @@ static int batadv_tt_local_transmit_size(u16 num_vlan, u16 num_entries) return hdr_size + batadv_tt_len(num_entries); } +/** + * batadv_tt_local_transmit_size_max() - get the maximum size of a full local table + * response + * @bat_priv: the bat priv with all the mesh interface information + * + * The full table response is sent as a single unicast TVLV packet. Its + * tvlv_len, which covers the TVLV header and the TT TVLV, is only 16 bit wide. + * This limits the response size even when packet_size_max is larger. + * + * Return: maximum size of a full local table response in bytes + */ +static int batadv_tt_local_transmit_size_max(struct batadv_priv *bat_priv) +{ + int tvlv_size_max = sizeof(struct batadv_unicast_tvlv_packet) + U16_MAX; + int packet_size_max = READ_ONCE(bat_priv->packet_size_max); + + return min(packet_size_max, tvlv_size_max); +} + /** * batadv_tt_local_reserve() - reserve room in the transmittable local table * @bat_priv: the bat priv with all the mesh interface information @@ -290,22 +312,23 @@ static int batadv_tt_local_transmit_size(u16 num_vlan, u16 num_entries) static bool batadv_tt_local_reserve(struct batadv_priv *bat_priv, u16 num_vlan, u16 num_entries, int *table_size) { - int packet_size_max = READ_ONCE(bat_priv->packet_size_max); + int size_max = batadv_tt_local_transmit_size_max(bat_priv); + unsigned int entries; + unsigned int vlans; scoped_guard(spinlock_bh, &bat_priv->tt.reserve_lock) { - bat_priv->tt.reserved_vlans += num_vlan; - bat_priv->tt.reserved_entries += num_entries; + vlans = bat_priv->tt.reserved_vlans + num_vlan; + entries = bat_priv->tt.reserved_entries + num_entries; - *table_size = batadv_tt_local_transmit_size(bat_priv->tt.reserved_vlans, - bat_priv->tt.reserved_entries); - if (*table_size <= packet_size_max) - return true; + *table_size = batadv_tt_local_transmit_size(vlans, entries); + if (*table_size > size_max) + return false; - bat_priv->tt.reserved_vlans -= num_vlan; - bat_priv->tt.reserved_entries -= num_entries; + bat_priv->tt.reserved_vlans = vlans; + bat_priv->tt.reserved_entries = entries; } - return false; + return true; } /** @@ -339,9 +362,10 @@ static bool batadv_tt_local_reserve_entry(struct batadv_priv *bat_priv, return true; net_ratelimited_function(batadv_info, bat_priv->mesh_iface, - "Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n", + "Local translation table size (%i) exceeds maximum table response size (%i); Ignoring new local tt entry: %pM\n", table_size, - READ_ONCE(bat_priv->packet_size_max), addr); + batadv_tt_local_transmit_size_max(bat_priv), + addr); return false; } @@ -374,8 +398,9 @@ bool batadv_tt_local_reserve_vlan(struct batadv_priv *bat_priv, return true; net_ratelimited_function(batadv_info, bat_priv->mesh_iface, - "Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new VLAN: %d\n", - table_size, READ_ONCE(bat_priv->packet_size_max), + "Local translation table size (%i) exceeds maximum table response size (%i); Ignoring new VLAN: %d\n", + table_size, + batadv_tt_local_transmit_size_max(bat_priv), batadv_print_vid(vid)); return false; @@ -622,6 +647,9 @@ static u16 batadv_tt_flags_get(struct batadv_tt_common_entry *common) * @bat_priv: the bat priv with all the mesh interface information * @common: the TT entry involved in the event * @flags: flags of the TT entry combined with the event flags + * + * Only a single change per client is queued. It describes the latest state of + * the client and a new event therefore replaces an already queued one. */ static void __batadv_tt_local_event(struct batadv_priv *bat_priv, const struct batadv_tt_common_entry *common, @@ -631,7 +659,6 @@ static void __batadv_tt_local_event(struct batadv_priv *bat_priv, struct batadv_tt_change_node *entry; struct batadv_tt_change_node *safe; bool del_op_requested; - bool del_op_entry; size_t changes; tt_change_node = kmem_cache_alloc(batadv_tt_change_cache, GFP_ATOMIC); @@ -643,9 +670,17 @@ static void __batadv_tt_local_event(struct batadv_priv *bat_priv, ether_addr_copy(tt_change_node->change.addr, common->addr); tt_change_node->change.vid = htons(common->vid); - tt_change_node->change.flags = flags; del_op_requested = flags & BATADV_TT_CLIENT_DEL; + /* the ROAM flag is only meaningful for the receiver in combination + * with a DEL. An ADD with ROAM would be interpreted as a roaming + * advertisement + */ + if (!del_op_requested) + flags &= ~BATADV_TT_CLIENT_ROAM; + + tt_change_node->change.flags = flags; + /* check for ADD+DEL, DEL+ADD, ADD+ADD or DEL+DEL events */ spin_lock_bh(&bat_priv->tt.changes_list_lock); changes = READ_ONCE(bat_priv->tt.local_changes); @@ -657,25 +692,12 @@ static void __batadv_tt_local_event(struct batadv_priv *bat_priv, if (entry->change.vid != tt_change_node->change.vid) continue; - del_op_entry = entry->change.flags & BATADV_TT_CLIENT_DEL; - if (del_op_requested != del_op_entry) { - /* DEL+ADD in the same orig interval have no effect and - * can be removed to avoid silly behaviour on the - * receiver side. The other way around (ADD+DEL) can - * happen in case of roaming of a client still in the - * NEW state. Roaming of NEW clients is now possible due - * to automatically recognition of "temporary" clients - */ - list_del(&entry->list); - kmem_cache_free(batadv_tt_change_cache, entry); - changes--; - } else { - /* this is a second add or del in the same originator - * interval. It could mean that flags have been changed - * (e.g. double add): update them - */ - entry->change.flags = flags; - } + /* the other nodes may know the client with different + * flags. A DEL+ADD must therefore still announce the + * current flags and an ADD+DEL must still remove the + * client. ADD+ADD and DEL+DEL only update the flags + */ + entry->change.flags = flags; kmem_cache_free(batadv_tt_change_cache, tt_change_node); goto update_changes; @@ -727,9 +749,9 @@ static u16 batadv_tt_entries(u16 tt_len) */ static int batadv_tt_local_table_transmit_size(struct batadv_priv *bat_priv) { + unsigned int tt_local_entries = 0; struct batadv_meshif_vlan *vlan; - u16 tt_local_entries = 0; - u16 num_vlan = 0; + unsigned int num_vlan = 0; rcu_read_lock(); hlist_for_each_entry_rcu(vlan, &bat_priv->meshif_vlan_list, list) { @@ -811,14 +833,21 @@ static void batadv_tt_local_add_roam(struct batadv_priv *bat_priv, if (!tt_global) return; - /* Check whether it is a roaming, but don't do anything if the roaming - * process has already been handled + /* Handle newly roamed client - but not roamed back client. + * + * A client which roamed back must always be reclaimed from the + * originator(s) which sent the ROAMING_ADV. The global entry created by + * this ROAMING_ADV might still carry BATADV_TT_CLIENT_ROAM because the + * new originator did not yet announce the client. */ - scoped_guard(spinlock_bh, &tt_global->common.flags_lock) { - if (tt_global->common.flags & BATADV_TT_CLIENT_ROAM) - return; + if (!roamed_back) { + scoped_guard(spinlock_bh, &tt_global->common.flags_lock) { + /* Check whether it is a roaming, but don't do anything + * if the roaming process has already been handled. + */ + if (tt_global->common.flags & BATADV_TT_CLIENT_ROAM) + return; - if (!roamed_back) { /* The global entry has to be marked as ROAMING and has to be * kept for consistency purpose. * @@ -886,6 +915,7 @@ static bool batadv_tt_local_add_existing(struct batadv_priv *bat_priv, bool *roamed_back) { struct batadv_tt_common_entry *common = &tt_local->common; + bool readded = false; tt_local->last_seen = jiffies; @@ -900,8 +930,7 @@ static bool batadv_tt_local_add_existing(struct batadv_priv *bat_priv, * flag can be reset like it was never enqueued */ common->flags &= ~BATADV_TT_CLIENT_PENDING; - - return true; + readded = true; } if (common->flags & BATADV_TT_CLIENT_ROAM) { @@ -918,7 +947,7 @@ static bool batadv_tt_local_add_existing(struct batadv_priv *bat_priv, } } - return false; + return readded; } /** @@ -1319,22 +1348,24 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, * batadv_tt_tvlv_container_update() - update the translation table tvlv * container after local tt changes have been committed * @bat_priv: the bat priv with all the mesh interface information + * @changes_list: list of committed changes which will be consumed + * @tt_diff_entries_num: number of entries in @changes_list */ -static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv) +static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv, + struct list_head *changes_list, + int tt_diff_entries_num) { struct batadv_tvlv_tt_change *tt_change; struct batadv_tt_change_node *entry; struct batadv_tvlv_tt_data *tt_data; struct batadv_tt_change_node *safe; int tt_diff_entries_count = 0; - int tt_diff_entries_num = 0; bool drop_changes = false; size_t tt_extra_len = 0; int tt_change_len = 0; int tt_diff_len; u16 tvlv_len; - tt_diff_entries_num = READ_ONCE(bat_priv->tt.local_changes); tt_diff_len = batadv_tt_len(tt_diff_entries_num); /* if we have too many changes for one packet don't send any @@ -1352,19 +1383,39 @@ static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv) tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv, &tt_data, &tt_change, &tt_diff_len); - if (!tvlv_len) + if (!tvlv_len) { + /* the changes are lost and the other nodes have to request the + * full table. Don't answer TT requests for the new TTVN with + * the stale changeset of the previous TTVN. Without changes, the + * TTVN was not incremented and the last changeset is still valid + */ + if (drop_changes || tt_diff_entries_num > 0) { + list_for_each_entry_safe(entry, safe, changes_list, + list) { + list_del(&entry->list); + kmem_cache_free(batadv_tt_change_cache, entry); + } + + spin_lock_bh(&bat_priv->tt.last_changeset_lock); + kfree(bat_priv->tt.last_changeset); + bat_priv->tt.last_changeset = NULL; + bat_priv->tt.last_changeset_len = 0; + spin_unlock_bh(&bat_priv->tt.last_changeset_lock); + } + + /* announce the already committed TTVN in the next OGM interval + * instead of waiting for BATADV_TT_OGM_APPEND_MAX idle rounds + */ + atomic_set(&bat_priv->tt.ogm_append_cnt, 0); return; + } tt_data->flags = BATADV_TT_OGM_DIFF; if (!drop_changes && tt_diff_len == 0) goto container_register; - spin_lock_bh(&bat_priv->tt.changes_list_lock); - WRITE_ONCE(bat_priv->tt.local_changes, 0); - - list_for_each_entry_safe(entry, safe, &bat_priv->tt.changes_list, - list) { + list_for_each_entry_safe(entry, safe, changes_list, list) { if (tt_diff_entries_count < tt_diff_entries_num) { memcpy(tt_change + tt_diff_entries_count, &entry->change, @@ -1374,7 +1425,6 @@ static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv) list_del(&entry->list); kmem_cache_free(batadv_tt_change_cache, entry); } - spin_unlock_bh(&bat_priv->tt.changes_list_lock); tt_extra_len = batadv_tt_len(tt_diff_entries_num - tt_diff_entries_count); @@ -3342,8 +3392,12 @@ static bool batadv_tt_local_valid(void *entry_ptr, if (tt_flags & BATADV_TT_CLIENT_NEW) return false; + /* the entry is (still) announced. A ROAM flag of an entry which is + * pending to be removed must not be transmitted. Otherwise, the + * receiver would handle it like a roaming advertisement + */ if (flags) - *flags = tt_flags; + *flags = tt_flags & ~BATADV_TT_CLIENT_ROAM; return true; } @@ -3922,21 +3976,30 @@ static void _batadv_tt_update_changes(struct batadv_priv *bat_priv, u16 tt_num_changes, u8 ttvn) { int roams; + u8 flags; int i; for (i = 0; i < tt_num_changes; i++) { - if ((tt_change + i)->flags & BATADV_TT_CLIENT_DEL) { - roams = (tt_change + i)->flags & BATADV_TT_CLIENT_ROAM; + flags = (tt_change + i)->flags; + + if (flags & BATADV_TT_CLIENT_DEL) { + roams = flags & BATADV_TT_CLIENT_ROAM; batadv_tt_global_del(bat_priv, orig_node, (tt_change + i)->addr, ntohs((tt_change + i)->vid), "tt removed by changes", roams); } else { + /* an announced client cannot have roamed away from + * orig_node. Only a ROAMING_ADV is allowed to add a + * global entry with the ROAM flag + */ + flags &= ~BATADV_TT_CLIENT_ROAM; + if (!batadv_tt_global_add(bat_priv, orig_node, (tt_change + i)->addr, ntohs((tt_change + i)->vid), - (tt_change + i)->flags, ttvn)) + flags, ttvn)) /* In case of problem while storing a * global_entry, we stop the updating * procedure without committing the @@ -4412,11 +4475,25 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv) */ static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv) { + LIST_HEAD(changes_list); + int changes_num; + lockdep_assert_held(&bat_priv->tt.commit_lock); - if (READ_ONCE(bat_priv->tt.local_changes) == 0) { + /* atomically get the list of changes. The TTVN decision and the + * announced changeset must cover the same events. Events queued + * afterwards stay in the tt.changes_list for the next TT round + */ + spin_lock_bh(&bat_priv->tt.changes_list_lock); + list_splice_init(&bat_priv->tt.changes_list, &changes_list); + changes_num = READ_ONCE(bat_priv->tt.local_changes); + WRITE_ONCE(bat_priv->tt.local_changes, 0); + spin_unlock_bh(&bat_priv->tt.changes_list_lock); + + if (changes_num == 0) { if (!batadv_atomic_dec_not_zero(&bat_priv->tt.ogm_append_cnt)) - batadv_tt_tvlv_container_update(bat_priv); + batadv_tt_tvlv_container_update(bat_priv, &changes_list, + changes_num); return; } @@ -4433,7 +4510,7 @@ static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv) /* reset the sending counter */ atomic_set(&bat_priv->tt.ogm_append_cnt, BATADV_TT_OGM_APPEND_MAX); - batadv_tt_tvlv_container_update(bat_priv); + batadv_tt_tvlv_container_update(bat_priv, &changes_list, changes_num); } /** @@ -4675,16 +4752,16 @@ void batadv_tt_local_resize_to_mtu(struct net_device *mesh_iface) struct batadv_priv *bat_priv = netdev_priv(mesh_iface); int timeout = BATADV_TT_LOCAL_TIMEOUT / 2; bool reduced = false; - int packet_size_max; int table_size; + int size_max; - packet_size_max = READ_ONCE(bat_priv->packet_size_max); + size_max = batadv_tt_local_transmit_size_max(bat_priv); spin_lock_bh(&bat_priv->tt.commit_lock); while (timeout) { table_size = batadv_tt_local_table_transmit_size(bat_priv); - if (packet_size_max >= table_size) + if (size_max >= table_size) break; batadv_tt_local_purge(bat_priv, timeout); @@ -4693,8 +4770,8 @@ void batadv_tt_local_resize_to_mtu(struct net_device *mesh_iface) timeout /= 2; reduced = true; net_ratelimited_function(batadv_info, mesh_iface, - "Forced to purge local tt entries to fit new maximum fragment MTU (%i)\n", - packet_size_max); + "Forced to purge local tt entries to fit new maximum table response size (%i)\n", + size_max); } /* commit these changes immediately, to avoid synchronization problem diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index c19caa84e692..b3e6dbb360da 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -2047,7 +2047,13 @@ struct batadv_forw_packet { /** @direct_link_flags: direct link flags for aggregated OGM packets */ DECLARE_BITMAP(direct_link_flags, BATADV_MAX_AGGREGATION_PACKETS); - /** @num_packets: counter for aggregated OGMv1 packets */ + /** + * @num_packets: counter for aggregated OGMv1 packets. Set to + * BATADV_MAX_AGGREGATION_PACKETS (independent of the real number of + * aggregated packets) by the send worker to prevent further + * aggregation while the packet is emitted. It must therefore not be + * used to iterate over the aggregated packets + */ u8 num_packets; /** @delayed_work: work queue callback item for packet sending */