Source to ./atm_vsar.c
/*
* Cisco router simulation platform.
* Copyright (c) 2007 Christophe Fillot ([email protected])
*
* ATM Virtual Segmentation & Reassembly Engine.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>
#include "utils.h"
#include "registry.h"
#include "crc.h"
#include "atm.h"
#include "net_io.h"
#include "atm_vsar.h"
/* Segmentation Context */
struct atm_seg_context {
netio_desc_t *nio;
m_uint8_t txfifo_cell[ATM_CELL_SIZE];
size_t txfifo_pos,txfifo_avail;
size_t aal5_len;
m_uint32_t aal5_crc;
m_uint32_t atm_hdr;
char *buffer;
size_t buf_len;
};
/* Send the ATM cell in FIFO */
static void atm_send_cell(struct atm_seg_context *asc)
{
m_hton32(asc->txfifo_cell,asc->atm_hdr);
atm_insert_hec(asc->txfifo_cell);
netio_send(asc->nio,asc->txfifo_cell,ATM_CELL_SIZE);
}
/* Clear the TX fifo */
static void atm_clear_tx_fifo(struct atm_seg_context *asc)
{
asc->txfifo_avail = ATM_PAYLOAD_SIZE;
asc->txfifo_pos = ATM_HDR_SIZE;
memset(asc->txfifo_cell,0,ATM_CELL_SIZE);
}
/* Add padding to the FIFO */
static void atm_add_tx_padding(struct atm_seg_context *asc,size_t len)
{
if (len > asc->txfifo_avail)
len = asc->txfifo_avail;
memset(&asc->txfifo_cell[asc->txfifo_pos],0,len);
asc->txfifo_pos += len;
asc->txfifo_avail -= len;
}
/* Send the TX fifo if it is empty */
static void atm_send_fifo(struct atm_seg_context *asc)
{
if (!asc->txfifo_avail) {
asc->aal5_crc = crc32_compute(~asc->aal5_crc,
&asc->txfifo_cell[ATM_HDR_SIZE],
ATM_PAYLOAD_SIZE);
atm_send_cell(asc);
atm_clear_tx_fifo(asc);
}
}
/* Store a packet in the TX FIFO */
static int atm_store_fifo(struct atm_seg_context *asc)
{
size_t len;
len = m_min(asc->buf_len,asc->txfifo_avail);
memcpy(&asc->txfifo_cell[asc->txfifo_pos],asc->buffer,len);
asc->buffer += len;
asc->buf_len -= len;
asc->txfifo_pos += len;
asc->txfifo_avail -= len;
if (!asc->txfifo_avail) {
atm_send_fifo(asc);
return(TRUE);
}
return(FALSE);
}
/* Add the AAL5 trailer to the TX FIFO */
static void atm_aal5_add_trailer(struct atm_seg_context *asc)
{
m_uint8_t *trailer;
trailer = &asc->txfifo_cell[ATM_AAL5_TRAILER_POS];
/* Control field + Length */
m_hton32(trailer,asc->aal5_len);
/* Final CRC-32 computation */
asc->aal5_crc = crc32_compute(~asc->aal5_crc,
&asc->txfifo_cell[ATM_HDR_SIZE],
ATM_PAYLOAD_SIZE - 4);
m_hton32(trailer+4,asc->aal5_crc);
/* Consider the FIFO as full */
asc->txfifo_avail = 0;
}
/* Send an AAL5 packet through an NIO (segmentation) */
int atm_aal5_send(netio_desc_t *nio,u_int vpi,u_int vci,
struct iovec *iov,int iovcnt)
{
struct atm_seg_context asc;
int i;
asc.nio = nio;
asc.aal5_len = 0;
asc.aal5_crc = 0; /* will be inverted by first CRC update */
atm_clear_tx_fifo(&asc);
/* prepare the atm header */
asc.atm_hdr = vpi << ATM_HDR_VPI_SHIFT;
asc.atm_hdr |= vci << ATM_HDR_VCI_SHIFT;
for(i=0;i<iovcnt;i++) {
asc.buffer = iov[i].iov_base;
asc.buf_len = iov[i].iov_len;
asc.aal5_len += iov[i].iov_len;
while(asc.buf_len > 0)
atm_store_fifo(&asc);
}
/*
* Add the PDU trailer. If we have enough room, add it in the last cell,
* otherwise create a new one.
*/
if (asc.txfifo_avail < ATM_AAL5_TRAILER_SIZE) {
atm_add_tx_padding(&asc,asc.txfifo_avail);
atm_send_fifo(&asc);
}
/* Set AAL5 end of packet in ATM header (PTI field) */
asc.atm_hdr |= ATM_PTI_EOP;
atm_add_tx_padding(&asc,asc.txfifo_avail - ATM_AAL5_TRAILER_SIZE);
atm_aal5_add_trailer(&asc);
atm_send_cell(&asc);
return(0);
}
/* Reset a receive context */
void atm_aal5_recv_reset(struct atm_reas_context *arc)
{
arc->buf_pos = 0;
arc->len = 0;
}
/* Receive an ATM cell and process reassembly */
int atm_aal5_recv(struct atm_reas_context *arc,m_uint8_t *cell)
{
m_uint32_t atm_hdr;
/* Check buffer boundary */
if ((arc->buf_pos + ATM_PAYLOAD_SIZE) > ATM_REAS_MAX_SIZE) {
atm_aal5_recv_reset(arc);
return(-1);
}
/* Get the PTI field: we cannot handle "network" traffic */
atm_hdr = m_ntoh32(cell);
if (atm_hdr & ATM_PTI_NETWORK)
return(2);
/* Copy the payload */
memcpy(&arc->buffer[arc->buf_pos],&cell[ATM_HDR_SIZE],ATM_PAYLOAD_SIZE);
arc->buf_pos += ATM_PAYLOAD_SIZE;
/*
* If this is the last cell of the packet, get the real length (the
* trailer is at the end).
*/
if (atm_hdr & ATM_PTI_EOP) {
arc->len = m_ntoh16(&cell[ATM_AAL5_TRAILER_POS+2]);
return((arc->len <= arc->buf_pos) ? 1 : -2);
}
return(0);
}
/* Send a packet through a rfc1483 bridge encap */
int atm_aal5_send_rfc1483b(netio_desc_t *nio,u_int vpi,u_int vci,
void *pkt,size_t len)
{
struct iovec vec[2];
vec[0].iov_base = (void *)atm_rfc1483b_header;
vec[0].iov_len = ATM_RFC1483B_HLEN;
vec[1].iov_base = pkt;
vec[1].iov_len = len;
return(atm_aal5_send(nio,vpi,vci,vec,2));
}