/*
* scamper_fds: manage events and file descriptors
*
* $Id: scamper_fds.c,v 1.37 2007/05/13 21:54:40 mjl Exp $
*
* Matthew Luckie
*
* Supported by:
* The University of Waikato
* NLANR Measurement and Network Analysis
* CAIDA
* The WIDE Project
*
* Copyright (C) 2004-2007 The University of Waikato
*
* 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, version 2.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#if defined(__APPLE__)
#include <stdint.h>
#endif
#if defined(DMALLOC)
#include <dmalloc.h>
#endif
#include "scamper_fds.h"
#include "scamper_debug.h"
#include "scamper_icmp4.h"
#include "scamper_icmp6.h"
#include "scamper_udp4.h"
#include "scamper_udp6.h"
#include "scamper_tcp4.h"
#include "scamper_tcp6.h"
#include "scamper_dl.h"
#include "scamper_rtsock.h"
#include "utils.h"
#include "mjl_list.h"
#include "mjl_splaytree.h"
/*
* scamper_fd_poll
*
* node to hold callback details for the fd.
*/
typedef struct scamper_fd_poll
{
scamper_fd_t *fdn; /* back pointer to the fd struct */
scamper_fd_cb_t cb; /* callback to use when event arises */
void *param; /* user-defined parameter to pass to callback */
dlist_t *list; /* which list the node is in */
dlist_node_t *node; /* node in the poll list */
uint8_t flags; /* flags associated with structure */
} scamper_fd_poll_t;
/*
* scamper_fd
*
* a file descriptor, details of its type and other identifying information,
* and what to do when read/write events are found with select.
*/
struct scamper_fd
{
int fd; /* the file descriptor being polled */
int type; /* the type of the file descriptor */
int refcnt; /* number of references to this structure */
scamper_fd_poll_t read; /* if monitored for read events */
scamper_fd_poll_t write; /* if monitored for write events */
splaytree_node_t *node; /* node for this fd in the splaytree */
struct timeval tv; /* when this node should be expired */
union
{
struct fd_poll_tcp
{
uint16_t sport;
} fd_poll_tcp;
struct fd_poll_udp
{
uint16_t sport;
} fd_poll_udp;
struct fd_poll_dl
{
int ifindex;
} fd_poll_dl;
} fd_un;
};
#define SCAMPER_FD_TYPE_PRIVATE 0x00
#define SCAMPER_FD_TYPE_ICMP4 0x01
#define SCAMPER_FD_TYPE_ICMP6 0x02
#define SCAMPER_FD_TYPE_UDP4 0x03
#define SCAMPER_FD_TYPE_UDP6 0x04
#define SCAMPER_FD_TYPE_TCP4 0x05
#define SCAMPER_FD_TYPE_TCP6 0x06
#define SCAMPER_FD_TYPE_DL 0x07
#define SCAMPER_FD_TYPE_RTSOCK 0x08
#define SCAMPER_FD_POLL_FLAG_INACTIVE 0x01 /* the fd should not be polled */
#define fd_tcp_sport fd_un.fd_poll_tcp.sport
#define fd_udp_sport fd_un.fd_poll_udp.sport
#define fd_dl_ifindex fd_un.fd_poll_dl.ifindex
static splaytree_t *fd_tree = NULL;
static dlist_t *read_fds = NULL;
static dlist_t *write_fds = NULL;
static dlist_t *read_queue = NULL;
static dlist_t *write_queue = NULL;
static dlist_t *refcnt_0 = NULL;
#ifndef NDEBUG
static char *fd_tostr(scamper_fd_t *fdn)
{
static char buf[16];
switch(fdn->type)
{
case SCAMPER_FD_TYPE_PRIVATE:
return "private";
case SCAMPER_FD_TYPE_ICMP4:
return "icmp4";
case SCAMPER_FD_TYPE_ICMP6:
return "icmp6";
case SCAMPER_FD_TYPE_UDP4:
snprintf(buf, sizeof(buf), "udp4 %d", fdn->fd_udp_sport);
return buf;
case SCAMPER_FD_TYPE_UDP6:
snprintf(buf, sizeof(buf), "udp6 %d", fdn->fd_udp_sport);
return buf;
case SCAMPER_FD_TYPE_TCP4:
snprintf(buf, sizeof(buf), "tcp4 %d", fdn->fd_tcp_sport);
return buf;
case SCAMPER_FD_TYPE_TCP6:
snprintf(buf, sizeof(buf), "tcp6 %d", fdn->fd_tcp_sport);
return buf;
case SCAMPER_FD_TYPE_DL:
snprintf(buf, sizeof(buf), "dl %d", fdn->fd_dl_ifindex);
return buf;
case SCAMPER_FD_TYPE_RTSOCK:
return "rtsock";
}
return "?";
}
#endif
/*
* fd_free
*
* free up memory allocated to scamper's monitoring of the file descriptor.
*/
static void fd_free(scamper_fd_t *fdn)
{
scamper_debug(__func__, "fd %d type %s", fdn->fd, fd_tostr(fdn));
if(fdn->read.node != NULL)
{
dlist_node_pop(fdn->read.list, fdn->read.node);
}
if(fdn->write.node != NULL)
{
dlist_node_pop(fdn->write.list, fdn->write.node);
}
if(fdn->node != NULL)
{
splaytree_remove_node(fd_tree, fdn->node);
}
if(fdn->type == SCAMPER_FD_TYPE_DL)
{
scamper_dl_state_free(fdn->read.param);
}
free(fdn);
return;
}
/*
* fd_refcnt_0
*
* this function is called whenever a fdn with a refcnt field of zero is
* found.
*/
static void fd_refcnt_0(scamper_fd_t *fdn)
{
/*
* if the fd is in a list that is currently locked, then it can't be
* removed just yet
*/
if((fdn->read.list != NULL && dlist_islocked(fdn->read.list) != 0) ||
(fdn->write.list != NULL && dlist_islocked(fdn->write.list) != 0))
{
return;
}
/*
* if this is a private fd and the reference count has reached zero,
* then the scamper_fd structure can be freed up completely now
*/
if(fdn->type == SCAMPER_FD_TYPE_PRIVATE)
{
fd_free(fdn);
return;
}
/*
* this fd is a shared fd. detach it from any poll lists it is in.
*/
if(fdn->read.list != NULL)
{
scamper_fd_read_pause(fdn);
dlist_node_eject(fdn->read.list, fdn->read.node);
fdn->read.list = NULL;
}
if(fdn->write.list != NULL)
{
scamper_fd_write_pause(fdn);
dlist_node_eject(fdn->write.list, fdn->write.node);
fdn->write.list = NULL;
}
/*
* set this fd to be closed in one minute unless something else comes
* along and wants to use it. use the structure's read-node for this.
*/
gettimeofday_wrap(&fdn->tv);
fdn->tv.tv_sec += 60;
dlist_node_tail_push(refcnt_0, fdn->read.node);
fdn->read.list = refcnt_0;
return;
}
/*
* fd_refcnt_0_reap
*
* loop through the list of fds with a refcnt of zero, and reap them if their
* time has expired.
*/
static void fd_refcnt_0_reap(void)
{
scamper_fd_poll_t *fdp;
struct timeval tv;
/* nothing to do */
if(dlist_count(refcnt_0) == 0)
{
return;
}
gettimeofday_wrap(&tv);
while((fdp = (scamper_fd_poll_t *)dlist_head_get(refcnt_0)) != NULL)
{
if(timeval_cmp(&fdp->fdn->tv, &tv) > 0)
{
break;
}
close(fdp->fdn->fd);
fd_free(fdp->fdn);
}
return;
}
/*
* fd_set_assemble
*
* given a list of scamper_fd_poll_t structures held in a list, compose an
* fd_set for them to pass to select.
*/
static fd_set *fd_set_assemble(dlist_t *fds, fd_set *fdset, int *nfds)
{
scamper_fd_poll_t *fdp;
dlist_node_t *node;
int count = 0;
FD_ZERO(fdset);
node = dlist_head_node(fds);
while(node != NULL)
{
/* file descriptor associated with the node */
fdp = (scamper_fd_poll_t *)dlist_node_item(node);
/* get the next node incase this node is subsequently removed */
node = dlist_node_next(node);
/* if there is nothing using this fdn any longer, then stop polling it */
if(fdp->fdn->refcnt == 0)
{
fd_refcnt_0(fdp->fdn);
continue;
}
/* if the inactive flag is set, then skip over this file descriptor */
if((fdp->flags & SCAMPER_FD_POLL_FLAG_INACTIVE) != 0)
{
dlist_node_eject(fds, fdp->node);
fdp->list = NULL;
continue;
}
/* monitor this file descriptor */
FD_SET(fdp->fdn->fd, fdset);
count++;
/* update the maxfd seen if appropriate */
if(*nfds < fdp->fdn->fd)
{
*nfds = fdp->fdn->fd;
}
}
/*
* if there are no fds in the set to monitor, then return a null pointer
* to pass to select
*/
if(count == 0)
{
return NULL;
}
return fdset;
}
/*
* fd_set_check
*
* given an fd_set that has been passed to select, as well as a list of
* fds that are being monitored, figure out which ones have an event and
* use the callback provided to deal with the event.
*/
static void fd_set_check(fd_set *fdset, dlist_t *fds, int *count)
{
scamper_fd_poll_t *fdp;
dlist_node_t *node;
/* stop now if there is nothing to check */
if(fdset == NULL || *count == 0)
{
return;
}
/* nodes in this list should not be removed while this function is called */
dlist_lock(fds);
/* loop through */
node = dlist_head_node(fds);
while(node != NULL && *count > 0)
{
fdp = (scamper_fd_poll_t *)dlist_node_item(node);
node = dlist_node_next(node);
if(FD_ISSET(fdp->fdn->fd, fdset))
{
fdp->cb(fdp->fdn->fd, fdp->param);
(*count)--;
}
}
/* can modify the list now */
dlist_unlock(fds);
return;
}
/*
* fd_cmp
*
* given two scamper_fd_t structures, determine if their properties are
* the same. used to maintain the splaytree of existing file descriptors
* held by scamper.
*/
static int fd_cmp(const void *va, const void *vb)
{
const scamper_fd_t *a = (const scamper_fd_t *)va;
const scamper_fd_t *b = (const scamper_fd_t *)vb;
if(a->type < b->type) return -1;
if(a->type > b->type) return 1;
switch(a->type)
{
case SCAMPER_FD_TYPE_TCP4:
case SCAMPER_FD_TYPE_TCP6:
if(a->fd_tcp_sport < b->fd_tcp_sport) return -1;
if(a->fd_tcp_sport > b->fd_tcp_sport) return 1;
return 0;
case SCAMPER_FD_TYPE_UDP4:
case SCAMPER_FD_TYPE_UDP6:
if(a->fd_udp_sport < b->fd_udp_sport) return -1;
if(a->fd_udp_sport > b->fd_udp_sport) return 1;
return 0;
case SCAMPER_FD_TYPE_DL:
if(a->fd_dl_ifindex < b->fd_dl_ifindex) return -1;
if(a->fd_dl_ifindex > b->fd_dl_ifindex) return 1;
return 0;
}
return 0;
}
/*
* fd_alloc
*
* allocate a scamper_fd_t structure and do generic setup tasks.
*/
static scamper_fd_t *fd_alloc(int type, int fd)
{
scamper_fd_t *fdn = NULL;
if((fdn = malloc_zero(sizeof(scamper_fd_t))) == NULL)
{
goto err;
}
fdn->type = type;
fdn->fd = fd;
fdn->refcnt = 1;
/* set up to poll read ability */
if((fdn->read.node = dlist_node_alloc(&fdn->read)) == NULL)
{
goto err;
}
fdn->read.fdn = fdn;
fdn->read.flags = SCAMPER_FD_POLL_FLAG_INACTIVE;
/* set up to poll write ability */
if((fdn->write.node = dlist_node_alloc(&fdn->write)) == NULL)
{
goto err;
}
fdn->write.fdn = fdn;
fdn->write.flags = SCAMPER_FD_POLL_FLAG_INACTIVE;
return fdn;
err:
if(fdn != NULL) fd_free(fdn);
return NULL;
}
/*
* fd_find
*
* search the tree of file descriptors known to scamper for a matching
* entry. if one is found, increment its reference count and return it.
*/
static scamper_fd_t *fd_find(scamper_fd_t *findme)
{
scamper_fd_t *fdn;
if((fdn = splaytree_find(fd_tree, findme)) != NULL)
{
/*
* remove the node from the refcnt_0 list otherwise it will be
* reaped
*/
if(fdn->refcnt == 0)
{
assert(fdn->read.list == refcnt_0);
dlist_node_eject(fdn->read.list, fdn->read.node);
fdn->read.list = NULL;
}
fdn->refcnt++;
}
return fdn;
}
/*
* fd_null
*
* allocate a file descriptor of a specified type.
*/
static scamper_fd_t *fd_null(int type)
{
scamper_fd_t *fdn = NULL, findme;
int fd = -1;
/* first check if a sharable fd exists for this type */
findme.type = type;
if((fdn = fd_find(&findme)) != NULL)
{
return fdn;
}
if(type == SCAMPER_FD_TYPE_ICMP4) fd = scamper_icmp4_open();
else if(type == SCAMPER_FD_TYPE_ICMP6) fd = scamper_icmp6_open();
else if(type == SCAMPER_FD_TYPE_RTSOCK) fd = scamper_rtsock_open();
if(fd == -1 || (fdn = fd_alloc(type, fd)) == NULL ||
(fdn->node = splaytree_insert(fd_tree, fdn)) == NULL)
{
goto err;
}
return fdn;
err:
if(fd != -1) close(fd);
if(fdn != NULL) fd_free(fdn);
return NULL;
}
static scamper_fd_t *fd_tcp(int type, uint16_t sport)
{
scamper_fd_t *fdn = NULL, findme;
int fd = -1;
findme.type = type;
findme.fd_tcp_sport = sport;
if((fdn = fd_find(&findme)) != NULL)
{
return fdn;
}
if(type == SCAMPER_FD_TYPE_TCP4) fd = scamper_tcp4_open(sport);
else if(type == SCAMPER_FD_TYPE_TCP6) fd = scamper_tcp6_open(sport);
if(fd == -1 || (fdn = fd_alloc(type, fd)) == NULL)
{
goto err;
}
fdn->fd_tcp_sport = sport;
if((fdn->node = splaytree_insert(fd_tree, fdn)) == NULL)
{
goto err;
}
return fdn;
err:
if(fd != -1) close(fd);
if(fdn != NULL) fd_free(fdn);
return NULL;
}
static scamper_fd_t *fd_udp(int type, uint16_t sport)
{
scamper_fd_t *fdn, findme;
int fd = -1;
findme.type = type;
findme.fd_udp_sport = sport;
if((fdn = fd_find(&findme)) != NULL)
{
return fdn;
}
if(type == SCAMPER_FD_TYPE_UDP4) fd = scamper_udp4_open(sport);
else if(type == SCAMPER_FD_TYPE_UDP6) fd = scamper_udp6_open(sport);
if(fd == -1 || (fdn = fd_alloc(type, fd)) == NULL)
{
scamper_debug(__func__, "hello");
goto err;
}
fdn->fd_udp_sport = sport;
if((fdn->node = splaytree_insert(fd_tree, fdn)) == NULL)
{
goto err;
}
return fdn;
err:
if(fd != -1) close(fd);
if(fdn != NULL) fd_free(fdn);
return NULL;
}
static int fdp_list(void *item, void *param)
{
((scamper_fd_poll_t *)item)->list = (dlist_t *)param;
return 0;
}
/*
* scamper_fds_poll
*
* the money function: this function polls the file descriptors held by
* scamper. for each fd with an event, it calls the callback registered
* with the fd.
*/
int scamper_fds_poll(struct timeval *timeout)
{
fd_set rfds, *rfdsp;
fd_set wfds, *wfdsp;
int count, nfds = -1;
/* concat any new fds to monitor now */
dlist_foreach(read_queue, fdp_list, read_fds);
dlist_concat(read_fds, read_queue);
dlist_foreach(write_queue, fdp_list, write_fds);
dlist_concat(write_fds, write_queue);
/* compose the sets of file descriptors to monitor */
rfdsp = fd_set_assemble(read_fds, &rfds, &nfds);
wfdsp = fd_set_assemble(write_fds, &wfds, &nfds);
/* find out which file descriptors have an event */
if((count = select(nfds+1, rfdsp, wfdsp, NULL, timeout)) == -1)
{
printerror(errno, strerror, __func__, "select failed");
return -1;
}
/* reap any expired fds */
fd_refcnt_0_reap();
fd_set_check(rfdsp, read_fds, &count);
fd_set_check(wfdsp, write_fds, &count);
return 0;
}
/*
* scamper_fd_fd_get
*
* return the actual file descriptor associated with the scamper_fd_t
*/
int scamper_fd_fd_get(const scamper_fd_t *fdn)
{
return fdn->fd;
}
/*
* scamper_fd_fd_set
*
* set the file descriptor being monitored with the scamper_fd_t
*/
int scamper_fd_fd_set(scamper_fd_t *fdn, int fd)
{
fdn->fd = fd;
return 0;
}
/*
* scamper_fd_read_pause
*
* ignore any read events on the fd.
*/
void scamper_fd_read_pause(scamper_fd_t *fdn)
{
fdn->read.flags |= SCAMPER_FD_POLL_FLAG_INACTIVE;
return;
}
/*
* scamper_fd_read_unpause
*
* monitor read events on the fd. unset the inactive flag, and push the
* node back onto the read list
*/
void scamper_fd_read_unpause(scamper_fd_t *fdn)
{
assert(fdn->read.cb != NULL);
if((fdn->read.flags & SCAMPER_FD_POLL_FLAG_INACTIVE) != 0)
{
fdn->read.flags &= ~(SCAMPER_FD_POLL_FLAG_INACTIVE);
/*
* the fd may still be on the read fds list, just with the inactive bit
* set. if it isn't, then we have to put it on the queue.
*/
if(fdn->read.list != read_fds)
{
dlist_node_head_push(read_queue, fdn->read.node);
fdn->read.list = read_queue;
}
}
return;
}
/*
* scmaper_fd_write_pause
*
* ignore any write events on the fd
*/
void scamper_fd_write_pause(scamper_fd_t *fdn)
{
fdn->write.flags |= SCAMPER_FD_POLL_FLAG_INACTIVE;
return;
}
/*
* scamper_fd_write_unpause
*
* monitor write events on the fd. unset the inactive flag, and push the
* node back onto the write list
*/
void scamper_fd_write_unpause(scamper_fd_t *fdn)
{
assert(fdn->write.cb != NULL);
if((fdn->write.flags & SCAMPER_FD_POLL_FLAG_INACTIVE) != 0)
{
fdn->write.flags &= ~(SCAMPER_FD_POLL_FLAG_INACTIVE);
/*
* the fd may still be on the write fds list, just with the inactive bit
* set. if it isn't, then we have to put it on the queue.
*/
if(fdn->write.list != write_fds)
{
dlist_node_head_push(write_queue, fdn->write.node);
fdn->write.list = write_queue;
}
}
return;
}
void scamper_fd_read_set(scamper_fd_t *fdn, scamper_fd_cb_t cb, void *param)
{
assert(fdn->type == SCAMPER_FD_TYPE_PRIVATE);
fdn->read.cb = cb;
fdn->read.param = param;
return;
}
void scamper_fd_write_set(scamper_fd_t *fdn, scamper_fd_cb_t cb, void *param)
{
assert(fdn->type == SCAMPER_FD_TYPE_PRIVATE);
fdn->write.cb = cb;
fdn->write.param = param;
return;
}
void *scamper_fd_read_state(scamper_fd_t *fdn)
{
return fdn->read.param;
}
void *scamper_fd_write_state(scamper_fd_t *fdn)
{
return fdn->write.param;
}
scamper_fd_t *scamper_fd_icmp4(void)
{
scamper_fd_t *fdn;
if((fdn = fd_null(SCAMPER_FD_TYPE_ICMP4)) != NULL)
{
fdn->read.cb = scamper_icmp4_read_cb;
scamper_fd_read_unpause(fdn);
}
return fdn;
}
scamper_fd_t *scamper_fd_icmp6(void)
{
scamper_fd_t *fdn;
if((fdn = fd_null(SCAMPER_FD_TYPE_ICMP6)) != NULL)
{
fdn->read.cb = scamper_icmp6_read_cb;
scamper_fd_read_unpause(fdn);
}
return fdn;
}
scamper_fd_t *scamper_fd_rtsock(void)
{
scamper_fd_t *fdn;
if((fdn = fd_null(SCAMPER_FD_TYPE_RTSOCK)) != NULL)
{
fdn->read.cb = scamper_rtsock_read_cb;
scamper_fd_read_unpause(fdn);
}
return fdn;
}
scamper_fd_t *scamper_fd_tcp4(uint16_t sport)
{
return fd_tcp(SCAMPER_FD_TYPE_TCP4, sport);
}
scamper_fd_t *scamper_fd_tcp6(uint16_t sport)
{
return fd_tcp(SCAMPER_FD_TYPE_TCP6, sport);
}
scamper_fd_t *scamper_fd_udp4(uint16_t sport)
{
return fd_udp(SCAMPER_FD_TYPE_UDP4, sport);
}
scamper_fd_t *scamper_fd_udp6(uint16_t sport)
{
return fd_udp(SCAMPER_FD_TYPE_UDP6, sport);
}
scamper_fd_t *scamper_fd_dl(int ifindex)
{
scamper_fd_t *fdn = NULL, findme;
scamper_dl_t *dl = NULL;
int fd = -1;
findme.type = SCAMPER_FD_TYPE_DL;
findme.fd_dl_ifindex = ifindex;
if((fdn = fd_find(&findme)) != NULL)
{
return fdn;
}
/*
* open the file descriptor for the ifindex, and then allocate a scamper_fd
* for the file descriptor
*/
if((fd = scamper_dl_open(ifindex)) == -1 ||
(fdn = fd_alloc(SCAMPER_FD_TYPE_DL, fd)) == NULL)
{
goto err;
}
/*
* record the ifindex for the file descriptor, and then allocate the state
* that is maintained with it
*/
fdn->fd_dl_ifindex = ifindex;
/*
* 1. add the file descriptor to the splay tree
* 2. allocate state for the datalink file descriptor
*/
if((fdn->node = splaytree_insert(fd_tree, fdn)) == NULL ||
(dl = scamper_dl_state_alloc(fdn)) == NULL)
{
goto err;
}
/* set the file descriptor up for reading */
fdn->read.cb = scamper_dl_read_cb;
fdn->read.param = dl;
fdn->write.cb = NULL;
fdn->write.param = dl;
scamper_fd_read_unpause(fdn);
return fdn;
err:
if(fdn != NULL) free(fdn);
if(fd != -1) close(fd);
return NULL;
}
/*
* scamper_fd_private
*
* allocate a private fd for scamper to manage. this fd is not shared amongst
* scamper.
*/
scamper_fd_t *scamper_fd_private(int fd,
scamper_fd_cb_t read_cb, void *read_param,
scamper_fd_cb_t write_cb, void *write_param)
{
scamper_fd_t *fdn = NULL;
if((fdn = fd_alloc(SCAMPER_FD_TYPE_PRIVATE, fd)) == NULL)
{
goto err;
}
if(read_cb != NULL)
{
scamper_fd_read_set(fdn, read_cb, read_param);
scamper_fd_read_unpause(fdn);
}
if(write_cb != NULL)
{
scamper_fd_write_set(fdn, write_cb, write_param);
scamper_fd_write_unpause(fdn);
}
return fdn;
err:
if(fdn != NULL) fd_free(fdn);
return NULL;
}
/*
* scamper_fd_ifindex
*
* if the file descriptor is associated with a known ifindex, return details
* of it.
*/
int scamper_fd_ifindex(const scamper_fd_t *fdn, int *ifindex)
{
if(fdn->type == SCAMPER_FD_TYPE_DL)
{
*ifindex = fdn->fd_dl_ifindex;
return 0;
}
return -1;
}
/*
* scamper_fd_sport
*
* if the file descriptor has a known source port, return details of it.
*/
int scamper_fd_sport(const scamper_fd_t *fdn, uint16_t *sport)
{
switch(fdn->type)
{
case SCAMPER_FD_TYPE_UDP4:
case SCAMPER_FD_TYPE_UDP6:
*sport = fdn->fd_udp_sport;
return 0;
case SCAMPER_FD_TYPE_TCP4:
case SCAMPER_FD_TYPE_TCP6:
*sport = fdn->fd_tcp_sport;
return 0;
}
return -1;
}
/*
* scamper_fd_free
*
* this function reduces the reference count for a given file descriptor.
*
* if zero is reached, the fd will be dealt with when scamper_fd_poll is next
* called. the fd cannot be summarily removed here without the potential
* to screw up any current call to scamper_fd_poll as that function assumes
* the list remains intact for the duration of any events found with select.
*
*/
void scamper_fd_free(scamper_fd_t *fdn)
{
assert(fdn != NULL);
assert(fdn->refcnt > 0);
if(--fdn->refcnt == 0)
{
fd_refcnt_0(fdn);
}
return;
}
/*
* alloc_list
*
* helper function to allocate a list for scamper_fds_init
*/
static dlist_t *alloc_list(char *name)
{
dlist_t *list;
if((list = dlist_alloc()) == NULL)
{
printerror(errno, strerror, __func__, "alloc %s failed", name);
}
return list;
}
/*
* scamper_fds_init
*
* setup the global data structures necessary for scamper to manage a set of
* file descriptors
*/
int scamper_fds_init()
{
scamper_debug(__func__, "fd table size: %d", getdtablesize());
if((read_fds = alloc_list("read fd list")) == NULL ||
(read_queue = alloc_list("read fd queue")) == NULL ||
(write_fds = alloc_list("write fd list")) == NULL ||
(write_queue = alloc_list("write fd queue")) == NULL ||
(refcnt_0 = alloc_list("refcnt_0 list")) == NULL)
{
return -1;
}
if((fd_tree = splaytree_alloc(fd_cmp)) == NULL)
{
printerror(errno, strerror, __func__, "alloc fd tree failed");
return -1;
}
return 0;
}
/*
* cleanup_list
*
* helper function to remove scamper_fd_poll structures from any lists.
*/
static void cleanup_list(dlist_t *list)
{
scamper_fd_poll_t *poll;
if(list == NULL) return;
while((poll = dlist_head_pop(list)) != NULL)
{
poll->list = NULL;
poll->node = NULL;
}
dlist_free(list);
return;
}
/*
* scamper_fds_cleanup
*
* tidy up the state allocated to maintain fd records.
*/
void scamper_fds_cleanup()
{
scamper_fd_poll_t *fdp;
/* clean up the lists */
cleanup_list(read_fds); read_fds = NULL;
cleanup_list(write_fds); write_fds = NULL;
cleanup_list(read_queue); read_queue = NULL;
cleanup_list(write_queue); write_queue = NULL;
/* reap anything on the reap list */
if(refcnt_0 != NULL)
{
while((fdp = (scamper_fd_poll_t *)dlist_head_get(refcnt_0)) != NULL)
{
close(fdp->fdn->fd);
fd_free(fdp->fdn);
}
dlist_free(refcnt_0);
refcnt_0 = NULL;
}
/* clean up the tree */
if(fd_tree != NULL)
{
splaytree_free(fd_tree, NULL);
fd_tree = NULL;
}
return;
}
syntax highlighted by Code2HTML, v. 0.9.1