Commit a19d3d85e1b854e4a483a55d740a42458085560d

Authored by Marek Lindner
Committed by Antonio Quartulli
1 parent 4627456a77

batman-adv: limit local translation table max size

The local translation table size is limited by what can be
transferred from one node to another via a full table request.

The number of entries fitting into a full table request depend
on whether the fragmentation is enabled or not. Therefore this
patch introduces a max table size check and refuses to add
more local clients when that size is reached. Moreover, if the
max full table packet size changes (MTU change or fragmentation
is disabled) the local table is downsized instantaneously.

Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
Acked-by: Antonio Quartulli <ordex@autistici.org>

Showing 5 changed files with 173 additions and 36 deletions Side-by-side Diff

net/batman-adv/hard-interface.c
... ... @@ -266,17 +266,10 @@
266 266  
267 267 int batadv_hardif_min_mtu(struct net_device *soft_iface)
268 268 {
269   - const struct batadv_priv *bat_priv = netdev_priv(soft_iface);
  269 + struct batadv_priv *bat_priv = netdev_priv(soft_iface);
270 270 const struct batadv_hard_iface *hard_iface;
271   - /* allow big frames if all devices are capable to do so
272   - * (have MTU > 1500 + batadv_max_header_len())
273   - */
274 271 int min_mtu = ETH_DATA_LEN;
275   - int max_header_len = batadv_max_header_len();
276 272  
277   - if (atomic_read(&bat_priv->fragmentation))
278   - goto out;
279   -
280 273 rcu_read_lock();
281 274 list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
282 275 if ((hard_iface->if_status != BATADV_IF_ACTIVE) &&
283 276  
284 277  
285 278  
286 279  
... ... @@ -286,22 +279,40 @@
286 279 if (hard_iface->soft_iface != soft_iface)
287 280 continue;
288 281  
289   - min_mtu = min_t(int, hard_iface->net_dev->mtu - max_header_len,
290   - min_mtu);
  282 + min_mtu = min_t(int, hard_iface->net_dev->mtu, min_mtu);
291 283 }
292 284 rcu_read_unlock();
  285 +
  286 + atomic_set(&bat_priv->packet_size_max, min_mtu);
  287 +
  288 + if (atomic_read(&bat_priv->fragmentation) == 0)
  289 + goto out;
  290 +
  291 + /* with fragmentation enabled the maximum size of internally generated
  292 + * packets such as translation table exchanges or tvlv containers, etc
  293 + * has to be calculated
  294 + */
  295 + min_mtu = min_t(int, min_mtu, BATADV_FRAG_MAX_FRAG_SIZE);
  296 + min_mtu -= sizeof(struct batadv_frag_packet);
  297 + min_mtu *= BATADV_FRAG_MAX_FRAGMENTS;
  298 + atomic_set(&bat_priv->packet_size_max, min_mtu);
  299 +
  300 + /* with fragmentation enabled we can fragment external packets easily */
  301 + min_mtu = min_t(int, min_mtu, ETH_DATA_LEN);
  302 +
293 303 out:
294   - return min_mtu;
  304 + return min_mtu - batadv_max_header_len();
295 305 }
296 306  
297 307 /* adjusts the MTU if a new interface with a smaller MTU appeared. */
298 308 void batadv_update_min_mtu(struct net_device *soft_iface)
299 309 {
300   - int min_mtu;
  310 + soft_iface->mtu = batadv_hardif_min_mtu(soft_iface);
301 311  
302   - min_mtu = batadv_hardif_min_mtu(soft_iface);
303   - if (soft_iface->mtu != min_mtu)
304   - soft_iface->mtu = min_mtu;
  312 + /* Check if the local translate table should be cleaned up to match a
  313 + * new (and smaller) MTU.
  314 + */
  315 + batadv_tt_local_resize_to_mtu(soft_iface);
305 316 }
306 317  
307 318 static void
net/batman-adv/soft-interface.c
... ... @@ -166,7 +166,7 @@
166 166 unsigned int header_len = 0;
167 167 int data_len = skb->len, ret;
168 168 unsigned long brd_delay = 1;
169   - bool do_bcast = false;
  169 + bool do_bcast = false, client_added;
