/*
 * dctpLprocessSupport.c -- Support routines for the Lprocess port of dccp-tp
 *
 * Copyright (C) 2008 Tom Phelan
 *
 * This file is part of dccp-tp.
 *
 * Dccp-tp is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * Dccp-tp 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with dccp-tp.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Documentation and source code for dccp-tp is available at
 * http://www.phelan-4.com/dccp-tp/.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <syslog.h>
#include <stdarg.h>
#include <errno.h>
#include "avl.h"
#include "dctpCore.h"
#include "dctpSupport.h"
#include "dctpRawApi.h"

/*
 * Private function prototypes
 */
static int timerCompare(dctpTimer *t1, dctpTimer *t2);
static int timerCompareAVL(const void *a, const void *b, void *p);
static void *timerThread(void * arg);
static void initSockets(void);
static void *natV4UdpRcv(void *arg);
static void initUdpSockets(void);

#ifdef DEBUG
int pthread_mutexattr_setkind_np(pthread_mutexattr_t *, int);
#endif

/*
 * Defines for timers
 */
/* Root of the balanced binary tree holding running timers */
static avl_tree *timerTree;
/* Mutex and conditions for timers */
static pthread_mutex_t timerMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t timerCond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t timerThreadMutex = PTHREAD_MUTEX_INITIALIZER;
static uint_t timerThreadCount;

typedef struct _timerWorker {
    struct _timerWorker *next;
    int number;
    dctpTimer *t;
    pthread_cond_t cond;
    pthread_mutex_t mutex;
} timerWorker;

static timerWorker *timerWorkerHead;
static timerWorker *timerWorkerTail;

/*
 * dctpoSocketLockInit -- initialize the mutex in a socket
 */
void dctpoSocketLockInit(dctpSocket *sock) {
#ifdef DEBUG
    pthread_mutexattr_t mattr;

    pthread_mutexattr_init(&mattr);
    pthread_mutexattr_setkind_np(&mattr, PTHREAD_MUTEX_ERRORCHECK_NP);
    pthread_mutex_init(&(sock->socklock), &mattr);
#else
    pthread_mutex_init(&(sock->socklock), NULL);
#endif  /* DEBUG */
    pthread_cond_init(&(sock->sockwait), NULL);
}

/*
 * dctpoSocketLock -- acquire the lock on a socket
 */
void dctpoSocketLock(dctpSocket *sock) {
#ifdef DEBUG
    if (pthread_mutex_lock(&(sock->socklock)) != 0) {
	dctpoLog(DCTPLOG_DEBUG, "dctpoSocketLock: socket 0x%x already locked\n",
		 (uint32_t)sock);
	bpoint("dctpoSocketLock");
    }
#else
    pthread_mutex_lock(&(sock->socklock));
#endif  /* DEBUG */
}

/*
 * dctpoSocketUnlock -- release the lock on a socket
 */
void dctpoSocketUnlock(dctpSocket *sock) {
#ifdef DEBUG
    if (pthread_mutex_unlock(&(sock->socklock)) != 0) {
	dctpoLog(DCTPLOG_DEBUG, "dctpoSocketUnlock: socket 0x%x not locked\n",
		 (uint32_t)sock);
	bpoint("dctpoSocketUnlock");
    }
#else
    pthread_mutex_unlock(&(sock->socklock));
#endif  /* DEBUG */
}

/*
 * dctpoSocketWait -- wait for a condition on a socket
 */
void dctpoSocketWait(dctpSocket *sock) {
#ifdef DEBUG
    if (pthread_mutex_lock(&(sock->socklock)) != EDEADLK) {
	dctpoLog(DCTPLOG_DEBUG,
		 "dctpoSocketWait: socket 0x%x not locked by calling thread",
		 (uint32_t)sock);
	bpoint("dctpoSocketWait");
    }
#endif  /* DEBUG */
    pthread_cond_wait(&(sock->sockwait), &(sock->socklock));
}

/*
 * dctpoSocketSignal -- signal a change in condition on a socket
 */
void dctpoSocketSignal(dctpSocket *sock) {
    pthread_cond_broadcast(&(sock->sockwait));
}

/*
 * dctpoObjectLock -- acquire the lock on an object
 */
