Skip to content

Commit

Permalink
Merge tag 'rxrpc-rewrite-20170606' of git://git.kernel.org/pub/scm/li…
Browse files Browse the repository at this point in the history
…nux/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: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jun 6, 2017
2 parents 25f4115 + 4e25572 commit bb36314
Show file tree
Hide file tree
Showing 19 changed files with 245 additions and 59 deletions.
68 changes: 60 additions & 8 deletions Documentation/networking/rxrpc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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:
Expand All @@ -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.

Expand All @@ -634,15 +686,15 @@ 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

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:

Expand All @@ -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:

Expand Down
2 changes: 2 additions & 0 deletions include/linux/rxrpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions include/rxrpc/packet.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
1 change: 1 addition & 0 deletions include/trace/events/rxrpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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") \

Expand Down
88 changes: 64 additions & 24 deletions net/rxrpc/af_rxrpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -145,31 +144,48 @@ 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));
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;
}

local = rxrpc_lookup_local(sock_net(sock->sk), &rx->srx);
if (IS_ERR(local)) {
ret = PTR_ERR(local);
goto error_unlock;
}
if (service_id) {
write_lock(&local->services_lock);
if (rcu_access_pointer(local->service))
goto service_in_use;
rx->local = local;
rcu_assign_pointer(local->service, rx);
write_unlock(&local->services_lock);

rx->sk.sk_state = RXRPC_SERVER_BOUND;
} else {
rx->local = local;
rx->sk.sk_state = RXRPC_CLIENT_BOUND;
}
break;

if (service_id) {
write_lock(&local->services_lock);
if (rcu_access_pointer(local->service))
goto service_in_use;
rx->local = local;
rcu_assign_pointer(local->service, rx);
write_unlock(&local->services_lock);
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;

rx->sk.sk_state = RXRPC_SERVER_BOUND;
} else {
rx->local = local;
rx->sk.sk_state = RXRPC_CLIENT_BOUND;
default:
ret = -EINVAL;
goto error_unlock;
}

release_sock(&rx->sk);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down
17 changes: 14 additions & 3 deletions net/rxrpc/ar-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ enum {
RXRPC_CLIENT_UNBOUND, /* Unbound socket used as client */
RXRPC_CLIENT_BOUND, /* client local address bound */
RXRPC_SERVER_BOUND, /* server local address bound */
RXRPC_SERVER_BOUND2, /* second server local address bound */
RXRPC_SERVER_LISTENING, /* server listening for connections */
RXRPC_SERVER_LISTEN_DISABLED, /* server listening disabled */
RXRPC_CLOSE, /* socket is being closed */
Expand Down Expand Up @@ -142,8 +143,14 @@ struct rxrpc_sock {
u32 min_sec_level; /* minimum security level */
#define RXRPC_SECURITY_MAX RXRPC_SECURITY_ENCRYPT
bool exclusive; /* Exclusive connection for a client socket */
u16 second_service; /* Additional service bound to the endpoint */
struct {
/* Service upgrade information */
u16 from; /* Service ID to upgrade (if not 0) */
u16 to; /* service ID to upgrade to */
} service_upgrade;
sa_family_t family; /* Protocol family created with */
struct sockaddr_rxrpc srx; /* local address */
struct sockaddr_rxrpc srx; /* Primary Service/local addresses */
struct sockaddr_rxrpc connect_srx; /* Default client address from connect() */
};

Expand Down Expand Up @@ -313,6 +320,7 @@ struct rxrpc_conn_parameters {
struct rxrpc_peer *peer; /* Remote endpoint */
struct key *key; /* Security details */
bool exclusive; /* T if conn is exclusive */
bool upgrade; /* T if service ID can be upgraded */
u16 service_id; /* Service ID for this connection */
u32 security_level; /* Security level selected */
};
Expand All @@ -327,6 +335,7 @@ enum rxrpc_conn_flag {
RXRPC_CONN_EXPOSED, /* Conn has extra ref for exposure */
RXRPC_CONN_DONT_REUSE, /* Don't reuse this connection */
RXRPC_CONN_COUNTED, /* Counted by rxrpc_nr_client_conns */
RXRPC_CONN_PROBING_FOR_UPGRADE, /* Probing for service upgrade */
};

/*
Expand All @@ -343,6 +352,7 @@ enum rxrpc_conn_cache_state {
RXRPC_CONN_CLIENT_INACTIVE, /* Conn is not yet listed */
RXRPC_CONN_CLIENT_WAITING, /* Conn is on wait list, waiting for capacity */
RXRPC_CONN_CLIENT_ACTIVE, /* Conn is on active list, doing calls */
RXRPC_CONN_CLIENT_UPGRADE, /* Conn is on active list, probing for upgrade */
RXRPC_CONN_CLIENT_CULLED, /* Conn is culled and delisted, doing calls */
RXRPC_CONN_CLIENT_IDLE, /* Conn is on idle list, doing mostly nothing */
RXRPC_CONN__NR_CACHE_STATES
Expand Down Expand Up @@ -386,7 +396,6 @@ struct rxrpc_connection {
u32 call_counter; /* Call ID counter */
u32 last_call; /* ID of last call */
u8 last_type; /* Type of last packet */
u16 last_service_id;
union {
u32 last_seq;
u32 last_abort;
Expand Down Expand Up @@ -417,6 +426,7 @@ struct rxrpc_connection {
atomic_t serial; /* packet serial number counter */
unsigned int hi_serial; /* highest serial number received */
u32 security_nonce; /* response re-use preventer */
u16 service_id; /* Service ID, possibly upgraded */
u8 size_align; /* data size alignment (for security) */
u8 security_size; /* security header size */
u8 security_ix; /* security type */
Expand Down Expand Up @@ -859,7 +869,8 @@ static inline void rxrpc_put_connection(struct rxrpc_connection *conn)
struct rxrpc_connection *rxrpc_find_service_conn_rcu(struct rxrpc_peer *,
struct sk_buff *);
struct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *, gfp_t);
void rxrpc_new_incoming_connection(struct rxrpc_connection *, struct sk_buff *);
void rxrpc_new_incoming_connection(struct rxrpc_sock *,
struct rxrpc_connection *, struct sk_buff *);
void rxrpc_unpublish_service_conn(struct rxrpc_connection *);

/*
Expand Down
5 changes: 3 additions & 2 deletions net/rxrpc/call_accept.c
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx,
conn->params.local = local;
conn->params.peer = peer;
rxrpc_see_connection(conn);
rxrpc_new_incoming_connection(conn, skb);
rxrpc_new_incoming_connection(rx, conn, skb);
} else {
rxrpc_get_connection(conn);
}
Expand Down Expand Up @@ -341,7 +341,8 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local,

/* Get the socket providing the service */
rx = rcu_dereference(local->service);
if (rx && service_id == rx->srx.srx_service)
if (rx && (service_id == rx->srx.srx_service ||
service_id == rx->second_service))
goto found_service;

trace_rxrpc_abort("INV", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq,
Expand Down
Loading

0 comments on commit bb36314

Please sign in to comment.