Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bb363140 authored by David S. Miller's avatar David S. Miller
Browse files

Merge tag 'rxrpc-rewrite-20170606' of...

Merge tag 'rxrpc-rewrite-20170606' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs



David Howells says:

====================
rxrpc: Support service upgrade

Here's a set of patches that allow AF_RXRPC to support the AuriStor service
upgrade facility.  This allows the server to change the service ID
requested to an upgraded service if the client requests it upon the
initiation of a connection.

This is used by the AuriStor AFS-compatible servers to implement IPv6
handling and improved facilities by providing improved volume location,
volume, protection, file and cache management services.  Note that certain
parts of the AFS protocol carry hard-coded IPv4 addresses.

The reason AuriStor does it this way is that probing the improved service
ID first will not incur an ABORT or any other response on some servers if
the server is not listening on it - and so one have to employ a timeout.

This is implemented in the server by allowing an AF_RXRPC server to call
bind() twice on a socket to allow it to listen on two service IDs and then
call setsockopt() to instruct the server to upgrade one into the other if
the client requests it (by setting userStatus to 1 on the first DATA packet
on a connection).  If the upgrade occurs, all further operations on that
connection are done with the new service ID.  AF_RXRPC has to handle this
automatically as connections are not exposed to userspace.

Clients can request this facility by setting an RXRPC_UPGRADE_SERVICE
command in the sendmsg() control buffer and then observing the resultant
service ID in the msg_addr returned by recvmsg().  This should only be used
to probe the service.  Clients should then use the returned service ID in
all subsequent communications with that server.  Note that the kernel will
not retain this information should the connection expire from its cache.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 25f41150 4e255721
Loading
Loading
Loading
Loading
+60 −8
Original line number Diff line number Diff line
@@ -325,6 +325,8 @@ calls, to invoke certain actions and to report certain conditions. These are:
	RXRPC_LOCAL_ERROR	-rt error num	Local error encountered
	RXRPC_NEW_CALL		-r- n/a		New call received
	RXRPC_ACCEPT		s-- n/a		Accept new call
	RXRPC_EXCLUSIVE_CALL	s-- n/a		Make an exclusive client call
	RXRPC_UPGRADE_SERVICE	s-- n/a		Client call can be upgraded

	(SRT = usable in Sendmsg / delivered by Recvmsg / Terminal message)

@@ -387,6 +389,23 @@ calls, to invoke certain actions and to report certain conditions. These are:
     return error ENODATA.  If the user ID is already in use by another call,
     then error EBADSLT will be returned.

 (*) RXRPC_EXCLUSIVE_CALL

     This is used to indicate that a client call should be made on a one-off
     connection.  The connection is discarded once the call has terminated.

 (*) RXRPC_UPGRADE_SERVICE

     This is used to make a client call to probe if the specified service ID
     may be upgraded by the server.  The caller must check msg_name returned to
     recvmsg() for the service ID actually in use.  The operation probed must
     be one that takes the same arguments in both services.

     Once this has been used to establish the upgrade capability (or lack
     thereof) of the server, the service ID returned should be used for all
     future communication to that server and RXRPC_UPGRADE_SERVICE should no
     longer be set.


==============
SOCKET OPTIONS
@@ -433,6 +452,13 @@ AF_RXRPC sockets support a few socket options at the SOL_RXRPC level:
	 Encrypted checksum plus entire packet padded and encrypted, including
	 actual packet length.

 (*) RXRPC_UPGRADEABLE_SERVICE

     This is used to indicate that a service socket with two bindings may
     upgrade one bound service to the other if requested by the client.  optval
     must point to an array of two unsigned short ints.  The first is the
     service ID to upgrade from and the second the service ID to upgrade to.


========
SECURITY
@@ -559,6 +585,17 @@ A client would issue an operation by:
     buffer instead, and MSG_EOR will be flagged to indicate the end of that
     call.

A client may ask for a service ID it knows and ask that this be upgraded to a
better service if one is available by supplying RXRPC_UPGRADE_SERVICE on the
first sendmsg() of a call.  The client should then check srx_service in the
msg_name filled in by recvmsg() when collecting the result.  srx_service will
hold the same value as given to sendmsg() if the upgrade request was ignored by
the service - otherwise it will be altered to indicate the service ID the
server upgraded to.  Note that the upgraded service ID is chosen by the server.
The caller has to wait until it sees the service ID in the reply before sending
any more calls (further calls to the same destination will be blocked until the
probe is concluded).


