1834 lines
45 KiB
C
1834 lines
45 KiB
C
|
/*
|
||
|
* barebox RATP implementation.
|
||
|
* This is the barebox implementation for the Reliable Asynchronous
|
||
|
* Transfer Protocol (RATP) as described in RFC916.
|
||
|
*
|
||
|
* Copyright (C) 2015 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*/
|
||
|
|
||
|
#define pr_fmt(fmt) "ratp: " fmt
|
||
|
|
||
|
#include <common.h>
|
||
|
#include <malloc.h>
|
||
|
#include <getopt.h>
|
||
|
#include <ratp.h>
|
||
|
#include <crc.h>
|
||
|
#include <clock.h>
|
||
|
#include <asm/unaligned.h>
|
||
|
|
||
|
/*
|
||
|
* RATP packet format:
|
||
|
*
|
||
|
* Byte No.
|
||
|
*
|
||
|
* +-------------------------------+
|
||
|
* | |
|
||
|
* 1 | Synch Leader | Hex 01
|
||
|
* | |
|
||
|
* +-------------------------------+
|
||
|
* | S | A | F | R | S | A | E | S |
|
||
|
* 2 | Y | C | I | S | N | N | O | O | Control
|
||
|
* | N | K | N | T | | | R | |
|
||
|
* +-------------------------------+
|
||
|
* | |
|
||
|
* 3 | Data length (0-255) |
|
||
|
* | |
|
||
|
* +-------------------------------+
|
||
|
* | |
|
||
|
* 4 | Header Checksum |
|
||
|
* | |
|
||
|
* +-------------------------------+
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
struct ratp_header {
|
||
|
uint8_t synch;
|
||
|
uint8_t control;
|
||
|
uint8_t data_length;
|
||
|
uint8_t cksum;
|
||
|
};
|
||
|
|
||
|
#define RATP_CONTROL_SO (1 << 0)
|
||
|
#define RATP_CONTROL_EOR (1 << 1)
|
||
|
#define RATP_CONTROL_AN (1 << 2)
|
||
|
#define RATP_CONTROL_SN (1 << 3)
|
||
|
#define RATP_CONTROL_RST (1 << 4)
|
||
|
#define RATP_CONTROL_FIN (1 << 5)
|
||
|
#define RATP_CONTROL_ACK (1 << 6)
|
||
|
#define RATP_CONTROL_SYN (1 << 7)
|
||
|
|
||
|
enum ratp_state {
|
||
|
RATP_STATE_LISTEN,
|
||
|
RATP_STATE_SYN_SENT,
|
||
|
RATP_STATE_SYN_RECEIVED,
|
||
|
RATP_STATE_ESTABLISHED,
|
||
|
RATP_STATE_FIN_WAIT,
|
||
|
RATP_STATE_LAST_ACK,
|
||
|
RATP_STATE_CLOSING,
|
||
|
RATP_STATE_TIME_WAIT,
|
||
|
RATP_STATE_CLOSED,
|
||
|
};
|
||
|
|
||
|
struct ratp_message {
|
||
|
void *buf;
|
||
|
size_t len;
|
||
|
struct list_head list;
|
||
|
void (*complete)(void *ctx, int status);
|
||
|
void *complete_ctx;
|
||
|
int eor;
|
||
|
};
|
||
|
|
||
|
static char *ratp_state_str[] = {
|
||
|
[RATP_STATE_LISTEN] = "LISTEN",
|
||
|
[RATP_STATE_SYN_SENT] = "SYN_SENT",
|
||
|
[RATP_STATE_SYN_RECEIVED] = "SYN_RECEIVED",
|
||
|
[RATP_STATE_ESTABLISHED] = "ESTABLISHED",
|
||
|
[RATP_STATE_FIN_WAIT] = "FIN_WAIT",
|
||
|
[RATP_STATE_LAST_ACK] = "LAST_ACK",
|
||
|
[RATP_STATE_CLOSING] = "CLOSING",
|
||
|
[RATP_STATE_TIME_WAIT] = "TIME_WAIT",
|
||
|
[RATP_STATE_CLOSED] = "CLOSED",
|
||
|
};
|
||
|
|
||
|
struct ratp_internal {
|
||
|
struct ratp *ratp;
|
||
|
|
||
|
enum ratp_state state;
|
||
|
int sn_sent;
|
||
|
int sn_received;
|
||
|
int active;
|
||
|
|
||
|
void *recvbuf;
|
||
|
void *sendbuf;
|
||
|
int sendbuf_len;
|
||
|
|
||
|
struct list_head recvmsg;
|
||
|
struct list_head sendmsg;
|
||
|
|
||
|
struct ratp_message *sendmsg_current;
|
||
|
|
||
|
uint64_t timewait_timer_start;
|
||
|
uint64_t retransmission_timer_start;
|
||
|
int max_retransmission;
|
||
|
int retransmission_count;
|
||
|
int srtt;
|
||
|
int rto;
|
||
|
|
||
|
int status;
|
||
|
|
||
|
int in_ratp;
|
||
|
};
|
||
|
|
||
|
static bool ratp_sn(struct ratp_header *hdr)
|
||
|
{
|
||
|
return hdr->control & RATP_CONTROL_SN ? 1 : 0;
|
||
|
}
|
||
|
|
||
|
static bool ratp_an(struct ratp_header *hdr)
|
||
|
{
|
||
|
return hdr->control & RATP_CONTROL_AN ? 1 : 0;
|
||
|
}
|
||
|
|
||
|
#define ratp_set_sn(sn) (((sn) % 2) ? RATP_CONTROL_SN : 0)
|
||
|
#define ratp_set_an(an) (((an) % 2) ? RATP_CONTROL_AN : 0)
|
||
|
|
||
|
static inline int ratp_header_ok(struct ratp_internal *ri, struct ratp_header *h)
|
||
|
{
|
||
|
uint8_t cksum;
|
||
|
int ret;
|
||
|
|
||
|
cksum = h->control;
|
||
|
cksum += h->data_length;
|
||
|
cksum += h->cksum;
|
||
|
|
||
|
ret = cksum == 0xff ? 1 : 0;
|
||
|
|
||
|
if (ret)
|
||
|
pr_vdebug("Header ok\n");
|
||
|
else
|
||
|
pr_vdebug("Header cksum failed: %02x\n", cksum);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static bool ratp_has_data(struct ratp_header *hdr)
|
||
|
{
|
||
|
if (hdr->control & RATP_CONTROL_SO)
|
||
|
return 1;
|
||
|
if (hdr->data_length)
|
||
|
return 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void ratp_print_header(struct ratp_internal *ri, struct ratp_header *hdr,
|
||
|
const char *prefix)
|
||
|
{
|
||
|
uint8_t control = hdr->control;
|
||
|
|
||
|
pr_debug("%s>%s %s %s %s %s %s %s %s< len: %-3d\n",
|
||
|
prefix,
|
||
|
control & RATP_CONTROL_SO ? "so" : "--",
|
||
|
control & RATP_CONTROL_EOR ? "eor" : "---",
|
||
|
control & RATP_CONTROL_AN ? "an" : "--",
|
||
|
control & RATP_CONTROL_SN ? "sn" : "--",
|
||
|
control & RATP_CONTROL_RST ? "rst" : "---",
|
||
|
control & RATP_CONTROL_FIN ? "fin" : "---",
|
||
|
control & RATP_CONTROL_ACK ? "ack" : "---",
|
||
|
control & RATP_CONTROL_SYN ? "syn" : "---",
|
||
|
hdr->data_length);
|
||
|
|
||
|
#ifdef VERBOSE_DEBUG
|
||
|
if (hdr->data_length)
|
||
|
memory_display(hdr + 1, 0, hdr->data_length, 1, 0);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void ratp_create_packet(struct ratp_internal *ri, struct ratp_header *hdr,
|
||
|
uint8_t control, uint8_t length)
|
||
|
{
|
||
|
hdr->synch = 0x1;
|
||
|
hdr->control = control;
|
||
|
hdr->data_length = length;
|
||
|
hdr->cksum = (control + length) ^ 0xff;
|
||
|
}
|
||
|
|
||
|
static void ratp_state_change(struct ratp_internal *ri, enum ratp_state state)
|
||
|
{
|
||
|
pr_debug("state %-10s -> %-10s\n", ratp_state_str[ri->state],
|
||
|
ratp_state_str[state]);
|
||
|
|
||
|
ri->state = state;
|
||
|
}
|
||
|
|
||
|
#define RATP_CONTROL_SO (1 << 0)
|
||
|
#define RATP_CONTROL_EOR (1 << 1)
|
||
|
#define RATP_CONTROL_AN (1 << 2)
|
||
|
#define RATP_CONTROL_SN (1 << 3)
|
||
|
#define RATP_CONTROL_RST (1 << 4)
|
||
|
#define RATP_CONTROL_FIN (1 << 5)
|
||
|
#define RATP_CONTROL_ACK (1 << 6)
|
||
|
#define RATP_CONTROL_SYN (1 << 7)
|
||
|
|
||
|
static int ratp_send_pkt(struct ratp_internal *ri, void *pkt, int length)
|
||
|
{
|
||
|
struct ratp_header *hdr = (void *)pkt;
|
||
|
|
||
|
ratp_print_header(ri, hdr, "send");
|
||
|
|
||
|
if (ratp_has_data(hdr) ||
|
||
|
(hdr->control & (RATP_CONTROL_SYN | RATP_CONTROL_RST | RATP_CONTROL_FIN))) {
|
||
|
memcpy(ri->sendbuf, pkt, length);
|
||
|
ri->sn_sent = ratp_sn(hdr);
|
||
|
ri->sendbuf_len = length;
|
||
|
ri->retransmission_timer_start = get_time_ns();
|
||
|
ri->retransmission_count = 0;
|
||
|
}
|
||
|
|
||
|
return ri->ratp->send(ri->ratp, pkt, length);
|
||
|
}
|
||
|
|
||
|
static int ratp_send_hdr(struct ratp_internal *ri, uint8_t control)
|
||
|
{
|
||
|
struct ratp_header hdr = {};
|
||
|
|
||
|
ratp_create_packet(ri, &hdr, control, 0);
|
||
|
|
||
|
return ratp_send_pkt(ri, &hdr, sizeof(hdr));
|
||
|
}
|
||
|
|
||
|
static int ratp_recv_char(struct ratp_internal *ri, uint8_t *data, int poll_timeout_ms)
|
||
|
{
|
||
|
uint64_t start;
|
||
|
int ret;
|
||
|
|
||
|
start = get_time_ns();
|
||
|
|
||
|
while (1) {
|
||
|
ret = ri->ratp->recv(ri->ratp, data);
|
||
|
if (ret < 0 && ret != -EAGAIN)
|
||
|
return ret;
|
||
|
|
||
|
if (ret == 0)
|
||
|
return 0;
|
||
|
|
||
|
if (is_timeout(start, poll_timeout_ms * MSECOND))
|
||
|
return -EAGAIN;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int ratp_recv_pkt_header(struct ratp_internal *ri, struct ratp_header *hdr,
|
||
|
int poll_timeout_ms)
|
||
|
{
|
||
|
int ret;
|
||
|
uint8_t buf;
|
||
|
|
||
|
do {
|
||
|
ret = ratp_recv_char(ri, &buf, 0);
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
hdr->synch = buf;
|
||
|
} while (hdr->synch != 1);
|
||
|
ret = ratp_recv_char(ri, &buf, poll_timeout_ms);
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
|
||
|
hdr->control = buf;
|
||
|
ret = ratp_recv_char(ri, &buf, poll_timeout_ms);
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
|
||
|
hdr->data_length = buf;
|
||
|
|
||
|
ret = ratp_recv_char(ri, &buf, poll_timeout_ms);
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
|
||
|
hdr->cksum = buf;
|
||
|
|
||
|
if (!ratp_header_ok(ri, hdr))
|
||
|
return -EAGAIN;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int ratp_recv_pkt_data(struct ratp_internal *ri, void *data, uint8_t len,
|
||
|
int poll_timeout_ms)
|
||
|
{
|
||
|
uint16_t crc_expect, crc_read;
|
||
|
int ret, i;
|
||
|
|
||
|
for (i = 0; i < len + 2; i++) {
|
||
|
ret = ratp_recv_char(ri, data + i, poll_timeout_ms);
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
crc_expect = cyg_crc16(data, len);
|
||
|
|
||
|
crc_read = get_unaligned_be16(data + len);
|
||
|
|
||
|
if (crc_expect != crc_read) {
|
||
|
pr_vdebug("Wrong CRC: expected: 0x%04x, got 0x%04x\n",
|
||
|
crc_expect, crc_read);
|
||
|
return -EBADMSG;
|
||
|
} else {
|
||
|
pr_vdebug("correct CRC: 0x%04x\n", crc_expect);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int ratp_recv_pkt(struct ratp_internal *ri, void *pkt, int poll_timeout_ms)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
void *data = pkt + sizeof(struct ratp_header);
|
||
|
int ret;
|
||
|
|
||
|
ret = ratp_recv_pkt_header(ri, hdr, poll_timeout_ms);
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
|
||
|
if (hdr->control & (RATP_CONTROL_SO | RATP_CONTROL_RST | RATP_CONTROL_SYN |
|
||
|
RATP_CONTROL_FIN))
|
||
|
return 0;
|
||
|
|
||
|
if (hdr->data_length) {
|
||
|
ret = ratp_recv_pkt_data(ri, data, hdr->data_length,
|
||
|
poll_timeout_ms);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static bool ratp_an_expected(struct ratp_internal *ri, struct ratp_header *hdr)
|
||
|
{
|
||
|
return ratp_an(hdr) == (ri->sn_sent + 1) % 2;
|
||
|
}
|
||
|
|
||
|
static bool ratp_sn_expected(struct ratp_internal *ri, struct ratp_header *hdr)
|
||
|
{
|
||
|
return ratp_sn(hdr) != ri->sn_received;
|
||
|
}
|
||
|
|
||
|
static int ratp_send_ack(struct ratp_internal *ri, struct ratp_header *hdr)
|
||
|
{
|
||
|
uint8_t control = RATP_CONTROL_ACK;
|
||
|
int ret;
|
||
|
|
||
|
if (hdr->control & RATP_CONTROL_SN)
|
||
|
control |= RATP_CONTROL_AN;
|
||
|
else
|
||
|
control |= 0;
|
||
|
|
||
|
ret = ratp_send_hdr(ri, control);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int ratp_send_next_data(struct ratp_internal *ri)
|
||
|
{
|
||
|
uint16_t crc;
|
||
|
uint8_t control = RATP_CONTROL_ACK;
|
||
|
struct ratp_header *hdr;
|
||
|
int pktlen;
|
||
|
struct ratp_message *msg;
|
||
|
int len;
|
||
|
|
||
|
if (ri->sendmsg_current) {
|
||
|
pr_err("%s: busy\n", __func__);
|
||
|
return -EBUSY;
|
||
|
}
|
||
|
|
||
|
if (list_empty(&ri->sendmsg))
|
||
|
return 0;
|
||
|
|
||
|
msg = list_first_entry(&ri->sendmsg, struct ratp_message, list);
|
||
|
|
||
|
ri->sendmsg_current = msg;
|
||
|
|
||
|
list_del(&msg->list);
|
||
|
|
||
|
len = msg->len;
|
||
|
|
||
|
control = ratp_set_sn(ri->sn_sent + 1) |
|
||
|
ratp_set_an(ri->sn_received + 1) |
|
||
|
RATP_CONTROL_ACK;
|
||
|
|
||
|
hdr = msg->buf;
|
||
|
|
||
|
if (msg->eor)
|
||
|
control |= RATP_CONTROL_EOR;
|
||
|
|
||
|
if (len > 1) {
|
||
|
void *data = hdr + 1;
|
||
|
pktlen = sizeof(*hdr) + len + 2;
|
||
|
crc = cyg_crc16(data, len);
|
||
|
put_unaligned_be16(crc, data + len);
|
||
|
} else {
|
||
|
pktlen = sizeof(struct ratp_header);
|
||
|
control |= RATP_CONTROL_SO;
|
||
|
len = 0;
|
||
|
}
|
||
|
|
||
|
ratp_create_packet(ri, hdr, control, len);
|
||
|
|
||
|
ri->retransmission_count = 0;
|
||
|
|
||
|
ratp_send_pkt(ri, msg->buf, pktlen);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void ratp_start_time_wait_timer(struct ratp_internal *ri)
|
||
|
{
|
||
|
ri->timewait_timer_start = get_time_ns();
|
||
|
}
|
||
|
|
||
|
static void ratp_msg_done(struct ratp_internal *ri, struct ratp_message *msg, int status)
|
||
|
{
|
||
|
int alpha, beta, rtt;
|
||
|
|
||
|
if (!status) {
|
||
|
rtt = (unsigned long)(get_time_ns() - ri->retransmission_timer_start) / MSECOND;
|
||
|
|
||
|
alpha = 8;
|
||
|
beta = 15;
|
||
|
|
||
|
ri->srtt = (alpha * ri->srtt + (10 - alpha) * rtt) / 10;
|
||
|
ri->rto = max(200, beta * ri->srtt / 10);
|
||
|
|
||
|
pr_debug("%s: done. SRTT: %dms RTO: %dms status: %d\n",
|
||
|
__func__, ri->srtt, ri->rto, ri->status);
|
||
|
}
|
||
|
|
||
|
if (msg->complete)
|
||
|
msg->complete(msg->complete_ctx, status);
|
||
|
|
||
|
free(msg->buf);
|
||
|
free(msg);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This procedure details the behavior of the LISTEN state. First
|
||
|
* check the packet for the RST flag. If it is set then packet is
|
||
|
* discarded and ignored, return and continue the processing
|
||
|
* associated with this state.
|
||
|
*
|
||
|
* We assume now that the RST flag was not set. Check the packet
|
||
|
* for the ACK flag. If it is set we have an illegal condition
|
||
|
* since no connection has yet been opened. Send a RST packet
|
||
|
* with the correct response SN value:
|
||
|
*
|
||
|
* <SN=received AN><CTL=RST>
|
||
|
*
|
||
|
* Return to the current state without any further processing.
|
||
|
*
|
||
|
* We assume now that neither the RST nor the ACK flags were set.
|
||
|
* Check the packet for a SYN flag. If it is set then an attempt
|
||
|
* is being made to open a connection. Create a TCB for this
|
||
|
* connection. The sender has placed its MDL in the LENGTH field,
|
||
|
* also specified is the sender's initial SN value. Retrieve and
|
||
|
* place them into the TCB. Note that the presence of the SO flag
|
||
|
* is ignored since it has no meaning when either of the SYN, RST,
|
||
|
* or FIN flags are set.
|
||
|
*
|
||
|
* Send a SYN packet which acknowledges the SYN received. Choose
|
||
|
* the initial SN value and the MDL for this end of the
|
||
|
* connection:
|
||
|
*
|
||
|
* <SN=0><AN=received SN+1 modulo 2><CTL=SYN, ACK><LENGTH=MDL>
|
||
|
*
|
||
|
* and go to the RATP_STATE_SYN_RECEIVED state without any further
|
||
|
* processing.
|
||
|
*
|
||
|
* Any packet not satisfying the above tests is discarded and
|
||
|
* ignored. Return to the current state without any further
|
||
|
* processing.
|
||
|
*/
|
||
|
static void ratp_behaviour_a(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
if (hdr->control & RATP_CONTROL_RST)
|
||
|
return;
|
||
|
|
||
|
if (hdr->control & RATP_CONTROL_ACK) {
|
||
|
uint8_t control = RATP_CONTROL_RST;
|
||
|
|
||
|
if (hdr->control & RATP_CONTROL_AN)
|
||
|
control |= RATP_CONTROL_SN;
|
||
|
|
||
|
ratp_send_hdr(ri, control);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (hdr->control & RATP_CONTROL_SYN) {
|
||
|
struct ratp_header synack = {};
|
||
|
uint8_t control = RATP_CONTROL_SYN | RATP_CONTROL_ACK;
|
||
|
|
||
|
if (!(hdr->control & RATP_CONTROL_SN))
|
||
|
control |= RATP_CONTROL_AN;
|
||
|
|
||
|
ratp_create_packet(ri, &synack, control, 255);
|
||
|
ratp_send_pkt(ri, &synack, sizeof(synack));
|
||
|
|
||
|
ratp_state_change(ri, RATP_STATE_SYN_RECEIVED);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This procedure represents the behavior of the SYN-SENT state
|
||
|
* and is entered when this end of the connection decides to
|
||
|
* execute an active OPEN.
|
||
|
*
|
||
|
* First, check the packet for the ACK flag. If the ACK flag is
|
||
|
* set then check to see if the AN value was as expected. If it
|
||
|
* was continue below. Otherwise the AN value was unexpected. If
|
||
|
* the RST flag was set then discard the packet and return to the
|
||
|
* current state without any further processing, else send a
|
||
|
* reset:
|
||
|
*
|
||
|
* <SN=received AN><CTL=RST>
|
||
|
*
|
||
|
* Discard the packet and return to the current state without any
|
||
|
* further processing.
|
||
|
*
|
||
|
* At this point either the ACK flag was set and the AN value was
|
||
|
* as expected or ACK was not set. Second, check the RST flag.
|
||
|
* If the RST flag is set there are two cases:
|
||
|
*
|
||
|
* . If the ACK flag is set then discard the packet, flush the
|
||
|
* retransmission queue, inform the user "Error: Connection
|
||
|
* refused", delete the TCB, and go to the CLOSED state without
|
||
|
* any further processing.
|
||
|
*
|
||
|
* 2. If the ACK flag was not set then discard the packet and
|
||
|
* return to this state without any further processing.
|
||
|
*
|
||
|
* At this point we assume the packet contained an ACK which was
|
||
|
* Ok, or there was no ACK, and there was no RST. Now check the
|
||
|
* packet for the SYN flag. If the ACK flag was set then our SYN
|
||
|
* has been acknowledged. Store MDL received in the TCB. At this
|
||
|
* point we are technically in the ESTABLISHED state. Send an
|
||
|
* acknowledgment packet and any initial data which is queued to
|
||
|
* send:
|
||
|
*
|
||
|
* <SN=received AN><AN=received SN+1 modulo 2><CTL=ACK><DATA>
|
||
|
*
|
||
|
* Go to the ESTABLISHED state without any further processing.
|
||
|
*
|
||
|
* If the SYN flag was set but the ACK was not set then the other
|
||
|
* end of the connection has executed an active open also.
|
||
|
* Acknowledge the SYN, choose your MDL, and send:
|
||
|
*
|
||
|
* <SN=0><AN=received SN+1 modulo 2><CTL=SYN, ACK><LENGTH=MDL>
|
||
|
*
|
||
|
* Go to the SYN-RECEIVED state without any further processing.
|
||
|
*
|
||
|
* Any packet not satisfying the above tests is discarded and
|
||
|
* ignored. Return to the current state without any further
|
||
|
* processing.
|
||
|
*/
|
||
|
static void ratp_behaviour_b(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
if ((hdr->control & RATP_CONTROL_ACK) && !ratp_an_expected(ri, hdr)) {
|
||
|
if (!(hdr->control & RATP_CONTROL_RST)) {
|
||
|
uint8_t control = RATP_CONTROL_RST;
|
||
|
|
||
|
control = RATP_CONTROL_RST |
|
||
|
ratp_set_sn(ratp_an(hdr));
|
||
|
|
||
|
ratp_send_hdr(ri, control);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (hdr->control & RATP_CONTROL_RST) {
|
||
|
if (hdr->control & RATP_CONTROL_ACK) {
|
||
|
ri->status = -ECONNREFUSED;
|
||
|
|
||
|
pr_debug("Connection refused\n");
|
||
|
|
||
|
ratp_state_change(ri, RATP_STATE_CLOSED);
|
||
|
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (hdr->control & RATP_CONTROL_SYN) {
|
||
|
uint8_t control;
|
||
|
|
||
|
if (hdr->control & RATP_CONTROL_ACK) {
|
||
|
control = ratp_set_sn(ratp_an(hdr)) |
|
||
|
ratp_set_an(!ratp_sn(hdr)) |
|
||
|
RATP_CONTROL_ACK;
|
||
|
} else {
|
||
|
control = ratp_set_an(!ratp_sn(hdr)) |
|
||
|
RATP_CONTROL_SYN |
|
||
|
RATP_CONTROL_ACK;
|
||
|
|
||
|
}
|
||
|
|
||
|
ri->sn_received = ratp_sn(hdr);
|
||
|
|
||
|
ratp_send_hdr(ri, control);
|
||
|
ratp_state_change(ri, RATP_STATE_ESTABLISHED);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Examine the received SN field value. If the SN value was
|
||
|
* expected then return and continue the processing associated
|
||
|
* with this state.
|
||
|
*
|
||
|
* We now assume the SN value was not what was expected.
|
||
|
*
|
||
|
* If either RST or FIN were set discard the packet and return to
|
||
|
* the current state without any further processing.
|
||
|
*
|
||
|
* If neither RST nor FIN flags were set it is assumed that this
|
||
|
* packet is a duplicate of one already received. Send an ACK
|
||
|
* back:
|
||
|
*
|
||
|
* <SN=received AN><AN=received SN+1 modulo 2><CTL=ACK>
|
||
|
*
|
||
|
* Discard the duplicate packet and return to the current state
|
||
|
* without any further processing.
|
||
|
*/
|
||
|
static int ratp_behaviour_c1(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
int ret;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
if (ratp_sn_expected(ri, hdr)) {
|
||
|
pr_vdebug("%s: sn is expected\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (!(hdr->control & RATP_CONTROL_RST) &&
|
||
|
!(hdr->control & RATP_CONTROL_FIN)) {
|
||
|
ret = ratp_send_ack(ri, hdr);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Examine the received SN field value. If the SN value was
|
||
|
* expected then return and continue the processing associated
|
||
|
* with this state.
|
||
|
*
|
||
|
* We now assume the SN value was not what was expected.
|
||
|
*
|
||
|
* If either RST or FIN were set discard the packet and return to
|
||
|
* the current state without any further processing.
|
||
|
*
|
||
|
* If SYN was set we assume that the other end crashed and has
|
||
|
* attempted to open a new connection. We respond by sending a
|
||
|
* legal reset:
|
||
|
*
|
||
|
* <SN=received AN><AN=received SN+1 modulo 2><CTL=RST, ACK>
|
||
|
*
|
||
|
* This will cause the other end, currently in the SYN-SENT state,
|
||
|
* to close. Flush the retransmission queue, inform the user
|
||
|
* "Error: Connection reset", discard the packet, delete the TCB,
|
||
|
* and go to the CLOSED state without any further processing.
|
||
|
*
|
||
|
* If neither RST, FIN, nor SYN flags were set it is assumed that
|
||
|
* this packet is a duplicate of one already received. Send an
|
||
|
* ACK back:
|
||
|
*
|
||
|
* <SN=received AN><AN=received SN+1 modulo 2><CTL=ACK>
|
||
|
*
|
||
|
* Discard the duplicate packet and return to the current state
|
||
|
* without any further processing.
|
||
|
*/
|
||
|
static int ratp_behaviour_c2(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
int ret;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
if (!ratp_has_data(hdr))
|
||
|
return 0;
|
||
|
|
||
|
if (ratp_sn_expected(ri, hdr))
|
||
|
return 0;
|
||
|
|
||
|
if ((hdr->control & RATP_CONTROL_RST) ||
|
||
|
(hdr->control & RATP_CONTROL_FIN))
|
||
|
return 1;
|
||
|
|
||
|
if (hdr->control & RATP_CONTROL_SYN) {
|
||
|
ri->status = -ECONNRESET;
|
||
|
pr_debug("Error: Connection reset\n");
|
||
|
ratp_state_change(ri, RATP_STATE_CLOSED);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (!ratp_has_data(hdr))
|
||
|
return 1;
|
||
|
|
||
|
pr_debug("Sending ack for duplicate message\n");
|
||
|
ret = ratp_send_ack(ri, hdr);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* The packet is examined for a RST flag. If RST is not set then
|
||
|
* return and continue the processing associated with this state.
|
||
|
*
|
||
|
* RST is now assumed to have been set. If the connection was
|
||
|
* originally initiated from the LISTEN state (it was passively
|
||
|
* opened) then flush the retransmission queue, discard the
|
||
|
* packet, and go to the LISTEN state without any further
|
||
|
* processing.
|
||
|
*
|
||
|
* If instead the connection was initiated actively (came from the
|
||
|
* SYN-SENT state) then flush the retransmission queue, inform the
|
||
|
* user "Error: Connection refused", discard the packet, delete
|
||
|
* the TCB, and go to the CLOSED state without any further
|
||
|
* processing.
|
||
|
*/
|
||
|
static int ratp_behaviour_d1(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
if (!(hdr->control & RATP_CONTROL_RST))
|
||
|
return 0;
|
||
|
|
||
|
if (!(ri->active)) {
|
||
|
ratp_state_change(ri, RATP_STATE_LISTEN);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
ri->status = -ECONNREFUSED;
|
||
|
|
||
|
pr_debug("Error: connection refused\n");
|
||
|
|
||
|
ratp_state_change(ri, RATP_STATE_CLOSED);
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* The packet is examined for a RST flag. If RST is not set then
|
||
|
* return and continue the processing associated with this state.
|
||
|
*
|
||
|
* RST is now assumed to have been set. Any data remaining to be
|
||
|
* sent is flushed. The retransmission queue is flushed, the user
|
||
|
* is informed "Error: Connection reset.", discard the packet,
|
||
|
* delete the TCB, and go to the CLOSED state without any further
|
||
|
* processing.
|
||
|
*/
|
||
|
static int ratp_behaviour_d2(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
if (!(hdr->control & RATP_CONTROL_RST))
|
||
|
return 0;
|
||
|
|
||
|
ri->status = -ECONNRESET;
|
||
|
|
||
|
pr_debug("connection reset\n");
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* The packet is examined for a RST flag. If RST is not set then
|
||
|
* return and continue the processing associated with this state.
|
||
|
*
|
||
|
* RST is now assumed to have been set. Discard the packet,
|
||
|
* delete the TCB, and go to the CLOSED state without any further
|
||
|
* processing.
|
||
|
*/
|
||
|
static int ratp_behaviour_d3(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
if (!(hdr->control & RATP_CONTROL_RST))
|
||
|
return 0;
|
||
|
|
||
|
ratp_state_change(ri, RATP_STATE_CLOSED);
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Check the presence of the SYN flag. If the SYN flag is not set
|
||
|
* then return and continue the processing associated with this
|
||
|
* state.
|
||
|
*
|
||
|
* We now assume that the SYN flag was set. The presence of a SYN
|
||
|
* here is an error. Flush the retransmission queue, send a legal
|
||
|
* RST packet.
|
||
|
*
|
||
|
* If the ACK flag was set then send:
|
||
|
*
|
||
|
* <SN=received AN><CTL=RST>
|
||
|
*
|
||
|
* If the ACK flag was not set then send:
|
||
|
*
|
||
|
* <SN=0><CTL=RST>
|
||
|
*
|
||
|
* The user should receive the message "Error: Connection reset.",
|
||
|
* then delete the TCB and go to the CLOSED state without any
|
||
|
* further processing.
|
||
|
*/
|
||
|
static int ratp_behaviour_e(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
uint8_t control;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
if (!(hdr->control & RATP_CONTROL_SYN))
|
||
|
return 0;
|
||
|
|
||
|
ri->status = -ECONNRESET;
|
||
|
|
||
|
control = RATP_CONTROL_RST;
|
||
|
|
||
|
if (hdr->control & RATP_CONTROL_ACK)
|
||
|
control |= ratp_set_sn(ratp_an(hdr));
|
||
|
|
||
|
ratp_send_hdr(ri, control);
|
||
|
|
||
|
pr_debug("connection reset\n");
|
||
|
|
||
|
ratp_state_change(ri, RATP_STATE_CLOSED);
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Check the presence of the ACK flag. If ACK is not set then
|
||
|
* discard the packet and return without any further processing.
|
||
|
*
|
||
|
* We now assume that the ACK flag was set. If the AN field value
|
||
|
* was as expected then return and continue the processing
|
||
|
* associated with this state.
|
||
|
*
|
||
|
* We now assume that the ACK flag was set and that the AN field
|
||
|
* value was unexpected. If the connection was originally
|
||
|
* initiated from the LISTEN state (it was passively opened) then
|
||
|
* flush the retransmission queue, discard the packet, and send a
|
||
|
* legal RST packet:
|
||
|
*
|
||
|
* <SN=received AN><CTL=RST>
|
||
|
*
|
||
|
* Then delete the TCB and go to the LISTEN state without any
|
||
|
* further processing.
|
||
|
*
|
||
|
* Otherwise the connection was initiated actively (came from the
|
||
|
* SYN-SENT state) then inform the user "Error: Connection
|
||
|
* refused", flush the retransmission queue, discard the packet,
|
||
|
* and send a legal RST packet:
|
||
|
*
|
||
|
* <SN=received AN><CTL=RST>
|
||
|
*
|
||
|
* Then delete the TCB and go to the CLOSED state without any
|
||
|
* further processing.
|
||
|
*/
|
||
|
static int ratp_behaviour_f1(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
uint8_t control;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
if (!(hdr->control & RATP_CONTROL_ACK))
|
||
|
return 1;
|
||
|
|
||
|
if (ratp_an_expected(ri, hdr))
|
||
|
return 0;
|
||
|
|
||
|
control = RATP_CONTROL_RST | ratp_set_sn(ratp_an(hdr));
|
||
|
ratp_send_hdr(ri, control);
|
||
|
|
||
|
if (ri->active) {
|
||
|
ratp_state_change(ri, RATP_STATE_CLOSED);
|
||
|
ri->status = -ECONNREFUSED;
|
||
|
|
||
|
pr_debug("connection refused\n");
|
||
|
} else {
|
||
|
ratp_state_change(ri, RATP_STATE_LISTEN);
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Check the presence of the ACK flag. If ACK is not set then
|
||
|
* discard the packet and return without any further processing.
|
||
|
*
|
||
|
* We now assume that the ACK flag was set. If the AN field value
|
||
|
* was as expected then flush the retransmission queue and inform
|
||
|
* the user with an "Ok" if a buffer has been entirely
|
||
|
* acknowledged. Another packet containing data may now be sent.
|
||
|
* Return and continue the processing associated with this state.
|
||
|
*
|
||
|
* We now assume that the ACK flag was set and that the AN field
|
||
|
* value was unexpected. This is assumed to indicate a duplicate
|
||
|
* acknowledgment. It is ignored, return and continue the
|
||
|
* processing associated with this state.
|
||
|
*/
|
||
|
static int ratp_behaviour_f2(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
if (!(hdr->control & RATP_CONTROL_ACK))
|
||
|
return 1;
|
||
|
|
||
|
if (ratp_an_expected(ri, hdr)) {
|
||
|
pr_debug("Data succesfully sent\n");
|
||
|
if (ri->sendmsg_current)
|
||
|
ratp_msg_done(ri, ri->sendmsg_current, 0);
|
||
|
ri->sendmsg_current = NULL;
|
||
|
return 0;
|
||
|
} else {
|
||
|
pr_vdebug("%s: an not expected\n", __func__);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Check the presence of the ACK flag. If ACK is not set then
|
||
|
* discard the packet and return without any further processing.
|
||
|
*
|
||
|
* We now assume that the ACK flag was set. If the AN field value
|
||
|
* was as expected then continue the processing associated with
|
||
|
* this state.
|
||
|
*
|
||
|
* We now assume that the ACK flag was set and that the AN field
|
||
|
* value was unexpected. This is ignored, return and continue
|
||
|
* with the processing associated with this state.
|
||
|
*/
|
||
|
static int ratp_behaviour_f3(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
if (!(hdr->control & RATP_CONTROL_ACK))
|
||
|
return 1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This procedure represents the behavior of the CLOSED state of a
|
||
|
* connection. All incoming packets are discarded. If the packet
|
||
|
* had the RST flag set take no action. Otherwise it is necessary
|
||
|
* to build a RST packet. Since this end is closed the other end
|
||
|
* of the connection has incorrect data about the state of the
|
||
|
* connection and should be so informed.
|
||
|
*
|
||
|
* If the ACK flag was set then send:
|
||
|
*
|
||
|
* <SN=received AN><CTL=RST>
|
||
|
*
|
||
|
* If the ACK flag was not set then send:
|
||
|
*
|
||
|
* <SN=0><AN=received SN+1 modulo 2><CTL=RST, ACK>
|
||
|
*
|
||
|
* After sending the reset packet return to the current state
|
||
|
* without any further processing.
|
||
|
*/
|
||
|
static int ratp_behaviour_g(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
uint8_t control;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
control = RATP_CONTROL_RST;
|
||
|
|
||
|
if (hdr->control & RATP_CONTROL_ACK)
|
||
|
control |= ratp_set_sn(ratp_an(hdr));
|
||
|
else
|
||
|
control = ratp_set_an(ratp_sn(hdr) + 1) | RATP_CONTROL_ACK;
|
||
|
|
||
|
ratp_send_hdr(ri, control);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Our SYN has been acknowledged. At this point we are
|
||
|
* technically in the ESTABLISHED state. Send any initial data
|
||
|
* which is queued to send:
|
||
|
*
|
||
|
* <SN=received AN><AN=received SN+1 modulo 2><CTL=ACK><DATA>
|
||
|
*
|
||
|
* Go to the ESTABLISHED state and execute procedure I1 to process
|
||
|
* any data which might be in this packet.
|
||
|
*
|
||
|
* Any packet not satisfying the above tests is discarded and
|
||
|
* ignored. Return to the current state without any further
|
||
|
* processing.
|
||
|
*/
|
||
|
static int ratp_behaviour_h1(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
ratp_state_change(ri, RATP_STATE_ESTABLISHED);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Check the presence of the FIN flag. If FIN is not set then
|
||
|
* continue the processing associated with this state.
|
||
|
*
|
||
|
* We now assume that the FIN flag was set. This means the other
|
||
|
* end has decided to close the connection. Flush the
|
||
|
* retransmission queue. If any data remains to be sent then
|
||
|
* inform the user "Warning: Data left unsent." The user must
|
||
|
* also be informed "Connection closing." An acknowledgment for
|
||
|
* the FIN must be sent which also indicates this end is closing:
|
||
|
*
|
||
|
* <SN=received AN><AN=received SN + 1 modulo 2><CTL=FIN, ACK>
|
||
|
*
|
||
|
* Go to the LAST-ACK state without any further processing.
|
||
|
*/
|
||
|
static int ratp_behaviour_h2(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
uint8_t control;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
if (!(hdr->control & RATP_CONTROL_FIN))
|
||
|
return 0;
|
||
|
|
||
|
ri->status = -ENETDOWN;
|
||
|
|
||
|
control = ratp_set_sn(ratp_an(hdr)) |
|
||
|
ratp_set_an(ratp_sn(hdr) + 1) |
|
||
|
RATP_CONTROL_FIN |
|
||
|
RATP_CONTROL_ACK;
|
||
|
|
||
|
ratp_send_hdr(ri, control);
|
||
|
|
||
|
ratp_state_change(ri, RATP_STATE_LAST_ACK);
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This state represents the final behavior of the FIN-WAIT state.
|
||
|
*
|
||
|
* If the packet did not contain a FIN we assume this packet is a
|
||
|
* duplicate and that the other end of the connection has not seen
|
||
|
* the FIN packet we sent earlier. Rely upon retransmission of
|
||
|
* our earlier FIN packet to inform the other end of our desire to
|
||
|
* close. Discard the packet and return without any further
|
||
|
* processing.
|
||
|
*
|
||
|
* At this point we have a packet which should contain a FIN. By
|
||
|
* the rules of this protocol an ACK of a FIN requires a FIN, ACK
|
||
|
* in response and no data. If the packet contains data we have
|
||
|
* detected an illegal condition. Send a reset:
|
||
|
* <SN=received AN><AN=received SN+1 modulo 2><CTL=RST, ACK>
|
||
|
*
|
||
|
* Discard the packet, flush the retransmission queue, inform the
|
||
|
* ser "Error: Connection reset.", delete the TCB, and go to the
|
||
|
* CLOSED state without any further processing.
|
||
|
*
|
||
|
* We now assume that the FIN flag was set and no data was
|
||
|
* contained in the packet. If the AN field value was expected
|
||
|
* then this packet acknowledges a previously sent FIN packet.
|
||
|
* The other end of the connection is then also assumed to be
|
||
|
* closing and expects an acknowledgment. Send an acknowledgment
|
||
|
* of the FIN:
|
||
|
*
|
||
|
* <SN=received AN><AN=received SN+1 modulo 2><CTL=ACK>
|
||
|
*
|
||
|
* Start the 2*SRTT timer associated with the TIME-WAIT state,
|
||
|
* discard the packet, and go to the TIME-WAIT state without any
|
||
|
* further processing.
|
||
|
*
|
||
|
* Otherwise the AN field value was unexpected. This indicates a
|
||
|
* simultaneous closing by both sides of the connection. Send an
|
||
|
* acknowledgment of the FIN:
|
||
|
*
|
||
|
* <SN=received AN><AN=received SN+1 modulo 2><CTL=ACK>
|
||
|
*
|
||
|
* Discard the packet, and go to the CLOSING state without any
|
||
|
* further processing.
|
||
|
*/
|
||
|
static int ratp_behaviour_h3(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
uint8_t control;
|
||
|
int expected;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
if (!(hdr->control & RATP_CONTROL_FIN))
|
||
|
return 1;
|
||
|
|
||
|
if (ratp_has_data(hdr)) {
|
||
|
control = ratp_set_sn(ratp_an(hdr)) |
|
||
|
ratp_set_an(ratp_sn(hdr) + 1) |
|
||
|
RATP_CONTROL_RST |
|
||
|
RATP_CONTROL_ACK;
|
||
|
ratp_send_hdr(ri, control);
|
||
|
ri->status = -ECONNRESET;
|
||
|
pr_debug("Error: Connection reset\n");
|
||
|
ratp_state_change(ri, RATP_STATE_CLOSED);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
control = ratp_set_sn(ratp_an(hdr)) |
|
||
|
ratp_set_an(ratp_sn(hdr) + 1) |
|
||
|
RATP_CONTROL_ACK;
|
||
|
|
||
|
expected = ratp_an_expected(ri, hdr);
|
||
|
|
||
|
ratp_send_hdr(ri, control);
|
||
|
|
||
|
if (expected) {
|
||
|
ratp_state_change(ri, RATP_STATE_TIME_WAIT);
|
||
|
ratp_start_time_wait_timer(ri);
|
||
|
} else {
|
||
|
ratp_state_change(ri, RATP_STATE_CLOSING);
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This state represents the final behavior of the LAST-ACK state.
|
||
|
*
|
||
|
* If the AN field value is expected then this ACK is in response
|
||
|
* to the FIN, ACK packet recently sent. This is the final
|
||
|
* acknowledging message indicating both side's agreement to close
|
||
|
* the connection. Discard the packet, flush all queues, delete
|
||
|
* the TCB, and go to the CLOSED state without any further
|
||
|
* processing.
|
||
|
*
|
||
|
* Otherwise the AN field value was unexpected. Discard the
|
||
|
* packet and remain in the current state without any further
|
||
|
* processing.
|
||
|
*/
|
||
|
static int ratp_behaviour_h4(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
if (ratp_an_expected(ri, hdr))
|
||
|
ratp_state_change(ri, RATP_STATE_CLOSED);
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This state represents the final behavior of the CLOSING state.
|
||
|
*
|
||
|
* If the AN field value was expected then this packet
|
||
|
* acknowledges the FIN packet recently sent. This is the final
|
||
|
* acknowledging message indicating both side's agreement to close
|
||
|
* the connection. Start the 2*SRTT timer associated with the
|
||
|
* TIME-WAIT state, discard the packet, and go to the TIME-WAIT
|
||
|
* state without any further processing.
|
||
|
*
|
||
|
* Otherwise the AN field value was unexpected. Discard the
|
||
|
* packet and remain in the current state without any further
|
||
|
* processing.
|
||
|
*/
|
||
|
static int ratp_behaviour_h5(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
if (ratp_an_expected(ri, hdr)) {
|
||
|
ratp_state_change(ri, RATP_STATE_TIME_WAIT);
|
||
|
ratp_start_time_wait_timer(ri);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This state represents the behavior of the TIME-WAIT state.
|
||
|
* Check the presence of the ACK flag. If ACK is not set then
|
||
|
* discard the packet and return without any further processing.
|
||
|
*
|
||
|
* Check the presence of the FIN flag. If FIN is not set then
|
||
|
* discard the packet and return without any further processing.
|
||
|
*
|
||
|
* We now assume that the FIN flag was set. This situation
|
||
|
* indicates that the last acknowledgment of the FIN packet sent
|
||
|
* by the other end of the connection did not arrive. Resend the
|
||
|
* acknowledgment:
|
||
|
*
|
||
|
* <SN=received AN><AN=received SN+1 modulo 2><CTL=ACK>
|
||
|
*
|
||
|
* Restart the 2*SRTT timer, discard the packet, and remain in the
|
||
|
* current state without any further processing.
|
||
|
*/
|
||
|
static int ratp_behaviour_h6(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
uint8_t control;
|
||
|
|
||
|
pr_debug("%s\n", __func__);
|
||
|
|
||
|
if (!(hdr->control & RATP_CONTROL_ACK))
|
||
|
return 1;
|
||
|
|
||
|
if (!(hdr->control & RATP_CONTROL_FIN))
|
||
|
return 1;
|
||
|
|
||
|
control = ratp_set_sn(ratp_an(hdr) + 1) | RATP_CONTROL_ACK;
|
||
|
|
||
|
ratp_send_hdr(ri, control);
|
||
|
|
||
|
ratp_start_time_wait_timer(ri);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int msg_recv(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
struct ratp_message *msg;
|
||
|
|
||
|
pr_debug("%s: Put msg in receive queue\n", __func__);
|
||
|
|
||
|
msg = xzalloc(sizeof(*msg));
|
||
|
if (hdr->data_length) {
|
||
|
msg->len = hdr->data_length;
|
||
|
msg->buf = xzalloc(msg->len);
|
||
|
memcpy(msg->buf, pkt + sizeof(struct ratp_header), msg->len);
|
||
|
} else {
|
||
|
msg->len = 1;
|
||
|
msg->buf = xzalloc(1);
|
||
|
*(uint8_t *)msg->buf = hdr->data_length;
|
||
|
}
|
||
|
|
||
|
if (hdr->control & RATP_CONTROL_EOR)
|
||
|
msg->eor = 1;
|
||
|
|
||
|
list_add_tail(&msg->list, &ri->recvmsg);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This represents that stage of processing in the ESTABLISHED
|
||
|
* state in which all the flag bits have been processed and only
|
||
|
* data may remain. The packet is examined to see if it contains
|
||
|
* data. If not the packet is now discarded, return to the
|
||
|
* current state without any further processing.
|
||
|
*
|
||
|
* We assume the packet contained data, that either the SO flag
|
||
|
* was set or LENGTH is positive. That data is placed into the
|
||
|
* user's receive buffers. As these become full the user should
|
||
|
* be informed "Receive buffer full." An acknowledgment is sent:
|
||
|
*
|
||
|
* <SN=received AN><AN=received SN+1 modulo 2><CTL=ACK>
|
||
|
*
|
||
|
* If data is queued to send then it is most efficient to
|
||
|
* 'piggyback' this acknowledgment on that data packet.
|
||
|
*
|
||
|
* The packet is now discarded, return to the ESTABLISHED state
|
||
|
* without any further processing.
|
||
|
*/
|
||
|
static int ratp_behaviour_i1(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
uint8_t control = 0;
|
||
|
|
||
|
if (!hdr->data_length && !(hdr->control & RATP_CONTROL_SO))
|
||
|
return 1;
|
||
|
|
||
|
pr_vdebug("%s **received** %d\n", __func__, hdr->data_length);
|
||
|
|
||
|
ri->sn_received = ratp_sn(hdr);
|
||
|
|
||
|
msg_recv(ri, pkt);
|
||
|
|
||
|
if (list_empty(&ri->sendmsg) || ri->sendmsg_current) {
|
||
|
control = ratp_set_sn(!ri->sn_sent) |
|
||
|
ratp_set_an(ri->sn_received + 1) |
|
||
|
RATP_CONTROL_ACK;
|
||
|
|
||
|
ratp_send_hdr(ri, control);
|
||
|
} else {
|
||
|
ratp_send_next_data(ri);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* State machine as desribed in RFC916
|
||
|
*
|
||
|
* STATE BEHAVIOR
|
||
|
* =============+========================
|
||
|
* LISTEN | A
|
||
|
* -------------+------------------------
|
||
|
* SYN-SENT | B
|
||
|
* -------------+------------------------
|
||
|
* SYN-RECEIVED | C1 D1 E F1 H1
|
||
|
* -------------+------------------------
|
||
|
* ESTABLISHED | C2 D2 E F2 H2 I1
|
||
|
* -------------+------------------------
|
||
|
* FIN-WAIT | C2 D2 E F3 H3
|
||
|
* -------------+------------------------
|
||
|
* LAST-ACK | C2 D3 E F3 H4
|
||
|
* -------------+------------------------
|
||
|
* CLOSING | C2 D3 E F3 H5
|
||
|
* -------------+------------------------
|
||
|
* TIME-WAIT | D3 E F3 H6
|
||
|
* -------------+------------------------
|
||
|
* CLOSED | G
|
||
|
* -------------+------------------------
|
||
|
*/
|
||
|
|
||
|
static int ratp_state_machine(struct ratp_internal *ri, void *pkt)
|
||
|
{
|
||
|
struct ratp_header *hdr = pkt;
|
||
|
int ret;
|
||
|
|
||
|
ratp_print_header(ri, hdr, " recv");
|
||
|
pr_debug(" state %s\n", ratp_state_str[ri->state]);
|
||
|
|
||
|
switch (ri->state) {
|
||
|
case RATP_STATE_LISTEN:
|
||
|
ratp_behaviour_a(ri, pkt);
|
||
|
break;
|
||
|
case RATP_STATE_SYN_SENT:
|
||
|
ratp_behaviour_b(ri, pkt);
|
||
|
break;
|
||
|
case RATP_STATE_SYN_RECEIVED:
|
||
|
ret = ratp_behaviour_c1(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_d1(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_e(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_f1(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_h1(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
break;
|
||
|
case RATP_STATE_ESTABLISHED:
|
||
|
ret = ratp_behaviour_c2(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_d2(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_e(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_f2(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_h2(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_i1(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
break;
|
||
|
case RATP_STATE_FIN_WAIT:
|
||
|
ret = ratp_behaviour_c2(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_d2(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_e(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_f3(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_h3(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
break;
|
||
|
case RATP_STATE_LAST_ACK:
|
||
|
ret = ratp_behaviour_c2(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_d3(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_e(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_f3(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_h4(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
break;
|
||
|
case RATP_STATE_CLOSING:
|
||
|
ret = ratp_behaviour_c2(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_d3(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_e(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_f3(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_h5(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
break;
|
||
|
case RATP_STATE_TIME_WAIT:
|
||
|
ret = ratp_behaviour_d3(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_e(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_f3(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = ratp_behaviour_h6(ri, pkt);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
break;
|
||
|
case RATP_STATE_CLOSED:
|
||
|
ratp_behaviour_g(ri, pkt);
|
||
|
break;
|
||
|
};
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ratp_closed() - Check if a connection is closed
|
||
|
*
|
||
|
* Return: true if a connection is closed, false otherwise
|
||
|
*/
|
||
|
bool ratp_closed(struct ratp *ratp)
|
||
|
{
|
||
|
struct ratp_internal *ri = ratp->internal;
|
||
|
|
||
|
if (!ri)
|
||
|
return true;
|
||
|
|
||
|
return ri->state == RATP_STATE_CLOSED;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ratp_busy() - Check if we are inside the RATP code
|
||
|
*
|
||
|
* Needed for RATP debugging. The RATP console uses this to determine
|
||
|
* if it is called from inside the RATP code.
|
||
|
*
|
||
|
* Return: true if we are inside the RATP code, false otherwise
|
||
|
*/
|
||
|
bool ratp_busy(struct ratp *ratp)
|
||
|
{
|
||
|
struct ratp_internal *ri = ratp->internal;
|
||
|
|
||
|
if (!ri)
|
||
|
return false;
|
||
|
|
||
|
return ri->in_ratp != 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ratp_poll() - Execute RATP state machine
|
||
|
* @ratp: The RATP link
|
||
|
*
|
||
|
* This function should be executed periodically to keep the RATP state
|
||
|
* machine going.
|
||
|
*
|
||
|
* Return: 0 if successful, a negative error code otherwise.
|
||
|
*/
|
||
|
int ratp_poll(struct ratp *ratp)
|
||
|
{
|
||
|
struct ratp_internal *ri = ratp->internal;
|
||
|
int ret;
|
||
|
|
||
|
if (!ri)
|
||
|
return -ENETDOWN;
|
||
|
|
||
|
ri->in_ratp++;
|
||
|
|
||
|
ret = ratp_recv_pkt(ri, ri->recvbuf, 100);
|
||
|
if (ret == 0) {
|
||
|
|
||
|
if (ri->state == RATP_STATE_TIME_WAIT &&
|
||
|
is_timeout(ri->timewait_timer_start, ri->srtt * 2 * MSECOND)) {
|
||
|
pr_debug("2*SRTT timer timed out\n");
|
||
|
ret = -ECONNRESET;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
ret = ratp_state_machine(ri, ri->recvbuf);
|
||
|
if (ret < 0)
|
||
|
goto out;
|
||
|
|
||
|
if (ri->status < 0) {
|
||
|
ret = ri->status;
|
||
|
goto out;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ri->sendmsg_current && is_timeout(ri->retransmission_timer_start,
|
||
|
ri->rto * MSECOND)) {
|
||
|
|
||
|
ri->retransmission_count++;
|
||
|
if (ri->retransmission_count == ri->max_retransmission) {
|
||
|
ri->status = ret = -ETIMEDOUT;
|
||
|
ri->state = RATP_STATE_CLOSED;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
pr_debug("%s: retransmit\n", __func__);
|
||
|
|
||
|
ratp_print_header(ri, ri->sendbuf, "resend");
|
||
|
|
||
|
ri->retransmission_timer_start = get_time_ns();
|
||
|
|
||
|
ret = ri->ratp->send(ratp, ri->sendbuf, ri->sendbuf_len);
|
||
|
if (ret)
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
if (!ri->sendmsg_current && !list_empty(&ri->sendmsg))
|
||
|
ratp_send_next_data(ri);
|
||
|
|
||
|
ret = 0;
|
||
|
out:
|
||
|
ri->in_ratp--;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ratp_establish(): Establish a RATP link
|
||
|
* @ratp: The RATP link
|
||
|
* @active: if true actively create a connection
|
||
|
* @timeout_ms: Timeout in ms to wait until a connection is established. If
|
||
|
* 0 wait forever.
|
||
|
*
|
||
|
* This function establishes a link with the remote end. It expects the
|
||
|
* send and receive functions to be set, all other struct ratp_internal members can
|
||
|
* be left uninitialized.
|
||
|
*
|
||
|
* Return: 0 if successful, a negative error code otherwise.
|
||
|
*/
|
||
|
int ratp_establish(struct ratp *ratp, bool active, int timeout_ms)
|
||
|
{
|
||
|
struct ratp_internal *ri;
|
||
|
int ret;
|
||
|
uint64_t start;
|
||
|
|
||
|
ri = xzalloc(sizeof(*ri));
|
||
|
ri->ratp = ratp;
|
||
|
ratp->internal = ri;
|
||
|
|
||
|
ri->recvbuf = xmalloc(512);
|
||
|
ri->sendbuf = xmalloc(512);
|
||
|
INIT_LIST_HEAD(&ri->recvmsg);
|
||
|
INIT_LIST_HEAD(&ri->sendmsg);
|
||
|
ri->max_retransmission = 100;
|
||
|
ri->srtt = 100;
|
||
|
ri->rto = 100;
|
||
|
ri->active = active;
|
||
|
|
||
|
ri->in_ratp++;
|
||
|
|
||
|
if (ri->active) {
|
||
|
ratp_send_hdr(ri, RATP_CONTROL_SYN);
|
||
|
|
||
|
ratp_state_change(ri, RATP_STATE_SYN_SENT);
|
||
|
}
|
||
|
|
||
|
start = get_time_ns();
|
||
|
|
||
|
while (1) {
|
||
|
ret = ratp_poll(ri->ratp);
|
||
|
if (ret < 0)
|
||
|
goto out;
|
||
|
|
||
|
if (ri->state == RATP_STATE_ESTABLISHED) {
|
||
|
ret = 0;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
if (timeout_ms && is_timeout(start, MSECOND * timeout_ms)) {
|
||
|
ret = -ETIMEDOUT;
|
||
|
goto out;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
out:
|
||
|
if (ret) {
|
||
|
free(ri->recvbuf);
|
||
|
free(ri->sendbuf);
|
||
|
free(ri);
|
||
|
ratp->internal = NULL;
|
||
|
}
|
||
|
|
||
|
ri->in_ratp--;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void ratp_close(struct ratp *ratp)
|
||
|
{
|
||
|
struct ratp_internal *ri = ratp->internal;
|
||
|
struct ratp_message *msg, *tmp;
|
||
|
struct ratp_header fin = {};
|
||
|
|
||
|
if (!ri)
|
||
|
return;
|
||
|
|
||
|
if (ri->state == RATP_STATE_ESTABLISHED) {
|
||
|
uint64_t start;
|
||
|
u8 control;
|
||
|
|
||
|
pr_debug("Closing...\n");
|
||
|
|
||
|
ratp_state_change(ri, RATP_STATE_FIN_WAIT);
|
||
|
|
||
|
control = ratp_set_sn(!ri->sn_sent) |
|
||
|
ratp_set_an(ri->sn_received + 1) |
|
||
|
RATP_CONTROL_FIN | RATP_CONTROL_ACK;
|
||
|
|
||
|
ratp_create_packet(ri, &fin, control, 0);
|
||
|
|
||
|
ratp_send_pkt(ri, &fin, sizeof(fin));
|
||
|
|
||
|
start = get_time_ns();
|
||
|
|
||
|
while (!is_timeout(start, ri->srtt * MSECOND * 2))
|
||
|
ratp_poll(ratp);
|
||
|
}
|
||
|
|
||
|
list_for_each_entry_safe(msg, tmp, &ri->sendmsg, list)
|
||
|
ratp_msg_done(ri, msg, -ECONNRESET);
|
||
|
|
||
|
free(ri);
|
||
|
ratp->internal = NULL;
|
||
|
|
||
|
pr_info("Closed\n");
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ratp_send_complete(): Send data over a RATP link
|
||
|
* @ratp: The RATP link
|
||
|
* @data: The data buffer
|
||
|
* @len: The length of the message to send
|
||
|
* @complete: The completion callback for the message
|
||
|
* @complete_ctx: context pointer for the completion callback
|
||
|
*
|
||
|
* Queue a RATP message for transmission. This only queues the message,
|
||
|
* ratp_poll has to be called to actually transfer the message.
|
||
|
* @complete will be called upon completion of the message.
|
||
|
*
|
||
|
* Return: 0 if successful, a negative error code otherwise.
|
||
|
*/
|
||
|
int ratp_send_complete(struct ratp *ratp, const void *data, size_t len,
|
||
|
void (*complete)(void *ctx, int status), void *complete_ctx)
|
||
|
{
|
||
|
struct ratp_internal *ri = ratp->internal;
|
||
|
struct ratp_message *msg;
|
||
|
|
||
|
if (!ri || ri->state != RATP_STATE_ESTABLISHED)
|
||
|
return -ENETDOWN;
|
||
|
|
||
|
if (!len)
|
||
|
return -EINVAL;
|
||
|
|
||
|
ri->in_ratp++;
|
||
|
|
||
|
while (len) {
|
||
|
int now = min((int)len, 255);
|
||
|
|
||
|
msg = xzalloc(sizeof(*msg));
|
||
|
msg->buf = xzalloc(sizeof(struct ratp_header) + now + 2);
|
||
|
msg->len = now;
|
||
|
memcpy(msg->buf + sizeof(struct ratp_header), data, now);
|
||
|
|
||
|
list_add_tail(&msg->list, &ri->sendmsg);
|
||
|
|
||
|
len -= now;
|
||
|
}
|
||
|
|
||
|
msg->eor = 1;
|
||
|
msg->complete = complete;
|
||
|
msg->complete_ctx = complete_ctx;
|
||
|
|
||
|
ri->in_ratp--;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ratp_send(): Send data over a RATP link
|
||
|
* @ratp: The RATP link
|
||
|
* @data: The data buffer
|
||
|
* @len: The length of the message to send
|
||
|
*
|
||
|
* Queue a RATP message for transmission. This only queues the message,
|
||
|
* ratp_poll has to be called to actually transfer the message.
|
||
|
*
|
||
|
* Return: 0 if successful, a negative error code otherwise.
|
||
|
*/
|
||
|
int ratp_send(struct ratp *ratp, const void *data, size_t len)
|
||
|
{
|
||
|
return ratp_send_complete(ratp, data, len, NULL, NULL);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ratp_recv() - Receive data from a RATP link
|
||
|
* @ratp: The RATP link
|
||
|
* @data: Pointer to data
|
||
|
* @len: The length of the data in bytes
|
||
|
*
|
||
|
* If a message is available it fills @data with a pointer to the data.
|
||
|
* This function does not wait for new messages. If no data is available
|
||
|
* -EAGAIN is returned. If data is received @data has to be freed by the
|
||
|
* caller.
|
||
|
*
|
||
|
* Return: 0 if successful, a negative error code otherwise.
|
||
|
*/
|
||
|
int ratp_recv(struct ratp *ratp, void **data, size_t *len)
|
||
|
{
|
||
|
struct ratp_internal *ri = ratp->internal;
|
||
|
struct ratp_message *msg, *tmp;
|
||
|
void *pos;
|
||
|
int num = 0;
|
||
|
|
||
|
*len = 0;
|
||
|
|
||
|
if (!ri || ri->state != RATP_STATE_ESTABLISHED)
|
||
|
return -ENETDOWN;
|
||
|
|
||
|
if (list_empty(&ri->recvmsg))
|
||
|
return -EAGAIN;
|
||
|
|
||
|
list_for_each_entry(msg, &ri->recvmsg, list) {
|
||
|
*len += msg->len;
|
||
|
num++;
|
||
|
if (msg->eor)
|
||
|
goto eor;
|
||
|
}
|
||
|
|
||
|
return -EAGAIN;
|
||
|
|
||
|
eor:
|
||
|
*data = malloc(*len);
|
||
|
if (!*data)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
pos = *data;
|
||
|
|
||
|
list_for_each_entry_safe(msg, tmp, &ri->recvmsg, list) {
|
||
|
memcpy(pos, msg->buf, msg->len);
|
||
|
pos += msg->len;
|
||
|
|
||
|
list_del(&msg->list);
|
||
|
|
||
|
free(msg->buf);
|
||
|
free(msg);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|