void dctpoObjectLock(dctpSupportMutex_t *lock) {
#ifdef DEBUG
    if (pthread_mutex_lock(lock) != 0) {
	dctpoLog(DCTPLOG_DEBUG, "dctpoObjectLock: lock 0x%x already locked\n",
		 (uint32_t)lock);
	bpoint("dctpoObjectLock");
    }
#else
    pthread_mutex_lock(lock);
#endif  /* DEBUG */
}

/*
 * dctpoObjectUnlock -- release the lock on an object
 */
void dctpoObjectUnlock(dctpSupportMutex_t *lock) {
#ifdef DEBUG
    if (pthread_mutex_unlock(lock) != 0) {
	dctpoLog(DCTPLOG_DEBUG, "dctpoObjectUnlock: lock 0x%x not locked\n",
		 (uint32_t)lock);
	bpoint("dctpoObjectUnlock");
    }
#else
    pthread_mutex_unlock(lock);
#endif  /* DEBUG */
}

/*
 * dctpoMemcpy -- copy between memory locations
 */
void dctpoMemcpy(void *dest, void *src, int len) {
    memcpy(dest, src, len);
}

/*
 * dctpoMemset -- set a block of memory to a value
 */
void dctpoMemset(void *dst, int val, int len) {
    memset(dst, val, len);
}

/*
 * dctpoMalloc -- allocate memory from heap
 */
void *dctpoMalloc(int size) {
#ifdef DEBUG
    uint32_t *m;

    if ((m = malloc(size + sizeof(uint32_t))) != NULL) {
	*m = DCTPSOCK_MAGICNO;
	return(m + 1);
    } else {
	return(NULL);
    }
#else
    return(malloc(size));
#endif
}

/*
 * dctpRealloc -- expands memory allocated from heap
 */
void *dctpoRealloc(void *ptr, int size) {
#ifdef DEBUG
    uint32_t *m = ((uint32_t *)ptr) - 1;

    if (*m != DCTPSOCK_MAGICNO) {
	dctpoLog(DCTPLOG_DEBUG, "dctpoRealloc: memory already free\n");
	bpoint("dctpoRealloc");
    }
    return(realloc(m, size + sizeof(uint32_t)));
#else
    return(realloc(ptr, size));
#endif
}

/*
 * dctpoFree -- return memory to the heap
 */
void dctpoFree(void *ptr) {
#ifdef DEBUG
    uint32_t *m = ((uint32_t *)ptr) - 1;

    if (*m != DCTPSOCK_MAGICNO) {
	dctpoLog(DCTPLOG_DEBUG, "dctpoFree: memory already free\n");
	bpoint("dctpoFree");
    }
    *m = 0;
    free(m);
#else
    free(ptr);
#endif
}

/*
 * dctpoStartTimer -- start a timer
 */
void dctpoStartTimer(dctpTimer *t, uint32_t timeout,
		     dctpTimerAction action, void *arg) {
    if (t->running) {
	dctpoStopTimer(t);
    }
    pthread_mutex_lock(&timerMutex);
    t->running = 1;
    t->expires = dctpoNow() + timeout;
    t->action = action;
    t->arg = arg;
    avl_insert(timerTree, (void *)t);
    pthread_cond_broadcast(&timerCond);
    pthread_mutex_unlock(&timerMutex);
}

/*
 * dctpoStopTimer -- stop a timer
 */
void dctpoStopTimer(dctpTimer *t) {
    pthread_mutex_lock(&timerMutex);
    if (t->running) {
	avl_delete(timerTree, (void *)t);
	t->running = 0;
	pthread_cond_broadcast(&timerCond);
    }
    pthread_mutex_unlock(&timerMutex);
}

/*
 * timerFuncWrapper -- utility to call the action function for an expired timer
 */
static void *timerFuncWrapper(void * arg) {
    timerWorker *tw = (timerWorker *)arg;

    pthread_mutex_lock(&(tw->mutex));
    while (1) {
	/* Execute the action function */
	tw->t->action(tw->t->arg);
	/* Put tw back on the list for more work */
	pthread_mutex_lock(&timerThreadMutex);
	tw->next = NULL;
	if (timerWorkerTail != NULL) {
	    timerWorkerTail->next = tw;
	} else {
	    timerWorkerHead = tw;
	}
	timerWorkerTail = tw;
	/* Wait for more work */
	pthread_cond_broadcast(&timerCond);
	pthread_mutex_unlock(&timerThreadMutex);
	pthread_cond_wait(&(tw->cond), &(tw->mutex));
    }
    return(NULL);
}

