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