Hi All,
This is a Request for Comments on my GSoC product: A new fragmentation implementation. The new fragmentation code supports more than two fragments and more than just one type of unicast packets. It works by checking the size of the packet after it is prepended with its own unicast-like header. If fragmentation is needed, each fragment is encapsulated with the fragmentation code. When merged at either an intermediate hop or the destination, the packet is delivered to its corresponding handler, which knows nothing about fragmentation.
A brief[1] and more technical[2] description of the new design is available on project-wiki.
[1] http://www.open-mesh.org/projects/open-mesh/wiki/gsoc2012-hundeboll-final-re... [2] http://www.open-mesh.org/projects/batman-adv/wiki/Fragmentation
The first patch provides a wrapper for the lookup of the best next-hop for unicast packets, which was essentially boiler plate code in several functions. Later this will ease the fragmentation code, which needs to know both the final destination and the next hop, and thus is called from within this wrapper.
The second patch removes the old fragmentation code, but leaves behind the creation of a configuration file in sysfs, used to disable/enable fragmentation.
The third patch provides support for processing received fragments. These are either: * Forwarded if the size of the merged packet would lead to refragmentation. * Buffered for later merge. * Used to merge the original packet if all fragments are received.
The fourth patch inserts a size check of outgoing packets and fragmentation if needed. The size of fragments are limited to 1400 bytes to avoid teh need merge-and-fragment at intermediate hops with an MTU lower than that of the original fragmenter.
The fifth patch modifies the announcement of clients to be aware of the new limit on packet sizes. It now ignores new clients if it would make the local table too big to send in a (fragmented) full table response.
Comments are welcome!
Martin Hundebøll (5): batman-adv: Add wrapper to look up neighbor and send skb batman-adv: Remove old fragmentation code batman-adv: Receive fragmented packets and merge batman-adv: Fragment and send skbs larger than mtu batman-adv: Limit local translation table max packet size
Makefile.kbuild | 2 +- fragmentation.c | 493 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fragmentation.h | 56 ++++++ hard-interface.c | 26 ++- main.c | 5 +- main.h | 4 + originator.c | 17 +- packet.h | 34 ++-- routing.c | 150 ++++++---------- routing.h | 2 + send.c | 92 ++++++++++ send.h | 4 + soft-interface.c | 22 ++- translation-table.c | 251 +++++++++++++++++--------- translation-table.h | 3 +- types.h | 39 +++++ unicast.c | 373 --------------------------------------- unicast.h | 58 ------- vis.c | 35 +--- 19 files changed, 995 insertions(+), 671 deletions(-) create mode 100644 fragmentation.c create mode 100644 fragmentation.h delete mode 100644 unicast.c delete mode 100644 unicast.h
By adding batadv_send_skb_to_orig() in send.c, we can remove duplicate code that looks up the next hop and then calls batadv_send_skb_packet().
Furthermore, this prepares the upcoming new implementation of fragmentation, which requires the next hop to route packets.
Please note that this doesn't entirely remove the next-hop lookup in routing.c and unicast.c, since it is used by the current fragmentation code.
Also note that the next-hop info is removed from debug messages in translation-table.c, since it is looked up elsewhere.
Signed-off-by: Martin Hundebøll martin@hundeboll.net --- routing.c | 37 +++++++----------------------- send.c | 20 +++++++++++++++++ send.h | 3 +++ translation-table.c | 65 ++++++++++++++--------------------------------------- unicast.c | 10 ++++----- vis.c | 35 ++++++++--------------------- 6 files changed, 62 insertions(+), 108 deletions(-)
diff --git a/routing.c b/routing.c index 939fc01..53d991c 100644 --- a/routing.c +++ b/routing.c @@ -284,7 +284,6 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv, { struct batadv_hard_iface *primary_if = NULL; struct batadv_orig_node *orig_node = NULL; - struct batadv_neigh_node *router = NULL; struct batadv_icmp_packet_rr *icmp_packet; int ret = NET_RX_DROP;
@@ -306,10 +305,6 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv, if (!orig_node) goto out;
- router = batadv_orig_node_get_router(orig_node); - if (!router) - goto out; - /* create a copy of the skb, if needed, to modify it. */ if (skb_cow(skb, ETH_HLEN) < 0) goto out; @@ -321,14 +316,12 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv, icmp_packet->msg_type = BATADV_ECHO_REPLY; icmp_packet->header.ttl = BATADV_TTL;
- batadv_send_skb_packet(skb, router->if_incoming, router->addr); - ret = NET_RX_SUCCESS; + if (batadv_send_skb_to_orig(skb, orig_node, NULL)) + ret = NET_RX_SUCCESS;
out: if (primary_if) batadv_hardif_free_ref(primary_if); - if (router) - batadv_neigh_node_free_ref(router); if (orig_node) batadv_orig_node_free_ref(orig_node); return ret; @@ -339,7 +332,6 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv, { struct batadv_hard_iface *primary_if = NULL; struct batadv_orig_node *orig_node = NULL; - struct batadv_neigh_node *router = NULL; struct batadv_icmp_packet *icmp_packet; int ret = NET_RX_DROP;
@@ -361,10 +353,6 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv, if (!orig_node) goto out;
- router = batadv_orig_node_get_router(orig_node); - if (!router) - goto out; - /* create a copy of the skb, if needed, to modify it. */ if (skb_cow(skb, ETH_HLEN) < 0) goto out; @@ -376,14 +364,12 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv, icmp_packet->msg_type = BATADV_TTL_EXCEEDED; icmp_packet->header.ttl = BATADV_TTL;
- batadv_send_skb_packet(skb, router->if_incoming, router->addr); - ret = NET_RX_SUCCESS; + if (batadv_send_skb_to_orig(skb, orig_node, NULL)) + ret = NET_RX_SUCCESS;
out: if (primary_if) batadv_hardif_free_ref(primary_if); - if (router) - batadv_neigh_node_free_ref(router); if (orig_node) batadv_orig_node_free_ref(orig_node); return ret; @@ -397,7 +383,6 @@ int batadv_recv_icmp_packet(struct sk_buff *skb, struct batadv_icmp_packet_rr *icmp_packet; struct ethhdr *ethhdr; struct batadv_orig_node *orig_node = NULL; - struct batadv_neigh_node *router = NULL; int hdr_size = sizeof(struct batadv_icmp_packet); int ret = NET_RX_DROP;
@@ -446,10 +431,6 @@ int batadv_recv_icmp_packet(struct sk_buff *skb, if (!orig_node) goto out;
- router = batadv_orig_node_get_router(orig_node); - if (!router) - goto out; - /* create a copy of the skb, if needed, to modify it. */ if (skb_cow(skb, ETH_HLEN) < 0) goto out; @@ -460,12 +441,10 @@ int batadv_recv_icmp_packet(struct sk_buff *skb, icmp_packet->header.ttl--;
/* route it */ - batadv_send_skb_packet(skb, router->if_incoming, router->addr); - ret = NET_RX_SUCCESS; + if (batadv_send_skb_to_orig(skb, orig_node, recv_if)) + ret = NET_RX_SUCCESS;
out: - if (router) - batadv_neigh_node_free_ref(router); if (orig_node) batadv_orig_node_free_ref(orig_node); return ret; @@ -907,8 +886,8 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, skb->len + ETH_HLEN);
/* route it */ - batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); - ret = NET_RX_SUCCESS; + if (batadv_send_skb_to_orig(skb, orig_node, recv_if)) + ret = NET_RX_SUCCESS;
out: if (neigh_node) diff --git a/send.c b/send.c index 570a8bc..4a6400f 100644 --- a/send.c +++ b/send.c @@ -77,6 +77,26 @@ send_skb_err: return NET_XMIT_DROP; }
+bool batadv_send_skb_to_orig(struct sk_buff *skb, + struct batadv_orig_node *orig_node, + struct batadv_hard_iface *recv_if) +{ + struct batadv_priv *bat_priv = orig_node->bat_priv; + struct batadv_neigh_node *neigh_node; + + /* batadv_find_router() increases neigh_nodes refcount if found. */ + neigh_node = batadv_find_router(bat_priv, orig_node, recv_if); + if (!neigh_node) + return false; + + /* route it */ + batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + + batadv_neigh_node_free_ref(neigh_node); + + return true; +} + void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); diff --git a/send.h b/send.h index 643329b..0078dec 100644 --- a/send.h +++ b/send.h @@ -23,6 +23,9 @@ int batadv_send_skb_packet(struct sk_buff *skb, struct batadv_hard_iface *hard_iface, const uint8_t *dst_addr); +bool batadv_send_skb_to_orig(struct sk_buff *skb, + struct batadv_orig_node *orig_node, + struct batadv_hard_iface *recv_if); void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface); int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, const struct sk_buff *skb, diff --git a/translation-table.c b/translation-table.c index 37ae4a9..2cee8b2 100644 --- a/translation-table.c +++ b/translation-table.c @@ -1519,7 +1519,6 @@ static int batadv_send_tt_request(struct batadv_priv *bat_priv, { struct sk_buff *skb = NULL; struct batadv_tt_query_packet *tt_request; - struct batadv_neigh_node *neigh_node = NULL; struct batadv_hard_iface *primary_if; struct batadv_tt_req_node *tt_req_node = NULL; int ret = 1; @@ -1557,23 +1556,15 @@ static int batadv_send_tt_request(struct batadv_priv *bat_priv, if (full_table) tt_request->flags |= BATADV_TT_FULL_TABLE;
- neigh_node = batadv_orig_node_get_router(dst_orig_node); - if (!neigh_node) - goto out; - - batadv_dbg(BATADV_DBG_TT, bat_priv, - "Sending TT_REQUEST to %pM via %pM [%c]\n", - dst_orig_node->orig, neigh_node->addr, - (full_table ? 'F' : '.')); + batadv_dbg(BATADV_DBG_TT, bat_priv, "Sending TT_REQUEST to %pM [%c]\n", + dst_orig_node->orig, (full_table ? 'F' : '.'));
batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_TX);
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); - ret = 0; + if (batadv_send_skb_to_orig(skb, dst_orig_node, NULL)) + ret = 0;
out: - if (neigh_node) - batadv_neigh_node_free_ref(neigh_node); if (primary_if) batadv_hardif_free_ref(primary_if); if (ret) @@ -1593,7 +1584,6 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv, { struct batadv_orig_node *req_dst_orig_node = NULL; struct batadv_orig_node *res_dst_orig_node = NULL; - struct batadv_neigh_node *neigh_node = NULL; struct batadv_hard_iface *primary_if = NULL; uint8_t orig_ttvn, req_ttvn, ttvn; int ret = false; @@ -1619,10 +1609,6 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv, if (!res_dst_orig_node) goto out;
- neigh_node = batadv_orig_node_get_router(res_dst_orig_node); - if (!neigh_node) - goto out; - primary_if = batadv_primary_if_get_selected(bat_priv); if (!primary_if) goto out; @@ -1694,14 +1680,13 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv, tt_response->flags |= BATADV_TT_FULL_TABLE;
batadv_dbg(BATADV_DBG_TT, bat_priv, - "Sending TT_RESPONSE %pM via %pM for %pM (ttvn: %u)\n", - res_dst_orig_node->orig, neigh_node->addr, - req_dst_orig_node->orig, req_ttvn); + "Sending TT_RESPONSE %pM for %pM (ttvn: %u)\n", + res_dst_orig_node->orig, req_dst_orig_node->orig, req_ttvn);
batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX);
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); - ret = true; + if (batadv_send_skb_to_orig(skb, res_dst_orig_node, NULL)) + ret = true; goto out;
unlock: @@ -1712,8 +1697,6 @@ out: batadv_orig_node_free_ref(res_dst_orig_node); if (req_dst_orig_node) batadv_orig_node_free_ref(req_dst_orig_node); - if (neigh_node) - batadv_neigh_node_free_ref(neigh_node); if (primary_if) batadv_hardif_free_ref(primary_if); if (!ret) @@ -1727,7 +1710,6 @@ batadv_send_my_tt_response(struct batadv_priv *bat_priv, struct batadv_tt_query_packet *tt_request) { struct batadv_orig_node *orig_node = NULL; - struct batadv_neigh_node *neigh_node = NULL; struct batadv_hard_iface *primary_if = NULL; uint8_t my_ttvn, req_ttvn, ttvn; int ret = false; @@ -1752,10 +1734,6 @@ batadv_send_my_tt_response(struct batadv_priv *bat_priv, if (!orig_node) goto out;
- neigh_node = batadv_orig_node_get_router(orig_node); - if (!neigh_node) - goto out; - primary_if = batadv_primary_if_get_selected(bat_priv); if (!primary_if) goto out; @@ -1819,14 +1797,14 @@ batadv_send_my_tt_response(struct batadv_priv *bat_priv, tt_response->flags |= BATADV_TT_FULL_TABLE;
batadv_dbg(BATADV_DBG_TT, bat_priv, - "Sending TT_RESPONSE to %pM via %pM [%c]\n", - orig_node->orig, neigh_node->addr, + "Sending TT_RESPONSE to %pM [%c]\n", + orig_node->orig, (tt_response->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX);
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); - ret = true; + if (batadv_send_skb_to_orig(skb, orig_node, NULL)) + ret = true; goto out;
unlock: @@ -1834,8 +1812,6 @@ unlock: out: if (orig_node) batadv_orig_node_free_ref(orig_node); - if (neigh_node) - batadv_neigh_node_free_ref(neigh_node); if (primary_if) batadv_hardif_free_ref(primary_if); if (!ret) @@ -2103,7 +2079,6 @@ unlock: static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client, struct batadv_orig_node *orig_node) { - struct batadv_neigh_node *neigh_node = NULL; struct sk_buff *skb = NULL; struct batadv_roam_adv_packet *roam_adv_packet; int ret = 1; @@ -2136,23 +2111,17 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client, memcpy(roam_adv_packet->dst, orig_node->orig, ETH_ALEN); memcpy(roam_adv_packet->client, client, ETH_ALEN);
- neigh_node = batadv_orig_node_get_router(orig_node); - if (!neigh_node) - goto out; - batadv_dbg(BATADV_DBG_TT, bat_priv, - "Sending ROAMING_ADV to %pM (client %pM) via %pM\n", - orig_node->orig, client, neigh_node->addr); + "Sending ROAMING_ADV to %pM (client %pM)\n", + orig_node->orig, client);
batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_TX);
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); - ret = 0; + if (batadv_send_skb_to_orig(skb, orig_node, NULL)) + ret = 0;
out: - if (neigh_node) - batadv_neigh_node_free_ref(neigh_node); - if (ret) + if (ret && skb) kfree_skb(skb); return; } diff --git a/unicast.c b/unicast.c index f397232..add5d1f 100644 --- a/unicast.c +++ b/unicast.c @@ -298,7 +298,7 @@ int batadv_unicast_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv) struct batadv_orig_node *orig_node; struct batadv_neigh_node *neigh_node; int data_len = skb->len; - int ret = 1; + int ret = NET_RX_DROP; unsigned int dev_mtu;
/* get routing information */ @@ -358,16 +358,16 @@ find_router: goto out; }
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); - ret = 0; - goto out; + + if (batadv_send_skb_to_orig(skb, orig_node, NULL)) + ret = NET_RX_SUCCESS;
out: if (neigh_node) batadv_neigh_node_free_ref(neigh_node); if (orig_node) batadv_orig_node_free_ref(orig_node); - if (ret == 1) + if (ret == NET_RX_DROP) kfree_skb(skb); return ret; } diff --git a/vis.c b/vis.c index 5abd145..79589a3 100644 --- a/vis.c +++ b/vis.c @@ -698,15 +698,12 @@ static void batadv_purge_vis_packets(struct batadv_priv *bat_priv) static void batadv_broadcast_vis_packet(struct batadv_priv *bat_priv, struct batadv_vis_info *info) { - struct batadv_neigh_node *router; struct batadv_hashtable *hash = bat_priv->orig_hash; struct hlist_node *node; struct hlist_head *head; struct batadv_orig_node *orig_node; struct batadv_vis_packet *packet; struct sk_buff *skb; - struct batadv_hard_iface *hard_iface; - uint8_t dstaddr[ETH_ALEN]; uint32_t i;
@@ -722,30 +719,20 @@ static void batadv_broadcast_vis_packet(struct batadv_priv *bat_priv, if (!(orig_node->flags & BATADV_VIS_SERVER)) continue;
- router = batadv_orig_node_get_router(orig_node); - if (!router) - continue; - /* don't send it if we already received the packet from * this node. */ if (batadv_recv_list_is_in(bat_priv, &info->recv_list, - orig_node->orig)) { - batadv_neigh_node_free_ref(router); + orig_node->orig)) continue; - }
memcpy(packet->target_orig, orig_node->orig, ETH_ALEN); - hard_iface = router->if_incoming; - memcpy(dstaddr, router->addr, ETH_ALEN); - - batadv_neigh_node_free_ref(router); - skb = skb_clone(info->skb_packet, GFP_ATOMIC); - if (skb) - batadv_send_skb_packet(skb, hard_iface, - dstaddr); + if (!skb) + continue;
+ if (!batadv_send_skb_to_orig(skb, orig_node, NULL)) + kfree_skb(skb); } rcu_read_unlock(); } @@ -755,7 +742,6 @@ static void batadv_unicast_vis_packet(struct batadv_priv *bat_priv, struct batadv_vis_info *info) { struct batadv_orig_node *orig_node; - struct batadv_neigh_node *router = NULL; struct sk_buff *skb; struct batadv_vis_packet *packet;
@@ -765,17 +751,14 @@ static void batadv_unicast_vis_packet(struct batadv_priv *bat_priv, if (!orig_node) goto out;
- router = batadv_orig_node_get_router(orig_node); - if (!router) + skb = skb_clone(info->skb_packet, GFP_ATOMIC); + if (!skb) goto out;
- skb = skb_clone(info->skb_packet, GFP_ATOMIC); - if (skb) - batadv_send_skb_packet(skb, router->if_incoming, router->addr); + if (!batadv_send_skb_to_orig(skb, orig_node, NULL)) + kfree_skb(skb);
out: - if (router) - batadv_neigh_node_free_ref(router); if (orig_node) batadv_orig_node_free_ref(orig_node); }
Remove the existing fragmentation code before adding the new version and delete unicast.{h,c}.
batadv_unicast_send_skb() is moved to send.c and renamed to batadv_send_skb_unicast().
fragmentation entry in sysfs (bat_priv->fragmentation) is kept for use in the new fragmentation code.
BATADV_UNICAST_FRAG packet type is renamed to BATADV_FRAG for use in the new fragmentation code.
Signed-off-by: Martin Hundebøll martin@hundeboll.net --- Makefile.kbuild | 1 - main.c | 2 - originator.c | 9 -- packet.h | 18 +-- routing.c | 80 +----------- send.c | 58 +++++++++ send.h | 1 + soft-interface.c | 3 +- unicast.c | 373 ------------------------------------------------------- unicast.h | 58 --------- 10 files changed, 62 insertions(+), 541 deletions(-) delete mode 100644 unicast.c delete mode 100644 unicast.h
diff --git a/Makefile.kbuild b/Makefile.kbuild index 8676d2b..9d622ad 100644 --- a/Makefile.kbuild +++ b/Makefile.kbuild @@ -36,5 +36,4 @@ batman-adv-y += send.o batman-adv-y += soft-interface.o batman-adv-y += sysfs.o batman-adv-y += translation-table.o -batman-adv-y += unicast.o batman-adv-y += vis.o diff --git a/main.c b/main.c index b4aa470..a93328f 100644 --- a/main.c +++ b/main.c @@ -276,8 +276,6 @@ static void batadv_recv_handler_init(void) batadv_rx_handler[BATADV_ICMP] = batadv_recv_icmp_packet; /* unicast packet */ batadv_rx_handler[BATADV_UNICAST] = batadv_recv_unicast_packet; - /* fragmented unicast packet */ - batadv_rx_handler[BATADV_UNICAST_FRAG] = batadv_recv_ucast_frag_packet; /* broadcast packet */ batadv_rx_handler[BATADV_BCAST] = batadv_recv_bcast_packet; /* vis packet */ diff --git a/originator.c b/originator.c index ac9bdf8..6ebf771 100644 --- a/originator.c +++ b/originator.c @@ -24,7 +24,6 @@ #include "routing.h" #include "gateway_client.h" #include "hard-interface.h" -#include "unicast.h" #include "soft-interface.h" #include "bridge_loop_avoidance.h"
@@ -138,7 +137,6 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu)
spin_unlock_bh(&orig_node->neigh_list_lock);
- batadv_frag_list_free(&orig_node->frag_list); batadv_tt_global_del_orig(orig_node->bat_priv, orig_node, "originator timed out");
@@ -244,9 +242,6 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, size = bat_priv->num_ifaces * sizeof(uint8_t); orig_node->bcast_own_sum = kzalloc(size, GFP_ATOMIC);
- INIT_LIST_HEAD(&orig_node->frag_list); - orig_node->last_frag_packet = 0; - if (!orig_node->bcast_own_sum) goto free_bcast_own;
@@ -372,10 +367,6 @@ static void _batadv_purge_orig(struct batadv_priv *bat_priv) batadv_orig_node_free_ref(orig_node); continue; } - - if (batadv_has_timed_out(orig_node->last_frag_packet, - BATADV_FRAG_TIMEOUT)) - batadv_frag_list_free(&orig_node->frag_list); } spin_unlock_bh(list_lock); } diff --git a/packet.h b/packet.h index 2d23a14..ce6e502 100644 --- a/packet.h +++ b/packet.h @@ -28,7 +28,7 @@ enum batadv_packettype { BATADV_UNICAST = 0x03, BATADV_BCAST = 0x04, BATADV_VIS = 0x05, - BATADV_UNICAST_FRAG = 0x06, + BATADV_FRAG = 0x06, BATADV_TT_QUERY = 0x07, BATADV_ROAM_ADV = 0x08, }; @@ -58,12 +58,6 @@ enum batadv_vis_packettype { BATADV_VIS_TYPE_CLIENT_UPDATE = 1, };
-/* fragmentation defines */ -enum batadv_unicast_frag_flags { - BATADV_UNI_FRAG_HEAD = BIT(0), - BATADV_UNI_FRAG_LARGETAIL = BIT(1), -}; - /* TT_QUERY subtypes */ #define BATADV_TT_QUERY_TYPE_MASK 0x3
@@ -161,16 +155,6 @@ struct batadv_unicast_packet { uint8_t dest[ETH_ALEN]; } __packed;
-struct batadv_unicast_frag_packet { - struct batadv_header header; - uint8_t ttvn; /* destination translation table version number */ - uint8_t dest[ETH_ALEN]; - uint8_t flags; - uint8_t align; - uint8_t orig[ETH_ALEN]; - __be16 seqno; -} __packed; - struct batadv_bcast_packet { struct batadv_header header; uint8_t reserved; diff --git a/routing.c b/routing.c index 53d991c..ca4847e 100644 --- a/routing.c +++ b/routing.c @@ -26,7 +26,6 @@ #include "translation-table.h" #include "originator.h" #include "vis.h" -#include "unicast.h" #include "bridge_loop_avoidance.h"
static int batadv_route_unicast_packet(struct sk_buff *skb, @@ -816,11 +815,9 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, { struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct batadv_orig_node *orig_node = NULL; - struct batadv_neigh_node *neigh_node = NULL; struct batadv_unicast_packet *unicast_packet; struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb); int ret = NET_RX_DROP; - struct sk_buff *new_skb;
unicast_packet = (struct batadv_unicast_packet *)skb->data;
@@ -837,47 +834,12 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, if (!orig_node) goto out;
- /* find_router() increases neigh_nodes refcount if found. */ - neigh_node = batadv_find_router(bat_priv, orig_node, recv_if); - - if (!neigh_node) - goto out; - /* create a copy of the skb, if needed, to modify it. */ if (skb_cow(skb, ETH_HLEN) < 0) goto out;
- unicast_packet = (struct batadv_unicast_packet *)skb->data; - - if (unicast_packet->header.packet_type == BATADV_UNICAST && - atomic_read(&bat_priv->fragmentation) && - skb->len > neigh_node->if_incoming->net_dev->mtu) { - ret = batadv_frag_send_skb(skb, bat_priv, - neigh_node->if_incoming, - neigh_node->addr); - goto out; - } - - if (unicast_packet->header.packet_type == BATADV_UNICAST_FRAG && - batadv_frag_can_reassemble(skb, - neigh_node->if_incoming->net_dev->mtu)) { - - ret = batadv_frag_reassemble_skb(skb, bat_priv, &new_skb); - - if (ret == NET_RX_DROP) - goto out; - - /* packet was buffered for late merge */ - if (!new_skb) { - ret = NET_RX_SUCCESS; - goto out; - } - - skb = new_skb; - unicast_packet = (struct batadv_unicast_packet *)skb->data; - } - /* decrement ttl */ + unicast_packet = (struct batadv_unicast_packet *)skb->data; unicast_packet->header.ttl--;
/* Update stats counter */ @@ -890,8 +852,6 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, ret = NET_RX_SUCCESS;
out: - if (neigh_node) - batadv_neigh_node_free_ref(neigh_node); if (orig_node) batadv_orig_node_free_ref(orig_node); return ret; @@ -1000,44 +960,6 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, return batadv_route_unicast_packet(skb, recv_if); }
-int batadv_recv_ucast_frag_packet(struct sk_buff *skb, - struct batadv_hard_iface *recv_if) -{ - struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); - struct batadv_unicast_frag_packet *unicast_packet; - int hdr_size = sizeof(*unicast_packet); - struct sk_buff *new_skb = NULL; - int ret; - - if (batadv_check_unicast_packet(skb, hdr_size) < 0) - return NET_RX_DROP; - - if (!batadv_check_unicast_ttvn(bat_priv, skb)) - return NET_RX_DROP; - - unicast_packet = (struct batadv_unicast_frag_packet *)skb->data; - - /* packet for me */ - if (batadv_is_my_mac(unicast_packet->dest)) { - - ret = batadv_frag_reassemble_skb(skb, bat_priv, &new_skb); - - if (ret == NET_RX_DROP) - return NET_RX_DROP; - - /* packet was buffered for late merge */ - if (!new_skb) - return NET_RX_SUCCESS; - - batadv_interface_rx(recv_if->soft_iface, new_skb, recv_if, - sizeof(struct batadv_unicast_packet), NULL); - return NET_RX_SUCCESS; - } - - return batadv_route_unicast_packet(skb, recv_if); -} - - int batadv_recv_bcast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if) { diff --git a/send.c b/send.c index 4a6400f..f894903 100644 --- a/send.c +++ b/send.c @@ -25,6 +25,7 @@ #include "hard-interface.h" #include "vis.h" #include "gateway_common.h" +#include "gateway_client.h" #include "originator.h"
static void batadv_send_outstanding_bcast_packet(struct work_struct *work); @@ -97,6 +98,63 @@ bool batadv_send_skb_to_orig(struct sk_buff *skb, return true; }
+int batadv_send_skb_unicast(struct batadv_priv *bat_priv, struct sk_buff *skb) +{ + struct ethhdr *ethhdr = (struct ethhdr *)skb->data; + struct batadv_unicast_packet *unicast_packet; + struct batadv_orig_node *orig_node; + int ret = NET_RX_DROP; + + /* If the packet is and DHCP request and the gateway selection finds + * a gateway, translation table lookup is skipped. + * */ + if (!is_multicast_ether_addr(ethhdr->h_dest) || + (orig_node = batadv_gw_get_selected_orig(bat_priv)) == NULL) { + /* check for tt host - increases orig_node refcount. + * returns NULL in case of AP isolation and the packet is + * eventually dropped. + */ + orig_node = batadv_transtable_search(bat_priv, ethhdr->h_source, + ethhdr->h_dest); + } + + if (!orig_node) + goto out; + + if (batadv_skb_head_push(skb, sizeof(*unicast_packet)) < 0) + goto out; + + unicast_packet = (struct batadv_unicast_packet *)skb->data; + + unicast_packet->header.version = BATADV_COMPAT_VERSION; + /* batman packet type: unicast */ + unicast_packet->header.packet_type = BATADV_UNICAST; + /* set unicast ttl */ + unicast_packet->header.ttl = BATADV_TTL; + /* copy the destination for faster routing */ + memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN); + /* set the destination tt version number */ + unicast_packet->ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); + + /* inform the destination node that we are still missing a correct route + * for this client. The destination will receive this packet and will + * try to reroute it because the ttvn contained in the header is less + * than the current one + */ + if (batadv_tt_global_client_is_roaming(bat_priv, ethhdr->h_dest)) + unicast_packet->ttvn = unicast_packet->ttvn - 1; + + if (batadv_send_skb_to_orig(skb, orig_node, NULL)) + ret = NET_RX_SUCCESS; + +out: + if (orig_node) + batadv_orig_node_free_ref(orig_node); + if (ret == NET_RX_DROP) + kfree_skb(skb); + return ret; +} + void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); diff --git a/send.h b/send.h index 0078dec..2e2b880 100644 --- a/send.h +++ b/send.h @@ -26,6 +26,7 @@ int batadv_send_skb_packet(struct sk_buff *skb, bool batadv_send_skb_to_orig(struct sk_buff *skb, struct batadv_orig_node *orig_node, struct batadv_hard_iface *recv_if); +int batadv_send_skb_unicast(struct batadv_priv *bat_priv, struct sk_buff *skb); void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface); int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, const struct sk_buff *skb, diff --git a/soft-interface.c b/soft-interface.c index 1aee7db..fbec8a4 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -33,7 +33,6 @@ #include <linux/ethtool.h> #include <linux/etherdevice.h> #include <linux/if_vlan.h> -#include "unicast.h" #include "bridge_loop_avoidance.h"
@@ -249,7 +248,7 @@ static int batadv_interface_tx(struct sk_buff *skb, goto dropped; }
- ret = batadv_unicast_send_skb(skb, bat_priv); + ret = batadv_send_skb_unicast(bat_priv, skb); if (ret != 0) goto dropped_freed; } diff --git a/unicast.c b/unicast.c deleted file mode 100644 index add5d1f..0000000 --- a/unicast.c +++ /dev/null @@ -1,373 +0,0 @@ -/* Copyright (C) 2010-2012 B.A.T.M.A.N. contributors: - * - * Andreas Langer - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - */ - -#include "main.h" -#include "unicast.h" -#include "send.h" -#include "soft-interface.h" -#include "gateway_client.h" -#include "originator.h" -#include "hash.h" -#include "translation-table.h" -#include "routing.h" -#include "hard-interface.h" - - -static struct sk_buff * -batadv_frag_merge_packet(struct list_head *head, - struct batadv_frag_packet_list_entry *tfp, - struct sk_buff *skb) -{ - struct batadv_unicast_frag_packet *up; - struct sk_buff *tmp_skb; - struct batadv_unicast_packet *unicast_packet; - int hdr_len = sizeof(*unicast_packet); - int uni_diff = sizeof(*up) - hdr_len; - uint8_t *packet_pos; - - up = (struct batadv_unicast_frag_packet *)skb->data; - /* set skb to the first part and tmp_skb to the second part */ - if (up->flags & BATADV_UNI_FRAG_HEAD) { - tmp_skb = tfp->skb; - } else { - tmp_skb = skb; - skb = tfp->skb; - } - - if (skb_linearize(skb) < 0 || skb_linearize(tmp_skb) < 0) - goto err; - - skb_pull(tmp_skb, sizeof(*up)); - if (pskb_expand_head(skb, 0, tmp_skb->len, GFP_ATOMIC) < 0) - goto err; - - /* move free entry to end */ - tfp->skb = NULL; - tfp->seqno = 0; - list_move_tail(&tfp->list, head); - - memcpy(skb_put(skb, tmp_skb->len), tmp_skb->data, tmp_skb->len); - kfree_skb(tmp_skb); - - memmove(skb->data + uni_diff, skb->data, hdr_len); - packet_pos = skb_pull(skb, uni_diff); - unicast_packet = (struct batadv_unicast_packet *)packet_pos; - unicast_packet->header.packet_type = BATADV_UNICAST; - - return skb; - -err: - /* free buffered skb, skb will be freed later */ - kfree_skb(tfp->skb); - return NULL; -} - -static void batadv_frag_create_entry(struct list_head *head, - struct sk_buff *skb) -{ - struct batadv_frag_packet_list_entry *tfp; - struct batadv_unicast_frag_packet *up; - - up = (struct batadv_unicast_frag_packet *)skb->data; - - /* free and oldest packets stand at the end */ - tfp = list_entry((head)->prev, typeof(*tfp), list); - kfree_skb(tfp->skb); - - tfp->seqno = ntohs(up->seqno); - tfp->skb = skb; - list_move(&tfp->list, head); - return; -} - -static int batadv_frag_create_buffer(struct list_head *head) -{ - int i; - struct batadv_frag_packet_list_entry *tfp; - - for (i = 0; i < BATADV_FRAG_BUFFER_SIZE; i++) { - tfp = kmalloc(sizeof(*tfp), GFP_ATOMIC); - if (!tfp) { - batadv_frag_list_free(head); - return -ENOMEM; - } - tfp->skb = NULL; - tfp->seqno = 0; - INIT_LIST_HEAD(&tfp->list); - list_add(&tfp->list, head); - } - - return 0; -} - -static struct batadv_frag_packet_list_entry * -batadv_frag_search_packet(struct list_head *head, - const struct batadv_unicast_frag_packet *up) -{ - struct batadv_frag_packet_list_entry *tfp; - struct batadv_unicast_frag_packet *tmp_up = NULL; - int is_head_tmp, is_head; - uint16_t search_seqno; - - if (up->flags & BATADV_UNI_FRAG_HEAD) - search_seqno = ntohs(up->seqno)+1; - else - search_seqno = ntohs(up->seqno)-1; - - is_head = !!(up->flags & BATADV_UNI_FRAG_HEAD); - - list_for_each_entry(tfp, head, list) { - - if (!tfp->skb) - continue; - - if (tfp->seqno == ntohs(up->seqno)) - goto mov_tail; - - tmp_up = (struct batadv_unicast_frag_packet *)tfp->skb->data; - - if (tfp->seqno == search_seqno) { - is_head_tmp = !!(tmp_up->flags & BATADV_UNI_FRAG_HEAD); - if (is_head_tmp != is_head) - return tfp; - else - goto mov_tail; - } - } - return NULL; - -mov_tail: - list_move_tail(&tfp->list, head); - return NULL; -} - -void batadv_frag_list_free(struct list_head *head) -{ - struct batadv_frag_packet_list_entry *pf, *tmp_pf; - - if (!list_empty(head)) { - - list_for_each_entry_safe(pf, tmp_pf, head, list) { - kfree_skb(pf->skb); - list_del(&pf->list); - kfree(pf); - } - } - return; -} - -/* frag_reassemble_skb(): - * returns NET_RX_DROP if the operation failed - skb is left intact - * returns NET_RX_SUCCESS if the fragment was buffered (skb_new will be NULL) - * or the skb could be reassembled (skb_new will point to the new packet and - * skb was freed) - */ -int batadv_frag_reassemble_skb(struct sk_buff *skb, - struct batadv_priv *bat_priv, - struct sk_buff **new_skb) -{ - struct batadv_orig_node *orig_node; - struct batadv_frag_packet_list_entry *tmp_frag_entry; - int ret = NET_RX_DROP; - struct batadv_unicast_frag_packet *unicast_packet; - - unicast_packet = (struct batadv_unicast_frag_packet *)skb->data; - *new_skb = NULL; - - orig_node = batadv_orig_hash_find(bat_priv, unicast_packet->orig); - if (!orig_node) - goto out; - - orig_node->last_frag_packet = jiffies; - - if (list_empty(&orig_node->frag_list) && - batadv_frag_create_buffer(&orig_node->frag_list)) { - pr_debug("couldn't create frag buffer\n"); - goto out; - } - - tmp_frag_entry = batadv_frag_search_packet(&orig_node->frag_list, - unicast_packet); - - if (!tmp_frag_entry) { - batadv_frag_create_entry(&orig_node->frag_list, skb); - ret = NET_RX_SUCCESS; - goto out; - } - - *new_skb = batadv_frag_merge_packet(&orig_node->frag_list, - tmp_frag_entry, skb); - /* if not, merge failed */ - if (*new_skb) - ret = NET_RX_SUCCESS; - -out: - if (orig_node) - batadv_orig_node_free_ref(orig_node); - return ret; -} - -int batadv_frag_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv, - struct batadv_hard_iface *hard_iface, - const uint8_t dstaddr[]) -{ - struct batadv_unicast_packet tmp_uc, *unicast_packet; - struct batadv_hard_iface *primary_if; - struct sk_buff *frag_skb; - struct batadv_unicast_frag_packet *frag1, *frag2; - int uc_hdr_len = sizeof(*unicast_packet); - int ucf_hdr_len = sizeof(*frag1); - int data_len = skb->len - uc_hdr_len; - int large_tail = 0, ret = NET_RX_DROP; - uint16_t seqno; - - primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) - goto dropped; - - frag_skb = dev_alloc_skb(data_len - (data_len / 2) + ucf_hdr_len); - if (!frag_skb) - goto dropped; - skb_reserve(frag_skb, ucf_hdr_len); - - unicast_packet = (struct batadv_unicast_packet *)skb->data; - memcpy(&tmp_uc, unicast_packet, uc_hdr_len); - skb_split(skb, frag_skb, data_len / 2 + uc_hdr_len); - - if (batadv_skb_head_push(skb, ucf_hdr_len - uc_hdr_len) < 0 || - batadv_skb_head_push(frag_skb, ucf_hdr_len) < 0) - goto drop_frag; - - frag1 = (struct batadv_unicast_frag_packet *)skb->data; - frag2 = (struct batadv_unicast_frag_packet *)frag_skb->data; - - memcpy(frag1, &tmp_uc, sizeof(tmp_uc)); - - frag1->header.ttl--; - frag1->header.version = BATADV_COMPAT_VERSION; - frag1->header.packet_type = BATADV_UNICAST_FRAG; - - memcpy(frag1->orig, primary_if->net_dev->dev_addr, ETH_ALEN); - memcpy(frag2, frag1, sizeof(*frag2)); - - if (data_len & 1) - large_tail = BATADV_UNI_FRAG_LARGETAIL; - - frag1->flags = BATADV_UNI_FRAG_HEAD | large_tail; - frag2->flags = large_tail; - - seqno = atomic_add_return(2, &hard_iface->frag_seqno); - frag1->seqno = htons(seqno - 1); - frag2->seqno = htons(seqno); - - batadv_send_skb_packet(skb, hard_iface, dstaddr); - batadv_send_skb_packet(frag_skb, hard_iface, dstaddr); - ret = NET_RX_SUCCESS; - goto out; - -drop_frag: - kfree_skb(frag_skb); -dropped: - kfree_skb(skb); -out: - if (primary_if) - batadv_hardif_free_ref(primary_if); - return ret; -} - -int batadv_unicast_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv) -{ - struct ethhdr *ethhdr = (struct ethhdr *)skb->data; - struct batadv_unicast_packet *unicast_packet; - struct batadv_orig_node *orig_node; - struct batadv_neigh_node *neigh_node; - int data_len = skb->len; - int ret = NET_RX_DROP; - unsigned int dev_mtu; - - /* get routing information */ - if (is_multicast_ether_addr(ethhdr->h_dest)) { - orig_node = batadv_gw_get_selected_orig(bat_priv); - if (orig_node) - goto find_router; - } - - /* check for tt host - increases orig_node refcount. - * returns NULL in case of AP isolation - */ - orig_node = batadv_transtable_search(bat_priv, ethhdr->h_source, - ethhdr->h_dest); - -find_router: - /* find_router(): - * - if orig_node is NULL it returns NULL - * - increases neigh_nodes refcount if found. - */ - neigh_node = batadv_find_router(bat_priv, orig_node, NULL); - - if (!neigh_node) - goto out; - - if (batadv_skb_head_push(skb, sizeof(*unicast_packet)) < 0) - goto out; - - unicast_packet = (struct batadv_unicast_packet *)skb->data; - - unicast_packet->header.version = BATADV_COMPAT_VERSION; - /* batman packet type: unicast */ - unicast_packet->header.packet_type = BATADV_UNICAST; - /* set unicast ttl */ - unicast_packet->header.ttl = BATADV_TTL; - /* copy the destination for faster routing */ - memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN); - /* set the destination tt version number */ - unicast_packet->ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); - - /* inform the destination node that we are still missing a correct route - * for this client. The destination will receive this packet and will - * try to reroute it because the ttvn contained in the header is less - * than the current one - */ - if (batadv_tt_global_client_is_roaming(bat_priv, ethhdr->h_dest)) - unicast_packet->ttvn = unicast_packet->ttvn - 1; - - dev_mtu = neigh_node->if_incoming->net_dev->mtu; - if (atomic_read(&bat_priv->fragmentation) && - data_len + sizeof(*unicast_packet) > dev_mtu) { - /* send frag skb decreases ttl */ - unicast_packet->header.ttl++; - ret = batadv_frag_send_skb(skb, bat_priv, - neigh_node->if_incoming, - neigh_node->addr); - goto out; - } - - - if (batadv_send_skb_to_orig(skb, orig_node, NULL)) - ret = NET_RX_SUCCESS; - -out: - if (neigh_node) - batadv_neigh_node_free_ref(neigh_node); - if (orig_node) - batadv_orig_node_free_ref(orig_node); - if (ret == NET_RX_DROP) - kfree_skb(skb); - return ret; -} diff --git a/unicast.h b/unicast.h deleted file mode 100644 index 1c46e2e..0000000 --- a/unicast.h +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright (C) 2010-2012 B.A.T.M.A.N. contributors: - * - * Andreas Langer - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - */ - -#ifndef _NET_BATMAN_ADV_UNICAST_H_ -#define _NET_BATMAN_ADV_UNICAST_H_ - -#include "packet.h" - -#define BATADV_FRAG_TIMEOUT 10000 /* purge frag list entries after time in ms */ -#define BATADV_FRAG_BUFFER_SIZE 6 /* number of list elements in buffer */ - -int batadv_frag_reassemble_skb(struct sk_buff *skb, - struct batadv_priv *bat_priv, - struct sk_buff **new_skb); -void batadv_frag_list_free(struct list_head *head); -int batadv_unicast_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv); -int batadv_frag_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv, - struct batadv_hard_iface *hard_iface, - const uint8_t dstaddr[]); - -static inline int batadv_frag_can_reassemble(const struct sk_buff *skb, int mtu) -{ - const struct batadv_unicast_frag_packet *unicast_packet; - int uneven_correction = 0; - unsigned int merged_size; - - unicast_packet = (struct batadv_unicast_frag_packet *)skb->data; - - if (unicast_packet->flags & BATADV_UNI_FRAG_LARGETAIL) { - if (unicast_packet->flags & BATADV_UNI_FRAG_HEAD) - uneven_correction = 1; - else - uneven_correction = -1; - } - - merged_size = (skb->len - sizeof(*unicast_packet)) * 2; - merged_size += sizeof(struct batadv_unicast_packet) + uneven_correction; - - return merged_size <= mtu; -} - -#endif /* _NET_BATMAN_ADV_UNICAST_H_ */
Fragments arriving at their destination are buffered for later merge. Merged packets are passed to the main receive function as had they never been fragmented.
Fragments are forwarded without merging if the MTU of the outgoing interface is smaller than the size of the merged packet.
Signed-off-by: Martin Hundebøll martin@hundeboll.net --- Makefile.kbuild | 1 + fragmentation.c | 372 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ fragmentation.h | 31 +++++ main.c | 3 + main.h | 3 + originator.c | 12 +- packet.h | 29 +++++ routing.c | 59 +++++++++ routing.h | 2 + soft-interface.c | 4 + types.h | 33 +++++ 11 files changed, 548 insertions(+), 1 deletion(-) create mode 100644 fragmentation.c create mode 100644 fragmentation.h
diff --git a/Makefile.kbuild b/Makefile.kbuild index 9d622ad..49d9b49 100644 --- a/Makefile.kbuild +++ b/Makefile.kbuild @@ -23,6 +23,7 @@ batman-adv-y += bat_iv_ogm.o batman-adv-y += bitarray.o batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o batman-adv-y += debugfs.o +batman-adv-y += fragmentation.o batman-adv-y += gateway_client.o batman-adv-y += gateway_common.o batman-adv-y += hard-interface.o diff --git a/fragmentation.c b/fragmentation.c new file mode 100644 index 0000000..839bb54 --- /dev/null +++ b/fragmentation.c @@ -0,0 +1,372 @@ +/* Copyright (C) 2012 B.A.T.M.A.N. contributors: + * + * Martin Hundebøll martin@hundeboll.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "main.h" +#include "fragmentation.h" +#include "send.h" +#include "originator.h" +#include "routing.h" +#include "hard-interface.h" +#include "soft-interface.h" + + +/** + * batadv_frag_clear_list() - Delete entries in the fragment buffer list. + * @head: head of list with entries. + * + * Frees fragments in the passed hlist. Should be called with appropriate lock. + */ +static void batadv_frag_clear_list(struct hlist_head *head) +{ + struct batadv_frag_list_entry *entry; + struct hlist_node *node, *node_tmp; + + hlist_for_each_entry_safe(entry, node, node_tmp, head, list) { + hlist_del(node); + kfree_skb(entry->skb); + kfree(entry); + } +} + +/** + * batadv_frag_clear_orig() - Free fragments associated to an orig. + * @orig_node: Originator to free fragments from. + */ +void batadv_frag_clear_orig(struct batadv_orig_node *orig_node) +{ + uint8_t i; + + for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) { + spin_lock_bh(&orig_node->fragments[i].lock); + batadv_frag_clear_list(&orig_node->fragments[i].head); + orig_node->fragments[i].size = 0; + spin_unlock_bh(&orig_node->fragments[i].lock); + } +} + +/** + * batadv_frag_check_orig() - Free timed out fragments from an orig. + * @orig_node: originator to check fragments from. + * + * Check timestamp of every hlist in fragment table and delete all entries if + * timed out. + */ +void batadv_frag_check_orig(struct batadv_orig_node *orig_node) +{ + struct batadv_frag_table_entry *frags; + uint8_t i; + + for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) { + frags = &orig_node->fragments[i]; + spin_lock_bh(&frags->lock); + + if (!hlist_empty(&frags->head) && + batadv_has_timed_out(frags->timestamp, BATADV_FRAG_TIMEOUT)) + batadv_frag_clear_list(&frags->head); + + spin_unlock_bh(&frags->lock); + } +} + +/** + * batadv_frag_size_limit() - Maximum possible size of fragmented packet. + */ +static inline int batadv_frag_size_limit(void) +{ + int limit = BATADV_FRAG_MAX_FRAG_SIZE; + limit -= sizeof(struct batadv_frag_packet); + limit *= BATADV_FRAG_MAX_FRAGMENTS; + return limit; +} + +/** + * batadv_frag_init_list() - Check and prepare fragment buffer for new fragment. + * @frags: Entry in fragments table to init. + * @seqno: Sequence number of the received fragment. + * + * Make list ready for a fragment with sequence number "seqno". Delete existing + * entries if they have a "old" sequence number. + * + * Returns true if list is empty and caller can just insert the new fragment + * without searching for the right position. + * + * Caller must hold frags->lock. + */ +static bool batadv_frag_init_list(struct batadv_frag_table_entry *frags, + uint16_t seqno) +{ + if (frags->seqno == seqno) + return false; + + if (!hlist_empty(&frags->head)) + batadv_frag_clear_list(&frags->head); + + frags->size = 0; + frags->seqno = seqno; + + return true; +} + +/** + * batadv_frag_insert_packet() - Insert a fragment into an fragment buffer. + * @orig_node: Originator that the fragment was received from. + * @skb: skb to insert. + * @head: head to attach complete lists of fragments to. + * + * Insert a new fragment into the reverse ordered list in the right table entry. + * The hash table entry is cleared if "old" fragments exist in it. + * + * Returns true if skb is buffered, false on error. If the list has all the + * fragments needed to merge the packet, the list is moved to the passed head. + */ +static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node, + struct sk_buff *skb, + struct hlist_head *head) +{ + struct batadv_frag_table_entry *frags; + struct batadv_frag_list_entry *new = NULL, *curr = NULL; + struct batadv_frag_packet *packet; + struct hlist_node *node; + uint8_t bucket; + uint16_t seqno, hdr_size = sizeof(struct batadv_frag_packet); + + if (skb_linearize(skb) < 0) + goto err; + + packet = (struct batadv_frag_packet *)skb->data; + seqno = ntohs(packet->seqno); + bucket = seqno % BATADV_FRAG_BUFFER_COUNT; + + new = kmalloc(sizeof(*new), GFP_ATOMIC); + if (!new) + goto err; + + new->skb = skb; + new->no = packet->no; + + /* Select entry in the "fragment table" and delete any prior fragments + * with another sequence number. batadv_frag_init_list() returns true, + * if the list is empty at return. + */ + frags = &orig_node->fragments[bucket]; + spin_lock_bh(&frags->lock); + if (batadv_frag_init_list(frags, seqno)) { + hlist_add_head(&new->list, &frags->head); + frags->size = skb->len - hdr_size; + frags->timestamp = jiffies; + goto out; + } + + /* Find the position for the new fragment. */ + hlist_for_each_entry(curr, node, &frags->head, list) { + /* Drop packet if fragment already exists. */ + if (curr->no == new->no) + goto err_unlock; + + /* Order fragments from highest to lowest. */ + if (curr->no < new->no) { + hlist_add_before(&new->list, &curr->list); + frags->size += skb->len - hdr_size; + frags->timestamp = jiffies; + goto out; + } + } + + /* We reached the end of the list, so insert after 'curr'. */ + if (likely(curr)) { + hlist_add_after(&curr->list, &new->list); + frags->size += skb->len - hdr_size; + frags->timestamp = jiffies; + } + +out: + if (frags->size > batadv_frag_size_limit() || + ntohs(packet->total_size) > batadv_frag_size_limit()) { + /* Clear list if total size of either the list or the packet exceeds + * the maximum size of one merged packet. + */ + batadv_frag_clear_list(&frags->head); + frags->size = 0; + } else if (ntohs(packet->total_size) == frags->size) { + /* All fragments received. Hand over list to caller. */ + hlist_move_list(&frags->head, head); + frags->size = 0; + } + + spin_unlock_bh(&frags->lock); + return true; + +err_unlock: + spin_unlock_bh(&frags->lock); + kfree(new); +err: + return false; +} + +/** + * batadv_frag_merge_packets() - Merge a list of fragments. + * @head: head if list with fragments. + * @skb: Packet with total size of skb after merging. + * + * Expands the first skb in the list and copies the content of the remaining + * skb's into the expanded one. After doing so, it clears the list. + * + * Returns NULL on error. + */ +static struct sk_buff * +batadv_frag_merge_packets(struct hlist_head *head, struct sk_buff *skb) +{ + struct batadv_frag_packet *packet; + struct batadv_frag_list_entry *entry; + struct hlist_node *node; + struct sk_buff *skb_out = NULL; + int size, hdr_size = sizeof(struct batadv_frag_packet); + + /* Make sure incoming skb has non-bogus data. */ + packet = (struct batadv_frag_packet *)skb->data; + size = ntohs(packet->total_size); + if (size > batadv_frag_size_limit()) + goto free; + + /* Remove first entry, as this is the destination for the rest of the + * fragments. + */ + entry = hlist_entry(head->first, struct batadv_frag_list_entry, list); + hlist_del(&entry->list); + skb_out = entry->skb; + kfree(entry); + + /* Make room for the rest of the fragments. */ + if (pskb_expand_head(skb_out, 0, size - skb->len, GFP_ATOMIC) < 0) { + kfree_skb(skb_out); + goto free; + } + + /* Move the existing MAC header to just before the payload. (Override + * the fragment header.) + */ + skb_pull_rcsum(skb_out, hdr_size); + memmove(skb_out->data - ETH_HLEN, skb_mac_header(skb_out), ETH_HLEN); + skb_set_mac_header(skb_out, -ETH_HLEN); + skb_reset_network_header(skb_out); + skb_reset_transport_header(skb_out); + + /* Copy the payload of the each fragment into the last skb */ + hlist_for_each_entry(entry, node, head, list) { + size = entry->skb->len - hdr_size; + memcpy(skb_put(skb_out, size), entry->skb->data + hdr_size, size); + } + +free: + /* Locking is not needed, because 'head' is not part of any orig. */ + batadv_frag_clear_list(head); + return skb_out; +} + +/** + * batadv_frag_skb_buffer() - Buffer fragment for later merge. + * @skb: skb to buffer. + * @orig_node_src: Originator that the skb is received from. + * + * Add fragment to buffer and merge fragments if possible. + * + * There are three possible outcomes: 1) Packet is merged: Return true and + * set *skb to merged packet; 2) Packet is buffered: Return true and set *skb + * to NULL; 3) Error: Return false and leave skb as is. + */ +bool batadv_frag_skb_buffer(struct sk_buff **skb, + struct batadv_orig_node *orig_node_src) +{ + struct sk_buff *skb_out = NULL; + struct hlist_head head = HLIST_HEAD_INIT; + bool ret = false; + + /* Add packet to buffer and table entry if merge is possible. */ + if (!batadv_frag_insert_packet(orig_node_src, *skb, &head)) + goto out_err; + + /* Leave if more fragments are needed to merge. */ + if (hlist_empty(&head)) + goto out; + + skb_out = batadv_frag_merge_packets(&head, *skb); + if (!skb_out) + goto out_err; + +out: + *skb = skb_out; + ret = true; +out_err: + return ret; +} + +/** + * batadv_frag_skb_fwd() - Forward fragments that would exceed MTU when merged. + * @skb: skb to forward. + * @recv_if: Interface that the skb is received on. + * @orig_node_src: Originator that the skb is received from. + * + * Look up the next-hop of the fragments payload and check if the merged packet + * will exceed the MTU towards the next-hop. If so, the fragment is forwarded + * without merging it. + * + * Returns true if the fragment is consumed/forwarded, false otherwise. + */ +bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, + struct batadv_orig_node *orig_node_src) +{ + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct batadv_orig_node *orig_node_dst = NULL; + struct batadv_neigh_node *neigh_node = NULL; + struct batadv_frag_packet *packet; + uint16_t total_size; + bool ret = false; + + packet = (struct batadv_frag_packet *)skb->data; + orig_node_dst = batadv_orig_hash_find(bat_priv, packet->dest); + if (!orig_node_dst) + goto out; + + neigh_node = batadv_find_router(bat_priv, orig_node_dst, recv_if); + if (!neigh_node) + goto out; + + /* Forward the fragment, if the merged packet would be too big to + * be assembled. + */ + total_size = ntohs(packet->total_size) + ETH_HLEN; + if (total_size > neigh_node->if_incoming->net_dev->mtu) { + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD); + batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES, + skb->len + ETH_HLEN); + + packet->header.ttl--; + batadv_send_skb_packet(skb, neigh_node->if_incoming, + neigh_node->addr); + ret = true; + } + +out: + if (orig_node_dst) + batadv_orig_node_free_ref(orig_node_dst); + if (neigh_node) + batadv_neigh_node_free_ref(neigh_node); + return ret; +} diff --git a/fragmentation.h b/fragmentation.h new file mode 100644 index 0000000..16d1a62 --- /dev/null +++ b/fragmentation.h @@ -0,0 +1,31 @@ +/* Copyright (C) 2012 B.A.T.M.A.N. contributors: + * + * Martin Hundebøll martin@hundeboll.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _NET_BATMAN_ADV_FRAGMENTATION_H_ +#define _NET_BATMAN_ADV_FRAGMENTATION_H_ + +void batadv_frag_clear_orig(struct batadv_orig_node *orig); +void batadv_frag_check_orig(struct batadv_orig_node *orig); +bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, + struct batadv_orig_node *orig_node_src); +bool batadv_frag_skb_buffer(struct sk_buff **skb, + struct batadv_orig_node *orig_node); + +#endif /* _NET_BATMAN_ADV_FRAGMENTATION_H_ */ diff --git a/main.c b/main.c index a93328f..b633de1 100644 --- a/main.c +++ b/main.c @@ -32,6 +32,7 @@ #include "vis.h" #include "hash.h" #include "bat_algo.h" +#include "fragmentation.h"
/* List manipulations on hardif_list have to be rtnl_lock()'ed, @@ -280,6 +281,8 @@ static void batadv_recv_handler_init(void) batadv_rx_handler[BATADV_BCAST] = batadv_recv_bcast_packet; /* vis packet */ batadv_rx_handler[BATADV_VIS] = batadv_recv_vis_packet; + /* Fragmented packets */ + batadv_rx_handler[BATADV_FRAG] = batadv_recv_frag_packet; /* Translation table query (request or response) */ batadv_rx_handler[BATADV_TT_QUERY] = batadv_recv_tt_query; /* Roaming advertisement */ diff --git a/main.h b/main.h index f9a3115..d55200d 100644 --- a/main.h +++ b/main.h @@ -116,6 +116,9 @@ enum batadv_uev_type { };
#define BATADV_GW_THRESHOLD 50 +#define BATADV_FRAG_BUFFER_COUNT 8 +#define BATADV_FRAG_MAX_FRAGMENTS 16 +#define BATADV_FRAG_TIMEOUT 10000
/* Debug Messages */ #ifdef pr_fmt diff --git a/originator.c b/originator.c index 6ebf771..f3463a5 100644 --- a/originator.c +++ b/originator.c @@ -26,6 +26,7 @@ #include "hard-interface.h" #include "soft-interface.h" #include "bridge_loop_avoidance.h" +#include "fragmentation.h"
static void batadv_purge_orig(struct work_struct *work);
@@ -137,6 +138,7 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu)
spin_unlock_bh(&orig_node->neigh_list_lock);
+ batadv_frag_clear_orig(orig_node); batadv_tt_global_del_orig(orig_node->bat_priv, orig_node, "originator timed out");
@@ -192,7 +194,7 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, const uint8_t *addr) { struct batadv_orig_node *orig_node; - int size; + int size, i; int hash_added; unsigned long reset_time;
@@ -242,6 +244,12 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, size = bat_priv->num_ifaces * sizeof(uint8_t); orig_node->bcast_own_sum = kzalloc(size, GFP_ATOMIC);
+ for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) { + INIT_HLIST_HEAD(&orig_node->fragments[i].head); + spin_lock_init(&orig_node->fragments[i].lock); + orig_node->fragments[i].size = 0; + } + if (!orig_node->bcast_own_sum) goto free_bcast_own;
@@ -367,6 +375,8 @@ static void _batadv_purge_orig(struct batadv_priv *bat_priv) batadv_orig_node_free_ref(orig_node); continue; } + + batadv_frag_check_orig(orig_node); } spin_unlock_bh(list_lock); } diff --git a/packet.h b/packet.h index ce6e502..5f7ea63 100644 --- a/packet.h +++ b/packet.h @@ -58,6 +58,15 @@ enum batadv_vis_packettype { BATADV_VIS_TYPE_CLIENT_UPDATE = 1, };
+/** + * enum batadv_frag_flags - flags for fragments. + * @BATADV_FRAG_NO_FLAGS: No flags are set. + * @BATADV_FRAG_LAST: Last fragment in a set of fragments. + */ +enum batadv_frag_flags { + BATADV_FRAG_LAST = BIT(0), +}; + /* TT_QUERY subtypes */ #define BATADV_TT_QUERY_TYPE_MASK 0x3
@@ -155,6 +164,26 @@ struct batadv_unicast_packet { uint8_t dest[ETH_ALEN]; } __packed;
+/** + * struct batadv_frag_packet - Network structure for fragments. + * @header: Common batman-adv header with type, compatibility version, and ttl. + * @dest: Final destination used when routing fragments. + * @orig: Originator of the fragment used when merging the packet. + * @no: Fragment number within this sequence. + * @reserved: Unused + * @seqno: Sequence identification. + * @total_size: Size of the merged packet. + */ +struct batadv_frag_packet { + struct batadv_header header; + uint8_t dest[ETH_ALEN]; + uint8_t orig[ETH_ALEN]; + uint8_t no:4; + uint8_t reserved:4; + __be16 seqno; + __be16 total_size; +} __packed; + struct batadv_bcast_packet { struct batadv_header header; uint8_t reserved; diff --git a/routing.c b/routing.c index ca4847e..42d980b 100644 --- a/routing.c +++ b/routing.c @@ -27,6 +27,7 @@ #include "originator.h" #include "vis.h" #include "bridge_loop_avoidance.h" +#include "fragmentation.h"
static int batadv_route_unicast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); @@ -960,6 +961,64 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, return batadv_route_unicast_packet(skb, recv_if); }
+/** + * batadv_recv_frag_packet() - Process received fragment. + * @skb: The received fragment. + * @recv_if: Interface that the skb is received on. + * + * This function does one of the three following things: 1) Forward fragment, if + * the assembled packet will exceed our MTU; 2) Buffer fragment, if we till + * lack further fragments; 3) Merge fragments, if we have all needed parts. + * + * Returns NET_RX_DROP if the skb is not consumed, NET_RX_SUCCESS otherwise. + */ +int batadv_recv_frag_packet(struct sk_buff *skb, + struct batadv_hard_iface *recv_if) +{ + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct batadv_orig_node *orig_node_src = NULL; + struct batadv_frag_packet *packet; + int ret = NET_RX_DROP; + + if (batadv_check_unicast_packet(skb, sizeof(*packet)) < 0) + goto out; + + packet = (struct batadv_frag_packet *)skb->data; + orig_node_src = batadv_orig_hash_find(bat_priv, packet->orig); + if (!orig_node_src) + goto out; + + /* Route the fragment if it is not for us and too big to be merged. */ + if (!batadv_is_my_mac(packet->dest) && + batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) { + ret = NET_RX_SUCCESS; + goto out; + } + + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_RX); + batadv_add_counter(bat_priv, BATADV_CNT_FRAG_RX_BYTES, + skb->len + ETH_HLEN); + + /* Add fragment to buffer and merge if possible. */ + if (!batadv_frag_skb_buffer(&skb, orig_node_src)) + goto out; + + /* Deliver merged packet to the appropriate handler, if it was + * merged + */ + if (skb) + batadv_batman_skb_recv(skb, recv_if->net_dev, + &recv_if->batman_adv_ptype, NULL); + + ret = NET_RX_SUCCESS; + +out: + if (orig_node_src) + batadv_orig_node_free_ref(orig_node_src); + + return ret; +} + int batadv_recv_bcast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if) { diff --git a/routing.h b/routing.h index 9262279..2eb1578 100644 --- a/routing.h +++ b/routing.h @@ -33,6 +33,8 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); int batadv_recv_ucast_frag_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); +int batadv_recv_frag_packet(struct sk_buff *skb, + struct batadv_hard_iface *iface); int batadv_recv_bcast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); int batadv_recv_vis_packet(struct sk_buff *skb, diff --git a/soft-interface.c b/soft-interface.c index fbec8a4..df0bedc 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -546,6 +546,10 @@ static const struct { { "mgmt_tx_bytes" }, { "mgmt_rx" }, { "mgmt_rx_bytes" }, + { "frag_rx" }, + { "frag_rx_bytes" }, + { "frag_fwd" }, + { "frag_fwd_bytes" }, { "tt_request_tx" }, { "tt_request_rx" }, { "tt_response_tx" }, diff --git a/types.h b/types.h index 2ed82ca..477f87a 100644 --- a/types.h +++ b/types.h @@ -45,6 +45,34 @@ struct batadv_hard_iface { };
/** + * struct batadv_frag_table_entry - Head in the fragment buffer table. + * @head: Head of list with fragments. + * @lock: Lock to protect the list of fragments. + * @timestamp: Time (jiffie) of last received fragment. + * @seqno: Sequence number of the fragments in the list. + * @size: Accumulated size of packets in list. + */ +struct batadv_frag_table_entry { + struct hlist_head head; + spinlock_t lock; /* Protects head */ + unsigned long timestamp; + uint16_t seqno; + uint16_t size; +}; + +/** + * struct batadv_frag_list_entry - Entry in a list of fragments. + * @list: List node information + * @skb: Fragment + * @no: Fragment number in the set. + */ +struct batadv_frag_list_entry { + struct hlist_node list; + struct sk_buff *skb; + uint8_t no; +}; + +/** * struct batadv_orig_node - structure for orig_list maintaining nodes of mesh * @primary_addr: hosts primary interface address * @last_seen: when last packet from this node was received @@ -105,6 +133,7 @@ struct batadv_orig_node { spinlock_t tt_list_lock; /* protects tt_list */ atomic_t bond_candidates; struct list_head bond_list; + struct batadv_frag_table_entry fragments[BATADV_FRAG_BUFFER_COUNT]; };
struct batadv_gw_node { @@ -156,6 +185,10 @@ enum batadv_counters { BATADV_CNT_MGMT_TX_BYTES, BATADV_CNT_MGMT_RX, BATADV_CNT_MGMT_RX_BYTES, + BATADV_CNT_FRAG_RX, + BATADV_CNT_FRAG_RX_BYTES, + BATADV_CNT_FRAG_FWD, + BATADV_CNT_FRAG_FWD_BYTES, BATADV_CNT_TT_REQUEST_TX, BATADV_CNT_TT_REQUEST_RX, BATADV_CNT_TT_RESPONSE_TX,
Non-broadcast packets larger than MTU are fragmented and sent with an encapsulating header. Up to 16 fragments are supported, which are sent in reverse order on the wire.
Signed-off-by: Martin Hundebøll martin@hundeboll.net --- fragmentation.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ fragmentation.h | 3 ++ main.h | 1 + packet.h | 9 ----- send.c | 26 +++++++++--- soft-interface.c | 7 ++++ types.h | 3 ++ 7 files changed, 155 insertions(+), 15 deletions(-)
diff --git a/fragmentation.c b/fragmentation.c index 839bb54..77a8a0c 100644 --- a/fragmentation.c +++ b/fragmentation.c @@ -370,3 +370,124 @@ out: batadv_neigh_node_free_ref(neigh_node); return ret; } + +/** + * batadv_frag_create() - Create a fragment from skb. + * @skb: skb to create fragment from. + * @frag_head: header to use in new fragment. + * @mtu: Size of new fragment. + * + * Split the passed skb into two fragments: A new one with size matching the + * passed mtu and the old one with the rest. The new skb contains data from the + * tail of the old skb. + * + * Returns the new fragment, NULL on error. + */ +static struct sk_buff *batadv_frag_create(struct sk_buff *skb, + struct batadv_frag_packet *frag_head, + unsigned int mtu) +{ + struct sk_buff *skb_fragment; + unsigned header_size = sizeof(*frag_head); + unsigned fragment_size = mtu - header_size - ETH_HLEN; + + skb_fragment = dev_alloc_skb(mtu); + if (!skb_fragment) + goto out_err; + + /* Eat the last mtu-bytes of the skb */ + skb_reserve(skb_fragment, header_size); + skb_split(skb, skb_fragment, skb->len - fragment_size); + + /* Add the header */ + skb_push(skb_fragment, header_size); + memcpy(skb_fragment->data, frag_head, header_size); + + return skb_fragment; + +out_err: + return NULL; +} + +/** + * batadv_frag_send_packet() - Create up to 16 fragments from the passed skb. + * @skb: skb to create fragments from. + * @orig_node: Final destination of the created fragments. + * @neigh_node: Next-hop of the created fragments. + * + * Returns true on success, false otherwise. + */ +bool batadv_frag_send_packet(struct sk_buff *skb, + struct batadv_orig_node *orig_node, + struct batadv_neigh_node *neigh_node) +{ + struct batadv_priv *bat_priv; + struct batadv_hard_iface *primary_if; + struct batadv_frag_packet frag_header; + struct sk_buff *skb_fragment; + unsigned mtu = neigh_node->if_incoming->net_dev->mtu; + unsigned header_size = sizeof(frag_header); + unsigned max_fragment_size, max_packet_size; + + /* To avoid merge and refragmentation at next-hops we never send fragments + * larger than BATADV_FRAG_MAX_FRAG_SIZE + */ + mtu = min_t(int, mtu, BATADV_FRAG_MAX_FRAG_SIZE); + max_fragment_size = (mtu - header_size - ETH_HLEN); + max_packet_size = max_fragment_size*BATADV_FRAG_MAX_FRAGMENTS; + + /* Don't even try to fragment, if we need more than 16 fragments */ + if (skb->len > max_packet_size) + goto out_err; + + bat_priv = orig_node->bat_priv; + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out_err; + + /* Create one header to be copied to all fragments */ + frag_header.header.packet_type = BATADV_FRAG; + frag_header.header.version = BATADV_COMPAT_VERSION; + frag_header.header.ttl = BATADV_TTL; + frag_header.seqno = htons(atomic_inc_return(&bat_priv->frag_seqno)); + frag_header.reserved = 0; + frag_header.no = 0; + frag_header.total_size = htons(skb->len); + memcpy(frag_header.orig, primary_if->net_dev->dev_addr, ETH_ALEN); + memcpy(frag_header.dest, orig_node->orig, ETH_ALEN); + + /* Eat and send fragments from the tail of skb */ + while (skb->len > max_fragment_size) { + skb_fragment = batadv_frag_create(skb, &frag_header, mtu); + if (!skb_fragment) + goto out_err; + + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX); + batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES, + skb_fragment->len + ETH_HLEN); + batadv_send_skb_packet(skb_fragment, neigh_node->if_incoming, + neigh_node->addr); + frag_header.no++; + + /* The initial check in this function should cover this case */ + if (frag_header.no == BATADV_FRAG_MAX_FRAGMENTS - 1) + goto out_err; + } + + /* Make room for the fragment header. */ + if (batadv_skb_head_push(skb, header_size) < 0 || + pskb_expand_head(skb, header_size + ETH_HLEN, 0, GFP_ATOMIC) < 0) + goto out_err; + + memcpy(skb->data, &frag_header, header_size); + + /* Send the last fragment */ + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX); + batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES, + skb->len + ETH_HLEN); + batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + + return true; +out_err: + return false; +} diff --git a/fragmentation.h b/fragmentation.h index 16d1a62..ed85d97 100644 --- a/fragmentation.h +++ b/fragmentation.h @@ -27,5 +27,8 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, struct batadv_orig_node *orig_node_src); bool batadv_frag_skb_buffer(struct sk_buff **skb, struct batadv_orig_node *orig_node); +bool batadv_frag_send_packet(struct sk_buff *skb, + struct batadv_orig_node *orig_node, + struct batadv_neigh_node *neigh_node);
#endif /* _NET_BATMAN_ADV_FRAGMENTATION_H_ */ diff --git a/main.h b/main.h index d55200d..8b1daf7 100644 --- a/main.h +++ b/main.h @@ -118,6 +118,7 @@ enum batadv_uev_type { #define BATADV_GW_THRESHOLD 50 #define BATADV_FRAG_BUFFER_COUNT 8 #define BATADV_FRAG_MAX_FRAGMENTS 16 +#define BATADV_FRAG_MAX_FRAG_SIZE 1400 #define BATADV_FRAG_TIMEOUT 10000
/* Debug Messages */ diff --git a/packet.h b/packet.h index 5f7ea63..314f9db 100644 --- a/packet.h +++ b/packet.h @@ -58,15 +58,6 @@ enum batadv_vis_packettype { BATADV_VIS_TYPE_CLIENT_UPDATE = 1, };
-/** - * enum batadv_frag_flags - flags for fragments. - * @BATADV_FRAG_NO_FLAGS: No flags are set. - * @BATADV_FRAG_LAST: Last fragment in a set of fragments. - */ -enum batadv_frag_flags { - BATADV_FRAG_LAST = BIT(0), -}; - /* TT_QUERY subtypes */ #define BATADV_TT_QUERY_TYPE_MASK 0x3
diff --git a/send.c b/send.c index f894903..eb9e85d 100644 --- a/send.c +++ b/send.c @@ -27,6 +27,7 @@ #include "gateway_common.h" #include "gateway_client.h" #include "originator.h" +#include "fragmentation.h"
static void batadv_send_outstanding_bcast_packet(struct work_struct *work);
@@ -84,18 +85,31 @@ bool batadv_send_skb_to_orig(struct sk_buff *skb, { struct batadv_priv *bat_priv = orig_node->bat_priv; struct batadv_neigh_node *neigh_node; + bool ret = false;
/* batadv_find_router() increases neigh_nodes refcount if found. */ neigh_node = batadv_find_router(bat_priv, orig_node, recv_if); if (!neigh_node) - return false; - - /* route it */ - batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + goto out;
- batadv_neigh_node_free_ref(neigh_node); + /* Check if the skb is too large to send in one piece and fragment + * it if needed. + */ + if (atomic_read(&bat_priv->fragmentation) && + skb->len + ETH_HLEN > neigh_node->if_incoming->net_dev->mtu) { + /* Fragment and send packet. */ + ret = batadv_frag_send_packet(skb, orig_node, neigh_node); + } else { + /* Send in one piece. */ + batadv_send_skb_packet(skb, neigh_node->if_incoming, + neigh_node->addr); + ret = true; + }
- return true; +out: + if (neigh_node) + batadv_neigh_node_free_ref(neigh_node); + return ret; }
int batadv_send_skb_unicast(struct batadv_priv *bat_priv, struct sk_buff *skb) diff --git a/soft-interface.c b/soft-interface.c index df0bedc..c21f1fc 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -382,6 +382,7 @@ struct net_device *batadv_softif_create(const char *name) { struct net_device *soft_iface; struct batadv_priv *bat_priv; + uint32_t random_seqno; int ret; size_t cnt_len = sizeof(uint64_t) * BATADV_CNT_NUM;
@@ -434,6 +435,10 @@ struct net_device *batadv_softif_create(const char *name) bat_priv->tt.last_changeset_len = 0; bat_priv->tt.poss_change = false;
+ /* randomize initial seqno to avoid collision */ + get_random_bytes(&random_seqno, sizeof(random_seqno)); + atomic_set(&bat_priv->frag_seqno, random_seqno); + bat_priv->primary_if = NULL; bat_priv->num_ifaces = 0;
@@ -546,6 +551,8 @@ static const struct { { "mgmt_tx_bytes" }, { "mgmt_rx" }, { "mgmt_rx_bytes" }, + { "frag_tx" }, + { "frag_tx_bytes" }, { "frag_rx" }, { "frag_rx_bytes" }, { "frag_fwd" }, diff --git a/types.h b/types.h index 477f87a..0450b57 100644 --- a/types.h +++ b/types.h @@ -185,6 +185,8 @@ enum batadv_counters { BATADV_CNT_MGMT_TX_BYTES, BATADV_CNT_MGMT_RX, BATADV_CNT_MGMT_RX_BYTES, + BATADV_CNT_FRAG_TX, + BATADV_CNT_FRAG_TX_BYTES, BATADV_CNT_FRAG_RX, BATADV_CNT_FRAG_RX_BYTES, BATADV_CNT_FRAG_FWD, @@ -266,6 +268,7 @@ struct batadv_priv { atomic_t aggregated_ogms; /* boolean */ atomic_t bonding; /* boolean */ atomic_t fragmentation; /* boolean */ + atomic_t frag_seqno; /* uint */ atomic_t ap_isolation; /* boolean */ atomic_t bridge_loop_avoidance; /* boolean */ atomic_t vis_mode; /* VIS_TYPE_* */
The size of the local translation table should not increase the maximum packet size, which is determined by the lowest MTU of hard interfaces and the maximum number of fragments.
This patch adds a check before adding new entries to the table and a new check to remove entries if the MTU decreases.
Signed-off-by: Martin Hundebøll martin@hundeboll.net --- fragmentation.h | 22 +++++++ hard-interface.c | 26 +++++--- soft-interface.c | 8 ++- translation-table.c | 186 +++++++++++++++++++++++++++++++++++++++++++--------- translation-table.h | 3 +- types.h | 3 + 6 files changed, 205 insertions(+), 43 deletions(-)
diff --git a/fragmentation.h b/fragmentation.h index ed85d97..0b95008 100644 --- a/fragmentation.h +++ b/fragmentation.h @@ -31,4 +31,26 @@ bool batadv_frag_send_packet(struct sk_buff *skb, struct batadv_orig_node *orig_node, struct batadv_neigh_node *neigh_node);
+/** + * batadv_frag_max_packet_size() - Get maximum packet size with fragmentation. + * @bat_priv: Batman-adv private data. + * + * Returns the maximum size of one unicast packet if fragmentation is disabled + * or maximum size of one fragmented packet if fragmentation is enabled. + */ +static inline int batadv_frag_max_packet_size(struct batadv_priv *bat_priv) +{ + int mtu = atomic_read(&bat_priv->min_mtu); + + /* Return mtu for one packet if fragmentation is disabled. */ + if (!atomic_read(&bat_priv->fragmentation)) + return mtu - ETH_HLEN; + + /* Determine max size with multiple fragments */ + mtu = min_t(int, mtu, BATADV_FRAG_MAX_FRAG_SIZE); + mtu -= sizeof(struct batadv_frag_packet); + mtu -= ETH_HLEN; + return mtu*BATADV_FRAG_MAX_FRAGMENTS; +} + #endif /* _NET_BATMAN_ADV_FRAGMENTATION_H_ */ diff --git a/hard-interface.c b/hard-interface.c index d112fd6..80b9465 100644 --- a/hard-interface.c +++ b/hard-interface.c @@ -180,16 +180,11 @@ static void batadv_check_known_mac_addr(const struct net_device *net_dev)
int batadv_hardif_min_mtu(struct net_device *soft_iface) { - const struct batadv_priv *bat_priv = netdev_priv(soft_iface); + struct batadv_priv *bat_priv = netdev_priv(soft_iface); const struct batadv_hard_iface *hard_iface; - /* allow big frames if all devices are capable to do so - * (have MTU > 1500 + BAT_HEADER_LEN) - */ int min_mtu = ETH_DATA_LEN;
- if (atomic_read(&bat_priv->fragmentation)) - goto out; - + /* Check if any interface has lower MTU than the current minimum MTU. */ rcu_read_lock(); list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { if ((hard_iface->if_status != BATADV_IF_ACTIVE) && @@ -204,8 +199,16 @@ int batadv_hardif_min_mtu(struct net_device *soft_iface) min_mtu); } rcu_read_unlock(); -out: - return min_mtu; + + /* Update current minimum MTU. */ + atomic_set(&bat_priv->min_mtu, min_mtu); + + if (atomic_read(&bat_priv->fragmentation)) + /* Allow big frames if fragmentation is enabled. */ + return ETH_DATA_LEN; + else + /* Limit frame size if fragmentation is disabled. */ + return min_mtu; }
/* adjusts the MTU if a new interface with a smaller MTU appeared. */ @@ -216,6 +219,11 @@ void batadv_update_min_mtu(struct net_device *soft_iface) min_mtu = batadv_hardif_min_mtu(soft_iface); if (soft_iface->mtu != min_mtu) soft_iface->mtu = min_mtu; + + /* Check if the local translate table should be cleaned up to match a + * new (and smaller) MTU. + */ + batadv_tt_check_mtu(soft_iface); }
static void diff --git a/soft-interface.c b/soft-interface.c index c21f1fc..4a9dfbd 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -171,8 +171,10 @@ static int batadv_interface_tx(struct sk_buff *skb, if (batadv_bla_tx(bat_priv, skb, vid)) goto dropped;
- /* Register the client MAC in the transtable */ - batadv_tt_local_add(soft_iface, ethhdr->h_source, skb->skb_iif); + /* Register the client MAC in the transtable. Drop packet if the entry + * wasn't added. */ + if (!batadv_tt_local_add(soft_iface, ethhdr->h_source, skb->skb_iif)) + goto dropped;
/* don't accept stp packets. STP does not help in meshes. * better use the bridge loop avoidance ... @@ -422,12 +424,14 @@ struct net_device *batadv_softif_create(const char *name) atomic_set(&bat_priv->fragmentation, 1); atomic_set(&bat_priv->bcast_queue_left, BATADV_BCAST_QUEUE_LEN); atomic_set(&bat_priv->batman_queue_left, BATADV_BATMAN_QUEUE_LEN); + atomic_set(&bat_priv->min_mtu, ETH_DATA_LEN);
atomic_set(&bat_priv->mesh_state, BATADV_MESH_INACTIVE); atomic_set(&bat_priv->bcast_seqno, 1); atomic_set(&bat_priv->tt.vn, 0); atomic_set(&bat_priv->tt.local_changes, 0); atomic_set(&bat_priv->tt.ogm_append_cnt, 0); + atomic_set(&bat_priv->tt.reduced_table, 0); #ifdef CONFIG_BATMAN_ADV_BLA atomic_set(&bat_priv->bla.num_requests, 0); #endif diff --git a/translation-table.c b/translation-table.c index 2cee8b2..2b6c42b 100644 --- a/translation-table.c +++ b/translation-table.c @@ -26,6 +26,7 @@ #include "originator.h" #include "routing.h" #include "bridge_loop_avoidance.h" +#include "fragmentation.h"
#include <linux/crc16.h>
@@ -237,7 +238,7 @@ static int batadv_tt_local_init(struct batadv_priv *bat_priv) return 0; }
-void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, +bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, int ifindex) { struct batadv_priv *bat_priv = netdev_priv(soft_iface); @@ -246,7 +247,8 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, struct hlist_head *head; struct hlist_node *node; struct batadv_tt_orig_list_entry *orig_entry; - int hash_added; + int hash_added, size = sizeof(struct batadv_tt_query_packet); + bool ret = false;
tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr);
@@ -254,6 +256,19 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, tt_local_entry->last_seen = jiffies; /* possibly unset the BATADV_TT_CLIENT_PENDING flag */ tt_local_entry->common.flags &= ~BATADV_TT_CLIENT_PENDING; + ret = true; + goto out; + } + + /* Ignore it if we cannot send it in a full table response. */ + size += batadv_tt_len(atomic_read(&bat_priv->tt.local_entry_num) + 1); + if (size > batadv_frag_max_packet_size(bat_priv)) { + if (!net_ratelimit()) + goto out; + + batadv_info(soft_iface, + "Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n", + size, batadv_frag_max_packet_size(bat_priv), addr); goto out; }
@@ -318,11 +333,16 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, tt_global_entry->common.flags |= BATADV_TT_CLIENT_ROAM; tt_global_entry->roam_at = jiffies; } + + ret = true; + out: if (tt_local_entry) batadv_tt_local_entry_free_ref(tt_local_entry); if (tt_global_entry) batadv_tt_global_entry_free_ref(tt_global_entry); + + return ret; }
static void batadv_tt_realloc_packet_buff(unsigned char **packet_buff, @@ -520,8 +540,9 @@ out: batadv_tt_local_entry_free_ref(tt_local_entry); }
-static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv, - struct hlist_head *head) +static void +batadv_tt_local_purge_list(struct batadv_priv *bat_priv, + struct hlist_head *head) { struct batadv_tt_local_entry *tt_local_entry; struct batadv_tt_common_entry *tt_common_entry; @@ -544,7 +565,8 @@ static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv, continue;
batadv_tt_local_set_pending(bat_priv, tt_local_entry, - BATADV_TT_CLIENT_DEL, "timed out"); + BATADV_TT_CLIENT_DEL, + "timed out"); } }
@@ -1448,7 +1470,6 @@ static int batadv_tt_global_valid(const void *entry_ptr, static struct sk_buff * batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn, struct batadv_hashtable *hash, - struct batadv_hard_iface *primary_if, int (*valid_cb)(const void *, const void *), void *cb_data) { @@ -1458,17 +1479,11 @@ batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn, struct hlist_node *node; struct hlist_head *head; struct sk_buff *skb = NULL; - uint16_t tt_tot, tt_count; + uint16_t tt_count = 0; ssize_t tt_query_size = sizeof(struct batadv_tt_query_packet); uint32_t i; size_t len;
- if (tt_query_size + tt_len > primary_if->soft_iface->mtu) { - tt_len = primary_if->soft_iface->mtu - tt_query_size; - tt_len -= tt_len % sizeof(struct batadv_tt_change); - } - tt_tot = tt_len / sizeof(struct batadv_tt_change); - len = tt_query_size + tt_len; skb = dev_alloc_skb(len + ETH_HLEN); if (!skb) @@ -1479,7 +1494,6 @@ batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn, tt_response->ttvn = ttvn;
tt_change = (struct batadv_tt_change *)(skb->data + tt_query_size); - tt_count = 0;
rcu_read_lock(); for (i = 0; i < hash->size; i++) { @@ -1487,9 +1501,6 @@ batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
hlist_for_each_entry_rcu(tt_common_entry, node, head, hash_entry) { - if (tt_count == tt_tot) - break; - if ((valid_cb) && (!valid_cb(tt_common_entry, cb_data))) continue;
@@ -1582,9 +1593,9 @@ static bool batadv_send_other_tt_response(struct batadv_priv *bat_priv, struct batadv_tt_query_packet *tt_request) { + struct batadv_hard_iface *iface; struct batadv_orig_node *req_dst_orig_node = NULL; struct batadv_orig_node *res_dst_orig_node = NULL; - struct batadv_hard_iface *primary_if = NULL; uint8_t orig_ttvn, req_ttvn, ttvn; int ret = false; unsigned char *tt_buff; @@ -1609,8 +1620,8 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv, if (!res_dst_orig_node) goto out;
- primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) + iface = batadv_primary_if_get_selected(bat_priv); + if (!iface) goto out;
orig_ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn); @@ -1658,9 +1669,20 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv, tt_len *= sizeof(struct batadv_tt_change); ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn);
+ /* Don't send the response, if larger than fragmented packet. */ + len = sizeof(*tt_response) + tt_len; + if (len > batadv_frag_max_packet_size(bat_priv)) { + if (!net_ratelimit()) + goto out; + + batadv_info(iface->soft_iface, + "Ignoring TT_REQUEST from %pM; Response size exceeds max packet size.\n", + res_dst_orig_node->orig); + goto out; + } + skb = batadv_tt_response_fill_table(tt_len, ttvn, bat_priv->tt.global_hash, - primary_if, batadv_tt_global_valid, req_dst_orig_node); if (!skb) @@ -1697,8 +1719,6 @@ out: batadv_orig_node_free_ref(res_dst_orig_node); if (req_dst_orig_node) batadv_orig_node_free_ref(req_dst_orig_node); - if (primary_if) - batadv_hardif_free_ref(primary_if); if (!ret) kfree_skb(skb); return ret; @@ -1775,9 +1795,14 @@ batadv_send_my_tt_response(struct batadv_priv *bat_priv, tt_len *= sizeof(struct batadv_tt_change); ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn);
+ /* If we have an inconsistent table due to reduced MTU, we + * increase ttvn temporarily. + * */ + if (atomic_read(&bat_priv->tt.reduced_table)) + ttvn++; + skb = batadv_tt_response_fill_table(tt_len, ttvn, bat_priv->tt.local_hash, - primary_if, batadv_tt_local_valid_entry, NULL); if (!skb) @@ -2195,8 +2220,73 @@ out: return changed_num; }
+/** + * batadv_tt_local_test_pending() - Callback to test pending flag of an entry. + * @bat_priv: Batman-adv private data. + * @entry: Local entry to be deleted. + * @data: Not used + * + * This callback is made to be passed as a purge function to + * batadv_tt_local_purge_pending(), where it is called with every entry. It + * checks if the entry is flagged as pending and frees it if so. + */ +static int batadv_tt_local_test_pending(struct batadv_priv *bat_priv, + void *entry, void *data) +{ + struct batadv_tt_common_entry *tt_common_entry = entry; + + if (!(tt_common_entry->flags & BATADV_TT_CLIENT_PENDING)) + return 0; + + batadv_dbg(BATADV_DBG_TT, bat_priv, + "Deleting local tt entry (%pM): pending\n", + tt_common_entry->addr); + + return 1; +} + +/** + * batadv_tt_local_test_timeout() - Callback to test timeout of an entry. + * @bat_priv: Batman-adv private data. + * @entry: Local entry to be deleted. + * @data: Pointer to timeout in milliseconds to check the entry with. + * + * This callback is made to be passed as a purge function to + * batadv_tt_local_purge_pending(), where it is called with every entry. It + * checks if the entry is timed out and frees it immediately, instead of + * flagging it as pending, and then adds the removal to the changeset. + */ +static int batadv_tt_local_test_timeout(struct batadv_priv *bat_priv, + void *entry, void *data) +{ + struct batadv_tt_common_entry *tt_common_entry = entry; + struct batadv_tt_local_entry *tt_local_entry; + int *timeout = data; + + if (tt_common_entry->flags & BATADV_TT_CLIENT_PENDING) + return 0; + + tt_local_entry = container_of(tt_common_entry, + struct batadv_tt_local_entry, common); + + if (!batadv_has_timed_out(tt_local_entry->last_seen, *timeout)) + return 0; + + batadv_dbg(BATADV_DBG_TT, bat_priv, + "Deleting local tt entry (%pM): forced\n", + tt_common_entry->addr); + + batadv_tt_local_event(bat_priv, tt_local_entry->common.addr, + BATADV_TT_CLIENT_DEL); + + return 1; +} + /* Purge out all the tt local entries marked with BATADV_TT_CLIENT_PENDING */ -static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv) +static void batadv_tt_local_purge_pending(struct batadv_priv *bat_priv, + int (test)(struct batadv_priv *, + void *, void *), + void *data) { struct batadv_hashtable *hash = bat_priv->tt.local_hash; struct batadv_tt_common_entry *tt_common; @@ -2216,13 +2306,9 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv) spin_lock_bh(list_lock); hlist_for_each_entry_safe(tt_common, node, node_tmp, head, hash_entry) { - if (!(tt_common->flags & BATADV_TT_CLIENT_PENDING)) + if (!test(bat_priv, tt_common, data)) continue;
- batadv_dbg(BATADV_DBG_TT, bat_priv, - "Deleting local tt entry (%pM): pending\n", - tt_common->addr); - atomic_dec(&bat_priv->tt.local_entry_num); hlist_del_rcu(node); tt_local = container_of(tt_common, @@ -2249,11 +2335,13 @@ static int batadv_tt_commit_changes(struct batadv_priv *bat_priv,
/* all reset entries have to be counted as local entries */ atomic_add(changed_num, &bat_priv->tt.local_entry_num); - batadv_tt_local_purge_pending_clients(bat_priv); + batadv_tt_local_purge_pending(bat_priv, + batadv_tt_local_test_pending, NULL); bat_priv->tt.local_crc = batadv_tt_local_crc(bat_priv);
/* Increment the TTVN only once per OGM interval */ atomic_inc(&bat_priv->tt.vn); + atomic_set(&bat_priv->tt.reduced_table, 0); batadv_dbg(BATADV_DBG_TT, bat_priv, "Local changes committed, updating to ttvn %u\n", (uint8_t)atomic_read(&bat_priv->tt.vn)); @@ -2430,3 +2518,39 @@ bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv, out: return ret; } + +/** + * batadv_tt_check_mtu() - Make local table fit a fragmented packet. + * @bat_priv: Private data + * @mtu: Lowest MTU of local interfaces. + * + * Remove entries older than 'timeout' and half timeout if more entries needs + * to be removed. + */ +void batadv_tt_check_mtu(struct net_device *soft_iface) +{ + struct batadv_priv *bat_priv = netdev_priv(soft_iface); + int max_size = batadv_frag_max_packet_size(bat_priv); + int num = atomic_read(&bat_priv->tt.local_entry_num); + int hdr_size = sizeof(struct batadv_tt_query_packet); + int table_size = hdr_size + batadv_tt_len(num); + int timeout = BATADV_TT_LOCAL_TIMEOUT/2; + + while (table_size > max_size) { + batadv_tt_local_purge_pending(bat_priv, + batadv_tt_local_test_timeout, + &timeout); + + num = atomic_read(&bat_priv->tt.local_entry_num); + table_size = hdr_size + batadv_tt_len(num); + timeout /= 2; + atomic_set(&bat_priv->tt.reduced_table, 1); + + if (!net_ratelimit()) + continue; + + batadv_info(soft_iface, + "Forced to purge local tt entries to fit new fragment MTU (%i)\n", + max_size); + } +} diff --git a/translation-table.h b/translation-table.h index 811fffd..380ad86 100644 --- a/translation-table.h +++ b/translation-table.h @@ -22,7 +22,7 @@
int batadv_tt_len(int changes_num); int batadv_tt_init(struct batadv_priv *bat_priv); -void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, +bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, int ifindex); void batadv_tt_local_remove(struct batadv_priv *bat_priv, const uint8_t *addr, const char *message, @@ -62,5 +62,6 @@ bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv, bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, const unsigned char *addr); +void batadv_tt_check_mtu(struct net_device *soft_iface);
#endif /* _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ */ diff --git a/types.h b/types.h index 0450b57..fa17288 100644 --- a/types.h +++ b/types.h @@ -211,6 +211,7 @@ enum batadv_counters { * @changes_list: tracks tt local changes within an originator interval * @req_list: list of pending tt_requests * @local_crc: Checksum of the local table, recomputed before sending a new OGM + * @reduced_table: Flag to make own tt_responses carry an increased ttvn. */ struct batadv_priv_tt { atomic_t vn; @@ -231,6 +232,7 @@ struct batadv_priv_tt { int16_t last_changeset_len; spinlock_t last_changeset_lock; /* protects last_changeset */ struct delayed_work work; + atomic_t reduced_table; };
#ifdef CONFIG_BATMAN_ADV_BLA @@ -281,6 +283,7 @@ struct batadv_priv { atomic_t bcast_seqno; atomic_t bcast_queue_left; atomic_t batman_queue_left; + atomic_t min_mtu; char num_ifaces; struct batadv_debug_log *debug_log; struct kobject *mesh_obj;
b.a.t.m.a.n@lists.open-mesh.org