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