// SPDX-License-Identifier: GPL-2.0-or-later /* Local endpoint object management * * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com)
*/
/* * Handle an ICMP/ICMP6 error turning up at the tunnel. Push it through the * usual mechanism so that it gets parsed and presented through the UDP * socket's error_report().
*/ staticvoid rxrpc_encap_err_rcv(struct sock *sk, struct sk_buff *skb, int err,
__be16 port, u32 info, u8 *payload)
{ if (ip_hdr(skb)->version == IPVERSION) return ip_icmp_error(sk, skb, err, port, info, payload); if (IS_ENABLED(CONFIG_AF_RXRPC_IPV6)) return ipv6_icmp_error(sk, skb, err, port, info, payload);
}
/* * Set or clear the Don't Fragment flag on a socket.
*/ void rxrpc_local_dont_fragment(conststruct rxrpc_local *local, bool set)
{ if (set)
ip_sock_set_mtu_discover(local->socket->sk, IP_PMTUDISC_DO); else
ip_sock_set_mtu_discover(local->socket->sk, IP_PMTUDISC_DONT);
}
/* * Compare a local to an address. Return -ve, 0 or +ve to indicate less than, * same or greater than. * * We explicitly don't compare the RxRPC service ID as we want to reject * conflicting uses by differing services. Further, we don't want to share * addresses with different options (IPv6), so we don't compare those bits * either.
*/ staticlong rxrpc_local_cmp_key(conststruct rxrpc_local *local, conststruct sockaddr_rxrpc *srx)
{ long diff;
switch (srx->transport.family) { case AF_INET: /* If the choice of UDP port is left up to the transport, then * the endpoint record doesn't match.
*/ return ((u16 __force)local->srx.transport.sin.sin_port -
(u16 __force)srx->transport.sin.sin_port) ?:
memcmp(&local->srx.transport.sin.sin_addr,
&srx->transport.sin.sin_addr, sizeof(struct in_addr)); #ifdef CONFIG_AF_RXRPC_IPV6 case AF_INET6: /* If the choice of UDP6 port is left up to the transport, then * the endpoint record doesn't match.
*/ return ((u16 __force)local->srx.transport.sin6.sin6_port -
(u16 __force)srx->transport.sin6.sin6_port) ?:
memcmp(&local->srx.transport.sin6.sin6_addr,
&srx->transport.sin6.sin6_addr, sizeof(struct in6_addr)); #endif default:
BUG();
}
}
/* set the socket up */
usk = local->socket->sk;
usk->sk_error_report = rxrpc_error_report;
switch (srx->transport.family) { case AF_INET6: /* we want to receive ICMPv6 errors */
ip6_sock_set_recverr(usk);
/* Fall through and set IPv4 options too otherwise we don't get * errors from IPv4 packets sent through the IPv6 socket.
*/
fallthrough; case AF_INET: /* we want to receive ICMP errors */
ip_sock_set_recverr(usk);
/* we want to set the don't fragment bit */
rxrpc_local_dont_fragment(local, true); break;
default:
BUG();
}
io_thread = kthread_run(rxrpc_io_thread, local, "krxrpcio/%u", ntohs(udp_conf.local_udp_port)); if (IS_ERR(io_thread)) {
ret = PTR_ERR(io_thread); goto error_sock;
}
/* * Look up or create a new local endpoint using the specified local address.
*/ struct rxrpc_local *rxrpc_lookup_local(struct net *net, conststruct sockaddr_rxrpc *srx)
{ struct rxrpc_local *local; struct rxrpc_net *rxnet = rxrpc_net(net); struct hlist_node *cursor; long diff; int ret;
hlist_for_each(cursor, &rxnet->local_endpoints) {
local = hlist_entry(cursor, struct rxrpc_local, link);
diff = rxrpc_local_cmp_key(local, srx); if (diff != 0) continue;
/* Services aren't allowed to share transport sockets, so * reject that here. It is possible that the object is dying - * but it may also still have the local transport address that * we want bound.
*/ if (srx->srx_service) {
local = NULL; goto addr_in_use;
}
/* Found a match. We want to replace a dying object. * Attempting to bind the transport socket may still fail if * we're attempting to use a local address that the dying * object is still using.
*/ if (!rxrpc_use_local(local, rxrpc_local_use_lookup)) break;
goto found;
}
local = rxrpc_alloc_local(net, srx); if (!local) goto nomem;
ret = rxrpc_open_socket(local, net); if (ret < 0) goto sock_error;
/* * Get a ref on a local endpoint.
*/ struct rxrpc_local *rxrpc_get_local(struct rxrpc_local *local, enum rxrpc_local_trace why)
{ int r, u;
u = atomic_read(&local->active_users);
__refcount_inc(&local->ref, &r);
trace_rxrpc_local(local->debug_id, why, r + 1, u); return local;
}
/* * Get a ref on a local endpoint unless its usage has already reached 0.
*/ struct rxrpc_local *rxrpc_get_local_maybe(struct rxrpc_local *local, enum rxrpc_local_trace why)
{ int r, u;
if (local && __refcount_inc_not_zero(&local->ref, &r)) {
u = atomic_read(&local->active_users);
trace_rxrpc_local(local->debug_id, why, r + 1, u); return local;
}
return NULL;
}
/* * Drop a ref on a local endpoint.
*/ void rxrpc_put_local(struct rxrpc_local *local, enum rxrpc_local_trace why)
{ unsignedint debug_id; bool dead; int r, u;
if (local) {
debug_id = local->debug_id;
u = atomic_read(&local->active_users);
dead = __refcount_dec_and_test(&local->ref, &r);
trace_rxrpc_local(debug_id, why, r, u);
if (dead)
call_rcu(&local->rcu, rxrpc_local_rcu);
}
}
/* * Start using a local endpoint.
*/ struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *local, enum rxrpc_local_trace why)
{
local = rxrpc_get_local_maybe(local, rxrpc_local_get_for_use); if (!local) return NULL;
if (!__rxrpc_use_local(local, why)) {
rxrpc_put_local(local, rxrpc_local_put_for_use); return NULL;
}
return local;
}
/* * Cease using a local endpoint. Once the number of active users reaches 0, we * start the closure of the transport in the I/O thread..
*/ void rxrpc_unuse_local(struct rxrpc_local *local, enum rxrpc_local_trace why)
{ unsignedint debug_id; int r, u;
if (local) {
debug_id = local->debug_id;
r = refcount_read(&local->ref);
u = atomic_dec_return(&local->active_users);
trace_rxrpc_local(debug_id, why, r, u); if (u == 0)
kthread_stop(local->io_thread);
}
}
/* * Destroy a local endpoint's socket and then hand the record to RCU to dispose * of. * * Closing the socket cannot be done from bottom half context or RCU callback * context because it might sleep.
*/ void rxrpc_destroy_local(struct rxrpc_local *local)
{ struct socket *socket = local->socket; struct rxrpc_net *rxnet = local->rxnet;
/* At this point, there should be no more packets coming in to the * local endpoint.
*/ #ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
rxrpc_purge_queue(&local->rx_delay_queue); #endif
rxrpc_purge_queue(&local->rx_queue);
rxrpc_purge_client_connections(local);
page_frag_cache_drain(&local->tx_alloc);
}
/* * Destroy a local endpoint after the RCU grace period expires.
*/ staticvoid rxrpc_local_rcu(struct rcu_head *rcu)
{ struct rxrpc_local *local = container_of(rcu, struct rxrpc_local, rcu);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.