170 170 unsigned short vid;
171 171 uint32_t seqno;
172 172  
... ... @@ -196,9 +196,12 @@
196 196 ethhdr = (struct ethhdr *)skb->data;
197 197  
198 198 /* Register the client MAC in the transtable */
199   - if (!is_multicast_ether_addr(ethhdr->h_source))
200   - batadv_tt_local_add(soft_iface, ethhdr->h_source, vid,
201   - skb->skb_iif);
  199 + if (!is_multicast_ether_addr(ethhdr->h_source)) {
  200 + client_added = batadv_tt_local_add(soft_iface, ethhdr->h_source,
  201 + vid, skb->skb_iif);
  202 + if (!client_added)
  203 + goto dropped;
  204 + }
202 205  
203 206 /* don't accept stp packets. STP does not help in meshes.
204 207 * better use the bridge loop avoidance ...
... ... @@ -674,6 +677,7 @@
674 677 atomic_set(&bat_priv->log_level, 0);
675 678 #endif
676 679 atomic_set(&bat_priv->fragmentation, 1);
  680 + atomic_set(&bat_priv->packet_size_max, ETH_DATA_LEN);
677 681 atomic_set(&bat_priv->bcast_queue_left, BATADV_BCAST_QUEUE_LEN);
678 682 atomic_set(&bat_priv->batman_queue_left, BATADV_BATMAN_QUEUE_LEN);
679 683  
net/batman-adv/translation-table.c
... ... @@ -401,6 +401,35 @@
401 401 return tt_len / batadv_tt_len(1);
402 402 }
403 403  
  404 +/**
  405 + * batadv_tt_local_table_transmit_size - calculates the local translation table
  406 + * size when transmitted over the air
  407 + * @bat_priv: the bat priv with all the soft interface information
  408 + *
  409 + * Returns local translation table size in bytes.
  410 + */
  411 +static int batadv_tt_local_table_transmit_size(struct batadv_priv *bat_priv)
  412 +{
  413 + uint16_t num_vlan = 0, tt_local_entries = 0;
  414 + struct batadv_softif_vlan *vlan;
  415 + int hdr_size;
  416 +
  417 + rcu_read_lock();
  418 + hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
  419 + num_vlan++;
  420 + tt_local_entries += atomic_read(&vlan->tt.num_entries);
  421 + }
  422 + rcu_read_unlock();
  423 +
  424 + /* header size of tvlv encapsulated tt response payload */
  425 + hdr_size = sizeof(struct batadv_unicast_tvlv_packet);
  426 + hdr_size += sizeof(struct batadv_tvlv_hdr);
  427 + hdr_size += sizeof(struct batadv_tvlv_tt_data);
  428 + hdr_size += num_vlan * sizeof(struct batadv_tvlv_tt_vlan_data);
  429 +
  430 + return hdr_size + batadv_tt_len(tt_local_entries);
  431 +}
  432 +
