⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ar-accept.c

📁 linux 内核源代码
💻 C
字号:
/* incoming call handling * * Copyright (C) 2007 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 License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */#include <linux/module.h>#include <linux/net.h>#include <linux/skbuff.h>#include <linux/errqueue.h>#include <linux/udp.h>#include <linux/in.h>#include <linux/in6.h>#include <linux/icmp.h>#include <net/sock.h>#include <net/af_rxrpc.h>#include <net/ip.h>#include "ar-internal.h"/* * generate a connection-level abort */static int rxrpc_busy(struct rxrpc_local *local, struct sockaddr_rxrpc *srx,		      struct rxrpc_header *hdr){	struct msghdr msg;	struct kvec iov[1];	size_t len;	int ret;	_enter("%d,,", local->debug_id);	msg.msg_name	= &srx->transport.sin;	msg.msg_namelen	= sizeof(srx->transport.sin);	msg.msg_control	= NULL;	msg.msg_controllen = 0;	msg.msg_flags	= 0;	hdr->seq	= 0;	hdr->type	= RXRPC_PACKET_TYPE_BUSY;	hdr->flags	= 0;	hdr->userStatus	= 0;	hdr->_rsvd	= 0;	iov[0].iov_base	= hdr;	iov[0].iov_len	= sizeof(*hdr);	len = iov[0].iov_len;	hdr->serial = htonl(1);	_proto("Tx BUSY %%%u", ntohl(hdr->serial));	ret = kernel_sendmsg(local->socket, &msg, iov, 1, len);	if (ret < 0) {		_leave(" = -EAGAIN [sendmsg failed: %d]", ret);		return -EAGAIN;	}	_leave(" = 0");	return 0;}/* * accept an incoming call that needs peer, transport and/or connection setting * up */static int rxrpc_accept_incoming_call(struct rxrpc_local *local,				      struct rxrpc_sock *rx,				      struct sk_buff *skb,				      struct sockaddr_rxrpc *srx){	struct rxrpc_connection *conn;	struct rxrpc_transport *trans;	struct rxrpc_skb_priv *sp, *nsp;	struct rxrpc_peer *peer;	struct rxrpc_call *call;	struct sk_buff *notification;	int ret;	_enter("");	sp = rxrpc_skb(skb);	/* get a notification message to send to the server app */	notification = alloc_skb(0, GFP_NOFS);	rxrpc_new_skb(notification);	notification->mark = RXRPC_SKB_MARK_NEW_CALL;	peer = rxrpc_get_peer(srx, GFP_NOIO);	if (IS_ERR(peer)) {		_debug("no peer");		ret = -EBUSY;		goto error;	}	trans = rxrpc_get_transport(local, peer, GFP_NOIO);	rxrpc_put_peer(peer);	if (!trans) {		_debug("no trans");		ret = -EBUSY;		goto error;	}	conn = rxrpc_incoming_connection(trans, &sp->hdr, GFP_NOIO);	rxrpc_put_transport(trans);	if (IS_ERR(conn)) {		_debug("no conn");		ret = PTR_ERR(conn);		goto error;	}	call = rxrpc_incoming_call(rx, conn, &sp->hdr, GFP_NOIO);	rxrpc_put_connection(conn);	if (IS_ERR(call)) {		_debug("no call");		ret = PTR_ERR(call);		goto error;	}	/* attach the call to the socket */	read_lock_bh(&local->services_lock);	if (rx->sk.sk_state == RXRPC_CLOSE)		goto invalid_service;	write_lock(&rx->call_lock);	if (!test_and_set_bit(RXRPC_CALL_INIT_ACCEPT, &call->flags)) {		rxrpc_get_call(call);		spin_lock(&call->conn->state_lock);		if (sp->hdr.securityIndex > 0 &&		    call->conn->state == RXRPC_CONN_SERVER_UNSECURED) {			_debug("await conn sec");			list_add_tail(&call->accept_link, &rx->secureq);			call->conn->state = RXRPC_CONN_SERVER_CHALLENGING;			atomic_inc(&call->conn->usage);			set_bit(RXRPC_CONN_CHALLENGE, &call->conn->events);			rxrpc_queue_conn(call->conn);		} else {			_debug("conn ready");			call->state = RXRPC_CALL_SERVER_ACCEPTING;			list_add_tail(&call->accept_link, &rx->acceptq);			rxrpc_get_call(call);			nsp = rxrpc_skb(notification);			nsp->call = call;			ASSERTCMP(atomic_read(&call->usage), >=, 3);			_debug("notify");			spin_lock(&call->lock);			ret = rxrpc_queue_rcv_skb(call, notification, true,						  false);			spin_unlock(&call->lock);			notification = NULL;			if (ret < 0)				BUG();		}		spin_unlock(&call->conn->state_lock);		_debug("queued");	}	write_unlock(&rx->call_lock);	_debug("process");	rxrpc_fast_process_packet(call, skb);	_debug("done");	read_unlock_bh(&local->services_lock);	rxrpc_free_skb(notification);	rxrpc_put_call(call);	_leave(" = 0");	return 0;invalid_service:	_debug("invalid");	read_unlock_bh(&local->services_lock);	read_lock_bh(&call->state_lock);	if (!test_bit(RXRPC_CALL_RELEASE, &call->flags) &&	    !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) {		rxrpc_get_call(call);		rxrpc_queue_call(call);	}	read_unlock_bh(&call->state_lock);	rxrpc_put_call(call);	ret = -ECONNREFUSED;error:	rxrpc_free_skb(notification);	_leave(" = %d", ret);	return ret;}/* * accept incoming calls that need peer, transport and/or connection setting up * - the packets we get are all incoming client DATA packets that have seq == 1 */void rxrpc_accept_incoming_calls(struct work_struct *work){	struct rxrpc_local *local =		container_of(work, struct rxrpc_local, acceptor);	struct rxrpc_skb_priv *sp;	struct sockaddr_rxrpc srx;	struct rxrpc_sock *rx;	struct sk_buff *skb;	__be16 service_id;	int ret;	_enter("%d", local->debug_id);	read_lock_bh(&rxrpc_local_lock);	if (atomic_read(&local->usage) > 0)		rxrpc_get_local(local);	else		local = NULL;	read_unlock_bh(&rxrpc_local_lock);	if (!local) {		_leave(" [local dead]");		return;	}process_next_packet:	skb = skb_dequeue(&local->accept_queue);	if (!skb) {		rxrpc_put_local(local);		_leave("\n");		return;	}	_net("incoming call skb %p", skb);	sp = rxrpc_skb(skb);	/* determine the remote address */	memset(&srx, 0, sizeof(srx));	srx.srx_family = AF_RXRPC;	srx.transport.family = local->srx.transport.family;	srx.transport_type = local->srx.transport_type;	switch (srx.transport.family) {	case AF_INET:		srx.transport_len = sizeof(struct sockaddr_in);		srx.transport.sin.sin_port = udp_hdr(skb)->source;		srx.transport.sin.sin_addr.s_addr = ip_hdr(skb)->saddr;		break;	default:		goto busy;	}	/* get the socket providing the service */	service_id = sp->hdr.serviceId;	read_lock_bh(&local->services_lock);	list_for_each_entry(rx, &local->services, listen_link) {		if (rx->service_id == service_id &&		    rx->sk.sk_state != RXRPC_CLOSE)			goto found_service;	}	read_unlock_bh(&local->services_lock);	goto invalid_service;found_service:	_debug("found service %hd", ntohs(rx->service_id));	if (sk_acceptq_is_full(&rx->sk))		goto backlog_full;	sk_acceptq_added(&rx->sk);	sock_hold(&rx->sk);	read_unlock_bh(&local->services_lock);	ret = rxrpc_accept_incoming_call(local, rx, skb, &srx);	if (ret < 0)		sk_acceptq_removed(&rx->sk);	sock_put(&rx->sk);	switch (ret) {	case -ECONNRESET: /* old calls are ignored */	case -ECONNABORTED: /* aborted calls are reaborted or ignored */	case 0:		goto process_next_packet;	case -ECONNREFUSED:		goto invalid_service;	case -EBUSY:		goto busy;	case -EKEYREJECTED:		goto security_mismatch;	default:		BUG();	}backlog_full:	read_unlock_bh(&local->services_lock);busy:	rxrpc_busy(local, &srx, &sp->hdr);	rxrpc_free_skb(skb);	goto process_next_packet;invalid_service:	skb->priority = RX_INVALID_OPERATION;	rxrpc_reject_packet(local, skb);	goto process_next_packet;	/* can't change connection security type mid-flow */security_mismatch:	skb->priority = RX_PROTOCOL_ERROR;	rxrpc_reject_packet(local, skb);	goto process_next_packet;}/* * handle acceptance of a call by userspace * - assign the user call ID to the call at the front of the queue */struct rxrpc_call *rxrpc_accept_call(struct rxrpc_sock *rx,				     unsigned long user_call_ID){	struct rxrpc_call *call;	struct rb_node *parent, **pp;	int ret;	_enter(",%lx", user_call_ID);	ASSERT(!irqs_disabled());	write_lock(&rx->call_lock);	ret = -ENODATA;	if (list_empty(&rx->acceptq))		goto out;	/* check the user ID isn't already in use */	ret = -EBADSLT;	pp = &rx->calls.rb_node;	parent = NULL;	while (*pp) {		parent = *pp;		call = rb_entry(parent, struct rxrpc_call, sock_node);		if (user_call_ID < call->user_call_ID)			pp = &(*pp)->rb_left;		else if (user_call_ID > call->user_call_ID)			pp = &(*pp)->rb_right;		else			goto out;	}	/* dequeue the first call and check it's still valid */	call = list_entry(rx->acceptq.next, struct rxrpc_call, accept_link);	list_del_init(&call->accept_link);	sk_acceptq_removed(&rx->sk);	write_lock_bh(&call->state_lock);	switch (call->state) {	case RXRPC_CALL_SERVER_ACCEPTING:		call->state = RXRPC_CALL_SERVER_RECV_REQUEST;		break;	case RXRPC_CALL_REMOTELY_ABORTED:	case RXRPC_CALL_LOCALLY_ABORTED:		ret = -ECONNABORTED;		goto out_release;	case RXRPC_CALL_NETWORK_ERROR:		ret = call->conn->error;		goto out_release;	case RXRPC_CALL_DEAD:		ret = -ETIME;		goto out_discard;	default:		BUG();	}	/* formalise the acceptance */	call->user_call_ID = user_call_ID;	rb_link_node(&call->sock_node, parent, pp);	rb_insert_color(&call->sock_node, &rx->calls);	if (test_and_set_bit(RXRPC_CALL_HAS_USERID, &call->flags))		BUG();	if (test_and_set_bit(RXRPC_CALL_ACCEPTED, &call->events))		BUG();	rxrpc_queue_call(call);	rxrpc_get_call(call);	write_unlock_bh(&call->state_lock);	write_unlock(&rx->call_lock);	_leave(" = %p{%d}", call, call->debug_id);	return call;	/* if the call is already dying or dead, then we leave the socket's ref	 * on it to be released by rxrpc_dead_call_expired() as induced by	 * rxrpc_release_call() */out_release:	_debug("release %p", call);	if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) &&	    !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events))		rxrpc_queue_call(call);out_discard:	write_unlock_bh(&call->state_lock);	_debug("discard %p", call);out:	write_unlock(&rx->call_lock);	_leave(" = %d", ret);	return ERR_PTR(ret);}/* * handle rejectance of a call by userspace * - reject the call at the front of the queue */int rxrpc_reject_call(struct rxrpc_sock *rx){	struct rxrpc_call *call;	int ret;	_enter("");	ASSERT(!irqs_disabled());	write_lock(&rx->call_lock);	ret = -ENODATA;	if (list_empty(&rx->acceptq))		goto out;	/* dequeue the first call and check it's still valid */	call = list_entry(rx->acceptq.next, struct rxrpc_call, accept_link);	list_del_init(&call->accept_link);	sk_acceptq_removed(&rx->sk);	write_lock_bh(&call->state_lock);	switch (call->state) {	case RXRPC_CALL_SERVER_ACCEPTING:		call->state = RXRPC_CALL_SERVER_BUSY;		if (test_and_set_bit(RXRPC_CALL_REJECT_BUSY, &call->events))			rxrpc_queue_call(call);		ret = 0;		goto out_release;	case RXRPC_CALL_REMOTELY_ABORTED:	case RXRPC_CALL_LOCALLY_ABORTED:		ret = -ECONNABORTED;		goto out_release;	case RXRPC_CALL_NETWORK_ERROR:		ret = call->conn->error;		goto out_release;	case RXRPC_CALL_DEAD:		ret = -ETIME;		goto out_discard;	default:		BUG();	}	/* if the call is already dying or dead, then we leave the socket's ref	 * on it to be released by rxrpc_dead_call_expired() as induced by	 * rxrpc_release_call() */out_release:	_debug("release %p", call);	if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) &&	    !test_and_set_bit(RXRPC_CALL_RELEASE, &call->events))		rxrpc_queue_call(call);out_discard:	write_unlock_bh(&call->state_lock);	_debug("discard %p", call);out:	write_unlock(&rx->call_lock);	_leave(" = %d", ret);	return ret;}/** * rxrpc_kernel_accept_call - Allow a kernel service to accept an incoming call * @sock: The socket on which the impending call is waiting * @user_call_ID: The tag to attach to the call * * Allow a kernel service to accept an incoming call, assuming the incoming * call is still valid. */struct rxrpc_call *rxrpc_kernel_accept_call(struct socket *sock,					    unsigned long user_call_ID){	struct rxrpc_call *call;	_enter(",%lx", user_call_ID);	call = rxrpc_accept_call(rxrpc_sk(sock->sk), user_call_ID);	_leave(" = %p", call);	return call;}EXPORT_SYMBOL(rxrpc_kernel_accept_call);/** * rxrpc_kernel_reject_call - Allow a kernel service to reject an incoming call * @sock: The socket on which the impending call is waiting * * Allow a kernel service to reject an incoming call with a BUSY message, * assuming the incoming call is still valid. */int rxrpc_kernel_reject_call(struct socket *sock){	int ret;	_enter("");	ret = rxrpc_reject_call(rxrpc_sk(sock->sk));	_leave(" = %d", ret);	return ret;}EXPORT_SYMBOL(rxrpc_kernel_reject_call);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -