18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (C) 2013-2020 B.A.T.M.A.N. contributors: 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Martin Hundebøll <martin@hundeboll.net> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include "fragmentation.h" 88c2ecf20Sopenharmony_ci#include "main.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/atomic.h> 118c2ecf20Sopenharmony_ci#include <linux/byteorder/generic.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/gfp.h> 158c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 168c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/lockdep.h> 198c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 208c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 238c2ecf20Sopenharmony_ci#include <linux/string.h> 248c2ecf20Sopenharmony_ci#include <uapi/linux/batadv_packet.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "hard-interface.h" 278c2ecf20Sopenharmony_ci#include "originator.h" 288c2ecf20Sopenharmony_ci#include "routing.h" 298c2ecf20Sopenharmony_ci#include "send.h" 308c2ecf20Sopenharmony_ci#include "soft-interface.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/** 338c2ecf20Sopenharmony_ci * batadv_frag_clear_chain() - delete entries in the fragment buffer chain 348c2ecf20Sopenharmony_ci * @head: head of chain with entries. 358c2ecf20Sopenharmony_ci * @dropped: whether the chain is cleared because all fragments are dropped 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * Free fragments in the passed hlist. Should be called with appropriate lock. 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistatic void batadv_frag_clear_chain(struct hlist_head *head, bool dropped) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct batadv_frag_list_entry *entry; 428c2ecf20Sopenharmony_ci struct hlist_node *node; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(entry, node, head, list) { 458c2ecf20Sopenharmony_ci hlist_del(&entry->list); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (dropped) 488c2ecf20Sopenharmony_ci kfree_skb(entry->skb); 498c2ecf20Sopenharmony_ci else 508c2ecf20Sopenharmony_ci consume_skb(entry->skb); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci kfree(entry); 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/** 578c2ecf20Sopenharmony_ci * batadv_frag_purge_orig() - free fragments associated to an orig 588c2ecf20Sopenharmony_ci * @orig_node: originator to free fragments from 598c2ecf20Sopenharmony_ci * @check_cb: optional function to tell if an entry should be purged 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_civoid batadv_frag_purge_orig(struct batadv_orig_node *orig_node, 628c2ecf20Sopenharmony_ci bool (*check_cb)(struct batadv_frag_table_entry *)) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct batadv_frag_table_entry *chain; 658c2ecf20Sopenharmony_ci u8 i; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) { 688c2ecf20Sopenharmony_ci chain = &orig_node->fragments[i]; 698c2ecf20Sopenharmony_ci spin_lock_bh(&chain->lock); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (!check_cb || check_cb(chain)) { 728c2ecf20Sopenharmony_ci batadv_frag_clear_chain(&chain->fragment_list, true); 738c2ecf20Sopenharmony_ci chain->size = 0; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci spin_unlock_bh(&chain->lock); 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/** 818c2ecf20Sopenharmony_ci * batadv_frag_size_limit() - maximum possible size of packet to be fragmented 828c2ecf20Sopenharmony_ci * 838c2ecf20Sopenharmony_ci * Return: the maximum size of payload that can be fragmented. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_cistatic int batadv_frag_size_limit(void) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci int limit = BATADV_FRAG_MAX_FRAG_SIZE; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci limit -= sizeof(struct batadv_frag_packet); 908c2ecf20Sopenharmony_ci limit *= BATADV_FRAG_MAX_FRAGMENTS; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return limit; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/** 968c2ecf20Sopenharmony_ci * batadv_frag_init_chain() - check and prepare fragment chain for new fragment 978c2ecf20Sopenharmony_ci * @chain: chain in fragments table to init 988c2ecf20Sopenharmony_ci * @seqno: sequence number of the received fragment 998c2ecf20Sopenharmony_ci * 1008c2ecf20Sopenharmony_ci * Make chain ready for a fragment with sequence number "seqno". Delete existing 1018c2ecf20Sopenharmony_ci * entries if they have an "old" sequence number. 1028c2ecf20Sopenharmony_ci * 1038c2ecf20Sopenharmony_ci * Caller must hold chain->lock. 1048c2ecf20Sopenharmony_ci * 1058c2ecf20Sopenharmony_ci * Return: true if chain is empty and the caller can just insert the new 1068c2ecf20Sopenharmony_ci * fragment without searching for the right position. 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_cistatic bool batadv_frag_init_chain(struct batadv_frag_table_entry *chain, 1098c2ecf20Sopenharmony_ci u16 seqno) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci lockdep_assert_held(&chain->lock); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (chain->seqno == seqno) 1148c2ecf20Sopenharmony_ci return false; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (!hlist_empty(&chain->fragment_list)) 1178c2ecf20Sopenharmony_ci batadv_frag_clear_chain(&chain->fragment_list, true); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci chain->size = 0; 1208c2ecf20Sopenharmony_ci chain->seqno = seqno; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return true; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/** 1268c2ecf20Sopenharmony_ci * batadv_frag_insert_packet() - insert a fragment into a fragment chain 1278c2ecf20Sopenharmony_ci * @orig_node: originator that the fragment was received from 1288c2ecf20Sopenharmony_ci * @skb: skb to insert 1298c2ecf20Sopenharmony_ci * @chain_out: list head to attach complete chains of fragments to 1308c2ecf20Sopenharmony_ci * 1318c2ecf20Sopenharmony_ci * Insert a new fragment into the reverse ordered chain in the right table 1328c2ecf20Sopenharmony_ci * entry. The hash table entry is cleared if "old" fragments exist in it. 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * Return: true if skb is buffered, false on error. If the chain has all the 1358c2ecf20Sopenharmony_ci * fragments needed to merge the packet, the chain is moved to the passed head 1368c2ecf20Sopenharmony_ci * to avoid locking the chain in the table. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_cistatic bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node, 1398c2ecf20Sopenharmony_ci struct sk_buff *skb, 1408c2ecf20Sopenharmony_ci struct hlist_head *chain_out) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct batadv_frag_table_entry *chain; 1438c2ecf20Sopenharmony_ci struct batadv_frag_list_entry *frag_entry_new = NULL, *frag_entry_curr; 1448c2ecf20Sopenharmony_ci struct batadv_frag_list_entry *frag_entry_last = NULL; 1458c2ecf20Sopenharmony_ci struct batadv_frag_packet *frag_packet; 1468c2ecf20Sopenharmony_ci u8 bucket; 1478c2ecf20Sopenharmony_ci u16 seqno, hdr_size = sizeof(struct batadv_frag_packet); 1488c2ecf20Sopenharmony_ci bool ret = false; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* Linearize packet to avoid linearizing 16 packets in a row when doing 1518c2ecf20Sopenharmony_ci * the later merge. Non-linear merge should be added to remove this 1528c2ecf20Sopenharmony_ci * linearization. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci if (skb_linearize(skb) < 0) 1558c2ecf20Sopenharmony_ci goto err; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci frag_packet = (struct batadv_frag_packet *)skb->data; 1588c2ecf20Sopenharmony_ci seqno = ntohs(frag_packet->seqno); 1598c2ecf20Sopenharmony_ci bucket = seqno % BATADV_FRAG_BUFFER_COUNT; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci frag_entry_new = kmalloc(sizeof(*frag_entry_new), GFP_ATOMIC); 1628c2ecf20Sopenharmony_ci if (!frag_entry_new) 1638c2ecf20Sopenharmony_ci goto err; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci frag_entry_new->skb = skb; 1668c2ecf20Sopenharmony_ci frag_entry_new->no = frag_packet->no; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* Select entry in the "chain table" and delete any prior fragments 1698c2ecf20Sopenharmony_ci * with another sequence number. batadv_frag_init_chain() returns true, 1708c2ecf20Sopenharmony_ci * if the list is empty at return. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ci chain = &orig_node->fragments[bucket]; 1738c2ecf20Sopenharmony_ci spin_lock_bh(&chain->lock); 1748c2ecf20Sopenharmony_ci if (batadv_frag_init_chain(chain, seqno)) { 1758c2ecf20Sopenharmony_ci hlist_add_head(&frag_entry_new->list, &chain->fragment_list); 1768c2ecf20Sopenharmony_ci chain->size = skb->len - hdr_size; 1778c2ecf20Sopenharmony_ci chain->timestamp = jiffies; 1788c2ecf20Sopenharmony_ci chain->total_size = ntohs(frag_packet->total_size); 1798c2ecf20Sopenharmony_ci ret = true; 1808c2ecf20Sopenharmony_ci goto out; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* Find the position for the new fragment. */ 1848c2ecf20Sopenharmony_ci hlist_for_each_entry(frag_entry_curr, &chain->fragment_list, list) { 1858c2ecf20Sopenharmony_ci /* Drop packet if fragment already exists. */ 1868c2ecf20Sopenharmony_ci if (frag_entry_curr->no == frag_entry_new->no) 1878c2ecf20Sopenharmony_ci goto err_unlock; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* Order fragments from highest to lowest. */ 1908c2ecf20Sopenharmony_ci if (frag_entry_curr->no < frag_entry_new->no) { 1918c2ecf20Sopenharmony_ci hlist_add_before(&frag_entry_new->list, 1928c2ecf20Sopenharmony_ci &frag_entry_curr->list); 1938c2ecf20Sopenharmony_ci chain->size += skb->len - hdr_size; 1948c2ecf20Sopenharmony_ci chain->timestamp = jiffies; 1958c2ecf20Sopenharmony_ci ret = true; 1968c2ecf20Sopenharmony_ci goto out; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* store current entry because it could be the last in list */ 2008c2ecf20Sopenharmony_ci frag_entry_last = frag_entry_curr; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* Reached the end of the list, so insert after 'frag_entry_last'. */ 2048c2ecf20Sopenharmony_ci if (likely(frag_entry_last)) { 2058c2ecf20Sopenharmony_ci hlist_add_behind(&frag_entry_new->list, &frag_entry_last->list); 2068c2ecf20Sopenharmony_ci chain->size += skb->len - hdr_size; 2078c2ecf20Sopenharmony_ci chain->timestamp = jiffies; 2088c2ecf20Sopenharmony_ci ret = true; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ciout: 2128c2ecf20Sopenharmony_ci if (chain->size > batadv_frag_size_limit() || 2138c2ecf20Sopenharmony_ci chain->total_size != ntohs(frag_packet->total_size) || 2148c2ecf20Sopenharmony_ci chain->total_size > batadv_frag_size_limit()) { 2158c2ecf20Sopenharmony_ci /* Clear chain if total size of either the list or the packet 2168c2ecf20Sopenharmony_ci * exceeds the maximum size of one merged packet. Don't allow 2178c2ecf20Sopenharmony_ci * packets to have different total_size. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci batadv_frag_clear_chain(&chain->fragment_list, true); 2208c2ecf20Sopenharmony_ci chain->size = 0; 2218c2ecf20Sopenharmony_ci } else if (ntohs(frag_packet->total_size) == chain->size) { 2228c2ecf20Sopenharmony_ci /* All fragments received. Hand over chain to caller. */ 2238c2ecf20Sopenharmony_ci hlist_move_list(&chain->fragment_list, chain_out); 2248c2ecf20Sopenharmony_ci chain->size = 0; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cierr_unlock: 2288c2ecf20Sopenharmony_ci spin_unlock_bh(&chain->lock); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cierr: 2318c2ecf20Sopenharmony_ci if (!ret) { 2328c2ecf20Sopenharmony_ci kfree(frag_entry_new); 2338c2ecf20Sopenharmony_ci kfree_skb(skb); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return ret; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/** 2408c2ecf20Sopenharmony_ci * batadv_frag_merge_packets() - merge a chain of fragments 2418c2ecf20Sopenharmony_ci * @chain: head of chain with fragments 2428c2ecf20Sopenharmony_ci * 2438c2ecf20Sopenharmony_ci * Expand the first skb in the chain and copy the content of the remaining 2448c2ecf20Sopenharmony_ci * skb's into the expanded one. After doing so, clear the chain. 2458c2ecf20Sopenharmony_ci * 2468c2ecf20Sopenharmony_ci * Return: the merged skb or NULL on error. 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_cistatic struct sk_buff * 2498c2ecf20Sopenharmony_cibatadv_frag_merge_packets(struct hlist_head *chain) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct batadv_frag_packet *packet; 2528c2ecf20Sopenharmony_ci struct batadv_frag_list_entry *entry; 2538c2ecf20Sopenharmony_ci struct sk_buff *skb_out; 2548c2ecf20Sopenharmony_ci int size, hdr_size = sizeof(struct batadv_frag_packet); 2558c2ecf20Sopenharmony_ci bool dropped = false; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* Remove first entry, as this is the destination for the rest of the 2588c2ecf20Sopenharmony_ci * fragments. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci entry = hlist_entry(chain->first, struct batadv_frag_list_entry, list); 2618c2ecf20Sopenharmony_ci hlist_del(&entry->list); 2628c2ecf20Sopenharmony_ci skb_out = entry->skb; 2638c2ecf20Sopenharmony_ci kfree(entry); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci packet = (struct batadv_frag_packet *)skb_out->data; 2668c2ecf20Sopenharmony_ci size = ntohs(packet->total_size) + hdr_size; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* Make room for the rest of the fragments. */ 2698c2ecf20Sopenharmony_ci if (pskb_expand_head(skb_out, 0, size - skb_out->len, GFP_ATOMIC) < 0) { 2708c2ecf20Sopenharmony_ci kfree_skb(skb_out); 2718c2ecf20Sopenharmony_ci skb_out = NULL; 2728c2ecf20Sopenharmony_ci dropped = true; 2738c2ecf20Sopenharmony_ci goto free; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Move the existing MAC header to just before the payload. (Override 2778c2ecf20Sopenharmony_ci * the fragment header.) 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_ci skb_pull(skb_out, hdr_size); 2808c2ecf20Sopenharmony_ci skb_out->ip_summed = CHECKSUM_NONE; 2818c2ecf20Sopenharmony_ci memmove(skb_out->data - ETH_HLEN, skb_mac_header(skb_out), ETH_HLEN); 2828c2ecf20Sopenharmony_ci skb_set_mac_header(skb_out, -ETH_HLEN); 2838c2ecf20Sopenharmony_ci skb_reset_network_header(skb_out); 2848c2ecf20Sopenharmony_ci skb_reset_transport_header(skb_out); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* Copy the payload of the each fragment into the last skb */ 2878c2ecf20Sopenharmony_ci hlist_for_each_entry(entry, chain, list) { 2888c2ecf20Sopenharmony_ci size = entry->skb->len - hdr_size; 2898c2ecf20Sopenharmony_ci skb_put_data(skb_out, entry->skb->data + hdr_size, size); 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cifree: 2938c2ecf20Sopenharmony_ci /* Locking is not needed, because 'chain' is not part of any orig. */ 2948c2ecf20Sopenharmony_ci batadv_frag_clear_chain(chain, dropped); 2958c2ecf20Sopenharmony_ci return skb_out; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci/** 2998c2ecf20Sopenharmony_ci * batadv_frag_skb_buffer() - buffer fragment for later merge 3008c2ecf20Sopenharmony_ci * @skb: skb to buffer 3018c2ecf20Sopenharmony_ci * @orig_node_src: originator that the skb is received from 3028c2ecf20Sopenharmony_ci * 3038c2ecf20Sopenharmony_ci * Add fragment to buffer and merge fragments if possible. 3048c2ecf20Sopenharmony_ci * 3058c2ecf20Sopenharmony_ci * There are three possible outcomes: 1) Packet is merged: Return true and 3068c2ecf20Sopenharmony_ci * set *skb to merged packet; 2) Packet is buffered: Return true and set *skb 3078c2ecf20Sopenharmony_ci * to NULL; 3) Error: Return false and free skb. 3088c2ecf20Sopenharmony_ci * 3098c2ecf20Sopenharmony_ci * Return: true when the packet is merged or buffered, false when skb is not 3108c2ecf20Sopenharmony_ci * used. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_cibool batadv_frag_skb_buffer(struct sk_buff **skb, 3138c2ecf20Sopenharmony_ci struct batadv_orig_node *orig_node_src) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct sk_buff *skb_out = NULL; 3168c2ecf20Sopenharmony_ci struct hlist_head head = HLIST_HEAD_INIT; 3178c2ecf20Sopenharmony_ci bool ret = false; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* Add packet to buffer and table entry if merge is possible. */ 3208c2ecf20Sopenharmony_ci if (!batadv_frag_insert_packet(orig_node_src, *skb, &head)) 3218c2ecf20Sopenharmony_ci goto out_err; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* Leave if more fragments are needed to merge. */ 3248c2ecf20Sopenharmony_ci if (hlist_empty(&head)) 3258c2ecf20Sopenharmony_ci goto out; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci skb_out = batadv_frag_merge_packets(&head); 3288c2ecf20Sopenharmony_ci if (!skb_out) 3298c2ecf20Sopenharmony_ci goto out_err; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ciout: 3328c2ecf20Sopenharmony_ci ret = true; 3338c2ecf20Sopenharmony_ciout_err: 3348c2ecf20Sopenharmony_ci *skb = skb_out; 3358c2ecf20Sopenharmony_ci return ret; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci/** 3398c2ecf20Sopenharmony_ci * batadv_frag_skb_fwd() - forward fragments that would exceed MTU when merged 3408c2ecf20Sopenharmony_ci * @skb: skb to forward 3418c2ecf20Sopenharmony_ci * @recv_if: interface that the skb is received on 3428c2ecf20Sopenharmony_ci * @orig_node_src: originator that the skb is received from 3438c2ecf20Sopenharmony_ci * 3448c2ecf20Sopenharmony_ci * Look up the next-hop of the fragments payload and check if the merged packet 3458c2ecf20Sopenharmony_ci * will exceed the MTU towards the next-hop. If so, the fragment is forwarded 3468c2ecf20Sopenharmony_ci * without merging it. 3478c2ecf20Sopenharmony_ci * 3488c2ecf20Sopenharmony_ci * Return: true if the fragment is consumed/forwarded, false otherwise. 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_cibool batadv_frag_skb_fwd(struct sk_buff *skb, 3518c2ecf20Sopenharmony_ci struct batadv_hard_iface *recv_if, 3528c2ecf20Sopenharmony_ci struct batadv_orig_node *orig_node_src) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); 3558c2ecf20Sopenharmony_ci struct batadv_orig_node *orig_node_dst; 3568c2ecf20Sopenharmony_ci struct batadv_neigh_node *neigh_node = NULL; 3578c2ecf20Sopenharmony_ci struct batadv_frag_packet *packet; 3588c2ecf20Sopenharmony_ci u16 total_size; 3598c2ecf20Sopenharmony_ci bool ret = false; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci packet = (struct batadv_frag_packet *)skb->data; 3628c2ecf20Sopenharmony_ci orig_node_dst = batadv_orig_hash_find(bat_priv, packet->dest); 3638c2ecf20Sopenharmony_ci if (!orig_node_dst) 3648c2ecf20Sopenharmony_ci goto out; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci neigh_node = batadv_find_router(bat_priv, orig_node_dst, recv_if); 3678c2ecf20Sopenharmony_ci if (!neigh_node) 3688c2ecf20Sopenharmony_ci goto out; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* Forward the fragment, if the merged packet would be too big to 3718c2ecf20Sopenharmony_ci * be assembled. 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_ci total_size = ntohs(packet->total_size); 3748c2ecf20Sopenharmony_ci if (total_size > neigh_node->if_incoming->net_dev->mtu) { 3758c2ecf20Sopenharmony_ci batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD); 3768c2ecf20Sopenharmony_ci batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES, 3778c2ecf20Sopenharmony_ci skb->len + ETH_HLEN); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci packet->ttl--; 3808c2ecf20Sopenharmony_ci batadv_send_unicast_skb(skb, neigh_node); 3818c2ecf20Sopenharmony_ci ret = true; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ciout: 3858c2ecf20Sopenharmony_ci if (orig_node_dst) 3868c2ecf20Sopenharmony_ci batadv_orig_node_put(orig_node_dst); 3878c2ecf20Sopenharmony_ci if (neigh_node) 3888c2ecf20Sopenharmony_ci batadv_neigh_node_put(neigh_node); 3898c2ecf20Sopenharmony_ci return ret; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci/** 3938c2ecf20Sopenharmony_ci * batadv_frag_create() - create a fragment from skb 3948c2ecf20Sopenharmony_ci * @net_dev: outgoing device for fragment 3958c2ecf20Sopenharmony_ci * @skb: skb to create fragment from 3968c2ecf20Sopenharmony_ci * @frag_head: header to use in new fragment 3978c2ecf20Sopenharmony_ci * @fragment_size: size of new fragment 3988c2ecf20Sopenharmony_ci * 3998c2ecf20Sopenharmony_ci * Split the passed skb into two fragments: A new one with size matching the 4008c2ecf20Sopenharmony_ci * passed mtu and the old one with the rest. The new skb contains data from the 4018c2ecf20Sopenharmony_ci * tail of the old skb. 4028c2ecf20Sopenharmony_ci * 4038c2ecf20Sopenharmony_ci * Return: the new fragment, NULL on error. 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_cistatic struct sk_buff *batadv_frag_create(struct net_device *net_dev, 4068c2ecf20Sopenharmony_ci struct sk_buff *skb, 4078c2ecf20Sopenharmony_ci struct batadv_frag_packet *frag_head, 4088c2ecf20Sopenharmony_ci unsigned int fragment_size) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci unsigned int ll_reserved = LL_RESERVED_SPACE(net_dev); 4118c2ecf20Sopenharmony_ci unsigned int tailroom = net_dev->needed_tailroom; 4128c2ecf20Sopenharmony_ci struct sk_buff *skb_fragment; 4138c2ecf20Sopenharmony_ci unsigned int header_size = sizeof(*frag_head); 4148c2ecf20Sopenharmony_ci unsigned int mtu = fragment_size + header_size; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci skb_fragment = dev_alloc_skb(ll_reserved + mtu + tailroom); 4178c2ecf20Sopenharmony_ci if (!skb_fragment) 4188c2ecf20Sopenharmony_ci goto err; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci skb_fragment->priority = skb->priority; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* Eat the last mtu-bytes of the skb */ 4238c2ecf20Sopenharmony_ci skb_reserve(skb_fragment, ll_reserved + header_size); 4248c2ecf20Sopenharmony_ci skb_split(skb, skb_fragment, skb->len - fragment_size); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* Add the header */ 4278c2ecf20Sopenharmony_ci skb_push(skb_fragment, header_size); 4288c2ecf20Sopenharmony_ci memcpy(skb_fragment->data, frag_head, header_size); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cierr: 4318c2ecf20Sopenharmony_ci return skb_fragment; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci/** 4358c2ecf20Sopenharmony_ci * batadv_frag_send_packet() - create up to 16 fragments from the passed skb 4368c2ecf20Sopenharmony_ci * @skb: skb to create fragments from 4378c2ecf20Sopenharmony_ci * @orig_node: final destination of the created fragments 4388c2ecf20Sopenharmony_ci * @neigh_node: next-hop of the created fragments 4398c2ecf20Sopenharmony_ci * 4408c2ecf20Sopenharmony_ci * Return: the netdev tx status or a negative errno code on a failure 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_ciint batadv_frag_send_packet(struct sk_buff *skb, 4438c2ecf20Sopenharmony_ci struct batadv_orig_node *orig_node, 4448c2ecf20Sopenharmony_ci struct batadv_neigh_node *neigh_node) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct net_device *net_dev = neigh_node->if_incoming->net_dev; 4478c2ecf20Sopenharmony_ci struct batadv_priv *bat_priv; 4488c2ecf20Sopenharmony_ci struct batadv_hard_iface *primary_if = NULL; 4498c2ecf20Sopenharmony_ci struct batadv_frag_packet frag_header; 4508c2ecf20Sopenharmony_ci struct sk_buff *skb_fragment; 4518c2ecf20Sopenharmony_ci unsigned int mtu = net_dev->mtu; 4528c2ecf20Sopenharmony_ci unsigned int header_size = sizeof(frag_header); 4538c2ecf20Sopenharmony_ci unsigned int max_fragment_size, num_fragments; 4548c2ecf20Sopenharmony_ci int ret; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* To avoid merge and refragmentation at next-hops we never send 4578c2ecf20Sopenharmony_ci * fragments larger than BATADV_FRAG_MAX_FRAG_SIZE 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_ci mtu = min_t(unsigned int, mtu, BATADV_FRAG_MAX_FRAG_SIZE); 4608c2ecf20Sopenharmony_ci max_fragment_size = mtu - header_size; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (skb->len == 0 || max_fragment_size == 0) 4638c2ecf20Sopenharmony_ci return -EINVAL; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci num_fragments = (skb->len - 1) / max_fragment_size + 1; 4668c2ecf20Sopenharmony_ci max_fragment_size = (skb->len - 1) / num_fragments + 1; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* Don't even try to fragment, if we need more than 16 fragments */ 4698c2ecf20Sopenharmony_ci if (num_fragments > BATADV_FRAG_MAX_FRAGMENTS) { 4708c2ecf20Sopenharmony_ci ret = -EAGAIN; 4718c2ecf20Sopenharmony_ci goto free_skb; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci bat_priv = orig_node->bat_priv; 4758c2ecf20Sopenharmony_ci primary_if = batadv_primary_if_get_selected(bat_priv); 4768c2ecf20Sopenharmony_ci if (!primary_if) { 4778c2ecf20Sopenharmony_ci ret = -EINVAL; 4788c2ecf20Sopenharmony_ci goto free_skb; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* GRO might have added fragments to the fragment list instead of 4828c2ecf20Sopenharmony_ci * frags[]. But this is not handled by skb_split and must be 4838c2ecf20Sopenharmony_ci * linearized to avoid incorrect length information after all 4848c2ecf20Sopenharmony_ci * batman-adv fragments were created and submitted to the 4858c2ecf20Sopenharmony_ci * hard-interface 4868c2ecf20Sopenharmony_ci */ 4878c2ecf20Sopenharmony_ci if (skb_has_frag_list(skb) && __skb_linearize(skb)) { 4888c2ecf20Sopenharmony_ci ret = -ENOMEM; 4898c2ecf20Sopenharmony_ci goto free_skb; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* Create one header to be copied to all fragments */ 4938c2ecf20Sopenharmony_ci frag_header.packet_type = BATADV_UNICAST_FRAG; 4948c2ecf20Sopenharmony_ci frag_header.version = BATADV_COMPAT_VERSION; 4958c2ecf20Sopenharmony_ci frag_header.ttl = BATADV_TTL; 4968c2ecf20Sopenharmony_ci frag_header.seqno = htons(atomic_inc_return(&bat_priv->frag_seqno)); 4978c2ecf20Sopenharmony_ci frag_header.reserved = 0; 4988c2ecf20Sopenharmony_ci frag_header.no = 0; 4998c2ecf20Sopenharmony_ci frag_header.total_size = htons(skb->len); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* skb->priority values from 256->263 are magic values to 5028c2ecf20Sopenharmony_ci * directly indicate a specific 802.1d priority. This is used 5038c2ecf20Sopenharmony_ci * to allow 802.1d priority to be passed directly in from VLAN 5048c2ecf20Sopenharmony_ci * tags, etc. 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_ci if (skb->priority >= 256 && skb->priority <= 263) 5078c2ecf20Sopenharmony_ci frag_header.priority = skb->priority - 256; 5088c2ecf20Sopenharmony_ci else 5098c2ecf20Sopenharmony_ci frag_header.priority = 0; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci ether_addr_copy(frag_header.orig, primary_if->net_dev->dev_addr); 5128c2ecf20Sopenharmony_ci ether_addr_copy(frag_header.dest, orig_node->orig); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* Eat and send fragments from the tail of skb */ 5158c2ecf20Sopenharmony_ci while (skb->len > max_fragment_size) { 5168c2ecf20Sopenharmony_ci /* The initial check in this function should cover this case */ 5178c2ecf20Sopenharmony_ci if (unlikely(frag_header.no == BATADV_FRAG_MAX_FRAGMENTS - 1)) { 5188c2ecf20Sopenharmony_ci ret = -EINVAL; 5198c2ecf20Sopenharmony_ci goto put_primary_if; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci skb_fragment = batadv_frag_create(net_dev, skb, &frag_header, 5238c2ecf20Sopenharmony_ci max_fragment_size); 5248c2ecf20Sopenharmony_ci if (!skb_fragment) { 5258c2ecf20Sopenharmony_ci ret = -ENOMEM; 5268c2ecf20Sopenharmony_ci goto put_primary_if; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX); 5308c2ecf20Sopenharmony_ci batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES, 5318c2ecf20Sopenharmony_ci skb_fragment->len + ETH_HLEN); 5328c2ecf20Sopenharmony_ci ret = batadv_send_unicast_skb(skb_fragment, neigh_node); 5338c2ecf20Sopenharmony_ci if (ret != NET_XMIT_SUCCESS) { 5348c2ecf20Sopenharmony_ci ret = NET_XMIT_DROP; 5358c2ecf20Sopenharmony_ci goto put_primary_if; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci frag_header.no++; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* make sure that there is at least enough head for the fragmentation 5428c2ecf20Sopenharmony_ci * and ethernet headers 5438c2ecf20Sopenharmony_ci */ 5448c2ecf20Sopenharmony_ci ret = skb_cow_head(skb, ETH_HLEN + header_size); 5458c2ecf20Sopenharmony_ci if (ret < 0) 5468c2ecf20Sopenharmony_ci goto put_primary_if; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci skb_push(skb, header_size); 5498c2ecf20Sopenharmony_ci memcpy(skb->data, &frag_header, header_size); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* Send the last fragment */ 5528c2ecf20Sopenharmony_ci batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX); 5538c2ecf20Sopenharmony_ci batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES, 5548c2ecf20Sopenharmony_ci skb->len + ETH_HLEN); 5558c2ecf20Sopenharmony_ci ret = batadv_send_unicast_skb(skb, neigh_node); 5568c2ecf20Sopenharmony_ci /* skb was consumed */ 5578c2ecf20Sopenharmony_ci skb = NULL; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ciput_primary_if: 5608c2ecf20Sopenharmony_ci batadv_hardif_put(primary_if); 5618c2ecf20Sopenharmony_cifree_skb: 5628c2ecf20Sopenharmony_ci kfree_skb(skb); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci return ret; 5658c2ecf20Sopenharmony_ci} 566