/* Local endpoint object management * * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public Licence * as published by the Free Software Foundation; either version * 2 of the Licence, or (at your option) any later version. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include "ar-internal.h" static void rxrpc_local_processor(struct work_struct *); static void rxrpc_local_rcu(struct rcu_head *); static DEFINE_MUTEX(rxrpc_local_mutex); static LIST_HEAD(rxrpc_local_endpoints); /* * 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. */ static long rxrpc_local_cmp_key(const struct rxrpc_local *local, const struct sockaddr_rxrpc *srx) { long diff; diff = ((local->srx.transport_type - srx->transport_type) ?: (local->srx.transport_len - srx->transport_len) ?: (local->srx.transport.family - srx->transport.family)); if (diff != 0) return 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(); } } /* * Allocate a new local endpoint. */ static struct rxrpc_local *rxrpc_alloc_local(const struct sockaddr_rxrpc *srx) { struct rxrpc_local *local; local = kzalloc(sizeof(struct rxrpc_local), GFP_KERNEL); if (local) { atomic_set(&local->usage, 1); INIT_LIST_HEAD(&local->link); INIT_WORK(&local->processor, rxrpc_local_processor); init_rwsem(&local->defrag_sem); skb_queue_head_init(&local->reject_queue); skb_queue_head_init(&local->event_queue); local->client_conns = RB_ROOT; spin_lock_init(&local->client_conns_lock); spin_lock_init(&local->lock); rwlock_init(&local->services_lock); local->debug_id = atomic_inc_return(&rxrpc_debug_id); memcpy(&local->srx, srx, sizeof(*srx)); } _leave(" = %p", local); return local; } /* * create the local socket * - must be called with rxrpc_local_mutex locked */ static int rxrpc_open_socket(struct rxrpc_local *local) { struct sock *sock; int ret, opt; _enter("%p{%d,%d}", local, local->srx.transport_type, local->srx.transport.family); /* create a socket to represent the local endpoint */ ret = sock_create_kern(&init_net, local->srx.transport.family, local->srx.transport_type, 0, &local->socket); if (ret < 0) { _leave(" = %d [socket]", ret); return ret; } /* if a local address was supplied then bind it */ if (local->srx.transport_len > sizeof(sa_family_t)) { _debug("bind"); ret = kernel_bind(local->socket, (struct sockaddr *)&local->srx.transport, local->srx.transport_len); if (ret < 0) { _debug("bind failed %d", ret); goto error; } } /* we want to receive ICMP errors */ opt = 1; ret = kernel_setsockopt(local->socket, SOL_IP, IP_RECVERR, (char *) &opt, sizeof(opt)); if (ret < 0) { _debug("setsockopt failed"); goto error; } /* we want to set the don't fragment bit */ opt = IP_PMTUDISC_DO; ret = kernel_setsockopt(local->socket, SOL_IP, IP_MTU_DISCOVER, (char *) &opt, sizeof(opt)); if (ret < 0) { _debug("setsockopt failed"); goto error; } /* set the socket up */ sock = local->socket->sk; sock->sk_user_data = local; sock->sk_data_ready = rxrpc_data_ready; sock->sk_error_report = rxrpc_error_report; _leave(" = 0"); return 0; error: kernel_sock_shutdown(local->socket, SHUT_RDWR); local->socket->sk->sk_user_data = NULL; sock_release(local->socket); local->socket = NULL; _leave(" = %d", ret); return ret; } /* * Look up or create a new local endpoint using the specified local address. */ struct rxrpc_local *rxrpc_lookup_local(const struct sockaddr_rxrpc *srx) { struct rxrpc_local *local; struct list_head *cursor; const char *age; long diff; int ret; _enter("{%d,%d,%pISp}", srx->transport_type, srx->transport.family, &srx->transport); mutex_lock(&rxrpc_local_mutex); for (cursor = rxrpc_local_endpoints.next; cursor != &rxrpc_local_endpoints; cursor = cursor->next) { local = list_entry(cursor, struct rxrpc_local, link); diff = rxrpc_local_cmp_key(local, srx); if (diff < 0) continue; if (diff > 0) break; /* 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 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_get_local_maybe(local)) { cursor = cursor->next; list_del_init(&local->link); break; } age = "old"; goto found; } local = rxrpc_alloc_local(srx); if (!local) goto nomem; ret = rxrpc_open_socket(local); if (ret < 0) goto sock_error; list_add_tail(&local->link, cursor); age = "new"; found: mutex_unlock(&rxrpc_local_mutex); _net("LOCAL %s %d {%pISp}", age, local->debug_id, &local->srx.transport); _leave(" = %p", local); return local; nomem: ret = -ENOMEM; sock_error: mutex_unlock(&rxrpc_local_mutex); kfree(local); _leave(" = %d", ret); return ERR_PTR(ret); addr_in_use: mutex_unlock(&rxrpc_local_mutex); _leave(" = -EADDRINUSE"); return ERR_PTR(-EADDRINUSE); } /* * A local endpoint reached its end of life. */ void __rxrpc_put_local(struct rxrpc_local *local) { _enter("%d", local->debug_id); rxrpc_queue_work(&local->processor); } /* * 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. */ static void rxrpc_local_destroyer(struct rxrpc_local *local) { struct socket *socket = local->socket; _enter("%d", local->debug_id); /* We can get a race between an incoming call packet queueing the * processor again and the work processor starting the destruction * process which will shut down the UDP socket. */ if (local->dead) { _leave(" [already dead]"); return; } local->dead = true; mutex_lock(&rxrpc_local_mutex); list_del_init(&local->link); mutex_unlock(&rxrpc_local_mutex); ASSERT(RB_EMPTY_ROOT(&local->client_conns)); ASSERT(!local->service); if (socket) { local->socket = NULL; kernel_sock_shutdown(socket, SHUT_RDWR); socket->sk->sk_user_data = NULL; sock_release(socket); } /* At this point, there should be no more packets coming in to the * local endpoint. */ rxrpc_purge_queue(&local->reject_queue); rxrpc_purge_queue(&local->event_queue); _debug("rcu local %d", local->debug_id); call_rcu(&local->rcu, rxrpc_local_rcu); } /* * Process events on an endpoint */ static void rxrpc_local_processor(struct work_struct *work) { struct rxrpc_local *local = container_of(work, struct rxrpc_local, processor); bool again; _enter("%d", local->debug_id); do { again = false; if (atomic_read(&local->usage) == 0) return rxrpc_local_destroyer(local); if (!skb_queue_empty(&local->reject_queue)) { rxrpc_reject_packets(local); again = true; } if (!skb_queue_empty(&local->event_queue)) { rxrpc_process_local_events(local); again = true; } } while (again); } /* * Destroy a local endpoint after the RCU grace period expires. */ static void rxrpc_local_rcu(struct rcu_head *rcu) { struct rxrpc_local *local = container_of(rcu, struct rxrpc_local, rcu); _enter("%d", local->debug_id); ASSERT(!work_pending(&local->processor)); _net("DESTROY LOCAL %d", local->debug_id); kfree(local); _leave(""); } /* * Verify the local endpoint list is empty by this point. */ void __exit rxrpc_destroy_all_locals(void) { struct rxrpc_local *local; _enter(""); flush_workqueue(rxrpc_workqueue); if (!list_empty(&rxrpc_local_endpoints)) { mutex_lock(&rxrpc_local_mutex); list_for_each_entry(local, &rxrpc_local_endpoints, link) { pr_err("AF_RXRPC: Leaked local %p {%d}\n", local, atomic_read(&local->usage)); } mutex_unlock(&rxrpc_local_mutex); BUG(); } rcu_barrier(); } aa'>plain -rw-r--r--bpf-loader.h6215logplain -rw-r--r--bpf-prologue.c11226logplain -rw-r--r--bpf-prologue.h847logplain -rw-r--r--build-id.c19054logplain -rw-r--r--build-id.h1859logplain d---------c++184logplain -rw-r--r--cache.h733logplain -rw-r--r--call-path.c2893logplain -rw-r--r--call-path.h2203logplain -rw-r--r--callchain.c29332logplain -rw-r--r--callchain.h7881logplain -rw-r--r--cgroup.c3195logplain -rw-r--r--cgroup.h359logplain -rw-r--r--cloexec.c1950logplain -rw-r--r--cloexec.h251logplain -rw-r--r--color.c4787logplain -rw-r--r--color.h1647logplain -rw-r--r--comm.c2239logplain -rw-r--r--comm.h561logplain -rw-r--r--config.c16262logplain -rw-r--r--config.h2113logplain -rw-r--r--counts.c1026logplain -rw-r--r--counts.h790logplain -rw-r--r--cpumap.c12627logplain -rw-r--r--cpumap.h1954logplain -rw-r--r--cs-etm.h2061logplain -rw-r--r--ctype.c2018logplain -rw-r--r--data-convert-bt.c36671logplain -rw-r--r--data-convert-bt.h302logplain -rw-r--r--data-convert.h141logplain -rw-r--r--data.c3459logplain -rw-r--r--data.h1369logplain -rw-r--r--db-export.c11434logplain -rw-r--r--db-export.h3816logplain -rw-r--r--debug.c4417logplain -rw-r--r--debug.h2023logplain -rw-r--r--demangle-java.c4219logplain -rw-r--r--demangle-java.h249logplain -rw-r--r--demangle-rust.c6602logplain -rw-r--r--demangle-rust.h170logplain -rw-r--r--drv_configs.c1834logplain -rw-r--r--drv_configs.h844logplain -rw-r--r--dso.c31994logplain -rw-r--r--dso.h10399logplain -rw-r--r--dwarf-aux.c33828logplain -rw-r--r--dwarf-aux.h5101logplain -rw-r--r--dwarf-regs.c1816logplain -rw-r--r--env.c1884logplain -rw-r--r--env.h1268logplain -rw-r--r--event.c36670logplain -rw-r--r--event.h15997logplain -rw-r--r--evlist.c47104logplain -rw-r--r--evlist.h12584logplain -rw-r--r--evsel.c63917logplain -rw-r--r--evsel.h13041logplain -rw-r--r--evsel_fprintf.c5831logplain -rw-r--r--find-vdso-map.c581logplain -rw-r--r--genelf.c11653logplain -rw-r--r--genelf.h1814logplain -rw-r--r--genelf_debug.c14374logplain -rwxr-xr-xgenerate-cmdlist.sh1141logplain -rw-r--r--group.h122logplain -rw-r--r--header.c73410logplain -rw-r--r--header.h4365logplain -rw-r--r--help-unknown-cmd.c3221logplain