void rxrpc_process_connection(struct work_struct *work) { struct rxrpc_connection *conn = container_of(work, struct rxrpc_connection, processor); struct rxrpc_skb_priv *sp; struct sk_buff *skb; u32 abort_code = RX_PROTOCOL_ERROR; int ret; _enter("{%d}", conn->debug_id); atomic_inc(&conn->usage); if (test_and_clear_bit(RXRPC_CONN_CHALLENGE, &conn->events)) { rxrpc_secure_connection(conn); rxrpc_put_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))) { sp = rxrpc_skb(skb); ret = rxrpc_process_event(conn, skb, &abort_code); switch (ret) { case -EPROTO: case -EKEYEXPIRED: case -EKEYREJECTED: goto protocol_error; case -EAGAIN: goto requeue_and_leave; case -ECONNABORTED: default: rxrpc_put_connection(conn); rxrpc_free_skb(skb); break; } } out: rxrpc_put_connection(conn); _leave(""); return; requeue_and_leave: skb_queue_head(&conn->rx_queue, skb); goto out; protocol_error: if (rxrpc_abort_connection(conn, -ret, abort_code) < 0) goto requeue_and_leave; rxrpc_put_connection(conn); rxrpc_free_skb(skb); _leave(" [EPROTO]"); goto out; }
/* * connection-level event processor */ void rxrpc_process_connection(struct work_struct *work) { struct rxrpc_connection *conn = container_of(work, struct rxrpc_connection, processor); struct sk_buff *skb; u32 abort_code = RX_PROTOCOL_ERROR; int ret; rxrpc_see_connection(conn); if (test_and_clear_bit(RXRPC_CONN_EV_CHALLENGE, &conn->events)) rxrpc_secure_connection(conn); /* Process delayed ACKs whose time has come. */ if (conn->flags & RXRPC_CONN_FINAL_ACK_MASK) rxrpc_process_delayed_final_acks(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_rx_seen); ret = rxrpc_process_event(conn, skb, &abort_code); switch (ret) { case -EPROTO: case -EKEYEXPIRED: case -EKEYREJECTED: goto protocol_error; case -ENOMEM: case -EAGAIN: goto requeue_and_leave; case -ECONNABORTED: default: rxrpc_free_skb(skb, rxrpc_skb_rx_freed); break; } } out: rxrpc_put_connection(conn); _leave(""); return; requeue_and_leave: skb_queue_head(&conn->rx_queue, skb); goto out; protocol_error: if (rxrpc_abort_connection(conn, ret, abort_code) < 0) goto requeue_and_leave; rxrpc_free_skb(skb, rxrpc_skb_rx_freed); goto out; }
/* * Send an ABORT call packet. */ int rxrpc_send_abort_packet(struct rxrpc_call *call) { struct rxrpc_connection *conn = NULL; struct rxrpc_abort_buffer pkt; struct msghdr msg; struct kvec iov[1]; rxrpc_serial_t serial; int ret; spin_lock_bh(&call->lock); if (call->conn) conn = rxrpc_get_connection_maybe(call->conn); spin_unlock_bh(&call->lock); if (!conn) return -ECONNRESET; msg.msg_name = &call->peer->srx.transport; msg.msg_namelen = call->peer->srx.transport_len; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; pkt.whdr.epoch = htonl(conn->proto.epoch); pkt.whdr.cid = htonl(call->cid); pkt.whdr.callNumber = htonl(call->call_id); pkt.whdr.seq = 0; pkt.whdr.type = RXRPC_PACKET_TYPE_ABORT; pkt.whdr.flags = conn->out_clientflag; pkt.whdr.userStatus = 0; pkt.whdr.securityIndex = call->security_ix; pkt.whdr._rsvd = 0; pkt.whdr.serviceId = htons(call->service_id); pkt.abort_code = htonl(call->abort_code); iov[0].iov_base = &pkt; iov[0].iov_len = sizeof(pkt); serial = atomic_inc_return(&conn->serial); pkt.whdr.serial = htonl(serial); ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 1, sizeof(pkt)); rxrpc_put_connection(conn); return ret; }
/* * 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_skb_priv *sp, *nsp; 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); if (!notification) { _debug("no memory"); ret = -ENOMEM; goto error_nofree; } rxrpc_new_skb(notification); notification->mark = RXRPC_SKB_MARK_NEW_CALL; conn = rxrpc_incoming_connection(local, srx, skb); if (IS_ERR(conn)) { _debug("no conn"); ret = PTR_ERR(conn); goto error; } call = rxrpc_incoming_call(rx, conn, skb); 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_SERVICE_UNSECURED) { _debug("await conn sec"); list_add_tail(&call->accept_link, &rx->secureq); call->conn->state = RXRPC_CONN_SERVICE_CHALLENGING; set_bit(RXRPC_CONN_EV_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); atomic_inc(&call->skb_count); 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; BUG_ON(ret < 0); } spin_unlock(&call->conn->state_lock); _debug("queued"); }
/* * Send an ACK call packet. */ int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping) { struct rxrpc_connection *conn = NULL; struct rxrpc_ack_buffer *pkt; struct msghdr msg; struct kvec iov[2]; rxrpc_serial_t serial; rxrpc_seq_t hard_ack, top; size_t len, n; int ret; u8 reason; spin_lock_bh(&call->lock); if (call->conn) conn = rxrpc_get_connection_maybe(call->conn); spin_unlock_bh(&call->lock); if (!conn) return -ECONNRESET; pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); if (!pkt) { rxrpc_put_connection(conn); return -ENOMEM; } msg.msg_name = &call->peer->srx.transport; msg.msg_namelen = call->peer->srx.transport_len; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; pkt->whdr.epoch = htonl(conn->proto.epoch); pkt->whdr.cid = htonl(call->cid); pkt->whdr.callNumber = htonl(call->call_id); pkt->whdr.seq = 0; pkt->whdr.type = RXRPC_PACKET_TYPE_ACK; pkt->whdr.flags = RXRPC_SLOW_START_OK | conn->out_clientflag; pkt->whdr.userStatus = 0; pkt->whdr.securityIndex = call->security_ix; pkt->whdr._rsvd = 0; pkt->whdr.serviceId = htons(call->service_id); spin_lock_bh(&call->lock); if (ping) { reason = RXRPC_ACK_PING; } else { reason = call->ackr_reason; if (!call->ackr_reason) { spin_unlock_bh(&call->lock); ret = 0; goto out; } call->ackr_reason = 0; } n = rxrpc_fill_out_ack(call, pkt, &hard_ack, &top, reason); spin_unlock_bh(&call->lock); iov[0].iov_base = pkt; iov[0].iov_len = sizeof(pkt->whdr) + sizeof(pkt->ack) + n; iov[1].iov_base = &pkt->ackinfo; iov[1].iov_len = sizeof(pkt->ackinfo); len = iov[0].iov_len + iov[1].iov_len; serial = atomic_inc_return(&conn->serial); pkt->whdr.serial = htonl(serial); trace_rxrpc_tx_ack(call, serial, ntohl(pkt->ack.firstPacket), ntohl(pkt->ack.serial), pkt->ack.reason, pkt->ack.nAcks); if (ping) { call->ping_serial = serial; smp_wmb(); /* We need to stick a time in before we send the packet in case * the reply gets back before kernel_sendmsg() completes - but * asking UDP to send the packet can take a relatively long * time, so we update the time after, on the assumption that * the packet transmission is more likely to happen towards the * end of the kernel_sendmsg() call. */ call->ping_time = ktime_get_real(); set_bit(RXRPC_CALL_PINGING, &call->flags); trace_rxrpc_rtt_tx(call, rxrpc_rtt_tx_ping, serial); } ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len); if (ping) call->ping_time = ktime_get_real(); if (call->state < RXRPC_CALL_COMPLETE) { if (ret < 0) { if (ping) clear_bit(RXRPC_CALL_PINGING, &call->flags); rxrpc_propose_ACK(call, pkt->ack.reason, ntohs(pkt->ack.maxSkew), ntohl(pkt->ack.serial), true, true, rxrpc_propose_ack_retry_tx); } else { spin_lock_bh(&call->lock); if (after(hard_ack, call->ackr_consumed)) call->ackr_consumed = hard_ack; if (after(top, call->ackr_seen)) call->ackr_seen = top; spin_unlock_bh(&call->lock); } } out: rxrpc_put_connection(conn); kfree(pkt); return ret; }