1530 lines
38 KiB
C
1530 lines
38 KiB
C
/*
|
|
* Cisco router simulation platform.
|
|
* Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr)
|
|
*
|
|
* Network Input/Output Abstraction Layer.
|
|
*/
|
|
|
|
/* By default, Cygwin supports only 64 FDs with select()! */
|
|
#ifdef __CYGWIN__
|
|
#define FD_SETSIZE 1024
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <sys/wait.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <pthread.h>
|
|
|
|
#ifdef __linux__
|
|
#include <net/if.h>
|
|
#include <linux/if_tun.h>
|
|
#endif
|
|
|
|
#include "net.h"
|
|
#include "net_io.h"
|
|
|
|
/* Free a NetIO descriptor */
|
|
static int netio_free (void *data, void *arg);
|
|
#if 0
|
|
/* NIO RX listener */
|
|
static pthread_mutex_t netio_rxl_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
static pthread_mutex_t netio_rxq_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
static struct netio_rx_listener *netio_rxl_list = NULL;
|
|
static struct netio_rx_listener *netio_rxl_add_list = NULL;
|
|
static netio_desc_t *netio_rxl_remove_list = NULL;
|
|
static pthread_t netio_rxl_thread;
|
|
static pthread_cond_t netio_rxl_cond;
|
|
|
|
#define NETIO_RXL_LOCK() pthread_mutex_lock(&netio_rxl_mutex);
|
|
#define NETIO_RXL_UNLOCK() pthread_mutex_unlock(&netio_rxl_mutex);
|
|
|
|
#define NETIO_RXQ_LOCK() pthread_mutex_lock(&netio_rxq_mutex);
|
|
#define NETIO_RXQ_UNLOCK() pthread_mutex_unlock(&netio_rxq_mutex);
|
|
#endif
|
|
|
|
/* NetIO type */
|
|
typedef struct {
|
|
char *name;
|
|
char *desc;
|
|
} netio_type_t;
|
|
|
|
/* NETIO types (must follow the enum definition) */
|
|
static netio_type_t netio_types[NETIO_TYPE_MAX] = {
|
|
{"unix", "UNIX local sockets"},
|
|
{"vde", "Virtual Distributed Ethernet / UML switch"},
|
|
{"tap", "Linux/FreeBSD TAP device"},
|
|
{"udp", "UDP sockets"},
|
|
{"tcp_cli", "TCP client"},
|
|
{"tcp_ser", "TCP server"},
|
|
#ifdef LINUX_ETH
|
|
{"linux_eth", "Linux Ethernet device"},
|
|
#endif
|
|
#ifdef GEN_ETH
|
|
{"gen_eth", "Generic Ethernet device (PCAP)"},
|
|
#endif
|
|
{"fifo", "FIFO (intra-hypervisor)"},
|
|
{"null", "Null device"},
|
|
};
|
|
|
|
/* Get NETIO type given a description */
|
|
int netio_get_type (char *type)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NETIO_TYPE_MAX; i++)
|
|
if (!strcmp (type, netio_types[i].name))
|
|
return (i);
|
|
|
|
return (-1);
|
|
}
|
|
|
|
/* Show the NETIO types */
|
|
void netio_show_types (void)
|
|
{
|
|
int i;
|
|
|
|
printf ("Available NETIO types:\n");
|
|
|
|
for (i = 0; i < NETIO_TYPE_MAX; i++)
|
|
printf (" * %-10s : %s\n", netio_types[i].name, netio_types[i].desc);
|
|
|
|
printf ("\n");
|
|
}
|
|
|
|
/* Create a new NetIO descriptor */
|
|
static netio_desc_t *netio_create (char *name)
|
|
{
|
|
netio_desc_t *nio;
|
|
|
|
if (!(nio = malloc (sizeof (*nio))))
|
|
return NULL;
|
|
|
|
/* setup as a NULL descriptor */
|
|
memset (nio, 0, sizeof (*nio));
|
|
nio->type = NETIO_TYPE_NULL;
|
|
|
|
/* save name for registry */
|
|
if (!(nio->name = strdup (name))) {
|
|
free (nio);
|
|
return NULL;
|
|
}
|
|
|
|
return nio;
|
|
}
|
|
|
|
/* Send a packet through a NetIO descriptor */
|
|
ssize_t netio_send (netio_desc_t * nio, void *pkt, size_t len)
|
|
{
|
|
|
|
if (!nio)
|
|
return (-1);
|
|
|
|
if (nio->debug) {
|
|
printf ("NIO %s: sending a packet of %lu bytes:\n", nio->name,
|
|
(u_long) len);
|
|
mem_dump (stdout, pkt, len);
|
|
}
|
|
|
|
return (nio->send (nio->dptr, pkt, len));
|
|
}
|
|
|
|
/* Receive a packet through a NetIO descriptor */
|
|
ssize_t netio_recv (netio_desc_t * nio, void *pkt, size_t max_len)
|
|
{
|
|
ssize_t len;
|
|
|
|
if (!nio)
|
|
return (-1);
|
|
|
|
/* Receive the packet */
|
|
if ((len = nio->recv (nio->dptr, pkt, max_len)) <= 0)
|
|
return (-1);
|
|
|
|
if (nio->debug) {
|
|
printf ("NIO %s: receiving a packet of %ld bytes:\n", nio->name,
|
|
(long) len);
|
|
mem_dump (stdout, pkt, len);
|
|
}
|
|
|
|
return (len);
|
|
}
|
|
|
|
/* Get a NetIO FD */
|
|
int netio_get_fd (netio_desc_t * nio)
|
|
{
|
|
int fd = -1;
|
|
|
|
switch (nio->type) {
|
|
//case NETIO_TYPE_UNIX:
|
|
// fd = nio->u.nud.fd;
|
|
// break;
|
|
//case NETIO_TYPE_VDE:
|
|
// fd = nio->u.nvd.data_fd;
|
|
// break;
|
|
case NETIO_TYPE_TAP:
|
|
fd = nio->u.ntd.fd;
|
|
break;
|
|
//case NETIO_TYPE_TCP_CLI:
|
|
//case NETIO_TYPE_TCP_SER:
|
|
//case NETIO_TYPE_UDP:
|
|
// fd = nio->u.nid.fd;
|
|
// break;
|
|
//#ifdef LINUX_ETH
|
|
// case NETIO_TYPE_LINUX_ETH:
|
|
// fd = nio->u.nled.fd;
|
|
// break;
|
|
//#endif
|
|
}
|
|
|
|
return (fd);
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* =========================================================================
|
|
* UNIX sockets
|
|
* =========================================================================
|
|
*/
|
|
|
|
/* Create an UNIX socket */
|
|
static int netio_unix_create_socket (netio_unix_desc_t * nud)
|
|
{
|
|
struct sockaddr_un local_sock;
|
|
|
|
if ((nud->fd = socket (AF_UNIX, SOCK_DGRAM, 0)) == -1) {
|
|
perror ("netio_unix: socket");
|
|
return (-1);
|
|
}
|
|
|
|
memset (&local_sock, 0, sizeof (local_sock));
|
|
local_sock.sun_family = AF_UNIX;
|
|
strcpy (local_sock.sun_path, nud->local_filename);
|
|
|
|
if (bind (nud->fd, (struct sockaddr *) &local_sock,
|
|
sizeof (local_sock)) == -1) {
|
|
perror ("netio_unix: bind");
|
|
return (-1);
|
|
}
|
|
|
|
return (nud->fd);
|
|
}
|
|
|
|
/* Free a NetIO unix descriptor */
|
|
static void netio_unix_free (netio_unix_desc_t * nud)
|
|
{
|
|
if (nud->fd != -1)
|
|
close (nud->fd);
|
|
|
|
if (nud->local_filename) {
|
|
unlink (nud->local_filename);
|
|
free (nud->local_filename);
|
|
}
|
|
}
|
|
|
|
/* Allocate a new NetIO UNIX descriptor */
|
|
static int netio_unix_create (netio_unix_desc_t * nud, char *local,
|
|
char *remote)
|
|
{
|
|
memset (nud, 0, sizeof (*nud));
|
|
nud->fd = -1;
|
|
|
|
/* check lengths */
|
|
if ((strlen (local) >= sizeof (nud->remote_sock.sun_path))
|
|
|| (strlen (remote) >= sizeof (nud->remote_sock.sun_path)))
|
|
goto nomem_error;
|
|
|
|
if (!(nud->local_filename = strdup (local)))
|
|
goto nomem_error;
|
|
|
|
if (netio_unix_create_socket (nud) == -1)
|
|
return (-1);
|
|
|
|
/* prepare the remote info */
|
|
nud->remote_sock.sun_family = AF_UNIX;
|
|
strcpy (nud->remote_sock.sun_path, remote);
|
|
return (0);
|
|
|
|
nomem_error:
|
|
fprintf (stderr,
|
|
"netio_unix_create: " "invalid file size or insufficient memory\n");
|
|
return (-1);
|
|
}
|
|
|
|
/* Write a packet to an UNIX socket */
|
|
static ssize_t netio_unix_send (netio_unix_desc_t * nud, void *pkt,
|
|
size_t pkt_len)
|
|
{
|
|
return (sendto (nud->fd, pkt, pkt_len, 0,
|
|
(struct sockaddr *) &nud->remote_sock,
|
|
sizeof (&nud->remote_sock)));
|
|
}
|
|
|
|
/* Receive a packet from an UNIX socket */
|
|
static ssize_t netio_unix_recv (netio_unix_desc_t * nud, void *pkt,
|
|
size_t max_len)
|
|
{
|
|
return (recvfrom (nud->fd, pkt, max_len, 0, NULL, NULL));
|
|
}
|
|
|
|
/* Save the NIO configuration */
|
|
static void netio_unix_save_cfg (netio_desc_t * nio, FILE * fd)
|
|
{
|
|
netio_unix_desc_t *nud = nio->dptr;
|
|
fprintf (fd, "nio create_unix %s %s %s\n", nio->name, nud->local_filename,
|
|
nud->remote_sock.sun_path);
|
|
}
|
|
|
|
/* Create a new NetIO descriptor with UNIX method */
|
|
netio_desc_t *netio_desc_create_unix (char *nio_name, char *local,
|
|
char *remote)
|
|
{
|
|
netio_desc_t *nio;
|
|
|
|
if (!(nio = netio_create (nio_name)))
|
|
return NULL;
|
|
|
|
if (netio_unix_create (&nio->u.nud, local, remote) == -1) {
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
nio->type = NETIO_TYPE_UNIX;
|
|
nio->send = (void *) netio_unix_send;
|
|
nio->recv = (void *) netio_unix_recv;
|
|
nio->save_cfg = netio_unix_save_cfg;
|
|
nio->dptr = &nio->u.nud;
|
|
|
|
if (netio_record (nio) == -1) {
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
return nio;
|
|
}
|
|
|
|
/*
|
|
* =========================================================================
|
|
* VDE (Virtual Distributed Ethernet) interface
|
|
* =========================================================================
|
|
*/
|
|
|
|
/* Free a NetIO VDE descriptor */
|
|
static void netio_vde_free (netio_vde_desc_t * nvd)
|
|
{
|
|
if (nvd->data_fd != -1)
|
|
close (nvd->data_fd);
|
|
|
|
if (nvd->ctrl_fd != -1)
|
|
close (nvd->ctrl_fd);
|
|
|
|
if (nvd->local_filename) {
|
|
unlink (nvd->local_filename);
|
|
free (nvd->local_filename);
|
|
}
|
|
}
|
|
|
|
/* Create a new NetIO VDE descriptor */
|
|
static int netio_vde_create (netio_vde_desc_t * nvd, char *control,
|
|
char *local)
|
|
{
|
|
struct sockaddr_un ctrl_sock, tst;
|
|
struct vde_request_v3 req;
|
|
ssize_t len;
|
|
int res;
|
|
|
|
memset (nvd, 0, sizeof (*nvd));
|
|
nvd->ctrl_fd = nvd->data_fd = -1;
|
|
|
|
if ((strlen (control) >= sizeof (ctrl_sock.sun_path))
|
|
|| (strlen (local) >= sizeof (nvd->remote_sock.sun_path))) {
|
|
fprintf (stderr, "netio_vde_create: bad filenames specified\n");
|
|
return (-1);
|
|
}
|
|
|
|
/* Copy the local filename */
|
|
if (!(nvd->local_filename = strdup (local))) {
|
|
fprintf (stderr, "netio_vde_create: insufficient memory\n");
|
|
return (-1);
|
|
}
|
|
|
|
/* Connect to the VDE switch controller */
|
|
nvd->ctrl_fd = socket (AF_UNIX, SOCK_STREAM, 0);
|
|
if (nvd->ctrl_fd < 0) {
|
|
perror ("netio_vde_create: socket(control)");
|
|
return (-1);
|
|
}
|
|
|
|
memset (&ctrl_sock, 0, sizeof (ctrl_sock));
|
|
ctrl_sock.sun_family = AF_UNIX;
|
|
strcpy (ctrl_sock.sun_path, control);
|
|
|
|
res =
|
|
connect (nvd->ctrl_fd, (struct sockaddr *) &ctrl_sock,
|
|
sizeof (ctrl_sock));
|
|
|
|
if (res < 0) {
|
|
perror ("netio_vde_create: connect(control)");
|
|
return (-1);
|
|
}
|
|
|
|
tst.sun_family = AF_UNIX;
|
|
strcpy (tst.sun_path, local);
|
|
|
|
/* Create the data connection */
|
|
nvd->data_fd = socket (AF_UNIX, SOCK_DGRAM, 0);
|
|
if (nvd->data_fd < 0) {
|
|
perror ("netio_vde_create: socket(data)");
|
|
return (-1);
|
|
}
|
|
|
|
if (bind (nvd->data_fd, (struct sockaddr *) &tst, sizeof (tst)) < 0) {
|
|
perror ("netio_vde_create: bind(data)");
|
|
return (-1);
|
|
}
|
|
|
|
/* Now, process to registration */
|
|
memset (&req, 0, sizeof (req));
|
|
req.sock.sun_family = AF_UNIX;
|
|
strcpy (req.sock.sun_path, local);
|
|
req.magic = VDE_SWITCH_MAGIC;
|
|
req.version = VDE_SWITCH_VERSION;
|
|
req.type = VDE_REQ_NEW_CONTROL;
|
|
|
|
len = write (nvd->ctrl_fd, &req, sizeof (req));
|
|
if (len != sizeof (req)) {
|
|
perror ("netio_vde_create: write(req)");
|
|
return (-1);
|
|
}
|
|
|
|
/* Read the remote socket descriptor */
|
|
len = read (nvd->ctrl_fd, &nvd->remote_sock, sizeof (nvd->remote_sock));
|
|
if (len != sizeof (nvd->remote_sock)) {
|
|
perror ("netio_vde_create: read(req)");
|
|
return (-1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Write a packet to a VDE data socket */
|
|
static ssize_t netio_vde_send (netio_vde_desc_t * nvd, void *pkt,
|
|
size_t pkt_len)
|
|
{
|
|
return (sendto (nvd->data_fd, pkt, pkt_len, 0,
|
|
(struct sockaddr *) &nvd->remote_sock,
|
|
sizeof (nvd->remote_sock)));
|
|
}
|
|
|
|
/* Receive a packet from a VDE socket */
|
|
static ssize_t netio_vde_recv (netio_vde_desc_t * nvd, void *pkt,
|
|
size_t max_len)
|
|
{
|
|
return (recvfrom (nvd->data_fd, pkt, max_len, 0, NULL, NULL));
|
|
}
|
|
|
|
/* Save the NIO configuration */
|
|
static void netio_vde_save_cfg (netio_desc_t * nio, FILE * fd)
|
|
{
|
|
netio_vde_desc_t *nvd = nio->dptr;
|
|
fprintf (fd, "nio create_vde %s %s %s\n", nio->name,
|
|
nvd->remote_sock.sun_path, nvd->local_filename);
|
|
}
|
|
|
|
/* Create a new NetIO descriptor with VDE method */
|
|
netio_desc_t *netio_desc_create_vde (char *nio_name, char *control,
|
|
char *local)
|
|
{
|
|
netio_vde_desc_t *nvd;
|
|
netio_desc_t *nio;
|
|
|
|
if (!(nio = netio_create (nio_name)))
|
|
return NULL;
|
|
|
|
nvd = &nio->u.nvd;
|
|
|
|
if (netio_vde_create (nvd, control, local) == -1) {
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
nio->type = NETIO_TYPE_VDE;
|
|
nio->send = (void *) netio_vde_send;
|
|
nio->recv = (void *) netio_vde_recv;
|
|
nio->save_cfg = netio_vde_save_cfg;
|
|
nio->dptr = &nio->u.nvd;
|
|
|
|
if (netio_record (nio) == -1) {
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
return nio;
|
|
}
|
|
#endif
|
|
/*
|
|
* =========================================================================
|
|
* TAP devices
|
|
* =========================================================================
|
|
*/
|
|
|
|
/* Free a NetIO TAP descriptor */
|
|
static void netio_tap_free (netio_tap_desc_t * ntd)
|
|
{
|
|
if (ntd->fd != -1)
|
|
close (ntd->fd);
|
|
}
|
|
|
|
/* Open a TAP device */
|
|
static int netio_tap_open (char *tap_devname)
|
|
{
|
|
#ifdef __linux__
|
|
struct ifreq ifr;
|
|
int fd, err;
|
|
|
|
if ((fd = open ("/dev/net/tun", O_RDWR)) < 0)
|
|
return (-1);
|
|
|
|
memset (&ifr, 0, sizeof (ifr));
|
|
|
|
/* Flags: IFF_TUN - TUN device (no Ethernet headers)
|
|
* IFF_TAP - TAP device
|
|
*
|
|
* IFF_NO_PI - Do not provide packet information
|
|
*/
|
|
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
|
if (*tap_devname)
|
|
strncpy (ifr.ifr_name, tap_devname, IFNAMSIZ);
|
|
|
|
if ((err = ioctl (fd, TUNSETIFF, (void *) &ifr)) < 0) {
|
|
close (fd);
|
|
return err;
|
|
}
|
|
|
|
strcpy (tap_devname, ifr.ifr_name);
|
|
return (fd);
|
|
#else
|
|
int i, fd = -1;
|
|
|
|
if (*tap_devname) {
|
|
fd = open (tap_devname, O_RDWR);
|
|
} else {
|
|
for (i = 0; i < 16; i++) {
|
|
snprintf (tap_devname, NETIO_DEV_MAXLEN, "/dev/tap%d", i);
|
|
|
|
if ((fd = open (tap_devname, O_RDWR)) >= 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (fd);
|
|
#endif
|
|
}
|
|
|
|
/* Allocate a new NetIO TAP descriptor */
|
|
static int netio_tap_create (netio_tap_desc_t * ntd, char *tap_name)
|
|
{
|
|
if (strlen (tap_name) >= NETIO_DEV_MAXLEN) {
|
|
fprintf (stderr,
|
|
"netio_tap_create: bad TAP device string specified.\n");
|
|
return (-1);
|
|
}
|
|
|
|
memset (ntd, 0, sizeof (*ntd));
|
|
strcpy (ntd->filename, tap_name);
|
|
ntd->fd = netio_tap_open (ntd->filename);
|
|
|
|
if (ntd->fd == -1) {
|
|
fprintf (stderr,
|
|
"netio_tap_create: unable to open TAP device %s (%s)\n", tap_name,
|
|
strerror (errno));
|
|
return (-1);
|
|
}
|
|
/*SET NO BLOCKING */
|
|
if (fcntl (ntd->fd, F_SETFL, O_NONBLOCK) == -1)
|
|
printf ("Set file descriptor to non-blocking mode failed\n");
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Write a packet to a TAP device */
|
|
static ssize_t netio_tap_send (netio_tap_desc_t * ntd, void *pkt,
|
|
size_t pkt_len)
|
|
{
|
|
return (write (ntd->fd, pkt, pkt_len));
|
|
}
|
|
|
|
/* Receive a packet through a TAP device */
|
|
static ssize_t netio_tap_recv (netio_tap_desc_t * ntd, void *pkt,
|
|
size_t max_len)
|
|
{
|
|
return (read (ntd->fd, pkt, max_len));
|
|
}
|
|
|
|
/* Create a new NetIO descriptor with TAP method */
|
|
netio_desc_t *netio_desc_create_tap (char *nio_name, char *tap_name)
|
|
{
|
|
netio_tap_desc_t *ntd;
|
|
netio_desc_t *nio;
|
|
|
|
if (!(nio = netio_create (nio_name)))
|
|
return NULL;
|
|
|
|
ntd = &nio->u.ntd;
|
|
|
|
if (netio_tap_create (ntd, tap_name) == -1) {
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
nio->type = NETIO_TYPE_TAP;
|
|
nio->send = (void *) netio_tap_send;
|
|
nio->recv = (void *) netio_tap_recv;
|
|
nio->dptr = &nio->u.ntd;
|
|
|
|
return nio;
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* =========================================================================
|
|
* TCP sockets
|
|
* =========================================================================
|
|
*/
|
|
|
|
/* Free a NetIO TCP descriptor */
|
|
static void netio_tcp_free (netio_inet_desc_t * nid)
|
|
{
|
|
if (nid->fd != -1)
|
|
close (nid->fd);
|
|
}
|
|
|
|
/*
|
|
* very simple protocol to send packets over tcp
|
|
* 32 bits in network format - size of packet, then packet itself and so on.
|
|
*/
|
|
static ssize_t netio_tcp_send (netio_inet_desc_t * nid, void *pkt,
|
|
size_t pkt_len)
|
|
{
|
|
u_long l = htonl (pkt_len);
|
|
|
|
if (write (nid->fd, &l, sizeof (l)) == -1)
|
|
return (-1);
|
|
|
|
return (write (nid->fd, pkt, pkt_len));
|
|
}
|
|
|
|
static ssize_t netio_tcp_recv (netio_inet_desc_t * nid, void *pkt,
|
|
size_t max_len)
|
|
{
|
|
u_long l;
|
|
|
|
if (read (nid->fd, &l, sizeof (l)) != sizeof (l))
|
|
return (-1);
|
|
|
|
if (ntohl (l) > max_len)
|
|
return (-1);
|
|
|
|
return (read (nid->fd, pkt, ntohl (l)));
|
|
}
|
|
|
|
static int netio_tcp_cli_create (netio_inet_desc_t * nid, char *host,
|
|
char *port)
|
|
{
|
|
struct sockaddr_in serv;
|
|
struct servent *sp;
|
|
struct hostent *hp;
|
|
|
|
if ((nid->fd = socket (PF_INET, SOCK_STREAM, 0)) < 0) {
|
|
perror ("netio_tcp_cli_create: socket");
|
|
return (-1);
|
|
}
|
|
|
|
memset (&serv, 0, sizeof (serv));
|
|
serv.sin_family = AF_INET;
|
|
|
|
if (atoi (port) == 0) {
|
|
if (!(sp = getservbyname (port, "tcp"))) {
|
|
fprintf (stderr,
|
|
"netio_tcp_cli_create: port %s is neither "
|
|
"number not service %s\n", port, strerror (errno));
|
|
close (nid->fd);
|
|
return (-1);
|
|
}
|
|
serv.sin_port = sp->s_port;
|
|
} else
|
|
serv.sin_port = htons (atoi (port));
|
|
|
|
if (inet_addr (host) == INADDR_NONE) {
|
|
if (!(hp = gethostbyname (host))) {
|
|
fprintf (stderr, "netio_tcp_cli_create: no host %s\n", host);
|
|
close (nid->fd);
|
|
return (-1);
|
|
}
|
|
serv.sin_addr.s_addr = *hp->h_addr;
|
|
} else
|
|
serv.sin_addr.s_addr = inet_addr (host);
|
|
|
|
if (connect (nid->fd, (struct sockaddr *) &serv, sizeof (serv)) < 0) {
|
|
fprintf (stderr, "netio_tcp_cli_create: connect to %s:%s failed %s\n",
|
|
host, port, strerror (errno));
|
|
close (nid->fd);
|
|
return (-1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/* Create a new NetIO descriptor with TCP_CLI method */
|
|
netio_desc_t *netio_desc_create_tcp_cli (char *nio_name, char *host,
|
|
char *port)
|
|
{
|
|
netio_desc_t *nio;
|
|
|
|
if (!(nio = netio_create (nio_name)))
|
|
return NULL;
|
|
|
|
if (netio_tcp_cli_create (&nio->u.nid, host, port) < 0) {
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
nio->type = NETIO_TYPE_TCP_CLI;
|
|
nio->send = (void *) netio_tcp_send;
|
|
nio->recv = (void *) netio_tcp_recv;
|
|
nio->dptr = &nio->u.nid;
|
|
|
|
if (netio_record (nio) == -1) {
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
return nio;
|
|
}
|
|
|
|
static int netio_tcp_ser_create (netio_inet_desc_t * nid, char *port)
|
|
{
|
|
struct sockaddr_in serv;
|
|
struct servent *sp;
|
|
int sock_fd;
|
|
|
|
if ((sock_fd = socket (PF_INET, SOCK_STREAM, 0)) < 0) {
|
|
perror ("netio_tcp_cli_create: socket\n");
|
|
return (-1);
|
|
}
|
|
|
|
memset (&serv, 0, sizeof (serv));
|
|
serv.sin_family = AF_INET;
|
|
serv.sin_addr.s_addr = htonl (INADDR_ANY);
|
|
|
|
if (atoi (port) == 0) {
|
|
if (!(sp = getservbyname (port, "tcp"))) {
|
|
fprintf (stderr,
|
|
"netio_tcp_ser_create: port %s is neither "
|
|
"number not service %s\n", port, strerror (errno));
|
|
close (sock_fd);
|
|
return (-1);
|
|
}
|
|
serv.sin_port = sp->s_port;
|
|
} else
|
|
serv.sin_port = htons (atoi (port));
|
|
|
|
if (bind (sock_fd, (struct sockaddr *) &serv, sizeof (serv)) < 0) {
|
|
fprintf (stderr, "netio_tcp_ser_create: bind %s failed %s\n", port,
|
|
strerror (errno));
|
|
close (sock_fd);
|
|
return (-1);
|
|
}
|
|
|
|
if (listen (sock_fd, 1) < 0) {
|
|
fprintf (stderr, "netio_tcp_ser_create: listen %s failed %s\n", port,
|
|
strerror (errno));
|
|
close (sock_fd);
|
|
return (-1);
|
|
}
|
|
|
|
fprintf (stderr, "Waiting connection on port %s...\n", port);
|
|
|
|
if ((nid->fd = accept (sock_fd, NULL, NULL)) < 0) {
|
|
fprintf (stderr, "netio_tcp_ser_create: accept %s failed %s\n", port,
|
|
strerror (errno));
|
|
close (sock_fd);
|
|
return (-1);
|
|
}
|
|
|
|
fprintf (stderr, "Connected\n");
|
|
|
|
close (sock_fd);
|
|
return (0);
|
|
}
|
|
|
|
/* Create a new NetIO descriptor with TCP_SER method */
|
|
netio_desc_t *netio_desc_create_tcp_ser (char *nio_name, char *port)
|
|
{
|
|
netio_desc_t *nio;
|
|
|
|
if (!(nio = netio_create (nio_name)))
|
|
return NULL;
|
|
|
|
if (netio_tcp_ser_create (&nio->u.nid, port) == -1) {
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
nio->type = NETIO_TYPE_TCP_SER;
|
|
nio->send = (void *) netio_tcp_send;
|
|
nio->recv = (void *) netio_tcp_recv;
|
|
nio->dptr = &nio->u.nid;
|
|
|
|
if (netio_record (nio) == -1) {
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
return nio;
|
|
}
|
|
|
|
/*
|
|
* =========================================================================
|
|
* UDP sockets
|
|
* =========================================================================
|
|
*/
|
|
|
|
/* Free a NetIO UDP descriptor */
|
|
static void netio_udp_free (netio_inet_desc_t * nid)
|
|
{
|
|
if (nid->remote_host) {
|
|
free (nid->remote_host);
|
|
nid->remote_host = NULL;
|
|
}
|
|
|
|
if (nid->fd != -1)
|
|
close (nid->fd);
|
|
}
|
|
|
|
/* Write a packet to an UDP socket */
|
|
static ssize_t netio_udp_send (netio_inet_desc_t * nid, void *pkt,
|
|
size_t pkt_len)
|
|
{
|
|
return (send (nid->fd, pkt, pkt_len, 0));
|
|
}
|
|
|
|
/* Receive a packet from an UDP socket */
|
|
static ssize_t netio_udp_recv (netio_inet_desc_t * nid, void *pkt,
|
|
size_t max_len)
|
|
{
|
|
return (recvfrom (nid->fd, pkt, max_len, 0, NULL, NULL));
|
|
}
|
|
|
|
/* Save the NIO configuration */
|
|
static void netio_udp_save_cfg (netio_desc_t * nio, FILE * fd)
|
|
{
|
|
netio_inet_desc_t *nid = nio->dptr;
|
|
fprintf (fd, "nio create_udp %s %d %s %d\n", nio->name, nid->local_port,
|
|
nid->remote_host, nid->remote_port);
|
|
}
|
|
|
|
/* Create a new NetIO descriptor with UDP method */
|
|
netio_desc_t *netio_desc_create_udp (char *nio_name, int local_port,
|
|
char *remote_host, int remote_port)
|
|
{
|
|
netio_inet_desc_t *nid;
|
|
netio_desc_t *nio;
|
|
|
|
if (!(nio = netio_create (nio_name)))
|
|
return NULL;
|
|
|
|
nid = &nio->u.nid;
|
|
nid->local_port = local_port;
|
|
nid->remote_port = remote_port;
|
|
|
|
if (!(nid->remote_host = strdup (remote_host))) {
|
|
fprintf (stderr, "netio_desc_create_udp: insufficient memory\n");
|
|
goto error;
|
|
}
|
|
|
|
if ((nid->fd = udp_connect (local_port, remote_host, remote_port)) < 0) {
|
|
fprintf (stderr,
|
|
"netio_desc_create_udp: unable to connect to %s:%d\n",
|
|
remote_host, remote_port);
|
|
goto error;
|
|
}
|
|
|
|
nio->type = NETIO_TYPE_UDP;
|
|
nio->send = (void *) netio_udp_send;
|
|
nio->recv = (void *) netio_udp_recv;
|
|
nio->save_cfg = netio_udp_save_cfg;
|
|
nio->dptr = &nio->u.nid;
|
|
|
|
if (netio_record (nio) == -1)
|
|
goto error;
|
|
|
|
return nio;
|
|
|
|
error:
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* =========================================================================
|
|
* Linux RAW Ethernet driver
|
|
* =========================================================================
|
|
*/
|
|
#ifdef LINUX_ETH
|
|
/* Free a NetIO raw ethernet descriptor */
|
|
static void netio_lnxeth_free (netio_lnxeth_desc_t * nled)
|
|
{
|
|
if (nled->fd != -1)
|
|
close (nled->fd);
|
|
}
|
|
|
|
/* Write a packet to a raw Ethernet socket */
|
|
static ssize_t netio_lnxeth_send (netio_lnxeth_desc_t * nled, void *pkt,
|
|
size_t pkt_len)
|
|
{
|
|
return (lnx_eth_send (nled->fd, nled->dev_id, pkt, pkt_len));
|
|
}
|
|
|
|
/* Receive a packet from an raw Ethernet socket */
|
|
static ssize_t netio_lnxeth_recv (netio_lnxeth_desc_t * nled, void *pkt,
|
|
size_t max_len)
|
|
{
|
|
return (lnx_eth_recv (nled->fd, pkt, max_len));
|
|
}
|
|
|
|
/* Save the NIO configuration */
|
|
static void netio_lnxeth_save_cfg (netio_desc_t * nio, FILE * fd)
|
|
{
|
|
netio_lnxeth_desc_t *nled = nio->dptr;
|
|
fprintf (fd, "nio create_linux_eth %s %s\n", nio->name, nled->dev_name);
|
|
}
|
|
|
|
/* Create a new NetIO descriptor with raw Ethernet method */
|
|
netio_desc_t *netio_desc_create_lnxeth (char *nio_name, char *dev_name)
|
|
{
|
|
netio_lnxeth_desc_t *nled;
|
|
netio_desc_t *nio;
|
|
|
|
if (!(nio = netio_create (nio_name)))
|
|
return NULL;
|
|
|
|
nled = &nio->u.nled;
|
|
|
|
if (strlen (dev_name) >= NETIO_DEV_MAXLEN) {
|
|
fprintf (stderr,
|
|
"netio_desc_create_lnxeth: bad Ethernet device string "
|
|
"specified.\n");
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
strcpy (nled->dev_name, dev_name);
|
|
|
|
nled->fd = lnx_eth_init_socket (dev_name);
|
|
nled->dev_id = lnx_eth_get_dev_index (dev_name);
|
|
|
|
if (nled->fd < 0) {
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
nio->type = NETIO_TYPE_LINUX_ETH;
|
|
nio->send = (void *) netio_lnxeth_send;
|
|
nio->recv = (void *) netio_lnxeth_recv;
|
|
nio->save_cfg = netio_lnxeth_save_cfg;
|
|
nio->dptr = &nio->u.nled;
|
|
|
|
if (netio_record (nio) == -1) {
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
return nio;
|
|
}
|
|
#endif /* LINUX_ETH */
|
|
|
|
/*
|
|
* =========================================================================
|
|
* Generic RAW Ethernet driver
|
|
* =========================================================================
|
|
*/
|
|
#ifdef GEN_ETH
|
|
/* Free a NetIO raw ethernet descriptor */
|
|
static void netio_geneth_free (netio_geneth_desc_t * nged)
|
|
{
|
|
gen_eth_close (nged->pcap_dev);
|
|
}
|
|
|
|
/* Write a packet to an Ethernet device */
|
|
static ssize_t netio_geneth_send (netio_geneth_desc_t * nged, void *pkt,
|
|
size_t pkt_len)
|
|
{
|
|
return (gen_eth_send (nged->pcap_dev, pkt, pkt_len));
|
|
}
|
|
|
|
/* Receive a packet from an Ethernet device */
|
|
static ssize_t netio_geneth_recv (netio_geneth_desc_t * nged, void *pkt,
|
|
size_t max_len)
|
|
{
|
|
return (gen_eth_recv (nged->pcap_dev, pkt, max_len));
|
|
}
|
|
|
|
/* Save the NIO configuration */
|
|
static void netio_geneth_save_cfg (netio_desc_t * nio, FILE * fd)
|
|
{
|
|
netio_geneth_desc_t *nged = nio->dptr;
|
|
fprintf (fd, "nio create_gen_eth %s %s\n", nio->name, nged->dev_name);
|
|
}
|
|
|
|
/* Create a new NetIO descriptor with generic raw Ethernet method */
|
|
netio_desc_t *netio_desc_create_geneth (char *nio_name, char *dev_name)
|
|
{
|
|
netio_geneth_desc_t *nged;
|
|
netio_desc_t *nio;
|
|
|
|
if (!(nio = netio_create (nio_name)))
|
|
return NULL;
|
|
|
|
nged = &nio->u.nged;
|
|
|
|
if (strlen (dev_name) >= NETIO_DEV_MAXLEN) {
|
|
fprintf (stderr,
|
|
"netio_desc_create_geneth: bad Ethernet device string "
|
|
"specified.\n");
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
strcpy (nged->dev_name, dev_name);
|
|
|
|
if (!(nged->pcap_dev = gen_eth_init (dev_name))) {
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
nio->type = NETIO_TYPE_GEN_ETH;
|
|
nio->send = (void *) netio_geneth_send;
|
|
nio->recv = (void *) netio_geneth_recv;
|
|
nio->save_cfg = netio_geneth_save_cfg;
|
|
nio->dptr = &nio->u.nged;
|
|
|
|
if (netio_record (nio) == -1) {
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
return nio;
|
|
}
|
|
#endif /* GEN_ETH */
|
|
|
|
/*
|
|
* =========================================================================
|
|
* FIFO Driver (intra-hypervisor communications)
|
|
* =========================================================================
|
|
*/
|
|
|
|
/* Extract the first packet of the FIFO */
|
|
static netio_fifo_pkt_t *netio_fifo_extract_pkt (netio_fifo_desc_t * nfd)
|
|
{
|
|
netio_fifo_pkt_t *p;
|
|
|
|
if (!(p = nfd->head))
|
|
return NULL;
|
|
|
|
nfd->pkt_count--;
|
|
nfd->head = p->next;
|
|
|
|
if (!nfd->head)
|
|
nfd->last = NULL;
|
|
|
|
return p;
|
|
}
|
|
|
|
/* Insert a packet into the FIFO (in tail) */
|
|
static void netio_fifo_insert_pkt (netio_fifo_desc_t * nfd,
|
|
netio_fifo_pkt_t * p)
|
|
{
|
|
pthread_mutex_lock (&nfd->lock);
|
|
|
|
nfd->pkt_count++;
|
|
p->next = NULL;
|
|
|
|
if (nfd->last) {
|
|
nfd->last->next = p;
|
|
} else {
|
|
nfd->head = p;
|
|
}
|
|
|
|
nfd->last = p;
|
|
pthread_mutex_unlock (&nfd->lock);
|
|
}
|
|
|
|
/* Free the packet list */
|
|
static void netio_fifo_free_pkt_list (netio_fifo_desc_t * nfd)
|
|
{
|
|
netio_fifo_pkt_t *p, *next;
|
|
|
|
for (p = nfd->head; p; p = next) {
|
|
next = p->next;
|
|
free (p);
|
|
}
|
|
|
|
nfd->head = nfd->last = NULL;
|
|
nfd->pkt_count = 0;
|
|
}
|
|
|
|
/* Establish a cross-connect between two FIFO NetIO */
|
|
int netio_fifo_crossconnect (netio_desc_t * a, netio_desc_t * b)
|
|
{
|
|
netio_fifo_desc_t *pa, *pb;
|
|
|
|
if ((a->type != NETIO_TYPE_FIFO) || (b->type != NETIO_TYPE_FIFO))
|
|
return (-1);
|
|
|
|
pa = &a->u.nfd;
|
|
pb = &b->u.nfd;
|
|
|
|
/* A => B */
|
|
pthread_mutex_lock (&pa->endpoint_lock);
|
|
pthread_mutex_lock (&pa->lock);
|
|
pa->endpoint = pb;
|
|
netio_fifo_free_pkt_list (pa);
|
|
pthread_mutex_unlock (&pa->lock);
|
|
pthread_mutex_unlock (&pa->endpoint_lock);
|
|
|
|
/* B => A */
|
|
pthread_mutex_lock (&pb->endpoint_lock);
|
|
pthread_mutex_lock (&pb->lock);
|
|
pb->endpoint = pa;
|
|
netio_fifo_free_pkt_list (pb);
|
|
pthread_mutex_unlock (&pb->lock);
|
|
pthread_mutex_unlock (&pb->endpoint_lock);
|
|
return (0);
|
|
}
|
|
|
|
/* Unbind an endpoint */
|
|
static void netio_fifo_unbind_endpoint (netio_fifo_desc_t * nfd)
|
|
{
|
|
pthread_mutex_lock (&nfd->endpoint_lock);
|
|
nfd->endpoint = NULL;
|
|
pthread_mutex_unlock (&nfd->endpoint_lock);
|
|
}
|
|
|
|
/* Free a NetIO FIFO descriptor */
|
|
static void netio_fifo_free (netio_fifo_desc_t * nfd)
|
|
{
|
|
if (nfd->endpoint)
|
|
netio_fifo_unbind_endpoint (nfd->endpoint);
|
|
|
|
netio_fifo_free_pkt_list (nfd);
|
|
pthread_mutex_destroy (&nfd->lock);
|
|
pthread_cond_destroy (&nfd->cond);
|
|
}
|
|
|
|
/* Send a packet (to the endpoint FIFO) */
|
|
static ssize_t netio_fifo_send (netio_fifo_desc_t * nfd, void *pkt,
|
|
size_t pkt_len)
|
|
{
|
|
netio_fifo_pkt_t *p;
|
|
size_t len;
|
|
|
|
pthread_mutex_lock (&nfd->endpoint_lock);
|
|
|
|
/* The cross-connect must have been established before */
|
|
if (!nfd->endpoint)
|
|
goto error;
|
|
|
|
/* Allocate a a new packet and insert it into the endpoint FIFO */
|
|
len = sizeof (netio_fifo_pkt_t) + pkt_len;
|
|
if (!(p = malloc (len)))
|
|
goto error;
|
|
|
|
memcpy (p->pkt, pkt, pkt_len);
|
|
p->pkt_len = pkt_len;
|
|
netio_fifo_insert_pkt (nfd->endpoint, p);
|
|
pthread_cond_signal (&nfd->endpoint->cond);
|
|
pthread_mutex_unlock (&nfd->endpoint_lock);
|
|
return (pkt_len);
|
|
|
|
error:
|
|
pthread_mutex_unlock (&nfd->endpoint_lock);
|
|
return (-1);
|
|
}
|
|
|
|
/* Read a packet from the local FIFO queue */
|
|
static ssize_t netio_fifo_recv (netio_fifo_desc_t * nfd, void *pkt,
|
|
size_t max_len)
|
|
{
|
|
struct timespec ts;
|
|
m_tmcnt_t expire;
|
|
netio_fifo_pkt_t *p;
|
|
size_t len = -1;
|
|
|
|
/* Wait for the endpoint to signal a new arriving packet */
|
|
expire = m_gettime_usec () + 50000;
|
|
ts.tv_sec = expire / 1000000;
|
|
ts.tv_nsec = (expire % 1000000) * 1000;
|
|
|
|
pthread_mutex_lock (&nfd->lock);
|
|
pthread_cond_timedwait (&nfd->cond, &nfd->lock, &ts);
|
|
|
|
/* Extract a packet from the list */
|
|
p = netio_fifo_extract_pkt (nfd);
|
|
pthread_mutex_unlock (&nfd->lock);
|
|
|
|
if (p) {
|
|
len = m_min (p->pkt_len, max_len);
|
|
memcpy (pkt, p->pkt, len);
|
|
free (p);
|
|
}
|
|
|
|
return (len);
|
|
}
|
|
|
|
/* Create a new NetIO descriptor with FIFO method */
|
|
netio_desc_t *netio_desc_create_fifo (char *nio_name)
|
|
{
|
|
netio_fifo_desc_t *nfd;
|
|
netio_desc_t *nio;
|
|
|
|
if (!(nio = netio_create (nio_name)))
|
|
return NULL;
|
|
|
|
nfd = &nio->u.nfd;
|
|
pthread_mutex_init (&nfd->lock, NULL);
|
|
pthread_mutex_init (&nfd->endpoint_lock, NULL);
|
|
pthread_cond_init (&nfd->cond, NULL);
|
|
|
|
nio->type = NETIO_TYPE_FIFO;
|
|
nio->send = (void *) netio_fifo_send;
|
|
nio->recv = (void *) netio_fifo_recv;
|
|
nio->dptr = nfd;
|
|
|
|
if (netio_record (nio) == -1) {
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
return nio;
|
|
}
|
|
|
|
/*
|
|
* =========================================================================
|
|
* NULL Driver (does nothing, used for debugging)
|
|
* =========================================================================
|
|
*/
|
|
static ssize_t netio_null_send (void *null_ptr, void *pkt, size_t pkt_len)
|
|
{
|
|
return (pkt_len);
|
|
}
|
|
|
|
static ssize_t netio_null_recv (void *null_ptr, void *pkt, size_t max_len)
|
|
{
|
|
usleep (200000);
|
|
return (-1);
|
|
}
|
|
|
|
static void netio_null_save_cfg (netio_desc_t * nio, FILE * fd)
|
|
{
|
|
fprintf (fd, "nio create_null %s\n", nio->name);
|
|
}
|
|
|
|
/* Create a new NetIO descriptor with NULL method */
|
|
netio_desc_t *netio_desc_create_null (char *nio_name)
|
|
{
|
|
netio_desc_t *nio;
|
|
|
|
if (!(nio = netio_create (nio_name)))
|
|
return NULL;
|
|
|
|
nio->type = NETIO_TYPE_NULL;
|
|
nio->send = (void *) netio_null_send;
|
|
nio->recv = (void *) netio_null_recv;
|
|
nio->save_cfg = netio_null_save_cfg;
|
|
nio->dptr = NULL;
|
|
|
|
if (netio_record (nio) == -1) {
|
|
netio_free (nio, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
return nio;
|
|
}
|
|
#endif
|
|
|
|
/* Free a NetIO descriptor */
|
|
static int netio_free (void *data, void *arg)
|
|
{
|
|
netio_desc_t *nio = data;
|
|
|
|
if (nio) {
|
|
|
|
switch (nio->type) {
|
|
case NETIO_TYPE_TAP:
|
|
netio_tap_free (&nio->u.ntd);
|
|
break;
|
|
case NETIO_TYPE_NULL:
|
|
break;
|
|
default:
|
|
fprintf (stderr, "NETIO: unknown descriptor type %u\n",
|
|
nio->type);
|
|
}
|
|
free (nio->name);
|
|
free (nio);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* =========================================================================
|
|
* RX Listeners
|
|
* =========================================================================
|
|
*/
|
|
|
|
/* Find a RX listener */
|
|
static inline struct netio_rx_listener *netio_rxl_find (netio_desc_t * nio)
|
|
{
|
|
struct netio_rx_listener *rxl;
|
|
|
|
for (rxl = netio_rxl_list; rxl; rxl = rxl->next)
|
|
if (rxl->nio == nio)
|
|
return rxl;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Remove a NIO from the listener list */
|
|
static int netio_rxl_remove_internal (netio_desc_t * nio)
|
|
{
|
|
struct netio_rx_listener *rxl;
|
|
int res = -1;
|
|
|
|
if ((rxl = netio_rxl_find (nio))) {
|
|
/* we suppress this NIO only when the ref count hits 0 */
|
|
rxl->ref_count--;
|
|
|
|
if (!rxl->ref_count) {
|
|
/* remove this listener from the double linked list */
|
|
if (rxl->next)
|
|
rxl->next->prev = rxl->prev;
|
|
|
|
if (rxl->prev)
|
|
rxl->prev->next = rxl->next;
|
|
else
|
|
netio_rxl_list = rxl->next;
|
|
|
|
/* if this is non-FD NIO, wait for thread to terminate */
|
|
if (netio_get_fd (rxl->nio) == -1) {
|
|
rxl->running = FALSE;
|
|
pthread_join (rxl->spec_thread, NULL);
|
|
}
|
|
|
|
free (rxl);
|
|
}
|
|
|
|
res = 0;
|
|
}
|
|
|
|
return (res);
|
|
}
|
|
|
|
/* Add a RXL listener to the listener list */
|
|
static void netio_rxl_add_internal (struct netio_rx_listener *rxl)
|
|
{
|
|
struct netio_rx_listener *tmp;
|
|
|
|
if ((tmp = netio_rxl_find (rxl->nio))) {
|
|
tmp->ref_count++;
|
|
free (rxl);
|
|
} else {
|
|
rxl->prev = NULL;
|
|
rxl->next = netio_rxl_list;
|
|
if (rxl->next)
|
|
rxl->next->prev = rxl;
|
|
netio_rxl_list = rxl;
|
|
}
|
|
}
|
|
|
|
/* RX Listener dedicated thread (for non-FD NIO) */
|
|
static void *netio_rxl_spec_thread (void *arg)
|
|
{
|
|
struct netio_rx_listener *rxl = arg;
|
|
netio_desc_t *nio = rxl->nio;
|
|
ssize_t pkt_len;
|
|
while (rxl->running) {
|
|
|
|
pkt_len = netio_recv (nio, nio->rx_pkt, sizeof (nio->rx_pkt));
|
|
if (pkt_len > 0) {
|
|
rxl->rx_handler (nio, nio->rx_pkt, pkt_len, rxl->arg1, rxl->arg2);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* RX Listener General Thread */
|
|
void *netio_rxl_gen_thread (void *arg)
|
|
{
|
|
struct netio_rx_listener *rxl;
|
|
ssize_t pkt_len;
|
|
netio_desc_t *nio;
|
|
struct timeval tv;
|
|
int fd, fd_max, res;
|
|
fd_set rfds;
|
|
|
|
for (;;) {
|
|
NETIO_RXL_LOCK ();
|
|
|
|
NETIO_RXQ_LOCK ();
|
|
/* Add the new waiting NIO to the active list */
|
|
while (netio_rxl_add_list != NULL) {
|
|
rxl = netio_rxl_add_list;
|
|
netio_rxl_add_list = netio_rxl_add_list->next;
|
|
netio_rxl_add_internal (rxl);
|
|
}
|
|
|
|
/* Delete the NIO present in the remove list */
|
|
while (netio_rxl_remove_list != NULL) {
|
|
nio = netio_rxl_remove_list;
|
|
netio_rxl_remove_list = netio_rxl_remove_list->rxl_next;
|
|
netio_rxl_remove_internal (nio);
|
|
}
|
|
|
|
pthread_cond_broadcast (&netio_rxl_cond);
|
|
NETIO_RXQ_UNLOCK ();
|
|
|
|
/* Build the FD set */
|
|
FD_ZERO (&rfds);
|
|
fd_max = -1;
|
|
for (rxl = netio_rxl_list; rxl; rxl = rxl->next) {
|
|
if ((fd = netio_get_fd (rxl->nio)) == -1)
|
|
continue;
|
|
|
|
if (fd > fd_max)
|
|
fd_max = fd;
|
|
FD_SET (fd, &rfds);
|
|
}
|
|
NETIO_RXL_UNLOCK ();
|
|
|
|
/* Wait for incoming packets */
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 2 * 1000; /* 2 ms */
|
|
res = select (fd_max + 1, &rfds, NULL, NULL, &tv);
|
|
|
|
if (res == -1) {
|
|
if (errno != EINTR)
|
|
perror ("netio_rxl_thread: select");
|
|
continue;
|
|
}
|
|
|
|
/* Examine active FDs and call user handlers */
|
|
NETIO_RXL_LOCK ();
|
|
|
|
for (rxl = netio_rxl_list; rxl; rxl = rxl->next) {
|
|
nio = rxl->nio;
|
|
|
|
if ((fd = netio_get_fd (nio)) == -1)
|
|
continue;
|
|
|
|
if (FD_ISSET (fd, &rfds)) {
|
|
{
|
|
pkt_len =
|
|
netio_recv (nio, nio->rx_pkt, sizeof (nio->rx_pkt));
|
|
|
|
if (pkt_len > 0) {
|
|
rxl->rx_handler (nio, nio->rx_pkt, pkt_len, rxl->arg1,
|
|
rxl->arg2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
NETIO_RXL_UNLOCK ();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Add a RX listener in the listener list */
|
|
int netio_rxl_add (netio_desc_t * nio, netio_rx_handler_t rx_handler,
|
|
void *arg1, void *arg2)
|
|
{
|
|
struct netio_rx_listener *rxl;
|
|
|
|
NETIO_RXQ_LOCK ();
|
|
|
|
if (!(rxl = malloc (sizeof (*rxl)))) {
|
|
NETIO_RXQ_UNLOCK ();
|
|
fprintf (stderr, "netio_rxl_add: unable to create structure.\n");
|
|
return (-1);
|
|
}
|
|
|
|
memset (rxl, 0, sizeof (*rxl));
|
|
rxl->nio = nio;
|
|
rxl->ref_count = 1;
|
|
rxl->rx_handler = rx_handler;
|
|
rxl->arg1 = arg1;
|
|
rxl->arg2 = arg2;
|
|
rxl->running = TRUE;
|
|
|
|
if ((netio_get_fd (rxl->nio) == -1) &&
|
|
pthread_create (&rxl->spec_thread, NULL, netio_rxl_spec_thread, rxl)) {
|
|
NETIO_RXQ_UNLOCK ();
|
|
fprintf (stderr,
|
|
"netio_rxl_add: unable to create specific thread.\n");
|
|
free (rxl);
|
|
return (-1);
|
|
}
|
|
|
|
rxl->next = netio_rxl_add_list;
|
|
netio_rxl_add_list = rxl;
|
|
|
|
pthread_cond_wait (&netio_rxl_cond, &netio_rxq_mutex);
|
|
NETIO_RXQ_UNLOCK ();
|
|
return (0);
|
|
}
|
|
|
|
/* Remove a NIO from the listener list */
|
|
int netio_rxl_remove (netio_desc_t * nio)
|
|
{
|
|
NETIO_RXQ_LOCK ();
|
|
nio->rxl_next = netio_rxl_remove_list;
|
|
netio_rxl_remove_list = nio;
|
|
pthread_cond_wait (&netio_rxl_cond, &netio_rxq_mutex);
|
|
NETIO_RXQ_UNLOCK ();
|
|
return (0);
|
|
}
|
|
|
|
/* Initialize the RXL thread */
|
|
int netio_rxl_init (void)
|
|
{
|
|
pthread_cond_init (&netio_rxl_cond, NULL);
|
|
|
|
if (pthread_create (&netio_rxl_thread, NULL, netio_rxl_gen_thread, NULL)) {
|
|
perror ("netio_rxl_init: pthread_create");
|
|
return (-1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
#endif
|