// SPDX-License-Identifier: GPL-2.0-or-later /* connection-level event handling * * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com)
*/
/* * Set the completion state on an aborted connection.
*/ staticbool rxrpc_set_conn_aborted(struct rxrpc_connection *conn,
s32 abort_code, int err, enum rxrpc_call_completion compl)
{ bool aborted = false;
if (conn->state != RXRPC_CONN_ABORTED) {
spin_lock_irq(&conn->state_lock); if (conn->state != RXRPC_CONN_ABORTED) {
conn->abort_code = abort_code;
conn->error = err;
conn->completion = compl; /* Order the abort info before the state change. */
smp_store_release(&conn->state, RXRPC_CONN_ABORTED);
set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags);
set_bit(RXRPC_CONN_EV_ABORT_CALLS, &conn->events);
aborted = true;
}
spin_unlock_irq(&conn->state_lock);
}
return aborted;
}
/* * Mark a socket buffer to indicate that the connection it's on should be aborted.
*/ int rxrpc_abort_conn(struct rxrpc_connection *conn, struct sk_buff *skb,
s32 abort_code, int err, enum rxrpc_abort_reason why)
{
u32 cid = conn->proto.cid, call = 0, seq = 0;
if (skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
if (sp && sp->hdr.type == RXRPC_PACKET_TYPE_ACK) { if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
&pkt.ack, sizeof(pkt.ack)) < 0) return; if (pkt.ack.reason == RXRPC_ACK_PING_RESPONSE) return;
}
chan = &conn->channels[channel];
/* If the last call got moved on whilst we were waiting to run, just * ignore this packet.
*/
call_id = chan->last_call; if (skb && call_id != sp->hdr.callNumber) return;
/* * pass a connection-level abort onto all calls on that connection
*/ staticvoid rxrpc_abort_calls(struct rxrpc_connection *conn)
{ struct rxrpc_call *call; int i;
for (i = 0; i < RXRPC_MAXCALLS; i++) {
call = conn->channels[i].call; if (call) {
rxrpc_see_call(call, rxrpc_call_see_conn_abort);
rxrpc_set_call_completion(call,
conn->completion,
conn->abort_code,
conn->error);
rxrpc_poke_call(call, rxrpc_call_poke_conn_abort);
}
}
_leave("");
}
/* * mark a call as being on a now-secured channel * - must be called with BH's disabled.
*/ staticvoid rxrpc_call_is_secure(struct rxrpc_call *call)
{ if (call && __test_and_clear_bit(RXRPC_CALL_CONN_CHALLENGING, &call->flags))
rxrpc_notify_socket(call);
}
switch (sp->hdr.type) { case RXRPC_PACKET_TYPE_CHALLENGE:
ret = conn->security->respond_to_challenge(conn, skb);
sp->chall.conn = NULL;
rxrpc_put_connection(conn, rxrpc_conn_put_challenge_input); return ret;
case RXRPC_PACKET_TYPE_RESPONSE:
ret = conn->security->verify_response(conn, skb); if (ret < 0) return ret;
ret = conn->security->init_connection_security(
conn, conn->key->payload.data[0]); if (ret < 0) return ret;
spin_lock_irq(&conn->state_lock); if (conn->state == RXRPC_CONN_SERVICE_CHALLENGING)
conn->state = RXRPC_CONN_SERVICE;
spin_unlock_irq(&conn->state_lock);
if (conn->state == RXRPC_CONN_SERVICE) { /* Offload call state flipping to the I/O thread. As * we've already received the packet, put it on the * front of the queue.
*/
sp->poke_conn = rxrpc_get_connection(
conn, rxrpc_conn_get_poke_secured);
skb->mark = RXRPC_SKB_MARK_SERVICE_CONN_SECURED;
rxrpc_get_skb(skb, rxrpc_skb_get_conn_secured);
skb_queue_head(&conn->local->rx_queue, skb);
rxrpc_wake_up_io_thread(conn->local);
} return 0;
default:
WARN_ON_ONCE(1); return -EPROTO;
}
}
/* * set up security and issue a challenge
*/ staticvoid rxrpc_secure_connection(struct rxrpc_connection *conn)
{ if (conn->security->issue_challenge(conn) < 0)
rxrpc_abort_conn(conn, NULL, RX_CALL_DEAD, -ENOMEM,
rxrpc_abort_nomem);
}
/* * Process delayed final ACKs that we haven't subsumed into a subsequent call.
*/ void rxrpc_process_delayed_final_acks(struct rxrpc_connection *conn, bool force)
{ unsignedlong j = jiffies, next_j; unsignedint channel; bool set;
if (test_and_clear_bit(RXRPC_CONN_EV_CHALLENGE, &conn->events))
rxrpc_secure_connection(conn);
/* go through the conn-level event packets, releasing the ref on this
* connection that each one has when we've finished with it */ while ((skb = skb_dequeue(&conn->rx_queue))) {
rxrpc_see_skb(skb, rxrpc_skb_see_conn_work);
ret = rxrpc_process_event(conn, skb); switch (ret) { case -ENOMEM: case -EAGAIN:
skb_queue_head(&conn->rx_queue, skb);
rxrpc_queue_conn(conn, rxrpc_conn_queue_retry_work); break; default:
rxrpc_free_skb(skb, rxrpc_skb_put_conn_work); break;
}
}
}
if (__rxrpc_use_local(conn->local, rxrpc_local_use_conn_work)) {
rxrpc_do_process_connection(conn);
rxrpc_unuse_local(conn->local, rxrpc_local_unuse_conn_work);
}
}
/* * post connection-level events to the connection * - this includes challenges, responses, some aborts and call terminal packet * retransmission.
*/ staticvoid rxrpc_post_packet_to_conn(struct rxrpc_connection *conn, struct sk_buff *skb)
{
_enter("%p,%p", conn, skb);
/* * Post a CHALLENGE packet to the socket of one of a connection's calls so that * it can get application data to include in the packet, possibly querying * userspace.
*/ staticbool rxrpc_post_challenge(struct rxrpc_connection *conn, struct sk_buff *skb)
{ struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_call *call = NULL; struct rxrpc_sock *rx; bool respond = false;
switch (sp->hdr.type) { case RXRPC_PACKET_TYPE_BUSY: /* Just ignore BUSY packets for now. */ returntrue;
case RXRPC_PACKET_TYPE_ABORT: if (rxrpc_is_conn_aborted(conn)) returntrue;
rxrpc_input_conn_abort(conn, skb);
rxrpc_abort_calls(conn); returntrue;
case RXRPC_PACKET_TYPE_CHALLENGE:
rxrpc_see_skb(skb, rxrpc_skb_see_oob_challenge); if (rxrpc_is_conn_aborted(conn)) { if (conn->completion == RXRPC_CALL_LOCALLY_ABORTED)
rxrpc_send_conn_abort(conn); returntrue;
} if (!conn->security->validate_challenge(conn, skb)) returnfalse; return rxrpc_post_challenge(conn, skb);
case RXRPC_PACKET_TYPE_RESPONSE: if (rxrpc_is_conn_aborted(conn)) { if (conn->completion == RXRPC_CALL_LOCALLY_ABORTED)
rxrpc_send_conn_abort(conn); returntrue;
}
rxrpc_post_packet_to_conn(conn, skb); returntrue;
/* Process delayed ACKs whose time has come. */ if (conn->flags & RXRPC_CONN_FINAL_ACK_MASK)
rxrpc_process_delayed_final_acks(conn, false);
}
/* * Post a RESPONSE message to the I/O thread for transmission.
*/ void rxrpc_post_response(struct rxrpc_connection *conn, struct sk_buff *skb)
{ struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_local *local = conn->local; struct sk_buff *old;
_enter("%x", sp->resp.challenge_serial);
spin_lock_irq(&local->lock);
old = conn->tx_response; if (old) { struct rxrpc_skb_priv *osp = rxrpc_skb(skb);
/* Always go with the response to the most recent challenge. */ if (after(sp->resp.challenge_serial, osp->resp.challenge_serial))
conn->tx_response = old; else
old = skb;
} else {
conn->tx_response = skb;
}
spin_unlock_irq(&local->lock);
rxrpc_poke_conn(conn, rxrpc_conn_get_poke_response);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.