404 433 static int batadv_tt_local_init(struct batadv_priv *bat_priv)
405 434 {
406 435 if (bat_priv->tt.local_hash)
407 436  
... ... @@ -439,8 +468,10 @@
439 468 * @vid: VLAN identifier
440 469 * @ifindex: index of the interface where the client is connected to (useful to
441 470 * identify wireless clients)
  471 + *
  472 + * Returns true if the client was successfully added, false otherwise.
442 473 */
443   -void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
  474 +bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
444 475 unsigned short vid, int ifindex)
445 476 {
446 477 struct batadv_priv *bat_priv = netdev_priv(soft_iface);
... ... @@ -448,8 +479,8 @@
448 479 struct batadv_tt_global_entry *tt_global;
449 480 struct hlist_head *head;
450 481 struct batadv_tt_orig_list_entry *orig_entry;
451   - int hash_added;
452   - bool roamed_back = false;
  482 + int hash_added, table_size, packet_size_max;
  483 + bool ret = false, roamed_back = false;
453 484  
454 485 tt_local = batadv_tt_local_hash_find(bat_priv, addr, vid);
455 486 tt_global = batadv_tt_global_hash_find(bat_priv, addr, vid);
... ... @@ -484,6 +515,17 @@
484 515 goto check_roaming;
485 516 }
486 517  
  518 + /* Ignore the client if we cannot send it in a full table response. */
  519 + table_size = batadv_tt_local_table_transmit_size(bat_priv);
  520 + table_size += batadv_tt_len(1);
  521 + packet_size_max = atomic_read(&bat_priv->packet_size_max);
  522 + if (table_size > packet_size_max) {
  523 + net_ratelimited_function(batadv_info, soft_iface,
  524 + "Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n",
  525 + table_size, packet_size_max, addr);
  526 + goto out;
  527 + }
  528 +
487 529 tt_local = kmalloc(sizeof(*tt_local), GFP_ATOMIC);
488 530 if (!tt_local)
489 531 goto out;
490 532  
... ... @@ -550,11 +592,14 @@
550 592 }
551 593 }
552 594  
  595 + ret = true;
  596 +
553 597 out:
554 598 if (tt_local)
555 599 batadv_tt_local_entry_free_ref(tt_local);
556 600 if (tt_global)
557 601 batadv_tt_global_entry_free_ref(tt_global);
  602 + return ret;
558 603 }
559 604  
560 605 /**
561 606  
... ... @@ -926,8 +971,16 @@
926 971 return curr_flags;
927 972 }
928 973  
  974 +/**
  975 + * batadv_tt_local_purge_list - purge inactive tt local entries
  976 + * @bat_priv: the bat priv with all the soft interface information
  977 + * @head: pointer to the list containing the local tt entries
  978 + * @timeout: parameter deciding whether a given tt local entry is considered
  979 + * inactive or not
  980 + */
929 981 static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,
930   - struct hlist_head *head)
  982 + struct hlist_head *head,
  983 + int timeout)
931 984 {
932 985 struct batadv_tt_local_entry *tt_local_entry;
933 986 struct batadv_tt_common_entry *tt_common_entry;
... ... @@ -945,8 +998,7 @@
945 998 if (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING)
946 999 continue;
947 1000  
948   - if (!batadv_has_timed_out(tt_local_entry->last_seen,
949   - BATADV_TT_LOCAL_TIMEOUT))
  1001 + if (!batadv_has_timed_out(tt_local_entry->last_seen, timeout))
950 1002 continue;
951 1003  
952 1004 batadv_tt_local_set_pending(bat_priv, tt_local_entry,
... ... @@ -954,7 +1006,14 @@
954 1006 }
955 1007 }
956 1008  
957   -static void batadv_tt_local_purge(struct batadv_priv *bat_priv)
  1009 +/**
  1010 + * batadv_tt_local_purge - purge inactive tt local entries
  1011 + * @bat_priv: the bat priv with all the soft interface information
  1012 + * @timeout: parameter deciding whether a given tt local entry is considered
  1013 + * inactive or not
  1014 + */
  1015 +static void batadv_tt_local_purge(struct batadv_priv *bat_priv,
  1016 + int timeout)