====================
EXAMPLE SERVER USAGE
@@ -588,7 +625,7 @@ A server would be set up to accept operations in the following manner:
     The keyring can be manipulated after it has been given to the socket. This
     permits the server to add more keys, replace keys, etc. whilst it is live.

 (2) A local address must then be bound:
 (3) A local address must then be bound:

	struct sockaddr_rxrpc srx = {
		.srx_family	= AF_RXRPC,
@@ -600,11 +637,26 @@ A server would be set up to accept operations in the following manner:
	};
	bind(server, &srx, sizeof(srx));

 (3) The server is then set to listen out for incoming calls:
     More than one service ID may be bound to a socket, provided the transport
     parameters are the same.  The limit is currently two.  To do this, bind()
     should be called twice.

 (4) If service upgrading is required, first two service IDs must have been
     bound and then the following option must be set:

	unsigned short service_ids[2] = { from_ID, to_ID };
	setsockopt(server, SOL_RXRPC, RXRPC_UPGRADEABLE_SERVICE,
		   service_ids, sizeof(service_ids));

     This will automatically upgrade connections on service from_ID to service
     to_ID if they request it.  This will be reflected in msg_name obtained
     through recvmsg() when the request data is delivered to userspace.

 (5) The server is then set to listen out for incoming calls:

	listen(server, 100);

 (4) The kernel notifies the server of pending incoming connections by sending
 (6) The kernel notifies the server of pending incoming connections by sending
     it a message for each.  This is received with recvmsg() on the server
     socket.  It has no data, and has a single dataless control message
     attached:
@@ -616,13 +668,13 @@ A server would be set up to accept operations in the following manner:
     the time it is accepted - in which case the first call still on the queue
     will be accepted.

 (5) The server then accepts the new call by issuing a sendmsg() with two
 (7) The server then accepts the new call by issuing a sendmsg() with two
     pieces of control data and no actual data:

	RXRPC_ACCEPT		- indicate connection acceptance
	RXRPC_USER_CALL_ID	- specify user ID for this call

 (6) The first request data packet will then be posted to the server socket for
 (8) The first request data packet will then be posted to the server socket for
     recvmsg() to pick up.  At that point, the RxRPC address for the call can
     be read from the address fields in the msghdr struct.

@@ -634,7 +686,7 @@ A server would be set up to accept operations in the following manner:

	RXRPC_USER_CALL_ID	- specifies the user ID for this call

 (8) The reply data should then be posted to the server socket using a series
 (9) The reply data should then be posted to the server socket using a series
     of sendmsg() calls, each with the following control messages attached:

	RXRPC_USER_CALL_ID	- specifies the user ID for this call
@@ -642,7 +694,7 @@ A server would be set up to accept operations in the following manner:
     MSG_MORE should be set in msghdr::msg_flags on all but the last message
     for a particular call.

 (9) The final ACK from the client will be posted for retrieval by recvmsg()
(10) The final ACK from the client will be posted for retrieval by recvmsg()
     when it is received.  It will take the form of a dataless message with two
     control messages attached:

@@ -652,7 +704,7 @@ A server would be set up to accept operations in the following manner:
     MSG_EOR will be flagged to indicate that this is the final message for
     this call.

(10) Up to the point the final packet of reply data is sent, the call can be
(11) Up to the point the final packet of reply data is sent, the call can be
     aborted by calling sendmsg() with a dataless message with the following
     control messages attached:

