18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* Peer event handling, typically ICMP messages. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. 58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/net.h> 108c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 118c2ecf20Sopenharmony_ci#include <linux/errqueue.h> 128c2ecf20Sopenharmony_ci#include <linux/udp.h> 138c2ecf20Sopenharmony_ci#include <linux/in.h> 148c2ecf20Sopenharmony_ci#include <linux/in6.h> 158c2ecf20Sopenharmony_ci#include <linux/icmp.h> 168c2ecf20Sopenharmony_ci#include <net/sock.h> 178c2ecf20Sopenharmony_ci#include <net/af_rxrpc.h> 188c2ecf20Sopenharmony_ci#include <net/ip.h> 198c2ecf20Sopenharmony_ci#include "ar-internal.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic void rxrpc_store_error(struct rxrpc_peer *, struct sock_exterr_skb *); 228c2ecf20Sopenharmony_cistatic void rxrpc_distribute_error(struct rxrpc_peer *, int, 238c2ecf20Sopenharmony_ci enum rxrpc_call_completion); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * Find the peer associated with an ICMP packet. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_cistatic struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local, 298c2ecf20Sopenharmony_ci const struct sk_buff *skb, 308c2ecf20Sopenharmony_ci struct sockaddr_rxrpc *srx) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct sock_exterr_skb *serr = SKB_EXT_ERR(skb); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci _enter(""); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci memset(srx, 0, sizeof(*srx)); 378c2ecf20Sopenharmony_ci srx->transport_type = local->srx.transport_type; 388c2ecf20Sopenharmony_ci srx->transport_len = local->srx.transport_len; 398c2ecf20Sopenharmony_ci srx->transport.family = local->srx.transport.family; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci /* Can we see an ICMP4 packet on an ICMP6 listening socket? and vice 428c2ecf20Sopenharmony_ci * versa? 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci switch (srx->transport.family) { 458c2ecf20Sopenharmony_ci case AF_INET: 468c2ecf20Sopenharmony_ci srx->transport_len = sizeof(srx->transport.sin); 478c2ecf20Sopenharmony_ci srx->transport.family = AF_INET; 488c2ecf20Sopenharmony_ci srx->transport.sin.sin_port = serr->port; 498c2ecf20Sopenharmony_ci switch (serr->ee.ee_origin) { 508c2ecf20Sopenharmony_ci case SO_EE_ORIGIN_ICMP: 518c2ecf20Sopenharmony_ci _net("Rx ICMP"); 528c2ecf20Sopenharmony_ci memcpy(&srx->transport.sin.sin_addr, 538c2ecf20Sopenharmony_ci skb_network_header(skb) + serr->addr_offset, 548c2ecf20Sopenharmony_ci sizeof(struct in_addr)); 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci case SO_EE_ORIGIN_ICMP6: 578c2ecf20Sopenharmony_ci _net("Rx ICMP6 on v4 sock"); 588c2ecf20Sopenharmony_ci memcpy(&srx->transport.sin.sin_addr, 598c2ecf20Sopenharmony_ci skb_network_header(skb) + serr->addr_offset + 12, 608c2ecf20Sopenharmony_ci sizeof(struct in_addr)); 618c2ecf20Sopenharmony_ci break; 628c2ecf20Sopenharmony_ci default: 638c2ecf20Sopenharmony_ci memcpy(&srx->transport.sin.sin_addr, &ip_hdr(skb)->saddr, 648c2ecf20Sopenharmony_ci sizeof(struct in_addr)); 658c2ecf20Sopenharmony_ci break; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci break; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#ifdef CONFIG_AF_RXRPC_IPV6 708c2ecf20Sopenharmony_ci case AF_INET6: 718c2ecf20Sopenharmony_ci switch (serr->ee.ee_origin) { 728c2ecf20Sopenharmony_ci case SO_EE_ORIGIN_ICMP6: 738c2ecf20Sopenharmony_ci _net("Rx ICMP6"); 748c2ecf20Sopenharmony_ci srx->transport.sin6.sin6_port = serr->port; 758c2ecf20Sopenharmony_ci memcpy(&srx->transport.sin6.sin6_addr, 768c2ecf20Sopenharmony_ci skb_network_header(skb) + serr->addr_offset, 778c2ecf20Sopenharmony_ci sizeof(struct in6_addr)); 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci case SO_EE_ORIGIN_ICMP: 808c2ecf20Sopenharmony_ci _net("Rx ICMP on v6 sock"); 818c2ecf20Sopenharmony_ci srx->transport_len = sizeof(srx->transport.sin); 828c2ecf20Sopenharmony_ci srx->transport.family = AF_INET; 838c2ecf20Sopenharmony_ci srx->transport.sin.sin_port = serr->port; 848c2ecf20Sopenharmony_ci memcpy(&srx->transport.sin.sin_addr, 858c2ecf20Sopenharmony_ci skb_network_header(skb) + serr->addr_offset, 868c2ecf20Sopenharmony_ci sizeof(struct in_addr)); 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci default: 898c2ecf20Sopenharmony_ci memcpy(&srx->transport.sin6.sin6_addr, 908c2ecf20Sopenharmony_ci &ipv6_hdr(skb)->saddr, 918c2ecf20Sopenharmony_ci sizeof(struct in6_addr)); 928c2ecf20Sopenharmony_ci break; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci break; 958c2ecf20Sopenharmony_ci#endif 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci default: 988c2ecf20Sopenharmony_ci BUG(); 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return rxrpc_lookup_peer_rcu(local, srx); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * Handle an MTU/fragmentation problem. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_cistatic void rxrpc_adjust_mtu(struct rxrpc_peer *peer, struct sock_exterr_skb *serr) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci u32 mtu = serr->ee.ee_info; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci _net("Rx ICMP Fragmentation Needed (%d)", mtu); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* wind down the local interface MTU */ 1148c2ecf20Sopenharmony_ci if (mtu > 0 && peer->if_mtu == 65535 && mtu < peer->if_mtu) { 1158c2ecf20Sopenharmony_ci peer->if_mtu = mtu; 1168c2ecf20Sopenharmony_ci _net("I/F MTU %u", mtu); 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (mtu == 0) { 1208c2ecf20Sopenharmony_ci /* they didn't give us a size, estimate one */ 1218c2ecf20Sopenharmony_ci mtu = peer->if_mtu; 1228c2ecf20Sopenharmony_ci if (mtu > 1500) { 1238c2ecf20Sopenharmony_ci mtu >>= 1; 1248c2ecf20Sopenharmony_ci if (mtu < 1500) 1258c2ecf20Sopenharmony_ci mtu = 1500; 1268c2ecf20Sopenharmony_ci } else { 1278c2ecf20Sopenharmony_ci mtu -= 100; 1288c2ecf20Sopenharmony_ci if (mtu < peer->hdrsize) 1298c2ecf20Sopenharmony_ci mtu = peer->hdrsize + 4; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (mtu < peer->mtu) { 1348c2ecf20Sopenharmony_ci spin_lock_bh(&peer->lock); 1358c2ecf20Sopenharmony_ci peer->mtu = mtu; 1368c2ecf20Sopenharmony_ci peer->maxdata = peer->mtu - peer->hdrsize; 1378c2ecf20Sopenharmony_ci spin_unlock_bh(&peer->lock); 1388c2ecf20Sopenharmony_ci _net("Net MTU %u (maxdata %u)", 1398c2ecf20Sopenharmony_ci peer->mtu, peer->maxdata); 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* 1448c2ecf20Sopenharmony_ci * Handle an error received on the local endpoint. 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_civoid rxrpc_error_report(struct sock *sk) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct sock_exterr_skb *serr; 1498c2ecf20Sopenharmony_ci struct sockaddr_rxrpc srx; 1508c2ecf20Sopenharmony_ci struct rxrpc_local *local; 1518c2ecf20Sopenharmony_ci struct rxrpc_peer *peer; 1528c2ecf20Sopenharmony_ci struct sk_buff *skb; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci rcu_read_lock(); 1558c2ecf20Sopenharmony_ci local = rcu_dereference_sk_user_data(sk); 1568c2ecf20Sopenharmony_ci if (unlikely(!local)) { 1578c2ecf20Sopenharmony_ci rcu_read_unlock(); 1588c2ecf20Sopenharmony_ci return; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci _enter("%p{%d}", sk, local->debug_id); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* Clear the outstanding error value on the socket so that it doesn't 1638c2ecf20Sopenharmony_ci * cause kernel_sendmsg() to return it later. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci sock_error(sk); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci skb = sock_dequeue_err_skb(sk); 1688c2ecf20Sopenharmony_ci if (!skb) { 1698c2ecf20Sopenharmony_ci rcu_read_unlock(); 1708c2ecf20Sopenharmony_ci _leave("UDP socket errqueue empty"); 1718c2ecf20Sopenharmony_ci return; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci rxrpc_new_skb(skb, rxrpc_skb_received); 1748c2ecf20Sopenharmony_ci serr = SKB_EXT_ERR(skb); 1758c2ecf20Sopenharmony_ci if (!skb->len && serr->ee.ee_origin == SO_EE_ORIGIN_TIMESTAMPING) { 1768c2ecf20Sopenharmony_ci _leave("UDP empty message"); 1778c2ecf20Sopenharmony_ci rcu_read_unlock(); 1788c2ecf20Sopenharmony_ci rxrpc_free_skb(skb, rxrpc_skb_freed); 1798c2ecf20Sopenharmony_ci return; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci peer = rxrpc_lookup_peer_icmp_rcu(local, skb, &srx); 1838c2ecf20Sopenharmony_ci if (peer && !rxrpc_get_peer_maybe(peer)) 1848c2ecf20Sopenharmony_ci peer = NULL; 1858c2ecf20Sopenharmony_ci if (!peer) { 1868c2ecf20Sopenharmony_ci rcu_read_unlock(); 1878c2ecf20Sopenharmony_ci rxrpc_free_skb(skb, rxrpc_skb_freed); 1888c2ecf20Sopenharmony_ci _leave(" [no peer]"); 1898c2ecf20Sopenharmony_ci return; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci trace_rxrpc_rx_icmp(peer, &serr->ee, &srx); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if ((serr->ee.ee_origin == SO_EE_ORIGIN_ICMP && 1958c2ecf20Sopenharmony_ci serr->ee.ee_type == ICMP_DEST_UNREACH && 1968c2ecf20Sopenharmony_ci serr->ee.ee_code == ICMP_FRAG_NEEDED)) { 1978c2ecf20Sopenharmony_ci rxrpc_adjust_mtu(peer, serr); 1988c2ecf20Sopenharmony_ci rcu_read_unlock(); 1998c2ecf20Sopenharmony_ci rxrpc_free_skb(skb, rxrpc_skb_freed); 2008c2ecf20Sopenharmony_ci rxrpc_put_peer(peer); 2018c2ecf20Sopenharmony_ci _leave(" [MTU update]"); 2028c2ecf20Sopenharmony_ci return; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci rxrpc_store_error(peer, serr); 2068c2ecf20Sopenharmony_ci rcu_read_unlock(); 2078c2ecf20Sopenharmony_ci rxrpc_free_skb(skb, rxrpc_skb_freed); 2088c2ecf20Sopenharmony_ci rxrpc_put_peer(peer); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci _leave(""); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/* 2148c2ecf20Sopenharmony_ci * Map an error report to error codes on the peer record. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_cistatic void rxrpc_store_error(struct rxrpc_peer *peer, 2178c2ecf20Sopenharmony_ci struct sock_exterr_skb *serr) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci enum rxrpc_call_completion compl = RXRPC_CALL_NETWORK_ERROR; 2208c2ecf20Sopenharmony_ci struct sock_extended_err *ee; 2218c2ecf20Sopenharmony_ci int err; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci _enter(""); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ee = &serr->ee; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci err = ee->ee_errno; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci switch (ee->ee_origin) { 2308c2ecf20Sopenharmony_ci case SO_EE_ORIGIN_ICMP: 2318c2ecf20Sopenharmony_ci switch (ee->ee_type) { 2328c2ecf20Sopenharmony_ci case ICMP_DEST_UNREACH: 2338c2ecf20Sopenharmony_ci switch (ee->ee_code) { 2348c2ecf20Sopenharmony_ci case ICMP_NET_UNREACH: 2358c2ecf20Sopenharmony_ci _net("Rx Received ICMP Network Unreachable"); 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci case ICMP_HOST_UNREACH: 2388c2ecf20Sopenharmony_ci _net("Rx Received ICMP Host Unreachable"); 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci case ICMP_PORT_UNREACH: 2418c2ecf20Sopenharmony_ci _net("Rx Received ICMP Port Unreachable"); 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci case ICMP_NET_UNKNOWN: 2448c2ecf20Sopenharmony_ci _net("Rx Received ICMP Unknown Network"); 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci case ICMP_HOST_UNKNOWN: 2478c2ecf20Sopenharmony_ci _net("Rx Received ICMP Unknown Host"); 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci default: 2508c2ecf20Sopenharmony_ci _net("Rx Received ICMP DestUnreach code=%u", 2518c2ecf20Sopenharmony_ci ee->ee_code); 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci case ICMP_TIME_EXCEEDED: 2578c2ecf20Sopenharmony_ci _net("Rx Received ICMP TTL Exceeded"); 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci default: 2618c2ecf20Sopenharmony_ci _proto("Rx Received ICMP error { type=%u code=%u }", 2628c2ecf20Sopenharmony_ci ee->ee_type, ee->ee_code); 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci break; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci case SO_EE_ORIGIN_NONE: 2688c2ecf20Sopenharmony_ci case SO_EE_ORIGIN_LOCAL: 2698c2ecf20Sopenharmony_ci _proto("Rx Received local error { error=%d }", err); 2708c2ecf20Sopenharmony_ci compl = RXRPC_CALL_LOCAL_ERROR; 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci case SO_EE_ORIGIN_ICMP6: 2748c2ecf20Sopenharmony_ci if (err == EACCES) 2758c2ecf20Sopenharmony_ci err = EHOSTUNREACH; 2768c2ecf20Sopenharmony_ci fallthrough; 2778c2ecf20Sopenharmony_ci default: 2788c2ecf20Sopenharmony_ci _proto("Rx Received error report { orig=%u }", ee->ee_origin); 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci rxrpc_distribute_error(peer, err, compl); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/* 2868c2ecf20Sopenharmony_ci * Distribute an error that occurred on a peer. 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_cistatic void rxrpc_distribute_error(struct rxrpc_peer *peer, int error, 2898c2ecf20Sopenharmony_ci enum rxrpc_call_completion compl) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct rxrpc_call *call; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(call, &peer->error_targets, error_link) { 2948c2ecf20Sopenharmony_ci rxrpc_see_call(call); 2958c2ecf20Sopenharmony_ci rxrpc_set_call_completion(call, compl, 0, -error); 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/* 3008c2ecf20Sopenharmony_ci * Perform keep-alive pings. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_cistatic void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet, 3038c2ecf20Sopenharmony_ci struct list_head *collector, 3048c2ecf20Sopenharmony_ci time64_t base, 3058c2ecf20Sopenharmony_ci u8 cursor) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct rxrpc_peer *peer; 3088c2ecf20Sopenharmony_ci const u8 mask = ARRAY_SIZE(rxnet->peer_keepalive) - 1; 3098c2ecf20Sopenharmony_ci time64_t keepalive_at; 3108c2ecf20Sopenharmony_ci int slot; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci spin_lock_bh(&rxnet->peer_hash_lock); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci while (!list_empty(collector)) { 3158c2ecf20Sopenharmony_ci peer = list_entry(collector->next, 3168c2ecf20Sopenharmony_ci struct rxrpc_peer, keepalive_link); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci list_del_init(&peer->keepalive_link); 3198c2ecf20Sopenharmony_ci if (!rxrpc_get_peer_maybe(peer)) 3208c2ecf20Sopenharmony_ci continue; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (__rxrpc_use_local(peer->local)) { 3238c2ecf20Sopenharmony_ci spin_unlock_bh(&rxnet->peer_hash_lock); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci keepalive_at = peer->last_tx_at + RXRPC_KEEPALIVE_TIME; 3268c2ecf20Sopenharmony_ci slot = keepalive_at - base; 3278c2ecf20Sopenharmony_ci _debug("%02x peer %u t=%d {%pISp}", 3288c2ecf20Sopenharmony_ci cursor, peer->debug_id, slot, &peer->srx.transport); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (keepalive_at <= base || 3318c2ecf20Sopenharmony_ci keepalive_at > base + RXRPC_KEEPALIVE_TIME) { 3328c2ecf20Sopenharmony_ci rxrpc_send_keepalive(peer); 3338c2ecf20Sopenharmony_ci slot = RXRPC_KEEPALIVE_TIME; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* A transmission to this peer occurred since last we 3378c2ecf20Sopenharmony_ci * examined it so put it into the appropriate future 3388c2ecf20Sopenharmony_ci * bucket. 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_ci slot += cursor; 3418c2ecf20Sopenharmony_ci slot &= mask; 3428c2ecf20Sopenharmony_ci spin_lock_bh(&rxnet->peer_hash_lock); 3438c2ecf20Sopenharmony_ci list_add_tail(&peer->keepalive_link, 3448c2ecf20Sopenharmony_ci &rxnet->peer_keepalive[slot & mask]); 3458c2ecf20Sopenharmony_ci rxrpc_unuse_local(peer->local); 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci rxrpc_put_peer_locked(peer); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci spin_unlock_bh(&rxnet->peer_hash_lock); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci/* 3548c2ecf20Sopenharmony_ci * Perform keep-alive pings with VERSION packets to keep any NAT alive. 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_civoid rxrpc_peer_keepalive_worker(struct work_struct *work) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct rxrpc_net *rxnet = 3598c2ecf20Sopenharmony_ci container_of(work, struct rxrpc_net, peer_keepalive_work); 3608c2ecf20Sopenharmony_ci const u8 mask = ARRAY_SIZE(rxnet->peer_keepalive) - 1; 3618c2ecf20Sopenharmony_ci time64_t base, now, delay; 3628c2ecf20Sopenharmony_ci u8 cursor, stop; 3638c2ecf20Sopenharmony_ci LIST_HEAD(collector); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci now = ktime_get_seconds(); 3668c2ecf20Sopenharmony_ci base = rxnet->peer_keepalive_base; 3678c2ecf20Sopenharmony_ci cursor = rxnet->peer_keepalive_cursor; 3688c2ecf20Sopenharmony_ci _enter("%lld,%u", base - now, cursor); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (!rxnet->live) 3718c2ecf20Sopenharmony_ci return; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* Remove to a temporary list all the peers that are currently lodged 3748c2ecf20Sopenharmony_ci * in expired buckets plus all new peers. 3758c2ecf20Sopenharmony_ci * 3768c2ecf20Sopenharmony_ci * Everything in the bucket at the cursor is processed this 3778c2ecf20Sopenharmony_ci * second; the bucket at cursor + 1 goes at now + 1s and so 3788c2ecf20Sopenharmony_ci * on... 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_ci spin_lock_bh(&rxnet->peer_hash_lock); 3818c2ecf20Sopenharmony_ci list_splice_init(&rxnet->peer_keepalive_new, &collector); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci stop = cursor + ARRAY_SIZE(rxnet->peer_keepalive); 3848c2ecf20Sopenharmony_ci while (base <= now && (s8)(cursor - stop) < 0) { 3858c2ecf20Sopenharmony_ci list_splice_tail_init(&rxnet->peer_keepalive[cursor & mask], 3868c2ecf20Sopenharmony_ci &collector); 3878c2ecf20Sopenharmony_ci base++; 3888c2ecf20Sopenharmony_ci cursor++; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci base = now; 3928c2ecf20Sopenharmony_ci spin_unlock_bh(&rxnet->peer_hash_lock); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci rxnet->peer_keepalive_base = base; 3958c2ecf20Sopenharmony_ci rxnet->peer_keepalive_cursor = cursor; 3968c2ecf20Sopenharmony_ci rxrpc_peer_keepalive_dispatch(rxnet, &collector, base, cursor); 3978c2ecf20Sopenharmony_ci ASSERT(list_empty(&collector)); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* Schedule the timer for the next occupied timeslot. */ 4008c2ecf20Sopenharmony_ci cursor = rxnet->peer_keepalive_cursor; 4018c2ecf20Sopenharmony_ci stop = cursor + RXRPC_KEEPALIVE_TIME - 1; 4028c2ecf20Sopenharmony_ci for (; (s8)(cursor - stop) < 0; cursor++) { 4038c2ecf20Sopenharmony_ci if (!list_empty(&rxnet->peer_keepalive[cursor & mask])) 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci base++; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci now = ktime_get_seconds(); 4098c2ecf20Sopenharmony_ci delay = base - now; 4108c2ecf20Sopenharmony_ci if (delay < 1) 4118c2ecf20Sopenharmony_ci delay = 1; 4128c2ecf20Sopenharmony_ci delay *= HZ; 4138c2ecf20Sopenharmony_ci if (rxnet->live) 4148c2ecf20Sopenharmony_ci timer_reduce(&rxnet->peer_keepalive_timer, jiffies + delay); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci _leave(""); 4178c2ecf20Sopenharmony_ci} 418