HelenOS sources

root/uspace/srv/net/tcp/inet.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tcp_inet_ev_recv
  2. tcp_transmit_pdu
  3. tcp_received_pdu
  4. tcp_inet_init

/*
 * Copyright (c) 2015 Jiri Svoboda
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/** @addtogroup tcp
 * @{
 */

/**
 * @file TCP inet interfacing
 */

#include <bitops.h>
#include <byteorder.h>
#include <errno.h>
#include <inet/inet.h>
#include <mem.h>
#include <io/log.h>
#include <stdlib.h>

#include "inet.h"
#include "pdu.h"
#include "rqueue.h"
#include "std.h"

#define NAME       "tcp"

static errno_t tcp_inet_ev_recv(inet_dgram_t *dgram);
static void tcp_received_pdu(tcp_pdu_t *pdu);

static inet_ev_ops_t tcp_inet_ev_ops = {
        .recv = tcp_inet_ev_recv
};

/** Received datagram callback */
static errno_t tcp_inet_ev_recv(inet_dgram_t *dgram)
{
        uint8_t *pdu_raw;
        size_t pdu_raw_size;

        log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_inet_ev_recv()");

        pdu_raw = dgram->data;
        pdu_raw_size = dgram->size;

        /* Split into header and payload. */

        log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_inet_ev_recv() - split header/payload");

        tcp_pdu_t *pdu;
        size_t hdr_size;
        tcp_header_t *hdr;
        uint32_t data_offset;

        if (pdu_raw_size < sizeof(tcp_header_t)) {
                log_msg(LOG_DEFAULT, LVL_WARN, "pdu_raw_size = %zu < sizeof(tcp_header_t) = %zu",
                    pdu_raw_size, sizeof(tcp_header_t));
                return EINVAL;
        }

        hdr = (tcp_header_t *)pdu_raw;
        data_offset = BIT_RANGE_EXTRACT(uint32_t, DF_DATA_OFFSET_h, DF_DATA_OFFSET_l,
            uint16_t_be2host(hdr->doff_flags));

        hdr_size = sizeof(uint32_t) * data_offset;

        if (pdu_raw_size < hdr_size) {
                log_msg(LOG_DEFAULT, LVL_WARN, "pdu_raw_size = %zu < hdr_size = %zu",
                    pdu_raw_size, hdr_size);
                return EINVAL;
        }

        if (hdr_size < sizeof(tcp_header_t)) {
                log_msg(LOG_DEFAULT, LVL_WARN, "hdr_size = %zu < sizeof(tcp_header_t) = %zu",
                    hdr_size, sizeof(tcp_header_t));
                return EINVAL;
        }

        log_msg(LOG_DEFAULT, LVL_DEBUG, "pdu_raw_size=%zu, hdr_size=%zu",
            pdu_raw_size, hdr_size);
        pdu = tcp_pdu_create(pdu_raw, hdr_size, pdu_raw + hdr_size,
            pdu_raw_size - hdr_size);
        if (pdu == NULL) {
                log_msg(LOG_DEFAULT, LVL_WARN, "Failed creating PDU. Dropped.");
                return ENOMEM;
        }

        pdu->src = dgram->src;
        pdu->dest = dgram->dest;

        tcp_received_pdu(pdu);
        tcp_pdu_delete(pdu);

        return EOK;
}

/** Transmit PDU over network layer. */
void tcp_transmit_pdu(tcp_pdu_t *pdu)
{
        errno_t rc;
        uint8_t *pdu_raw;
        size_t pdu_raw_size;
        inet_dgram_t dgram;

        pdu_raw_size = pdu->header_size + pdu->text_size;
        pdu_raw = malloc(pdu_raw_size);
        if (pdu_raw == NULL) {
                log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to transmit PDU. Out of memory.");
                return;
        }

        memcpy(pdu_raw, pdu->header, pdu->header_size);
        memcpy(pdu_raw + pdu->header_size, pdu->text,
            pdu->text_size);

        dgram.iplink = 0;
        dgram.src = pdu->src;
        dgram.dest = pdu->dest;
        dgram.tos = 0;
        dgram.data = pdu_raw;
        dgram.size = pdu_raw_size;

        rc = inet_send(&dgram, INET_TTL_MAX, 0);
        if (rc != EOK)
                log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to transmit PDU.");

        free(pdu_raw);
}

/** Process received PDU. */
static void tcp_received_pdu(tcp_pdu_t *pdu)
{
        tcp_segment_t *dseg;
        inet_ep2_t rident;

        log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_received_pdu()");

        if (tcp_pdu_decode(pdu, &rident, &dseg) != EOK) {
                log_msg(LOG_DEFAULT, LVL_WARN, "Not enough memory. PDU dropped.");
                return;
        }

        /* Insert decoded segment into rqueue */
        tcp_rqueue_insert_seg(&rident, dseg);
}

/** Initialize TCP inet interface. */
errno_t tcp_inet_init(void)
{
        errno_t rc;

        log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_inet_init()");

        rc = inet_init(IP_PROTO_TCP, &tcp_inet_ev_ops);
        if (rc != EOK) {
                log_msg(LOG_DEFAULT, LVL_ERROR, "Failed connecting to internet service.");
                return ENOENT;
        }

        return EOK;
}

/**
 * @}
 */

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub