blob: 07e3b9cb9377357a107cc36f16b27513b1f3f6c5 [file] [log] [blame]
#include <re_types.h>
#include <re_fmt.h>
#include <re_mem.h>
#include <re_mbuf.h>
#include <re_tcp.h>
#include <re_net.h>
#include <re_shim.h>
#define DEBUG_MODULE "shim"
#define DEBUG_LEVEL 5
#include <re_dbg.h>
struct shim {
struct tcp_conn *tc;
struct tcp_helper *th;
struct mbuf *mb;
shim_frame_h *frameh;
void *arg;
uint64_t n_tx;
uint64_t n_rx;
};
/* responsible for adding the SHIM header
- assumes that the sent MBUF contains a complete packet
*/
static bool shim_send_handler(int *err, struct mbuf *mb, void *arg)
{
struct shim *shim = arg;
size_t len;
(void)shim;
if (mb->pos < SHIM_HDR_SIZE) {
DEBUG_WARNING("send: not enough space for SHIM header\n");
*err = ENOMEM;
return true;
}
len = mbuf_get_left(mb);
mb->pos -= SHIM_HDR_SIZE;
*err = mbuf_write_u16(mb, htons(len));
mb->pos -= SHIM_HDR_SIZE;
++shim->n_tx;
return false;
}
static bool shim_recv_handler(int *errp, struct mbuf *mbx, bool *estab,
void *arg)
{
struct shim *shim = arg;
int err = 0;
(void)estab;
/* handle re-assembly */
if (!shim->mb) {
shim->mb = mbuf_alloc(1024);
if (!shim->mb) {
*errp = ENOMEM;
return true;
}
}
if (shim->mb) {
size_t pos;
pos = shim->mb->pos;
shim->mb->pos = shim->mb->end;
err = mbuf_write_mem(shim->mb, mbuf_buf(mbx),
mbuf_get_left(mbx));
if (err)
goto out;
shim->mb->pos = pos;
}
/* extract all SHIM-frames in the TCP-stream */
for (;;) {
size_t start, len, pos, end;
bool hdld;
start = shim->mb->pos;
if (mbuf_get_left(shim->mb) < (SHIM_HDR_SIZE))
break;
len = ntohs(mbuf_read_u16(shim->mb));
if (mbuf_get_left(shim->mb) < len)
goto rewind;
pos = shim->mb->pos;
end = shim->mb->end;
shim->mb->end = pos + len;
++shim->n_rx;
hdld = shim->frameh(shim->mb, shim->arg);
if (!hdld) {
/* XXX: handle multiple frames per segment */
shim->mb->pos = pos;
shim->mb->end = pos + len;
mbx->pos = mbx->end = 2;
err = mbuf_write_mem(mbx, mbuf_buf(shim->mb), len);
if (err)
goto out;
mbx->pos = 2;
shim->mb->pos = pos + len;
shim->mb->end = end;
return false; /* continue recv-handlers */
}
shim->mb->pos = pos + len;
shim->mb->end = end;
if (shim->mb->pos >= shim->mb->end) {
shim->mb = mem_deref(shim->mb);
break;
}
continue;
rewind:
shim->mb->pos = start;
break;
}
out:
if (err)
*errp = err;
return true; /* always handled */
}
static void destructor(void *arg)
{
struct shim *shim = arg;
mem_deref(shim->th);
mem_deref(shim->tc);
mem_deref(shim->mb);
}
int shim_insert(struct shim **shimp, struct tcp_conn *tc, int layer,
shim_frame_h *frameh, void *arg)
{
struct shim *shim;
int err;
if (!shimp || !tc || !frameh)
return EINVAL;
shim = mem_zalloc(sizeof(*shim), destructor);
if (!shim)
return ENOMEM;
shim->tc = mem_ref(tc);
err = tcp_register_helper(&shim->th, tc, layer, NULL,
shim_send_handler,
shim_recv_handler, shim);
if (err)
goto out;
shim->frameh = frameh;
shim->arg = arg;
out:
if (err)
mem_deref(shim);
else
*shimp = shim;
return err;
}
int shim_debug(struct re_printf *pf, const struct shim *shim)
{
if (!shim)
return 0;
return re_hprintf(pf, "tx=%llu, rx=%llu", shim->n_tx, shim->n_rx);
}