18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 6LoWPAN fragment reassembly 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Authors: 58c2ecf20Sopenharmony_ci * Alexander Aring <aar@pengutronix.de> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on: net/ipv6/reassembly.c 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "6LoWPAN: " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/net.h> 138c2ecf20Sopenharmony_ci#include <linux/list.h> 148c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 158c2ecf20Sopenharmony_ci#include <linux/random.h> 168c2ecf20Sopenharmony_ci#include <linux/jhash.h> 178c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/export.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <net/ieee802154_netdev.h> 228c2ecf20Sopenharmony_ci#include <net/6lowpan.h> 238c2ecf20Sopenharmony_ci#include <net/ipv6_frag.h> 248c2ecf20Sopenharmony_ci#include <net/inet_frag.h> 258c2ecf20Sopenharmony_ci#include <net/ip.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "6lowpan_i.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic const char lowpan_frags_cache_name[] = "lowpan-frags"; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic struct inet_frags lowpan_frags; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *skb, 348c2ecf20Sopenharmony_ci struct sk_buff *prev, struct net_device *ldev); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic void lowpan_frag_init(struct inet_frag_queue *q, const void *a) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci const struct frag_lowpan_compare_key *key = a; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(*key) > sizeof(q->key)); 418c2ecf20Sopenharmony_ci memcpy(&q->key, key, sizeof(*key)); 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic void lowpan_frag_expire(struct timer_list *t) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct inet_frag_queue *frag = from_timer(frag, t, timer); 478c2ecf20Sopenharmony_ci struct frag_queue *fq; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci fq = container_of(frag, struct frag_queue, q); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci spin_lock(&fq->q.lock); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (fq->q.flags & INET_FRAG_COMPLETE) 548c2ecf20Sopenharmony_ci goto out; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci inet_frag_kill(&fq->q); 578c2ecf20Sopenharmony_ciout: 588c2ecf20Sopenharmony_ci spin_unlock(&fq->q.lock); 598c2ecf20Sopenharmony_ci inet_frag_put(&fq->q); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic inline struct lowpan_frag_queue * 638c2ecf20Sopenharmony_cifq_find(struct net *net, const struct lowpan_802154_cb *cb, 648c2ecf20Sopenharmony_ci const struct ieee802154_addr *src, 658c2ecf20Sopenharmony_ci const struct ieee802154_addr *dst) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct netns_ieee802154_lowpan *ieee802154_lowpan = 688c2ecf20Sopenharmony_ci net_ieee802154_lowpan(net); 698c2ecf20Sopenharmony_ci struct frag_lowpan_compare_key key = {}; 708c2ecf20Sopenharmony_ci struct inet_frag_queue *q; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci key.tag = cb->d_tag; 738c2ecf20Sopenharmony_ci key.d_size = cb->d_size; 748c2ecf20Sopenharmony_ci key.src = *src; 758c2ecf20Sopenharmony_ci key.dst = *dst; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci q = inet_frag_find(ieee802154_lowpan->fqdir, &key); 788c2ecf20Sopenharmony_ci if (!q) 798c2ecf20Sopenharmony_ci return NULL; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return container_of(q, struct lowpan_frag_queue, q); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int lowpan_frag_queue(struct lowpan_frag_queue *fq, 858c2ecf20Sopenharmony_ci struct sk_buff *skb, u8 frag_type) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct sk_buff *prev_tail; 888c2ecf20Sopenharmony_ci struct net_device *ldev; 898c2ecf20Sopenharmony_ci int end, offset, err; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* inet_frag_queue_* functions use skb->cb; see struct ipfrag_skb_cb 928c2ecf20Sopenharmony_ci * in inet_fragment.c 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct lowpan_802154_cb) > sizeof(struct inet_skb_parm)); 958c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct lowpan_802154_cb) > sizeof(struct inet6_skb_parm)); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (fq->q.flags & INET_FRAG_COMPLETE) 988c2ecf20Sopenharmony_ci goto err; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci offset = lowpan_802154_cb(skb)->d_offset << 3; 1018c2ecf20Sopenharmony_ci end = lowpan_802154_cb(skb)->d_size; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* Is this the final fragment? */ 1048c2ecf20Sopenharmony_ci if (offset + skb->len == end) { 1058c2ecf20Sopenharmony_ci /* If we already have some bits beyond end 1068c2ecf20Sopenharmony_ci * or have different end, the segment is corrupted. 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_ci if (end < fq->q.len || 1098c2ecf20Sopenharmony_ci ((fq->q.flags & INET_FRAG_LAST_IN) && end != fq->q.len)) 1108c2ecf20Sopenharmony_ci goto err; 1118c2ecf20Sopenharmony_ci fq->q.flags |= INET_FRAG_LAST_IN; 1128c2ecf20Sopenharmony_ci fq->q.len = end; 1138c2ecf20Sopenharmony_ci } else { 1148c2ecf20Sopenharmony_ci if (end > fq->q.len) { 1158c2ecf20Sopenharmony_ci /* Some bits beyond end -> corruption. */ 1168c2ecf20Sopenharmony_ci if (fq->q.flags & INET_FRAG_LAST_IN) 1178c2ecf20Sopenharmony_ci goto err; 1188c2ecf20Sopenharmony_ci fq->q.len = end; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci ldev = skb->dev; 1238c2ecf20Sopenharmony_ci if (ldev) 1248c2ecf20Sopenharmony_ci skb->dev = NULL; 1258c2ecf20Sopenharmony_ci barrier(); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci prev_tail = fq->q.fragments_tail; 1288c2ecf20Sopenharmony_ci err = inet_frag_queue_insert(&fq->q, skb, offset, end); 1298c2ecf20Sopenharmony_ci if (err) 1308c2ecf20Sopenharmony_ci goto err; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci fq->q.stamp = skb->tstamp; 1338c2ecf20Sopenharmony_ci if (frag_type == LOWPAN_DISPATCH_FRAG1) 1348c2ecf20Sopenharmony_ci fq->q.flags |= INET_FRAG_FIRST_IN; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci fq->q.meat += skb->len; 1378c2ecf20Sopenharmony_ci add_frag_mem_limit(fq->q.fqdir, skb->truesize); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && 1408c2ecf20Sopenharmony_ci fq->q.meat == fq->q.len) { 1418c2ecf20Sopenharmony_ci int res; 1428c2ecf20Sopenharmony_ci unsigned long orefdst = skb->_skb_refdst; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci skb->_skb_refdst = 0UL; 1458c2ecf20Sopenharmony_ci res = lowpan_frag_reasm(fq, skb, prev_tail, ldev); 1468c2ecf20Sopenharmony_ci skb->_skb_refdst = orefdst; 1478c2ecf20Sopenharmony_ci return res; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci skb_dst_drop(skb); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return -1; 1528c2ecf20Sopenharmony_cierr: 1538c2ecf20Sopenharmony_ci kfree_skb(skb); 1548c2ecf20Sopenharmony_ci return -1; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/* Check if this packet is complete. 1588c2ecf20Sopenharmony_ci * 1598c2ecf20Sopenharmony_ci * It is called with locked fq, and caller must check that 1608c2ecf20Sopenharmony_ci * queue is eligible for reassembly i.e. it is not COMPLETE, 1618c2ecf20Sopenharmony_ci * the last and the first frames arrived and all the bits are here. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_cistatic int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *skb, 1648c2ecf20Sopenharmony_ci struct sk_buff *prev_tail, struct net_device *ldev) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci void *reasm_data; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci inet_frag_kill(&fq->q); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci reasm_data = inet_frag_reasm_prepare(&fq->q, skb, prev_tail); 1718c2ecf20Sopenharmony_ci if (!reasm_data) 1728c2ecf20Sopenharmony_ci goto out_oom; 1738c2ecf20Sopenharmony_ci inet_frag_reasm_finish(&fq->q, skb, reasm_data, false); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci skb->dev = ldev; 1768c2ecf20Sopenharmony_ci skb->tstamp = fq->q.stamp; 1778c2ecf20Sopenharmony_ci fq->q.rb_fragments = RB_ROOT; 1788c2ecf20Sopenharmony_ci fq->q.fragments_tail = NULL; 1798c2ecf20Sopenharmony_ci fq->q.last_run_head = NULL; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return 1; 1828c2ecf20Sopenharmony_ciout_oom: 1838c2ecf20Sopenharmony_ci net_dbg_ratelimited("lowpan_frag_reasm: no memory for reassembly\n"); 1848c2ecf20Sopenharmony_ci return -1; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int lowpan_frag_rx_handlers_result(struct sk_buff *skb, 1888c2ecf20Sopenharmony_ci lowpan_rx_result res) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci switch (res) { 1918c2ecf20Sopenharmony_ci case RX_QUEUED: 1928c2ecf20Sopenharmony_ci return NET_RX_SUCCESS; 1938c2ecf20Sopenharmony_ci case RX_CONTINUE: 1948c2ecf20Sopenharmony_ci /* nobody cared about this packet */ 1958c2ecf20Sopenharmony_ci net_warn_ratelimited("%s: received unknown dispatch\n", 1968c2ecf20Sopenharmony_ci __func__); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci fallthrough; 1998c2ecf20Sopenharmony_ci default: 2008c2ecf20Sopenharmony_ci /* all others failure */ 2018c2ecf20Sopenharmony_ci return NET_RX_DROP; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic lowpan_rx_result lowpan_frag_rx_h_iphc(struct sk_buff *skb) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci int ret; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (!lowpan_is_iphc(*skb_network_header(skb))) 2108c2ecf20Sopenharmony_ci return RX_CONTINUE; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci ret = lowpan_iphc_decompress(skb); 2138c2ecf20Sopenharmony_ci if (ret < 0) 2148c2ecf20Sopenharmony_ci return RX_DROP; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return RX_QUEUED; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int lowpan_invoke_frag_rx_handlers(struct sk_buff *skb) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci lowpan_rx_result res; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci#define CALL_RXH(rxh) \ 2248c2ecf20Sopenharmony_ci do { \ 2258c2ecf20Sopenharmony_ci res = rxh(skb); \ 2268c2ecf20Sopenharmony_ci if (res != RX_CONTINUE) \ 2278c2ecf20Sopenharmony_ci goto rxh_next; \ 2288c2ecf20Sopenharmony_ci } while (0) 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* likely at first */ 2318c2ecf20Sopenharmony_ci CALL_RXH(lowpan_frag_rx_h_iphc); 2328c2ecf20Sopenharmony_ci CALL_RXH(lowpan_rx_h_ipv6); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cirxh_next: 2358c2ecf20Sopenharmony_ci return lowpan_frag_rx_handlers_result(skb, res); 2368c2ecf20Sopenharmony_ci#undef CALL_RXH 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci#define LOWPAN_FRAG_DGRAM_SIZE_HIGH_MASK 0x07 2408c2ecf20Sopenharmony_ci#define LOWPAN_FRAG_DGRAM_SIZE_HIGH_SHIFT 8 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int lowpan_get_cb(struct sk_buff *skb, u8 frag_type, 2438c2ecf20Sopenharmony_ci struct lowpan_802154_cb *cb) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci bool fail; 2468c2ecf20Sopenharmony_ci u8 high = 0, low = 0; 2478c2ecf20Sopenharmony_ci __be16 d_tag = 0; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci fail = lowpan_fetch_skb(skb, &high, 1); 2508c2ecf20Sopenharmony_ci fail |= lowpan_fetch_skb(skb, &low, 1); 2518c2ecf20Sopenharmony_ci /* remove the dispatch value and use first three bits as high value 2528c2ecf20Sopenharmony_ci * for the datagram size 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_ci cb->d_size = (high & LOWPAN_FRAG_DGRAM_SIZE_HIGH_MASK) << 2558c2ecf20Sopenharmony_ci LOWPAN_FRAG_DGRAM_SIZE_HIGH_SHIFT | low; 2568c2ecf20Sopenharmony_ci fail |= lowpan_fetch_skb(skb, &d_tag, 2); 2578c2ecf20Sopenharmony_ci cb->d_tag = ntohs(d_tag); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (frag_type == LOWPAN_DISPATCH_FRAGN) { 2608c2ecf20Sopenharmony_ci fail |= lowpan_fetch_skb(skb, &cb->d_offset, 1); 2618c2ecf20Sopenharmony_ci } else { 2628c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 2638c2ecf20Sopenharmony_ci cb->d_offset = 0; 2648c2ecf20Sopenharmony_ci /* check if datagram_size has ipv6hdr on FRAG1 */ 2658c2ecf20Sopenharmony_ci fail |= cb->d_size < sizeof(struct ipv6hdr); 2668c2ecf20Sopenharmony_ci /* check if we can dereference the dispatch value */ 2678c2ecf20Sopenharmony_ci fail |= !skb->len; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (unlikely(fail)) 2718c2ecf20Sopenharmony_ci return -EIO; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ciint lowpan_frag_rcv(struct sk_buff *skb, u8 frag_type) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct lowpan_frag_queue *fq; 2798c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 2808c2ecf20Sopenharmony_ci struct lowpan_802154_cb *cb = lowpan_802154_cb(skb); 2818c2ecf20Sopenharmony_ci struct ieee802154_hdr hdr = {}; 2828c2ecf20Sopenharmony_ci int err; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) 2858c2ecf20Sopenharmony_ci goto err; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci err = lowpan_get_cb(skb, frag_type, cb); 2888c2ecf20Sopenharmony_ci if (err < 0) 2898c2ecf20Sopenharmony_ci goto err; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (frag_type == LOWPAN_DISPATCH_FRAG1) { 2928c2ecf20Sopenharmony_ci err = lowpan_invoke_frag_rx_handlers(skb); 2938c2ecf20Sopenharmony_ci if (err == NET_RX_DROP) 2948c2ecf20Sopenharmony_ci goto err; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (cb->d_size > IPV6_MIN_MTU) { 2988c2ecf20Sopenharmony_ci net_warn_ratelimited("lowpan_frag_rcv: datagram size exceeds MTU\n"); 2998c2ecf20Sopenharmony_ci goto err; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci fq = fq_find(net, cb, &hdr.source, &hdr.dest); 3038c2ecf20Sopenharmony_ci if (fq != NULL) { 3048c2ecf20Sopenharmony_ci int ret; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci spin_lock(&fq->q.lock); 3078c2ecf20Sopenharmony_ci ret = lowpan_frag_queue(fq, skb, frag_type); 3088c2ecf20Sopenharmony_ci spin_unlock(&fq->q.lock); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci inet_frag_put(&fq->q); 3118c2ecf20Sopenharmony_ci return ret; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cierr: 3158c2ecf20Sopenharmony_ci kfree_skb(skb); 3168c2ecf20Sopenharmony_ci return -1; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic struct ctl_table lowpan_frags_ns_ctl_table[] = { 3228c2ecf20Sopenharmony_ci { 3238c2ecf20Sopenharmony_ci .procname = "6lowpanfrag_high_thresh", 3248c2ecf20Sopenharmony_ci .maxlen = sizeof(unsigned long), 3258c2ecf20Sopenharmony_ci .mode = 0644, 3268c2ecf20Sopenharmony_ci .proc_handler = proc_doulongvec_minmax, 3278c2ecf20Sopenharmony_ci }, 3288c2ecf20Sopenharmony_ci { 3298c2ecf20Sopenharmony_ci .procname = "6lowpanfrag_low_thresh", 3308c2ecf20Sopenharmony_ci .maxlen = sizeof(unsigned long), 3318c2ecf20Sopenharmony_ci .mode = 0644, 3328c2ecf20Sopenharmony_ci .proc_handler = proc_doulongvec_minmax, 3338c2ecf20Sopenharmony_ci }, 3348c2ecf20Sopenharmony_ci { 3358c2ecf20Sopenharmony_ci .procname = "6lowpanfrag_time", 3368c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 3378c2ecf20Sopenharmony_ci .mode = 0644, 3388c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 3398c2ecf20Sopenharmony_ci }, 3408c2ecf20Sopenharmony_ci { } 3418c2ecf20Sopenharmony_ci}; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci/* secret interval has been deprecated */ 3448c2ecf20Sopenharmony_cistatic int lowpan_frags_secret_interval_unused; 3458c2ecf20Sopenharmony_cistatic struct ctl_table lowpan_frags_ctl_table[] = { 3468c2ecf20Sopenharmony_ci { 3478c2ecf20Sopenharmony_ci .procname = "6lowpanfrag_secret_interval", 3488c2ecf20Sopenharmony_ci .data = &lowpan_frags_secret_interval_unused, 3498c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 3508c2ecf20Sopenharmony_ci .mode = 0644, 3518c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 3528c2ecf20Sopenharmony_ci }, 3538c2ecf20Sopenharmony_ci { } 3548c2ecf20Sopenharmony_ci}; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic int __net_init lowpan_frags_ns_sysctl_register(struct net *net) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct ctl_table *table; 3598c2ecf20Sopenharmony_ci struct ctl_table_header *hdr; 3608c2ecf20Sopenharmony_ci struct netns_ieee802154_lowpan *ieee802154_lowpan = 3618c2ecf20Sopenharmony_ci net_ieee802154_lowpan(net); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci table = lowpan_frags_ns_ctl_table; 3648c2ecf20Sopenharmony_ci if (!net_eq(net, &init_net)) { 3658c2ecf20Sopenharmony_ci table = kmemdup(table, sizeof(lowpan_frags_ns_ctl_table), 3668c2ecf20Sopenharmony_ci GFP_KERNEL); 3678c2ecf20Sopenharmony_ci if (table == NULL) 3688c2ecf20Sopenharmony_ci goto err_alloc; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* Don't export sysctls to unprivileged users */ 3718c2ecf20Sopenharmony_ci if (net->user_ns != &init_user_ns) 3728c2ecf20Sopenharmony_ci table[0].procname = NULL; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci table[0].data = &ieee802154_lowpan->fqdir->high_thresh; 3768c2ecf20Sopenharmony_ci table[0].extra1 = &ieee802154_lowpan->fqdir->low_thresh; 3778c2ecf20Sopenharmony_ci table[1].data = &ieee802154_lowpan->fqdir->low_thresh; 3788c2ecf20Sopenharmony_ci table[1].extra2 = &ieee802154_lowpan->fqdir->high_thresh; 3798c2ecf20Sopenharmony_ci table[2].data = &ieee802154_lowpan->fqdir->timeout; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci hdr = register_net_sysctl(net, "net/ieee802154/6lowpan", table); 3828c2ecf20Sopenharmony_ci if (hdr == NULL) 3838c2ecf20Sopenharmony_ci goto err_reg; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci ieee802154_lowpan->sysctl.frags_hdr = hdr; 3868c2ecf20Sopenharmony_ci return 0; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cierr_reg: 3898c2ecf20Sopenharmony_ci if (!net_eq(net, &init_net)) 3908c2ecf20Sopenharmony_ci kfree(table); 3918c2ecf20Sopenharmony_cierr_alloc: 3928c2ecf20Sopenharmony_ci return -ENOMEM; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic void __net_exit lowpan_frags_ns_sysctl_unregister(struct net *net) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct ctl_table *table; 3988c2ecf20Sopenharmony_ci struct netns_ieee802154_lowpan *ieee802154_lowpan = 3998c2ecf20Sopenharmony_ci net_ieee802154_lowpan(net); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci table = ieee802154_lowpan->sysctl.frags_hdr->ctl_table_arg; 4028c2ecf20Sopenharmony_ci unregister_net_sysctl_table(ieee802154_lowpan->sysctl.frags_hdr); 4038c2ecf20Sopenharmony_ci if (!net_eq(net, &init_net)) 4048c2ecf20Sopenharmony_ci kfree(table); 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic struct ctl_table_header *lowpan_ctl_header; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic int __init lowpan_frags_sysctl_register(void) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci lowpan_ctl_header = register_net_sysctl(&init_net, 4128c2ecf20Sopenharmony_ci "net/ieee802154/6lowpan", 4138c2ecf20Sopenharmony_ci lowpan_frags_ctl_table); 4148c2ecf20Sopenharmony_ci return lowpan_ctl_header == NULL ? -ENOMEM : 0; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic void lowpan_frags_sysctl_unregister(void) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci unregister_net_sysctl_table(lowpan_ctl_header); 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci#else 4228c2ecf20Sopenharmony_cistatic inline int lowpan_frags_ns_sysctl_register(struct net *net) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic inline void lowpan_frags_ns_sysctl_unregister(struct net *net) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic inline int __init lowpan_frags_sysctl_register(void) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci return 0; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic inline void lowpan_frags_sysctl_unregister(void) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci#endif 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic int __net_init lowpan_frags_init_net(struct net *net) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct netns_ieee802154_lowpan *ieee802154_lowpan = 4448c2ecf20Sopenharmony_ci net_ieee802154_lowpan(net); 4458c2ecf20Sopenharmony_ci int res; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci res = fqdir_init(&ieee802154_lowpan->fqdir, &lowpan_frags, net); 4498c2ecf20Sopenharmony_ci if (res < 0) 4508c2ecf20Sopenharmony_ci return res; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci ieee802154_lowpan->fqdir->high_thresh = IPV6_FRAG_HIGH_THRESH; 4538c2ecf20Sopenharmony_ci ieee802154_lowpan->fqdir->low_thresh = IPV6_FRAG_LOW_THRESH; 4548c2ecf20Sopenharmony_ci ieee802154_lowpan->fqdir->timeout = IPV6_FRAG_TIMEOUT; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci res = lowpan_frags_ns_sysctl_register(net); 4578c2ecf20Sopenharmony_ci if (res < 0) 4588c2ecf20Sopenharmony_ci fqdir_exit(ieee802154_lowpan->fqdir); 4598c2ecf20Sopenharmony_ci return res; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic void __net_exit lowpan_frags_pre_exit_net(struct net *net) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci struct netns_ieee802154_lowpan *ieee802154_lowpan = 4658c2ecf20Sopenharmony_ci net_ieee802154_lowpan(net); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci fqdir_pre_exit(ieee802154_lowpan->fqdir); 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic void __net_exit lowpan_frags_exit_net(struct net *net) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci struct netns_ieee802154_lowpan *ieee802154_lowpan = 4738c2ecf20Sopenharmony_ci net_ieee802154_lowpan(net); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci lowpan_frags_ns_sysctl_unregister(net); 4768c2ecf20Sopenharmony_ci fqdir_exit(ieee802154_lowpan->fqdir); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic struct pernet_operations lowpan_frags_ops = { 4808c2ecf20Sopenharmony_ci .init = lowpan_frags_init_net, 4818c2ecf20Sopenharmony_ci .pre_exit = lowpan_frags_pre_exit_net, 4828c2ecf20Sopenharmony_ci .exit = lowpan_frags_exit_net, 4838c2ecf20Sopenharmony_ci}; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic u32 lowpan_key_hashfn(const void *data, u32 len, u32 seed) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci return jhash2(data, 4888c2ecf20Sopenharmony_ci sizeof(struct frag_lowpan_compare_key) / sizeof(u32), seed); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic u32 lowpan_obj_hashfn(const void *data, u32 len, u32 seed) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci const struct inet_frag_queue *fq = data; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return jhash2((const u32 *)&fq->key, 4968c2ecf20Sopenharmony_ci sizeof(struct frag_lowpan_compare_key) / sizeof(u32), seed); 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic int lowpan_obj_cmpfn(struct rhashtable_compare_arg *arg, const void *ptr) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci const struct frag_lowpan_compare_key *key = arg->key; 5028c2ecf20Sopenharmony_ci const struct inet_frag_queue *fq = ptr; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci return !!memcmp(&fq->key, key, sizeof(*key)); 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic const struct rhashtable_params lowpan_rhash_params = { 5088c2ecf20Sopenharmony_ci .head_offset = offsetof(struct inet_frag_queue, node), 5098c2ecf20Sopenharmony_ci .hashfn = lowpan_key_hashfn, 5108c2ecf20Sopenharmony_ci .obj_hashfn = lowpan_obj_hashfn, 5118c2ecf20Sopenharmony_ci .obj_cmpfn = lowpan_obj_cmpfn, 5128c2ecf20Sopenharmony_ci .automatic_shrinking = true, 5138c2ecf20Sopenharmony_ci}; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ciint __init lowpan_net_frag_init(void) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci int ret; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci lowpan_frags.constructor = lowpan_frag_init; 5208c2ecf20Sopenharmony_ci lowpan_frags.destructor = NULL; 5218c2ecf20Sopenharmony_ci lowpan_frags.qsize = sizeof(struct frag_queue); 5228c2ecf20Sopenharmony_ci lowpan_frags.frag_expire = lowpan_frag_expire; 5238c2ecf20Sopenharmony_ci lowpan_frags.frags_cache_name = lowpan_frags_cache_name; 5248c2ecf20Sopenharmony_ci lowpan_frags.rhash_params = lowpan_rhash_params; 5258c2ecf20Sopenharmony_ci ret = inet_frags_init(&lowpan_frags); 5268c2ecf20Sopenharmony_ci if (ret) 5278c2ecf20Sopenharmony_ci goto out; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci ret = lowpan_frags_sysctl_register(); 5308c2ecf20Sopenharmony_ci if (ret) 5318c2ecf20Sopenharmony_ci goto err_sysctl; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci ret = register_pernet_subsys(&lowpan_frags_ops); 5348c2ecf20Sopenharmony_ci if (ret) 5358c2ecf20Sopenharmony_ci goto err_pernet; 5368c2ecf20Sopenharmony_ciout: 5378c2ecf20Sopenharmony_ci return ret; 5388c2ecf20Sopenharmony_cierr_pernet: 5398c2ecf20Sopenharmony_ci lowpan_frags_sysctl_unregister(); 5408c2ecf20Sopenharmony_cierr_sysctl: 5418c2ecf20Sopenharmony_ci inet_frags_fini(&lowpan_frags); 5428c2ecf20Sopenharmony_ci return ret; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_civoid lowpan_net_frag_exit(void) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci lowpan_frags_sysctl_unregister(); 5488c2ecf20Sopenharmony_ci unregister_pernet_subsys(&lowpan_frags_ops); 5498c2ecf20Sopenharmony_ci inet_frags_fini(&lowpan_frags); 5508c2ecf20Sopenharmony_ci} 551