1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2008
5 *	Swinburne University of Technology, Melbourne, Australia.
6 *
7 *  Redistribution and use in source and binary forms, with or without
8 *  modification, are permitted provided that the following conditions
9 *  are met:
10 *  1. Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 *  2. Redistributions in binary form must reproduce the above copyright
13 *     notice, this list of conditions and the following disclaimer in the
14 *     documentation and/or other materials provided with the distribution.
15 *
16 *  THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS "AS IS" AND
17 *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 *  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20 *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 *  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 *  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 *  SUCH DAMAGE.
27 */
28
29/*
30 * Alias_sctp forms part of the libalias kernel module to handle
31 * Network Address Translation (NAT) for the SCTP protocol.
32 *
33 *  This software was developed by David A. Hayes and Jason But
34 *
35 * The design is outlined in CAIA technical report number  080618A
36 * (D. Hayes and J. But, "Alias_sctp Version 0.1: SCTP NAT implementation in IPFW")
37 *
38 * Development is part of the CAIA SONATA project,
39 * proposed by Jason But and Grenville Armitage:
40 * http://caia.swin.edu.au/urp/sonata/
41 *
42 *
43 * This project has been made possible in part by a grant from
44 * the Cisco University Research Program Fund at Community
45 * Foundation Silicon Valley.
46 *
47 */
48/** @mainpage
49 * Alias_sctp is part of the SONATA (http://caia.swin.edu.au/urp/sonata) project
50 * to develop and release a BSD licensed implementation of a Network Address
51 * Translation (NAT) module that supports the Stream Control Transmission
52 * Protocol (SCTP).
53 *
54 * Traditional address and port number look ups are inadequate for SCTP's
55 * operation due to both processing requirements and issues with multi-homing.
56 * Alias_sctp integrates with FreeBSD's ipfw/libalias NAT system.
57 *
58 * Version 0.2 features include:
59 * - Support for global multi-homing
60 * - Support for ASCONF modification from Internet Draft
61 *   (draft-stewart-behave-sctpnat-04, R. Stewart and M. Tuexen, "Stream control
62 *   transmission protocol (SCTP) network address translation," Jul. 2008) to
63 *   provide support for multi-homed privately addressed hosts
64 * - Support for forwarding of T-flagged packets
65 * - Generation and delivery of AbortM/ErrorM packets upon detection of NAT
66 *   collisions
67 * - Per-port forwarding rules
68 * - Dynamically controllable logging and statistics
69 * - Dynamic management of timers
70 * - Dynamic control of hash-table size
71 */
72
73/* $FreeBSD$ */
74
75#ifdef _KERNEL
76#include <machine/stdarg.h>
77#include <sys/param.h>
78#include <sys/gsb_crc32.h>
79#include <sys/systm.h>
80#include <sys/kernel.h>
81#include <sys/module.h>
82#include <sys/syslog.h>
83#include <netinet/libalias/alias_sctp.h>
84#include <netinet/libalias/alias.h>
85#include <netinet/libalias/alias_local.h>
86#include <netinet/sctp_crc32.h>
87#include <machine/in_cksum.h>
88#else
89#include "alias_sctp.h"
90#include <arpa/inet.h>
91#include "alias.h"
92#include "alias_local.h"
93#include <machine/in_cksum.h>
94#include <sys/libkern.h>
95#endif //#ifdef _KERNEL
96
97/* ----------------------------------------------------------------------
98 *                          FUNCTION PROTOTYPES
99 * ----------------------------------------------------------------------
100 */
101/* Packet Parsing Functions */
102static int sctp_PktParser(struct libalias *la, int direction, struct ip *pip,
103    struct sctp_nat_msg *sm, struct sctp_nat_assoc **passoc);
104static int GetAsconfVtags(struct libalias *la, struct sctp_nat_msg *sm,
105    uint32_t *l_vtag, uint32_t *g_vtag, int direction);
106static int IsASCONFack(struct libalias *la, struct sctp_nat_msg *sm, int direction);
107
108static void AddGlobalIPAddresses(struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int direction);
109static int  Add_Global_Address_to_List(struct sctp_nat_assoc *assoc,  struct sctp_GlobalAddress *G_addr);
110static void RmGlobalIPAddresses(struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int direction);
111static int IsADDorDEL(struct libalias *la, struct sctp_nat_msg *sm, int direction);
112
113/* State Machine Functions */
114static int ProcessSctpMsg(struct libalias *la, int direction, \
115    struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc);
116
117static int ID_process(struct libalias *la, int direction,\
118    struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm);
119static int INi_process(struct libalias *la, int direction,\
120    struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm);
121static int INa_process(struct libalias *la, int direction,\
122    struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm);
123static int UP_process(struct libalias *la, int direction,\
124    struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm);
125static int CL_process(struct libalias *la, int direction,\
126    struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm);
127static void TxAbortErrorM(struct libalias *la,  struct sctp_nat_msg *sm,\
128    struct sctp_nat_assoc *assoc, int sndrply, int direction);
129
130/* Hash Table Functions */
131static struct sctp_nat_assoc *
132FindSctpLocal(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);
133static struct sctp_nat_assoc *
134FindSctpGlobal(struct libalias *la, struct in_addr g_addr, uint32_t g_vtag, uint16_t g_port, uint16_t l_port, int *partial_match);
135static struct sctp_nat_assoc *
136FindSctpGlobalClash(struct libalias *la,  struct sctp_nat_assoc *Cassoc);
137static struct sctp_nat_assoc *
138FindSctpLocalT(struct libalias *la,  struct in_addr g_addr, uint32_t l_vtag, uint16_t g_port, uint16_t l_port);
139static struct sctp_nat_assoc *
140FindSctpGlobalT(struct libalias *la, struct in_addr g_addr, uint32_t g_vtag, uint16_t l_port, uint16_t g_port);
141
142static int AddSctpAssocLocal(struct libalias *la, struct sctp_nat_assoc *assoc, struct in_addr g_addr);
143static int AddSctpAssocGlobal(struct libalias *la, struct sctp_nat_assoc *assoc);
144static void RmSctpAssoc(struct libalias *la, struct sctp_nat_assoc *assoc);
145static void freeGlobalAddressList(struct sctp_nat_assoc *assoc);
146
147/* Timer Queue Functions */
148static void sctp_AddTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc);
149static void sctp_RmTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc);
150static void sctp_ResetTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc, int newexp);
151void sctp_CheckTimers(struct libalias *la);
152
153/* Logging Functions */
154static void logsctperror(char *errormsg, uint32_t vtag, int error, int direction);
155static void logsctpparse(int direction, struct sctp_nat_msg *sm);
156static void logsctpassoc(struct sctp_nat_assoc *assoc, char *s);
157static void logTimerQ(struct libalias *la);
158static void logSctpGlobal(struct libalias *la);
159static void logSctpLocal(struct libalias *la);
160#ifdef _KERNEL
161static void SctpAliasLog(const char *format, ...);
162#endif
163
164/** @defgroup external External code changes and modifications
165 *
166 * Some changes have been made to files external to alias_sctp.(c|h). These
167 * changes are primarily due to code needing to call static functions within
168 * those files or to perform extra functionality that can only be performed
169 * within these files.
170 */
171/** @ingroup external
172 * @brief Log current statistics for the libalias instance
173 *
174 * This function is defined in alias_db.c, since it calls static functions in
175 * this file
176 *
177 * Calls the higher level ShowAliasStats() in alias_db.c which logs all current
178 * statistics about the libalias instance - including SCTP statistics
179 *
180 * @param la Pointer to the libalias instance
181 */
182void SctpShowAliasStats(struct libalias *la);
183
184#ifdef _KERNEL
185
186static MALLOC_DEFINE(M_SCTPNAT, "sctpnat", "sctp nat dbs");
187/* Use kernel allocator. */
188#ifdef _SYS_MALLOC_H_
189#define	sn_malloc(x)	malloc(x, M_SCTPNAT, M_NOWAIT|M_ZERO)
190#define	sn_calloc(n,x)	mallocarray((n), (x), M_SCTPNAT, M_NOWAIT|M_ZERO)
191#define	sn_free(x)	free(x, M_SCTPNAT)
192#endif// #ifdef _SYS_MALLOC_H_
193
194#else //#ifdef	_KERNEL
195#define	sn_malloc(x)	malloc(x)
196#define	sn_calloc(n, x)	calloc(n, x)
197#define	sn_free(x)	free(x)
198
199#endif //#ifdef	_KERNEL
200
201/** @defgroup packet_parser SCTP Packet Parsing
202 *
203 * Macros to:
204 * - Return pointers to the first and next SCTP chunks within an SCTP Packet
205 * - Define possible return values of the packet parsing process
206 * - SCTP message types for storing in the sctp_nat_msg structure @{
207 */
208
209#define SN_SCTP_FIRSTCHUNK(sctphead)	(struct sctp_chunkhdr *)(((char *)sctphead) + sizeof(struct sctphdr))
210/**< Returns a pointer to the first chunk in an SCTP packet given a pointer to the SCTP header */
211
212#define SN_SCTP_NEXTCHUNK(chunkhead)	(struct sctp_chunkhdr *)(((char *)chunkhead) + SCTP_SIZE32(ntohs(chunkhead->chunk_length)))
213/**< Returns a pointer to the next chunk in an SCTP packet given a pointer to the current chunk */
214
215#define SN_SCTP_NEXTPARAM(param)	(struct sctp_paramhdr *)(((char *)param) + SCTP_SIZE32(ntohs(param->param_length)))
216/**< Returns a pointer to the next parameter in an SCTP packet given a pointer to the current parameter */
217
218#define SN_MIN_CHUNK_SIZE        4    /**< Smallest possible SCTP chunk size in bytes */
219#define SN_MIN_PARAM_SIZE        4    /**< Smallest possible SCTP param size in bytes */
220#define SN_VTAG_PARAM_SIZE      12    /**< Size of  SCTP ASCONF vtag param in bytes */
221#define SN_ASCONFACK_PARAM_SIZE  8    /**< Size of  SCTP ASCONF ACK param in bytes */
222
223/* Packet parsing return codes */
224#define SN_PARSE_OK                  0    /**< Packet parsed for SCTP messages */
225#define SN_PARSE_ERROR_IPSHL         1    /**< Packet parsing error - IP and SCTP common header len */
226#define SN_PARSE_ERROR_AS_MALLOC     2    /**< Packet parsing error - assoc malloc */
227#define SN_PARSE_ERROR_CHHL          3    /**< Packet parsing error - Chunk header len */
228#define SN_PARSE_ERROR_DIR           4    /**< Packet parsing error - Direction */
229#define SN_PARSE_ERROR_VTAG          5    /**< Packet parsing error - Vtag */
230#define SN_PARSE_ERROR_CHUNK         6    /**< Packet parsing error - Chunk */
231#define SN_PARSE_ERROR_PORT          7    /**< Packet parsing error - Port=0 */
232#define SN_PARSE_ERROR_LOOKUP        8    /**< Packet parsing error - Lookup */
233#define SN_PARSE_ERROR_PARTIALLOOKUP 9    /**< Packet parsing error - partial lookup only found */
234#define SN_PARSE_ERROR_LOOKUP_ABORT  10   /**< Packet parsing error - Lookup - but abort packet */
235
236/* Alias_sctp performs its processing based on a number of key messages */
237#define SN_SCTP_ABORT       0x0000    /**< a packet containing an ABORT chunk */
238#define SN_SCTP_INIT        0x0001    /**< a packet containing an INIT chunk */
239#define SN_SCTP_INITACK     0x0002    /**< a packet containing an INIT-ACK chunk */
240#define SN_SCTP_SHUTCOMP    0x0010    /**< a packet containing a SHUTDOWN-COMPLETE chunk */
241#define SN_SCTP_SHUTACK     0x0020    /**< a packet containing a SHUTDOWN-ACK chunk */
242#define SN_SCTP_ASCONF      0x0100    /**< a packet containing an ASCONF chunk */
243#define SN_SCTP_ASCONFACK   0x0200    /**< a packet containing an ASCONF-ACK chunk */
244#define SN_SCTP_OTHER       0xFFFF    /**< a packet containing a chunk that is not of interest */
245/** @}
246 * @defgroup state_machine SCTP NAT State Machine
247 *
248 * Defines the various states an association can be within the NAT @{
249 */
250#define SN_ID  0x0000		/**< Idle state */
251#define SN_INi 0x0010		/**< Initialising, waiting for InitAck state */
252#define SN_INa 0x0020		/**< Initialising, waiting for AddIpAck state */
253#define SN_UP  0x0100		/**< Association in UP state */
254#define SN_CL  0x1000		/**< Closing state */
255#define SN_RM  0x2000		/**< Removing state */
256/** @}
257 * @defgroup Logging Logging Functionality
258 *
259 * Define various log levels and a macro to call specified log functions only if
260 * the current log level (sysctl_log_level) matches the specified level @{
261 */
262#define	SN_LOG_LOW	  0
263#define SN_LOG_EVENT      1
264#define	SN_LOG_INFO	  2
265#define	SN_LOG_DETAIL	  3
266#define	SN_LOG_DEBUG	  4
267#define	SN_LOG_DEBUG_MAX  5
268
269#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 */
270/** @}
271 * @defgroup Hash Hash Table Macros and Functions
272 *
273 * Defines minimum/maximum/default values for the hash table size @{
274 */
275#define SN_MIN_HASH_SIZE        101   /**< Minimum hash table size (set to stop users choosing stupid values) */
276#define SN_MAX_HASH_SIZE    1000001   /**< Maximum hash table size (NB must be less than max int) */
277#define SN_DEFAULT_HASH_SIZE   2003   /**< A reasonable default size for the hash tables */
278
279#define SN_LOCAL_TBL           0x01   /**< assoc in local table */
280#define SN_GLOBAL_TBL          0x02   /**< assoc in global table */
281#define SN_BOTH_TBL            0x03   /**< assoc in both tables */
282#define SN_WAIT_TOLOCAL        0x10   /**< assoc waiting for TOLOCAL asconf ACK*/
283#define SN_WAIT_TOGLOBAL       0x20   /**< assoc waiting for TOLOCAL asconf ACK*/
284#define SN_NULL_TBL            0x00   /**< assoc in No table */
285#define SN_MAX_GLOBAL_ADDRESSES 100   /**< absolute maximum global address count*/
286
287#define SN_ADD_OK                 0   /**< Association added to the table */
288#define SN_ADD_CLASH              1   /**< Clash when trying to add the assoc. info to the table */
289
290#define SN_TABLE_HASH(vtag, port, size) (((u_int) vtag + (u_int) port) % (u_int) size) /**< Calculate the hash table lookup position */
291/** @}
292 * @defgroup Timer Timer Queue Macros and Functions
293 *
294 * Timer macros set minimum/maximum timeout values and calculate timer expiry
295 * times for the provided libalias instance @{
296 */
297#define SN_MIN_TIMER 1
298#define SN_MAX_TIMER 600
299#define SN_TIMER_QUEUE_SIZE SN_MAX_TIMER+2
300
301#define SN_I_T(la) (la->timeStamp + sysctl_init_timer)       /**< INIT State expiration time in seconds */
302#define SN_U_T(la) (la->timeStamp + sysctl_up_timer)         /**< UP State expiration time in seconds */
303#define SN_C_T(la) (la->timeStamp + sysctl_shutdown_timer)   /**< CL State expiration time in seconds */
304#define SN_X_T(la) (la->timeStamp + sysctl_holddown_timer)   /**< Wait after a shutdown complete in seconds */
305/** @}
306 * @defgroup sysctl SysCtl Variable and callback function declarations
307 *
308 * Sysctl variables to modify NAT functionality in real-time along with associated functions
309 * to manage modifications to the sysctl variables @{
310 */
311
312/* Callbacks */
313int sysctl_chg_loglevel(SYSCTL_HANDLER_ARGS);
314int sysctl_chg_timer(SYSCTL_HANDLER_ARGS);
315int sysctl_chg_hashtable_size(SYSCTL_HANDLER_ARGS);
316int sysctl_chg_error_on_ootb(SYSCTL_HANDLER_ARGS);
317int sysctl_chg_accept_global_ootb_addip(SYSCTL_HANDLER_ARGS);
318int sysctl_chg_initialising_chunk_proc_limit(SYSCTL_HANDLER_ARGS);
319int sysctl_chg_chunk_proc_limit(SYSCTL_HANDLER_ARGS);
320int sysctl_chg_param_proc_limit(SYSCTL_HANDLER_ARGS);
321int sysctl_chg_track_global_addresses(SYSCTL_HANDLER_ARGS);
322
323/* Sysctl variables */
324/** @brief net.inet.ip.alias.sctp.log_level */
325static u_int sysctl_log_level = 0; /**< Stores the current level of logging */
326/** @brief net.inet.ip.alias.sctp.init_timer */
327static u_int sysctl_init_timer = 15; /**< Seconds to hold an association in the table waiting for an INIT-ACK or AddIP-ACK */
328/** @brief net.inet.ip.alias.sctp.up_timer */
329static u_int sysctl_up_timer = 300; /**< Seconds to hold an association in the table while no packets are transmitted */
330/** @brief net.inet.ip.alias.sctp.shutdown_timer */
331static u_int sysctl_shutdown_timer = 15; /**< Seconds to hold an association in the table waiting for a SHUTDOWN-COMPLETE */
332/** @brief net.inet.ip.alias.sctp.holddown_timer */
333static 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) */
334/** @brief net.inet.ip.alias.sctp.hashtable_size */
335static 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 */
336/** @brief net.inet.ip.alias.sctp.error_on_ootb */
337static u_int sysctl_error_on_ootb = 1; /**< NAT response  to receipt of OOTB packet
338					  (0 - No response, 1 - NAT will send ErrorM only to local side,
339					  2 -  NAT will send local ErrorM and global ErrorM if there was a partial association match
340					  3 - NAT will send ErrorM to both local and global) */
341/** @brief net.inet.ip.alias.sctp.accept_global_ootb_addip */
342static 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)) */
343/** @brief net.inet.ip.alias.sctp.initialising_chunk_proc_limit */
344static 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) */
345/** @brief net.inet.ip.alias.sctp.param_proc_limit */
346static u_int sysctl_chunk_proc_limit = 5; /**< A limit on the number of chunks that should be searched (DoS prevention) */
347/** @brief net.inet.ip.alias.sctp.param_proc_limit */
348static u_int sysctl_param_proc_limit = 25; /**< A limit on the number of parameters (in chunks) that should be searched (DoS prevention) */
349/** @brief net.inet.ip.alias.sctp.track_global_addresses */
350static 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)
351						   If set to >=1 the NAT will track that many global IP addresses. This may reduce look up table conflicts, but increases processing */
352
353#define SN_NO_ERROR_ON_OOTB              0 /**< Send no errorM on out of the blue packets */
354#define SN_LOCAL_ERROR_ON_OOTB           1 /**< Send only local errorM on out of the blue packets */
355#define SN_LOCALandPARTIAL_ERROR_ON_OOTB 2 /**< Send local errorM and global errorM for out of the blue packets only if partial match found */
356#define SN_ERROR_ON_OOTB                 3 /**< Send errorM on out of the blue packets */
357
358#ifdef SYSCTL_NODE
359
360SYSCTL_DECL(_net_inet);
361SYSCTL_DECL(_net_inet_ip);
362SYSCTL_DECL(_net_inet_ip_alias);
363
364static SYSCTL_NODE(_net_inet_ip_alias, OID_AUTO, sctp,
365    CTLFLAG_RW | CTLFLAG_MPSAFE, NULL,
366    "SCTP NAT");
367SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, log_level,
368    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
369    &sysctl_log_level, 0, sysctl_chg_loglevel, "IU",
370    "Level of detail (0 - default, 1 - event, 2 - info, 3 - detail, 4 - debug, 5 - max debug)");
371SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, init_timer,
372    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
373    &sysctl_init_timer, 0, sysctl_chg_timer, "IU",
374    "Timeout value (s) while waiting for (INIT-ACK|AddIP-ACK)");
375SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, up_timer,
376    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
377    &sysctl_up_timer, 0, sysctl_chg_timer, "IU",
378    "Timeout value (s) to keep an association up with no traffic");
379SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, shutdown_timer,
380    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
381    &sysctl_shutdown_timer, 0, sysctl_chg_timer, "IU",
382    "Timeout value (s) while waiting for SHUTDOWN-COMPLETE");
383SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, holddown_timer,
384    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
385    &sysctl_holddown_timer, 0, sysctl_chg_timer, "IU",
386    "Hold association in table for this many seconds after receiving a SHUTDOWN-COMPLETE");
387SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, hashtable_size,
388    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
389    &sysctl_hashtable_size, 0, sysctl_chg_hashtable_size, "IU",
390    "Size of hash tables used for NAT lookups (100 < prime_number > 1000001)");
391SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, error_on_ootb,
392    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
393    &sysctl_error_on_ootb, 0, sysctl_chg_error_on_ootb, "IU",
394    "ErrorM sent on receipt of ootb packet:\n\t0 - none,\n"
395    "\t1 - to local only,\n"
396    "\t2 - to local and global if a partial association match,\n"
397    "\t3 - to local and global (DoS risk)");
398SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, accept_global_ootb_addip,
399    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
400    &sysctl_accept_global_ootb_addip, 0, sysctl_chg_accept_global_ootb_addip, "IU",
401    "NAT response to receipt of global OOTB AddIP:\n"
402    "\t0 - No response,\n"
403    "\t1 - NAT will accept OOTB global AddIP messages for processing (Security risk)");
404SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, initialising_chunk_proc_limit,
405    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
406    &sysctl_initialising_chunk_proc_limit, 0,
407    sysctl_chg_initialising_chunk_proc_limit, "IU",
408    "Number of chunks that should be processed if there is no current "
409    "association found:\n\t > 0 (A high value is a DoS risk)");
410SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, chunk_proc_limit,
411    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
412    &sysctl_chunk_proc_limit, 0, sysctl_chg_chunk_proc_limit, "IU",
413    "Number of chunks that should be processed to find key chunk:\n"
414    "\t>= initialising_chunk_proc_limit (A high value is a DoS risk)");
415SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, param_proc_limit,
416    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
417    &sysctl_param_proc_limit, 0, sysctl_chg_param_proc_limit, "IU",
418    "Number of parameters (in a chunk) that should be processed to find key "
419    "parameters:\n\t> 1 (A high value is a DoS risk)");
420SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, track_global_addresses,
421    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
422    &sysctl_track_global_addresses, 0, sysctl_chg_track_global_addresses, "IU",
423    "Configures the global address tracking option within the NAT:\n"
424    "\t0 - Global tracking is disabled,\n"
425    "\t> 0 - enables tracking but limits the number of global IP addresses to this value");
426
427#endif /* SYSCTL_NODE */
428/** @}
429 * @ingroup sysctl
430 * @brief sysctl callback for changing net.inet.ip.fw.sctp.log_level
431 *
432 * Updates the variable sysctl_log_level to the provided value and ensures
433 * it is in the valid range (SN_LOG_LOW -> SN_LOG_DEBUG)
434 */
435int sysctl_chg_loglevel(SYSCTL_HANDLER_ARGS)
436{
437	u_int level = *(u_int *)arg1;
438	int error;
439
440	error = sysctl_handle_int(oidp, &level, 0, req);
441	if (error)
442		return (error);
443
444	level = (level > SN_LOG_DEBUG_MAX) ? (SN_LOG_DEBUG_MAX) : (level);
445	level = (level < SN_LOG_LOW) ? (SN_LOG_LOW) : (level);
446	sysctl_log_level = level;
447	return (0);
448}
449
450/** @ingroup sysctl
451 * @brief sysctl callback for changing net.inet.ip.fw.sctp.(init_timer|up_timer|shutdown_timer)
452 *
453 * Updates the timer-based sysctl variables. The new values are sanity-checked
454 * to make sure that they are within the range SN_MIN_TIMER-SN_MAX_TIMER. The
455 * holddown timer is allowed to be 0
456 */
457int sysctl_chg_timer(SYSCTL_HANDLER_ARGS)
458{
459	u_int timer = *(u_int *)arg1;
460	int error;
461
462	error = sysctl_handle_int(oidp, &timer, 0, req);
463	if (error)
464		return (error);
465
466	timer = (timer > SN_MAX_TIMER) ? (SN_MAX_TIMER) : (timer);
467
468	if (((u_int *)arg1) != &sysctl_holddown_timer) {
469		timer = (timer < SN_MIN_TIMER) ? (SN_MIN_TIMER) : (timer);
470	}
471
472	*(u_int *)arg1 = timer;
473
474	return (0);
475}
476
477/** @ingroup sysctl
478 * @brief sysctl callback for changing net.inet.ip.alias.sctp.hashtable_size
479 *
480 * Updates the hashtable_size sysctl variable. The new value should be a prime
481 * number.  We sanity check to ensure that the size is within the range
482 * SN_MIN_HASH_SIZE-SN_MAX_HASH_SIZE. We then check the provided number to see
483 * if it is prime. We approximate by checking that (2,3,5,7,11) are not factors,
484 * incrementing the user provided value until we find a suitable number.
485 */
486int sysctl_chg_hashtable_size(SYSCTL_HANDLER_ARGS)
487{
488	u_int size = *(u_int *)arg1;
489	int error;
490
491	error = sysctl_handle_int(oidp, &size, 0, req);
492	if (error)
493		return (error);
494
495	size = (size < SN_MIN_HASH_SIZE) ? (SN_MIN_HASH_SIZE) : ((size > SN_MAX_HASH_SIZE) ? (SN_MAX_HASH_SIZE) : (size));
496
497	size |= 0x00000001; /* make odd */
498
499	for (;(((size % 3) == 0) || ((size % 5) == 0) || ((size % 7) == 0) || ((size % 11) == 0)); size+=2);
500	sysctl_hashtable_size = size;
501
502	return (0);
503}
504
505/** @ingroup sysctl
506 * @brief sysctl callback for changing net.inet.ip.alias.sctp.error_on_ootb
507 *
508 * Updates the error_on_clash sysctl variable.
509 * If set to 0, no ErrorM will be sent if there is a look up table clash
510 * If set to 1, an ErrorM is sent only to the local side
511 * If set to 2, an ErrorM is sent to the local side and global side if there is
512 *                                                  a partial association match
513 * If set to 3, an ErrorM is sent to both local and global sides (DoS) risk.
514 */
515int sysctl_chg_error_on_ootb(SYSCTL_HANDLER_ARGS)
516{
517	u_int flag = *(u_int *)arg1;
518	int error;
519
520	error = sysctl_handle_int(oidp, &flag, 0, req);
521	if (error)
522		return (error);
523
524	sysctl_error_on_ootb = (flag > SN_ERROR_ON_OOTB) ? SN_ERROR_ON_OOTB: flag;
525
526	return (0);
527}
528
529/** @ingroup sysctl
530 * @brief sysctl callback for changing net.inet.ip.alias.sctp.accept_global_ootb_addip
531 *
532 * If set to 1 the NAT will accept ootb global addip messages for processing (Security risk)
533 * Default is 0, only responding to local ootb AddIP messages
534 */
535int sysctl_chg_accept_global_ootb_addip(SYSCTL_HANDLER_ARGS)
536{
537	u_int flag = *(u_int *)arg1;
538	int error;
539
540	error = sysctl_handle_int(oidp, &flag, 0, req);
541	if (error)
542		return (error);
543
544	sysctl_accept_global_ootb_addip = (flag == 1) ? 1: 0;
545
546	return (0);
547}
548
549/** @ingroup sysctl
550 * @brief sysctl callback for changing net.inet.ip.alias.sctp.initialising_chunk_proc_limit
551 *
552 * Updates the initialising_chunk_proc_limit sysctl variable.  Number of chunks
553 * that should be processed if there is no current association found: > 0 (A
554 * high value is a DoS risk)
555 */
556int sysctl_chg_initialising_chunk_proc_limit(SYSCTL_HANDLER_ARGS)
557{
558	u_int proclimit = *(u_int *)arg1;
559	int error;
560
561	error = sysctl_handle_int(oidp, &proclimit, 0, req);
562	if (error)
563		return (error);
564
565	sysctl_initialising_chunk_proc_limit = (proclimit < 1) ? 1: proclimit;
566	sysctl_chunk_proc_limit =
567		(sysctl_chunk_proc_limit < sysctl_initialising_chunk_proc_limit) ? sysctl_initialising_chunk_proc_limit : sysctl_chunk_proc_limit;
568
569	return (0);
570}
571
572/** @ingroup sysctl
573 * @brief sysctl callback for changing net.inet.ip.alias.sctp.chunk_proc_limit
574 *
575 * Updates the chunk_proc_limit sysctl variable.
576 * Number of chunks that should be processed to find key chunk:
577 *  >= initialising_chunk_proc_limit (A high value is a DoS risk)
578 */
579int sysctl_chg_chunk_proc_limit(SYSCTL_HANDLER_ARGS)
580{
581	u_int proclimit = *(u_int *)arg1;
582	int error;
583
584	error = sysctl_handle_int(oidp, &proclimit, 0, req);
585	if (error)
586		return (error);
587
588	sysctl_chunk_proc_limit =
589		(proclimit < sysctl_initialising_chunk_proc_limit) ? sysctl_initialising_chunk_proc_limit : proclimit;
590
591	return (0);
592}
593
594/** @ingroup sysctl
595 * @brief sysctl callback for changing net.inet.ip.alias.sctp.param_proc_limit
596 *
597 * Updates the param_proc_limit sysctl variable.
598 * Number of parameters that should be processed to find key parameters:
599 *  > 1 (A high value is a DoS risk)
600 */
601int sysctl_chg_param_proc_limit(SYSCTL_HANDLER_ARGS)
602{
603	u_int proclimit = *(u_int *)arg1;
604	int error;
605
606	error = sysctl_handle_int(oidp, &proclimit, 0, req);
607	if (error)
608		return (error);
609
610	sysctl_param_proc_limit =
611		(proclimit < 2) ? 2 : proclimit;
612
613	return (0);
614}
615
616/** @ingroup sysctl
617 * @brief sysctl callback for changing net.inet.ip.alias.sctp.track_global_addresses
618 *
619 *Configures the global address tracking option within the NAT (0 - Global
620 *tracking is disabled, > 0 - enables tracking but limits the number of global
621 *IP addresses to this value)
622 */
623int sysctl_chg_track_global_addresses(SYSCTL_HANDLER_ARGS)
624{
625	u_int num_to_track = *(u_int *)arg1;
626	int error;
627
628	error = sysctl_handle_int(oidp, &num_to_track, 0, req);
629	if (error)
630		return (error);
631
632	sysctl_track_global_addresses = (num_to_track > SN_MAX_GLOBAL_ADDRESSES) ? SN_MAX_GLOBAL_ADDRESSES : num_to_track;
633
634	return (0);
635}
636
637/* ----------------------------------------------------------------------
638 *                            CODE BEGINS HERE
639 * ----------------------------------------------------------------------
640 */
641/**
642 * @brief Initialises the SCTP NAT Implementation
643 *
644 * Creates the look-up tables and the timer queue and initialises all state
645 * variables
646 *
647 * @param la Pointer to the relevant libalias instance
648 */
649void
650AliasSctpInit(struct libalias *la)
651{
652	/* Initialise association tables*/
653	int i;
654	la->sctpNatTableSize = sysctl_hashtable_size;
655	SN_LOG(SN_LOG_EVENT,
656	    SctpAliasLog("Initialising SCTP NAT Instance (hash_table_size:%d)\n", la->sctpNatTableSize));
657	la->sctpTableLocal = sn_calloc(la->sctpNatTableSize, sizeof(struct sctpNatTableL));
658	la->sctpTableGlobal = sn_calloc(la->sctpNatTableSize, sizeof(struct sctpNatTableG));
659	la->sctpNatTimer.TimerQ = sn_calloc(SN_TIMER_QUEUE_SIZE, sizeof(struct sctpTimerQ));
660	/* Initialise hash table */
661	for (i = 0; i < la->sctpNatTableSize; i++) {
662		LIST_INIT(&la->sctpTableLocal[i]);
663		LIST_INIT(&la->sctpTableGlobal[i]);
664	}
665
666	/* Initialise circular timer Q*/
667	for (i = 0; i < SN_TIMER_QUEUE_SIZE; i++)
668		LIST_INIT(&la->sctpNatTimer.TimerQ[i]);
669#ifdef _KERNEL
670	la->sctpNatTimer.loc_time=time_uptime; /* la->timeStamp is not set yet */
671#else
672	la->sctpNatTimer.loc_time=la->timeStamp;
673#endif
674	la->sctpNatTimer.cur_loc = 0;
675	la->sctpLinkCount = 0;
676}
677
678/**
679 * @brief Cleans-up the SCTP NAT Implementation prior to unloading
680 *
681 * Removes all entries from the timer queue, freeing associations as it goes.
682 * We then free memory allocated to the look-up tables and the time queue
683 *
684 * NOTE: We do not need to traverse the look-up tables as each association
685 *       will always have an entry in the timer queue, freeing this memory
686 *       once will free all memory allocated to entries in the look-up tables
687 *
688 * @param la Pointer to the relevant libalias instance
689 */
690void
691AliasSctpTerm(struct libalias *la)
692{
693	struct sctp_nat_assoc *assoc1, *assoc2;
694	int                   i;
695
696	LIBALIAS_LOCK_ASSERT(la);
697	SN_LOG(SN_LOG_EVENT,
698	    SctpAliasLog("Removing SCTP NAT Instance\n"));
699	for (i = 0; i < SN_TIMER_QUEUE_SIZE; i++) {
700		assoc1 = LIST_FIRST(&la->sctpNatTimer.TimerQ[i]);
701		while (assoc1 != NULL) {
702			freeGlobalAddressList(assoc1);
703			assoc2 = LIST_NEXT(assoc1, timer_Q);
704			sn_free(assoc1);
705			assoc1 = assoc2;
706		}
707	}
708
709	sn_free(la->sctpTableLocal);
710	sn_free(la->sctpTableGlobal);
711	sn_free(la->sctpNatTimer.TimerQ);
712}
713
714/**
715 * @brief Handles SCTP packets passed from libalias
716 *
717 * This function needs to actually NAT/drop packets and possibly create and
718 * send AbortM or ErrorM packets in response. The process involves:
719 * - Validating the direction parameter passed by the caller
720 * - Checking and handling any expired timers for the NAT
721 * - Calling sctp_PktParser() to parse the packet
722 * - Call ProcessSctpMsg() to decide the appropriate outcome and to update
723 *   the NAT tables
724 * - Based on the return code either:
725 *   - NAT the packet
726 *   - Construct and send an ErrorM|AbortM packet
727 *   - Mark the association for removal from the tables
728 * - Potentially remove the association from all lookup tables
729 * - Return the appropriate result to libalias
730 *
731 * @param la Pointer to the relevant libalias instance
732 * @param pip Pointer to IP packet to process
733 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
734 *
735 * @return  PKT_ALIAS_OK | PKT_ALIAS_IGNORE | PKT_ALIAS_ERROR
736 */
737int
738SctpAlias(struct libalias *la, struct ip *pip, int direction)
739{
740	int rtnval;
741	struct sctp_nat_msg msg;
742	struct sctp_nat_assoc *assoc = NULL;
743
744	if ((direction != SN_TO_LOCAL) && (direction != SN_TO_GLOBAL)) {
745		SctpAliasLog("ERROR: Invalid direction\n");
746		return (PKT_ALIAS_ERROR);
747	}
748
749	sctp_CheckTimers(la); /* Check timers */
750
751	/* Parse the packet */
752	rtnval = sctp_PktParser(la, direction, pip, &msg, &assoc); //using *char (change to mbuf when get code from paolo)
753	switch (rtnval) {
754	case SN_PARSE_OK:
755		break;
756	case SN_PARSE_ERROR_CHHL:
757		/* Not an error if there is a chunk length parsing error and this is a fragmented packet */
758		if (ntohs(pip->ip_off) & IP_MF) {
759			rtnval = SN_PARSE_OK;
760			break;
761		}
762		SN_LOG(SN_LOG_EVENT,
763		    logsctperror("SN_PARSE_ERROR", msg.sctp_hdr->v_tag, rtnval, direction));
764		return (PKT_ALIAS_ERROR);
765	case SN_PARSE_ERROR_PARTIALLOOKUP:
766		if (sysctl_error_on_ootb > SN_LOCALandPARTIAL_ERROR_ON_OOTB) {
767			SN_LOG(SN_LOG_EVENT,
768			    logsctperror("SN_PARSE_ERROR", msg.sctp_hdr->v_tag, rtnval, direction));
769			return (PKT_ALIAS_ERROR);
770		}
771	case SN_PARSE_ERROR_LOOKUP:
772		if (sysctl_error_on_ootb == SN_ERROR_ON_OOTB ||
773		    (sysctl_error_on_ootb == SN_LOCALandPARTIAL_ERROR_ON_OOTB && direction == SN_TO_LOCAL) ||
774		    (sysctl_error_on_ootb == SN_LOCAL_ERROR_ON_OOTB && direction == SN_TO_GLOBAL)) {
775			TxAbortErrorM(la, &msg, assoc, SN_REFLECT_ERROR, direction); /*NB assoc=NULL */
776			return (PKT_ALIAS_RESPOND);
777		}
778	default:
779		SN_LOG(SN_LOG_EVENT,
780		    logsctperror("SN_PARSE_ERROR", msg.sctp_hdr->v_tag, rtnval, direction));
781		return (PKT_ALIAS_ERROR);
782	}
783
784	SN_LOG(SN_LOG_DETAIL,
785	    logsctpassoc(assoc, "*");
786	    logsctpparse(direction, &msg);
787	);
788
789	/* Process the SCTP message */
790	rtnval = ProcessSctpMsg(la, direction, &msg, assoc);
791
792	SN_LOG(SN_LOG_DEBUG_MAX,
793	    logsctpassoc(assoc, "-");
794	    logSctpLocal(la);
795	    logSctpGlobal(la);
796	);
797	SN_LOG(SN_LOG_DEBUG, logTimerQ(la));
798
799	switch (rtnval) {
800	case SN_NAT_PKT:
801		switch (direction) {
802		case SN_TO_LOCAL:
803			DifferentialChecksum(&(msg.ip_hdr->ip_sum),
804			    &(assoc->l_addr), &(msg.ip_hdr->ip_dst), 2);
805			msg.ip_hdr->ip_dst = assoc->l_addr; /* change dst address to local address*/
806			break;
807		case SN_TO_GLOBAL:
808			DifferentialChecksum(&(msg.ip_hdr->ip_sum),
809			    &(assoc->a_addr),  &(msg.ip_hdr->ip_src), 2);
810			msg.ip_hdr->ip_src = assoc->a_addr; /* change src to alias addr*/
811			break;
812		default:
813			rtnval = SN_DROP_PKT; /* shouldn't get here, but if it does drop packet */
814			SN_LOG(SN_LOG_LOW, logsctperror("ERROR: Invalid direction", msg.sctp_hdr->v_tag, rtnval, direction));
815			break;
816		}
817		break;
818	case SN_DROP_PKT:
819		SN_LOG(SN_LOG_DETAIL, logsctperror("SN_DROP_PKT", msg.sctp_hdr->v_tag, rtnval, direction));
820		break;
821	case SN_REPLY_ABORT:
822	case SN_REPLY_ERROR:
823	case SN_SEND_ABORT:
824		TxAbortErrorM(la, &msg, assoc, rtnval, direction);
825		break;
826	default:
827		// big error, remove association and go to idle and write log messages
828		SN_LOG(SN_LOG_LOW, logsctperror("SN_PROCESSING_ERROR", msg.sctp_hdr->v_tag, rtnval, direction));
829		assoc->state = SN_RM;/* Mark for removal*/
830		break;
831	}
832
833	/* Remove association if tagged for removal */
834	if (assoc->state == SN_RM) {
835		if (assoc->TableRegister) {
836			sctp_RmTimeOut(la, assoc);
837			RmSctpAssoc(la, assoc);
838		}
839		LIBALIAS_LOCK_ASSERT(la);
840		freeGlobalAddressList(assoc);
841		sn_free(assoc);
842	}
843	switch (rtnval) {
844	case SN_NAT_PKT:
845		return (PKT_ALIAS_OK);
846	case SN_SEND_ABORT:
847		return (PKT_ALIAS_OK);
848	case SN_REPLY_ABORT:
849	case SN_REPLY_ERROR:
850	case SN_REFLECT_ERROR:
851		return (PKT_ALIAS_RESPOND);
852	case SN_DROP_PKT:
853	default:
854		return (PKT_ALIAS_ERROR);
855	}
856}
857
858/**
859 * @brief Send an AbortM or ErrorM
860 *
861 * We construct the new SCTP packet to send in place of the existing packet we
862 * have been asked to NAT. This function can only be called if the original
863 * packet was successfully parsed as a valid SCTP packet.
864 *
865 * An AbortM (without cause) packet is the smallest SCTP packet available and as
866 * such there is always space in the existing packet buffer to fit the AbortM
867 * packet. An ErrorM packet is 4 bytes longer than the (the error cause is not
868 * optional). An ErrorM is sent in response to an AddIP when the Vtag/address
869 * combination, if added, will produce a conflict in the association look up
870 * tables. It may also be used for an unexpected packet - a packet with no
871 * matching association in the NAT table and we are requesting an AddIP so we
872 * can add it.  The smallest valid SCTP packet while the association is in an
873 * up-state is a Heartbeat packet, which is big enough to be transformed to an
874 * ErrorM.
875 *
876 * We create a temporary character array to store the packet as we are constructing
877 * it. We then populate the array with appropriate values based on:
878 * - Packet type (AbortM | ErrorM)
879 * - Initial packet direction (SN_TO_LOCAL | SN_TO_GLOBAL)
880 * - NAT response (Send packet | Reply packet)
881 *
882 * Once complete, we copy the contents of the temporary packet over the original
883 * SCTP packet we were asked to NAT
884 *
885 * @param la Pointer to the relevant libalias instance
886 * @param sm Pointer to sctp message information
887 * @param assoc Pointer to current association details
888 * @param sndrply SN_SEND_ABORT | SN_REPLY_ABORT | SN_REPLY_ERROR
889 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
890 */
891static uint32_t
892local_sctp_finalize_crc32(uint32_t crc32c)
893{
894	/* This routine is duplicated from SCTP
895	 * we need to do that since it MAY be that SCTP
896	 * is NOT compiled into the kernel. The CRC32C routines
897	 * however are always available in libkern.
898	 */
899	uint32_t result;
900#if BYTE_ORDER == BIG_ENDIAN
901	uint8_t byte0, byte1, byte2, byte3;
902
903#endif
904	/* Complement the result */
905	result = ~crc32c;
906#if BYTE_ORDER == BIG_ENDIAN
907	/*
908	 * For BIG-ENDIAN.. aka Motorola byte order the result is in
909	 * little-endian form. So we must manually swap the bytes. Then we
910	 * can call htonl() which does nothing...
911	 */
912	byte0 = result & 0x000000ff;
913	byte1 = (result >> 8) & 0x000000ff;
914	byte2 = (result >> 16) & 0x000000ff;
915	byte3 = (result >> 24) & 0x000000ff;
916	crc32c = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3);
917#else
918	/*
919	 * For INTEL platforms the result comes out in network order. No
920	 * htonl is required or the swap above. So we optimize out both the
921	 * htonl and the manual swap above.
922	 */
923	crc32c = result;
924#endif
925	return (crc32c);
926}
927
928static void
929TxAbortErrorM(struct libalias *la, struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int sndrply, int direction)
930{
931	int sctp_size = sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_error_cause);
932	int ip_size = sizeof(struct ip) + sctp_size;
933	int include_error_cause = 1;
934	char tmp_ip[ip_size];
935	char addrbuf[INET_ADDRSTRLEN];
936
937	if (ntohs(sm->ip_hdr->ip_len) < ip_size) { /* short packet, cannot send error cause */
938		include_error_cause = 0;
939		ip_size = ip_size -  sizeof(struct sctp_error_cause);
940		sctp_size = sctp_size -  sizeof(struct sctp_error_cause);
941	}
942	/* Assign header pointers packet */
943	struct ip* ip = (struct ip *) tmp_ip;
944	struct sctphdr* sctp_hdr = (struct sctphdr *) ((char *) ip + sizeof(*ip));
945	struct sctp_chunkhdr* chunk_hdr = (struct sctp_chunkhdr *) ((char *) sctp_hdr + sizeof(*sctp_hdr));
946	struct sctp_error_cause* error_cause = (struct sctp_error_cause *) ((char *) chunk_hdr + sizeof(*chunk_hdr));
947
948	/* construct ip header */
949	ip->ip_v = sm->ip_hdr->ip_v;
950	ip->ip_hl = 5; /* 5*32 bit words */
951	ip->ip_tos = 0;
952	ip->ip_len = htons(ip_size);
953	ip->ip_id = sm->ip_hdr->ip_id;
954	ip->ip_off = 0;
955	ip->ip_ttl = 255;
956	ip->ip_p = IPPROTO_SCTP;
957	/*
958	  The definitions below should be removed when they make it into the SCTP stack
959	*/
960#define SCTP_MIDDLEBOX_FLAG 0x02
961#define SCTP_NAT_TABLE_COLLISION 0x00b0
962#define SCTP_MISSING_NAT 0x00b1
963	chunk_hdr->chunk_type = (sndrply & SN_TX_ABORT) ? SCTP_ABORT_ASSOCIATION : SCTP_OPERATION_ERROR;
964	chunk_hdr->chunk_flags = SCTP_MIDDLEBOX_FLAG;
965	if (include_error_cause) {
966		error_cause->code = htons((sndrply & SN_REFLECT_ERROR) ? SCTP_MISSING_NAT : SCTP_NAT_TABLE_COLLISION);
967		error_cause->length = htons(sizeof(struct sctp_error_cause));
968		chunk_hdr->chunk_length = htons(sizeof(*chunk_hdr) + sizeof(struct sctp_error_cause));
969	} else {
970		chunk_hdr->chunk_length = htons(sizeof(*chunk_hdr));
971	}
972
973	/* set specific values */
974	switch (sndrply) {
975	case SN_REFLECT_ERROR:
976		chunk_hdr->chunk_flags |= SCTP_HAD_NO_TCB; /* set Tbit */
977		sctp_hdr->v_tag = sm->sctp_hdr->v_tag;
978		break;
979	case SN_REPLY_ERROR:
980		sctp_hdr->v_tag = (direction == SN_TO_LOCAL) ? assoc->g_vtag : assoc->l_vtag ;
981		break;
982	case SN_SEND_ABORT:
983		sctp_hdr->v_tag = sm->sctp_hdr->v_tag;
984		break;
985	case SN_REPLY_ABORT:
986		sctp_hdr->v_tag = sm->sctpchnk.Init->initiate_tag;
987		break;
988	}
989
990	/* Set send/reply values */
991	if (sndrply == SN_SEND_ABORT) { /*pass through NAT */
992		ip->ip_src = (direction == SN_TO_LOCAL) ? sm->ip_hdr->ip_src : assoc->a_addr;
993		ip->ip_dst = (direction == SN_TO_LOCAL) ? assoc->l_addr : sm->ip_hdr->ip_dst;
994		sctp_hdr->src_port = sm->sctp_hdr->src_port;
995		sctp_hdr->dest_port = sm->sctp_hdr->dest_port;
996	} else { /* reply and reflect */
997		ip->ip_src = sm->ip_hdr->ip_dst;
998		ip->ip_dst = sm->ip_hdr->ip_src;
999		sctp_hdr->src_port = sm->sctp_hdr->dest_port;
1000		sctp_hdr->dest_port = sm->sctp_hdr->src_port;
1001	}
1002
1003	/* Calculate IP header checksum */
1004	ip->ip_sum = in_cksum_hdr(ip);
1005
1006	/* calculate SCTP header CRC32 */
1007	sctp_hdr->checksum = 0;
1008	sctp_hdr->checksum = local_sctp_finalize_crc32(calculate_crc32c(0xffffffff, (unsigned char *) sctp_hdr, sctp_size));
1009
1010	memcpy(sm->ip_hdr, ip, ip_size);
1011
1012	SN_LOG(SN_LOG_EVENT,SctpAliasLog("%s %s 0x%x (->%s:%u vtag=0x%x crc=0x%x)\n",
1013	    ((sndrply == SN_SEND_ABORT) ? "Sending" : "Replying"),
1014	    ((sndrply & SN_TX_ERROR) ? "ErrorM" : "AbortM"),
1015	    (include_error_cause ? ntohs(error_cause->code) : 0),
1016	    inet_ntoa_r(ip->ip_dst, INET_NTOA_BUF(addrbuf)),
1017	    ntohs(sctp_hdr->dest_port),
1018	    ntohl(sctp_hdr->v_tag), ntohl(sctp_hdr->checksum)));
1019}
1020
1021/* ----------------------------------------------------------------------
1022 *                           PACKET PARSER CODE
1023 * ----------------------------------------------------------------------
1024 */
1025/** @addtogroup packet_parser
1026 *
1027 * These functions parse the SCTP packet and fill a sctp_nat_msg structure
1028 * with the parsed contents.
1029 */
1030/** @ingroup packet_parser
1031 * @brief Parses SCTP packets for the key SCTP chunk that will be processed
1032 *
1033 * This module parses SCTP packets for the key SCTP chunk that will be processed
1034 * The module completes the sctp_nat_msg structure and either retrieves the
1035 * relevant (existing) stored association from the Hash Tables or creates a new
1036 * association entity with state SN_ID
1037 *
1038 * @param la Pointer to the relevant libalias instance
1039 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1040 * @param pip
1041 * @param sm Pointer to sctp message information
1042 * @param passoc Pointer to the association this SCTP Message belongs to
1043 *
1044 * @return SN_PARSE_OK | SN_PARSE_ERROR_*
1045 */
1046static int
1047sctp_PktParser(struct libalias *la, int direction, struct ip *pip,
1048    struct sctp_nat_msg *sm, struct sctp_nat_assoc **passoc)
1049//sctp_PktParser(int direction, struct mbuf *ipak, int ip_hdr_len,struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc)
1050{
1051	struct sctphdr *sctp_hdr;
1052	struct sctp_chunkhdr *chunk_hdr;
1053	struct sctp_paramhdr *param_hdr;
1054	struct in_addr ipv4addr;
1055	int bytes_left; /* bytes left in ip packet */
1056	int chunk_length;
1057	int chunk_count;
1058	int partial_match = 0;
1059	//  mbuf *mp;
1060	//  int mlen;
1061
1062	//  mlen = SCTP_HEADER_LEN(i_pak);
1063	//  mp = SCTP_HEADER_TO_CHAIN(i_pak); /* does nothing in bsd since header and chain not separate */
1064
1065	/*
1066	 * Note, that if the VTag is zero, it must be an INIT
1067	 * Also, I am only interested in the content of INIT and ADDIP chunks
1068	 */
1069
1070	// no mbuf stuff from Paolo yet so ...
1071	sm->ip_hdr = pip;
1072	/* remove ip header length from the bytes_left */
1073	bytes_left = ntohs(pip->ip_len) - (pip->ip_hl << 2);
1074
1075	/* Check SCTP header length and move to first chunk */
1076	if (bytes_left < sizeof(struct sctphdr)) {
1077		sm->sctp_hdr = NULL;
1078		return (SN_PARSE_ERROR_IPSHL); /* packet not long enough*/
1079	}
1080
1081	sm->sctp_hdr = sctp_hdr = (struct sctphdr *) ip_next(pip);
1082	bytes_left -= sizeof(struct sctphdr);
1083
1084	/* Check for valid ports (zero valued ports would find partially initialised associations */
1085	if (sctp_hdr->src_port == 0 || sctp_hdr->dest_port == 0)
1086		return (SN_PARSE_ERROR_PORT);
1087
1088	/* Check length of first chunk */
1089	if (bytes_left < SN_MIN_CHUNK_SIZE) /* malformed chunk - could cause endless loop*/
1090		return (SN_PARSE_ERROR_CHHL); /* packet not long enough for this chunk */
1091
1092	/* First chunk */
1093	chunk_hdr = SN_SCTP_FIRSTCHUNK(sctp_hdr);
1094
1095	chunk_length = SCTP_SIZE32(ntohs(chunk_hdr->chunk_length));
1096	if ((chunk_length < SN_MIN_CHUNK_SIZE) || (chunk_length > bytes_left)) /* malformed chunk - could cause endless loop*/
1097		return (SN_PARSE_ERROR_CHHL);
1098
1099	if ((chunk_hdr->chunk_flags & SCTP_HAD_NO_TCB) &&
1100	    ((chunk_hdr->chunk_type == SCTP_ABORT_ASSOCIATION) ||
1101		(chunk_hdr->chunk_type == SCTP_SHUTDOWN_COMPLETE))) {
1102		/* T-Bit set */
1103		if (direction == SN_TO_LOCAL)
1104			*passoc = FindSctpGlobalT(la,  pip->ip_src, sctp_hdr->v_tag, sctp_hdr->dest_port, sctp_hdr->src_port);
1105		else
1106			*passoc = FindSctpLocalT(la, pip->ip_dst, sctp_hdr->v_tag, sctp_hdr->dest_port, sctp_hdr->src_port);
1107	} else {
1108		/* Proper v_tag settings */
1109		if (direction == SN_TO_LOCAL)
1110			*passoc = FindSctpGlobal(la, pip->ip_src, sctp_hdr->v_tag, sctp_hdr->src_port, sctp_hdr->dest_port, &partial_match);
1111		else
1112			*passoc = FindSctpLocal(la, pip->ip_src,  pip->ip_dst, sctp_hdr->v_tag, sctp_hdr->src_port, sctp_hdr->dest_port);
1113	}
1114
1115	chunk_count = 1;
1116	/* Real packet parsing occurs below */
1117	sm->msg = SN_SCTP_OTHER;/* Initialise to largest value*/
1118	sm->chunk_length = 0; /* only care about length for key chunks */
1119	while (IS_SCTP_CONTROL(chunk_hdr)) {
1120		switch (chunk_hdr->chunk_type) {
1121		case SCTP_INITIATION:
1122			if (chunk_length < sizeof(struct sctp_init_chunk)) /* malformed chunk*/
1123				return (SN_PARSE_ERROR_CHHL);
1124			sm->msg = SN_SCTP_INIT;
1125			sm->sctpchnk.Init = (struct sctp_init *) ((char *) chunk_hdr + sizeof(struct sctp_chunkhdr));
1126			sm->chunk_length = chunk_length;
1127			/* if no existing association, create a new one */
1128			if (*passoc == NULL) {
1129				if (sctp_hdr->v_tag == 0) { //Init requires vtag=0
1130					*passoc = (struct sctp_nat_assoc *) sn_malloc(sizeof(struct sctp_nat_assoc));
1131					if (*passoc == NULL) {/* out of resources */
1132						return (SN_PARSE_ERROR_AS_MALLOC);
1133					}
1134					/* Initialize association - sn_malloc initializes memory to zeros */
1135					(*passoc)->state = SN_ID;
1136					LIST_INIT(&((*passoc)->Gaddr)); /* always initialise to avoid memory problems */
1137					(*passoc)->TableRegister = SN_NULL_TBL;
1138					return (SN_PARSE_OK);
1139				}
1140				return (SN_PARSE_ERROR_VTAG);
1141			}
1142			return (SN_PARSE_ERROR_LOOKUP);
1143		case SCTP_INITIATION_ACK:
1144			if (chunk_length < sizeof(struct sctp_init_ack_chunk)) /* malformed chunk*/
1145				return (SN_PARSE_ERROR_CHHL);
1146			sm->msg = SN_SCTP_INITACK;
1147			sm->sctpchnk.InitAck = (struct sctp_init_ack *) ((char *) chunk_hdr + sizeof(struct sctp_chunkhdr));
1148			sm->chunk_length = chunk_length;
1149			return ((*passoc == NULL) ? (SN_PARSE_ERROR_LOOKUP) : (SN_PARSE_OK));
1150		case SCTP_ABORT_ASSOCIATION: /* access only minimum sized chunk */
1151			sm->msg = SN_SCTP_ABORT;
1152			sm->chunk_length = chunk_length;
1153			return ((*passoc == NULL) ? (SN_PARSE_ERROR_LOOKUP_ABORT) : (SN_PARSE_OK));
1154		case SCTP_SHUTDOWN_ACK:
1155			if (chunk_length < sizeof(struct sctp_shutdown_ack_chunk)) /* malformed chunk*/
1156				return (SN_PARSE_ERROR_CHHL);
1157			if (sm->msg > SN_SCTP_SHUTACK) {
1158				sm->msg = SN_SCTP_SHUTACK;
1159				sm->chunk_length = chunk_length;
1160			}
1161			break;
1162		case SCTP_SHUTDOWN_COMPLETE:  /* minimum sized chunk */
1163			if (sm->msg > SN_SCTP_SHUTCOMP) {
1164				sm->msg = SN_SCTP_SHUTCOMP;
1165				sm->chunk_length = chunk_length;
1166			}
1167			return ((*passoc == NULL) ? (SN_PARSE_ERROR_LOOKUP) : (SN_PARSE_OK));
1168		case SCTP_ASCONF:
1169			if (sm->msg > SN_SCTP_ASCONF) {
1170				if (chunk_length < (sizeof(struct  sctp_asconf_chunk) + sizeof(struct  sctp_ipv4addr_param))) /* malformed chunk*/
1171					return (SN_PARSE_ERROR_CHHL);
1172				//leave parameter searching to later, if required
1173				param_hdr = (struct sctp_paramhdr *) ((char *) chunk_hdr + sizeof(struct sctp_asconf_chunk)); /*compulsory IP parameter*/
1174				if (ntohs(param_hdr->param_type) == SCTP_IPV4_ADDRESS) {
1175					if ((*passoc == NULL) && (direction == SN_TO_LOCAL)) { /* AddIP with no association */
1176						/* try look up with the ASCONF packet's alternative address */
1177						ipv4addr.s_addr = ((struct sctp_ipv4addr_param *) param_hdr)->addr;
1178						*passoc = FindSctpGlobal(la, ipv4addr, sctp_hdr->v_tag, sctp_hdr->src_port, sctp_hdr->dest_port, &partial_match);
1179					}
1180					param_hdr = (struct sctp_paramhdr *)
1181						((char *) param_hdr + sizeof(struct sctp_ipv4addr_param)); /*asconf's compulsory address parameter */
1182					sm->chunk_length = chunk_length - sizeof(struct  sctp_asconf_chunk) - sizeof(struct  sctp_ipv4addr_param); /* rest of chunk */
1183				} else {
1184					if (chunk_length < (sizeof(struct  sctp_asconf_chunk) + sizeof(struct  sctp_ipv6addr_param))) /* malformed chunk*/
1185						return (SN_PARSE_ERROR_CHHL);
1186					param_hdr = (struct sctp_paramhdr *)
1187						((char *) param_hdr + sizeof(struct sctp_ipv6addr_param)); /*asconf's compulsory address parameter */
1188					sm->chunk_length = chunk_length - sizeof(struct  sctp_asconf_chunk) - sizeof(struct  sctp_ipv6addr_param); /* rest of chunk */
1189				}
1190				sm->msg = SN_SCTP_ASCONF;
1191				sm->sctpchnk.Asconf = param_hdr;
1192
1193				if (*passoc == NULL) { /* AddIP with no association */
1194					*passoc = (struct sctp_nat_assoc *) sn_malloc(sizeof(struct sctp_nat_assoc));
1195					if (*passoc == NULL) {/* out of resources */
1196						return (SN_PARSE_ERROR_AS_MALLOC);
1197					}
1198					/* Initialize association  - sn_malloc initializes memory to zeros */
1199					(*passoc)->state = SN_ID;
1200					LIST_INIT(&((*passoc)->Gaddr)); /* always initialise to avoid memory problems */
1201					(*passoc)->TableRegister = SN_NULL_TBL;
1202					return (SN_PARSE_OK);
1203				}
1204			}
1205			break;
1206		case SCTP_ASCONF_ACK:
1207			if (sm->msg > SN_SCTP_ASCONFACK) {
1208				if (chunk_length < sizeof(struct  sctp_asconf_ack_chunk)) /* malformed chunk*/
1209					return (SN_PARSE_ERROR_CHHL);
1210				//leave parameter searching to later, if required
1211				param_hdr = (struct sctp_paramhdr *) ((char *) chunk_hdr
1212				    + sizeof(struct sctp_asconf_ack_chunk));
1213				sm->msg = SN_SCTP_ASCONFACK;
1214				sm->sctpchnk.Asconf = param_hdr;
1215				sm->chunk_length = chunk_length - sizeof(struct sctp_asconf_ack_chunk);
1216			}
1217			break;
1218		default:
1219			break; /* do nothing*/
1220		}
1221
1222		/* if no association is found exit - we need to find an Init or AddIP within sysctl_initialising_chunk_proc_limit */
1223		if ((*passoc == NULL) && (chunk_count >= sysctl_initialising_chunk_proc_limit))
1224			return (SN_PARSE_ERROR_LOOKUP);
1225
1226		/* finished with this chunk, on to the next chunk*/
1227		bytes_left-= chunk_length;
1228
1229		/* Is this the end of the packet ? */
1230		if (bytes_left == 0)
1231			return (*passoc == NULL) ? (SN_PARSE_ERROR_LOOKUP) : (SN_PARSE_OK);
1232
1233		/* Are there enough bytes in packet to at least retrieve length of next chunk ? */
1234		if (bytes_left < SN_MIN_CHUNK_SIZE)
1235			return (SN_PARSE_ERROR_CHHL);
1236
1237		chunk_hdr = SN_SCTP_NEXTCHUNK(chunk_hdr);
1238
1239		/* Is the chunk long enough to not cause endless look and are there enough bytes in packet to read the chunk ? */
1240		chunk_length = SCTP_SIZE32(ntohs(chunk_hdr->chunk_length));
1241		if ((chunk_length < SN_MIN_CHUNK_SIZE) || (chunk_length > bytes_left))
1242			return (SN_PARSE_ERROR_CHHL);
1243		if (++chunk_count > sysctl_chunk_proc_limit)
1244			return (SN_PARSE_OK); /* limit for processing chunks, take what we get */
1245	}
1246
1247	if (*passoc == NULL)
1248		return (partial_match) ? (SN_PARSE_ERROR_PARTIALLOOKUP) : (SN_PARSE_ERROR_LOOKUP);
1249	else
1250		return (SN_PARSE_OK);
1251}
1252
1253/** @ingroup packet_parser
1254 * @brief Extract Vtags from Asconf Chunk
1255 *
1256 * GetAsconfVtags scans an Asconf Chunk for the vtags parameter, and then
1257 * extracts the vtags.
1258 *
1259 * GetAsconfVtags is not called from within sctp_PktParser. It is called only
1260 * from within ID_process when an AddIP has been received.
1261 *
1262 * @param la Pointer to the relevant libalias instance
1263 * @param sm Pointer to sctp message information
1264 * @param l_vtag Pointer to the local vtag in the association this SCTP Message belongs to
1265 * @param g_vtag Pointer to the local vtag in the association this SCTP Message belongs to
1266 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1267 *
1268 * @return 1 - success | 0 - fail
1269 */
1270static int
1271GetAsconfVtags(struct libalias *la, struct sctp_nat_msg *sm, uint32_t *l_vtag, uint32_t *g_vtag, int direction)
1272{
1273	/* To be removed when information is in the sctp headers */
1274#define SCTP_VTAG_PARAM 0xC007
1275	struct sctp_vtag_param {
1276		struct sctp_paramhdr ph;/* type=SCTP_VTAG_PARAM */
1277		uint32_t local_vtag;
1278		uint32_t remote_vtag;
1279	} __attribute__((packed));
1280
1281	struct sctp_vtag_param *vtag_param;
1282	struct sctp_paramhdr *param;
1283	int bytes_left;
1284	int param_size;
1285	int param_count;
1286
1287	param_count = 1;
1288	param = sm->sctpchnk.Asconf;
1289	param_size = SCTP_SIZE32(ntohs(param->param_length));
1290	bytes_left = sm->chunk_length;
1291	/* step through Asconf parameters */
1292	while((bytes_left >= param_size) && (bytes_left >= SN_VTAG_PARAM_SIZE)) {
1293		if (ntohs(param->param_type) == SCTP_VTAG_PARAM) {
1294			vtag_param = (struct sctp_vtag_param *) param;
1295			switch (direction) {
1296				/* The Internet draft is a little ambigious as to order of these vtags.
1297				   We think it is this way around. If we are wrong, the order will need
1298				   to be changed. */
1299			case SN_TO_GLOBAL:
1300				*g_vtag = vtag_param->local_vtag;
1301				*l_vtag = vtag_param->remote_vtag;
1302				break;
1303			case SN_TO_LOCAL:
1304				*g_vtag = vtag_param->remote_vtag;
1305				*l_vtag = vtag_param->local_vtag;
1306				break;
1307			}
1308			return (1); /* found */
1309		}
1310
1311		bytes_left -= param_size;
1312		if (bytes_left < SN_MIN_PARAM_SIZE)
1313			return (0);
1314
1315		param = SN_SCTP_NEXTPARAM(param);
1316		param_size = SCTP_SIZE32(ntohs(param->param_length));
1317		if (++param_count > sysctl_param_proc_limit) {
1318			SN_LOG(SN_LOG_EVENT,
1319			    logsctperror("Parameter parse limit exceeded (GetAsconfVtags)",
1320				sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction));
1321			return (0); /* not found limit exceeded*/
1322		}
1323	}
1324	return (0); /* not found */
1325}
1326
1327/** @ingroup packet_parser
1328 * @brief AddGlobalIPAddresses from Init,InitAck,or AddIP packets
1329 *
1330 * AddGlobalIPAddresses scans an SCTP chunk (in sm) for Global IP addresses, and
1331 * adds them.
1332 *
1333 * @param sm Pointer to sctp message information
1334 * @param assoc Pointer to the association this SCTP Message belongs to
1335 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1336 *
1337 */
1338static void
1339AddGlobalIPAddresses(struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int direction)
1340{
1341	struct sctp_ipv4addr_param *ipv4_param;
1342	struct sctp_paramhdr *param = NULL;
1343	struct sctp_GlobalAddress *G_Addr;
1344	struct in_addr g_addr = {0};
1345	int bytes_left = 0;
1346	int param_size;
1347	int param_count, addr_param_count = 0;
1348
1349	switch (direction) {
1350	case SN_TO_GLOBAL: /* does not contain global addresses */
1351		g_addr = sm->ip_hdr->ip_dst;
1352		bytes_left = 0; /* force exit */
1353		break;
1354	case SN_TO_LOCAL:
1355		g_addr = sm->ip_hdr->ip_src;
1356		param_count = 1;
1357		switch (sm->msg) {
1358		case SN_SCTP_INIT:
1359			bytes_left = sm->chunk_length - sizeof(struct sctp_init_chunk);
1360			param = (struct sctp_paramhdr *)((char *)sm->sctpchnk.Init + sizeof(struct sctp_init));
1361			break;
1362		case SN_SCTP_INITACK:
1363			bytes_left = sm->chunk_length - sizeof(struct sctp_init_ack_chunk);
1364			param = (struct sctp_paramhdr *)((char *)sm->sctpchnk.InitAck + sizeof(struct sctp_init_ack));
1365			break;
1366		case SN_SCTP_ASCONF:
1367			bytes_left = sm->chunk_length;
1368			param = sm->sctpchnk.Asconf;
1369			break;
1370		}
1371	}
1372	if (bytes_left >= SN_MIN_PARAM_SIZE)
1373		param_size = SCTP_SIZE32(ntohs(param->param_length));
1374	else
1375		param_size = bytes_left+1; /* force skip loop */
1376
1377	if ((assoc->state == SN_ID) && ((sm->msg == SN_SCTP_INIT) || (bytes_left < SN_MIN_PARAM_SIZE))) {/* add pkt address */
1378		G_Addr = (struct sctp_GlobalAddress *) sn_malloc(sizeof(struct sctp_GlobalAddress));
1379		if (G_Addr == NULL) {/* out of resources */
1380			SN_LOG(SN_LOG_EVENT,
1381			    logsctperror("AddGlobalIPAddress: No resources for adding global address - revert to no tracking",
1382				sm->sctp_hdr->v_tag,  0, direction));
1383			assoc->num_Gaddr = 0; /* don't track any more for this assoc*/
1384			sysctl_track_global_addresses=0;
1385			return;
1386		}
1387		G_Addr->g_addr = g_addr;
1388		if (!Add_Global_Address_to_List(assoc, G_Addr))
1389			SN_LOG(SN_LOG_EVENT,
1390			    logsctperror("AddGlobalIPAddress: Address already in list",
1391				sm->sctp_hdr->v_tag,  assoc->num_Gaddr, direction));
1392	}
1393
1394	/* step through parameters */
1395	while((bytes_left >= param_size) && (bytes_left >= sizeof(struct sctp_ipv4addr_param))) {
1396		if (assoc->num_Gaddr >= sysctl_track_global_addresses) {
1397			SN_LOG(SN_LOG_EVENT,
1398			    logsctperror("AddGlobalIPAddress: Maximum Number of addresses reached",
1399				sm->sctp_hdr->v_tag,  sysctl_track_global_addresses, direction));
1400			return;
1401		}
1402		switch (ntohs(param->param_type)) {
1403		case SCTP_ADD_IP_ADDRESS:
1404			/* skip to address parameter - leave param_size so bytes left will be calculated properly*/
1405			param = (struct sctp_paramhdr *) &((struct sctp_asconf_addrv4_param *) param)->addrp;
1406			/* FALLTHROUGH */
1407		case SCTP_IPV4_ADDRESS:
1408			ipv4_param = (struct sctp_ipv4addr_param *) param;
1409			/* add addresses to association */
1410			G_Addr = (struct sctp_GlobalAddress *) sn_malloc(sizeof(struct sctp_GlobalAddress));
1411			if (G_Addr == NULL) {/* out of resources */
1412				SN_LOG(SN_LOG_EVENT,
1413				    logsctperror("AddGlobalIPAddress: No resources for adding global address - revert to no tracking",
1414					sm->sctp_hdr->v_tag,  0, direction));
1415				assoc->num_Gaddr = 0; /* don't track any more for this assoc*/
1416				sysctl_track_global_addresses=0;
1417				return;
1418			}
1419			/* add address */
1420			addr_param_count++;
1421			if ((sm->msg == SN_SCTP_ASCONF) && (ipv4_param->addr == INADDR_ANY)) { /* use packet address */
1422				G_Addr->g_addr = g_addr;
1423				if (!Add_Global_Address_to_List(assoc, G_Addr))
1424					SN_LOG(SN_LOG_EVENT,
1425					    logsctperror("AddGlobalIPAddress: Address already in list",
1426						sm->sctp_hdr->v_tag,  assoc->num_Gaddr, direction));
1427				return; /*shouldn't be any other addresses if the zero address is given*/
1428			} else {
1429				G_Addr->g_addr.s_addr = ipv4_param->addr;
1430				if (!Add_Global_Address_to_List(assoc, G_Addr))
1431					SN_LOG(SN_LOG_EVENT,
1432					    logsctperror("AddGlobalIPAddress: Address already in list",
1433						sm->sctp_hdr->v_tag,  assoc->num_Gaddr, direction));
1434			}
1435		}
1436
1437		bytes_left -= param_size;
1438		if (bytes_left < SN_MIN_PARAM_SIZE)
1439			break;
1440
1441		param = SN_SCTP_NEXTPARAM(param);
1442		param_size = SCTP_SIZE32(ntohs(param->param_length));
1443		if (++param_count > sysctl_param_proc_limit) {
1444			SN_LOG(SN_LOG_EVENT,
1445			    logsctperror("Parameter parse limit exceeded (AddGlobalIPAddress)",
1446				sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction));
1447			break; /* limit exceeded*/
1448		}
1449	}
1450	if (addr_param_count == 0) {
1451		SN_LOG(SN_LOG_DETAIL,
1452		    logsctperror("AddGlobalIPAddress: no address parameters to add",
1453			sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction));
1454	}
1455}
1456
1457/**
1458 * @brief Add_Global_Address_to_List
1459 *
1460 * Adds a global IP address to an associations address list, if it is not
1461 * already there.  The first address added us usually the packet's address, and
1462 * is most likely to be used, so it is added at the beginning. Subsequent
1463 * addresses are added after this one.
1464 *
1465 * @param assoc Pointer to the association this SCTP Message belongs to
1466 * @param G_addr Pointer to the global address to add
1467 *
1468 * @return 1 - success | 0 - fail
1469 */
1470static int
1471Add_Global_Address_to_List(struct sctp_nat_assoc *assoc,  struct sctp_GlobalAddress *G_addr)
1472{
1473	struct sctp_GlobalAddress *iter_G_Addr = NULL, *first_G_Addr = NULL;
1474	first_G_Addr = LIST_FIRST(&(assoc->Gaddr));
1475	if (first_G_Addr == NULL) {
1476		LIST_INSERT_HEAD(&(assoc->Gaddr), G_addr, list_Gaddr); /* add new address to beginning of list*/
1477	} else {
1478		LIST_FOREACH(iter_G_Addr, &(assoc->Gaddr), list_Gaddr) {
1479			if (G_addr->g_addr.s_addr == iter_G_Addr->g_addr.s_addr)
1480				return (0); /* already exists, so don't add */
1481		}
1482		LIST_INSERT_AFTER(first_G_Addr, G_addr, list_Gaddr); /* add address to end of list*/
1483	}
1484	assoc->num_Gaddr++;
1485	return (1); /* success */
1486}
1487
1488/** @ingroup packet_parser
1489 * @brief RmGlobalIPAddresses from DelIP packets
1490 *
1491 * RmGlobalIPAddresses scans an ASCONF chunk for DelIP parameters to remove the
1492 * given Global IP addresses from the association. It will not delete the
1493 * the address if it is a list of one address.
1494 *
1495 *
1496 * @param sm Pointer to sctp message information
1497 * @param assoc Pointer to the association this SCTP Message belongs to
1498 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1499 *
1500 */
1501static void
1502RmGlobalIPAddresses(struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int direction)
1503{
1504	struct sctp_asconf_addrv4_param *asconf_ipv4_param;
1505	struct sctp_paramhdr *param;
1506	struct sctp_GlobalAddress *G_Addr, *G_Addr_tmp;
1507	struct in_addr g_addr;
1508	int bytes_left;
1509	int param_size;
1510	int param_count;
1511
1512	if (direction == SN_TO_GLOBAL)
1513		g_addr = sm->ip_hdr->ip_dst;
1514	else
1515		g_addr = sm->ip_hdr->ip_src;
1516
1517	bytes_left = sm->chunk_length;
1518	param_count = 1;
1519	param = sm->sctpchnk.Asconf;
1520	if (bytes_left >= SN_MIN_PARAM_SIZE) {
1521		param_size = SCTP_SIZE32(ntohs(param->param_length));
1522	} else {
1523		SN_LOG(SN_LOG_EVENT,
1524		    logsctperror("RmGlobalIPAddress: truncated packet - cannot remove IP addresses",
1525			sm->sctp_hdr->v_tag, sysctl_track_global_addresses, direction));
1526		return;
1527	}
1528
1529	/* step through Asconf parameters */
1530	while((bytes_left >= param_size) && (bytes_left >= sizeof(struct sctp_ipv4addr_param))) {
1531		if (ntohs(param->param_type) == SCTP_DEL_IP_ADDRESS) {
1532			asconf_ipv4_param = (struct sctp_asconf_addrv4_param *) param;
1533			if (asconf_ipv4_param->addrp.addr == INADDR_ANY) { /* remove all bar pkt address */
1534				LIST_FOREACH_SAFE(G_Addr, &(assoc->Gaddr), list_Gaddr, G_Addr_tmp) {
1535					if (G_Addr->g_addr.s_addr != sm->ip_hdr->ip_src.s_addr) {
1536						if (assoc->num_Gaddr > 1) { /* only delete if more than one */
1537							LIST_REMOVE(G_Addr, list_Gaddr);
1538							sn_free(G_Addr);
1539							assoc->num_Gaddr--;
1540						} else {
1541							SN_LOG(SN_LOG_EVENT,
1542							    logsctperror("RmGlobalIPAddress: Request to remove last IP address (didn't)",
1543								sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction));
1544						}
1545					}
1546				}
1547				return; /*shouldn't be any other addresses if the zero address is given*/
1548			} else {
1549				LIST_FOREACH_SAFE(G_Addr, &(assoc->Gaddr), list_Gaddr, G_Addr_tmp) {
1550					if (G_Addr->g_addr.s_addr == asconf_ipv4_param->addrp.addr) {
1551						if (assoc->num_Gaddr > 1) { /* only delete if more than one */
1552							LIST_REMOVE(G_Addr, list_Gaddr);
1553							sn_free(G_Addr);
1554							assoc->num_Gaddr--;
1555							break; /* Since add only adds new addresses, there should be no double entries */
1556						} else {
1557							SN_LOG(SN_LOG_EVENT,
1558							    logsctperror("RmGlobalIPAddress: Request to remove last IP address (didn't)",
1559								sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction));
1560						}
1561					}
1562				}
1563			}
1564		}
1565		bytes_left -= param_size;
1566		if (bytes_left == 0)
1567			return;
1568		else if (bytes_left < SN_MIN_PARAM_SIZE) {
1569			SN_LOG(SN_LOG_EVENT,
1570			    logsctperror("RmGlobalIPAddress: truncated packet - may not have removed all IP addresses",
1571				sm->sctp_hdr->v_tag, sysctl_track_global_addresses, direction));
1572			return;
1573		}
1574
1575		param = SN_SCTP_NEXTPARAM(param);
1576		param_size = SCTP_SIZE32(ntohs(param->param_length));
1577		if (++param_count > sysctl_param_proc_limit) {
1578			SN_LOG(SN_LOG_EVENT,
1579			    logsctperror("Parameter parse limit exceeded (RmGlobalIPAddress)",
1580				sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction));
1581			return; /* limit exceeded*/
1582		}
1583	}
1584}
1585
1586/**  @ingroup packet_parser
1587 * @brief Check that ASCONF was successful
1588 *
1589 * Each ASCONF configuration parameter carries a correlation ID which should be
1590 * matched with an ASCONFack. This is difficult for a NAT, since every
1591 * association could potentially have a number of outstanding ASCONF
1592 * configuration parameters, which should only be activated on receipt of the
1593 * ACK.
1594 *
1595 * Currently we only look for an ACK when the NAT is setting up a new
1596 * association (ie AddIP for a connection that the NAT does not know about
1597 * because the original Init went through a public interface or another NAT)
1598 * Since there is currently no connection on this path, there should be no other
1599 * ASCONF configuration parameters outstanding, so we presume that if there is
1600 * an ACK that it is responding to the AddIP and activate the new association.
1601 *
1602 * @param la Pointer to the relevant libalias instance
1603 * @param sm Pointer to sctp message information
1604 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1605 *
1606 * @return 1 - success | 0 - fail
1607 */
1608static int
1609IsASCONFack(struct libalias *la, struct sctp_nat_msg *sm, int direction)
1610{
1611	struct sctp_paramhdr *param;
1612	int bytes_left;
1613	int param_size;
1614	int param_count;
1615
1616	param_count = 1;
1617	param = sm->sctpchnk.Asconf;
1618	param_size = SCTP_SIZE32(ntohs(param->param_length));
1619	if (param_size == 8)
1620		return (1); /*success - default acknowledgement of everything */
1621
1622	bytes_left = sm->chunk_length;
1623	if (bytes_left < param_size)
1624		return (0); /* not found */
1625	/* step through Asconf parameters */
1626	while(bytes_left >= SN_ASCONFACK_PARAM_SIZE) {
1627		if (ntohs(param->param_type) == SCTP_SUCCESS_REPORT)
1628			return (1); /* success - but can't match correlation IDs - should only be one */
1629		/* check others just in case */
1630		bytes_left -= param_size;
1631		if (bytes_left >= SN_MIN_PARAM_SIZE)
1632			param = SN_SCTP_NEXTPARAM(param);
1633		else
1634			return (0);
1635
1636		param_size = SCTP_SIZE32(ntohs(param->param_length));
1637		if (bytes_left < param_size)
1638			return (0);
1639
1640		if (++param_count > sysctl_param_proc_limit) {
1641			SN_LOG(SN_LOG_EVENT,
1642			    logsctperror("Parameter parse limit exceeded (IsASCONFack)",
1643				sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction));
1644			return (0); /* not found limit exceeded*/
1645		}
1646	}
1647	return (0); /* not success */
1648}
1649
1650/**  @ingroup packet_parser
1651 * @brief Check to see if ASCONF contains an Add IP or Del IP parameter
1652 *
1653 * IsADDorDEL scans an ASCONF packet to see if it contains an AddIP or DelIP
1654 * parameter
1655 *
1656 * @param la Pointer to the relevant libalias instance
1657 * @param sm Pointer to sctp message information
1658 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1659 *
1660 * @return SCTP_ADD_IP_ADDRESS | SCTP_DEL_IP_ADDRESS | 0 - fail
1661 */
1662static int
1663IsADDorDEL(struct libalias *la, struct sctp_nat_msg *sm, int direction)
1664{
1665	struct sctp_paramhdr *param;
1666	int bytes_left;
1667	int param_size;
1668	int param_count;
1669
1670	param_count = 1;
1671	param = sm->sctpchnk.Asconf;
1672	param_size = SCTP_SIZE32(ntohs(param->param_length));
1673
1674	bytes_left = sm->chunk_length;
1675	if (bytes_left < param_size)
1676		return (0); /* not found */
1677	/* step through Asconf parameters */
1678	while(bytes_left >= SN_ASCONFACK_PARAM_SIZE) {
1679		if (ntohs(param->param_type) == SCTP_ADD_IP_ADDRESS)
1680			return (SCTP_ADD_IP_ADDRESS);
1681		else if (ntohs(param->param_type) == SCTP_DEL_IP_ADDRESS)
1682			return (SCTP_DEL_IP_ADDRESS);
1683		/* check others just in case */
1684		bytes_left -= param_size;
1685		if (bytes_left >= SN_MIN_PARAM_SIZE)
1686			param = SN_SCTP_NEXTPARAM(param);
1687		else
1688			return (0); /*Neither found */
1689
1690		param_size = SCTP_SIZE32(ntohs(param->param_length));
1691		if (bytes_left < param_size)
1692			return (0);
1693
1694		if (++param_count > sysctl_param_proc_limit) {
1695			SN_LOG(SN_LOG_EVENT,
1696			    logsctperror("Parameter parse limit exceeded IsADDorDEL)",
1697				sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction));
1698			return (0); /* not found limit exceeded*/
1699		}
1700	}
1701	return (0);  /*Neither found */
1702}
1703
1704/* ----------------------------------------------------------------------
1705 *                            STATE MACHINE CODE
1706 * ----------------------------------------------------------------------
1707 */
1708/** @addtogroup state_machine
1709 *
1710 * The SCTP NAT State Machine functions will:
1711 * - Process an already parsed packet
1712 * - Use the existing NAT Hash Tables
1713 * - Determine the next state for the association
1714 * - Update the NAT Hash Tables and Timer Queues
1715 * - Return the appropriate action to take with the packet
1716 */
1717/** @ingroup state_machine
1718 * @brief Process SCTP message
1719 *
1720 * This function is the base state machine. It calls the processing engine for
1721 * each state.
1722 *
1723 * @param la Pointer to the relevant libalias instance
1724 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1725 * @param sm Pointer to sctp message information
1726 * @param assoc Pointer to the association this SCTP Message belongs to
1727 *
1728 * @return SN_DROP_PKT | SN_NAT_PKT | SN_REPLY_ABORT | SN_REPLY_ERROR | SN_PROCESSING_ERROR
1729 */
1730static int
1731ProcessSctpMsg(struct libalias *la, int direction, struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc)
1732{
1733	int rtnval;
1734
1735	switch (assoc->state) {
1736	case SN_ID: /* Idle */
1737		rtnval = ID_process(la, direction, assoc, sm);
1738		if (rtnval != SN_NAT_PKT) {
1739			assoc->state = SN_RM;/* Mark for removal*/
1740		}
1741		return (rtnval);
1742	case SN_INi: /* Initialising - Init */
1743		return (INi_process(la, direction, assoc, sm));
1744	case SN_INa: /* Initialising - AddIP */
1745		return (INa_process(la, direction, assoc, sm));
1746	case SN_UP:  /* Association UP */
1747		return (UP_process(la, direction, assoc, sm));
1748	case SN_CL:  /* Association Closing */
1749		return (CL_process(la, direction, assoc, sm));
1750	}
1751	return (SN_PROCESSING_ERROR);
1752}
1753
1754/** @ingroup state_machine
1755 * @brief Process SCTP message while in the Idle state
1756 *
1757 * This function looks for an Incoming INIT or AddIP message.
1758 *
1759 * All other SCTP messages are invalid when in SN_ID, and are dropped.
1760 *
1761 * @param la Pointer to the relevant libalias instance
1762 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1763 * @param sm Pointer to sctp message information
1764 * @param assoc Pointer to the association this SCTP Message belongs to
1765 *
1766 * @return SN_NAT_PKT | SN_DROP_PKT | SN_REPLY_ABORT | SN_REPLY_ERROR
1767 */
1768static int
1769ID_process(struct libalias *la, int direction, struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm)
1770{
1771	switch (sm->msg) {
1772	case SN_SCTP_ASCONF:           /* a packet containing an ASCONF chunk with ADDIP */
1773		if (!sysctl_accept_global_ootb_addip && (direction == SN_TO_LOCAL))
1774			return (SN_DROP_PKT);
1775		/* if this Asconf packet does not contain the Vtag parameters it is of no use in Idle state */
1776		if (!GetAsconfVtags(la, sm, &(assoc->l_vtag), &(assoc->g_vtag), direction))
1777			return (SN_DROP_PKT);
1778		/* FALLTHROUGH */
1779	case SN_SCTP_INIT:            /* a packet containing an INIT chunk or an ASCONF AddIP */
1780		if (sysctl_track_global_addresses)
1781			AddGlobalIPAddresses(sm, assoc, direction);
1782		switch (direction) {
1783		case SN_TO_GLOBAL:
1784			assoc->l_addr = sm->ip_hdr->ip_src;
1785			assoc->a_addr = FindAliasAddress(la, assoc->l_addr);
1786			assoc->l_port = sm->sctp_hdr->src_port;
1787			assoc->g_port = sm->sctp_hdr->dest_port;
1788			if (sm->msg == SN_SCTP_INIT)
1789				assoc->g_vtag = sm->sctpchnk.Init->initiate_tag;
1790			if (AddSctpAssocGlobal(la, assoc)) /* DB clash: need to add dst address */
1791				return ((sm->msg == SN_SCTP_INIT) ? SN_REPLY_ABORT : SN_REPLY_ERROR);
1792			if (sm->msg == SN_SCTP_ASCONF) {
1793				if (AddSctpAssocLocal(la, assoc, sm->ip_hdr->ip_dst)) /* DB clash */
1794					return (SN_REPLY_ERROR);
1795				assoc->TableRegister |= SN_WAIT_TOLOCAL; /* wait for tolocal ack */
1796			}
1797		break;
1798		case SN_TO_LOCAL:
1799			assoc->l_addr = FindSctpRedirectAddress(la, sm);
1800			assoc->a_addr = sm->ip_hdr->ip_dst;
1801			assoc->l_port = sm->sctp_hdr->dest_port;
1802			assoc->g_port = sm->sctp_hdr->src_port;
1803			if (sm->msg == SN_SCTP_INIT)
1804				assoc->l_vtag = sm->sctpchnk.Init->initiate_tag;
1805			if (AddSctpAssocLocal(la, assoc, sm->ip_hdr->ip_src)) /* DB clash */
1806				return ((sm->msg == SN_SCTP_INIT) ? SN_REPLY_ABORT : SN_REPLY_ERROR);
1807			if (sm->msg == SN_SCTP_ASCONF) {
1808				if (AddSctpAssocGlobal(la, assoc)) /* DB clash: need to add src address */
1809					return (SN_REPLY_ERROR);
1810				assoc->TableRegister |= SN_WAIT_TOGLOBAL; /* wait for toglobal ack */
1811			}
1812			break;
1813		}
1814		assoc->state = (sm->msg == SN_SCTP_INIT) ? SN_INi : SN_INa;
1815		assoc->exp = SN_I_T(la);
1816		sctp_AddTimeOut(la,assoc);
1817		return (SN_NAT_PKT);
1818	default: /* Any other type of SCTP message is not valid in Idle */
1819		return (SN_DROP_PKT);
1820	}
1821	return (SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */
1822}
1823
1824/** @ingroup state_machine
1825 * @brief Process SCTP message while waiting for an INIT-ACK message
1826 *
1827 * Only an INIT-ACK, resent INIT, or an ABORT SCTP packet are valid in this
1828 * state, all other packets are dropped.
1829 *
1830 * @param la Pointer to the relevant libalias instance
1831 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1832 * @param sm Pointer to sctp message information
1833 * @param assoc Pointer to the association this SCTP Message belongs to
1834 *
1835 * @return SN_NAT_PKT | SN_DROP_PKT | SN_REPLY_ABORT
1836 */
1837static int
1838INi_process(struct libalias *la, int direction, struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm)
1839{
1840	switch (sm->msg) {
1841	case SN_SCTP_INIT:            /* a packet containing a retransmitted INIT chunk */
1842		sctp_ResetTimeOut(la, assoc, SN_I_T(la));
1843		return (SN_NAT_PKT);
1844	case SN_SCTP_INITACK:         /* a packet containing an INIT-ACK chunk */
1845		switch (direction) {
1846		case SN_TO_LOCAL:
1847			if (assoc->num_Gaddr) /*If tracking global addresses for this association */
1848				AddGlobalIPAddresses(sm, assoc, direction);
1849			assoc->l_vtag = sm->sctpchnk.Init->initiate_tag;
1850			if (AddSctpAssocLocal(la, assoc, sm->ip_hdr->ip_src)) { /* DB clash */
1851				assoc->state = SN_RM;/* Mark for removal*/
1852				return (SN_SEND_ABORT);
1853			}
1854			break;
1855		case SN_TO_GLOBAL:
1856			assoc->l_addr = sm->ip_hdr->ip_src; // Only if not set in Init! *
1857			assoc->g_vtag = sm->sctpchnk.Init->initiate_tag;
1858			if (AddSctpAssocGlobal(la, assoc)) { /* DB clash */
1859				assoc->state = SN_RM;/* Mark for removal*/
1860				return (SN_SEND_ABORT);
1861			}
1862			break;
1863		}
1864		assoc->state = SN_UP;/* association established for NAT */
1865		sctp_ResetTimeOut(la,assoc, SN_U_T(la));
1866		return (SN_NAT_PKT);
1867	case SN_SCTP_ABORT:           /* a packet containing an ABORT chunk */
1868		assoc->state = SN_RM;/* Mark for removal*/
1869		return (SN_NAT_PKT);
1870	default:
1871		return (SN_DROP_PKT);
1872	}
1873	return (SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */
1874}
1875
1876/** @ingroup state_machine
1877 * @brief Process SCTP message while waiting for an AddIp-ACK message
1878 *
1879 * Only an AddIP-ACK, resent AddIP, or an ABORT message are valid, all other
1880 * SCTP packets are dropped
1881 *
1882 * @param la Pointer to the relevant libalias instance
1883 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1884 * @param sm Pointer to sctp message information
1885 * @param assoc Pointer to the association this SCTP Message belongs to
1886 *
1887 * @return SN_NAT_PKT | SN_DROP_PKT
1888 */
1889static int
1890INa_process(struct libalias *la, int direction,struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm)
1891{
1892	switch (sm->msg) {
1893	case SN_SCTP_ASCONF:           /* a packet containing an ASCONF chunk*/
1894		sctp_ResetTimeOut(la,assoc, SN_I_T(la));
1895		return (SN_NAT_PKT);
1896	case SN_SCTP_ASCONFACK:        /* a packet containing an ASCONF chunk with a ADDIP-ACK */
1897		switch (direction) {
1898		case SN_TO_LOCAL:
1899			if (!(assoc->TableRegister & SN_WAIT_TOLOCAL)) /* wrong direction */
1900				return (SN_DROP_PKT);
1901			break;
1902		case SN_TO_GLOBAL:
1903			if (!(assoc->TableRegister & SN_WAIT_TOGLOBAL)) /* wrong direction */
1904				return (SN_DROP_PKT);
1905		}
1906		if (IsASCONFack(la,sm,direction)) {
1907			assoc->TableRegister &= SN_BOTH_TBL; /* remove wait flags */
1908			assoc->state = SN_UP; /* association established for NAT */
1909			sctp_ResetTimeOut(la,assoc, SN_U_T(la));
1910			return (SN_NAT_PKT);
1911		} else {
1912			assoc->state = SN_RM;/* Mark for removal*/
1913			return (SN_NAT_PKT);
1914		}
1915	case SN_SCTP_ABORT:           /* a packet containing an ABORT chunk */
1916		assoc->state = SN_RM;/* Mark for removal*/
1917		return (SN_NAT_PKT);
1918	default:
1919		return (SN_DROP_PKT);
1920	}
1921	return (SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */
1922}
1923
1924/** @ingroup state_machine
1925 * @brief Process SCTP messages while association is UP redirecting packets
1926 *
1927 * While in the SN_UP state, all packets for the particular association
1928 * are passed. Only a SHUT-ACK or an ABORT will cause a change of state.
1929 *
1930 * @param la Pointer to the relevant libalias instance
1931 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1932 * @param sm Pointer to sctp message information
1933 * @param assoc Pointer to the association this SCTP Message belongs to
1934 *
1935 * @return SN_NAT_PKT | SN_DROP_PKT
1936 */
1937static int
1938UP_process(struct libalias *la, int direction, struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm)
1939{
1940	switch (sm->msg) {
1941	case SN_SCTP_SHUTACK:         /* a packet containing a SHUTDOWN-ACK chunk */
1942		assoc->state = SN_CL;
1943		sctp_ResetTimeOut(la,assoc, SN_C_T(la));
1944		return (SN_NAT_PKT);
1945	case SN_SCTP_ABORT:           /* a packet containing an ABORT chunk */
1946		assoc->state = SN_RM;/* Mark for removal*/
1947		return (SN_NAT_PKT);
1948	case SN_SCTP_ASCONF:           /* a packet containing an ASCONF chunk*/
1949		if ((direction == SN_TO_LOCAL) && assoc->num_Gaddr) /*If tracking global addresses for this association & from global side */
1950			switch (IsADDorDEL(la,sm,direction)) {
1951			case SCTP_ADD_IP_ADDRESS:
1952				AddGlobalIPAddresses(sm, assoc, direction);
1953				break;
1954			case SCTP_DEL_IP_ADDRESS:
1955				RmGlobalIPAddresses(sm, assoc, direction);
1956				break;
1957			}
1958		/* fall through to default */
1959	default:
1960		sctp_ResetTimeOut(la,assoc, SN_U_T(la));
1961		return (SN_NAT_PKT);  /* forward packet */
1962	}
1963	return (SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */
1964}
1965
1966/** @ingroup state_machine
1967 * @brief Process SCTP message while association is in the process of closing
1968 *
1969 * This function waits for a SHUT-COMP to close the association. Depending on
1970 * the setting of sysctl_holddown_timer it may not remove the association
1971 * immediately, but leave it up until SN_X_T(la). Only SHUT-COMP, SHUT-ACK, and
1972 * ABORT packets are permitted in this state. All other packets are dropped.
1973 *
1974 * @param la Pointer to the relevant libalias instance
1975 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1976 * @param sm Pointer to sctp message information
1977 * @param assoc Pointer to the association this SCTP Message belongs to
1978 *
1979 * @return SN_NAT_PKT | SN_DROP_PKT
1980 */
1981static int
1982CL_process(struct libalias *la, int direction,struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm)
1983{
1984	switch (sm->msg) {
1985	case SN_SCTP_SHUTCOMP:        /* a packet containing a SHUTDOWN-COMPLETE chunk */
1986		assoc->state = SN_CL;  /* Stay in Close state until timeout */
1987		if (sysctl_holddown_timer > 0)
1988			sctp_ResetTimeOut(la, assoc, SN_X_T(la));/* allow to stay open for Tbit packets*/
1989		else
1990			assoc->state = SN_RM;/* Mark for removal*/
1991		return (SN_NAT_PKT);
1992	case SN_SCTP_SHUTACK:         /* a packet containing a SHUTDOWN-ACK chunk */
1993		assoc->state = SN_CL;  /* Stay in Close state until timeout */
1994		sctp_ResetTimeOut(la, assoc, SN_C_T(la));
1995		return (SN_NAT_PKT);
1996	case SN_SCTP_ABORT:           /* a packet containing an ABORT chunk */
1997		assoc->state = SN_RM;/* Mark for removal*/
1998		return (SN_NAT_PKT);
1999	default:
2000		return (SN_DROP_PKT);
2001	}
2002	return (SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */
2003}
2004
2005/* ----------------------------------------------------------------------
2006 *                           HASH TABLE CODE
2007 * ----------------------------------------------------------------------
2008 */
2009/** @addtogroup Hash
2010 *
2011 * The Hash functions facilitate searching the NAT Hash Tables for associations
2012 * as well as adding/removing associations from the table(s).
2013 */
2014/** @ingroup Hash
2015 * @brief Find the SCTP association given the local address, port and vtag
2016 *
2017 * Searches the local look-up table for the association entry matching the
2018 * provided local <address:ports:vtag> tuple
2019 *
2020 * @param la Pointer to the relevant libalias instance
2021 * @param l_addr local address
2022 * @param g_addr global address
2023 * @param l_vtag local Vtag
2024 * @param l_port local Port
2025 * @param g_port global Port
2026 *
2027 * @return pointer to association or NULL
2028 */
2029static struct sctp_nat_assoc *
2030FindSctpLocal(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)
2031{
2032	u_int i;
2033	struct sctp_nat_assoc *assoc = NULL;
2034	struct sctp_GlobalAddress *G_Addr = NULL;
2035
2036	if (l_vtag != 0) { /* an init packet, vtag==0 */
2037		i = SN_TABLE_HASH(l_vtag, l_port, la->sctpNatTableSize);
2038		LIST_FOREACH(assoc, &la->sctpTableLocal[i], list_L) {
2039			if ((assoc->l_vtag == l_vtag) && (assoc->l_port == l_port) && (assoc->g_port == g_port)\
2040			    && (assoc->l_addr.s_addr == l_addr.s_addr)) {
2041				if (assoc->num_Gaddr) {
2042					LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
2043						if (G_Addr->g_addr.s_addr == g_addr.s_addr)
2044							return (assoc);
2045					}
2046				} else {
2047					return (assoc);
2048				}
2049			}
2050		}
2051	}
2052	return (NULL);
2053}
2054
2055/** @ingroup Hash
2056 * @brief Check for Global Clash
2057 *
2058 * Searches the global look-up table for the association entry matching the
2059 * provided global <(addresses):ports:vtag> tuple
2060 *
2061 * @param la Pointer to the relevant libalias instance
2062 * @param Cassoc association being checked for a clash
2063 *
2064 * @return pointer to association or NULL
2065 */
2066static struct sctp_nat_assoc *
2067FindSctpGlobalClash(struct libalias *la, struct sctp_nat_assoc *Cassoc)
2068{
2069	u_int i;
2070	struct sctp_nat_assoc *assoc = NULL;
2071	struct sctp_GlobalAddress *G_Addr = NULL;
2072	struct sctp_GlobalAddress *G_AddrC = NULL;
2073
2074	if (Cassoc->g_vtag != 0) { /* an init packet, vtag==0 */
2075		i = SN_TABLE_HASH(Cassoc->g_vtag, Cassoc->g_port, la->sctpNatTableSize);
2076		LIST_FOREACH(assoc, &la->sctpTableGlobal[i], list_G) {
2077			if ((assoc->g_vtag == Cassoc->g_vtag) && (assoc->g_port == Cassoc->g_port) && (assoc->l_port == Cassoc->l_port)) {
2078				if (assoc->num_Gaddr) {
2079					LIST_FOREACH(G_AddrC, &(Cassoc->Gaddr), list_Gaddr) {
2080						LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
2081							if (G_Addr->g_addr.s_addr == G_AddrC->g_addr.s_addr)
2082								return (assoc);
2083						}
2084					}
2085				} else {
2086					return (assoc);
2087				}
2088			}
2089		}
2090	}
2091	return (NULL);
2092}
2093
2094/** @ingroup Hash
2095 * @brief Find the SCTP association given the global port and vtag
2096 *
2097 * Searches the global look-up table for the association entry matching the
2098 * provided global <address:ports:vtag> tuple
2099 *
2100 * If all but the global address match it sets partial_match to 1 to indicate a
2101 * partial match. If the NAT is tracking global IP addresses for this
2102 * association, the NAT may respond with an ERRORM to request the missing
2103 * address to be added.
2104 *
2105 * @param la Pointer to the relevant libalias instance
2106 * @param g_addr global address
2107 * @param g_vtag global vtag
2108 * @param g_port global port
2109 * @param l_port local port
2110 *
2111 * @return pointer to association or NULL
2112 */
2113static struct sctp_nat_assoc *
2114FindSctpGlobal(struct libalias *la, struct in_addr g_addr, uint32_t g_vtag, uint16_t g_port, uint16_t l_port, int *partial_match)
2115{
2116	u_int i;
2117	struct sctp_nat_assoc *assoc = NULL;
2118	struct sctp_GlobalAddress *G_Addr = NULL;
2119
2120	*partial_match = 0;
2121	if (g_vtag != 0) { /* an init packet, vtag==0 */
2122		i = SN_TABLE_HASH(g_vtag, g_port, la->sctpNatTableSize);
2123		LIST_FOREACH(assoc, &la->sctpTableGlobal[i], list_G) {
2124			if ((assoc->g_vtag == g_vtag) && (assoc->g_port == g_port) && (assoc->l_port == l_port)) {
2125				*partial_match = 1;
2126				if (assoc->num_Gaddr) {
2127					LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
2128						if (G_Addr->g_addr.s_addr == g_addr.s_addr)
2129							return (assoc);
2130					}
2131				} else {
2132					return (assoc);
2133				}
2134			}
2135		}
2136	}
2137	return (NULL);
2138}
2139
2140/** @ingroup Hash
2141 * @brief Find the SCTP association for a T-Flag message (given the global port and local vtag)
2142 *
2143 * Searches the local look-up table for a unique association entry matching the
2144 * provided global port and local vtag information
2145 *
2146 * @param la Pointer to the relevant libalias instance
2147 * @param g_addr global address
2148 * @param l_vtag local Vtag
2149 * @param g_port global Port
2150 * @param l_port local Port
2151 *
2152 * @return pointer to association or NULL
2153 */
2154static struct sctp_nat_assoc *
2155FindSctpLocalT(struct libalias *la, struct in_addr g_addr, uint32_t l_vtag, uint16_t g_port, uint16_t l_port)
2156{
2157	u_int i;
2158	struct sctp_nat_assoc *assoc = NULL, *lastmatch = NULL;
2159	struct sctp_GlobalAddress *G_Addr = NULL;
2160	int cnt = 0;
2161
2162	if (l_vtag != 0) { /* an init packet, vtag==0 */
2163		i = SN_TABLE_HASH(l_vtag, g_port, la->sctpNatTableSize);
2164		LIST_FOREACH(assoc, &la->sctpTableGlobal[i], list_G) {
2165			if ((assoc->g_vtag == l_vtag) && (assoc->g_port == g_port) && (assoc->l_port == l_port)) {
2166				if (assoc->num_Gaddr) {
2167					LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
2168						if (G_Addr->g_addr.s_addr == g_addr.s_addr)
2169							return (assoc); /* full match */
2170					}
2171				} else {
2172					if (++cnt > 1)
2173						return (NULL);
2174					lastmatch = assoc;
2175				}
2176			}
2177		}
2178	}
2179	/* If there is more than one match we do not know which local address to send to */
2180	return (cnt ? lastmatch : NULL);
2181}
2182
2183/** @ingroup Hash
2184 * @brief Find the SCTP association for a T-Flag message (given the local port and global vtag)
2185 *
2186 * Searches the global look-up table for a unique association entry matching the
2187 * provided local port and global vtag information
2188 *
2189 * @param la Pointer to the relevant libalias instance
2190 * @param g_addr global address
2191 * @param g_vtag global vtag
2192 * @param l_port local port
2193 * @param g_port global port
2194 *
2195 * @return pointer to association or NULL
2196 */
2197static struct sctp_nat_assoc *
2198FindSctpGlobalT(struct libalias *la, struct in_addr g_addr, uint32_t g_vtag, uint16_t l_port, uint16_t g_port)
2199{
2200	u_int i;
2201	struct sctp_nat_assoc *assoc = NULL;
2202	struct sctp_GlobalAddress *G_Addr = NULL;
2203
2204	if (g_vtag != 0) { /* an init packet, vtag==0 */
2205		i = SN_TABLE_HASH(g_vtag, l_port, la->sctpNatTableSize);
2206		LIST_FOREACH(assoc, &la->sctpTableLocal[i], list_L) {
2207			if ((assoc->l_vtag == g_vtag) && (assoc->l_port == l_port) && (assoc->g_port == g_port)) {
2208				if (assoc->num_Gaddr) {
2209					LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
2210						if (G_Addr->g_addr.s_addr == g_addr.s_addr)
2211							return (assoc);
2212					}
2213				} else {
2214					return (assoc);
2215				}
2216			}
2217		}
2218	}
2219	return (NULL);
2220}
2221
2222/** @ingroup Hash
2223 * @brief  Add the sctp association information to the local look up table
2224 *
2225 * Searches the local look-up table for an existing association with the same
2226 * details. If a match exists and is ONLY in the local look-up table then this
2227 * is a repeated INIT packet, we need to remove this association from the
2228 * look-up table and add the new association
2229 *
2230 * The new association is added to the head of the list and state is updated
2231 *
2232 * @param la Pointer to the relevant libalias instance
2233 * @param assoc pointer to sctp association
2234 * @param g_addr global address
2235 *
2236 * @return SN_ADD_OK | SN_ADD_CLASH
2237 */
2238static int
2239AddSctpAssocLocal(struct libalias *la, struct sctp_nat_assoc *assoc, struct in_addr g_addr)
2240{
2241	struct sctp_nat_assoc *found;
2242
2243	LIBALIAS_LOCK_ASSERT(la);
2244	found = FindSctpLocal(la, assoc->l_addr, g_addr, assoc->l_vtag, assoc->l_port, assoc->g_port);
2245	/*
2246	 * Note that if a different global address initiated this Init,
2247	 * ie it wasn't resent as presumed:
2248	 *  - the local receiver if receiving it for the first time will establish
2249	 *    an association with the new global host
2250	 *  - if receiving an init from a different global address after sending a
2251	 *    lost initack it will send an initack to the new global host, the first
2252	 *    association attempt will then be blocked if retried.
2253	 */
2254	if (found != NULL) {
2255		if ((found->TableRegister == SN_LOCAL_TBL) && (found->g_port == assoc->g_port)) { /* resent message */
2256			RmSctpAssoc(la, found);
2257			sctp_RmTimeOut(la, found);
2258			freeGlobalAddressList(found);
2259			sn_free(found);
2260		} else
2261			return (SN_ADD_CLASH);
2262	}
2263
2264	LIST_INSERT_HEAD(&la->sctpTableLocal[SN_TABLE_HASH(assoc->l_vtag, assoc->l_port, la->sctpNatTableSize)],
2265	    assoc, list_L);
2266	assoc->TableRegister |= SN_LOCAL_TBL;
2267	la->sctpLinkCount++; //increment link count
2268
2269	if (assoc->TableRegister == SN_BOTH_TBL) {
2270		/* libalias log -- controlled by libalias */
2271		if (la->packetAliasMode & PKT_ALIAS_LOG)
2272			SctpShowAliasStats(la);
2273
2274		SN_LOG(SN_LOG_INFO, logsctpassoc(assoc, "^"));
2275	}
2276
2277	return (SN_ADD_OK);
2278}
2279
2280/** @ingroup Hash
2281 * @brief  Add the sctp association information to the global look up table
2282 *
2283 * Searches the global look-up table for an existing association with the same
2284 * details. If a match exists and is ONLY in the global look-up table then this
2285 * is a repeated INIT packet, we need to remove this association from the
2286 * look-up table and add the new association
2287 *
2288 * The new association is added to the head of the list and state is updated
2289 *
2290 * @param la Pointer to the relevant libalias instance
2291 * @param assoc pointer to sctp association
2292 *
2293 * @return SN_ADD_OK | SN_ADD_CLASH
2294 */
2295static int
2296AddSctpAssocGlobal(struct libalias *la, struct sctp_nat_assoc *assoc)
2297{
2298	struct sctp_nat_assoc *found;
2299
2300	LIBALIAS_LOCK_ASSERT(la);
2301	found = FindSctpGlobalClash(la, assoc);
2302	if (found != NULL) {
2303		if ((found->TableRegister == SN_GLOBAL_TBL) &&
2304		    (found->l_addr.s_addr == assoc->l_addr.s_addr) &&
2305		    (found->l_port == assoc->l_port)) { /* resent message */
2306			RmSctpAssoc(la, found);
2307			sctp_RmTimeOut(la, found);
2308			freeGlobalAddressList(found);
2309			sn_free(found);
2310		} else
2311			return (SN_ADD_CLASH);
2312	}
2313
2314	LIST_INSERT_HEAD(&la->sctpTableGlobal[SN_TABLE_HASH(assoc->g_vtag, assoc->g_port, la->sctpNatTableSize)],
2315	    assoc, list_G);
2316	assoc->TableRegister |= SN_GLOBAL_TBL;
2317	la->sctpLinkCount++; //increment link count
2318
2319	if (assoc->TableRegister == SN_BOTH_TBL) {
2320		/* libalias log -- controlled by libalias */
2321		if (la->packetAliasMode & PKT_ALIAS_LOG)
2322			SctpShowAliasStats(la);
2323
2324		SN_LOG(SN_LOG_INFO, logsctpassoc(assoc, "^"));
2325	}
2326
2327	return (SN_ADD_OK);
2328}
2329
2330/** @ingroup Hash
2331 * @brief Remove the sctp association information from the look up table
2332 *
2333 * For each of the two (local/global) look-up tables, remove the association
2334 * from that table IF it has been registered in that table.
2335 *
2336 * NOTE: The calling code is responsible for freeing memory allocated to the
2337 *       association structure itself
2338 *
2339 * NOTE: The association is NOT removed from the timer queue
2340 *
2341 * @param la Pointer to the relevant libalias instance
2342 * @param assoc pointer to sctp association
2343 */
2344static void
2345RmSctpAssoc(struct libalias *la, struct sctp_nat_assoc *assoc)
2346{
2347	//  struct sctp_nat_assoc *found;
2348	if (assoc == NULL) {
2349		/* very bad, log and die*/
2350		SN_LOG(SN_LOG_LOW,
2351		    logsctperror("ERROR: alias_sctp:RmSctpAssoc(NULL)\n", 0, 0, SN_TO_NODIR));
2352		return;
2353	}
2354	/* log if association is fully up and now closing */
2355	if (assoc->TableRegister == SN_BOTH_TBL) {
2356		SN_LOG(SN_LOG_INFO, logsctpassoc(assoc, "$"));
2357	}
2358	LIBALIAS_LOCK_ASSERT(la);
2359	if (assoc->TableRegister & SN_LOCAL_TBL) {
2360		assoc->TableRegister ^= SN_LOCAL_TBL;
2361		la->sctpLinkCount--; //decrement link count
2362		LIST_REMOVE(assoc, list_L);
2363	}
2364
2365	if (assoc->TableRegister & SN_GLOBAL_TBL) {
2366		assoc->TableRegister ^= SN_GLOBAL_TBL;
2367		la->sctpLinkCount--; //decrement link count
2368		LIST_REMOVE(assoc, list_G);
2369	}
2370	//  sn_free(assoc); //Don't remove now, remove if needed later
2371	/* libalias logging -- controlled by libalias log definition */
2372	if (la->packetAliasMode & PKT_ALIAS_LOG)
2373		SctpShowAliasStats(la);
2374}
2375
2376/**
2377 * @ingroup Hash
2378 * @brief  free the Global Address List memory
2379 *
2380 * freeGlobalAddressList deletes all global IP addresses in an associations
2381 * global IP address list.
2382 *
2383 * @param assoc
2384 */
2385static void freeGlobalAddressList(struct sctp_nat_assoc *assoc)
2386{
2387	struct sctp_GlobalAddress *gaddr1=NULL,*gaddr2=NULL;
2388	/*free global address list*/
2389	gaddr1 = LIST_FIRST(&(assoc->Gaddr));
2390	while (gaddr1 != NULL) {
2391		gaddr2 = LIST_NEXT(gaddr1, list_Gaddr);
2392		sn_free(gaddr1);
2393		gaddr1 = gaddr2;
2394	}
2395}
2396/* ----------------------------------------------------------------------
2397 *                            TIMER QUEUE CODE
2398 * ----------------------------------------------------------------------
2399 */
2400/** @addtogroup Timer
2401 *
2402 * The timer queue management functions are designed to operate efficiently with
2403 * a minimum of interaction with the queues.
2404 *
2405 * Once a timeout is set in the queue it will not be altered in the queue unless
2406 * it has to be changed to a shorter time (usually only for aborts and closing).
2407 * On a queue timeout, the real expiry time is checked, and if not leq than the
2408 * timeout it is requeued (O(1)) at its later time. This is especially important
2409 * for normal packets sent during an association. When a timer expires, it is
2410 * updated to its new expiration time if necessary, or processed as a
2411 * timeout. This means that while in UP state, the timing queue is only altered
2412 * every U_T (every few minutes) for a particular association.
2413 */
2414/** @ingroup Timer
2415 * @brief Add an association timeout to the timer queue
2416 *
2417 * Determine the location in the queue to add the timeout and insert the
2418 * association into the list at that queue position
2419 *
2420 * @param la
2421 * @param assoc
2422 */
2423static void
2424sctp_AddTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc)
2425{
2426	int add_loc;
2427	LIBALIAS_LOCK_ASSERT(la);
2428	add_loc = assoc->exp - la->sctpNatTimer.loc_time + la->sctpNatTimer.cur_loc;
2429	if (add_loc >= SN_TIMER_QUEUE_SIZE)
2430		add_loc -= SN_TIMER_QUEUE_SIZE;
2431	LIST_INSERT_HEAD(&la->sctpNatTimer.TimerQ[add_loc], assoc, timer_Q);
2432	assoc->exp_loc = add_loc;
2433}
2434
2435/** @ingroup Timer
2436 * @brief Remove an association from timer queue
2437 *
2438 * This is an O(1) operation to remove the association pointer from its
2439 * current position in the timer queue
2440 *
2441 * @param la Pointer to the relevant libalias instance
2442 * @param assoc pointer to sctp association
2443 */
2444static void
2445sctp_RmTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc)
2446{
2447	LIBALIAS_LOCK_ASSERT(la);
2448	LIST_REMOVE(assoc, timer_Q);/* Note this is O(1) */
2449}
2450
2451/** @ingroup Timer
2452 * @brief Reset timer in timer queue
2453 *
2454 * Reset the actual timeout for the specified association. If it is earlier than
2455 * the existing timeout, then remove and re-install the association into the
2456 * queue
2457 *
2458 * @param la Pointer to the relevant libalias instance
2459 * @param assoc pointer to sctp association
2460 * @param newexp New expiration time
2461 */
2462static void
2463sctp_ResetTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc, int newexp)
2464{
2465	if (newexp < assoc->exp) {
2466		sctp_RmTimeOut(la, assoc);
2467		assoc->exp = newexp;
2468		sctp_AddTimeOut(la, assoc);
2469	} else {
2470		assoc->exp = newexp;
2471	}
2472}
2473
2474/** @ingroup Timer
2475 * @brief Check timer Q against current time
2476 *
2477 * Loop through each entry in the timer queue since the last time we processed
2478 * the timer queue until now (the current time). For each association in the
2479 * event list, we remove it from that position in the timer queue and check if
2480 * it has really expired. If so we:
2481 * - Log the timer expiry
2482 * - Remove the association from the NAT tables
2483 * - Release the memory used by the association
2484 *
2485 * If the timer hasn't really expired we place the association into its new
2486 * correct position in the timer queue.
2487 *
2488 * @param la  Pointer to the relevant libalias instance
2489 */
2490void
2491sctp_CheckTimers(struct libalias *la)
2492{
2493	struct sctp_nat_assoc *assoc;
2494
2495	LIBALIAS_LOCK_ASSERT(la);
2496	while(la->timeStamp >= la->sctpNatTimer.loc_time) {
2497		while (!LIST_EMPTY(&la->sctpNatTimer.TimerQ[la->sctpNatTimer.cur_loc])) {
2498			assoc = LIST_FIRST(&la->sctpNatTimer.TimerQ[la->sctpNatTimer.cur_loc]);
2499			//SLIST_REMOVE_HEAD(&la->sctpNatTimer.TimerQ[la->sctpNatTimer.cur_loc], timer_Q);
2500			LIST_REMOVE(assoc, timer_Q);
2501			if (la->timeStamp >= assoc->exp) { /* state expired */
2502				SN_LOG(((assoc->state == SN_CL) ? (SN_LOG_DEBUG) : (SN_LOG_INFO)),
2503				    logsctperror("Timer Expired", assoc->g_vtag, assoc->state, SN_TO_NODIR));
2504				RmSctpAssoc(la, assoc);
2505				freeGlobalAddressList(assoc);
2506				sn_free(assoc);
2507			} else {/* state not expired, reschedule timer*/
2508				sctp_AddTimeOut(la, assoc);
2509			}
2510		}
2511		/* Goto next location in the timer queue*/
2512		++la->sctpNatTimer.loc_time;
2513		if (++la->sctpNatTimer.cur_loc >= SN_TIMER_QUEUE_SIZE)
2514			la->sctpNatTimer.cur_loc = 0;
2515	}
2516}
2517
2518/* ----------------------------------------------------------------------
2519 *                              LOGGING CODE
2520 * ----------------------------------------------------------------------
2521 */
2522/** @addtogroup Logging
2523 *
2524 * The logging functions provide logging of different items ranging from logging
2525 * a simple message, through logging an association details to logging the
2526 * current state of the NAT tables
2527 */
2528/** @ingroup Logging
2529 * @brief Log sctp nat errors
2530 *
2531 * @param errormsg Error message to be logged
2532 * @param vtag Current Vtag
2533 * @param error Error number
2534 * @param direction Direction of packet
2535 */
2536static void
2537logsctperror(char *errormsg, uint32_t vtag, int error, int direction)
2538{
2539	char dir;
2540	switch (direction) {
2541	case SN_TO_LOCAL:
2542		dir = 'L';
2543		break;
2544	case SN_TO_GLOBAL:
2545		dir = 'G';
2546		break;
2547	default:
2548		dir = '*';
2549		break;
2550	}
2551	SctpAliasLog("->%c %s (vt=%u) %d\n", dir, errormsg, ntohl(vtag), error);
2552}
2553
2554/** @ingroup Logging
2555 * @brief Log what the parser parsed
2556 *
2557 * @param direction Direction of packet
2558 * @param sm Pointer to sctp message information
2559 */
2560static void
2561logsctpparse(int direction, struct sctp_nat_msg *sm)
2562{
2563	char *ploc, *pstate;
2564	switch (direction) {
2565	case SN_TO_LOCAL:
2566		ploc = "TO_LOCAL -";
2567		break;
2568	case SN_TO_GLOBAL:
2569		ploc = "TO_GLOBAL -";
2570		break;
2571	default:
2572		ploc = "";
2573	}
2574	switch (sm->msg) {
2575	case SN_SCTP_INIT:
2576		pstate = "Init";
2577		break;
2578	case SN_SCTP_INITACK:
2579		pstate = "InitAck";
2580		break;
2581	case SN_SCTP_ABORT:
2582		pstate = "Abort";
2583		break;
2584	case SN_SCTP_SHUTACK:
2585		pstate = "ShutAck";
2586		break;
2587	case SN_SCTP_SHUTCOMP:
2588		pstate = "ShutComp";
2589		break;
2590	case SN_SCTP_ASCONF:
2591		pstate = "Asconf";
2592		break;
2593	case SN_SCTP_ASCONFACK:
2594		pstate = "AsconfAck";
2595		break;
2596	case SN_SCTP_OTHER:
2597		pstate = "Other";
2598		break;
2599	default:
2600		pstate = "***ERROR***";
2601		break;
2602	}
2603	SctpAliasLog("Parsed: %s %s\n", ploc, pstate);
2604}
2605
2606/** @ingroup Logging
2607 * @brief Log an SCTP association's details
2608 *
2609 * @param assoc pointer to sctp association
2610 * @param s Character that indicates the state of processing for this packet
2611 */
2612static void logsctpassoc(struct sctp_nat_assoc *assoc, char *s)
2613{
2614	struct sctp_GlobalAddress *G_Addr = NULL;
2615	char *sp;
2616	char addrbuf[INET_ADDRSTRLEN];
2617
2618	switch (assoc->state) {
2619	case SN_ID:
2620		sp = "ID ";
2621		break;
2622	case SN_INi:
2623		sp = "INi ";
2624		break;
2625	case SN_INa:
2626		sp = "INa ";
2627		break;
2628	case SN_UP:
2629		sp = "UP ";
2630		break;
2631	case SN_CL:
2632		sp = "CL ";
2633		break;
2634	case SN_RM:
2635		sp = "RM ";
2636		break;
2637	default:
2638		sp = "***ERROR***";
2639		break;
2640	}
2641	SctpAliasLog("%sAssoc: %s exp=%u la=%s lv=%u lp=%u gv=%u gp=%u tbl=%d\n",
2642	    s, sp, assoc->exp, inet_ntoa_r(assoc->l_addr, addrbuf),
2643	    ntohl(assoc->l_vtag), ntohs(assoc->l_port),
2644	    ntohl(assoc->g_vtag), ntohs(assoc->g_port),
2645	    assoc->TableRegister);
2646	/* list global addresses */
2647	LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
2648		SctpAliasLog("\t\tga=%s\n",
2649		    inet_ntoa_r(G_Addr->g_addr, addrbuf));
2650	}
2651}
2652
2653/** @ingroup Logging
2654 * @brief Output Global table to log
2655 *
2656 * @param la Pointer to the relevant libalias instance
2657 */
2658static void logSctpGlobal(struct libalias *la)
2659{
2660	u_int i;
2661	struct sctp_nat_assoc *assoc = NULL;
2662
2663	SctpAliasLog("G->\n");
2664	for (i = 0; i < la->sctpNatTableSize; i++) {
2665		LIST_FOREACH(assoc, &la->sctpTableGlobal[i], list_G) {
2666			logsctpassoc(assoc, " ");
2667		}
2668	}
2669}
2670
2671/** @ingroup Logging
2672 * @brief  Output Local table to log
2673 *
2674 * @param la Pointer to the relevant libalias instance
2675 */
2676static void logSctpLocal(struct libalias *la)
2677{
2678	u_int i;
2679	struct sctp_nat_assoc *assoc = NULL;
2680
2681	SctpAliasLog("L->\n");
2682	for (i = 0; i < la->sctpNatTableSize; i++) {
2683		LIST_FOREACH(assoc, &la->sctpTableLocal[i], list_L) {
2684			logsctpassoc(assoc, " ");
2685		}
2686	}
2687}
2688
2689/** @ingroup Logging
2690 * @brief Output timer queue to log
2691 *
2692 * @param la Pointer to the relevant libalias instance
2693 */
2694static void logTimerQ(struct libalias *la)
2695{
2696	static char buf[50];
2697	u_int i;
2698	struct sctp_nat_assoc *assoc = NULL;
2699
2700	SctpAliasLog("t->\n");
2701	for (i = 0; i < SN_TIMER_QUEUE_SIZE; i++) {
2702		LIST_FOREACH(assoc, &la->sctpNatTimer.TimerQ[i], timer_Q) {
2703			snprintf(buf, 50, " l=%u ",i);
2704			//SctpAliasLog(la->logDesc," l=%d ",i);
2705			logsctpassoc(assoc, buf);
2706		}
2707	}
2708}
2709
2710/** @ingroup Logging
2711 * @brief Sctp NAT logging function
2712 *
2713 * This function is based on a similar function in alias_db.c
2714 *
2715 * @param str/stream logging descriptor
2716 * @param format printf type string
2717 */
2718#ifdef _KERNEL
2719static void
2720SctpAliasLog(const char *format, ...)
2721{
2722	char buffer[LIBALIAS_BUF_SIZE];
2723	va_list ap;
2724	va_start(ap, format);
2725	vsnprintf(buffer, LIBALIAS_BUF_SIZE, format, ap);
2726	va_end(ap);
2727	log(LOG_SECURITY | LOG_INFO, "alias_sctp: %s", buffer);
2728}
2729#else
2730static void
2731SctpAliasLog(FILE *stream, const char *format, ...)
2732{
2733	va_list ap;
2734
2735	va_start(ap, format);
2736	vfprintf(stream, format, ap);
2737	va_end(ap);
2738	fflush(stream);
2739}
2740#endif
2741