summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorpiso <piso@FreeBSD.org>2009-02-07 18:49:42 +0000
committerpiso <piso@FreeBSD.org>2009-02-07 18:49:42 +0000
commitd34fad29235ccc0edbe6d08993a47cfe168f59d9 (patch)
treeb37eefe3826754719d7024fb68cb7d239167d799 /sys
parentfabbde3ecefd44749fcd5f1c5d4349bda2c61c54 (diff)
parent8af3d78dd29ac07a9612994c5bdf8ea196ee5599 (diff)
downloadFreeBSD-src-d34fad29235ccc0edbe6d08993a47cfe168f59d9.zip
FreeBSD-src-d34fad29235ccc0edbe6d08993a47cfe168f59d9.tar.gz
Add SCTP NAT support.
Submitted by: CAIA (http://caia.swin.edu.au)
Diffstat (limited to 'sys')
-rw-r--r--sys/modules/libalias/libalias/Makefile2
-rw-r--r--sys/netinet/ip_fw_nat.c4
-rw-r--r--sys/netinet/libalias/alias.c26
-rw-r--r--sys/netinet/libalias/alias_db.c61
-rw-r--r--sys/netinet/libalias/alias_local.h36
-rw-r--r--sys/netinet/libalias/alias_sctp.c2665
-rw-r--r--sys/netinet/libalias/alias_sctp.h201
-rw-r--r--sys/netinet/sctp_crc32.c4
-rw-r--r--sys/netinet/sctp_crc32.h2
9 files changed, 2994 insertions, 7 deletions
diff --git a/sys/modules/libalias/libalias/Makefile b/sys/modules/libalias/libalias/Makefile
index a3b5347..d1030c8 100644
--- a/sys/modules/libalias/libalias/Makefile
+++ b/sys/modules/libalias/libalias/Makefile
@@ -3,6 +3,6 @@
.PATH: ${.CURDIR}/../../../netinet/libalias
KMOD= libalias
-SRCS= alias.c alias_db.c alias_proxy.c alias_util.c alias_mod.c
+SRCS= alias.c alias_db.c alias_proxy.c alias_util.c alias_mod.c alias_sctp.c
.include <bsd.kmod.mk>
diff --git a/sys/netinet/ip_fw_nat.c b/sys/netinet/ip_fw_nat.c
index 54c91d9..6ba0412 100644
--- a/sys/netinet/ip_fw_nat.c
+++ b/sys/netinet/ip_fw_nat.c
@@ -326,6 +326,10 @@ ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
else
retval = LibAliasOut(t->lib, c,
mcl->m_len + M_TRAILINGSPACE(mcl));
+ if (retval == PKT_ALIAS_RESPOND) {
+ m->m_flags |= M_SKIP_FIREWALL;
+ retval = PKT_ALIAS_OK;
+ }
if (retval != PKT_ALIAS_OK &&
retval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
/* XXX - should i add some logging? */
diff --git a/sys/netinet/libalias/alias.c b/sys/netinet/libalias/alias.c
index 7d38578..9d80da9 100644
--- a/sys/netinet/libalias/alias.c
+++ b/sys/netinet/libalias/alias.c
@@ -115,6 +115,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
+#include <sys/sysctl.h>
#else
#include <sys/types.h>
#include <stdlib.h>
@@ -143,6 +144,17 @@ __FBSDID("$FreeBSD$");
#include "alias_mod.h"
#endif
+/*
+ * Define libalias SYSCTL Node
+ */
+#ifdef SYSCTL_NODE
+
+SYSCTL_DECL(_net_inet);
+SYSCTL_DECL(_net_inet_ip);
+SYSCTL_NODE(_net_inet_ip, OID_AUTO, alias, CTLFLAG_RW, NULL, "Libalias sysctl API");
+
+#endif
+
static __inline int
twowords(void *p)
{
@@ -1335,6 +1347,11 @@ LibAliasInLocked(struct libalias *la, char *ptr, int maxpacketsize)
case IPPROTO_TCP:
iresult = TcpAliasIn(la, pip);
break;
+#ifdef _KERNEL
+ case IPPROTO_SCTP:
+ iresult = SctpAlias(la, pip, SN_TO_LOCAL);
+ break;
+#endif
case IPPROTO_GRE: {
int error;
struct alias_data ad = {
@@ -1477,10 +1494,15 @@ LibAliasOutLocked(struct libalias *la, char *ptr, /* valid IP packet */
case IPPROTO_UDP:
iresult = UdpAliasOut(la, pip, maxpacketsize, create);
break;
- case IPPROTO_TCP:
+ case IPPROTO_TCP:
iresult = TcpAliasOut(la, pip, maxpacketsize, create);
break;
- case IPPROTO_GRE: {
+#ifdef _KERNEL
+ case IPPROTO_SCTP:
+ iresult = SctpAlias(la, pip, SN_TO_GLOBAL);
+ break;
+#endif
+ case IPPROTO_GRE: {
int error;
struct alias_data ad = {
.lnk = NULL,
diff --git a/sys/netinet/libalias/alias_db.c b/sys/netinet/libalias/alias_db.c
index 93b226e..489933f 100644
--- a/sys/netinet/libalias/alias_db.c
+++ b/sys/netinet/libalias/alias_db.c
@@ -411,6 +411,8 @@ static void ShowAliasStats(struct libalias *);
static int InitPacketAliasLog(struct libalias *);
static void UninitPacketAliasLog(struct libalias *);
+void SctpShowAliasStats(struct libalias *la);
+
static u_int
StartPointIn(struct in_addr alias_addr,
u_short alias_port,
@@ -489,15 +491,17 @@ ShowAliasStats(struct libalias *la)
/* Used for debugging */
if (la->logDesc) {
int tot = la->icmpLinkCount + la->udpLinkCount +
+ (la->sctpLinkCount>>1) + /* sctp counts half associations */
la->tcpLinkCount + la->pptpLinkCount +
la->protoLinkCount + la->fragmentIdLinkCount +
la->fragmentPtrLinkCount;
AliasLog(la->logDesc,
- "icmp=%u, udp=%u, tcp=%u, pptp=%u, proto=%u, frag_id=%u frag_ptr=%u / tot=%u",
+ "icmp=%u, udp=%u, tcp=%u, sctp=%u, pptp=%u, proto=%u, frag_id=%u frag_ptr=%u / tot=%u",
la->icmpLinkCount,
la->udpLinkCount,
la->tcpLinkCount,
+ la->sctpLinkCount>>1, /* sctp counts half associations */
la->pptpLinkCount,
la->protoLinkCount,
la->fragmentIdLinkCount,
@@ -508,6 +512,13 @@ ShowAliasStats(struct libalias *la)
}
}
+void SctpShowAliasStats(struct libalias *la)
+{
+
+ ShowAliasStats(la);
+}
+
+
/* Internal routines for finding, deleting and adding links
Port Allocation:
@@ -1278,6 +1289,11 @@ _FindLinkIn(struct libalias *la, struct in_addr dst_addr,
src_port = lnk->src_port;
}
+ if (link_type == LINK_SCTP) {
+ lnk->src_addr = src_addr;
+ lnk->src_port = src_port;
+ return(lnk);
+ }
lnk = ReLink(lnk,
src_addr, dst_addr, alias_addr,
src_port, dst_port, alias_port,
@@ -2277,10 +2293,13 @@ LibAliasRedirectPort(struct libalias *la, struct in_addr src_addr, u_short src_p
case IPPROTO_TCP:
link_type = LINK_TCP;
break;
+ case IPPROTO_SCTP:
+ link_type = LINK_SCTP;
+ break;
default:
#ifdef LIBALIAS_DEBUG
fprintf(stderr, "PacketAliasRedirectPort(): ");
- fprintf(stderr, "only TCP and UDP protocols allowed\n");
+ fprintf(stderr, "only SCTP, TCP and UDP protocols allowed\n");
#endif
lnk = NULL;
goto getout;
@@ -2496,6 +2515,9 @@ LibAliasInit(struct libalias *la)
LIST_INIT(&la->linkTableOut[i]);
for (i = 0; i < LINK_TABLE_IN_SIZE; i++)
LIST_INIT(&la->linkTableIn[i]);
+#ifdef _KERNEL
+ AliasSctpInit(la);
+#endif
LIBALIAS_LOCK_INIT(la);
LIBALIAS_LOCK(la);
} else {
@@ -2503,6 +2525,10 @@ LibAliasInit(struct libalias *la)
la->deleteAllLinks = 1;
CleanupAliasData(la);
la->deleteAllLinks = 0;
+#ifdef _KERNEL
+ AliasSctpTerm(la);
+ AliasSctpInit(la);
+#endif
}
la->aliasAddress.s_addr = INADDR_ANY;
@@ -2511,6 +2537,7 @@ LibAliasInit(struct libalias *la)
la->icmpLinkCount = 0;
la->udpLinkCount = 0;
la->tcpLinkCount = 0;
+ la->sctpLinkCount = 0;
la->pptpLinkCount = 0;
la->protoLinkCount = 0;
la->fragmentIdLinkCount = 0;
@@ -2539,6 +2566,9 @@ LibAliasUninit(struct libalias *la)
{
LIBALIAS_LOCK(la);
+#ifdef _KERNEL
+ AliasSctpTerm(la);
+#endif
la->deleteAllLinks = 1;
CleanupAliasData(la);
la->deleteAllLinks = 0;
@@ -2879,3 +2909,30 @@ LibAliasSetSkinnyPort(struct libalias *la, unsigned int port)
la->skinnyPort = port;
LIBALIAS_UNLOCK(la);
}
+
+/*
+ * Find the address to redirect incoming packets
+ */
+struct in_addr
+FindSctpRedirectAddress(struct libalias *la, struct sctp_nat_msg *sm)
+{
+ struct alias_link *lnk;
+ struct in_addr redir;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ lnk = FindLinkIn(la, sm->ip_hdr->ip_src, sm->ip_hdr->ip_dst,
+ sm->sctp_hdr->dest_port,sm->sctp_hdr->dest_port, LINK_SCTP, 1);
+ if (lnk != NULL) {
+ return(lnk->src_addr); /* port redirect */
+ } else {
+ redir = FindOriginalAddress(la,sm->ip_hdr->ip_dst);
+ if (redir.s_addr == la->aliasAddress.s_addr ||
+ redir.s_addr == la->targetAddress.s_addr) { /* No address found */
+ lnk = FindLinkIn(la, sm->ip_hdr->ip_src, sm->ip_hdr->ip_dst,
+ NO_DEST_PORT, 0, LINK_SCTP, 1);
+ if (lnk != NULL)
+ return(lnk->src_addr); /* redirect proto */
+ }
+ return(redir); /* address redirect */
+ }
+}
diff --git a/sys/netinet/libalias/alias_local.h b/sys/netinet/libalias/alias_local.h
index fc7d649..e201394 100644
--- a/sys/netinet/libalias/alias_local.h
+++ b/sys/netinet/libalias/alias_local.h
@@ -57,6 +57,10 @@
/* XXX: LibAliasSetTarget() uses this constant. */
#define INADDR_NONE 0xffffffff
+
+#include <netinet/libalias/alias_sctp.h>
+#else
+#include "alias_sctp.h"
#endif
/* Sizes of input and output link tables */
@@ -147,7 +151,29 @@ struct libalias {
struct in_addr true_addr; /* in network byte order. */
u_short true_port; /* in host byte order. */
+
+ /*
+ * sctp code support
+ */
+
+ /* counts associations that have progressed to UP and not yet removed */
+ int sctpLinkCount;
#ifdef _KERNEL
+ /* timing queue for keeping track of association timeouts */
+ struct sctp_nat_timer sctpNatTimer;
+
+ /* size of hash table used in this instance */
+ u_int sctpNatTableSize;
+
+/*
+ * local look up table sorted by l_vtag/l_port
+ */
+ LIST_HEAD(sctpNatTableL, sctp_nat_assoc) *sctpTableLocal;
+/*
+ * global look up table sorted by g_vtag/g_port
+ */
+ LIST_HEAD(sctpNatTableG, sctp_nat_assoc) *sctpTableGlobal;
+
/*
* avoid races in libalias: every public function has to use it.
*/
@@ -199,6 +225,14 @@ struct libalias {
/* Prototypes */
/*
+ * SctpFunction prototypes
+ *
+ */
+void AliasSctpInit(struct libalias *la);
+void AliasSctpTerm(struct libalias *la);
+int SctpAlias(struct libalias *la, struct ip *ip, int direction);
+
+/*
* We do not calculate TCP checksums when libalias is a kernel
* module, since it has no idea about checksum offloading.
* If TCP data has changed, then we just set checksum to zero,
@@ -264,6 +298,8 @@ struct in_addr
FindOriginalAddress(struct libalias *la, struct in_addr _alias_addr);
struct in_addr
FindAliasAddress(struct libalias *la, struct in_addr _original_addr);
+struct in_addr
+FindSctpRedirectAddress(struct libalias *la, struct sctp_nat_msg *sm);
/* External data access/modification */
int
diff --git a/sys/netinet/libalias/alias_sctp.c b/sys/netinet/libalias/alias_sctp.c
new file mode 100644
index 0000000..89dc979
--- /dev/null
+++ b/sys/netinet/libalias/alias_sctp.c
@@ -0,0 +1,2665 @@
+/**
+ * @file alias_sctp.c
+ * Copyright (c) 2008, Centre for Advanced Internet Architectures
+ * Swinburne University of Technology, Melbourne, Australia
+ * (CRICOS number 00111D).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The names of the authors, the "Centre for Advanced Internet Architectures"
+ * and "Swinburne University of Technology" may not be used to endorse
+ * or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS "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 AUTHORS OR CONTRIBUTORS 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.
+ *
+ * Alias_sctp forms part of the libalias kernel module to handle
+ * Network Address Translation (NAT) for the SCTP protocol.
+ *
+ * This software was developed by David A. Hayes and Jason But
+ *
+ * The design is outlined in CAIA technical report number 080618A
+ * (D. Hayes and J. But, "Alias_sctp Version 0.1: SCTP NAT implementation in IPFW")
+ *
+ * Development is part of the CAIA SONATA project,
+ * proposed by Jason But and Grenville Armitage:
+ * http://caia.swin.edu.au/urp/sonata/
+ *
+ *
+ * This project has been made possible in part by a grant from
+ * the Cisco University Research Program Fund at Community
+ * Foundation Silicon Valley.
+ *
+ */
+/** @mainpage
+ * Alias_sctp is part of the SONATA (http://caia.swin.edu.au/urp/sonata) project
+ * to develop and release a BSD licensed implementation of a Network Address
+ * Translation (NAT) module that supports the Stream Control Transmission
+ * Protocol (SCTP).
+ *
+ * Traditional address and port number look ups are inadequate for SCTP's
+ * operation due to both processing requirements and issues with multi-homing.
+ * Alias_sctp integrates with FreeBSD's ipfw/libalias NAT system.
+ *
+ * Version 0.2 features include:
+ * - Support for global multi-homing
+ * - Support for ASCONF modification from Internet Draft
+ * (draft-stewart-behave-sctpnat-04, R. Stewart and M. Tuexen, "Stream control
+ * transmission protocol (SCTP) network address translation," Jul. 2008) to
+ * provide support for multi-homed privately addressed hosts
+ * - Support for forwarding of T-flagged packets
+ * - Generation and delivery of AbortM/ErrorM packets upon detection of NAT
+ * collisions
+ * - Per-port forwarding rules
+ * - Dynamically controllable logging and statistics
+ * - Dynamic management of timers
+ * - Dynamic control of hash-table size
+ */
+
+/* $FreeBSD$ */
+
+#ifdef _KERNEL
+#include <machine/stdarg.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/syslog.h>
+#include <netinet/libalias/alias_sctp.h>
+#include <netinet/libalias/alias.h>
+#include <netinet/libalias/alias_local.h>
+#include <netinet/sctp_crc32.h>
+#include <machine/in_cksum.h>
+#else
+#include "alias_sctp.h"
+#include <arpa/inet.h>
+#include "alias.h"
+#include "alias_local.h"
+#include <netinet/sctp_crc32.h>
+#include <machine/in_cksum.h>
+#endif //#ifdef _KERNEL
+
+/* ----------------------------------------------------------------------
+ * FUNCTION PROTOTYPES
+ * ----------------------------------------------------------------------
+ */
+/* Packet Parsing Functions */
+static int sctp_PktParser(struct libalias *la, int direction, struct ip *pip,
+ struct sctp_nat_msg *sm, struct sctp_nat_assoc **passoc);
+static int GetAsconfVtags(struct libalias *la, struct sctp_nat_msg *sm,
+ uint32_t *l_vtag, uint32_t *g_vtag, int direction);
+static int IsASCONFack(struct libalias *la, struct sctp_nat_msg *sm, int direction);
+
+static void AddGlobalIPAddresses(struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int direction);
+static int Add_Global_Address_to_List(struct sctp_nat_assoc *assoc, struct sctp_GlobalAddress *G_addr);
+static void RmGlobalIPAddresses(struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int direction);
+static int IsADDorDEL(struct libalias *la, struct sctp_nat_msg *sm, int direction);
+
+/* State Machine Functions */
+static int ProcessSctpMsg(struct libalias *la, int direction, \
+ struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc);
+
+static int ID_process(struct libalias *la, int direction,\
+ struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm);
+static int INi_process(struct libalias *la, int direction,\
+ struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm);
+static int INa_process(struct libalias *la, int direction,\
+ struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm);
+static int UP_process(struct libalias *la, int direction,\
+ struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm);
+static int CL_process(struct libalias *la, int direction,\
+ struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm);
+static void TxAbortErrorM(struct libalias *la, struct sctp_nat_msg *sm,\
+ struct sctp_nat_assoc *assoc, int sndrply, int direction);
+
+/* Hash Table Functions */
+static struct sctp_nat_assoc*
+FindSctpLocal(struct libalias *la, struct in_addr l_addr, struct in_addr g_addr, uint32_t l_vtag, uint16_t l_port, uint16_t g_port);
+static struct sctp_nat_assoc*
+FindSctpGlobal(struct libalias *la, struct in_addr g_addr, uint32_t g_vtag, uint16_t g_port, uint16_t l_port, int *partial_match);
+static struct sctp_nat_assoc*
+FindSctpGlobalClash(struct libalias *la, struct sctp_nat_assoc *Cassoc);
+static struct sctp_nat_assoc*
+FindSctpLocalT(struct libalias *la, struct in_addr g_addr, uint32_t l_vtag, uint16_t g_port, uint16_t l_port);
+static struct sctp_nat_assoc*
+FindSctpGlobalT(struct libalias *la, struct in_addr g_addr, uint32_t g_vtag, uint16_t l_port, uint16_t g_port);
+
+static int AddSctpAssocLocal(struct libalias *la, struct sctp_nat_assoc *assoc, struct in_addr g_addr);
+static int AddSctpAssocGlobal(struct libalias *la, struct sctp_nat_assoc *assoc);
+static void RmSctpAssoc(struct libalias *la, struct sctp_nat_assoc *assoc);
+static void freeGlobalAddressList(struct sctp_nat_assoc *assoc);
+
+/* Timer Queue Functions */
+static void sctp_AddTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc);
+static void sctp_RmTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc);
+static void sctp_ResetTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc, int newexp);
+void sctp_CheckTimers(struct libalias *la);
+
+
+/* Logging Functions */
+static void logsctperror(char* errormsg, uint32_t vtag, int error, int direction);
+static void logsctpparse(int direction, struct sctp_nat_msg *sm);
+static void logsctpassoc(struct sctp_nat_assoc *assoc, char *s);
+static void logTimerQ(struct libalias *la);
+static void logSctpGlobal(struct libalias *la);
+static void logSctpLocal(struct libalias *la);
+#ifdef _KERNEL
+static void SctpAliasLog(const char *format, ...);
+#endif
+
+/** @defgroup external External code changes and modifications
+ *
+ * Some changes have been made to files external to alias_sctp.(c|h). These
+ * changes are primarily due to code needing to call static functions within
+ * those files or to perform extra functionality that can only be performed
+ * within these files.
+ */
+/** @ingroup external
+ * @brief Log current statistics for the libalias instance
+ *
+ * This function is defined in alias_db.c, since it calls static functions in
+ * this file
+ *
+ * Calls the higher level ShowAliasStats() in alias_db.c which logs all current
+ * statistics about the libalias instance - including SCTP statistics
+ *
+ * @param la Pointer to the libalias instance
+ */
+void SctpShowAliasStats(struct libalias *la);
+
+#ifdef _KERNEL
+
+MALLOC_DEFINE(M_SCTPNAT, "sctpnat", "sctp nat dbs");
+/* Use kernel allocator. */
+#ifdef _SYS_MALLOC_H_
+#define sn_malloc(x) malloc(x, M_SCTPNAT, M_NOWAIT|M_ZERO)
+#define sn_calloc(n,x) sn_malloc(x * n)
+#define sn_free(x) free(x, M_SCTPNAT)
+#endif// #ifdef _SYS_MALLOC_H_
+
+#else //#ifdef _KERNEL
+#define sn_malloc(x) malloc(x)
+#define sn_calloc(n, x) calloc(n, x)
+#define sn_free(x) free(x)
+
+#endif //#ifdef _KERNEL
+
+/** @defgroup packet_parser SCTP Packet Parsing
+ *
+ * Macros to:
+ * - Return pointers to the first and next SCTP chunks within an SCTP Packet
+ * - Define possible return values of the packet parsing process
+ * - SCTP message types for storing in the sctp_nat_msg structure @{
+ */
+
+#define SN_SCTP_FIRSTCHUNK(sctphead) (struct sctp_chunkhdr *)(((char *)sctphead) + sizeof(struct sctphdr))
+/**< Returns a pointer to the first chunk in an SCTP packet given a pointer to the SCTP header */
+
+#define SN_SCTP_NEXTCHUNK(chunkhead) (struct sctp_chunkhdr *)(((char *)chunkhead) + SCTP_SIZE32(ntohs(chunkhead->chunk_length)))
+/**< Returns a pointer to the next chunk in an SCTP packet given a pointer to the current chunk */
+
+#define SN_SCTP_NEXTPARAM(param) (struct sctp_paramhdr *)(((char *)param) + SCTP_SIZE32(ntohs(param->param_length)))
+/**< Returns a pointer to the next parameter in an SCTP packet given a pointer to the current parameter */
+
+#define SN_MIN_CHUNK_SIZE 4 /**< Smallest possible SCTP chunk size in bytes */
+#define SN_MIN_PARAM_SIZE 4 /**< Smallest possible SCTP param size in bytes */
+#define SN_VTAG_PARAM_SIZE 12 /**< Size of SCTP ASCONF vtag param in bytes */
+#define SN_ASCONFACK_PARAM_SIZE 8 /**< Size of SCTP ASCONF ACK param in bytes */
+
+/* Packet parsing return codes */
+#define SN_PARSE_OK 0 /**< Packet parsed for SCTP messages */
+#define SN_PARSE_ERROR_IPSHL 1 /**< Packet parsing error - IP and SCTP common header len */
+#define SN_PARSE_ERROR_AS_MALLOC 2 /**< Packet parsing error - assoc malloc */
+#define SN_PARSE_ERROR_CHHL 3 /**< Packet parsing error - Chunk header len */
+#define SN_PARSE_ERROR_DIR 4 /**< Packet parsing error - Direction */
+#define SN_PARSE_ERROR_VTAG 5 /**< Packet parsing error - Vtag */
+#define SN_PARSE_ERROR_CHUNK 6 /**< Packet parsing error - Chunk */
+#define SN_PARSE_ERROR_PORT 7 /**< Packet parsing error - Port=0 */
+#define SN_PARSE_ERROR_LOOKUP 8 /**< Packet parsing error - Lookup */
+#define SN_PARSE_ERROR_PARTIALLOOKUP 9 /**< Packet parsing error - partial lookup only found */
+#define SN_PARSE_ERROR_LOOKUP_ABORT 10 /**< Packet parsing error - Lookup - but abort packet */
+
+/* Alias_sctp performs its processing based on a number of key messages */
+#define SN_SCTP_ABORT 0x0000 /**< a packet containing an ABORT chunk */
+#define SN_SCTP_INIT 0x0001 /**< a packet containing an INIT chunk */
+#define SN_SCTP_INITACK 0x0002 /**< a packet containing an INIT-ACK chunk */
+#define SN_SCTP_SHUTCOMP 0x0010 /**< a packet containing a SHUTDOWN-COMPLETE chunk */
+#define SN_SCTP_SHUTACK 0x0020 /**< a packet containing a SHUTDOWN-ACK chunk */
+#define SN_SCTP_ASCONF 0x0100 /**< a packet containing an ASCONF chunk */
+#define SN_SCTP_ASCONFACK 0x0200 /**< a packet containing an ASCONF-ACK chunk */
+#define SN_SCTP_OTHER 0xFFFF /**< a packet containing a chunk that is not of interest */
+
+/** @}
+ * @defgroup state_machine SCTP NAT State Machine
+ *
+ * Defines the various states an association can be within the NAT @{
+ */
+#define SN_ID 0x0000 /**< Idle state */
+#define SN_INi 0x0010 /**< Initialising, waiting for InitAck state */
+#define SN_INa 0x0020 /**< Initialising, waiting for AddIpAck state */
+#define SN_UP 0x0100 /**< Association in UP state */
+#define SN_CL 0x1000 /**< Closing state */
+#define SN_RM 0x2000 /**< Removing state */
+
+/** @}
+ * @defgroup Logging Logging Functionality
+ *
+ * Define various log levels and a macro to call specified log functions only if
+ * the current log level (sysctl_log_level) matches the specified level @{
+ */
+#define SN_LOG_LOW 0
+#define SN_LOG_EVENT 1
+#define SN_LOG_INFO 2
+#define SN_LOG_DETAIL 3
+#define SN_LOG_DEBUG 4
+#define SN_LOG_DEBUG_MAX 5
+
+#define SN_LOG(level, action) if (sysctl_log_level >= level) { action; } /**< Perform log action ONLY if the current log level meets the specified log level */
+
+/** @}
+ * @defgroup Hash Hash Table Macros and Functions
+ *
+ * Defines minimum/maximum/default values for the hash table size @{
+ */
+#define SN_MIN_HASH_SIZE 101 /**< Minimum hash table size (set to stop users choosing stupid values) */
+#define SN_MAX_HASH_SIZE 1000001 /**< Maximum hash table size (NB must be less than max int) */
+#define SN_DEFAULT_HASH_SIZE 2003 /**< A reasonable default size for the hash tables */
+
+#define SN_LOCAL_TBL 0x01 /**< assoc in local table */
+#define SN_GLOBAL_TBL 0x02 /**< assoc in global table */
+#define SN_BOTH_TBL 0x03 /**< assoc in both tables */
+#define SN_WAIT_TOLOCAL 0x10 /**< assoc waiting for TOLOCAL asconf ACK*/
+#define SN_WAIT_TOGLOBAL 0x20 /**< assoc waiting for TOLOCAL asconf ACK*/
+#define SN_NULL_TBL 0x00 /**< assoc in No table */
+#define SN_MAX_GLOBAL_ADDRESSES 100 /**< absolute maximum global address count*/
+
+#define SN_ADD_OK 0 /**< Association added to the table */
+#define SN_ADD_CLASH 1 /**< Clash when trying to add the assoc. info to the table */
+
+#define SN_TABLE_HASH(vtag, port, size) (((u_int) vtag + (u_int) port) % (u_int) size) /**< Calculate the hash table lookup position */
+
+/** @}
+ * @defgroup Timer Timer Queue Macros and Functions
+ *
+ * Timer macros set minimum/maximum timeout values and calculate timer expiry
+ * times for the provided libalias instance @{
+ */
+#define SN_MIN_TIMER 1
+#define SN_MAX_TIMER 600
+#define SN_TIMER_QUEUE_SIZE SN_MAX_TIMER+2
+
+#define SN_I_T(la) (la->timeStamp + sysctl_init_timer) /**< INIT State expiration time in seconds */
+#define SN_U_T(la) (la->timeStamp + sysctl_up_timer) /**< UP State expiration time in seconds */
+#define SN_C_T(la) (la->timeStamp + sysctl_shutdown_timer) /**< CL State expiration time in seconds */
+#define SN_X_T(la) (la->timeStamp + sysctl_holddown_timer) /**< Wait after a shutdown complete in seconds */
+
+/** @}
+ * @defgroup sysctl SysCtl Variable and callback function declarations
+ *
+ * Sysctl variables to modify NAT functionality in real-time along with associated functions
+ * to manage modifications to the sysctl variables @{
+ */
+
+/* Callbacks */
+int sysctl_chg_loglevel(SYSCTL_HANDLER_ARGS);
+int sysctl_chg_timer(SYSCTL_HANDLER_ARGS);
+int sysctl_chg_hashtable_size(SYSCTL_HANDLER_ARGS);
+int sysctl_chg_error_on_ootb(SYSCTL_HANDLER_ARGS);
+int sysctl_chg_accept_global_ootb_addip(SYSCTL_HANDLER_ARGS);
+int sysctl_chg_initialising_chunk_proc_limit(SYSCTL_HANDLER_ARGS);
+int sysctl_chg_chunk_proc_limit(SYSCTL_HANDLER_ARGS);
+int sysctl_chg_param_proc_limit(SYSCTL_HANDLER_ARGS);
+int sysctl_chg_track_global_addresses(SYSCTL_HANDLER_ARGS);
+
+/* Sysctl variables */
+/** @brief net.inet.ip.alias.sctp.log_level */
+static u_int sysctl_log_level = 0; /**< Stores the current level of logging */
+/** @brief net.inet.ip.alias.sctp.init_timer */
+static u_int sysctl_init_timer = 15; /**< Seconds to hold an association in the table waiting for an INIT-ACK or AddIP-ACK */
+/** @brief net.inet.ip.alias.sctp.up_timer */
+static u_int sysctl_up_timer = 300; /**< Seconds to hold an association in the table while no packets are transmitted */
+/** @brief net.inet.ip.alias.sctp.shutdown_timer */
+static u_int sysctl_shutdown_timer = 15; /**< Seconds to hold an association in the table waiting for a SHUTDOWN-COMPLETE */
+/** @brief net.inet.ip.alias.sctp.holddown_timer */
+static u_int sysctl_holddown_timer = 0; /**< Seconds to hold an association in the table after it has been shutdown (to allow for lost SHUTDOWN-COMPLETEs) */
+/** @brief net.inet.ip.alias.sctp.hashtable_size */
+static u_int sysctl_hashtable_size = SN_DEFAULT_HASH_SIZE; /**< Sets the hash table size for any NEW NAT instances (existing instances retain their existing Hash Table */
+/** @brief net.inet.ip.alias.sctp.error_on_ootb */
+static u_int sysctl_error_on_ootb = 1; /**< NAT response to receipt of OOTB packet
+ (0 - No response, 1 - NAT will send ErrorM only to local side,
+ 2 - NAT will send local ErrorM and global ErrorM if there was a partial association match
+ 3 - NAT will send ErrorM to both local and global) */
+/** @brief net.inet.ip.alias.sctp.accept_global_ootb_addip */
+static u_int sysctl_accept_global_ootb_addip = 0; /**<NAT responset to receipt of global OOTB AddIP (0 - No response, 1 - NAT will accept OOTB global AddIP messages for processing (Security risk)) */
+/** @brief net.inet.ip.alias.sctp.initialising_chunk_proc_limit */
+static u_int sysctl_initialising_chunk_proc_limit = 2; /**< A limit on the number of chunks that should be searched if there is no matching association (DoS prevention) */
+/** @brief net.inet.ip.alias.sctp.param_proc_limit */
+static u_int sysctl_chunk_proc_limit = 5; /**< A limit on the number of chunks that should be searched (DoS prevention) */
+/** @brief net.inet.ip.alias.sctp.param_proc_limit */
+static u_int sysctl_param_proc_limit = 25; /**< A limit on the number of parameters (in chunks) that should be searched (DoS prevention) */
+/** @brief net.inet.ip.alias.sctp.track_global_addresses */
+static u_int sysctl_track_global_addresses = 0; /**< Configures the global address tracking option within the NAT (0 - Global tracking is disabled, > 0 - enables tracking but limits the number of global IP addresses to this value)
+ If set to >=1 the NAT will track that many global IP addresses. This may reduce look up table conflicts, but increases processing */
+
+#define SN_NO_ERROR_ON_OOTB 0 /**< Send no errorM on out of the blue packets */
+#define SN_LOCAL_ERROR_ON_OOTB 1 /**< Send only local errorM on out of the blue packets */
+#define SN_LOCALandPARTIAL_ERROR_ON_OOTB 2 /**< Send local errorM and global errorM for out of the blue packets only if partial match found */
+#define SN_ERROR_ON_OOTB 3 /**< Send errorM on out of the blue packets */
+
+#ifdef SYSCTL_NODE
+
+SYSCTL_DECL(_net_inet);
+SYSCTL_DECL(_net_inet_ip);
+SYSCTL_DECL(_net_inet_ip_alias);
+
+SYSCTL_NODE(_net_inet_ip_alias, OID_AUTO, sctp, CTLFLAG_RW, NULL, "SCTP NAT");
+
+SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, log_level, CTLTYPE_UINT | CTLFLAG_RW,
+ &sysctl_log_level, 0, sysctl_chg_loglevel, "IU",
+ "Level of detail (0 - default, 1 - event, 2 - info, 3 - detail, 4 - debug, 5 - max debug)");
+SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, init_timer, CTLTYPE_UINT | CTLFLAG_RW,
+ &sysctl_init_timer, 0, sysctl_chg_timer, "IU",
+ "Timeout value (s) while waiting for (INIT-ACK|AddIP-ACK)");
+SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, up_timer, CTLTYPE_UINT | CTLFLAG_RW,
+ &sysctl_up_timer, 0, sysctl_chg_timer, "IU",
+ "Timeout value (s) to keep an association up with no traffic");
+SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, shutdown_timer, CTLTYPE_UINT | CTLFLAG_RW,
+ &sysctl_shutdown_timer, 0, sysctl_chg_timer, "IU",
+ "Timeout value (s) while waiting for SHUTDOWN-COMPLETE");
+SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, holddown_timer, CTLTYPE_UINT | CTLFLAG_RW,
+ &sysctl_holddown_timer, 0, sysctl_chg_timer, "IU",
+ "Hold association in table for this many seconds after receiving a SHUTDOWN-COMPLETE");
+SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, hashtable_size, CTLTYPE_UINT | CTLFLAG_RW,
+ &sysctl_hashtable_size, 0, sysctl_chg_hashtable_size, "IU",
+ "Size of hash tables used for NAT lookups (100 < prime_number > 1000001)");
+SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, error_on_ootb, CTLTYPE_UINT | CTLFLAG_RW,
+ &sysctl_error_on_ootb, 0, sysctl_chg_error_on_ootb, "IU",
+ "ErrorM sent on receipt of ootb packet:\n\t0 - none,\n\t1 - to local only,\n\t2 - to local and global if a partial association match,\n\t3 - to local and global (DoS risk)");
+SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, accept_global_ootb_addip, CTLTYPE_UINT | CTLFLAG_RW,
+ &sysctl_accept_global_ootb_addip, 0, sysctl_chg_accept_global_ootb_addip, "IU",
+ "NAT response to receipt of global OOTB AddIP:\n\t0 - No response,\n\t1 - NAT will accept OOTB global AddIP messages for processing (Security risk)");
+SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, initialising_chunk_proc_limit, CTLTYPE_UINT | CTLFLAG_RW,
+ &sysctl_initialising_chunk_proc_limit, 0, sysctl_chg_initialising_chunk_proc_limit, "IU",
+ "Number of chunks that should be processed if there is no current association found:\n\t > 0 (A high value is a DoS risk)");
+SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, chunk_proc_limit, CTLTYPE_UINT | CTLFLAG_RW,
+ &sysctl_chunk_proc_limit, 0, sysctl_chg_chunk_proc_limit, "IU",
+ "Number of chunks that should be processed to find key chunk:\n\t>= initialising_chunk_proc_limit (A high value is a DoS risk)");
+SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, param_proc_limit, CTLTYPE_UINT | CTLFLAG_RW,
+ &sysctl_param_proc_limit, 0, sysctl_chg_param_proc_limit, "IU",
+ "Number of parameters (in a chunk) that should be processed to find key parameters:\n\t> 1 (A high value is a DoS risk)");
+SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, track_global_addresses, CTLTYPE_UINT | CTLFLAG_RW,
+ &sysctl_track_global_addresses, 0, sysctl_chg_track_global_addresses, "IU",
+ "Configures the global address tracking option within the NAT:\n\t0 - Global tracking is disabled,\n\t> 0 - enables tracking but limits the number of global IP addresses to this value");
+
+#endif /* SYSCTL_NODE */
+
+/** @}
+ * @ingroup sysctl
+ * @brief sysctl callback for changing net.inet.ip.fw.sctp.log_level
+ *
+ * Updates the variable sysctl_log_level to the provided value and ensures
+ * it is in the valid range (SN_LOG_LOW -> SN_LOG_DEBUG)
+ */
+int sysctl_chg_loglevel(SYSCTL_HANDLER_ARGS)
+{
+ u_int level = *(u_int *)arg1;
+ int error;
+
+ error = sysctl_handle_int(oidp, &level, 0, req);
+ if (error) return (error);
+
+ sysctl_log_level = (level > SN_LOG_DEBUG_MAX)?(SN_LOG_DEBUG_MAX):(level);
+ sysctl_log_level = (level < SN_LOG_LOW)?(SN_LOG_LOW):(level);
+
+ return (0);
+}
+
+/** @ingroup sysctl
+ * @brief sysctl callback for changing net.inet.ip.fw.sctp.(init_timer|up_timer|shutdown_timer)
+ *
+ * Updates the timer-based sysctl variables. The new values are sanity-checked
+ * to make sure that they are within the range SN_MIN_TIMER-SN_MAX_TIMER. The
+ * holddown timer is allowed to be 0
+ */
+int sysctl_chg_timer(SYSCTL_HANDLER_ARGS)
+{
+ u_int timer = *(u_int *)arg1;
+ int error;
+
+ error = sysctl_handle_int(oidp, &timer, 0, req);
+ if (error) return (error);
+
+ timer = (timer > SN_MAX_TIMER)?(SN_MAX_TIMER):(timer);
+
+ if (((u_int *)arg1) != &sysctl_holddown_timer)
+ {
+ timer = (timer < SN_MIN_TIMER)?(SN_MIN_TIMER):(timer);
+ }
+
+ *(u_int *)arg1 = timer;
+
+ return (0);
+}
+
+/** @ingroup sysctl
+ * @brief sysctl callback for changing net.inet.ip.alias.sctp.hashtable_size
+ *
+ * Updates the hashtable_size sysctl variable. The new value should be a prime
+ * number. We sanity check to ensure that the size is within the range
+ * SN_MIN_HASH_SIZE-SN_MAX_HASH_SIZE. We then check the provided number to see
+ * if it is prime. We approximate by checking that (2,3,5,7,11) are not factors,
+ * incrementing the user provided value until we find a suitable number.
+ */
+int sysctl_chg_hashtable_size(SYSCTL_HANDLER_ARGS)
+{
+ u_int size = *(u_int *)arg1;
+ int error;
+
+ error = sysctl_handle_int(oidp, &size, 0, req);
+ if (error) return (error);
+
+ size = (size < SN_MIN_HASH_SIZE)?(SN_MIN_HASH_SIZE):((size > SN_MAX_HASH_SIZE)?(SN_MAX_HASH_SIZE):(size));
+
+ size |= 0x00000001; /* make odd */
+
+ for(;(((size % 3) == 0) || ((size % 5) == 0) || ((size % 7) == 0) || ((size % 11) == 0)); size+=2);
+ sysctl_hashtable_size = size;
+
+ return (0);
+}
+
+/** @ingroup sysctl
+ * @brief sysctl callback for changing net.inet.ip.alias.sctp.error_on_ootb
+ *
+ * Updates the error_on_clash sysctl variable.
+ * If set to 0, no ErrorM will be sent if there is a look up table clash
+ * If set to 1, an ErrorM is sent only to the local side
+ * If set to 2, an ErrorM is sent to the local side and global side if there is
+ * a partial association match
+ * If set to 3, an ErrorM is sent to both local and global sides (DoS) risk.
+ */
+int sysctl_chg_error_on_ootb(SYSCTL_HANDLER_ARGS)
+{
+ u_int flag = *(u_int *)arg1;
+ int error;
+
+ error = sysctl_handle_int(oidp, &flag, 0, req);
+ if (error) return (error);
+
+ sysctl_error_on_ootb = (flag > SN_ERROR_ON_OOTB) ? SN_ERROR_ON_OOTB: flag;
+
+ return (0);
+}
+
+/** @ingroup sysctl
+ * @brief sysctl callback for changing net.inet.ip.alias.sctp.accept_global_ootb_addip
+ *
+ * If set to 1 the NAT will accept ootb global addip messages for processing (Security risk)
+ * Default is 0, only responding to local ootb AddIP messages
+ */
+int sysctl_chg_accept_global_ootb_addip(SYSCTL_HANDLER_ARGS)
+{
+ u_int flag = *(u_int *)arg1;
+ int error;
+
+ error = sysctl_handle_int(oidp, &flag, 0, req);
+ if (error) return (error);
+
+ sysctl_accept_global_ootb_addip = (flag == 1) ? 1: 0;
+
+ return (0);
+}
+
+/** @ingroup sysctl
+ * @brief sysctl callback for changing net.inet.ip.alias.sctp.initialising_chunk_proc_limit
+ *
+ * Updates the initialising_chunk_proc_limit sysctl variable. Number of chunks
+ * that should be processed if there is no current association found: > 0 (A
+ * high value is a DoS risk)
+ */
+int sysctl_chg_initialising_chunk_proc_limit(SYSCTL_HANDLER_ARGS)
+{
+ u_int proclimit = *(u_int *)arg1;
+ int error;
+
+ error = sysctl_handle_int(oidp, &proclimit, 0, req);
+ if (error) return (error);
+
+ sysctl_initialising_chunk_proc_limit = (proclimit < 1) ? 1: proclimit;
+ sysctl_chunk_proc_limit =
+ (sysctl_chunk_proc_limit < sysctl_initialising_chunk_proc_limit) ? sysctl_initialising_chunk_proc_limit : sysctl_chunk_proc_limit;
+
+ return (0);
+}
+
+/** @ingroup sysctl
+ * @brief sysctl callback for changing net.inet.ip.alias.sctp.chunk_proc_limit
+ *
+ * Updates the chunk_proc_limit sysctl variable.
+ * Number of chunks that should be processed to find key chunk:
+ * >= initialising_chunk_proc_limit (A high value is a DoS risk)
+ */
+int sysctl_chg_chunk_proc_limit(SYSCTL_HANDLER_ARGS)
+{
+ u_int proclimit = *(u_int *)arg1;
+ int error;
+
+ error = sysctl_handle_int(oidp, &proclimit, 0, req);
+ if (error) return (error);
+
+ sysctl_chunk_proc_limit =
+ (proclimit < sysctl_initialising_chunk_proc_limit) ? sysctl_initialising_chunk_proc_limit : proclimit;
+
+ return (0);
+}
+
+
+/** @ingroup sysctl
+ * @brief sysctl callback for changing net.inet.ip.alias.sctp.param_proc_limit
+ *
+ * Updates the param_proc_limit sysctl variable.
+ * Number of parameters that should be processed to find key parameters:
+ * > 1 (A high value is a DoS risk)
+ */
+int sysctl_chg_param_proc_limit(SYSCTL_HANDLER_ARGS)
+{
+ u_int proclimit = *(u_int *)arg1;
+ int error;
+
+ error = sysctl_handle_int(oidp, &proclimit, 0, req);
+ if (error) return (error);
+
+ sysctl_param_proc_limit =
+ (proclimit < 2) ? 2 : proclimit;
+
+ return (0);
+}
+
+/** @ingroup sysctl
+ * @brief sysctl callback for changing net.inet.ip.alias.sctp.track_global_addresses
+ *
+ *Configures the global address tracking option within the NAT (0 - Global
+ *tracking is disabled, > 0 - enables tracking but limits the number of global
+ *IP addresses to this value)
+ */
+int sysctl_chg_track_global_addresses(SYSCTL_HANDLER_ARGS)
+{
+ u_int num_to_track = *(u_int *)arg1;
+ int error;
+
+ error = sysctl_handle_int(oidp, &num_to_track, 0, req);
+ if (error) return (error);
+
+ sysctl_track_global_addresses = (num_to_track > SN_MAX_GLOBAL_ADDRESSES) ? SN_MAX_GLOBAL_ADDRESSES : num_to_track;
+
+ return (0);
+}
+
+
+/* ----------------------------------------------------------------------
+ * CODE BEGINS HERE
+ * ----------------------------------------------------------------------
+ */
+/**
+ * @brief Initialises the SCTP NAT Implementation
+ *
+ * Creates the look-up tables and the timer queue and initialises all state
+ * variables
+ *
+ * @param la Pointer to the relevant libalias instance
+ */
+void AliasSctpInit(struct libalias *la)
+{
+ /* Initialise association tables*/
+ int i;
+ la->sctpNatTableSize = sysctl_hashtable_size;
+ SN_LOG(SN_LOG_EVENT,
+ SctpAliasLog("Initialising SCTP NAT Instance (hash_table_size:%d)\n", la->sctpNatTableSize));
+ la->sctpTableLocal = sn_calloc(la->sctpNatTableSize, sizeof(struct sctpNatTableL));
+ la->sctpTableGlobal = sn_calloc(la->sctpNatTableSize, sizeof(struct sctpNatTableG));
+ la->sctpNatTimer.TimerQ = sn_calloc(SN_TIMER_QUEUE_SIZE, sizeof(struct sctpTimerQ));
+ /* Initialise hash table */
+ for (i = 0; i < la->sctpNatTableSize; i++) {
+ LIST_INIT(&la->sctpTableLocal[i]);
+ LIST_INIT(&la->sctpTableGlobal[i]);
+ }
+
+ /* Initialise circular timer Q*/
+ for (i = 0; i < SN_TIMER_QUEUE_SIZE; i++)
+ LIST_INIT(&la->sctpNatTimer.TimerQ[i]);
+#ifdef _KERNEL
+ la->sctpNatTimer.loc_time=time_uptime; /* la->timeStamp is not set yet */
+#else
+ la->sctpNatTimer.loc_time=la->timeStamp;
+#endif
+ la->sctpNatTimer.cur_loc = 0;
+ la->sctpLinkCount = 0;
+}
+
+/**
+ * @brief Cleans-up the SCTP NAT Implementation prior to unloading
+ *
+ * Removes all entries from the timer queue, freeing associations as it goes.
+ * We then free memory allocated to the look-up tables and the time queue
+ *
+ * NOTE: We do not need to traverse the look-up tables as each association
+ * will always have an entry in the timer queue, freeing this memory
+ * once will free all memory allocated to entries in the look-up tables
+ *
+ * @param la Pointer to the relevant libalias instance
+ */
+void AliasSctpTerm(struct libalias *la)
+{
+ struct sctp_nat_assoc *assoc1, *assoc2;
+ int i;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ SN_LOG(SN_LOG_EVENT,
+ SctpAliasLog("Removing SCTP NAT Instance\n"));
+ for (i = 0; i < SN_TIMER_QUEUE_SIZE; i++) {
+ assoc1 = LIST_FIRST(&la->sctpNatTimer.TimerQ[i]);
+ while (assoc1 != NULL) {
+ freeGlobalAddressList(assoc1);
+ assoc2 = LIST_NEXT(assoc1, timer_Q);
+ sn_free(assoc1);
+ assoc1 = assoc2;
+ }
+ }
+
+ sn_free(la->sctpTableLocal);
+ sn_free(la->sctpTableGlobal);
+ sn_free(la->sctpNatTimer.TimerQ);
+}
+
+/**
+ * @brief Handles SCTP packets passed from libalias
+ *
+ * This function needs to actually NAT/drop packets and possibly create and
+ * send AbortM or ErrorM packets in response. The process involves:
+ * - Validating the direction parameter passed by the caller
+ * - Checking and handling any expired timers for the NAT
+ * - Calling sctp_PktParser() to parse the packet
+ * - Call ProcessSctpMsg() to decide the appropriate outcome and to update
+ * the NAT tables
+ * - Based on the return code either:
+ * - NAT the packet
+ * - Construct and send an ErrorM|AbortM packet
+ * - Mark the association for removal from the tables
+ * - Potentially remove the association from all lookup tables
+ * - Return the appropriate result to libalias
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param pip Pointer to IP packet to process
+ * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
+ *
+ * @return PKT_ALIAS_OK | PKT_ALIAS_IGNORE | PKT_ALIAS_ERROR
+ */
+int
+SctpAlias(struct libalias *la, struct ip *pip, int direction)
+{
+ int rtnval;
+ struct sctp_nat_msg msg;
+ struct sctp_nat_assoc *assoc = NULL;
+
+ if ((direction != SN_TO_LOCAL) && (direction != SN_TO_GLOBAL)) {
+ SctpAliasLog("ERROR: Invalid direction\n");
+ return(PKT_ALIAS_ERROR);
+ }
+
+ sctp_CheckTimers(la); /* Check timers */
+
+ /* Parse the packet */
+ rtnval = sctp_PktParser(la, direction, pip, &msg, &assoc); //using *char (change to mbuf when get code from paolo)
+ switch (rtnval) {
+ case SN_PARSE_OK:
+ break;
+ case SN_PARSE_ERROR_CHHL:
+ /* Not an error if there is a chunk length parsing error and this is a fragmented packet */
+ if (ntohs(pip->ip_off) & IP_MF) {
+ rtnval = SN_PARSE_OK;
+ break;
+ }
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("SN_PARSE_ERROR", msg.sctp_hdr->v_tag, rtnval, direction));
+ return(PKT_ALIAS_ERROR);
+ case SN_PARSE_ERROR_PARTIALLOOKUP:
+ if (sysctl_error_on_ootb > SN_LOCALandPARTIAL_ERROR_ON_OOTB) {
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("SN_PARSE_ERROR", msg.sctp_hdr->v_tag, rtnval, direction));
+ return(PKT_ALIAS_ERROR);
+ }
+ case SN_PARSE_ERROR_LOOKUP:
+ if (sysctl_error_on_ootb == SN_ERROR_ON_OOTB ||
+ (sysctl_error_on_ootb == SN_LOCALandPARTIAL_ERROR_ON_OOTB && direction == SN_TO_LOCAL) ||
+ (sysctl_error_on_ootb == SN_LOCAL_ERROR_ON_OOTB && direction == SN_TO_GLOBAL)) {
+ TxAbortErrorM(la, &msg, assoc, SN_REFLECT_ERROR, direction); /*NB assoc=NULL */
+ return(PKT_ALIAS_RESPOND);
+ }
+ default:
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("SN_PARSE_ERROR", msg.sctp_hdr->v_tag, rtnval, direction));
+ return(PKT_ALIAS_ERROR);
+ }
+
+ SN_LOG(SN_LOG_DETAIL,
+ logsctpassoc(assoc, "*");
+ logsctpparse(direction, &msg);
+ );
+
+ /* Process the SCTP message */
+ rtnval = ProcessSctpMsg(la, direction, &msg, assoc);
+
+ SN_LOG(SN_LOG_DEBUG_MAX,
+ logsctpassoc(assoc, "-");
+ logSctpLocal(la);
+ logSctpGlobal(la);
+ );
+ SN_LOG(SN_LOG_DEBUG, logTimerQ(la));
+
+ switch(rtnval){
+ case SN_NAT_PKT:
+ switch(direction) {
+ case SN_TO_LOCAL:
+ DifferentialChecksum(&(msg.ip_hdr->ip_sum),
+ &(assoc->l_addr), &(msg.ip_hdr->ip_dst), 2);
+ msg.ip_hdr->ip_dst = assoc->l_addr; /* change dst address to local address*/
+ break;
+ case SN_TO_GLOBAL:
+ DifferentialChecksum(&(msg.ip_hdr->ip_sum),
+ &(assoc->a_addr), &(msg.ip_hdr->ip_src), 2);
+ msg.ip_hdr->ip_src = assoc->a_addr; /* change src to alias addr*/
+ break;
+ default:
+ rtnval = SN_DROP_PKT; /* shouldn't get here, but if it does drop packet */
+ SN_LOG(SN_LOG_LOW, logsctperror("ERROR: Invalid direction", msg.sctp_hdr->v_tag, rtnval, direction));
+ break;
+ }
+ break;
+ case SN_DROP_PKT:
+ SN_LOG(SN_LOG_DETAIL, logsctperror("SN_DROP_PKT", msg.sctp_hdr->v_tag, rtnval, direction));
+ break;
+ case SN_REPLY_ABORT:
+ case SN_REPLY_ERROR:
+ case SN_SEND_ABORT:
+ TxAbortErrorM(la, &msg, assoc, rtnval, direction);
+ break;
+ default:
+ // big error, remove association and go to idle and write log messages
+ SN_LOG(SN_LOG_LOW, logsctperror("SN_PROCESSING_ERROR", msg.sctp_hdr->v_tag, rtnval, direction));
+ assoc->state=SN_RM;/* Mark for removal*/
+ break;
+ }
+
+ /* Remove association if tagged for removal */
+ if (assoc->state == SN_RM) {
+ if (assoc->TableRegister) {
+ sctp_RmTimeOut(la, assoc);
+ RmSctpAssoc(la, assoc);
+ }
+ LIBALIAS_LOCK_ASSERT(la);
+ freeGlobalAddressList(assoc);
+ sn_free(assoc);
+ }
+ switch(rtnval) {
+ case SN_NAT_PKT:
+ return(PKT_ALIAS_OK);
+ case SN_SEND_ABORT:
+ return(PKT_ALIAS_OK);
+ case SN_REPLY_ABORT:
+ case SN_REPLY_ERROR:
+ case SN_REFLECT_ERROR:
+ return(PKT_ALIAS_RESPOND);
+ case SN_DROP_PKT:
+ default:
+ return(PKT_ALIAS_ERROR);
+ }
+}
+
+/**
+ * @brief Send an AbortM or ErrorM
+ *
+ * We construct the new SCTP packet to send in place of the existing packet we
+ * have been asked to NAT. This function can only be called if the original
+ * packet was successfully parsed as a valid SCTP packet.
+ *
+ * An AbortM (without cause) packet is the smallest SCTP packet available and as
+ * such there is always space in the existing packet buffer to fit the AbortM
+ * packet. An ErrorM packet is 4 bytes longer than the (the error cause is not
+ * optional). An ErrorM is sent in response to an AddIP when the Vtag/address
+ * combination, if added, will produce a conflict in the association look up
+ * tables. It may also be used for an unexpected packet - a packet with no
+ * matching association in the NAT table and we are requesting an AddIP so we
+ * can add it. The smallest valid SCTP packet while the association is in an
+ * up-state is a Heartbeat packet, which is big enough to be transformed to an
+ * ErrorM.
+ *
+ * We create a temporary character array to store the packet as we are constructing
+ * it. We then populate the array with appropriate values based on:
+ * - Packet type (AbortM | ErrorM)
+ * - Initial packet direction (SN_TO_LOCAL | SN_TO_GLOBAL)
+ * - NAT response (Send packet | Reply packet)
+ *
+ * Once complete, we copy the contents of the temporary packet over the original
+ * SCTP packet we were asked to NAT
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param sm Pointer to sctp message information
+ * @param assoc Pointer to current association details
+ * @param sndrply SN_SEND_ABORT | SN_REPLY_ABORT | SN_REPLY_ERROR
+ * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
+ */
+static void
+TxAbortErrorM(struct libalias *la, struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int sndrply, int direction)
+{
+ int sctp_size = sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_error_cause);
+ int ip_size = sizeof(struct ip) + sctp_size;
+ int include_error_cause = 1;
+ char tmp_ip[ip_size];
+
+ if (ntohs(sm->ip_hdr->ip_len) < ip_size) { /* short packet, cannot send error cause */
+ include_error_cause = 0;
+ ip_size = ip_size - sizeof(struct sctp_error_cause);
+ sctp_size = sctp_size - sizeof(struct sctp_error_cause);
+ }
+ /* Assign header pointers packet */
+ struct ip* ip = (struct ip *) tmp_ip;
+ struct sctphdr* sctp_hdr = (struct sctphdr *) ((char *) ip + sizeof(*ip));
+ struct sctp_chunkhdr* chunk_hdr = (struct sctp_chunkhdr *) ((char *) sctp_hdr + sizeof(*sctp_hdr));
+ struct sctp_error_cause* error_cause = (struct sctp_error_cause *) ((char *) chunk_hdr + sizeof(*chunk_hdr));
+
+ /* construct ip header */
+ ip->ip_v = sm->ip_hdr->ip_v;
+ ip->ip_hl = 5; /* 5*32 bit words */
+ ip->ip_tos = 0;
+ ip->ip_len = htons(ip_size);
+ ip->ip_id = sm->ip_hdr->ip_id;
+ ip->ip_off = 0;
+ ip->ip_ttl = 255;
+ ip->ip_p = IPPROTO_SCTP;
+ /*
+ The definitions below should be removed when they make it into the SCTP stack
+ */
+#define SCTP_MIDDLEBOX_FLAG 0x02
+#define SCTP_NAT_TABLE_COLLISION 0x00b0
+#define SCTP_MISSING_NAT 0x00b1
+ chunk_hdr->chunk_type = (sndrply & SN_TX_ABORT) ? SCTP_ABORT_ASSOCIATION : SCTP_OPERATION_ERROR;
+ chunk_hdr->chunk_flags = SCTP_MIDDLEBOX_FLAG;
+ if (include_error_cause) {
+ error_cause->code = htons((sndrply & SN_REFLECT_ERROR) ? SCTP_MISSING_NAT : SCTP_NAT_TABLE_COLLISION);
+ error_cause->length = htons(sizeof(struct sctp_error_cause));
+ chunk_hdr->chunk_length = htons(sizeof(*chunk_hdr) + sizeof(struct sctp_error_cause));
+ } else {
+ chunk_hdr->chunk_length = htons(sizeof(*chunk_hdr));
+ }
+
+ /* set specific values */
+ switch(sndrply) {
+ case SN_REFLECT_ERROR:
+ chunk_hdr->chunk_flags |= SCTP_HAD_NO_TCB; /* set Tbit */
+ sctp_hdr->v_tag = sm->sctp_hdr->v_tag;
+ break;
+ case SN_REPLY_ERROR:
+ sctp_hdr->v_tag = (direction == SN_TO_LOCAL) ? assoc->g_vtag : assoc->l_vtag ;
+ break;
+ case SN_SEND_ABORT:
+ sctp_hdr->v_tag = sm->sctp_hdr->v_tag;
+ break;
+ case SN_REPLY_ABORT:
+ sctp_hdr->v_tag = sm->sctpchnk.Init->initiate_tag;
+ break;
+ }
+
+ /* Set send/reply values */
+ if (sndrply == SN_SEND_ABORT) { /*pass through NAT */
+ ip->ip_src = (direction == SN_TO_LOCAL) ? sm->ip_hdr->ip_src : assoc->a_addr;
+ ip->ip_dst = (direction == SN_TO_LOCAL) ? assoc->l_addr : sm->ip_hdr->ip_dst;
+ sctp_hdr->src_port = sm->sctp_hdr->src_port;
+ sctp_hdr->dest_port = sm->sctp_hdr->dest_port;
+ } else { /* reply and reflect */
+ ip->ip_src = sm->ip_hdr->ip_dst;
+ ip->ip_dst = sm->ip_hdr->ip_src;
+ sctp_hdr->src_port = sm->sctp_hdr->dest_port;
+ sctp_hdr->dest_port = sm->sctp_hdr->src_port;
+ }
+
+ /* Calculate IP header checksum */
+ ip->ip_sum = in_cksum_hdr(ip);
+
+ /* calculate SCTP header CRC32 */
+ sctp_hdr->checksum = 0;
+ sctp_hdr->checksum = sctp_finalize_crc32(update_crc32(0xffffffff, (unsigned char *) sctp_hdr, sctp_size));
+
+ memcpy(sm->ip_hdr, ip, ip_size);
+
+ SN_LOG(SN_LOG_EVENT,SctpAliasLog("%s %s 0x%x (->%s:%u vtag=0x%x crc=0x%x)\n",
+ ((sndrply == SN_SEND_ABORT) ? "Sending" : "Replying"),
+ ((sndrply & SN_TX_ERROR) ? "ErrorM" : "AbortM"),
+ (include_error_cause ? ntohs(error_cause->code) : 0),
+ inet_ntoa(ip->ip_dst),ntohs(sctp_hdr->dest_port),
+ ntohl(sctp_hdr->v_tag), ntohl(sctp_hdr->checksum)));
+}
+
+/* ----------------------------------------------------------------------
+ * PACKET PARSER CODE
+ * ----------------------------------------------------------------------
+ */
+/** @addtogroup packet_parser
+ *
+ * These functions parse the SCTP packet and fill a sctp_nat_msg structure
+ * with the parsed contents.
+ */
+/** @ingroup packet_parser
+ * @brief Parses SCTP packets for the key SCTP chunk that will be processed
+ *
+ * This module parses SCTP packets for the key SCTP chunk that will be processed
+ * The module completes the sctp_nat_msg structure and either retrieves the
+ * relevant (existing) stored association from the Hash Tables or creates a new
+ * association entity with state SN_ID
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
+ * @param pip
+ * @param sm Pointer to sctp message information
+ * @param passoc Pointer to the association this SCTP Message belongs to
+ *
+ * @return SN_PARSE_OK | SN_PARSE_ERROR_*
+ */
+static int
+sctp_PktParser(struct libalias *la, int direction, struct ip *pip,
+ struct sctp_nat_msg *sm, struct sctp_nat_assoc **passoc)
+//sctp_PktParser(int direction, struct mbuf *ipak, int ip_hdr_len,struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc)
+{
+ struct sctphdr *sctp_hdr;
+ struct sctp_chunkhdr *chunk_hdr;
+ struct sctp_paramhdr *param_hdr;
+ struct in_addr ipv4addr;
+ int bytes_left; /* bytes left in ip packet */
+ int chunk_length;
+ int chunk_count;
+ int partial_match = 0;
+ // mbuf *mp;
+ // int mlen;
+
+ // mlen = SCTP_HEADER_LEN(i_pak);
+ // mp = SCTP_HEADER_TO_CHAIN(i_pak); /* does nothing in bsd since header and chain not separate */
+
+ /*
+ * Note, that if the VTag is zero, it must be an INIT
+ * Also, I am only interested in the content of INIT and ADDIP chunks
+ */
+
+ // no mbuf stuff from Paolo yet so ...
+ sm->ip_hdr = pip;
+ /* remove ip header length from the bytes_left */
+ bytes_left = ntohs(pip->ip_len) - (pip->ip_hl << 2);
+
+ /* Check SCTP header length and move to first chunk */
+ if (bytes_left < sizeof(struct sctphdr)) {
+ sm->sctp_hdr = NULL;
+ return(SN_PARSE_ERROR_IPSHL); /* packet not long enough*/
+ }
+
+ sm->sctp_hdr = sctp_hdr = (struct sctphdr *) ip_next(pip);
+ bytes_left -= sizeof(struct sctphdr);
+
+ /* Check for valid ports (zero valued ports would find partially initialised associations */
+ if (sctp_hdr->src_port == 0 || sctp_hdr->dest_port == 0)
+ return(SN_PARSE_ERROR_PORT);
+
+ /* Check length of first chunk */
+ if (bytes_left < SN_MIN_CHUNK_SIZE) /* malformed chunk - could cause endless loop*/
+ return(SN_PARSE_ERROR_CHHL); /* packet not long enough for this chunk */
+
+ /* First chunk */
+ chunk_hdr = SN_SCTP_FIRSTCHUNK(sctp_hdr);
+
+ chunk_length = SCTP_SIZE32(ntohs(chunk_hdr->chunk_length));
+ if ((chunk_length < SN_MIN_CHUNK_SIZE) || (chunk_length > bytes_left)) /* malformed chunk - could cause endless loop*/
+ return(SN_PARSE_ERROR_CHHL);
+
+ if ((chunk_hdr->chunk_flags & SCTP_HAD_NO_TCB) &&
+ ((chunk_hdr->chunk_type == SCTP_ABORT_ASSOCIATION) ||
+ (chunk_hdr->chunk_type == SCTP_SHUTDOWN_COMPLETE))) {
+ /* T-Bit set */
+ if (direction == SN_TO_LOCAL)
+ *passoc = FindSctpGlobalT(la, pip->ip_src, sctp_hdr->v_tag, sctp_hdr->dest_port, sctp_hdr->src_port);
+ else
+ *passoc = FindSctpLocalT(la, pip->ip_dst, sctp_hdr->v_tag, sctp_hdr->dest_port, sctp_hdr->src_port);
+ } else {
+ /* Proper v_tag settings */
+ if (direction == SN_TO_LOCAL)
+ *passoc = FindSctpGlobal(la, pip->ip_src, sctp_hdr->v_tag, sctp_hdr->src_port, sctp_hdr->dest_port, &partial_match);
+ else
+ *passoc = FindSctpLocal(la, pip->ip_src, pip->ip_dst, sctp_hdr->v_tag, sctp_hdr->src_port, sctp_hdr->dest_port);
+ }
+
+ chunk_count = 1;
+ /* Real packet parsing occurs below */
+ sm->msg = SN_SCTP_OTHER;/* Initialise to largest value*/
+ sm->chunk_length = 0; /* only care about length for key chunks */
+ while (IS_SCTP_CONTROL(chunk_hdr)) {
+ switch(chunk_hdr->chunk_type) {
+ case SCTP_INITIATION:
+ if (chunk_length < sizeof(struct sctp_init_chunk)) /* malformed chunk*/
+ return(SN_PARSE_ERROR_CHHL);
+ sm->msg = SN_SCTP_INIT;
+ sm->sctpchnk.Init = (struct sctp_init *) ((char *) chunk_hdr + sizeof(struct sctp_chunkhdr));
+ sm->chunk_length = chunk_length;
+ /* if no existing association, create a new one */
+ if (*passoc == NULL) {
+ if (sctp_hdr->v_tag == 0){ //Init requires vtag=0
+ *passoc = (struct sctp_nat_assoc *) sn_malloc(sizeof(struct sctp_nat_assoc));
+ if (*passoc == NULL) {/* out of resources */
+ return(SN_PARSE_ERROR_AS_MALLOC);
+ }
+ /* Initialise association - malloc initialises memory to zeros */
+ (*passoc)->state = SN_ID;
+ LIST_INIT(&((*passoc)->Gaddr)); /* always initialise to avoid memory problems */
+ (*passoc)->TableRegister = SN_NULL_TBL;
+ return(SN_PARSE_OK);
+ }
+ return(SN_PARSE_ERROR_VTAG);
+ }
+ return(SN_PARSE_ERROR_LOOKUP);
+ case SCTP_INITIATION_ACK:
+ if (chunk_length < sizeof(struct sctp_init_ack_chunk)) /* malformed chunk*/
+ return(SN_PARSE_ERROR_CHHL);
+ sm->msg = SN_SCTP_INITACK;
+ sm->sctpchnk.InitAck = (struct sctp_init_ack *) ((char *) chunk_hdr + sizeof(struct sctp_chunkhdr));
+ sm->chunk_length = chunk_length;
+ return ((*passoc == NULL)?(SN_PARSE_ERROR_LOOKUP):(SN_PARSE_OK));
+ case SCTP_ABORT_ASSOCIATION: /* access only minimum sized chunk */
+ sm->msg = SN_SCTP_ABORT;
+ sm->chunk_length = chunk_length;
+ return ((*passoc == NULL)?(SN_PARSE_ERROR_LOOKUP_ABORT):(SN_PARSE_OK));
+ case SCTP_SHUTDOWN_ACK:
+ if (chunk_length < sizeof(struct sctp_shutdown_ack_chunk)) /* malformed chunk*/
+ return(SN_PARSE_ERROR_CHHL);
+ if (sm->msg > SN_SCTP_SHUTACK) {
+ sm->msg = SN_SCTP_SHUTACK;
+ sm->chunk_length = chunk_length;
+ }
+ break;
+ case SCTP_SHUTDOWN_COMPLETE: /* minimum sized chunk */
+ if (sm->msg > SN_SCTP_SHUTCOMP) {
+ sm->msg = SN_SCTP_SHUTCOMP;
+ sm->chunk_length = chunk_length;
+ }
+ return ((*passoc == NULL)?(SN_PARSE_ERROR_LOOKUP):(SN_PARSE_OK));
+ case SCTP_ASCONF:
+ if (sm->msg > SN_SCTP_ASCONF) {
+ if (chunk_length < (sizeof(struct sctp_asconf_chunk) + sizeof(struct sctp_ipv4addr_param))) /* malformed chunk*/
+ return(SN_PARSE_ERROR_CHHL);
+ //leave parameter searching to later, if required
+ param_hdr = (struct sctp_paramhdr *) ((char *) chunk_hdr + sizeof(struct sctp_asconf_chunk)); /*compulsory IP parameter*/
+ if (ntohs(param_hdr->param_type) == SCTP_IPV4_ADDRESS) {
+ if ((*passoc == NULL) && (direction == SN_TO_LOCAL)) { /* AddIP with no association */
+ /* try look up with the ASCONF packet's alternative address */
+ ipv4addr.s_addr = ((struct sctp_ipv4addr_param *) param_hdr)->addr;
+ *passoc = FindSctpGlobal(la, ipv4addr, sctp_hdr->v_tag, sctp_hdr->src_port, sctp_hdr->dest_port, &partial_match);
+ }
+ param_hdr = (struct sctp_paramhdr *)
+ ((char *) param_hdr + sizeof(struct sctp_ipv4addr_param)); /*asconf's compulsory address parameter */
+ sm->chunk_length = chunk_length - sizeof(struct sctp_asconf_chunk) - sizeof(struct sctp_ipv4addr_param); /* rest of chunk */
+ } else {
+ if (chunk_length < (sizeof(struct sctp_asconf_chunk) + sizeof(struct sctp_ipv6addr_param))) /* malformed chunk*/
+ return(SN_PARSE_ERROR_CHHL);
+ param_hdr = (struct sctp_paramhdr *)
+ ((char *) param_hdr + sizeof(struct sctp_ipv6addr_param)); /*asconf's compulsory address parameter */
+ sm->chunk_length = chunk_length - sizeof(struct sctp_asconf_chunk) - sizeof(struct sctp_ipv6addr_param); /* rest of chunk */
+ }
+ sm->msg = SN_SCTP_ASCONF;
+ sm->sctpchnk.Asconf = param_hdr;
+
+ if (*passoc == NULL) { /* AddIP with no association */
+ *passoc = (struct sctp_nat_assoc *) sn_malloc(sizeof(struct sctp_nat_assoc));
+ if (*passoc == NULL) {/* out of resources */
+ return(SN_PARSE_ERROR_AS_MALLOC);
+ }
+ /* Initialise association - malloc initialises memory to zeros */
+ (*passoc)->state = SN_ID;
+ LIST_INIT(&((*passoc)->Gaddr)); /* always initialise to avoid memory problems */
+ (*passoc)->TableRegister = SN_NULL_TBL;
+ return(SN_PARSE_OK);
+ }
+ }
+ break;
+ case SCTP_ASCONF_ACK:
+ if (sm->msg > SN_SCTP_ASCONFACK) {
+ if (chunk_length < sizeof(struct sctp_asconf_ack_chunk)) /* malformed chunk*/
+ return(SN_PARSE_ERROR_CHHL);
+ //leave parameter searching to later, if required
+ param_hdr = (struct sctp_paramhdr *) ((char *) chunk_hdr
+ + sizeof(struct sctp_asconf_ack_chunk));
+ sm->msg = SN_SCTP_ASCONFACK;
+ sm->sctpchnk.Asconf = param_hdr;
+ sm->chunk_length = chunk_length - sizeof(struct sctp_asconf_ack_chunk);
+ }
+ break;
+ default:
+ break; /* do nothing*/
+ }
+
+ /* if no association is found exit - we need to find an Init or AddIP within sysctl_initialising_chunk_proc_limit */
+ if ((*passoc == NULL) && (chunk_count >= sysctl_initialising_chunk_proc_limit))
+ return(SN_PARSE_ERROR_LOOKUP);
+
+ /* finished with this chunk, on to the next chunk*/
+ bytes_left-= chunk_length;
+
+ /* Is this the end of the packet ? */
+ if (bytes_left == 0)
+ return (*passoc == NULL)?(SN_PARSE_ERROR_LOOKUP):(SN_PARSE_OK);
+
+ /* Are there enough bytes in packet to at least retrieve length of next chunk ? */
+ if (bytes_left < SN_MIN_CHUNK_SIZE)
+ return(SN_PARSE_ERROR_CHHL);
+
+ chunk_hdr = SN_SCTP_NEXTCHUNK(chunk_hdr);
+
+ /* Is the chunk long enough to not cause endless look and are there enough bytes in packet to read the chunk ? */
+ chunk_length = SCTP_SIZE32(ntohs(chunk_hdr->chunk_length));
+ if ((chunk_length < SN_MIN_CHUNK_SIZE) || (chunk_length > bytes_left))
+ return(SN_PARSE_ERROR_CHHL);
+ if(++chunk_count > sysctl_chunk_proc_limit)
+ return(SN_PARSE_OK); /* limit for processing chunks, take what we get */
+ }
+
+ if (*passoc == NULL)
+ return (partial_match)?(SN_PARSE_ERROR_PARTIALLOOKUP):(SN_PARSE_ERROR_LOOKUP);
+ else
+ return(SN_PARSE_OK);
+}
+
+/** @ingroup packet_parser
+ * @brief Extract Vtags from Asconf Chunk
+ *
+ * GetAsconfVtags scans an Asconf Chunk for the vtags parameter, and then
+ * extracts the vtags.
+ *
+ * GetAsconfVtags is not called from within sctp_PktParser. It is called only
+ * from within ID_process when an AddIP has been received.
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param sm Pointer to sctp message information
+ * @param l_vtag Pointer to the local vtag in the association this SCTP Message belongs to
+ * @param g_vtag Pointer to the local vtag in the association this SCTP Message belongs to
+ * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
+ *
+ * @return 1 - success | 0 - fail
+ */
+static int
+GetAsconfVtags(struct libalias *la, struct sctp_nat_msg *sm, uint32_t *l_vtag, uint32_t *g_vtag, int direction)
+{
+ /* To be removed when information is in the sctp headers */
+#define SCTP_VTAG_PARAM 0xC007
+ struct sctp_vtag_param {
+ struct sctp_paramhdr ph;/* type=SCTP_VTAG_PARAM */
+ uint32_t local_vtag;
+ uint32_t remote_vtag;
+ } __attribute__((packed));
+
+ struct sctp_vtag_param *vtag_param;
+ struct sctp_paramhdr *param;
+ int bytes_left;
+ int param_size;
+ int param_count;
+
+ param_count = 1;
+ param = sm->sctpchnk.Asconf;
+ param_size = SCTP_SIZE32(ntohs(param->param_length));
+ bytes_left = sm->chunk_length;
+ /* step through Asconf parameters */
+ while((bytes_left >= param_size) && (bytes_left >= SN_VTAG_PARAM_SIZE)) {
+ if (ntohs(param->param_type) == SCTP_VTAG_PARAM) {
+ vtag_param = (struct sctp_vtag_param *) param;
+ switch(direction) {
+ /* The Internet draft is a little ambigious as to order of these vtags.
+ We think it is this way around. If we are wrong, the order will need
+ to be changed. */
+ case SN_TO_GLOBAL:
+ *g_vtag = vtag_param->local_vtag;
+ *l_vtag = vtag_param->remote_vtag;
+ break;
+ case SN_TO_LOCAL:
+ *g_vtag = vtag_param->remote_vtag;
+ *l_vtag = vtag_param->local_vtag;
+ break;
+ }
+ return(1); /* found */
+ }
+
+ bytes_left -= param_size;
+ if (bytes_left < SN_MIN_PARAM_SIZE) return(0);
+
+ param = SN_SCTP_NEXTPARAM(param);
+ param_size = SCTP_SIZE32(ntohs(param->param_length));
+ if (++param_count > sysctl_param_proc_limit) {
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("Parameter parse limit exceeded (GetAsconfVtags)",
+ sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction));
+ return(0); /* not found limit exceeded*/
+ }
+ }
+ return(0); /* not found */
+}
+
+/** @ingroup packet_parser
+ * @brief AddGlobalIPAddresses from Init,InitAck,or AddIP packets
+ *
+ * AddGlobalIPAddresses scans an SCTP chunk (in sm) for Global IP addresses, and
+ * adds them.
+ *
+ * @param sm Pointer to sctp message information
+ * @param assoc Pointer to the association this SCTP Message belongs to
+ * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
+ *
+ */
+static void
+AddGlobalIPAddresses(struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int direction)
+{
+ struct sctp_ipv4addr_param *ipv4_param;
+ struct sctp_paramhdr *param = NULL;
+ struct sctp_GlobalAddress *G_Addr;
+ struct in_addr g_addr = {0};
+ int bytes_left = 0;
+ int param_size;
+ int param_count, addr_param_count = 0;
+
+ switch(direction) {
+ case SN_TO_GLOBAL: /* does not contain global addresses */
+ g_addr = sm->ip_hdr->ip_dst;
+ bytes_left = 0; /* force exit */
+ break;
+ case SN_TO_LOCAL:
+ g_addr = sm->ip_hdr->ip_src;
+ param_count = 1;
+ switch(sm->msg) {
+ case SN_SCTP_INIT:
+ bytes_left = sm->chunk_length - sizeof(struct sctp_init_chunk);
+ param = (struct sctp_paramhdr *)((char *)sm->sctpchnk.Init + sizeof(struct sctp_init));
+ break;
+ case SN_SCTP_INITACK:
+ bytes_left = sm->chunk_length - sizeof(struct sctp_init_ack_chunk);
+ param = (struct sctp_paramhdr *)((char *)sm->sctpchnk.InitAck + sizeof(struct sctp_init_ack));
+ break;
+ case SN_SCTP_ASCONF:
+ bytes_left = sm->chunk_length;
+ param = sm->sctpchnk.Asconf;
+ break;
+ }
+ }
+ if (bytes_left >= SN_MIN_PARAM_SIZE)
+ param_size = SCTP_SIZE32(ntohs(param->param_length));
+ else
+ param_size = bytes_left+1; /* force skip loop */
+
+ if ((assoc->state == SN_ID) && ((sm->msg == SN_SCTP_INIT) || (bytes_left < SN_MIN_PARAM_SIZE))) {/* add pkt address */
+ G_Addr = (struct sctp_GlobalAddress *) sn_malloc(sizeof(struct sctp_GlobalAddress));
+ if (G_Addr == NULL) {/* out of resources */
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("AddGlobalIPAddress: No resources for adding global address - revert to no tracking",
+ sm->sctp_hdr->v_tag, 0, direction));
+ assoc->num_Gaddr = 0; /* don't track any more for this assoc*/
+ sysctl_track_global_addresses=0;
+ return;
+ }
+ G_Addr->g_addr = g_addr;
+ if (!Add_Global_Address_to_List(assoc, G_Addr))
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("AddGlobalIPAddress: Address already in list",
+ sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction));
+ }
+
+ /* step through parameters */
+ while((bytes_left >= param_size) && (bytes_left >= sizeof(struct sctp_ipv4addr_param))) {
+ if (assoc->num_Gaddr >= sysctl_track_global_addresses) {
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("AddGlobalIPAddress: Maximum Number of addresses reached",
+ sm->sctp_hdr->v_tag, sysctl_track_global_addresses, direction));
+ return;
+ }
+ switch(ntohs(param->param_type)) {
+ case SCTP_ADD_IP_ADDRESS:
+ /* skip to address parameter - leave param_size so bytes left will be calculated properly*/
+ param = (struct sctp_paramhdr *) &((struct sctp_asconf_addrv4_param *) param)->addrp;
+ case SCTP_IPV4_ADDRESS:
+ ipv4_param = (struct sctp_ipv4addr_param *) param;
+ /* add addresses to association */
+ G_Addr = (struct sctp_GlobalAddress *) sn_malloc(sizeof(struct sctp_GlobalAddress));
+ if (G_Addr == NULL) {/* out of resources */
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("AddGlobalIPAddress: No resources for adding global address - revert to no tracking",
+ sm->sctp_hdr->v_tag, 0, direction));
+ assoc->num_Gaddr = 0; /* don't track any more for this assoc*/
+ sysctl_track_global_addresses=0;
+ return;
+ }
+ /* add address */
+ addr_param_count++;
+ if ((sm->msg == SN_SCTP_ASCONF) && (ipv4_param->addr == INADDR_ANY)) { /* use packet address */
+ G_Addr->g_addr = g_addr;
+ if (!Add_Global_Address_to_List(assoc, G_Addr))
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("AddGlobalIPAddress: Address already in list",
+ sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction));
+ return; /*shouldn't be any other addresses if the zero address is given*/
+ } else {
+ G_Addr->g_addr.s_addr = ipv4_param->addr;
+ if (!Add_Global_Address_to_List(assoc, G_Addr))
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("AddGlobalIPAddress: Address already in list",
+ sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction));
+ }
+ }
+
+ bytes_left -= param_size;
+ if (bytes_left < SN_MIN_PARAM_SIZE)
+ break;
+
+ param = SN_SCTP_NEXTPARAM(param);
+ param_size = SCTP_SIZE32(ntohs(param->param_length));
+ if (++param_count > sysctl_param_proc_limit) {
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("Parameter parse limit exceeded (AddGlobalIPAddress)",
+ sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction));
+ break; /* limit exceeded*/
+ }
+ }
+ if (addr_param_count == 0) {
+ SN_LOG(SN_LOG_DETAIL,
+ logsctperror("AddGlobalIPAddress: no address parameters to add",
+ sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction));
+ }
+}
+
+/**
+ * @brief Add_Global_Address_to_List
+ *
+ * Adds a global IP address to an associations address list, if it is not
+ * already there. The first address added us usually the packet's address, and
+ * is most likely to be used, so it is added at the beginning. Subsequent
+ * addresses are added after this one.
+ *
+ * @param assoc Pointer to the association this SCTP Message belongs to
+ * @param G_addr Pointer to the global address to add
+ *
+ * @return 1 - success | 0 - fail
+ */
+static int Add_Global_Address_to_List(struct sctp_nat_assoc *assoc, struct sctp_GlobalAddress *G_addr)
+{
+ struct sctp_GlobalAddress *iter_G_Addr = NULL, *first_G_Addr = NULL;
+ first_G_Addr = LIST_FIRST(&(assoc->Gaddr));
+ if (first_G_Addr == NULL) {
+ LIST_INSERT_HEAD(&(assoc->Gaddr), G_addr, list_Gaddr); /* add new address to beginning of list*/
+ } else {
+ LIST_FOREACH(iter_G_Addr, &(assoc->Gaddr), list_Gaddr) {
+ if (G_addr->g_addr.s_addr == iter_G_Addr->g_addr.s_addr)
+ return(0); /* already exists, so don't add */
+ }
+ LIST_INSERT_AFTER(first_G_Addr, G_addr, list_Gaddr); /* add address to end of list*/
+ }
+ assoc->num_Gaddr++;
+ return(1); /* success */
+}
+
+/** @ingroup packet_parser
+ * @brief RmGlobalIPAddresses from DelIP packets
+ *
+ * RmGlobalIPAddresses scans an ASCONF chunk for DelIP parameters to remove the
+ * given Global IP addresses from the association. It will not delete the
+ * the address if it is a list of one address.
+ *
+ *
+ * @param sm Pointer to sctp message information
+ * @param assoc Pointer to the association this SCTP Message belongs to
+ * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
+ *
+ */
+static void
+RmGlobalIPAddresses(struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int direction)
+{
+ struct sctp_asconf_addrv4_param *asconf_ipv4_param;
+ struct sctp_paramhdr *param;
+ struct sctp_GlobalAddress *G_Addr, *G_Addr_tmp;
+ struct in_addr g_addr;
+ int bytes_left;
+ int param_size;
+ int param_count;
+
+ if(direction == SN_TO_GLOBAL)
+ g_addr = sm->ip_hdr->ip_dst;
+ else
+ g_addr = sm->ip_hdr->ip_src;
+
+ bytes_left = sm->chunk_length;
+ param_count = 1;
+ param = sm->sctpchnk.Asconf;
+ if (bytes_left >= SN_MIN_PARAM_SIZE) {
+ param_size = SCTP_SIZE32(ntohs(param->param_length));
+ } else {
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("RmGlobalIPAddress: truncated packet - cannot remove IP addresses",
+ sm->sctp_hdr->v_tag, sysctl_track_global_addresses, direction));
+ return;
+ }
+
+ /* step through Asconf parameters */
+ while((bytes_left >= param_size) && (bytes_left >= sizeof(struct sctp_ipv4addr_param))) {
+ if (ntohs(param->param_type) == SCTP_DEL_IP_ADDRESS) {
+ asconf_ipv4_param = (struct sctp_asconf_addrv4_param *) param;
+ if (asconf_ipv4_param->addrp.addr == INADDR_ANY) { /* remove all bar pkt address */
+ LIST_FOREACH_SAFE(G_Addr, &(assoc->Gaddr), list_Gaddr, G_Addr_tmp) {
+ if(G_Addr->g_addr.s_addr != sm->ip_hdr->ip_src.s_addr) {
+ if (assoc->num_Gaddr > 1) { /* only delete if more than one */
+ LIST_REMOVE(G_Addr, list_Gaddr);
+ sn_free(G_Addr);
+ assoc->num_Gaddr--;
+ } else {
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("RmGlobalIPAddress: Request to remove last IP address (didn't)",
+ sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction));
+ }
+ }
+ }
+ return; /*shouldn't be any other addresses if the zero address is given*/
+ } else {
+ LIST_FOREACH_SAFE(G_Addr, &(assoc->Gaddr), list_Gaddr, G_Addr_tmp) {
+ if(G_Addr->g_addr.s_addr == asconf_ipv4_param->addrp.addr) {
+ if (assoc->num_Gaddr > 1) { /* only delete if more than one */
+ LIST_REMOVE(G_Addr, list_Gaddr);
+ sn_free(G_Addr);
+ assoc->num_Gaddr--;
+ break; /* Since add only adds new addresses, there should be no double entries */
+ } else {
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("RmGlobalIPAddress: Request to remove last IP address (didn't)",
+ sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction));
+ }
+ }
+ }
+ }
+ }
+ bytes_left -= param_size;
+ if (bytes_left == 0) return;
+ else if (bytes_left < SN_MIN_PARAM_SIZE) {
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("RmGlobalIPAddress: truncated packet - may not have removed all IP addresses",
+ sm->sctp_hdr->v_tag, sysctl_track_global_addresses, direction));
+ return;
+ }
+
+ param = SN_SCTP_NEXTPARAM(param);
+ param_size = SCTP_SIZE32(ntohs(param->param_length));
+ if (++param_count > sysctl_param_proc_limit) {
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("Parameter parse limit exceeded (RmGlobalIPAddress)",
+ sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction));
+ return; /* limit exceeded*/
+ }
+ }
+}
+
+/** @ingroup packet_parser
+ * @brief Check that ASCONF was successful
+ *
+ * Each ASCONF configuration parameter carries a correlation ID which should be
+ * matched with an ASCONFack. This is difficult for a NAT, since every
+ * association could potentially have a number of outstanding ASCONF
+ * configuration parameters, which should only be activated on receipt of the
+ * ACK.
+ *
+ * Currently we only look for an ACK when the NAT is setting up a new
+ * association (ie AddIP for a connection that the NAT does not know about
+ * because the original Init went through a public interface or another NAT)
+ * Since there is currently no connection on this path, there should be no other
+ * ASCONF configuration parameters outstanding, so we presume that if there is
+ * an ACK that it is responding to the AddIP and activate the new association.
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param sm Pointer to sctp message information
+ * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
+ *
+ * @return 1 - success | 0 - fail
+ */
+static int
+IsASCONFack(struct libalias *la, struct sctp_nat_msg *sm, int direction)
+{
+ struct sctp_paramhdr *param;
+ int bytes_left;
+ int param_size;
+ int param_count;
+
+ param_count = 1;
+ param = sm->sctpchnk.Asconf;
+ param_size = SCTP_SIZE32(ntohs(param->param_length));
+ if (param_size == 8)
+ return(1); /*success - default acknowledgement of everything */
+
+ bytes_left = sm->chunk_length;
+ if (bytes_left < param_size)
+ return(0); /* not found */
+ /* step through Asconf parameters */
+ while(bytes_left >= SN_ASCONFACK_PARAM_SIZE) {
+ if (ntohs(param->param_type) == SCTP_SUCCESS_REPORT)
+ return(1); /* success - but can't match correlation IDs - should only be one */
+ /* check others just in case */
+ bytes_left -= param_size;
+ if (bytes_left >= SN_MIN_PARAM_SIZE) {
+ param = SN_SCTP_NEXTPARAM(param);
+ } else {
+ return(0);
+ }
+ param_size = SCTP_SIZE32(ntohs(param->param_length));
+ if (bytes_left < param_size) return(0);
+
+ if (++param_count > sysctl_param_proc_limit) {
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("Parameter parse limit exceeded (IsASCONFack)",
+ sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction));
+ return(0); /* not found limit exceeded*/
+ }
+ }
+ return(0); /* not success */
+}
+
+/** @ingroup packet_parser
+ * @brief Check to see if ASCONF contains an Add IP or Del IP parameter
+ *
+ * IsADDorDEL scans an ASCONF packet to see if it contains an AddIP or DelIP
+ * parameter
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param sm Pointer to sctp message information
+ * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
+ *
+ * @return SCTP_ADD_IP_ADDRESS | SCTP_DEL_IP_ADDRESS | 0 - fail
+ */
+static int
+IsADDorDEL(struct libalias *la, struct sctp_nat_msg *sm, int direction)
+{
+ struct sctp_paramhdr *param;
+ int bytes_left;
+ int param_size;
+ int param_count;
+
+ param_count = 1;
+ param = sm->sctpchnk.Asconf;
+ param_size = SCTP_SIZE32(ntohs(param->param_length));
+
+ bytes_left = sm->chunk_length;
+ if (bytes_left < param_size)
+ return(0); /* not found */
+ /* step through Asconf parameters */
+ while(bytes_left >= SN_ASCONFACK_PARAM_SIZE) {
+ if (ntohs(param->param_type) == SCTP_ADD_IP_ADDRESS)
+ return(SCTP_ADD_IP_ADDRESS);
+ else if (ntohs(param->param_type) == SCTP_DEL_IP_ADDRESS)
+ return(SCTP_DEL_IP_ADDRESS);
+ /* check others just in case */
+ bytes_left -= param_size;
+ if (bytes_left >= SN_MIN_PARAM_SIZE) {
+ param = SN_SCTP_NEXTPARAM(param);
+ } else {
+ return(0); /*Neither found */
+ }
+ param_size = SCTP_SIZE32(ntohs(param->param_length));
+ if (bytes_left < param_size) return(0);
+
+ if (++param_count > sysctl_param_proc_limit) {
+ SN_LOG(SN_LOG_EVENT,
+ logsctperror("Parameter parse limit exceeded IsADDorDEL)",
+ sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction));
+ return(0); /* not found limit exceeded*/
+ }
+ }
+ return(0); /*Neither found */
+}
+
+/* ----------------------------------------------------------------------
+ * STATE MACHINE CODE
+ * ----------------------------------------------------------------------
+ */
+/** @addtogroup state_machine
+ *
+ * The SCTP NAT State Machine functions will:
+ * - Process an already parsed packet
+ * - Use the existing NAT Hash Tables
+ * - Determine the next state for the association
+ * - Update the NAT Hash Tables and Timer Queues
+ * - Return the appropriate action to take with the packet
+ */
+/** @ingroup state_machine
+ * @brief Process SCTP message
+ *
+ * This function is the base state machine. It calls the processing engine for
+ * each state.
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
+ * @param sm Pointer to sctp message information
+ * @param assoc Pointer to the association this SCTP Message belongs to
+ *
+ * @return SN_DROP_PKT | SN_NAT_PKT | SN_REPLY_ABORT | SN_REPLY_ERROR | SN_PROCESSING_ERROR
+ */
+static int
+ProcessSctpMsg(struct libalias *la, int direction, struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc)
+{
+ int rtnval;
+
+ switch (assoc->state) {
+ case SN_ID: /* Idle */
+ rtnval = ID_process(la, direction, assoc, sm);
+ if (rtnval != SN_NAT_PKT) {
+ assoc->state = SN_RM;/* Mark for removal*/
+ }
+ return(rtnval);
+ case SN_INi: /* Initialising - Init */
+ return(INi_process(la, direction, assoc, sm));
+ case SN_INa: /* Initialising - AddIP */
+ return(INa_process(la, direction, assoc, sm));
+ case SN_UP: /* Association UP */
+ return(UP_process(la, direction, assoc, sm));
+ case SN_CL: /* Association Closing */
+ return(CL_process(la, direction, assoc, sm));
+ }
+ return(SN_PROCESSING_ERROR);
+}
+
+/** @ingroup state_machine
+ * @brief Process SCTP message while in the Idle state
+ *
+ * This function looks for an Incoming INIT or AddIP message.
+ *
+ * All other SCTP messages are invalid when in SN_ID, and are dropped.
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
+ * @param sm Pointer to sctp message information
+ * @param assoc Pointer to the association this SCTP Message belongs to
+ *
+ * @return SN_NAT_PKT | SN_DROP_PKT | SN_REPLY_ABORT | SN_REPLY_ERROR
+ */
+static int
+ID_process(struct libalias *la, int direction, struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm)
+{
+ switch(sm->msg) {
+ case SN_SCTP_ASCONF: /* a packet containing an ASCONF chunk with ADDIP */
+ if (!sysctl_accept_global_ootb_addip && (direction == SN_TO_LOCAL))
+ return(SN_DROP_PKT);
+ /* if this Asconf packet does not contain the Vtag parameters it is of no use in Idle state */
+ if (!GetAsconfVtags(la, sm, &(assoc->l_vtag), &(assoc->g_vtag), direction))
+ return(SN_DROP_PKT);
+ case SN_SCTP_INIT: /* a packet containing an INIT chunk or an ASCONF AddIP */
+ if (sysctl_track_global_addresses)
+ AddGlobalIPAddresses(sm, assoc, direction);
+ switch(direction){
+ case SN_TO_GLOBAL:
+ assoc->l_addr = sm->ip_hdr->ip_src;
+ assoc->a_addr = FindAliasAddress(la, assoc->l_addr);
+ assoc->l_port = sm->sctp_hdr->src_port;
+ assoc->g_port = sm->sctp_hdr->dest_port;
+ if(sm->msg == SN_SCTP_INIT)
+ assoc->g_vtag = sm->sctpchnk.Init->initiate_tag;
+ if (AddSctpAssocGlobal(la, assoc)) /* DB clash *///**** need to add dst address
+ return((sm->msg == SN_SCTP_INIT) ? SN_REPLY_ABORT : SN_REPLY_ERROR);
+ if(sm->msg == SN_SCTP_ASCONF) {
+ if (AddSctpAssocLocal(la, assoc, sm->ip_hdr->ip_dst)) /* DB clash */
+ return(SN_REPLY_ERROR);
+ assoc->TableRegister |= SN_WAIT_TOLOCAL; /* wait for tolocal ack */
+ }
+ break;
+ case SN_TO_LOCAL:
+ assoc->l_addr = FindSctpRedirectAddress(la, sm);
+ assoc->a_addr = sm->ip_hdr->ip_dst;
+ assoc->l_port = sm->sctp_hdr->dest_port;
+ assoc->g_port = sm->sctp_hdr->src_port;
+ if(sm->msg == SN_SCTP_INIT)
+ assoc->l_vtag = sm->sctpchnk.Init->initiate_tag;
+ if (AddSctpAssocLocal(la, assoc, sm->ip_hdr->ip_src)) /* DB clash */
+ return((sm->msg == SN_SCTP_INIT) ? SN_REPLY_ABORT : SN_REPLY_ERROR);
+ if(sm->msg == SN_SCTP_ASCONF) {
+ if (AddSctpAssocGlobal(la, assoc)) /* DB clash */ //**** need to add src address
+ return(SN_REPLY_ERROR);
+ assoc->TableRegister |= SN_WAIT_TOGLOBAL; /* wait for toglobal ack */
+ }
+ break;
+ }
+ assoc->state = (sm->msg == SN_SCTP_INIT) ? SN_INi : SN_INa;
+ assoc->exp = SN_I_T(la);
+ sctp_AddTimeOut(la,assoc);
+ return(SN_NAT_PKT);
+ default: /* Any other type of SCTP message is not valid in Idle */
+ return(SN_DROP_PKT);
+ }
+return(SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */
+}
+
+/** @ingroup state_machine
+ * @brief Process SCTP message while waiting for an INIT-ACK message
+ *
+ * Only an INIT-ACK, resent INIT, or an ABORT SCTP packet are valid in this
+ * state, all other packets are dropped.
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
+ * @param sm Pointer to sctp message information
+ * @param assoc Pointer to the association this SCTP Message belongs to
+ *
+ * @return SN_NAT_PKT | SN_DROP_PKT | SN_REPLY_ABORT
+ */
+static int
+INi_process(struct libalias *la, int direction, struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm)
+{
+ switch(sm->msg) {
+ case SN_SCTP_INIT: /* a packet containing a retransmitted INIT chunk */
+ sctp_ResetTimeOut(la, assoc, SN_I_T(la));
+ return(SN_NAT_PKT);
+ case SN_SCTP_INITACK: /* a packet containing an INIT-ACK chunk */
+ switch(direction){
+ case SN_TO_LOCAL:
+ if (assoc->num_Gaddr) /*If tracking global addresses for this association */
+ AddGlobalIPAddresses(sm, assoc, direction);
+ assoc->l_vtag = sm->sctpchnk.Init->initiate_tag;
+ if (AddSctpAssocLocal(la, assoc, sm->ip_hdr->ip_src)) { /* DB clash */
+ assoc->state = SN_RM;/* Mark for removal*/
+ return(SN_SEND_ABORT);
+ }
+ break;
+ case SN_TO_GLOBAL:
+ assoc->l_addr = sm->ip_hdr->ip_src; // Only if not set in Init! *
+ assoc->g_vtag = sm->sctpchnk.Init->initiate_tag;
+ if (AddSctpAssocGlobal(la, assoc)) { /* DB clash */
+ assoc->state = SN_RM;/* Mark for removal*/
+ return(SN_SEND_ABORT);
+ }
+ break;
+ }
+ assoc->state = SN_UP;/* association established for NAT */
+ sctp_ResetTimeOut(la,assoc, SN_U_T(la));
+ return(SN_NAT_PKT);
+ case SN_SCTP_ABORT: /* a packet containing an ABORT chunk */
+ assoc->state = SN_RM;/* Mark for removal*/
+ return(SN_NAT_PKT);
+ default:
+ return(SN_DROP_PKT);
+ }
+ return(SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */
+}
+
+/** @ingroup state_machine
+ * @brief Process SCTP message while waiting for an AddIp-ACK message
+ *
+ * Only an AddIP-ACK, resent AddIP, or an ABORT message are valid, all other
+ * SCTP packets are dropped
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
+ * @param sm Pointer to sctp message information
+ * @param assoc Pointer to the association this SCTP Message belongs to
+ *
+ * @return SN_NAT_PKT | SN_DROP_PKT
+ */
+static int
+INa_process(struct libalias *la, int direction,struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm)
+{
+ switch(sm->msg) {
+ case SN_SCTP_ASCONF: /* a packet containing an ASCONF chunk*/
+ sctp_ResetTimeOut(la,assoc, SN_I_T(la));
+ return(SN_NAT_PKT);
+ case SN_SCTP_ASCONFACK: /* a packet containing an ASCONF chunk with a ADDIP-ACK */
+ switch(direction){
+ case SN_TO_LOCAL:
+ if (!(assoc->TableRegister & SN_WAIT_TOLOCAL)) /* wrong direction */
+ return(SN_DROP_PKT);
+ break;
+ case SN_TO_GLOBAL:
+ if (!(assoc->TableRegister & SN_WAIT_TOGLOBAL)) /* wrong direction */
+ return(SN_DROP_PKT);
+ }
+ if (IsASCONFack(la,sm,direction)) {
+ assoc->TableRegister &= SN_BOTH_TBL; /* remove wait flags */
+ assoc->state = SN_UP; /* association established for NAT */
+ sctp_ResetTimeOut(la,assoc, SN_U_T(la));
+ return(SN_NAT_PKT);
+ } else {
+ assoc->state = SN_RM;/* Mark for removal*/
+ return(SN_NAT_PKT);
+ }
+ case SN_SCTP_ABORT: /* a packet containing an ABORT chunk */
+ assoc->state = SN_RM;/* Mark for removal*/
+ return(SN_NAT_PKT);
+ default:
+ return(SN_DROP_PKT);
+ }
+ return(SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */
+}
+
+/** @ingroup state_machine
+ * @brief Process SCTP messages while association is UP redirecting packets
+ *
+ * While in the SN_UP state, all packets for the particular association
+ * are passed. Only a SHUT-ACK or an ABORT will cause a change of state.
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
+ * @param sm Pointer to sctp message information
+ * @param assoc Pointer to the association this SCTP Message belongs to
+ *
+ * @return SN_NAT_PKT | SN_DROP_PKT
+ */
+static int
+UP_process(struct libalias *la, int direction, struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm)
+{
+ switch(sm->msg) {
+ case SN_SCTP_SHUTACK: /* a packet containing a SHUTDOWN-ACK chunk */
+ assoc->state = SN_CL;
+ sctp_ResetTimeOut(la,assoc, SN_C_T(la));
+ return(SN_NAT_PKT);
+ case SN_SCTP_ABORT: /* a packet containing an ABORT chunk */
+ assoc->state = SN_RM;/* Mark for removal*/
+ return(SN_NAT_PKT);
+ case SN_SCTP_ASCONF: /* a packet containing an ASCONF chunk*/
+ if ((direction == SN_TO_LOCAL) && assoc->num_Gaddr) /*If tracking global addresses for this association & from global side */
+ switch(IsADDorDEL(la,sm,direction)) {
+ case SCTP_ADD_IP_ADDRESS:
+ AddGlobalIPAddresses(sm, assoc, direction);
+ break;
+ case SCTP_DEL_IP_ADDRESS:
+ RmGlobalIPAddresses(sm, assoc, direction);
+ break;
+ } /* fall through to default */
+ default:
+ sctp_ResetTimeOut(la,assoc, SN_U_T(la));
+ return(SN_NAT_PKT); /* forward packet */
+ }
+ return(SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */
+}
+
+/** @ingroup state_machine
+ * @brief Process SCTP message while association is in the process of closing
+ *
+ * This function waits for a SHUT-COMP to close the association. Depending on
+ * the the setting of sysctl_holddown_timer it may not remove the association
+ * immediately, but leave it up until SN_X_T(la). Only SHUT-COMP, SHUT-ACK, and
+ * ABORT packets are permitted in this state. All other packets are dropped.
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
+ * @param sm Pointer to sctp message information
+ * @param assoc Pointer to the association this SCTP Message belongs to
+ *
+ * @return SN_NAT_PKT | SN_DROP_PKT
+ */
+static int
+CL_process(struct libalias *la, int direction,struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm)
+{
+ switch(sm->msg) {
+ case SN_SCTP_SHUTCOMP: /* a packet containing a SHUTDOWN-COMPLETE chunk */
+ assoc->state = SN_CL; /* Stay in Close state until timeout */
+ if (sysctl_holddown_timer > 0)
+ sctp_ResetTimeOut(la, assoc, SN_X_T(la));/* allow to stay open for Tbit packets*/
+ else
+ assoc->state = SN_RM;/* Mark for removal*/
+ return(SN_NAT_PKT);
+ case SN_SCTP_SHUTACK: /* a packet containing a SHUTDOWN-ACK chunk */
+ assoc->state = SN_CL; /* Stay in Close state until timeout */
+ sctp_ResetTimeOut(la, assoc, SN_C_T(la));
+ return(SN_NAT_PKT);
+ case SN_SCTP_ABORT: /* a packet containing an ABORT chunk */
+ assoc->state = SN_RM;/* Mark for removal*/
+ return(SN_NAT_PKT);
+ default:
+ return(SN_DROP_PKT);
+ }
+ return(SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */
+}
+
+/* ----------------------------------------------------------------------
+ * HASH TABLE CODE
+ * ----------------------------------------------------------------------
+ */
+/** @addtogroup Hash
+ *
+ * The Hash functions facilitate searching the NAT Hash Tables for associations
+ * as well as adding/removing associations from the table(s).
+ */
+/** @ingroup Hash
+ * @brief Find the SCTP association given the local address, port and vtag
+ *
+ * Searches the local look-up table for the association entry matching the
+ * provided local <address:ports:vtag> tuple
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param l_addr local address
+ * @param g_addr global address
+ * @param l_vtag local Vtag
+ * @param l_port local Port
+ * @param g_port global Port
+ *
+ * @return pointer to association or NULL
+ */
+static struct sctp_nat_assoc*
+FindSctpLocal(struct libalias *la, struct in_addr l_addr, struct in_addr g_addr, uint32_t l_vtag, uint16_t l_port, uint16_t g_port)
+{
+ u_int i;
+ struct sctp_nat_assoc *assoc = NULL;
+ struct sctp_GlobalAddress *G_Addr = NULL;
+
+ if (l_vtag != 0) { /* an init packet, vtag==0 */
+ i = SN_TABLE_HASH(l_vtag, l_port, la->sctpNatTableSize);
+ LIST_FOREACH(assoc, &la->sctpTableLocal[i], list_L) {
+ if ((assoc->l_vtag == l_vtag) && (assoc->l_port == l_port) && (assoc->g_port == g_port)\
+ && (assoc->l_addr.s_addr == l_addr.s_addr)) {
+ if (assoc->num_Gaddr) {
+ LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
+ if(G_Addr->g_addr.s_addr == g_addr.s_addr)
+ return(assoc);
+ }
+ } else {
+ return(assoc);
+ }
+ }
+ }
+ }
+ return(NULL);
+}
+
+/** @ingroup Hash
+ * @brief Check for Global Clash
+ *
+ * Searches the global look-up table for the association entry matching the
+ * provided global <(addresses):ports:vtag> tuple
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param Cassoc association being checked for a clash
+ *
+ * @return pointer to association or NULL
+ */
+static struct sctp_nat_assoc*
+FindSctpGlobalClash(struct libalias *la, struct sctp_nat_assoc *Cassoc)
+{
+ u_int i;
+ struct sctp_nat_assoc *assoc = NULL;
+ struct sctp_GlobalAddress *G_Addr = NULL;
+ struct sctp_GlobalAddress *G_AddrC = NULL;
+
+ if (Cassoc->g_vtag != 0) { /* an init packet, vtag==0 */
+ i = SN_TABLE_HASH(Cassoc->g_vtag, Cassoc->g_port, la->sctpNatTableSize);
+ LIST_FOREACH(assoc, &la->sctpTableGlobal[i], list_G) {
+ if ((assoc->g_vtag == Cassoc->g_vtag) && (assoc->g_port == Cassoc->g_port) && (assoc->l_port == Cassoc->l_port)) {
+ if (assoc->num_Gaddr) {
+ LIST_FOREACH(G_AddrC, &(Cassoc->Gaddr), list_Gaddr) {
+ LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
+ if(G_Addr->g_addr.s_addr == G_AddrC->g_addr.s_addr)
+ return(assoc);
+ }
+ }
+ } else {
+ return(assoc);
+ }
+ }
+ }
+ }
+ return(NULL);
+}
+
+/** @ingroup Hash
+ * @brief Find the SCTP association given the global port and vtag
+ *
+ * Searches the global look-up table for the association entry matching the
+ * provided global <address:ports:vtag> tuple
+ *
+ * If all but the global address match it sets partial_match to 1 to indicate a
+ * partial match. If the NAT is tracking global IP addresses for this
+ * association, the NAT may respond with an ERRORM to request the missing
+ * address to be added.
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param g_addr global address
+ * @param g_vtag global vtag
+ * @param g_port global port
+ * @param l_port local port
+ *
+ * @return pointer to association or NULL
+ */
+static struct sctp_nat_assoc*
+FindSctpGlobal(struct libalias *la, struct in_addr g_addr, uint32_t g_vtag, uint16_t g_port, uint16_t l_port, int *partial_match)
+{
+ u_int i;
+ struct sctp_nat_assoc *assoc = NULL;
+ struct sctp_GlobalAddress *G_Addr = NULL;
+
+ *partial_match = 0;
+ if (g_vtag != 0) { /* an init packet, vtag==0 */
+ i = SN_TABLE_HASH(g_vtag, g_port, la->sctpNatTableSize);
+ LIST_FOREACH(assoc, &la->sctpTableGlobal[i], list_G) {
+ if ((assoc->g_vtag == g_vtag) && (assoc->g_port == g_port) && (assoc->l_port == l_port)) {
+ *partial_match = 1;
+ if (assoc->num_Gaddr) {
+ LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
+ if(G_Addr->g_addr.s_addr == g_addr.s_addr)
+ return(assoc);
+ }
+ } else {
+ return(assoc);
+ }
+ }
+ }
+ }
+ return(NULL);
+}
+
+/** @ingroup Hash
+ * @brief Find the SCTP association for a T-Flag message (given the global port and local vtag)
+ *
+ * Searches the local look-up table for a unique association entry matching the
+ * provided global port and local vtag information
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param g_addr global address
+ * @param l_vtag local Vtag
+ * @param g_port global Port
+ * @param l_port local Port
+ *
+ * @return pointer to association or NULL
+ */
+static struct sctp_nat_assoc*
+FindSctpLocalT(struct libalias *la, struct in_addr g_addr, uint32_t l_vtag, uint16_t g_port, uint16_t l_port)
+{
+ u_int i;
+ struct sctp_nat_assoc *assoc = NULL, *lastmatch = NULL;
+ struct sctp_GlobalAddress *G_Addr = NULL;
+ int cnt = 0;
+
+ if (l_vtag != 0) { /* an init packet, vtag==0 */
+ i = SN_TABLE_HASH(l_vtag, g_port, la->sctpNatTableSize);
+ LIST_FOREACH(assoc, &la->sctpTableGlobal[i], list_G) {
+ if ((assoc->g_vtag == l_vtag) && (assoc->g_port == g_port) && (assoc->l_port == l_port)) {
+ if (assoc->num_Gaddr) {
+ LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
+ if(G_Addr->g_addr.s_addr == G_Addr->g_addr.s_addr)
+ return(assoc); /* full match */
+ }
+ } else {
+ if (++cnt > 1) return(NULL);
+ lastmatch = assoc;
+ }
+ }
+ }
+ }
+ /* If there is more than one match we do not know which local address to send to */
+ return( cnt ? lastmatch : NULL );
+}
+
+/** @ingroup Hash
+ * @brief Find the SCTP association for a T-Flag message (given the local port and global vtag)
+ *
+ * Searches the global look-up table for a unique association entry matching the
+ * provided local port and global vtag information
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param g_addr global address
+ * @param g_vtag global vtag
+ * @param l_port local port
+ * @param g_port global port
+ *
+ * @return pointer to association or NULL
+ */
+static struct sctp_nat_assoc*
+FindSctpGlobalT(struct libalias *la, struct in_addr g_addr, uint32_t g_vtag, uint16_t l_port, uint16_t g_port)
+{
+ u_int i;
+ struct sctp_nat_assoc *assoc = NULL;
+ struct sctp_GlobalAddress *G_Addr = NULL;
+
+ if (g_vtag != 0) { /* an init packet, vtag==0 */
+ i = SN_TABLE_HASH(g_vtag, l_port, la->sctpNatTableSize);
+ LIST_FOREACH(assoc, &la->sctpTableLocal[i], list_L) {
+ if ((assoc->l_vtag == g_vtag) && (assoc->l_port == l_port) && (assoc->g_port == g_port)) {
+ if (assoc->num_Gaddr) {
+ LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
+ if(G_Addr->g_addr.s_addr == g_addr.s_addr)
+ return(assoc);
+ }
+ } else {
+ return(assoc);
+ }
+ }
+ }
+ }
+ return(NULL);
+}
+
+/** @ingroup Hash
+ * @brief Add the sctp association information to the local look up table
+ *
+ * Searches the local look-up table for an existing association with the same
+ * details. If a match exists and is ONLY in the local look-up table then this
+ * is a repeated INIT packet, we need to remove this association from the
+ * look-up table and add the new association
+ *
+ * The new association is added to the head of the list and state is updated
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param assoc pointer to sctp association
+ * @param g_addr global address
+ *
+ * @return SN_ADD_OK | SN_ADD_CLASH
+ */
+static int
+AddSctpAssocLocal(struct libalias *la, struct sctp_nat_assoc *assoc, struct in_addr g_addr)
+{
+ struct sctp_nat_assoc *found;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ found = FindSctpLocal(la, assoc->l_addr, g_addr, assoc->l_vtag, assoc->l_port, assoc->g_port);
+ /*
+ * Note that if a different global address initiated this Init,
+ * ie it wasn't resent as presumed:
+ * - the local receiver if receiving it for the first time will establish
+ * an association with the new global host
+ * - if receiving an init from a different global address after sending a
+ * lost initack it will send an initack to the new global host, the first
+ * association attempt will then be blocked if retried.
+ */
+ if (found != NULL) {
+ if ((found->TableRegister == SN_LOCAL_TBL) && (found->g_port == assoc->g_port)) { /* resent message */
+ RmSctpAssoc(la, found);
+ sctp_RmTimeOut(la, found);
+ freeGlobalAddressList(found);
+ sn_free(found);
+ } else
+ return(SN_ADD_CLASH);
+ }
+
+ LIST_INSERT_HEAD(&la->sctpTableLocal[SN_TABLE_HASH(assoc->l_vtag, assoc->l_port, la->sctpNatTableSize)],
+ assoc, list_L);
+ assoc->TableRegister |= SN_LOCAL_TBL;
+ la->sctpLinkCount++; //increment link count
+
+ if (assoc->TableRegister == SN_BOTH_TBL) {
+ /* libalias log -- controlled by libalias */
+ if (la->packetAliasMode & PKT_ALIAS_LOG)
+ SctpShowAliasStats(la);
+
+ SN_LOG(SN_LOG_INFO, logsctpassoc(assoc, "^"));
+ }
+
+ return(SN_ADD_OK);
+}
+
+/** @ingroup Hash
+ * @brief Add the sctp association information to the global look up table
+ *
+ * Searches the global look-up table for an existing association with the same
+ * details. If a match exists and is ONLY in the global look-up table then this
+ * is a repeated INIT packet, we need to remove this association from the
+ * look-up table and add the new association
+ *
+ * The new association is added to the head of the list and state is updated
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param assoc pointer to sctp association
+ *
+ * @return SN_ADD_OK | SN_ADD_CLASH
+ */
+static int
+AddSctpAssocGlobal(struct libalias *la, struct sctp_nat_assoc *assoc)
+{
+ struct sctp_nat_assoc *found;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ found = FindSctpGlobalClash(la, assoc);
+ if (found != NULL) {
+ if ((found->TableRegister == SN_GLOBAL_TBL) && \
+ (found->l_addr.s_addr == assoc->l_addr.s_addr) && (found->l_port == assoc->l_port)) { /* resent message */
+ RmSctpAssoc(la, found);
+ sctp_RmTimeOut(la, found);
+ freeGlobalAddressList(found);
+ sn_free(found);
+ } else
+ return(SN_ADD_CLASH);
+ }
+
+ LIST_INSERT_HEAD(&la->sctpTableGlobal[SN_TABLE_HASH(assoc->g_vtag, assoc->g_port, la->sctpNatTableSize)],
+ assoc, list_G);
+ assoc->TableRegister |= SN_GLOBAL_TBL;
+ la->sctpLinkCount++; //increment link count
+
+ if (assoc->TableRegister == SN_BOTH_TBL) {
+ /* libalias log -- controlled by libalias */
+ if (la->packetAliasMode & PKT_ALIAS_LOG)
+ SctpShowAliasStats(la);
+
+ SN_LOG(SN_LOG_INFO, logsctpassoc(assoc, "^"));
+ }
+
+ return(SN_ADD_OK);
+}
+
+/** @ingroup Hash
+ * @brief Remove the sctp association information from the look up table
+ *
+ * For each of the two (local/global) look-up tables, remove the association
+ * from that table IF it has been registered in that table.
+ *
+ * NOTE: The calling code is responsible for freeing memory allocated to the
+ * association structure itself
+ *
+ * NOTE: The association is NOT removed from the timer queue
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param assoc pointer to sctp association
+ */
+static void
+RmSctpAssoc(struct libalias *la, struct sctp_nat_assoc *assoc)
+{
+ // struct sctp_nat_assoc *found;
+ if (assoc == NULL) {
+ /* very bad, log and die*/
+ SN_LOG(SN_LOG_LOW,
+ logsctperror("ERROR: alias_sctp:RmSctpAssoc(NULL)\n", 0, 0, SN_TO_NODIR));
+ return;
+ }
+ /* log if association is fully up and now closing */
+ if (assoc->TableRegister == SN_BOTH_TBL) {
+ SN_LOG(SN_LOG_INFO, logsctpassoc(assoc, "$"));
+ }
+ LIBALIAS_LOCK_ASSERT(la);
+ if (assoc->TableRegister & SN_LOCAL_TBL) {
+ assoc->TableRegister ^= SN_LOCAL_TBL;
+ la->sctpLinkCount--; //decrement link count
+ LIST_REMOVE(assoc, list_L);
+ }
+
+ if (assoc->TableRegister & SN_GLOBAL_TBL) {
+ assoc->TableRegister ^= SN_GLOBAL_TBL;
+ la->sctpLinkCount--; //decrement link count
+ LIST_REMOVE(assoc, list_G);
+ }
+ // sn_free(assoc); //Don't remove now, remove if needed later
+ /* libalias logging -- controlled by libalias log definition */
+ if (la->packetAliasMode & PKT_ALIAS_LOG)
+ SctpShowAliasStats(la);
+}
+
+/**
+ * @ingroup Hash
+ * @brief free the Global Address List memory
+ *
+ * freeGlobalAddressList deletes all global IP addresses in an associations
+ * global IP address list.
+ *
+ * @param assoc
+ */
+static void freeGlobalAddressList(struct sctp_nat_assoc *assoc)
+{
+ struct sctp_GlobalAddress *gaddr1=NULL,*gaddr2=NULL;
+ /*free global address list*/
+ gaddr1 = LIST_FIRST(&(assoc->Gaddr));
+ while (gaddr1 != NULL) {
+ gaddr2 = LIST_NEXT(gaddr1, list_Gaddr);
+ sn_free(gaddr1);
+ gaddr1 = gaddr2;
+ }
+}
+/* ----------------------------------------------------------------------
+ * TIMER QUEUE CODE
+ * ----------------------------------------------------------------------
+ */
+/** @addtogroup Timer
+ *
+ * The timer queue management functions are designed to operate efficiently with
+ * a minimum of interaction with the queues.
+ *
+ * Once a timeout is set in the queue it will not be altered in the queue unless
+ * it has to be changed to a shorter time (usually only for aborts and closing).
+ * On a queue timeout, the real expiry time is checked, and if not leq than the
+ * timeout it is requeued (O(1)) at its later time. This is especially important
+ * for normal packets sent during an association. When a timer expires, it is
+ * updated to its new expiration time if necessary, or processed as a
+ * timeout. This means that while in UP state, the timing queue is only altered
+ * every U_T (every few minutes) for a particular association.
+ */
+/** @ingroup Timer
+ * @brief Add an association timeout to the timer queue
+ *
+ * Determine the location in the queue to add the timeout and insert the
+ * association into the list at that queue position
+ *
+ * @param la
+ * @param assoc
+ */
+static void
+sctp_AddTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc)
+{
+ int add_loc;
+ LIBALIAS_LOCK_ASSERT(la);
+ add_loc = assoc->exp - la->sctpNatTimer.loc_time + la->sctpNatTimer.cur_loc;
+ if (add_loc >= SN_TIMER_QUEUE_SIZE)
+ add_loc -= SN_TIMER_QUEUE_SIZE;
+ LIST_INSERT_HEAD(&la->sctpNatTimer.TimerQ[add_loc], assoc, timer_Q);
+ assoc->exp_loc = add_loc;
+}
+
+/** @ingroup Timer
+ * @brief Remove an association from timer queue
+ *
+ * This is an O(1) operation to remove the association pointer from its
+ * current position in the timer queue
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param assoc pointer to sctp association
+ */
+static void
+sctp_RmTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc)
+{
+ LIBALIAS_LOCK_ASSERT(la);
+ LIST_REMOVE(assoc, timer_Q);/* Note this is O(1) */
+}
+
+
+/** @ingroup Timer
+ * @brief Reset timer in timer queue
+ *
+ * Reset the actual timeout for the specified association. If it is earlier than
+ * the existing timeout, then remove and re-install the association into the
+ * queue
+ *
+ * @param la Pointer to the relevant libalias instance
+ * @param assoc pointer to sctp association
+ * @param newexp New expiration time
+ */
+static void
+sctp_ResetTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc, int newexp)
+{
+ if (newexp < assoc->exp) {
+ sctp_RmTimeOut(la, assoc);
+ assoc->exp = newexp;
+ sctp_AddTimeOut(la, assoc);
+ } else {
+ assoc->exp = newexp;
+ }
+}
+
+/** @ingroup Timer
+ * @brief Check timer Q against current time
+ *
+ * Loop through each entry in the timer queue since the last time we processed
+ * the timer queue until now (the current time). For each association in the
+ * event list, we remove it from that position in the timer queue and check if
+ * it has really expired. If so we:
+ * - Log the timer expiry
+ * - Remove the association from the NAT tables
+ * - Release the memory used by the association
+ *
+ * If the timer hasn't really expired we place the association into its new
+ * correct position in the timer queue.
+ *
+ * @param la Pointer to the relevant libalias instance
+ */
+void
+sctp_CheckTimers(struct libalias *la)
+{
+ struct sctp_nat_assoc *assoc;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ while(la->timeStamp >= la->sctpNatTimer.loc_time) {
+ while (!LIST_EMPTY(&la->sctpNatTimer.TimerQ[la->sctpNatTimer.cur_loc])) {
+ assoc = LIST_FIRST(&la->sctpNatTimer.TimerQ[la->sctpNatTimer.cur_loc]);
+ //SLIST_REMOVE_HEAD(&la->sctpNatTimer.TimerQ[la->sctpNatTimer.cur_loc], timer_Q);
+ LIST_REMOVE(assoc, timer_Q);
+ if (la->timeStamp >= assoc->exp) { /* state expired */
+ SN_LOG(((assoc->state == SN_CL)?(SN_LOG_DEBUG):(SN_LOG_INFO)),
+ logsctperror("Timer Expired", assoc->g_vtag, assoc->state, SN_TO_NODIR));
+ RmSctpAssoc(la, assoc);
+ freeGlobalAddressList(assoc);
+ sn_free(assoc);
+ } else {/* state not expired, reschedule timer*/
+ sctp_AddTimeOut(la, assoc);
+ }
+ }
+ /* Goto next location in the timer queue*/
+ ++la->sctpNatTimer.loc_time;
+ if (++la->sctpNatTimer.cur_loc >= SN_TIMER_QUEUE_SIZE)
+ la->sctpNatTimer.cur_loc = 0;
+ }
+}
+
+/* ----------------------------------------------------------------------
+ * LOGGING CODE
+ * ----------------------------------------------------------------------
+ */
+/** @addtogroup Logging
+ *
+ * The logging functions provide logging of different items ranging from logging
+ * a simple message, through logging an association details to logging the
+ * current state of the NAT tables
+ */
+/** @ingroup Logging
+ * @brief Log sctp nat errors
+ *
+ * @param errormsg Error message to be logged
+ * @param vtag Current Vtag
+ * @param error Error number
+ * @param direction Direction of packet
+ */
+static void
+logsctperror(char* errormsg, uint32_t vtag, int error, int direction)
+{
+ char dir;
+ switch(direction) {
+ case SN_TO_LOCAL:
+ dir = 'L';
+ break;
+ case SN_TO_GLOBAL:
+ dir = 'G';
+ break;
+ default:
+ dir = '*';
+ break;
+ }
+ SctpAliasLog("->%c %s (vt=%u) %d\n", dir, errormsg, ntohl(vtag), error);
+}
+
+/** @ingroup Logging
+ * @brief Log what the parser parsed
+ *
+ * @param direction Direction of packet
+ * @param sm Pointer to sctp message information
+ */
+static void
+logsctpparse(int direction, struct sctp_nat_msg *sm)
+{
+ char *ploc, *pstate;
+ switch(direction) {
+ case SN_TO_LOCAL:
+ ploc = "TO_LOCAL -";
+ break;
+ case SN_TO_GLOBAL:
+ ploc = "TO_GLOBAL -";
+ break;
+ default:
+ ploc = "";
+ }
+ switch(sm->msg) {
+ case SN_SCTP_INIT:
+ pstate = "Init";
+ break;
+ case SN_SCTP_INITACK:
+ pstate = "InitAck";
+ break;
+ case SN_SCTP_ABORT:
+ pstate = "Abort";
+ break;
+ case SN_SCTP_SHUTACK:
+ pstate = "ShutAck";
+ break;
+ case SN_SCTP_SHUTCOMP:
+ pstate = "ShutComp";
+ break;
+ case SN_SCTP_ASCONF:
+ pstate = "Asconf";
+ break;
+ case SN_SCTP_ASCONFACK:
+ pstate = "AsconfAck";
+ break;
+ case SN_SCTP_OTHER:
+ pstate = "Other";
+ break;
+ default:
+ pstate = "***ERROR***";
+ break;
+ }
+ SctpAliasLog("Parsed: %s %s\n", ploc, pstate);
+}
+
+/** @ingroup Logging
+ * @brief Log an SCTP association's details
+ *
+ * @param assoc pointer to sctp association
+ * @param s Character that indicates the state of processing for this packet
+ */
+static void logsctpassoc(struct sctp_nat_assoc *assoc, char* s)
+{
+ struct sctp_GlobalAddress *G_Addr = NULL;
+ char *sp;
+ switch(assoc->state) {
+ case SN_ID:
+ sp = "ID ";
+ break;
+ case SN_INi:
+ sp = "INi ";
+ break;
+ case SN_INa:
+ sp = "INa ";
+ break;
+ case SN_UP:
+ sp = "UP ";
+ break;
+ case SN_CL:
+ sp = "CL ";
+ break;
+ case SN_RM:
+ sp = "RM ";
+ break;
+ default:
+ sp = "***ERROR***";
+ break;
+ }
+ SctpAliasLog("%sAssoc: %s exp=%u la=%s lv=%u lp=%u gv=%u gp=%u tbl=%d\n",
+ s, sp, assoc->exp, inet_ntoa(assoc->l_addr), ntohl(assoc->l_vtag),
+ ntohs(assoc->l_port), ntohl(assoc->g_vtag), ntohs(assoc->g_port),
+ assoc->TableRegister);
+ /* list global addresses */
+ LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
+ SctpAliasLog("\t\tga=%s\n",inet_ntoa(G_Addr->g_addr));
+ }
+}
+
+/** @ingroup Logging
+ * @brief Output Global table to log
+ *
+ * @param la Pointer to the relevant libalias instance
+ */
+static void logSctpGlobal(struct libalias *la)
+{
+ u_int i;
+ struct sctp_nat_assoc *assoc = NULL;
+
+ SctpAliasLog("G->\n");
+ for (i=0; i < la->sctpNatTableSize; i++) {
+ LIST_FOREACH(assoc, &la->sctpTableGlobal[i], list_G) {
+ logsctpassoc(assoc, " ");
+ }
+ }
+}
+
+/** @ingroup Logging
+ * @brief Output Local table to log
+ *
+ * @param la Pointer to the relevant libalias instance
+ */
+static void logSctpLocal(struct libalias *la)
+{
+ u_int i;
+ struct sctp_nat_assoc *assoc = NULL;
+
+ SctpAliasLog("L->\n");
+ for (i=0; i < la->sctpNatTableSize; i++) {
+ LIST_FOREACH(assoc, &la->sctpTableLocal[i], list_L) {
+ logsctpassoc(assoc, " ");
+ }
+ }
+}
+
+/** @ingroup Logging
+ * @brief Output timer queue to log
+ *
+ * @param la Pointer to the relevant libalias instance
+ */
+static void logTimerQ(struct libalias *la)
+{
+ static char buf[50];
+ u_int i;
+ struct sctp_nat_assoc *assoc = NULL;
+
+ SctpAliasLog("t->\n");
+ for (i=0; i < SN_TIMER_QUEUE_SIZE; i++) {
+ LIST_FOREACH(assoc, &la->sctpNatTimer.TimerQ[i], timer_Q) {
+ snprintf(buf, 50, " l=%u ",i);
+ //SctpAliasLog(la->logDesc," l=%d ",i);
+ logsctpassoc(assoc, buf);
+ }
+ }
+}
+
+/** @ingroup Logging
+ * @brief Sctp NAT logging function
+ *
+ * This function is based on a similar function in alias_db.c
+ *
+ * @param str/stream logging descriptor
+ * @param format printf type string
+ */
+#ifdef _KERNEL
+static void
+SctpAliasLog(const char *format, ...)
+{
+ char buffer[LIBALIAS_BUF_SIZE];
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(buffer, LIBALIAS_BUF_SIZE, format, ap);
+ va_end(ap);
+ log(LOG_SECURITY | LOG_INFO,
+ "alias_sctp: %s", buffer);
+}
+#else
+static void
+SctpAliasLog(FILE *stream, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(stream, format, ap);
+ va_end(ap);
+ fflush(stream);
+}
+#endif
diff --git a/sys/netinet/libalias/alias_sctp.h b/sys/netinet/libalias/alias_sctp.h
new file mode 100644
index 0000000..7953f43
--- /dev/null
+++ b/sys/netinet/libalias/alias_sctp.h
@@ -0,0 +1,201 @@
+/**
+ * @file alias_sctp.h
+ * Copyright (c) 2008, Centre for Advanced Internet Architectures
+ * Swinburne University of Technology, Melbourne, Australia
+ * (CRICOS number 00111D).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The names of the authors, the "Centre for Advanced Internet Architectures"
+ * and "Swinburne University of Technology" may not be used to endorse
+ * or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS "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 AUTHORS OR CONTRIBUTORS 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.
+ *
+ * Alias_sctp forms part of the libalias kernel module to handle
+ * Network Address Translation (NAT) for the SCTP protocol.
+ *
+ * This software was developed by David A. Hayes
+ * with leadership and advice from Jason But
+ *
+ * The design is outlined in CAIA technical report number 080618A
+ * (D. Hayes and J. But, "Alias_sctp Version 0.1: SCTP NAT implementation in IPFW")
+ *
+ * Development is part of the CAIA SONATA project,
+ * proposed by Jason But and Grenville Armitage:
+ * http://caia.swin.edu.au/urp/sonata/
+ *
+ *
+ * This project has been made possible in part by a grant from
+ * the Cisco University Research Program Fund at Community
+ * Foundation Silicon Valley.
+ *
+ */
+
+/* $FreeBSD$ */
+
+#ifndef _ALIAS_SCTP_H_
+#define _ALIAS_SCTP_H_
+
+#include <sys/param.h>
+#ifdef _KERNEL
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/uio.h>
+#include <sys/socketvar.h>
+#include <sys/syslog.h>
+#endif // #ifdef _KERNEL
+#include <sys/types.h>
+
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+/**
+ * These are defined in sctp_os_bsd.h, but it can't be included due to its local file
+ * inclusion, so I'm defining them here.
+ *
+ */
+#include <machine/cpufunc.h>
+#include <machine/cpu.h>
+/* The packed define for 64 bit platforms */
+#ifndef SCTP_PACKED
+#define SCTP_PACKED __attribute__((packed))
+#endif //#ifndef SCTP_PACKED
+#ifndef SCTP_UNUSED
+#define SCTP_UNUSED __attribute__((unused))
+#endif //#ifndef SCTP_UNUSED
+
+
+#include <netinet/sctp.h>
+//#include <netinet/sctp_os_bsd.h> --might be needed later for mbuf stuff
+#include <netinet/sctp_header.h>
+
+#ifndef _KERNEL
+#include <stdlib.h>
+#include <stdio.h>
+#include <curses.h>
+#endif //#ifdef _KERNEL
+
+
+#define LINK_SCTP IPPROTO_SCTP
+
+
+#define SN_TO_LOCAL 0 /**< packet traveling from global to local */
+#define SN_TO_GLOBAL 1 /**< packet traveling from local to global */
+#define SN_TO_NODIR 99 /**< used where direction is not important */
+
+#define SN_NAT_PKT 0x0000 /**< Network Address Translate packet */
+#define SN_DROP_PKT 0x0001 /**< drop packet (don't forward it) */
+#define SN_PROCESSING_ERROR 0x0003 /**< Packet processing error */
+#define SN_REPLY_ABORT 0x0010 /**< Reply with ABORT to sender (don't forward it) */
+#define SN_SEND_ABORT 0x0020 /**< Send ABORT to destination */
+#define SN_TX_ABORT 0x0030 /**< mask for transmitting abort */
+#define SN_REFLECT_ERROR 0x0100 /**< Reply with ERROR to sender on OOTB packet Tbit set */
+#define SN_REPLY_ERROR 0x0200 /**< Reply with ERROR to sender on ASCONF clash */
+#define SN_TX_ERROR 0x0300 /**< mask for transmitting error */
+
+
+#define PKT_ALIAS_RESPOND 0x1000 /**< Signal to libalias that there is a response packet to send */
+/*
+ * Data structures
+ */
+
+/**
+ * @brief sctp association information
+ *
+ * Structure that contains information about a particular sctp association
+ * currently under Network Address Translation.
+ * Information is stored in network byte order (as is libalias)***
+ */
+struct sctp_nat_assoc {
+ uint32_t l_vtag; /**< local side verification tag */
+ uint16_t l_port; /**< local side port number */
+ uint32_t g_vtag; /**< global side verification tag */
+ uint16_t g_port; /**< global side port number */
+ struct in_addr l_addr; /**< local ip address */
+ struct in_addr a_addr; /**< alias ip address */
+ int state; /**< current state of NAT association */
+ int TableRegister; /**< stores which look up tables association is registered in */
+ int exp; /**< timer expiration in seconds from uptime */
+ int exp_loc; /**< current location in timer_Q */
+ int num_Gaddr; /**< number of global IP addresses in the list */
+ LIST_HEAD(sctpGlobalAddresshead,sctp_GlobalAddress) Gaddr; /**< List of global addresses */
+ LIST_ENTRY (sctp_nat_assoc) list_L; /**< Linked list of pointers for Local table*/
+ LIST_ENTRY (sctp_nat_assoc) list_G; /**< Linked list of pointers for Global table */
+ LIST_ENTRY (sctp_nat_assoc) timer_Q; /**< Linked list of pointers for timer Q */
+//Using libalias locking
+};
+
+struct sctp_GlobalAddress {
+ struct in_addr g_addr;
+ LIST_ENTRY (sctp_GlobalAddress) list_Gaddr; /**< Linked list of pointers for Global table */
+};
+
+/**
+ * @brief SCTP chunk of interest
+ *
+ * The only chunks whose contents are of any interest are the INIT and ASCONF_AddIP
+ */
+union sctpChunkOfInt {
+ struct sctp_init *Init; /**< Pointer to Init Chunk */
+ struct sctp_init_ack *InitAck; /**< Pointer to Init Chunk */
+ struct sctp_paramhdr *Asconf; /**< Pointer to ASCONF chunk */
+};
+
+
+/**
+ * @brief SCTP message
+ *
+ * Structure containing the relevant information from the SCTP message
+ */
+struct sctp_nat_msg {
+ uint16_t msg; /**< one of the key messages defined above */
+#ifdef INET6
+ // struct ip6_hdr *ip_hdr; /**< pointer to ip packet header */ /*no inet6 support yet*/
+#else
+ struct ip *ip_hdr; /**< pointer to ip packet header */
+#endif //#ifdef INET6
+ struct sctphdr *sctp_hdr; /**< pointer to sctp common header */
+ union sctpChunkOfInt sctpchnk; /**< union of pointers to the chunk of interest */
+ int chunk_length; /**< length of chunk of interest */
+};
+
+
+/**
+ * @brief sctp nat timer queue structure
+ *
+ */
+
+struct sctp_nat_timer {
+ int loc_time; /**< time in seconds for the current location in the queue */
+ int cur_loc; /**< index of the current location in the circular queue */
+ LIST_HEAD(sctpTimerQ,sctp_nat_assoc) *TimerQ; /**< List of associations at this position in the timer Q */
+};
+
+
+
+#endif //#ifndef _ALIAS_SCTP_H
diff --git a/sys/netinet/sctp_crc32.c b/sys/netinet/sctp_crc32.c
index b85ccdd..3fba39e 100644
--- a/sys/netinet/sctp_crc32.c
+++ b/sys/netinet/sctp_crc32.c
@@ -599,7 +599,7 @@ sctp_crc32c_sb8_64_bit(uint32_t crc,
*
* none
*/
-static uint32_t
+uint32_t
update_crc32(uint32_t crc32c,
unsigned char *buffer,
unsigned int length)
@@ -697,7 +697,7 @@ old_update_crc32(uint32_t crc32c,
}
-static uint32_t
+uint32_t
sctp_finalize_crc32(uint32_t crc32c)
{
uint32_t result;
diff --git a/sys/netinet/sctp_crc32.h b/sys/netinet/sctp_crc32.h
index 634acd4..2c353d2 100644
--- a/sys/netinet/sctp_crc32.h
+++ b/sys/netinet/sctp_crc32.h
@@ -39,6 +39,8 @@ __FBSDID("$FreeBSD$");
#if defined(_KERNEL) || defined(__Userspace__)
uint32_t sctp_calculate_cksum(struct mbuf *, uint32_t);
void sctp_delayed_cksum(struct mbuf *);
+uint32_t update_crc32(uint32_t, unsigned char *, unsigned int);
+uint32_t sctp_finalize_crc32(uint32_t);
#endif /* _KERNEL */
OpenPOWER on IntegriCloud