/*
 * timerCompare -- used by the AVL library to compare expiration time for timers
 */
static int timerCompare(dctpTimer *t1, dctpTimer *t2) {
    if (t1 == t2) {
	return(0);
    } else if (t1->expires < t2->expires) {
	return(-1);
    } else if (t1->expires > t2->expires) {
	return(1);
    } else {
	return(0);
    }
}

/*
 * timerCompareAVL -- wrapper for the AVL library to do timer comparisons
 */
static int timerCompareAVL(const void *a, const void *b, void *p) {
    return(timerCompare((dctpTimer *)a, (dctpTimer *)b));
}

#define DCTPPTHREADS_MAXTIMERTHREADS 16

static int dispatchTimer(dctpTimer *t) {
    timerWorker *tw;
    pthread_t pt;
    pthread_attr_t pattr;

    /* Called with timerMutex locked */
    /* But need to lock timerThreadMutex */
    pthread_mutex_lock(&timerThreadMutex);
    if ((tw = timerWorkerHead) != NULL) {
	pthread_mutex_lock(&(tw->mutex));
	if ((timerWorkerHead = tw->next) == NULL) timerWorkerTail = NULL;
	tw->next = NULL;
	tw->t = t;
	pthread_cond_broadcast(&(tw->cond));
	pthread_mutex_unlock(&(tw->mutex));
	pthread_mutex_unlock(&timerThreadMutex);
	return(0);
    } else if ((timerThreadCount < DCTPPTHREADS_MAXTIMERTHREADS) &&
	       ((tw = malloc(sizeof(timerWorker))) != NULL)) {
	tw->next = NULL;
	tw->t = t;
	++timerThreadCount;
	tw->number = timerThreadCount;
	dctpoLog(DCTPLOG_DEBUG, "dispatchTimer: new timer worker 0x%x\n", (uint32_t)tw);
	pthread_mutex_init(&(tw->mutex), NULL);
	pthread_cond_init(&(tw->cond), NULL);
	pthread_attr_init(&pattr);
	pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED);
	if (pthread_create(&pt, &pattr, timerFuncWrapper, tw) != 0) {
	    free(tw);
	    pthread_mutex_unlock(&timerThreadMutex);
	    return(-1);
	}
	pthread_mutex_unlock(&timerThreadMutex);
	return(0);
    } else {
	pthread_mutex_unlock(&timerThreadMutex);
	return(-1);
    }
}

/*
 * timerThread -- thread that mananages timers
 */
static void *timerThread(void * arg) {
    avl_traverser trav;
    dctpTimer now, *t;
    struct timespec tspec;

    pthread_mutex_lock(&timerMutex);
    timerTree = avl_create(timerCompareAVL, NULL);
    while (1) {
	now.expires = dctpoNow();
	avl_init_traverser(&trav);
	while ((t = (dctpTimer *)avl_traverse(timerTree, &trav)) != NULL) {
	    if ((uint32_t)(t->arg) < 0x1000) bpoint("timerThread");
	    if (timerCompare(t, &now) <= 0) {
		/* Timer has expired */
		t->running = 0;
		avl_delete(timerTree, (void *)t);
		if (dispatchTimer(t) < 0) {
		    /* Couldn't create new thread, try again later */
		    t->running = 1;
		    avl_insert(timerTree, (void *)t);
		    break;
		}
	    } else {
		/* No more expired timres */
		break;
	    }
	    avl_init_traverser(&trav);
	}
	if (t != NULL) {
	    tspec.tv_sec = t->expires/1000000;
	    tspec.tv_nsec = (t->expires % 1000000)*1000;
	    pthread_cond_timedwait(&timerCond, &timerMutex, &tspec);
	} else {
	    pthread_cond_wait(&timerCond, &timerMutex);
	}
    }
}

/*
 * dctpoInit -- entry point for initializing dccp-tp
 */
void dctpoInit(void) {
    pthread_attr_t pattr;
    pthread_t thread;

    pthread_attr_init(&pattr);
    pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED);
    pthread_create(&thread, &pattr, timerThread, NULL);

    initSockets();
}

/*
 * dctpoChooseLocalAddr -- chooses a suitable local address for the given
 * remote address.
 */