958 1017 {
959 1018 struct batadv_hashtable *hash = bat_priv->tt.local_hash;
960 1019 struct hlist_head *head;
... ... @@ -966,7 +1025,7 @@
966 1025 list_lock = &hash->list_locks[i];
967 1026  
968 1027 spin_lock_bh(list_lock);
969   - batadv_tt_local_purge_list(bat_priv, head);
  1028 + batadv_tt_local_purge_list(bat_priv, head, timeout);
970 1029 spin_unlock_bh(list_lock);
971 1030 }
972 1031 }
... ... @@ -2383,6 +2442,15 @@
2383 2442 req_dst_orig_node);
2384 2443 }
2385 2444  
  2445 + /* Don't send the response, if larger than fragmented packet. */
  2446 + tt_len = sizeof(struct batadv_unicast_tvlv_packet) + tvlv_len;
  2447 + if (tt_len > atomic_read(&bat_priv->packet_size_max)) {
  2448 + net_ratelimited_function(batadv_info, bat_priv->soft_iface,
  2449 + "Ignoring TT_REQUEST from %pM; Response size exceeds max packet size.\n",
  2450 + res_dst_orig_node->orig);
  2451 + goto out;
  2452 + }
  2453 +
2386 2454 tvlv_tt_data->flags = BATADV_TT_RESPONSE;
2387 2455 tvlv_tt_data->ttvn = req_ttvn;
2388 2456  
... ... @@ -2859,7 +2927,7 @@
2859 2927 priv_tt = container_of(delayed_work, struct batadv_priv_tt, work);
2860 2928 bat_priv = container_of(priv_tt, struct batadv_priv, tt);
2861 2929  
2862   - batadv_tt_local_purge(bat_priv);
  2930 + batadv_tt_local_purge(bat_priv, BATADV_TT_LOCAL_TIMEOUT);
2863 2931 batadv_tt_global_purge(bat_priv);
2864 2932 batadv_tt_req_purge(bat_priv);
2865 2933 batadv_tt_roam_purge(bat_priv);
2866 2934  
2867 2935  
2868 2936  
2869 2937  
... ... @@ -2972,18 +3040,18 @@
2972 3040 }
2973 3041  
2974 3042 /**
2975   - * batadv_tt_local_commit_changes - commit all pending local tt changes which
2976   - * have been queued in the time since the last commit
  3043 + * batadv_tt_local_commit_changes_nolock - commit all pending local tt changes
  3044 + * which have been queued in the time since the last commit
2977 3045 * @bat_priv: the bat priv with all the soft interface information
  3046 + *
  3047 + * Caller must hold tt->commit_lock.
2978 3048 */
2979   -void batadv_tt_local_commit_changes(struct batadv_priv *bat_priv)
  3049 +static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv)
2980 3050 {
2981   - spin_lock_bh(&bat_priv->tt.commit_lock);
2982   -
2983 3051 if (atomic_read(&bat_priv->tt.local_changes) < 1) {
2984 3052 if (!batadv_atomic_dec_not_zero(&bat_priv->tt.ogm_append_cnt))
2985 3053 batadv_tt_tvlv_container_update(bat_priv);
2986   - goto out;
  3054 + return;
2987 3055 }
2988 3056  
2989 3057 batadv_tt_local_set_flags(bat_priv, BATADV_TT_CLIENT_NEW, false, true);
2990 3058  
... ... @@ -3000,8 +3068,17 @@
3000 3068 /* reset the sending counter */
3001 3069 atomic_set(&bat_priv->tt.ogm_append_cnt, BATADV_TT_OGM_APPEND_MAX);
3002 3070 batadv_tt_tvlv_container_update(bat_priv);
  3071 +}
