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