int dctpoChooseLocalAddr(dctpSockaddr *raddr, dctpSockaddr *laddr) {
    if (raddr->sa_family == DCTP_AF_INET) {
	if (((dctpSockaddrV4 *)raddr)->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
	    laddr->sa_family = DCTP_AF_INET;
	    ((dctpSockaddrV4 *)laddr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	    return(1);
	} else {
	    ((dctpSockaddrV4 *)laddr)->sin_addr.s_addr = htonl(INADDR_ANY);
	    return(1);
	}
    }
    return(0);
}

/*
 * dctpoRandomSeqno -- generates a 48-bit random number suitable for use
 * as an initial sequence number.
 */
uint64_t dctpoRandomSeqno(void) {
#ifdef DEBUG
    return(1);
#else
    static int randfd = -1;
    uint64_t val;

    if (randfd == -1) {
	if ((randfd = open("/dev/urandom", O_RDONLY)) < 0) {
	    dctpoLog(DCTPLOG_ERR, "dctpoRandom: can't open /dev/urandom: %s\n", strerror(errno));
	    return(0);
	}
    }
    if (read(randfd, &val, sizeof(val)) < 0) {
	dctpoLog(DCTPLOG_ERR, "dctpoRandom: can't read: %s\n", strerror(errno));
	return(0);
    }
    return(val & DCTP_48BITMASK);
#endif
}

/*
 * dctpoLog -- displays log messages
 */
void dctpoLog(int pri, char *form, ...) {
    int tid;
    va_list argp;
    static int syslogInit = 0;
    static char msg[1024];

    va_start(argp, form);
    if (!syslogInit) {
	openlog("dccp-tp", LOG_PERROR, LOG_USER);
	syslogInit = 1;
    }
    tid = sprintf(msg, "[%ld]: ", pthread_self());
    vsnprintf(&msg[tid], sizeof(msg) - tid, form, argp);
    syslog(pri, msg, argp);
}

/*
 * dctpoNow -- returns the current system time in nanoseconds
 */
uint64_t dctpoNow(void) {
    uint64_t now;
    struct timeval tval;

    gettimeofday(&tval, NULL);
    now = ((uint64_t)tval.tv_sec)*1000000 + ((uint64_t)tval.tv_usec);
    return(now);
}

/*
 * dctpoStrlen -- determines the length of a character string
 */
uint_t dctpoStrlen(char *s) {
    return(strlen(s));
}

/*
 * dctpoPacketMalloc -- allocates a packet buffer from the heap
 */
dctpPacket *dctpoPacketMalloc(int size) {
#ifdef DEBUG
    uint32_t *tmp;
#endif
    dctpPacket *pkt;

#ifdef DEBUG
    tmp = malloc(size + sizeof(dctpPacket) + DCTPPKT_DEF_HDRSPACE + sizeof(uint32_t));
    if (tmp == NULL) return(NULL);
    *tmp = DCTPSOCK_MAGICNO;
    pkt = (dctpPacket *)(tmp + 1);
#else
    pkt = malloc(size + sizeof(dctpPacket) + DCTPPKT_DEF_HDRSPACE);
    if (pkt == NULL) return(NULL);
#endif
    pkt->buflen = size + DCTPPKT_DEF_HDRSPACE;
    pkt->appdata = &pkt->buf[DCTPPKT_DEF_HDRSPACE];
    pkt->appdatalen = 0;
    pkt->dccphdr = pkt->appdata;
    pkt->dccphdrlen = 0;
    pkt->iphdr = pkt->appdata;
    pkt->nextPkt = NULL;
    pkt->chdr.doffset = 0;
    return(pkt);
}

/*
 * dctpoPacketFree -- returns a packet buffer to the heap
 */
void dctpoPacketFree(dctpPacket *pkt) {
#ifdef DEBUG
    uint32_t *tmp;

    tmp = ((uint32_t *)pkt) - 1;
    if (*tmp != DCTPSOCK_MAGICNO) {
	dctpoLog(DCTPLOG_ERR, "dctpoPacketFree: packet 0x%x already free\n", (uint32_t)pkt);
	bpoint("dctpoPacketFree");
	return;
    }
    *tmp = 0;
    free(tmp);
#else
    free(pkt);
#endif
}

/*
 * dctpoPacketCopyTo -- copies data from contiguous memory to the appdata
 * portion of a packet buffer.
 */
dctpPacket *dctpoPacketCopyTo(dctpPacket *pkt, uint_t offset, uint8_t *src, uint_t len) {
    /* Should check for enough room... */
    memcpy(&(pkt->appdata[offset]), src, len);
    return(pkt);
}

/*
 * dctpoPacketCopyFrom -- copies data from the appdata portion of a packet
 * buffer to contiguous memory.
 */
void dctpoPacketCopyFrom(dctpPacket *pkt, uint_t offset, uint8_t *dst, uint_t len) {
    /* Should check for reasonable input... */
    memcpy(dst, &(pkt->appdata[offset]), len);
}

/*
 * dctpoPacketCopyData -- copies data from the appdata portion of one packet
 * buffer to the appdata portion of another packet buffer.
 */
void dctpoPacketCopyData(dctpPacket *dpkt, dctpPacket *spkt) {
    memcpy(&(dpkt->appdata[dpkt->appdatalen]), spkt->appdata, spkt->appdatalen);
}

/*
 * dctpoPacketExtendDccpHdr - creates more room for the DCCP header in
 * a packet buffer.
 */
dctpPacket *dctpoPacketExtendDccpHdr(dctpPacket *pkt, uint_t len) {
    /* Should check for enough room... */
    pkt->dccphdr -= len;
    pkt->dccphdrlen += len;
    return(pkt);
}

/*
 * dctpoMemcmp -- compares to contiguous memory regions
 */
int dctpoMemcmp(void *s1, void *s2, uint_t len) {
    return(memcmp(s1, s2, len));
}

/*
 * Data for the NATv4 UDP socket
 */
static int natv4sock;
static uint16_t natv4port = DCTP_DEFAULTNATPORT;

/*
 * initSockets -- initializes the various (Unix) sockets for actual sending
 * and receiving of DCCP packets.  Called from dctpoInit.
 */
static void initSockets(void) {
    initUdpSockets();
}

/*
 * initUdpSockets -- initialize the UDP sockets and start the receiving threads
 */
static void initUdpSockets(void) {
    pthread_attr_t pattr;
    pthread_t thread;

    pthread_attr_init(&pattr);
    pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED);
    if ((natv4sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
	dctpoLog(DCTPLOG_ERR, "initUdpSockets: can't open NATv4 socket: %s\n", strerror(errno));
	return;
    }
    pthread_create(&thread, &pattr, natV4UdpRcv, NULL);
}

dctpSupportMutex_t readThreadLock;

/*
 * natV4UdpRcv -- thread for receiving NATv4 data
 */
static void *natV4UdpRcv(void *arg) {
    struct sockaddr_in laddr, raddr;
    dctpPacket *pkt;
    uint_t flen;
    int rlen;
    struct msghdr mhdr;
    struct iovec iov;
    struct cmsghdr *cmsg;
    struct in_pktinfo *pinfo;
    uint8_t *iphdr;
    uint8_t msgbuf[128];

    /* Allow getting/sending local address */
    rlen = 1;  /* Value for IP_PKTINFO */
    if (setsockopt(natv4sock, SOL_IP, IP_PKTINFO, &rlen, sizeof(rlen)) < 0) {
	dctpoLog(DCTPLOG_ERR, "natV4UdpRcv: can't set PKTINFO: %s\n", strerror(errno));
	return(NULL);
    }
    /* Bind local port/address */
    laddr.sin_family = AF_INET;
    laddr.sin_port = htons(natv4port);
    laddr.sin_addr.s_addr = htonl(INADDR_ANY);
    raddr.sin_family = AF_INET;
    if (bind(natv4sock, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) {
	dctpoLog(DCTPLOG_ERR, "natV4UdpRcv: can't bind socket: %s\n", strerror(errno));
	return(NULL);
    }
    /* Set up message header */
    mhdr.msg_name = &raddr;
    mhdr.msg_namelen = sizeof(struct sockaddr_in);
    mhdr.msg_iov = &iov;
    mhdr.msg_iovlen = 1;
    mhdr.msg_control = msgbuf;
    dctpoObjectLock(&readThreadLock);
    while (1) {
	if ((pkt = dctpoPacketMalloc(DCTP_MAXPKTSIZE)) != NULL) {
	    iov.iov_base = pkt->dccphdr;
	    iov.iov_len = pkt->buflen;
	    mhdr.msg_controllen = sizeof(msgbuf);
	    flen = sizeof(raddr);
	    if ((rlen = recvmsg(natv4sock, &mhdr, MSG_DONTWAIT)) < 0) {
		dctpoObjectUnlock(&readThreadLock);
		if ((rlen = recvmsg(natv4sock, &mhdr, 0)) < 0) {
		    dctpoLog(DCTPLOG_ERR, "natV4UdpRcv: error reading socket: %s\n",
			     strerror(errno));
		    dctpoPacketFree(pkt);
		    dctpoObjectLock(&readThreadLock);
		    continue;
		}
		dctpoObjectLock(&readThreadLock);
	    }
	    pinfo = NULL;
	    for (cmsg = CMSG_FIRSTHDR(&mhdr); cmsg != NULL;
		 cmsg = CMSG_NXTHDR(&mhdr, cmsg)) {
		if ((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
		    pinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
		    break;
		}
	    }
	    if (pinfo == NULL) {
		dctpoLog(DCTPLOG_ERR, "natV4UdpRcv: didn't find pktinfo struct\n");
		continue;
	    }
	    /* Reconstruct IP header */
	    pkt->iphdr -= 32;
	    iphdr = pkt->iphdr;
	    *((uint32_t *)iphdr) = pinfo->ipi_ifindex;   /* Add ifindex to header */
	    iphdr += 4;
	    *iphdr++ = 0x54;     /* IP version and header length */
	    *iphdr++ = 0;        /* TOS */
	    *((uint16_t *)iphdr) = dctpoNtohs(rlen + 28);  /* Total length */
	    iphdr += 2;
	    *((uint32_t *)iphdr) = 0;   /* Id, flags, foffse, don't check these later */
	    iphdr += 4;
	    *((uint32_t *)iphdr) = 0;   /* TTL, proto, hcsum, don't check these later */
	    iphdr += 4;
	    *((uint32_t *)iphdr) = pinfo->ipi_spec_dst.s_addr;  /* Source address */
	    iphdr += 4;
	    *((uint32_t *)iphdr) = pinfo->ipi_addr.s_addr;       /* Dest address */
	    iphdr += 4;                 /* Now do UDP header */
	    *((uint16_t *)iphdr) = raddr.sin_port;    /* Source port */
	    iphdr += 2;
	    *((uint16_t *)iphdr) = htons(natv4port);   /* Dest port */
	    iphdr += 2;
	    *((uint16_t *)iphdr) = htons(rlen + 8);    /* Length */
	    iphdr += 2;
	    *((uint16_t *)iphdr) = 1;                  /* Csum not problem if non-zero */
	    pkt->appdatalen = rlen;
	    dctpaNatV4Rcv(pkt);
	}
    }
}

/*
 * dctpoNatV4Xmt -- transmit a NATv4 encapsulated packet
 */
void dctpoNatV4Xmt(dctpSocket *sock, dctpPacket *pkt) {
    struct sockaddr_in addr;
    static pthread_mutex_t v4udpMutex = PTHREAD_MUTEX_INITIALIZER;

    addr.sin_family = AF_INET;
    addr.sin_port = htons(natv4port);
    addr.sin_addr.s_addr = ((dctpSockaddrV4 *)&(sock->remoteAddr))->sin_addr.s_addr;
    pthread_mutex_lock(&v4udpMutex);
    if (sendto(natv4sock, pkt->dccphdr, pkt->dccphdrlen + pkt->appdatalen, 0,
	       (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
	dctpoLog(DCTPLOG_ERR, "dctpoNatV4Xmt: can't send: %s\n", strerror(errno));
    }
    pthread_mutex_unlock(&v4udpMutex);
}

/*
 * dctpoHtons -- convert a short from host order to network order
 */
uint16_t dctpoHtons(uint16_t h) {
    return(htons(h));
}

/*
 * dctpoHtonl -- convert a long from host order to network order
 */
uint32_t dctpoHtonl(uint32_t h) {
    return(htonl(h));
}

/*
 * dctpoNtohs -- convert a short from network order to host order
 */
uint16_t dctpoNtohs(uint16_t ns) {
    return(ntohs(ns));
}

/*
 * dctpoNtohl -- convert a long from network order to host order
 */
uint32_t dctpoNtohl(uint32_t nl) {
    return(ntohl(nl));
}

#ifdef DEBUG
/*
 * bpoint -- debugging routine, place breakpoint here and then call from middle of code
 */
void bpoint(char *caller) {
    dctpoLog(DCTPLOG_DEBUG, "bpoint: called from %s\n", caller);
}
#endif

