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