diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index ceba28e9dce6..5519c2bef7f5 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -787,6 +787,10 @@ static int rxrpc_create(struct net *net, struct socket *sock, int protocol,
 	memset(&rx->srx, 0, sizeof(rx->srx));
 
 	rxnet = rxrpc_net(sock_net(&rx->sk));
+	mutex_lock(&rxnet->local_mutex);
+	hlist_add_head_rcu(&rx->ns_link, &rxnet->sockets);
+	mutex_unlock(&rxnet->local_mutex);
+
 	timer_reduce(&rxnet->peer_keepalive_timer, jiffies + 1);
 
 	_leave(" = 0 [%p]", rx);
@@ -851,6 +855,7 @@ static void rxrpc_sock_destructor(struct sock *sk)
 static int rxrpc_release_sock(struct sock *sk)
 {
 	struct rxrpc_sock *rx = rxrpc_sk(sk);
+	struct rxrpc_net *rxnet = rxrpc_net(sock_net(&rx->sk));
 
 	_enter("%p{%d,%d}", sk, sk->sk_state, refcount_read(&sk->sk_refcnt));
 
@@ -887,6 +892,10 @@ static int rxrpc_release_sock(struct sock *sk)
 	flush_workqueue(rxrpc_workqueue);
 	rxrpc_purge_queue(&sk->sk_receive_queue);
 
+	mutex_lock(&rxnet->local_mutex);
+	hlist_del_rcu(&rx->ns_link);
+	mutex_unlock(&rxnet->local_mutex);
+
 	rxrpc_unuse_local(rx->local);
 	rxrpc_put_local(rx->local);
 	rx->local = NULL;
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 571436064cd6..68d269598305 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -80,8 +80,9 @@ struct rxrpc_net {
 	struct work_struct	client_conn_reaper;
 	struct timer_list	client_conn_reap_timer;
 
+	struct hlist_head	sockets;	/* List of sockets */
 	struct hlist_head	local_endpoints;
-	struct mutex		local_mutex;	/* Lock for ->local_endpoints */
+	struct mutex		local_mutex;	/* Lock for ->local_endpoints, ->sockets */
 
 	DECLARE_HASHTABLE	(peer_hash, 10);
 	spinlock_t		peer_hash_lock;	/* Lock for ->peer_hash */
@@ -130,6 +131,7 @@ struct rxrpc_sock {
 	struct list_head	sock_calls;	/* List of calls owned by this socket */
 	struct list_head	to_be_accepted;	/* calls awaiting acceptance */
 	struct list_head	recvmsg_q;	/* Calls awaiting recvmsg's attention  */
+	struct hlist_node	ns_link;	/* Link in net->sockets */
 	rwlock_t		recvmsg_lock;	/* Lock for recvmsg_q */
 	struct key		*key;		/* security for this socket */
 	struct key		*securities;	/* list of server security descriptors */
@@ -1008,6 +1010,7 @@ extern const struct seq_operations rxrpc_call_seq_ops;
 extern const struct seq_operations rxrpc_connection_seq_ops;
 extern const struct seq_operations rxrpc_peer_seq_ops;
 extern const struct seq_operations rxrpc_local_seq_ops;
+extern const struct seq_operations rxrpc_socket_seq_ops;
 
 /*
  * recvmsg.c
diff --git a/net/rxrpc/net_ns.c b/net/rxrpc/net_ns.c
index bb4c25d6df64..4107099a259c 100644
--- a/net/rxrpc/net_ns.c
+++ b/net/rxrpc/net_ns.c
@@ -72,6 +72,7 @@ static __net_init int rxrpc_init_net(struct net *net)
 	timer_setup(&rxnet->client_conn_reap_timer,
 		    rxrpc_client_conn_reap_timeout, 0);
 
+	INIT_HLIST_HEAD(&rxnet->sockets);
 	INIT_HLIST_HEAD(&rxnet->local_endpoints);
 	mutex_init(&rxnet->local_mutex);
 
@@ -101,6 +102,9 @@ static __net_init int rxrpc_init_net(struct net *net)
 	proc_create_net("locals", 0444, rxnet->proc_net,
 			&rxrpc_local_seq_ops,
 			sizeof(struct seq_net_private));
+	proc_create_net("sockets", 0444, rxnet->proc_net,
+			&rxrpc_socket_seq_ops,
+			sizeof(struct seq_net_private));
 	return 0;
 
 err_proc:
diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c
index 245418943e01..b92303012338 100644
--- a/net/rxrpc/proc.c
+++ b/net/rxrpc/proc.c
@@ -397,3 +397,95 @@ const struct seq_operations rxrpc_local_seq_ops = {
 	.stop   = rxrpc_local_seq_stop,
 	.show   = rxrpc_local_seq_show,
 };
+
+/*
+ * Generate a list of extant sockets in /proc/net/rxrpc/sockets
+ */
+static int rxrpc_socket_seq_show(struct seq_file *seq, void *v)
+{
+	struct rxrpc_local *local;
+	struct rxrpc_sock *rx;
+	struct list_head *p;
+	char lbuff[50];
+	unsigned int nr_calls = 0, nr_acc = 0, nr_attend = 0;
+
+	if (v == SEQ_START_TOKEN) {
+		seq_puts(seq,
+			 "Proto Local                                          "
+			 " Use Svc1 Svc2 nCal nAcc nAtn\n");
+		return 0;
+	}
+
+	rx = hlist_entry(v, struct rxrpc_sock, ns_link);
+	local = rx->local;
+
+	if (local)
+		sprintf(lbuff, "%pISpc", &local->srx.transport);
+	else
+		sprintf(lbuff, "-");
+
+	read_lock(&rx->call_lock);
+	list_for_each(p, &rx->sock_calls) {
+		nr_calls++;
+	}
+	list_for_each(p, &rx->to_be_accepted) {
+		nr_acc++;
+	}
+	read_unlock(&rx->call_lock);
+
+	read_lock_bh(&rx->recvmsg_lock);
+	list_for_each(p, &rx->recvmsg_q) {
+		nr_attend++;
+	}
+	read_unlock_bh(&rx->recvmsg_lock);
+
+	seq_printf(seq,
+		   "UDP   %-47.47s %3d %4x %4x %4u %4u %4u\n",
+		   lbuff,
+		   refcount_read(&rx->sk.sk_refcnt),
+		   rx->srx.srx_service, rx->second_service,
+		   nr_calls, nr_acc, nr_attend);
+
+	return 0;
+}
+
+static void *rxrpc_socket_seq_start(struct seq_file *seq, loff_t *_pos)
+	__acquires(rcu)
+{
+	struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
+	unsigned int n;
+
+	rcu_read_lock();
+
+	if (*_pos >= UINT_MAX)
+		return NULL;
+
+	n = *_pos;
+	if (n == 0)
+		return SEQ_START_TOKEN;
+
+	return seq_hlist_start_rcu(&rxnet->sockets, n - 1);
+}
+
+static void *rxrpc_socket_seq_next(struct seq_file *seq, void *v, loff_t *_pos)
+{
+	struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
+
+	if (*_pos >= UINT_MAX)
+		return NULL;
+
+	return seq_hlist_next_rcu(v, &rxnet->sockets, _pos);
+}
+
+static void rxrpc_socket_seq_stop(struct seq_file *seq, void *v)
+	__releases(rcu)
+{
+	rcu_read_unlock();
+}
+
+const struct seq_operations rxrpc_socket_seq_ops = {
+	.start  = rxrpc_socket_seq_start,
+	.next   = rxrpc_socket_seq_next,
+	.stop   = rxrpc_socket_seq_stop,
+	.show   = rxrpc_socket_seq_show,
+};
