162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* Peer event handling, typically ICMP messages.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/net.h>
1062306a36Sopenharmony_ci#include <linux/skbuff.h>
1162306a36Sopenharmony_ci#include <linux/errqueue.h>
1262306a36Sopenharmony_ci#include <linux/udp.h>
1362306a36Sopenharmony_ci#include <linux/in.h>
1462306a36Sopenharmony_ci#include <linux/in6.h>
1562306a36Sopenharmony_ci#include <linux/icmp.h>
1662306a36Sopenharmony_ci#include <net/sock.h>
1762306a36Sopenharmony_ci#include <net/af_rxrpc.h>
1862306a36Sopenharmony_ci#include <net/ip.h>
1962306a36Sopenharmony_ci#include "ar-internal.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic void rxrpc_store_error(struct rxrpc_peer *, struct sk_buff *);
2262306a36Sopenharmony_cistatic void rxrpc_distribute_error(struct rxrpc_peer *, struct sk_buff *,
2362306a36Sopenharmony_ci				   enum rxrpc_call_completion, int);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * Find the peer associated with a local error.
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_cistatic struct rxrpc_peer *rxrpc_lookup_peer_local_rcu(struct rxrpc_local *local,
2962306a36Sopenharmony_ci						      const struct sk_buff *skb,
3062306a36Sopenharmony_ci						      struct sockaddr_rxrpc *srx)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct sock_exterr_skb *serr = SKB_EXT_ERR(skb);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	_enter("");
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	memset(srx, 0, sizeof(*srx));
3762306a36Sopenharmony_ci	srx->transport_type = local->srx.transport_type;
3862306a36Sopenharmony_ci	srx->transport_len = local->srx.transport_len;
3962306a36Sopenharmony_ci	srx->transport.family = local->srx.transport.family;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	/* Can we see an ICMP4 packet on an ICMP6 listening socket?  and vice
4262306a36Sopenharmony_ci	 * versa?
4362306a36Sopenharmony_ci	 */
4462306a36Sopenharmony_ci	switch (srx->transport.family) {
4562306a36Sopenharmony_ci	case AF_INET:
4662306a36Sopenharmony_ci		srx->transport_len = sizeof(srx->transport.sin);
4762306a36Sopenharmony_ci		srx->transport.family = AF_INET;
4862306a36Sopenharmony_ci		srx->transport.sin.sin_port = serr->port;
4962306a36Sopenharmony_ci		switch (serr->ee.ee_origin) {
5062306a36Sopenharmony_ci		case SO_EE_ORIGIN_ICMP:
5162306a36Sopenharmony_ci			memcpy(&srx->transport.sin.sin_addr,
5262306a36Sopenharmony_ci			       skb_network_header(skb) + serr->addr_offset,
5362306a36Sopenharmony_ci			       sizeof(struct in_addr));
5462306a36Sopenharmony_ci			break;
5562306a36Sopenharmony_ci		case SO_EE_ORIGIN_ICMP6:
5662306a36Sopenharmony_ci			memcpy(&srx->transport.sin.sin_addr,
5762306a36Sopenharmony_ci			       skb_network_header(skb) + serr->addr_offset + 12,
5862306a36Sopenharmony_ci			       sizeof(struct in_addr));
5962306a36Sopenharmony_ci			break;
6062306a36Sopenharmony_ci		default:
6162306a36Sopenharmony_ci			memcpy(&srx->transport.sin.sin_addr, &ip_hdr(skb)->saddr,
6262306a36Sopenharmony_ci			       sizeof(struct in_addr));
6362306a36Sopenharmony_ci			break;
6462306a36Sopenharmony_ci		}
6562306a36Sopenharmony_ci		break;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#ifdef CONFIG_AF_RXRPC_IPV6
6862306a36Sopenharmony_ci	case AF_INET6:
6962306a36Sopenharmony_ci		switch (serr->ee.ee_origin) {
7062306a36Sopenharmony_ci		case SO_EE_ORIGIN_ICMP6:
7162306a36Sopenharmony_ci			srx->transport.sin6.sin6_port = serr->port;
7262306a36Sopenharmony_ci			memcpy(&srx->transport.sin6.sin6_addr,
7362306a36Sopenharmony_ci			       skb_network_header(skb) + serr->addr_offset,
7462306a36Sopenharmony_ci			       sizeof(struct in6_addr));
7562306a36Sopenharmony_ci			break;
7662306a36Sopenharmony_ci		case SO_EE_ORIGIN_ICMP:
7762306a36Sopenharmony_ci			srx->transport_len = sizeof(srx->transport.sin);
7862306a36Sopenharmony_ci			srx->transport.family = AF_INET;
7962306a36Sopenharmony_ci			srx->transport.sin.sin_port = serr->port;
8062306a36Sopenharmony_ci			memcpy(&srx->transport.sin.sin_addr,
8162306a36Sopenharmony_ci			       skb_network_header(skb) + serr->addr_offset,
8262306a36Sopenharmony_ci			       sizeof(struct in_addr));
8362306a36Sopenharmony_ci			break;
8462306a36Sopenharmony_ci		default:
8562306a36Sopenharmony_ci			memcpy(&srx->transport.sin6.sin6_addr,
8662306a36Sopenharmony_ci			       &ipv6_hdr(skb)->saddr,
8762306a36Sopenharmony_ci			       sizeof(struct in6_addr));
8862306a36Sopenharmony_ci			break;
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci		break;
9162306a36Sopenharmony_ci#endif
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	default:
9462306a36Sopenharmony_ci		BUG();
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return rxrpc_lookup_peer_rcu(local, srx);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/*
10162306a36Sopenharmony_ci * Handle an MTU/fragmentation problem.
10262306a36Sopenharmony_ci */
10362306a36Sopenharmony_cistatic void rxrpc_adjust_mtu(struct rxrpc_peer *peer, unsigned int mtu)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	/* wind down the local interface MTU */
10662306a36Sopenharmony_ci	if (mtu > 0 && peer->if_mtu == 65535 && mtu < peer->if_mtu)
10762306a36Sopenharmony_ci		peer->if_mtu = mtu;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (mtu == 0) {
11062306a36Sopenharmony_ci		/* they didn't give us a size, estimate one */
11162306a36Sopenharmony_ci		mtu = peer->if_mtu;
11262306a36Sopenharmony_ci		if (mtu > 1500) {
11362306a36Sopenharmony_ci			mtu >>= 1;
11462306a36Sopenharmony_ci			if (mtu < 1500)
11562306a36Sopenharmony_ci				mtu = 1500;
11662306a36Sopenharmony_ci		} else {
11762306a36Sopenharmony_ci			mtu -= 100;
11862306a36Sopenharmony_ci			if (mtu < peer->hdrsize)
11962306a36Sopenharmony_ci				mtu = peer->hdrsize + 4;
12062306a36Sopenharmony_ci		}
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (mtu < peer->mtu) {
12462306a36Sopenharmony_ci		spin_lock(&peer->lock);
12562306a36Sopenharmony_ci		peer->mtu = mtu;
12662306a36Sopenharmony_ci		peer->maxdata = peer->mtu - peer->hdrsize;
12762306a36Sopenharmony_ci		spin_unlock(&peer->lock);
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/*
13262306a36Sopenharmony_ci * Handle an error received on the local endpoint.
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_civoid rxrpc_input_error(struct rxrpc_local *local, struct sk_buff *skb)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	struct sock_exterr_skb *serr = SKB_EXT_ERR(skb);
13762306a36Sopenharmony_ci	struct sockaddr_rxrpc srx;
13862306a36Sopenharmony_ci	struct rxrpc_peer *peer = NULL;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	_enter("L=%x", local->debug_id);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (!skb->len && serr->ee.ee_origin == SO_EE_ORIGIN_TIMESTAMPING) {
14362306a36Sopenharmony_ci		_leave("UDP empty message");
14462306a36Sopenharmony_ci		return;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	rcu_read_lock();
14862306a36Sopenharmony_ci	peer = rxrpc_lookup_peer_local_rcu(local, skb, &srx);
14962306a36Sopenharmony_ci	if (peer && !rxrpc_get_peer_maybe(peer, rxrpc_peer_get_input_error))
15062306a36Sopenharmony_ci		peer = NULL;
15162306a36Sopenharmony_ci	rcu_read_unlock();
15262306a36Sopenharmony_ci	if (!peer)
15362306a36Sopenharmony_ci		return;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	trace_rxrpc_rx_icmp(peer, &serr->ee, &srx);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if ((serr->ee.ee_origin == SO_EE_ORIGIN_ICMP &&
15862306a36Sopenharmony_ci	     serr->ee.ee_type == ICMP_DEST_UNREACH &&
15962306a36Sopenharmony_ci	     serr->ee.ee_code == ICMP_FRAG_NEEDED)) {
16062306a36Sopenharmony_ci		rxrpc_adjust_mtu(peer, serr->ee.ee_info);
16162306a36Sopenharmony_ci		goto out;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	rxrpc_store_error(peer, skb);
16562306a36Sopenharmony_ciout:
16662306a36Sopenharmony_ci	rxrpc_put_peer(peer, rxrpc_peer_put_input_error);
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci/*
17062306a36Sopenharmony_ci * Map an error report to error codes on the peer record.
17162306a36Sopenharmony_ci */
17262306a36Sopenharmony_cistatic void rxrpc_store_error(struct rxrpc_peer *peer, struct sk_buff *skb)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	enum rxrpc_call_completion compl = RXRPC_CALL_NETWORK_ERROR;
17562306a36Sopenharmony_ci	struct sock_exterr_skb *serr = SKB_EXT_ERR(skb);
17662306a36Sopenharmony_ci	struct sock_extended_err *ee = &serr->ee;
17762306a36Sopenharmony_ci	int err = ee->ee_errno;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	_enter("");
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	switch (ee->ee_origin) {
18262306a36Sopenharmony_ci	case SO_EE_ORIGIN_NONE:
18362306a36Sopenharmony_ci	case SO_EE_ORIGIN_LOCAL:
18462306a36Sopenharmony_ci		compl = RXRPC_CALL_LOCAL_ERROR;
18562306a36Sopenharmony_ci		break;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	case SO_EE_ORIGIN_ICMP6:
18862306a36Sopenharmony_ci		if (err == EACCES)
18962306a36Sopenharmony_ci			err = EHOSTUNREACH;
19062306a36Sopenharmony_ci		fallthrough;
19162306a36Sopenharmony_ci	case SO_EE_ORIGIN_ICMP:
19262306a36Sopenharmony_ci	default:
19362306a36Sopenharmony_ci		break;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	rxrpc_distribute_error(peer, skb, compl, err);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci/*
20062306a36Sopenharmony_ci * Distribute an error that occurred on a peer.
20162306a36Sopenharmony_ci */
20262306a36Sopenharmony_cistatic void rxrpc_distribute_error(struct rxrpc_peer *peer, struct sk_buff *skb,
20362306a36Sopenharmony_ci				   enum rxrpc_call_completion compl, int err)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct rxrpc_call *call;
20662306a36Sopenharmony_ci	HLIST_HEAD(error_targets);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	spin_lock(&peer->lock);
20962306a36Sopenharmony_ci	hlist_move_list(&peer->error_targets, &error_targets);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	while (!hlist_empty(&error_targets)) {
21262306a36Sopenharmony_ci		call = hlist_entry(error_targets.first,
21362306a36Sopenharmony_ci				   struct rxrpc_call, error_link);
21462306a36Sopenharmony_ci		hlist_del_init(&call->error_link);
21562306a36Sopenharmony_ci		spin_unlock(&peer->lock);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		rxrpc_see_call(call, rxrpc_call_see_distribute_error);
21862306a36Sopenharmony_ci		rxrpc_set_call_completion(call, compl, 0, -err);
21962306a36Sopenharmony_ci		rxrpc_input_call_event(call, skb);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		spin_lock(&peer->lock);
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	spin_unlock(&peer->lock);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci/*
22862306a36Sopenharmony_ci * Perform keep-alive pings.
22962306a36Sopenharmony_ci */
23062306a36Sopenharmony_cistatic void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet,
23162306a36Sopenharmony_ci					  struct list_head *collector,
23262306a36Sopenharmony_ci					  time64_t base,
23362306a36Sopenharmony_ci					  u8 cursor)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct rxrpc_peer *peer;
23662306a36Sopenharmony_ci	const u8 mask = ARRAY_SIZE(rxnet->peer_keepalive) - 1;
23762306a36Sopenharmony_ci	time64_t keepalive_at;
23862306a36Sopenharmony_ci	bool use;
23962306a36Sopenharmony_ci	int slot;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	spin_lock(&rxnet->peer_hash_lock);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	while (!list_empty(collector)) {
24462306a36Sopenharmony_ci		peer = list_entry(collector->next,
24562306a36Sopenharmony_ci				  struct rxrpc_peer, keepalive_link);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		list_del_init(&peer->keepalive_link);
24862306a36Sopenharmony_ci		if (!rxrpc_get_peer_maybe(peer, rxrpc_peer_get_keepalive))
24962306a36Sopenharmony_ci			continue;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci		use = __rxrpc_use_local(peer->local, rxrpc_local_use_peer_keepalive);
25262306a36Sopenharmony_ci		spin_unlock(&rxnet->peer_hash_lock);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		if (use) {
25562306a36Sopenharmony_ci			keepalive_at = peer->last_tx_at + RXRPC_KEEPALIVE_TIME;
25662306a36Sopenharmony_ci			slot = keepalive_at - base;
25762306a36Sopenharmony_ci			_debug("%02x peer %u t=%d {%pISp}",
25862306a36Sopenharmony_ci			       cursor, peer->debug_id, slot, &peer->srx.transport);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci			if (keepalive_at <= base ||
26162306a36Sopenharmony_ci			    keepalive_at > base + RXRPC_KEEPALIVE_TIME) {
26262306a36Sopenharmony_ci				rxrpc_send_keepalive(peer);
26362306a36Sopenharmony_ci				slot = RXRPC_KEEPALIVE_TIME;
26462306a36Sopenharmony_ci			}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci			/* A transmission to this peer occurred since last we
26762306a36Sopenharmony_ci			 * examined it so put it into the appropriate future
26862306a36Sopenharmony_ci			 * bucket.
26962306a36Sopenharmony_ci			 */
27062306a36Sopenharmony_ci			slot += cursor;
27162306a36Sopenharmony_ci			slot &= mask;
27262306a36Sopenharmony_ci			spin_lock(&rxnet->peer_hash_lock);
27362306a36Sopenharmony_ci			list_add_tail(&peer->keepalive_link,
27462306a36Sopenharmony_ci				      &rxnet->peer_keepalive[slot & mask]);
27562306a36Sopenharmony_ci			spin_unlock(&rxnet->peer_hash_lock);
27662306a36Sopenharmony_ci			rxrpc_unuse_local(peer->local, rxrpc_local_unuse_peer_keepalive);
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci		rxrpc_put_peer(peer, rxrpc_peer_put_keepalive);
27962306a36Sopenharmony_ci		spin_lock(&rxnet->peer_hash_lock);
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	spin_unlock(&rxnet->peer_hash_lock);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/*
28662306a36Sopenharmony_ci * Perform keep-alive pings with VERSION packets to keep any NAT alive.
28762306a36Sopenharmony_ci */
28862306a36Sopenharmony_civoid rxrpc_peer_keepalive_worker(struct work_struct *work)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct rxrpc_net *rxnet =
29162306a36Sopenharmony_ci		container_of(work, struct rxrpc_net, peer_keepalive_work);
29262306a36Sopenharmony_ci	const u8 mask = ARRAY_SIZE(rxnet->peer_keepalive) - 1;
29362306a36Sopenharmony_ci	time64_t base, now, delay;
29462306a36Sopenharmony_ci	u8 cursor, stop;
29562306a36Sopenharmony_ci	LIST_HEAD(collector);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	now = ktime_get_seconds();
29862306a36Sopenharmony_ci	base = rxnet->peer_keepalive_base;
29962306a36Sopenharmony_ci	cursor = rxnet->peer_keepalive_cursor;
30062306a36Sopenharmony_ci	_enter("%lld,%u", base - now, cursor);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (!rxnet->live)
30362306a36Sopenharmony_ci		return;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/* Remove to a temporary list all the peers that are currently lodged
30662306a36Sopenharmony_ci	 * in expired buckets plus all new peers.
30762306a36Sopenharmony_ci	 *
30862306a36Sopenharmony_ci	 * Everything in the bucket at the cursor is processed this
30962306a36Sopenharmony_ci	 * second; the bucket at cursor + 1 goes at now + 1s and so
31062306a36Sopenharmony_ci	 * on...
31162306a36Sopenharmony_ci	 */
31262306a36Sopenharmony_ci	spin_lock(&rxnet->peer_hash_lock);
31362306a36Sopenharmony_ci	list_splice_init(&rxnet->peer_keepalive_new, &collector);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	stop = cursor + ARRAY_SIZE(rxnet->peer_keepalive);
31662306a36Sopenharmony_ci	while (base <= now && (s8)(cursor - stop) < 0) {
31762306a36Sopenharmony_ci		list_splice_tail_init(&rxnet->peer_keepalive[cursor & mask],
31862306a36Sopenharmony_ci				      &collector);
31962306a36Sopenharmony_ci		base++;
32062306a36Sopenharmony_ci		cursor++;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	base = now;
32462306a36Sopenharmony_ci	spin_unlock(&rxnet->peer_hash_lock);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	rxnet->peer_keepalive_base = base;
32762306a36Sopenharmony_ci	rxnet->peer_keepalive_cursor = cursor;
32862306a36Sopenharmony_ci	rxrpc_peer_keepalive_dispatch(rxnet, &collector, base, cursor);
32962306a36Sopenharmony_ci	ASSERT(list_empty(&collector));
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	/* Schedule the timer for the next occupied timeslot. */
33262306a36Sopenharmony_ci	cursor = rxnet->peer_keepalive_cursor;
33362306a36Sopenharmony_ci	stop = cursor + RXRPC_KEEPALIVE_TIME - 1;
33462306a36Sopenharmony_ci	for (; (s8)(cursor - stop) < 0; cursor++) {
33562306a36Sopenharmony_ci		if (!list_empty(&rxnet->peer_keepalive[cursor & mask]))
33662306a36Sopenharmony_ci			break;
33762306a36Sopenharmony_ci		base++;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	now = ktime_get_seconds();
34162306a36Sopenharmony_ci	delay = base - now;
34262306a36Sopenharmony_ci	if (delay < 1)
34362306a36Sopenharmony_ci		delay = 1;
34462306a36Sopenharmony_ci	delay *= HZ;
34562306a36Sopenharmony_ci	if (rxnet->live)
34662306a36Sopenharmony_ci		timer_reduce(&rxnet->peer_keepalive_timer, jiffies + delay);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	_leave("");
34962306a36Sopenharmony_ci}
350