3003 3072  
3004   -out:
  3073 +/**
  3074 + * batadv_tt_local_commit_changes - commit all pending local tt changes which
  3075 + * have been queued in the time since the last commit
  3076 + * @bat_priv: the bat priv with all the soft interface information
  3077 + */
  3078 +void batadv_tt_local_commit_changes(struct batadv_priv *bat_priv)
  3079 +{
  3080 + spin_lock_bh(&bat_priv->tt.commit_lock);
  3081 + batadv_tt_local_commit_changes_nolock(bat_priv);
3005 3082 spin_unlock_bh(&bat_priv->tt.commit_lock);
3006 3083 }
3007 3084  
... ... @@ -3194,6 +3271,47 @@
3194 3271 ret = true;
3195 3272 out:
3196 3273 return ret;
  3274 +}
  3275 +
  3276 +/**
  3277 + * batadv_tt_local_resize_to_mtu - resize the local translation table fit the
  3278 + * maximum packet size that can be transported through the mesh
  3279 + * @soft_iface: netdev struct of the mesh interface
  3280 + *
  3281 + * Remove entries older than 'timeout' and half timeout if more entries need
  3282 + * to be removed.
  3283 + */
  3284 +void batadv_tt_local_resize_to_mtu(struct net_device *soft_iface)
  3285 +{
  3286 + struct batadv_priv *bat_priv = netdev_priv(soft_iface);
  3287 + int packet_size_max = atomic_read(&bat_priv->packet_size_max);
  3288 + int table_size, timeout = BATADV_TT_LOCAL_TIMEOUT / 2;
  3289 + bool reduced = false;
  3290 +
  3291 + spin_lock_bh(&bat_priv->tt.commit_lock);
  3292 +
  3293 + while (true) {
  3294 + table_size = batadv_tt_local_table_transmit_size(bat_priv);
  3295 + if (packet_size_max >= table_size)
  3296 + break;
  3297 +
  3298 + batadv_tt_local_purge(bat_priv, timeout);
  3299 + batadv_tt_local_purge_pending_clients(bat_priv);
  3300 +
  3301 + timeout /= 2;
  3302 + reduced = true;
  3303 + net_ratelimited_function(batadv_info, soft_iface,
  3304 + "Forced to purge local tt entries to fit new maximum fragment MTU (%i)\n",
  3305 + packet_size_max);
  3306 + }
  3307 +
  3308 + /* commit these changes immediately, to avoid synchronization problem
  3309 + * with the TTVN
  3310 + */
  3311 + if (reduced)
  3312 + batadv_tt_local_commit_changes_nolock(bat_priv);
  3313 +
  3314 + spin_unlock_bh(&bat_priv->tt.commit_lock);
3197 3315 }
3198 3316  
3199 3317 /**
net/batman-adv/translation-table.h
... ... @@ -21,7 +21,7 @@
21 21 #define _NET_BATMAN_ADV_TRANSLATION_TABLE_H_
22 22  
23 23 int batadv_tt_init(struct batadv_priv *bat_priv);
24   -void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
  24 +bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
25 25 unsigned short vid, int ifindex);
26 26 uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv,
27 27 const uint8_t *addr, unsigned short vid,
... ... @@ -45,6 +45,7 @@
45 45 uint8_t *addr, unsigned short vid);
46 46 bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv,
47 47 uint8_t *addr, unsigned short vid);
  48 +void batadv_tt_local_resize_to_mtu(struct net_device *soft_iface);
48 49 bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv,
49 50 struct batadv_orig_node *orig_node,
50 51 const unsigned char *addr,
net/batman-adv/types.h
... ... @@ -612,6 +612,8 @@
612 612 * @aggregated_ogms: bool indicating whether OGM aggregation is enabled
613 613 * @bonding: bool indicating whether traffic bonding is enabled
614 614 * @fragmentation: bool indicating whether traffic fragmentation is enabled
  615 + * @packet_size_max: max packet size that can be transmitted via
  616 + * multiple fragmented skbs or a single frame if fragmentation is disabled
615 617 * @frag_seqno: incremental counter to identify chains of egress fragments
616 618 * @bridge_loop_avoidance: bool indicating whether bridge loop avoidance is
617 619 * enabled
... ... @@ -658,6 +660,7 @@
658 660 atomic_t aggregated_ogms;
659 661 atomic_t bonding;
660 662 atomic_t fragmentation;
  663 + atomic_t packet_size_max;
661 664 atomic_t frag_seqno;
662 665 #ifdef CONFIG_BATMAN_ADV_BLA
663 666 atomic_t bridge_loop_avoidance;