/* * Reply to a version request */ static void rxrpc_send_version_request(struct rxrpc_local *local, struct rxrpc_host_header *hdr, struct sk_buff *skb) { struct rxrpc_wire_header whdr; struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct sockaddr_rxrpc srx; struct msghdr msg; struct kvec iov[2]; size_t len; int ret; _enter(""); if (rxrpc_extract_addr_from_skb(local, &srx, skb) < 0) return; msg.msg_name = &srx.transport; msg.msg_namelen = srx.transport_len; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; whdr.epoch = htonl(sp->hdr.epoch); whdr.cid = htonl(sp->hdr.cid); whdr.callNumber = htonl(sp->hdr.callNumber); whdr.seq = 0; whdr.serial = 0; whdr.type = RXRPC_PACKET_TYPE_VERSION; whdr.flags = RXRPC_LAST_PACKET | (~hdr->flags & RXRPC_CLIENT_INITIATED); whdr.userStatus = 0; whdr.securityIndex = 0; whdr._rsvd = 0; whdr.serviceId = htons(sp->hdr.serviceId); iov[0].iov_base = &whdr; iov[0].iov_len = sizeof(whdr); iov[1].iov_base = (char *)rxrpc_version_string; iov[1].iov_len = sizeof(rxrpc_version_string); len = iov[0].iov_len + iov[1].iov_len; _proto("Tx VERSION (reply)"); ret = kernel_sendmsg(local->socket, &msg, iov, 2, len); if (ret < 0) trace_rxrpc_tx_fail(local->debug_id, 0, ret, rxrpc_tx_fail_version_reply); _leave(""); }
/* * Retransmit terminal ACK or ABORT of the previous call. */ static void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn, struct sk_buff *skb, unsigned int channel) { struct rxrpc_skb_priv *sp = skb ? rxrpc_skb(skb) : NULL; struct rxrpc_channel *chan; struct msghdr msg; struct kvec iov[3]; struct { struct rxrpc_wire_header whdr; union { __be32 abort_code; struct rxrpc_ackpacket ack; }; } __attribute__((packed)) pkt; struct rxrpc_ackinfo ack_info; size_t len; int ret, ioc; u32 serial, mtu, call_id, padding; _enter("%d", conn->debug_id); chan = &conn->channels[channel]; /* If the last call got moved on whilst we were waiting to run, just * ignore this packet. */ call_id = READ_ONCE(chan->last_call); /* Sync with __rxrpc_disconnect_call() */ smp_rmb(); if (skb && call_id != sp->hdr.callNumber) return; msg.msg_name = &conn->params.peer->srx.transport; msg.msg_namelen = conn->params.peer->srx.transport_len; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; iov[0].iov_base = &pkt; iov[0].iov_len = sizeof(pkt.whdr); iov[1].iov_base = &padding; iov[1].iov_len = 3; iov[2].iov_base = &ack_info; iov[2].iov_len = sizeof(ack_info); pkt.whdr.epoch = htonl(conn->proto.epoch); pkt.whdr.cid = htonl(conn->proto.cid | channel); pkt.whdr.callNumber = htonl(call_id); pkt.whdr.seq = 0; pkt.whdr.type = chan->last_type; pkt.whdr.flags = conn->out_clientflag; pkt.whdr.userStatus = 0; pkt.whdr.securityIndex = conn->security_ix; pkt.whdr._rsvd = 0; pkt.whdr.serviceId = htons(conn->service_id); len = sizeof(pkt.whdr); switch (chan->last_type) { case RXRPC_PACKET_TYPE_ABORT: pkt.abort_code = htonl(chan->last_abort); iov[0].iov_len += sizeof(pkt.abort_code); len += sizeof(pkt.abort_code); ioc = 1; break; case RXRPC_PACKET_TYPE_ACK: mtu = conn->params.peer->if_mtu; mtu -= conn->params.peer->hdrsize; pkt.ack.bufferSpace = 0; pkt.ack.maxSkew = htons(skb ? skb->priority : 0); pkt.ack.firstPacket = htonl(chan->last_seq + 1); pkt.ack.previousPacket = htonl(chan->last_seq); pkt.ack.serial = htonl(skb ? sp->hdr.serial : 0); pkt.ack.reason = skb ? RXRPC_ACK_DUPLICATE : RXRPC_ACK_IDLE; pkt.ack.nAcks = 0; ack_info.rxMTU = htonl(rxrpc_rx_mtu); ack_info.maxMTU = htonl(mtu); ack_info.rwind = htonl(rxrpc_rx_window_size); ack_info.jumbo_max = htonl(rxrpc_rx_jumbo_max); pkt.whdr.flags |= RXRPC_SLOW_START_OK; padding = 0; iov[0].iov_len += sizeof(pkt.ack); len += sizeof(pkt.ack) + 3 + sizeof(ack_info); ioc = 3; break; default: return; } /* Resync with __rxrpc_disconnect_call() and check that the last call * didn't get advanced whilst we were filling out the packets. */ smp_rmb(); if (READ_ONCE(chan->last_call) != call_id) return; serial = atomic_inc_return(&conn->serial); pkt.whdr.serial = htonl(serial); switch (chan->last_type) { case RXRPC_PACKET_TYPE_ABORT: _proto("Tx ABORT %%%u { %d } [re]", serial, conn->local_abort); break; case RXRPC_PACKET_TYPE_ACK: trace_rxrpc_tx_ack(chan->call_debug_id, serial, ntohl(pkt.ack.firstPacket), ntohl(pkt.ack.serial), pkt.ack.reason, 0); _proto("Tx ACK %%%u [re]", serial); break; } ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, ioc, len); conn->params.peer->last_tx_at = ktime_get_seconds(); if (ret < 0) trace_rxrpc_tx_fail(chan->call_debug_id, serial, ret, rxrpc_tx_point_call_final_resend); else trace_rxrpc_tx_packet(chan->call_debug_id, &pkt.whdr, rxrpc_tx_point_call_final_resend); _leave(""); }
/* * generate a connection-level abort */ static int rxrpc_abort_connection(struct rxrpc_connection *conn, int error, u32 abort_code) { struct rxrpc_wire_header whdr; struct msghdr msg; struct kvec iov[2]; __be32 word; size_t len; u32 serial; int ret; _enter("%d,,%u,%u", conn->debug_id, error, abort_code); /* generate a connection-level abort */ spin_lock_bh(&conn->state_lock); if (conn->state >= RXRPC_CONN_REMOTELY_ABORTED) { spin_unlock_bh(&conn->state_lock); _leave(" = 0 [already dead]"); return 0; } conn->state = RXRPC_CONN_LOCALLY_ABORTED; spin_unlock_bh(&conn->state_lock); rxrpc_abort_calls(conn, RXRPC_CALL_LOCALLY_ABORTED, abort_code, error); msg.msg_name = &conn->params.peer->srx.transport; msg.msg_namelen = conn->params.peer->srx.transport_len; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; whdr.epoch = htonl(conn->proto.epoch); whdr.cid = htonl(conn->proto.cid); whdr.callNumber = 0; whdr.seq = 0; whdr.type = RXRPC_PACKET_TYPE_ABORT; whdr.flags = conn->out_clientflag; whdr.userStatus = 0; whdr.securityIndex = conn->security_ix; whdr._rsvd = 0; whdr.serviceId = htons(conn->service_id); word = htonl(conn->local_abort); iov[0].iov_base = &whdr; iov[0].iov_len = sizeof(whdr); iov[1].iov_base = &word; iov[1].iov_len = sizeof(word); len = iov[0].iov_len + iov[1].iov_len; serial = atomic_inc_return(&conn->serial); whdr.serial = htonl(serial); _proto("Tx CONN ABORT %%%u { %d }", serial, conn->local_abort); ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len); if (ret < 0) { trace_rxrpc_tx_fail(conn->debug_id, serial, ret, rxrpc_tx_point_conn_abort); _debug("sendmsg failed: %d", ret); return -EAGAIN; } trace_rxrpc_tx_packet(conn->debug_id, &whdr, rxrpc_tx_point_conn_abort); conn->params.peer->last_tx_at = ktime_get_seconds(); _leave(" = 0"); return 0; }