|
@@ -28,6 +28,15 @@ enum rxrpc_command {
|
|
|
RXRPC_CMD_REJECT_BUSY, /* [server] reject a call as busy */
|
|
|
};
|
|
|
|
|
|
+struct rxrpc_send_params {
|
|
|
+ s64 tx_total_len; /* Total Tx data length (if send data) */
|
|
|
+ unsigned long user_call_ID; /* User's call ID */
|
|
|
+ u32 abort_code; /* Abort code to Tx (if abort) */
|
|
|
+ enum rxrpc_command command : 8; /* The command to implement */
|
|
|
+ bool exclusive; /* Shared or exclusive call */
|
|
|
+ bool upgrade; /* If the connection is upgradeable */
|
|
|
+};
|
|
|
+
|
|
|
/*
|
|
|
* wait for space to appear in the transmit/ACK window
|
|
|
* - caller holds the socket locked
|
|
@@ -199,6 +208,13 @@ static int rxrpc_send_data(struct rxrpc_sock *rx,
|
|
|
|
|
|
more = msg->msg_flags & MSG_MORE;
|
|
|
|
|
|
+ if (call->tx_total_len != -1) {
|
|
|
+ if (len > call->tx_total_len)
|
|
|
+ return -EMSGSIZE;
|
|
|
+ if (!more && len != call->tx_total_len)
|
|
|
+ return -EMSGSIZE;
|
|
|
+ }
|
|
|
+
|
|
|
skb = call->tx_pending;
|
|
|
call->tx_pending = NULL;
|
|
|
rxrpc_see_skb(skb, rxrpc_skb_tx_seen);
|
|
@@ -291,6 +307,8 @@ static int rxrpc_send_data(struct rxrpc_sock *rx,
|
|
|
sp->remain -= copy;
|
|
|
skb->mark += copy;
|
|
|
copied += copy;
|
|
|
+ if (call->tx_total_len != -1)
|
|
|
+ call->tx_total_len -= copy;
|
|
|
}
|
|
|
|
|
|
/* check for the far side aborting the call or a network error
|
|
@@ -362,19 +380,12 @@ efault:
|
|
|
/*
|
|
|
* extract control messages from the sendmsg() control buffer
|
|
|
*/
|
|
|
-static int rxrpc_sendmsg_cmsg(struct msghdr *msg,
|
|
|
- unsigned long *user_call_ID,
|
|
|
- enum rxrpc_command *command,
|
|
|
- u32 *abort_code,
|
|
|
- bool *_exclusive,
|
|
|
- bool *_upgrade)
|
|
|
+static int rxrpc_sendmsg_cmsg(struct msghdr *msg, struct rxrpc_send_params *p)
|
|
|
{
|
|
|
struct cmsghdr *cmsg;
|
|
|
bool got_user_ID = false;
|
|
|
int len;
|
|
|
|
|
|
- *command = RXRPC_CMD_SEND_DATA;
|
|
|
-
|
|
|
if (msg->msg_controllen == 0)
|
|
|
return -EINVAL;
|
|
|
|
|
@@ -394,49 +405,55 @@ static int rxrpc_sendmsg_cmsg(struct msghdr *msg,
|
|
|
if (msg->msg_flags & MSG_CMSG_COMPAT) {
|
|
|
if (len != sizeof(u32))
|
|
|
return -EINVAL;
|
|
|
- *user_call_ID = *(u32 *) CMSG_DATA(cmsg);
|
|
|
+ p->user_call_ID = *(u32 *)CMSG_DATA(cmsg);
|
|
|
} else {
|
|
|
if (len != sizeof(unsigned long))
|
|
|
return -EINVAL;
|
|
|
- *user_call_ID = *(unsigned long *)
|
|
|
+ p->user_call_ID = *(unsigned long *)
|
|
|
CMSG_DATA(cmsg);
|
|
|
}
|
|
|
- _debug("User Call ID %lx", *user_call_ID);
|
|
|
got_user_ID = true;
|
|
|
break;
|
|
|
|
|
|
case RXRPC_ABORT:
|
|
|
- if (*command != RXRPC_CMD_SEND_DATA)
|
|
|
+ if (p->command != RXRPC_CMD_SEND_DATA)
|
|
|
return -EINVAL;
|
|
|
- *command = RXRPC_CMD_SEND_ABORT;
|
|
|
- if (len != sizeof(*abort_code))
|
|
|
+ p->command = RXRPC_CMD_SEND_ABORT;
|
|
|
+ if (len != sizeof(p->abort_code))
|
|
|
return -EINVAL;
|
|
|
- *abort_code = *(unsigned int *) CMSG_DATA(cmsg);
|
|
|
- _debug("Abort %x", *abort_code);
|
|
|
- if (*abort_code == 0)
|
|
|
+ p->abort_code = *(unsigned int *)CMSG_DATA(cmsg);
|
|
|
+ if (p->abort_code == 0)
|
|
|
return -EINVAL;
|
|
|
break;
|
|
|
|
|
|
case RXRPC_ACCEPT:
|
|
|
- if (*command != RXRPC_CMD_SEND_DATA)
|
|
|
+ if (p->command != RXRPC_CMD_SEND_DATA)
|
|
|
return -EINVAL;
|
|
|
- *command = RXRPC_CMD_ACCEPT;
|
|
|
+ p->command = RXRPC_CMD_ACCEPT;
|
|
|
if (len != 0)
|
|
|
return -EINVAL;
|
|
|
break;
|
|
|
|
|
|
case RXRPC_EXCLUSIVE_CALL:
|
|
|
- *_exclusive = true;
|
|
|
+ p->exclusive = true;
|
|
|
if (len != 0)
|
|
|
return -EINVAL;
|
|
|
break;
|
|
|
|
|
|
case RXRPC_UPGRADE_SERVICE:
|
|
|
- *_upgrade = true;
|
|
|
+ p->upgrade = true;
|
|
|
if (len != 0)
|
|
|
return -EINVAL;
|
|
|
break;
|
|
|
|
|
|
+ case RXRPC_TX_LENGTH:
|
|
|
+ if (p->tx_total_len != -1 || len != sizeof(__s64))
|
|
|
+ return -EINVAL;
|
|
|
+ p->tx_total_len = *(__s64 *)CMSG_DATA(cmsg);
|
|
|
+ if (p->tx_total_len < 0)
|
|
|
+ return -EINVAL;
|
|
|
+ break;
|
|
|
+
|
|
|
default:
|
|
|
return -EINVAL;
|
|
|
}
|
|
@@ -444,6 +461,8 @@ static int rxrpc_sendmsg_cmsg(struct msghdr *msg,
|
|
|
|
|
|
if (!got_user_ID)
|
|
|
return -EINVAL;
|
|
|
+ if (p->tx_total_len != -1 && p->command != RXRPC_CMD_SEND_DATA)
|
|
|
+ return -EINVAL;
|
|
|
_leave(" = 0");
|
|
|
return 0;
|
|
|
}
|
|
@@ -455,8 +474,7 @@ static int rxrpc_sendmsg_cmsg(struct msghdr *msg,
|
|
|
*/
|
|
|
static struct rxrpc_call *
|
|
|
rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg,
|
|
|
- unsigned long user_call_ID, bool exclusive,
|
|
|
- bool upgrade)
|
|
|
+ struct rxrpc_send_params *p)
|
|
|
__releases(&rx->sk.sk_lock.slock)
|
|
|
{
|
|
|
struct rxrpc_conn_parameters cp;
|
|
@@ -480,10 +498,11 @@ rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg,
|
|
|
cp.local = rx->local;
|
|
|
cp.key = rx->key;
|
|
|
cp.security_level = rx->min_sec_level;
|
|
|
- cp.exclusive = rx->exclusive | exclusive;
|
|
|
- cp.upgrade = upgrade;
|
|
|
+ cp.exclusive = rx->exclusive | p->exclusive;
|
|
|
+ cp.upgrade = p->upgrade;
|
|
|
cp.service_id = srx->srx_service;
|
|
|
- call = rxrpc_new_client_call(rx, &cp, srx, user_call_ID, GFP_KERNEL);
|
|
|
+ call = rxrpc_new_client_call(rx, &cp, srx, p->user_call_ID,
|
|
|
+ p->tx_total_len, GFP_KERNEL);
|
|
|
/* The socket is now unlocked */
|
|
|
|
|
|
_leave(" = %p\n", call);
|
|
@@ -499,26 +518,29 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
|
|
|
__releases(&rx->sk.sk_lock.slock)
|
|
|
{
|
|
|
enum rxrpc_call_state state;
|
|
|
- enum rxrpc_command cmd;
|
|
|
struct rxrpc_call *call;
|
|
|
- unsigned long user_call_ID = 0;
|
|
|
- bool exclusive = false;
|
|
|
- bool upgrade = true;
|
|
|
- u32 abort_code = 0;
|
|
|
int ret;
|
|
|
|
|
|
+ struct rxrpc_send_params p = {
|
|
|
+ .tx_total_len = -1,
|
|
|
+ .user_call_ID = 0,
|
|
|
+ .abort_code = 0,
|
|
|
+ .command = RXRPC_CMD_SEND_DATA,
|
|
|
+ .exclusive = false,
|
|
|
+ .upgrade = true,
|
|
|
+ };
|
|
|
+
|
|
|
_enter("");
|
|
|
|
|
|
- ret = rxrpc_sendmsg_cmsg(msg, &user_call_ID, &cmd, &abort_code,
|
|
|
- &exclusive, &upgrade);
|
|
|
+ ret = rxrpc_sendmsg_cmsg(msg, &p);
|
|
|
if (ret < 0)
|
|
|
goto error_release_sock;
|
|
|
|
|
|
- if (cmd == RXRPC_CMD_ACCEPT) {
|
|
|
+ if (p.command == RXRPC_CMD_ACCEPT) {
|
|
|
ret = -EINVAL;
|
|
|
if (rx->sk.sk_state != RXRPC_SERVER_LISTENING)
|
|
|
goto error_release_sock;
|
|
|
- call = rxrpc_accept_call(rx, user_call_ID, NULL);
|
|
|
+ call = rxrpc_accept_call(rx, p.user_call_ID, NULL);
|
|
|
/* The socket is now unlocked. */
|
|
|
if (IS_ERR(call))
|
|
|
return PTR_ERR(call);
|
|
@@ -526,13 +548,12 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- call = rxrpc_find_call_by_user_ID(rx, user_call_ID);
|
|
|
+ call = rxrpc_find_call_by_user_ID(rx, p.user_call_ID);
|
|
|
if (!call) {
|
|
|
ret = -EBADSLT;
|
|
|
- if (cmd != RXRPC_CMD_SEND_DATA)
|
|
|
+ if (p.command != RXRPC_CMD_SEND_DATA)
|
|
|
goto error_release_sock;
|
|
|
- call = rxrpc_new_client_call_for_sendmsg(rx, msg, user_call_ID,
|
|
|
- exclusive, upgrade);
|
|
|
+ call = rxrpc_new_client_call_for_sendmsg(rx, msg, &p);
|
|
|
/* The socket is now unlocked... */
|
|
|
if (IS_ERR(call))
|
|
|
return PTR_ERR(call);
|
|
@@ -556,6 +577,15 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
|
|
|
ret = -ERESTARTSYS;
|
|
|
goto error_put;
|
|
|
}
|
|
|
+
|
|
|
+ if (p.tx_total_len != -1) {
|
|
|
+ ret = -EINVAL;
|
|
|
+ if (call->tx_total_len != -1 ||
|
|
|
+ call->tx_pending ||
|
|
|
+ call->tx_top != 0)
|
|
|
+ goto error_put;
|
|
|
+ call->tx_total_len = p.tx_total_len;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
state = READ_ONCE(call->state);
|
|
@@ -565,11 +595,11 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
|
|
|
if (state >= RXRPC_CALL_COMPLETE) {
|
|
|
/* it's too late for this call */
|
|
|
ret = -ESHUTDOWN;
|
|
|
- } else if (cmd == RXRPC_CMD_SEND_ABORT) {
|
|
|
+ } else if (p.command == RXRPC_CMD_SEND_ABORT) {
|
|
|
ret = 0;
|
|
|
- if (rxrpc_abort_call("CMD", call, 0, abort_code, -ECONNABORTED))
|
|
|
+ if (rxrpc_abort_call("CMD", call, 0, p.abort_code, -ECONNABORTED))
|
|
|
ret = rxrpc_send_abort_packet(call);
|
|
|
- } else if (cmd != RXRPC_CMD_SEND_DATA) {
|
|
|
+ } else if (p.command != RXRPC_CMD_SEND_DATA) {
|
|
|
ret = -EINVAL;
|
|
|
} else if (rxrpc_is_client_call(call) &&
|
|
|
state != RXRPC_CALL_CLIENT_SEND_REQUEST) {
|
|
@@ -673,5 +703,24 @@ bool rxrpc_kernel_abort_call(struct socket *sock, struct rxrpc_call *call,
|
|
|
mutex_unlock(&call->user_mutex);
|
|
|
return aborted;
|
|
|
}
|
|
|
-
|
|
|
EXPORT_SYMBOL(rxrpc_kernel_abort_call);
|
|
|
+
|
|
|
+/**
|
|
|
+ * rxrpc_kernel_set_tx_length - Set the total Tx length on a call
|
|
|
+ * @sock: The socket the call is on
|
|
|
+ * @call: The call to be informed
|
|
|
+ * @tx_total_len: The amount of data to be transmitted for this call
|
|
|
+ *
|
|
|
+ * Allow a kernel service to set the total transmit length on a call. This
|
|
|
+ * allows buffer-to-packet encrypt-and-copy to be performed.
|
|
|
+ *
|
|
|
+ * This function is primarily for use for setting the reply length since the
|
|
|
+ * request length can be set when beginning the call.
|
|
|
+ */
|
|
|
+void rxrpc_kernel_set_tx_length(struct socket *sock, struct rxrpc_call *call,
|
|
|
+ s64 tx_total_len)
|
|
|
+{
|
|
|
+ WARN_ON(call->tx_total_len != -1);
|
|
|
+ call->tx_total_len = tx_total_len;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(rxrpc_kernel_set_tx_length);
|