+2 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ struct sockaddr_rxrpc {
#define RXRPC_SECURITY_KEYRING		2	/* [srvr] set ring of server security keys */
#define RXRPC_EXCLUSIVE_CONNECTION	3	/* Deprecated; use RXRPC_EXCLUSIVE_CALL instead */
#define RXRPC_MIN_SECURITY_LEVEL	4	/* minimum security level */
#define RXRPC_UPGRADEABLE_SERVICE	5	/* Upgrade service[0] -> service[1] */

/*
 * RxRPC control messages
@@ -53,6 +54,7 @@ struct sockaddr_rxrpc {
#define RXRPC_NEW_CALL		8	/* -r: [Service] new incoming call notification */
#define RXRPC_ACCEPT		9	/* s-: [Service] accept request */
#define RXRPC_EXCLUSIVE_CALL	10	/* s-: Call should be on exclusive connection */
#define RXRPC_UPGRADE_SERVICE	11	/* s-: Request service upgrade for client call */

/*
 * RxRPC security levels
+2 −0
Original line number Diff line number Diff line
@@ -58,6 +58,8 @@ struct rxrpc_wire_header {
#define RXRPC_SLOW_START_OK	0x20		/* [ACK] slow start supported */

	uint8_t		userStatus;	/* app-layer defined status */
#define RXRPC_USERSTATUS_SERVICE_UPGRADE 0x01	/* AuriStor service upgrade request */
	
	uint8_t		securityIndex;	/* security protocol ID */
	union {
		__be16	_rsvd;		/* reserved */
+1 −0
Original line number Diff line number Diff line
@@ -233,6 +233,7 @@ enum rxrpc_congest_change {
	EM(RXRPC_CONN_CLIENT_INACTIVE,		"Inac") \
	EM(RXRPC_CONN_CLIENT_WAITING,		"Wait") \
	EM(RXRPC_CONN_CLIENT_ACTIVE,		"Actv") \
	EM(RXRPC_CONN_CLIENT_UPGRADE,		"Upgd") \
	EM(RXRPC_CONN_CLIENT_CULLED,		"Cull") \
	E_(RXRPC_CONN_CLIENT_IDLE,		"Idle") \

+64 −24
Original line number Diff line number Diff line
@@ -131,9 +131,8 @@ static int rxrpc_validate_address(struct rxrpc_sock *rx,
static int rxrpc_bind(struct socket *sock, struct sockaddr *saddr, int len)
{
	struct sockaddr_rxrpc *srx = (struct sockaddr_rxrpc *)saddr;
	struct sock *sk = sock->sk;
	struct rxrpc_local *local;
	struct rxrpc_sock *rx = rxrpc_sk(sk);
	struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
	u16 service_id = srx->srx_service;
	int ret;

@@ -145,14 +144,10 @@ static int rxrpc_bind(struct socket *sock, struct sockaddr *saddr, int len)

	lock_sock(&rx->sk);

	if (rx->sk.sk_state != RXRPC_UNBOUND) {
		ret = -EINVAL;
		goto error_unlock;
	}

	memcpy(&rx->srx, srx, sizeof(rx->srx));

	local = rxrpc_lookup_local(sock_net(sock->sk), &rx->srx);
	switch (rx->sk.sk_state) {
	case RXRPC_UNBOUND:
		rx->srx = *srx;
		local = rxrpc_lookup_local(sock_net(&rx->sk), &rx->srx);
		if (IS_ERR(local)) {
			ret = PTR_ERR(local);
			goto error_unlock;
@@ -171,6 +166,27 @@ static int rxrpc_bind(struct socket *sock, struct sockaddr *saddr, int len)
			rx->local = local;
			rx->sk.sk_state = RXRPC_CLIENT_BOUND;
		}
		break;

	case RXRPC_SERVER_BOUND:
		ret = -EINVAL;
		if (service_id == 0)
			goto error_unlock;
		ret = -EADDRINUSE;
		if (service_id == rx->srx.srx_service)
			goto error_unlock;
		ret = -EINVAL;
		srx->srx_service = rx->srx.srx_service;
		if (memcmp(srx, &rx->srx, sizeof(*srx)) != 0)
			goto error_unlock;
		rx->second_service = service_id;
		rx->sk.sk_state = RXRPC_SERVER_BOUND2;
		break;

	default:
		ret = -EINVAL;
		goto error_unlock;
	}

	release_sock(&rx->sk);
	_leave(" = 0");
@@ -206,6 +222,7 @@ static int rxrpc_listen(struct socket *sock, int backlog)
		ret = -EADDRNOTAVAIL;
		break;
	case RXRPC_SERVER_BOUND:
	case RXRPC_SERVER_BOUND2:
		ASSERT(rx->local != NULL);
		max = READ_ONCE(rxrpc_max_backlog);
		ret = -EINVAL;
@@ -473,6 +490,7 @@ static int rxrpc_setsockopt(struct socket *sock, int level, int optname,
{
	struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
	unsigned int min_sec_level;
	u16 service_upgrade[2];
	int ret;

	_enter(",%d,%d,,%d", level, optname, optlen);
@@ -529,6 +547,28 @@ static int rxrpc_setsockopt(struct socket *sock, int level, int optname,
			rx->min_sec_level = min_sec_level;
			goto success;

		case RXRPC_UPGRADEABLE_SERVICE:
			ret = -EINVAL;
			if (optlen != sizeof(service_upgrade) ||
			    rx->service_upgrade.from != 0)
				goto error;
			ret = -EISCONN;
			if (rx->sk.sk_state != RXRPC_SERVER_BOUND2)
				goto error;
			ret = -EFAULT;
			if (copy_from_user(service_upgrade, optval,
					   sizeof(service_upgrade)) != 0)
				goto error;
			ret = -EINVAL;
			if ((service_upgrade[0] != rx->srx.srx_service ||
			     service_upgrade[1] != rx->second_service) &&
			    (service_upgrade[0] != rx->second_service ||
			     service_upgrade[1] != rx->srx.srx_service))
				goto error;
			rx->service_upgrade.from = service_upgrade[0];
			rx->service_upgrade.to = service_upgrade[1];
			goto success;

		default:
			break;
		}
Loading