2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
5 * Swinburne University of Technology, Melbourne, Australia.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS "AS IS" AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * Alias_sctp forms part of the libalias kernel module to handle
31 * Network Address Translation (NAT) for the SCTP protocol.
33 * This software was developed by David A. Hayes and Jason But
35 * The design is outlined in CAIA technical report number 080618A
36 * (D. Hayes and J. But, "Alias_sctp Version 0.1: SCTP NAT implementation in IPFW")
38 * Development is part of the CAIA SONATA project,
39 * proposed by Jason But and Grenville Armitage:
40 * http://caia.swin.edu.au/urp/sonata/
43 * This project has been made possible in part by a grant from
44 * the Cisco University Research Program Fund at Community
45 * Foundation Silicon Valley.
49 * Alias_sctp is part of the SONATA (http://caia.swin.edu.au/urp/sonata) project
50 * to develop and release a BSD licensed implementation of a Network Address
51 * Translation (NAT) module that supports the Stream Control Transmission
54 * Traditional address and port number look ups are inadequate for SCTP's
55 * operation due to both processing requirements and issues with multi-homing.
56 * Alias_sctp integrates with FreeBSD's ipfw/libalias NAT system.
58 * Version 0.2 features include:
59 * - Support for global multi-homing
60 * - Support for ASCONF modification from Internet Draft
61 * (draft-stewart-behave-sctpnat-04, R. Stewart and M. Tuexen, "Stream control
62 * transmission protocol (SCTP) network address translation," Jul. 2008) to
63 * provide support for multi-homed privately addressed hosts
64 * - Support for forwarding of T-flagged packets
65 * - Generation and delivery of AbortM/ErrorM packets upon detection of NAT
67 * - Per-port forwarding rules
68 * - Dynamically controllable logging and statistics
69 * - Dynamic management of timers
70 * - Dynamic control of hash-table size
76 #include <machine/stdarg.h>
77 #include <sys/param.h>
78 #include <sys/gsb_crc32.h>
79 #include <sys/systm.h>
80 #include <sys/kernel.h>
81 #include <sys/module.h>
82 #include <sys/syslog.h>
83 #include <netinet/libalias/alias_sctp.h>
84 #include <netinet/libalias/alias.h>
85 #include <netinet/libalias/alias_local.h>
86 #include <netinet/sctp_crc32.h>
87 #include <machine/in_cksum.h>
89 #include "alias_sctp.h"
90 #include <arpa/inet.h>
92 #include "alias_local.h"
93 #include <machine/in_cksum.h>
94 #include <sys/libkern.h>
95 #endif //#ifdef _KERNEL
97 /* ----------------------------------------------------------------------
99 * ----------------------------------------------------------------------
101 /* Packet Parsing Functions */
102 static int sctp_PktParser(struct libalias *la, int direction, struct ip *pip,
103 struct sctp_nat_msg *sm, struct sctp_nat_assoc **passoc);
104 static int GetAsconfVtags(struct libalias *la, struct sctp_nat_msg *sm,
105 uint32_t *l_vtag, uint32_t *g_vtag, int direction);
106 static int IsASCONFack(struct libalias *la, struct sctp_nat_msg *sm, int direction);
108 static void AddGlobalIPAddresses(struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int direction);
109 static int Add_Global_Address_to_List(struct sctp_nat_assoc *assoc, struct sctp_GlobalAddress *G_addr);
110 static void RmGlobalIPAddresses(struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int direction);
111 static int IsADDorDEL(struct libalias *la, struct sctp_nat_msg *sm, int direction);
113 /* State Machine Functions */
114 static int ProcessSctpMsg(struct libalias *la, int direction, \
115 struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc);
117 static int ID_process(struct libalias *la, int direction,\
118 struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm);
119 static int INi_process(struct libalias *la, int direction,\
120 struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm);
121 static int INa_process(struct libalias *la, int direction,\
122 struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm);
123 static int UP_process(struct libalias *la, int direction,\
124 struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm);
125 static int CL_process(struct libalias *la, int direction,\
126 struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm);
127 static void TxAbortErrorM(struct libalias *la, struct sctp_nat_msg *sm,\
128 struct sctp_nat_assoc *assoc, int sndrply, int direction);
130 /* Hash Table Functions */
131 static struct sctp_nat_assoc*
132 FindSctpLocal(struct libalias *la, struct in_addr l_addr, struct in_addr g_addr, uint32_t l_vtag, uint16_t l_port, uint16_t g_port);
133 static struct sctp_nat_assoc*
134 FindSctpGlobal(struct libalias *la, struct in_addr g_addr, uint32_t g_vtag, uint16_t g_port, uint16_t l_port, int *partial_match);
135 static struct sctp_nat_assoc*
136 FindSctpGlobalClash(struct libalias *la, struct sctp_nat_assoc *Cassoc);
137 static struct sctp_nat_assoc*
138 FindSctpLocalT(struct libalias *la, struct in_addr g_addr, uint32_t l_vtag, uint16_t g_port, uint16_t l_port);
139 static struct sctp_nat_assoc*
140 FindSctpGlobalT(struct libalias *la, struct in_addr g_addr, uint32_t g_vtag, uint16_t l_port, uint16_t g_port);
142 static int AddSctpAssocLocal(struct libalias *la, struct sctp_nat_assoc *assoc, struct in_addr g_addr);
143 static int AddSctpAssocGlobal(struct libalias *la, struct sctp_nat_assoc *assoc);
144 static void RmSctpAssoc(struct libalias *la, struct sctp_nat_assoc *assoc);
145 static void freeGlobalAddressList(struct sctp_nat_assoc *assoc);
147 /* Timer Queue Functions */
148 static void sctp_AddTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc);
149 static void sctp_RmTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc);
150 static void sctp_ResetTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc, int newexp);
151 void sctp_CheckTimers(struct libalias *la);
154 /* Logging Functions */
155 static void logsctperror(char* errormsg, uint32_t vtag, int error, int direction);
156 static void logsctpparse(int direction, struct sctp_nat_msg *sm);
157 static void logsctpassoc(struct sctp_nat_assoc *assoc, char *s);
158 static void logTimerQ(struct libalias *la);
159 static void logSctpGlobal(struct libalias *la);
160 static void logSctpLocal(struct libalias *la);
162 static void SctpAliasLog(const char *format, ...);
165 /** @defgroup external External code changes and modifications
167 * Some changes have been made to files external to alias_sctp.(c|h). These
168 * changes are primarily due to code needing to call static functions within
169 * those files or to perform extra functionality that can only be performed
170 * within these files.
172 /** @ingroup external
173 * @brief Log current statistics for the libalias instance
175 * This function is defined in alias_db.c, since it calls static functions in
178 * Calls the higher level ShowAliasStats() in alias_db.c which logs all current
179 * statistics about the libalias instance - including SCTP statistics
181 * @param la Pointer to the libalias instance
183 void SctpShowAliasStats(struct libalias *la);
187 static MALLOC_DEFINE(M_SCTPNAT, "sctpnat", "sctp nat dbs");
188 /* Use kernel allocator. */
189 #ifdef _SYS_MALLOC_H_
190 #define sn_malloc(x) malloc(x, M_SCTPNAT, M_NOWAIT|M_ZERO)
191 #define sn_calloc(n,x) mallocarray((n), (x), M_SCTPNAT, M_NOWAIT|M_ZERO)
192 #define sn_free(x) free(x, M_SCTPNAT)
193 #endif// #ifdef _SYS_MALLOC_H_
195 #else //#ifdef _KERNEL
196 #define sn_malloc(x) malloc(x)
197 #define sn_calloc(n, x) calloc(n, x)
198 #define sn_free(x) free(x)
200 #endif //#ifdef _KERNEL
202 /** @defgroup packet_parser SCTP Packet Parsing
205 * - Return pointers to the first and next SCTP chunks within an SCTP Packet
206 * - Define possible return values of the packet parsing process
207 * - SCTP message types for storing in the sctp_nat_msg structure @{
210 #define SN_SCTP_FIRSTCHUNK(sctphead) (struct sctp_chunkhdr *)(((char *)sctphead) + sizeof(struct sctphdr))
211 /**< Returns a pointer to the first chunk in an SCTP packet given a pointer to the SCTP header */
213 #define SN_SCTP_NEXTCHUNK(chunkhead) (struct sctp_chunkhdr *)(((char *)chunkhead) + SCTP_SIZE32(ntohs(chunkhead->chunk_length)))
214 /**< Returns a pointer to the next chunk in an SCTP packet given a pointer to the current chunk */
216 #define SN_SCTP_NEXTPARAM(param) (struct sctp_paramhdr *)(((char *)param) + SCTP_SIZE32(ntohs(param->param_length)))
217 /**< Returns a pointer to the next parameter in an SCTP packet given a pointer to the current parameter */
219 #define SN_MIN_CHUNK_SIZE 4 /**< Smallest possible SCTP chunk size in bytes */
220 #define SN_MIN_PARAM_SIZE 4 /**< Smallest possible SCTP param size in bytes */
221 #define SN_VTAG_PARAM_SIZE 12 /**< Size of SCTP ASCONF vtag param in bytes */
222 #define SN_ASCONFACK_PARAM_SIZE 8 /**< Size of SCTP ASCONF ACK param in bytes */
224 /* Packet parsing return codes */
225 #define SN_PARSE_OK 0 /**< Packet parsed for SCTP messages */
226 #define SN_PARSE_ERROR_IPSHL 1 /**< Packet parsing error - IP and SCTP common header len */
227 #define SN_PARSE_ERROR_AS_MALLOC 2 /**< Packet parsing error - assoc malloc */
228 #define SN_PARSE_ERROR_CHHL 3 /**< Packet parsing error - Chunk header len */
229 #define SN_PARSE_ERROR_DIR 4 /**< Packet parsing error - Direction */
230 #define SN_PARSE_ERROR_VTAG 5 /**< Packet parsing error - Vtag */
231 #define SN_PARSE_ERROR_CHUNK 6 /**< Packet parsing error - Chunk */
232 #define SN_PARSE_ERROR_PORT 7 /**< Packet parsing error - Port=0 */
233 #define SN_PARSE_ERROR_LOOKUP 8 /**< Packet parsing error - Lookup */
234 #define SN_PARSE_ERROR_PARTIALLOOKUP 9 /**< Packet parsing error - partial lookup only found */
235 #define SN_PARSE_ERROR_LOOKUP_ABORT 10 /**< Packet parsing error - Lookup - but abort packet */
237 /* Alias_sctp performs its processing based on a number of key messages */
238 #define SN_SCTP_ABORT 0x0000 /**< a packet containing an ABORT chunk */
239 #define SN_SCTP_INIT 0x0001 /**< a packet containing an INIT chunk */
240 #define SN_SCTP_INITACK 0x0002 /**< a packet containing an INIT-ACK chunk */
241 #define SN_SCTP_SHUTCOMP 0x0010 /**< a packet containing a SHUTDOWN-COMPLETE chunk */
242 #define SN_SCTP_SHUTACK 0x0020 /**< a packet containing a SHUTDOWN-ACK chunk */
243 #define SN_SCTP_ASCONF 0x0100 /**< a packet containing an ASCONF chunk */
244 #define SN_SCTP_ASCONFACK 0x0200 /**< a packet containing an ASCONF-ACK chunk */
245 #define SN_SCTP_OTHER 0xFFFF /**< a packet containing a chunk that is not of interest */
248 * @defgroup state_machine SCTP NAT State Machine
250 * Defines the various states an association can be within the NAT @{
252 #define SN_ID 0x0000 /**< Idle state */
253 #define SN_INi 0x0010 /**< Initialising, waiting for InitAck state */
254 #define SN_INa 0x0020 /**< Initialising, waiting for AddIpAck state */
255 #define SN_UP 0x0100 /**< Association in UP state */
256 #define SN_CL 0x1000 /**< Closing state */
257 #define SN_RM 0x2000 /**< Removing state */
260 * @defgroup Logging Logging Functionality
262 * Define various log levels and a macro to call specified log functions only if
263 * the current log level (sysctl_log_level) matches the specified level @{
266 #define SN_LOG_EVENT 1
267 #define SN_LOG_INFO 2
268 #define SN_LOG_DETAIL 3
269 #define SN_LOG_DEBUG 4
270 #define SN_LOG_DEBUG_MAX 5
272 #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 */
275 * @defgroup Hash Hash Table Macros and Functions
277 * Defines minimum/maximum/default values for the hash table size @{
279 #define SN_MIN_HASH_SIZE 101 /**< Minimum hash table size (set to stop users choosing stupid values) */
280 #define SN_MAX_HASH_SIZE 1000001 /**< Maximum hash table size (NB must be less than max int) */
281 #define SN_DEFAULT_HASH_SIZE 2003 /**< A reasonable default size for the hash tables */
283 #define SN_LOCAL_TBL 0x01 /**< assoc in local table */
284 #define SN_GLOBAL_TBL 0x02 /**< assoc in global table */
285 #define SN_BOTH_TBL 0x03 /**< assoc in both tables */
286 #define SN_WAIT_TOLOCAL 0x10 /**< assoc waiting for TOLOCAL asconf ACK*/
287 #define SN_WAIT_TOGLOBAL 0x20 /**< assoc waiting for TOLOCAL asconf ACK*/
288 #define SN_NULL_TBL 0x00 /**< assoc in No table */
289 #define SN_MAX_GLOBAL_ADDRESSES 100 /**< absolute maximum global address count*/
291 #define SN_ADD_OK 0 /**< Association added to the table */
292 #define SN_ADD_CLASH 1 /**< Clash when trying to add the assoc. info to the table */
294 #define SN_TABLE_HASH(vtag, port, size) (((u_int) vtag + (u_int) port) % (u_int) size) /**< Calculate the hash table lookup position */
297 * @defgroup Timer Timer Queue Macros and Functions
299 * Timer macros set minimum/maximum timeout values and calculate timer expiry
300 * times for the provided libalias instance @{
302 #define SN_MIN_TIMER 1
303 #define SN_MAX_TIMER 600
304 #define SN_TIMER_QUEUE_SIZE SN_MAX_TIMER+2
306 #define SN_I_T(la) (la->timeStamp + sysctl_init_timer) /**< INIT State expiration time in seconds */
307 #define SN_U_T(la) (la->timeStamp + sysctl_up_timer) /**< UP State expiration time in seconds */
308 #define SN_C_T(la) (la->timeStamp + sysctl_shutdown_timer) /**< CL State expiration time in seconds */
309 #define SN_X_T(la) (la->timeStamp + sysctl_holddown_timer) /**< Wait after a shutdown complete in seconds */
312 * @defgroup sysctl SysCtl Variable and callback function declarations
314 * Sysctl variables to modify NAT functionality in real-time along with associated functions
315 * to manage modifications to the sysctl variables @{
319 int sysctl_chg_loglevel(SYSCTL_HANDLER_ARGS);
320 int sysctl_chg_timer(SYSCTL_HANDLER_ARGS);
321 int sysctl_chg_hashtable_size(SYSCTL_HANDLER_ARGS);
322 int sysctl_chg_error_on_ootb(SYSCTL_HANDLER_ARGS);
323 int sysctl_chg_accept_global_ootb_addip(SYSCTL_HANDLER_ARGS);
324 int sysctl_chg_initialising_chunk_proc_limit(SYSCTL_HANDLER_ARGS);
325 int sysctl_chg_chunk_proc_limit(SYSCTL_HANDLER_ARGS);
326 int sysctl_chg_param_proc_limit(SYSCTL_HANDLER_ARGS);
327 int sysctl_chg_track_global_addresses(SYSCTL_HANDLER_ARGS);
329 /* Sysctl variables */
330 /** @brief net.inet.ip.alias.sctp.log_level */
331 static u_int sysctl_log_level = 0; /**< Stores the current level of logging */
332 /** @brief net.inet.ip.alias.sctp.init_timer */
333 static u_int sysctl_init_timer = 15; /**< Seconds to hold an association in the table waiting for an INIT-ACK or AddIP-ACK */
334 /** @brief net.inet.ip.alias.sctp.up_timer */
335 static u_int sysctl_up_timer = 300; /**< Seconds to hold an association in the table while no packets are transmitted */
336 /** @brief net.inet.ip.alias.sctp.shutdown_timer */
337 static u_int sysctl_shutdown_timer = 15; /**< Seconds to hold an association in the table waiting for a SHUTDOWN-COMPLETE */
338 /** @brief net.inet.ip.alias.sctp.holddown_timer */
339 static u_int sysctl_holddown_timer = 0; /**< Seconds to hold an association in the table after it has been shutdown (to allow for lost SHUTDOWN-COMPLETEs) */
340 /** @brief net.inet.ip.alias.sctp.hashtable_size */
341 static u_int sysctl_hashtable_size = SN_DEFAULT_HASH_SIZE; /**< Sets the hash table size for any NEW NAT instances (existing instances retain their existing Hash Table */
342 /** @brief net.inet.ip.alias.sctp.error_on_ootb */
343 static u_int sysctl_error_on_ootb = 1; /**< NAT response to receipt of OOTB packet
344 (0 - No response, 1 - NAT will send ErrorM only to local side,
345 2 - NAT will send local ErrorM and global ErrorM if there was a partial association match
346 3 - NAT will send ErrorM to both local and global) */
347 /** @brief net.inet.ip.alias.sctp.accept_global_ootb_addip */
348 static u_int sysctl_accept_global_ootb_addip = 0; /**<NAT responset to receipt of global OOTB AddIP (0 - No response, 1 - NAT will accept OOTB global AddIP messages for processing (Security risk)) */
349 /** @brief net.inet.ip.alias.sctp.initialising_chunk_proc_limit */
350 static u_int sysctl_initialising_chunk_proc_limit = 2; /**< A limit on the number of chunks that should be searched if there is no matching association (DoS prevention) */
351 /** @brief net.inet.ip.alias.sctp.param_proc_limit */
352 static u_int sysctl_chunk_proc_limit = 5; /**< A limit on the number of chunks that should be searched (DoS prevention) */
353 /** @brief net.inet.ip.alias.sctp.param_proc_limit */
354 static u_int sysctl_param_proc_limit = 25; /**< A limit on the number of parameters (in chunks) that should be searched (DoS prevention) */
355 /** @brief net.inet.ip.alias.sctp.track_global_addresses */
356 static u_int sysctl_track_global_addresses = 0; /**< Configures the global address tracking option within the NAT (0 - Global tracking is disabled, > 0 - enables tracking but limits the number of global IP addresses to this value)
357 If set to >=1 the NAT will track that many global IP addresses. This may reduce look up table conflicts, but increases processing */
359 #define SN_NO_ERROR_ON_OOTB 0 /**< Send no errorM on out of the blue packets */
360 #define SN_LOCAL_ERROR_ON_OOTB 1 /**< Send only local errorM on out of the blue packets */
361 #define SN_LOCALandPARTIAL_ERROR_ON_OOTB 2 /**< Send local errorM and global errorM for out of the blue packets only if partial match found */
362 #define SN_ERROR_ON_OOTB 3 /**< Send errorM on out of the blue packets */
366 SYSCTL_DECL(_net_inet);
367 SYSCTL_DECL(_net_inet_ip);
368 SYSCTL_DECL(_net_inet_ip_alias);
370 static SYSCTL_NODE(_net_inet_ip_alias, OID_AUTO, sctp, CTLFLAG_RW, NULL,
372 SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, log_level, CTLTYPE_UINT | CTLFLAG_RW,
373 &sysctl_log_level, 0, sysctl_chg_loglevel, "IU",
374 "Level of detail (0 - default, 1 - event, 2 - info, 3 - detail, 4 - debug, 5 - max debug)");
375 SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, init_timer, CTLTYPE_UINT | CTLFLAG_RW,
376 &sysctl_init_timer, 0, sysctl_chg_timer, "IU",
377 "Timeout value (s) while waiting for (INIT-ACK|AddIP-ACK)");
378 SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, up_timer, CTLTYPE_UINT | CTLFLAG_RW,
379 &sysctl_up_timer, 0, sysctl_chg_timer, "IU",
380 "Timeout value (s) to keep an association up with no traffic");
381 SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, shutdown_timer, CTLTYPE_UINT | CTLFLAG_RW,
382 &sysctl_shutdown_timer, 0, sysctl_chg_timer, "IU",
383 "Timeout value (s) while waiting for SHUTDOWN-COMPLETE");
384 SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, holddown_timer, CTLTYPE_UINT | CTLFLAG_RW,
385 &sysctl_holddown_timer, 0, sysctl_chg_timer, "IU",
386 "Hold association in table for this many seconds after receiving a SHUTDOWN-COMPLETE");
387 SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, hashtable_size, CTLTYPE_UINT | CTLFLAG_RW,
388 &sysctl_hashtable_size, 0, sysctl_chg_hashtable_size, "IU",
389 "Size of hash tables used for NAT lookups (100 < prime_number > 1000001)");
390 SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, error_on_ootb, CTLTYPE_UINT | CTLFLAG_RW,
391 &sysctl_error_on_ootb, 0, sysctl_chg_error_on_ootb, "IU",
392 "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)");
393 SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, accept_global_ootb_addip, CTLTYPE_UINT | CTLFLAG_RW,
394 &sysctl_accept_global_ootb_addip, 0, sysctl_chg_accept_global_ootb_addip, "IU",
395 "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)");
396 SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, initialising_chunk_proc_limit, CTLTYPE_UINT | CTLFLAG_RW,
397 &sysctl_initialising_chunk_proc_limit, 0, sysctl_chg_initialising_chunk_proc_limit, "IU",
398 "Number of chunks that should be processed if there is no current association found:\n\t > 0 (A high value is a DoS risk)");
399 SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, chunk_proc_limit, CTLTYPE_UINT | CTLFLAG_RW,
400 &sysctl_chunk_proc_limit, 0, sysctl_chg_chunk_proc_limit, "IU",
401 "Number of chunks that should be processed to find key chunk:\n\t>= initialising_chunk_proc_limit (A high value is a DoS risk)");
402 SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, param_proc_limit, CTLTYPE_UINT | CTLFLAG_RW,
403 &sysctl_param_proc_limit, 0, sysctl_chg_param_proc_limit, "IU",
404 "Number of parameters (in a chunk) that should be processed to find key parameters:\n\t> 1 (A high value is a DoS risk)");
405 SYSCTL_PROC(_net_inet_ip_alias_sctp, OID_AUTO, track_global_addresses, CTLTYPE_UINT | CTLFLAG_RW,
406 &sysctl_track_global_addresses, 0, sysctl_chg_track_global_addresses, "IU",
407 "Configures the global address tracking option within the NAT:\n\t0 - Global tracking is disabled,\n\t> 0 - enables tracking but limits the number of global IP addresses to this value");
409 #endif /* SYSCTL_NODE */
413 * @brief sysctl callback for changing net.inet.ip.fw.sctp.log_level
415 * Updates the variable sysctl_log_level to the provided value and ensures
416 * it is in the valid range (SN_LOG_LOW -> SN_LOG_DEBUG)
418 int sysctl_chg_loglevel(SYSCTL_HANDLER_ARGS)
420 u_int level = *(u_int *)arg1;
423 error = sysctl_handle_int(oidp, &level, 0, req);
424 if (error) return (error);
426 level = (level > SN_LOG_DEBUG_MAX) ? (SN_LOG_DEBUG_MAX) : (level);
427 level = (level < SN_LOG_LOW) ? (SN_LOG_LOW) : (level);
428 sysctl_log_level = level;
433 * @brief sysctl callback for changing net.inet.ip.fw.sctp.(init_timer|up_timer|shutdown_timer)
435 * Updates the timer-based sysctl variables. The new values are sanity-checked
436 * to make sure that they are within the range SN_MIN_TIMER-SN_MAX_TIMER. The
437 * holddown timer is allowed to be 0
439 int sysctl_chg_timer(SYSCTL_HANDLER_ARGS)
441 u_int timer = *(u_int *)arg1;
444 error = sysctl_handle_int(oidp, &timer, 0, req);
445 if (error) return (error);
447 timer = (timer > SN_MAX_TIMER) ? (SN_MAX_TIMER) : (timer);
449 if (((u_int *)arg1) != &sysctl_holddown_timer) {
450 timer = (timer < SN_MIN_TIMER) ? (SN_MIN_TIMER) : (timer);
453 *(u_int *)arg1 = timer;
459 * @brief sysctl callback for changing net.inet.ip.alias.sctp.hashtable_size
461 * Updates the hashtable_size sysctl variable. The new value should be a prime
462 * number. We sanity check to ensure that the size is within the range
463 * SN_MIN_HASH_SIZE-SN_MAX_HASH_SIZE. We then check the provided number to see
464 * if it is prime. We approximate by checking that (2,3,5,7,11) are not factors,
465 * incrementing the user provided value until we find a suitable number.
467 int sysctl_chg_hashtable_size(SYSCTL_HANDLER_ARGS)
469 u_int size = *(u_int *)arg1;
472 error = sysctl_handle_int(oidp, &size, 0, req);
473 if (error) return (error);
475 size = (size < SN_MIN_HASH_SIZE) ? (SN_MIN_HASH_SIZE) : ((size > SN_MAX_HASH_SIZE) ? (SN_MAX_HASH_SIZE) : (size));
477 size |= 0x00000001; /* make odd */
479 for (;(((size % 3) == 0) || ((size % 5) == 0) || ((size % 7) == 0) || ((size % 11) == 0)); size+=2);
480 sysctl_hashtable_size = size;
486 * @brief sysctl callback for changing net.inet.ip.alias.sctp.error_on_ootb
488 * Updates the error_on_clash sysctl variable.
489 * If set to 0, no ErrorM will be sent if there is a look up table clash
490 * If set to 1, an ErrorM is sent only to the local side
491 * If set to 2, an ErrorM is sent to the local side and global side if there is
492 * a partial association match
493 * If set to 3, an ErrorM is sent to both local and global sides (DoS) risk.
495 int sysctl_chg_error_on_ootb(SYSCTL_HANDLER_ARGS)
497 u_int flag = *(u_int *)arg1;
500 error = sysctl_handle_int(oidp, &flag, 0, req);
501 if (error) return (error);
503 sysctl_error_on_ootb = (flag > SN_ERROR_ON_OOTB) ? SN_ERROR_ON_OOTB: flag;
509 * @brief sysctl callback for changing net.inet.ip.alias.sctp.accept_global_ootb_addip
511 * If set to 1 the NAT will accept ootb global addip messages for processing (Security risk)
512 * Default is 0, only responding to local ootb AddIP messages
514 int sysctl_chg_accept_global_ootb_addip(SYSCTL_HANDLER_ARGS)
516 u_int flag = *(u_int *)arg1;
519 error = sysctl_handle_int(oidp, &flag, 0, req);
520 if (error) return (error);
522 sysctl_accept_global_ootb_addip = (flag == 1) ? 1: 0;
528 * @brief sysctl callback for changing net.inet.ip.alias.sctp.initialising_chunk_proc_limit
530 * Updates the initialising_chunk_proc_limit sysctl variable. Number of chunks
531 * that should be processed if there is no current association found: > 0 (A
532 * high value is a DoS risk)
534 int sysctl_chg_initialising_chunk_proc_limit(SYSCTL_HANDLER_ARGS)
536 u_int proclimit = *(u_int *)arg1;
539 error = sysctl_handle_int(oidp, &proclimit, 0, req);
540 if (error) return (error);
542 sysctl_initialising_chunk_proc_limit = (proclimit < 1) ? 1: proclimit;
543 sysctl_chunk_proc_limit =
544 (sysctl_chunk_proc_limit < sysctl_initialising_chunk_proc_limit) ? sysctl_initialising_chunk_proc_limit : sysctl_chunk_proc_limit;
550 * @brief sysctl callback for changing net.inet.ip.alias.sctp.chunk_proc_limit
552 * Updates the chunk_proc_limit sysctl variable.
553 * Number of chunks that should be processed to find key chunk:
554 * >= initialising_chunk_proc_limit (A high value is a DoS risk)
556 int sysctl_chg_chunk_proc_limit(SYSCTL_HANDLER_ARGS)
558 u_int proclimit = *(u_int *)arg1;
561 error = sysctl_handle_int(oidp, &proclimit, 0, req);
562 if (error) return (error);
564 sysctl_chunk_proc_limit =
565 (proclimit < sysctl_initialising_chunk_proc_limit) ? sysctl_initialising_chunk_proc_limit : proclimit;
572 * @brief sysctl callback for changing net.inet.ip.alias.sctp.param_proc_limit
574 * Updates the param_proc_limit sysctl variable.
575 * Number of parameters that should be processed to find key parameters:
576 * > 1 (A high value is a DoS risk)
578 int sysctl_chg_param_proc_limit(SYSCTL_HANDLER_ARGS)
580 u_int proclimit = *(u_int *)arg1;
583 error = sysctl_handle_int(oidp, &proclimit, 0, req);
584 if (error) return (error);
586 sysctl_param_proc_limit =
587 (proclimit < 2) ? 2 : proclimit;
593 * @brief sysctl callback for changing net.inet.ip.alias.sctp.track_global_addresses
595 *Configures the global address tracking option within the NAT (0 - Global
596 *tracking is disabled, > 0 - enables tracking but limits the number of global
597 *IP addresses to this value)
599 int sysctl_chg_track_global_addresses(SYSCTL_HANDLER_ARGS)
601 u_int num_to_track = *(u_int *)arg1;
604 error = sysctl_handle_int(oidp, &num_to_track, 0, req);
605 if (error) return (error);
607 sysctl_track_global_addresses = (num_to_track > SN_MAX_GLOBAL_ADDRESSES) ? SN_MAX_GLOBAL_ADDRESSES : num_to_track;
613 /* ----------------------------------------------------------------------
615 * ----------------------------------------------------------------------
618 * @brief Initialises the SCTP NAT Implementation
620 * Creates the look-up tables and the timer queue and initialises all state
623 * @param la Pointer to the relevant libalias instance
625 void AliasSctpInit(struct libalias *la)
627 /* Initialise association tables*/
629 la->sctpNatTableSize = sysctl_hashtable_size;
631 SctpAliasLog("Initialising SCTP NAT Instance (hash_table_size:%d)\n", la->sctpNatTableSize));
632 la->sctpTableLocal = sn_calloc(la->sctpNatTableSize, sizeof(struct sctpNatTableL));
633 la->sctpTableGlobal = sn_calloc(la->sctpNatTableSize, sizeof(struct sctpNatTableG));
634 la->sctpNatTimer.TimerQ = sn_calloc(SN_TIMER_QUEUE_SIZE, sizeof(struct sctpTimerQ));
635 /* Initialise hash table */
636 for (i = 0; i < la->sctpNatTableSize; i++) {
637 LIST_INIT(&la->sctpTableLocal[i]);
638 LIST_INIT(&la->sctpTableGlobal[i]);
641 /* Initialise circular timer Q*/
642 for (i = 0; i < SN_TIMER_QUEUE_SIZE; i++)
643 LIST_INIT(&la->sctpNatTimer.TimerQ[i]);
645 la->sctpNatTimer.loc_time=time_uptime; /* la->timeStamp is not set yet */
647 la->sctpNatTimer.loc_time=la->timeStamp;
649 la->sctpNatTimer.cur_loc = 0;
650 la->sctpLinkCount = 0;
654 * @brief Cleans-up the SCTP NAT Implementation prior to unloading
656 * Removes all entries from the timer queue, freeing associations as it goes.
657 * We then free memory allocated to the look-up tables and the time queue
659 * NOTE: We do not need to traverse the look-up tables as each association
660 * will always have an entry in the timer queue, freeing this memory
661 * once will free all memory allocated to entries in the look-up tables
663 * @param la Pointer to the relevant libalias instance
665 void AliasSctpTerm(struct libalias *la)
667 struct sctp_nat_assoc *assoc1, *assoc2;
670 LIBALIAS_LOCK_ASSERT(la);
672 SctpAliasLog("Removing SCTP NAT Instance\n"));
673 for (i = 0; i < SN_TIMER_QUEUE_SIZE; i++) {
674 assoc1 = LIST_FIRST(&la->sctpNatTimer.TimerQ[i]);
675 while (assoc1 != NULL) {
676 freeGlobalAddressList(assoc1);
677 assoc2 = LIST_NEXT(assoc1, timer_Q);
683 sn_free(la->sctpTableLocal);
684 sn_free(la->sctpTableGlobal);
685 sn_free(la->sctpNatTimer.TimerQ);
689 * @brief Handles SCTP packets passed from libalias
691 * This function needs to actually NAT/drop packets and possibly create and
692 * send AbortM or ErrorM packets in response. The process involves:
693 * - Validating the direction parameter passed by the caller
694 * - Checking and handling any expired timers for the NAT
695 * - Calling sctp_PktParser() to parse the packet
696 * - Call ProcessSctpMsg() to decide the appropriate outcome and to update
698 * - Based on the return code either:
700 * - Construct and send an ErrorM|AbortM packet
701 * - Mark the association for removal from the tables
702 * - Potentially remove the association from all lookup tables
703 * - Return the appropriate result to libalias
705 * @param la Pointer to the relevant libalias instance
706 * @param pip Pointer to IP packet to process
707 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
709 * @return PKT_ALIAS_OK | PKT_ALIAS_IGNORE | PKT_ALIAS_ERROR
712 SctpAlias(struct libalias *la, struct ip *pip, int direction)
715 struct sctp_nat_msg msg;
716 struct sctp_nat_assoc *assoc = NULL;
718 if ((direction != SN_TO_LOCAL) && (direction != SN_TO_GLOBAL)) {
719 SctpAliasLog("ERROR: Invalid direction\n");
720 return (PKT_ALIAS_ERROR);
723 sctp_CheckTimers(la); /* Check timers */
725 /* Parse the packet */
726 rtnval = sctp_PktParser(la, direction, pip, &msg, &assoc); //using *char (change to mbuf when get code from paolo)
730 case SN_PARSE_ERROR_CHHL:
731 /* Not an error if there is a chunk length parsing error and this is a fragmented packet */
732 if (ntohs(pip->ip_off) & IP_MF) {
733 rtnval = SN_PARSE_OK;
737 logsctperror("SN_PARSE_ERROR", msg.sctp_hdr->v_tag, rtnval, direction));
738 return (PKT_ALIAS_ERROR);
739 case SN_PARSE_ERROR_PARTIALLOOKUP:
740 if (sysctl_error_on_ootb > SN_LOCALandPARTIAL_ERROR_ON_OOTB) {
742 logsctperror("SN_PARSE_ERROR", msg.sctp_hdr->v_tag, rtnval, direction));
743 return (PKT_ALIAS_ERROR);
745 case SN_PARSE_ERROR_LOOKUP:
746 if (sysctl_error_on_ootb == SN_ERROR_ON_OOTB ||
747 (sysctl_error_on_ootb == SN_LOCALandPARTIAL_ERROR_ON_OOTB && direction == SN_TO_LOCAL) ||
748 (sysctl_error_on_ootb == SN_LOCAL_ERROR_ON_OOTB && direction == SN_TO_GLOBAL)) {
749 TxAbortErrorM(la, &msg, assoc, SN_REFLECT_ERROR, direction); /*NB assoc=NULL */
750 return (PKT_ALIAS_RESPOND);
754 logsctperror("SN_PARSE_ERROR", msg.sctp_hdr->v_tag, rtnval, direction));
755 return (PKT_ALIAS_ERROR);
758 SN_LOG(SN_LOG_DETAIL,
759 logsctpassoc(assoc, "*");
760 logsctpparse(direction, &msg);
763 /* Process the SCTP message */
764 rtnval = ProcessSctpMsg(la, direction, &msg, assoc);
766 SN_LOG(SN_LOG_DEBUG_MAX,
767 logsctpassoc(assoc, "-");
771 SN_LOG(SN_LOG_DEBUG, logTimerQ(la));
777 DifferentialChecksum(&(msg.ip_hdr->ip_sum),
778 &(assoc->l_addr), &(msg.ip_hdr->ip_dst), 2);
779 msg.ip_hdr->ip_dst = assoc->l_addr; /* change dst address to local address*/
782 DifferentialChecksum(&(msg.ip_hdr->ip_sum),
783 &(assoc->a_addr), &(msg.ip_hdr->ip_src), 2);
784 msg.ip_hdr->ip_src = assoc->a_addr; /* change src to alias addr*/
787 rtnval = SN_DROP_PKT; /* shouldn't get here, but if it does drop packet */
788 SN_LOG(SN_LOG_LOW, logsctperror("ERROR: Invalid direction", msg.sctp_hdr->v_tag, rtnval, direction));
793 SN_LOG(SN_LOG_DETAIL, logsctperror("SN_DROP_PKT", msg.sctp_hdr->v_tag, rtnval, direction));
798 TxAbortErrorM(la, &msg, assoc, rtnval, direction);
801 // big error, remove association and go to idle and write log messages
802 SN_LOG(SN_LOG_LOW, logsctperror("SN_PROCESSING_ERROR", msg.sctp_hdr->v_tag, rtnval, direction));
803 assoc->state=SN_RM;/* Mark for removal*/
807 /* Remove association if tagged for removal */
808 if (assoc->state == SN_RM) {
809 if (assoc->TableRegister) {
810 sctp_RmTimeOut(la, assoc);
811 RmSctpAssoc(la, assoc);
813 LIBALIAS_LOCK_ASSERT(la);
814 freeGlobalAddressList(assoc);
819 return (PKT_ALIAS_OK);
821 return (PKT_ALIAS_OK);
824 case SN_REFLECT_ERROR:
825 return (PKT_ALIAS_RESPOND);
828 return (PKT_ALIAS_ERROR);
833 * @brief Send an AbortM or ErrorM
835 * We construct the new SCTP packet to send in place of the existing packet we
836 * have been asked to NAT. This function can only be called if the original
837 * packet was successfully parsed as a valid SCTP packet.
839 * An AbortM (without cause) packet is the smallest SCTP packet available and as
840 * such there is always space in the existing packet buffer to fit the AbortM
841 * packet. An ErrorM packet is 4 bytes longer than the (the error cause is not
842 * optional). An ErrorM is sent in response to an AddIP when the Vtag/address
843 * combination, if added, will produce a conflict in the association look up
844 * tables. It may also be used for an unexpected packet - a packet with no
845 * matching association in the NAT table and we are requesting an AddIP so we
846 * can add it. The smallest valid SCTP packet while the association is in an
847 * up-state is a Heartbeat packet, which is big enough to be transformed to an
850 * We create a temporary character array to store the packet as we are constructing
851 * it. We then populate the array with appropriate values based on:
852 * - Packet type (AbortM | ErrorM)
853 * - Initial packet direction (SN_TO_LOCAL | SN_TO_GLOBAL)
854 * - NAT response (Send packet | Reply packet)
856 * Once complete, we copy the contents of the temporary packet over the original
857 * SCTP packet we were asked to NAT
859 * @param la Pointer to the relevant libalias instance
860 * @param sm Pointer to sctp message information
861 * @param assoc Pointer to current association details
862 * @param sndrply SN_SEND_ABORT | SN_REPLY_ABORT | SN_REPLY_ERROR
863 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
866 local_sctp_finalize_crc32(uint32_t crc32c)
868 /* This routine is duplicated from SCTP
869 * we need to do that since it MAY be that SCTP
870 * is NOT compiled into the kernel. The CRC32C routines
871 * however are always available in libkern.
874 #if BYTE_ORDER == BIG_ENDIAN
875 uint8_t byte0, byte1, byte2, byte3;
878 /* Complement the result */
880 #if BYTE_ORDER == BIG_ENDIAN
882 * For BIG-ENDIAN.. aka Motorola byte order the result is in
883 * little-endian form. So we must manually swap the bytes. Then we
884 * can call htonl() which does nothing...
886 byte0 = result & 0x000000ff;
887 byte1 = (result >> 8) & 0x000000ff;
888 byte2 = (result >> 16) & 0x000000ff;
889 byte3 = (result >> 24) & 0x000000ff;
890 crc32c = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3);
893 * For INTEL platforms the result comes out in network order. No
894 * htonl is required or the swap above. So we optimize out both the
895 * htonl and the manual swap above.
903 TxAbortErrorM(struct libalias *la, struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int sndrply, int direction)
905 int sctp_size = sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_error_cause);
906 int ip_size = sizeof(struct ip) + sctp_size;
907 int include_error_cause = 1;
908 char tmp_ip[ip_size];
909 char addrbuf[INET_ADDRSTRLEN];
911 if (ntohs(sm->ip_hdr->ip_len) < ip_size) { /* short packet, cannot send error cause */
912 include_error_cause = 0;
913 ip_size = ip_size - sizeof(struct sctp_error_cause);
914 sctp_size = sctp_size - sizeof(struct sctp_error_cause);
916 /* Assign header pointers packet */
917 struct ip* ip = (struct ip *) tmp_ip;
918 struct sctphdr* sctp_hdr = (struct sctphdr *) ((char *) ip + sizeof(*ip));
919 struct sctp_chunkhdr* chunk_hdr = (struct sctp_chunkhdr *) ((char *) sctp_hdr + sizeof(*sctp_hdr));
920 struct sctp_error_cause* error_cause = (struct sctp_error_cause *) ((char *) chunk_hdr + sizeof(*chunk_hdr));
922 /* construct ip header */
923 ip->ip_v = sm->ip_hdr->ip_v;
924 ip->ip_hl = 5; /* 5*32 bit words */
926 ip->ip_len = htons(ip_size);
927 ip->ip_id = sm->ip_hdr->ip_id;
930 ip->ip_p = IPPROTO_SCTP;
932 The definitions below should be removed when they make it into the SCTP stack
934 #define SCTP_MIDDLEBOX_FLAG 0x02
935 #define SCTP_NAT_TABLE_COLLISION 0x00b0
936 #define SCTP_MISSING_NAT 0x00b1
937 chunk_hdr->chunk_type = (sndrply & SN_TX_ABORT) ? SCTP_ABORT_ASSOCIATION : SCTP_OPERATION_ERROR;
938 chunk_hdr->chunk_flags = SCTP_MIDDLEBOX_FLAG;
939 if (include_error_cause) {
940 error_cause->code = htons((sndrply & SN_REFLECT_ERROR) ? SCTP_MISSING_NAT : SCTP_NAT_TABLE_COLLISION);
941 error_cause->length = htons(sizeof(struct sctp_error_cause));
942 chunk_hdr->chunk_length = htons(sizeof(*chunk_hdr) + sizeof(struct sctp_error_cause));
944 chunk_hdr->chunk_length = htons(sizeof(*chunk_hdr));
947 /* set specific values */
949 case SN_REFLECT_ERROR:
950 chunk_hdr->chunk_flags |= SCTP_HAD_NO_TCB; /* set Tbit */
951 sctp_hdr->v_tag = sm->sctp_hdr->v_tag;
954 sctp_hdr->v_tag = (direction == SN_TO_LOCAL) ? assoc->g_vtag : assoc->l_vtag ;
957 sctp_hdr->v_tag = sm->sctp_hdr->v_tag;
960 sctp_hdr->v_tag = sm->sctpchnk.Init->initiate_tag;
964 /* Set send/reply values */
965 if (sndrply == SN_SEND_ABORT) { /*pass through NAT */
966 ip->ip_src = (direction == SN_TO_LOCAL) ? sm->ip_hdr->ip_src : assoc->a_addr;
967 ip->ip_dst = (direction == SN_TO_LOCAL) ? assoc->l_addr : sm->ip_hdr->ip_dst;
968 sctp_hdr->src_port = sm->sctp_hdr->src_port;
969 sctp_hdr->dest_port = sm->sctp_hdr->dest_port;
970 } else { /* reply and reflect */
971 ip->ip_src = sm->ip_hdr->ip_dst;
972 ip->ip_dst = sm->ip_hdr->ip_src;
973 sctp_hdr->src_port = sm->sctp_hdr->dest_port;
974 sctp_hdr->dest_port = sm->sctp_hdr->src_port;
977 /* Calculate IP header checksum */
978 ip->ip_sum = in_cksum_hdr(ip);
980 /* calculate SCTP header CRC32 */
981 sctp_hdr->checksum = 0;
982 sctp_hdr->checksum = local_sctp_finalize_crc32(calculate_crc32c(0xffffffff, (unsigned char *) sctp_hdr, sctp_size));
984 memcpy(sm->ip_hdr, ip, ip_size);
986 SN_LOG(SN_LOG_EVENT,SctpAliasLog("%s %s 0x%x (->%s:%u vtag=0x%x crc=0x%x)\n",
987 ((sndrply == SN_SEND_ABORT) ? "Sending" : "Replying"),
988 ((sndrply & SN_TX_ERROR) ? "ErrorM" : "AbortM"),
989 (include_error_cause ? ntohs(error_cause->code) : 0),
990 inet_ntoa_r(ip->ip_dst, INET_NTOA_BUF(addrbuf)),
991 ntohs(sctp_hdr->dest_port),
992 ntohl(sctp_hdr->v_tag), ntohl(sctp_hdr->checksum)));
995 /* ----------------------------------------------------------------------
997 * ----------------------------------------------------------------------
999 /** @addtogroup packet_parser
1001 * These functions parse the SCTP packet and fill a sctp_nat_msg structure
1002 * with the parsed contents.
1004 /** @ingroup packet_parser
1005 * @brief Parses SCTP packets for the key SCTP chunk that will be processed
1007 * This module parses SCTP packets for the key SCTP chunk that will be processed
1008 * The module completes the sctp_nat_msg structure and either retrieves the
1009 * relevant (existing) stored association from the Hash Tables or creates a new
1010 * association entity with state SN_ID
1012 * @param la Pointer to the relevant libalias instance
1013 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1015 * @param sm Pointer to sctp message information
1016 * @param passoc Pointer to the association this SCTP Message belongs to
1018 * @return SN_PARSE_OK | SN_PARSE_ERROR_*
1021 sctp_PktParser(struct libalias *la, int direction, struct ip *pip,
1022 struct sctp_nat_msg *sm, struct sctp_nat_assoc **passoc)
1023 //sctp_PktParser(int direction, struct mbuf *ipak, int ip_hdr_len,struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc)
1025 struct sctphdr *sctp_hdr;
1026 struct sctp_chunkhdr *chunk_hdr;
1027 struct sctp_paramhdr *param_hdr;
1028 struct in_addr ipv4addr;
1029 int bytes_left; /* bytes left in ip packet */
1032 int partial_match = 0;
1036 // mlen = SCTP_HEADER_LEN(i_pak);
1037 // mp = SCTP_HEADER_TO_CHAIN(i_pak); /* does nothing in bsd since header and chain not separate */
1040 * Note, that if the VTag is zero, it must be an INIT
1041 * Also, I am only interested in the content of INIT and ADDIP chunks
1044 // no mbuf stuff from Paolo yet so ...
1046 /* remove ip header length from the bytes_left */
1047 bytes_left = ntohs(pip->ip_len) - (pip->ip_hl << 2);
1049 /* Check SCTP header length and move to first chunk */
1050 if (bytes_left < sizeof(struct sctphdr)) {
1051 sm->sctp_hdr = NULL;
1052 return (SN_PARSE_ERROR_IPSHL); /* packet not long enough*/
1055 sm->sctp_hdr = sctp_hdr = (struct sctphdr *) ip_next(pip);
1056 bytes_left -= sizeof(struct sctphdr);
1058 /* Check for valid ports (zero valued ports would find partially initialised associations */
1059 if (sctp_hdr->src_port == 0 || sctp_hdr->dest_port == 0)
1060 return (SN_PARSE_ERROR_PORT);
1062 /* Check length of first chunk */
1063 if (bytes_left < SN_MIN_CHUNK_SIZE) /* malformed chunk - could cause endless loop*/
1064 return (SN_PARSE_ERROR_CHHL); /* packet not long enough for this chunk */
1067 chunk_hdr = SN_SCTP_FIRSTCHUNK(sctp_hdr);
1069 chunk_length = SCTP_SIZE32(ntohs(chunk_hdr->chunk_length));
1070 if ((chunk_length < SN_MIN_CHUNK_SIZE) || (chunk_length > bytes_left)) /* malformed chunk - could cause endless loop*/
1071 return (SN_PARSE_ERROR_CHHL);
1073 if ((chunk_hdr->chunk_flags & SCTP_HAD_NO_TCB) &&
1074 ((chunk_hdr->chunk_type == SCTP_ABORT_ASSOCIATION) ||
1075 (chunk_hdr->chunk_type == SCTP_SHUTDOWN_COMPLETE))) {
1077 if (direction == SN_TO_LOCAL)
1078 *passoc = FindSctpGlobalT(la, pip->ip_src, sctp_hdr->v_tag, sctp_hdr->dest_port, sctp_hdr->src_port);
1080 *passoc = FindSctpLocalT(la, pip->ip_dst, sctp_hdr->v_tag, sctp_hdr->dest_port, sctp_hdr->src_port);
1082 /* Proper v_tag settings */
1083 if (direction == SN_TO_LOCAL)
1084 *passoc = FindSctpGlobal(la, pip->ip_src, sctp_hdr->v_tag, sctp_hdr->src_port, sctp_hdr->dest_port, &partial_match);
1086 *passoc = FindSctpLocal(la, pip->ip_src, pip->ip_dst, sctp_hdr->v_tag, sctp_hdr->src_port, sctp_hdr->dest_port);
1090 /* Real packet parsing occurs below */
1091 sm->msg = SN_SCTP_OTHER;/* Initialise to largest value*/
1092 sm->chunk_length = 0; /* only care about length for key chunks */
1093 while (IS_SCTP_CONTROL(chunk_hdr)) {
1094 switch (chunk_hdr->chunk_type) {
1095 case SCTP_INITIATION:
1096 if (chunk_length < sizeof(struct sctp_init_chunk)) /* malformed chunk*/
1097 return (SN_PARSE_ERROR_CHHL);
1098 sm->msg = SN_SCTP_INIT;
1099 sm->sctpchnk.Init = (struct sctp_init *) ((char *) chunk_hdr + sizeof(struct sctp_chunkhdr));
1100 sm->chunk_length = chunk_length;
1101 /* if no existing association, create a new one */
1102 if (*passoc == NULL) {
1103 if (sctp_hdr->v_tag == 0) { //Init requires vtag=0
1104 *passoc = (struct sctp_nat_assoc *) sn_malloc(sizeof(struct sctp_nat_assoc));
1105 if (*passoc == NULL) {/* out of resources */
1106 return (SN_PARSE_ERROR_AS_MALLOC);
1108 /* Initialize association - sn_malloc initializes memory to zeros */
1109 (*passoc)->state = SN_ID;
1110 LIST_INIT(&((*passoc)->Gaddr)); /* always initialise to avoid memory problems */
1111 (*passoc)->TableRegister = SN_NULL_TBL;
1112 return (SN_PARSE_OK);
1114 return (SN_PARSE_ERROR_VTAG);
1116 return (SN_PARSE_ERROR_LOOKUP);
1117 case SCTP_INITIATION_ACK:
1118 if (chunk_length < sizeof(struct sctp_init_ack_chunk)) /* malformed chunk*/
1119 return (SN_PARSE_ERROR_CHHL);
1120 sm->msg = SN_SCTP_INITACK;
1121 sm->sctpchnk.InitAck = (struct sctp_init_ack *) ((char *) chunk_hdr + sizeof(struct sctp_chunkhdr));
1122 sm->chunk_length = chunk_length;
1123 return ((*passoc == NULL) ? (SN_PARSE_ERROR_LOOKUP) : (SN_PARSE_OK));
1124 case SCTP_ABORT_ASSOCIATION: /* access only minimum sized chunk */
1125 sm->msg = SN_SCTP_ABORT;
1126 sm->chunk_length = chunk_length;
1127 return ((*passoc == NULL) ? (SN_PARSE_ERROR_LOOKUP_ABORT) : (SN_PARSE_OK));
1128 case SCTP_SHUTDOWN_ACK:
1129 if (chunk_length < sizeof(struct sctp_shutdown_ack_chunk)) /* malformed chunk*/
1130 return (SN_PARSE_ERROR_CHHL);
1131 if (sm->msg > SN_SCTP_SHUTACK) {
1132 sm->msg = SN_SCTP_SHUTACK;
1133 sm->chunk_length = chunk_length;
1136 case SCTP_SHUTDOWN_COMPLETE: /* minimum sized chunk */
1137 if (sm->msg > SN_SCTP_SHUTCOMP) {
1138 sm->msg = SN_SCTP_SHUTCOMP;
1139 sm->chunk_length = chunk_length;
1141 return ((*passoc == NULL) ? (SN_PARSE_ERROR_LOOKUP) : (SN_PARSE_OK));
1143 if (sm->msg > SN_SCTP_ASCONF) {
1144 if (chunk_length < (sizeof(struct sctp_asconf_chunk) + sizeof(struct sctp_ipv4addr_param))) /* malformed chunk*/
1145 return (SN_PARSE_ERROR_CHHL);
1146 //leave parameter searching to later, if required
1147 param_hdr = (struct sctp_paramhdr *) ((char *) chunk_hdr + sizeof(struct sctp_asconf_chunk)); /*compulsory IP parameter*/
1148 if (ntohs(param_hdr->param_type) == SCTP_IPV4_ADDRESS) {
1149 if ((*passoc == NULL) && (direction == SN_TO_LOCAL)) { /* AddIP with no association */
1150 /* try look up with the ASCONF packet's alternative address */
1151 ipv4addr.s_addr = ((struct sctp_ipv4addr_param *) param_hdr)->addr;
1152 *passoc = FindSctpGlobal(la, ipv4addr, sctp_hdr->v_tag, sctp_hdr->src_port, sctp_hdr->dest_port, &partial_match);
1154 param_hdr = (struct sctp_paramhdr *)
1155 ((char *) param_hdr + sizeof(struct sctp_ipv4addr_param)); /*asconf's compulsory address parameter */
1156 sm->chunk_length = chunk_length - sizeof(struct sctp_asconf_chunk) - sizeof(struct sctp_ipv4addr_param); /* rest of chunk */
1158 if (chunk_length < (sizeof(struct sctp_asconf_chunk) + sizeof(struct sctp_ipv6addr_param))) /* malformed chunk*/
1159 return (SN_PARSE_ERROR_CHHL);
1160 param_hdr = (struct sctp_paramhdr *)
1161 ((char *) param_hdr + sizeof(struct sctp_ipv6addr_param)); /*asconf's compulsory address parameter */
1162 sm->chunk_length = chunk_length - sizeof(struct sctp_asconf_chunk) - sizeof(struct sctp_ipv6addr_param); /* rest of chunk */
1164 sm->msg = SN_SCTP_ASCONF;
1165 sm->sctpchnk.Asconf = param_hdr;
1167 if (*passoc == NULL) { /* AddIP with no association */
1168 *passoc = (struct sctp_nat_assoc *) sn_malloc(sizeof(struct sctp_nat_assoc));
1169 if (*passoc == NULL) {/* out of resources */
1170 return (SN_PARSE_ERROR_AS_MALLOC);
1172 /* Initialize association - sn_malloc initializes memory to zeros */
1173 (*passoc)->state = SN_ID;
1174 LIST_INIT(&((*passoc)->Gaddr)); /* always initialise to avoid memory problems */
1175 (*passoc)->TableRegister = SN_NULL_TBL;
1176 return (SN_PARSE_OK);
1180 case SCTP_ASCONF_ACK:
1181 if (sm->msg > SN_SCTP_ASCONFACK) {
1182 if (chunk_length < sizeof(struct sctp_asconf_ack_chunk)) /* malformed chunk*/
1183 return (SN_PARSE_ERROR_CHHL);
1184 //leave parameter searching to later, if required
1185 param_hdr = (struct sctp_paramhdr *) ((char *) chunk_hdr
1186 + sizeof(struct sctp_asconf_ack_chunk));
1187 sm->msg = SN_SCTP_ASCONFACK;
1188 sm->sctpchnk.Asconf = param_hdr;
1189 sm->chunk_length = chunk_length - sizeof(struct sctp_asconf_ack_chunk);
1193 break; /* do nothing*/
1196 /* if no association is found exit - we need to find an Init or AddIP within sysctl_initialising_chunk_proc_limit */
1197 if ((*passoc == NULL) && (chunk_count >= sysctl_initialising_chunk_proc_limit))
1198 return (SN_PARSE_ERROR_LOOKUP);
1200 /* finished with this chunk, on to the next chunk*/
1201 bytes_left-= chunk_length;
1203 /* Is this the end of the packet ? */
1204 if (bytes_left == 0)
1205 return (*passoc == NULL) ? (SN_PARSE_ERROR_LOOKUP) : (SN_PARSE_OK);
1207 /* Are there enough bytes in packet to at least retrieve length of next chunk ? */
1208 if (bytes_left < SN_MIN_CHUNK_SIZE)
1209 return (SN_PARSE_ERROR_CHHL);
1211 chunk_hdr = SN_SCTP_NEXTCHUNK(chunk_hdr);
1213 /* Is the chunk long enough to not cause endless look and are there enough bytes in packet to read the chunk ? */
1214 chunk_length = SCTP_SIZE32(ntohs(chunk_hdr->chunk_length));
1215 if ((chunk_length < SN_MIN_CHUNK_SIZE) || (chunk_length > bytes_left))
1216 return (SN_PARSE_ERROR_CHHL);
1217 if (++chunk_count > sysctl_chunk_proc_limit)
1218 return (SN_PARSE_OK); /* limit for processing chunks, take what we get */
1221 if (*passoc == NULL)
1222 return (partial_match) ? (SN_PARSE_ERROR_PARTIALLOOKUP) : (SN_PARSE_ERROR_LOOKUP);
1224 return (SN_PARSE_OK);
1227 /** @ingroup packet_parser
1228 * @brief Extract Vtags from Asconf Chunk
1230 * GetAsconfVtags scans an Asconf Chunk for the vtags parameter, and then
1231 * extracts the vtags.
1233 * GetAsconfVtags is not called from within sctp_PktParser. It is called only
1234 * from within ID_process when an AddIP has been received.
1236 * @param la Pointer to the relevant libalias instance
1237 * @param sm Pointer to sctp message information
1238 * @param l_vtag Pointer to the local vtag in the association this SCTP Message belongs to
1239 * @param g_vtag Pointer to the local vtag in the association this SCTP Message belongs to
1240 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1242 * @return 1 - success | 0 - fail
1245 GetAsconfVtags(struct libalias *la, struct sctp_nat_msg *sm, uint32_t *l_vtag, uint32_t *g_vtag, int direction)
1247 /* To be removed when information is in the sctp headers */
1248 #define SCTP_VTAG_PARAM 0xC007
1249 struct sctp_vtag_param {
1250 struct sctp_paramhdr ph;/* type=SCTP_VTAG_PARAM */
1251 uint32_t local_vtag;
1252 uint32_t remote_vtag;
1253 } __attribute__((packed));
1255 struct sctp_vtag_param *vtag_param;
1256 struct sctp_paramhdr *param;
1262 param = sm->sctpchnk.Asconf;
1263 param_size = SCTP_SIZE32(ntohs(param->param_length));
1264 bytes_left = sm->chunk_length;
1265 /* step through Asconf parameters */
1266 while((bytes_left >= param_size) && (bytes_left >= SN_VTAG_PARAM_SIZE)) {
1267 if (ntohs(param->param_type) == SCTP_VTAG_PARAM) {
1268 vtag_param = (struct sctp_vtag_param *) param;
1269 switch (direction) {
1270 /* The Internet draft is a little ambigious as to order of these vtags.
1271 We think it is this way around. If we are wrong, the order will need
1274 *g_vtag = vtag_param->local_vtag;
1275 *l_vtag = vtag_param->remote_vtag;
1278 *g_vtag = vtag_param->remote_vtag;
1279 *l_vtag = vtag_param->local_vtag;
1282 return (1); /* found */
1285 bytes_left -= param_size;
1286 if (bytes_left < SN_MIN_PARAM_SIZE) return (0);
1288 param = SN_SCTP_NEXTPARAM(param);
1289 param_size = SCTP_SIZE32(ntohs(param->param_length));
1290 if (++param_count > sysctl_param_proc_limit) {
1291 SN_LOG(SN_LOG_EVENT,
1292 logsctperror("Parameter parse limit exceeded (GetAsconfVtags)",
1293 sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction));
1294 return (0); /* not found limit exceeded*/
1297 return (0); /* not found */
1300 /** @ingroup packet_parser
1301 * @brief AddGlobalIPAddresses from Init,InitAck,or AddIP packets
1303 * AddGlobalIPAddresses scans an SCTP chunk (in sm) for Global IP addresses, and
1306 * @param sm Pointer to sctp message information
1307 * @param assoc Pointer to the association this SCTP Message belongs to
1308 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1312 AddGlobalIPAddresses(struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int direction)
1314 struct sctp_ipv4addr_param *ipv4_param;
1315 struct sctp_paramhdr *param = NULL;
1316 struct sctp_GlobalAddress *G_Addr;
1317 struct in_addr g_addr = {0};
1320 int param_count, addr_param_count = 0;
1322 switch (direction) {
1323 case SN_TO_GLOBAL: /* does not contain global addresses */
1324 g_addr = sm->ip_hdr->ip_dst;
1325 bytes_left = 0; /* force exit */
1328 g_addr = sm->ip_hdr->ip_src;
1332 bytes_left = sm->chunk_length - sizeof(struct sctp_init_chunk);
1333 param = (struct sctp_paramhdr *)((char *)sm->sctpchnk.Init + sizeof(struct sctp_init));
1335 case SN_SCTP_INITACK:
1336 bytes_left = sm->chunk_length - sizeof(struct sctp_init_ack_chunk);
1337 param = (struct sctp_paramhdr *)((char *)sm->sctpchnk.InitAck + sizeof(struct sctp_init_ack));
1339 case SN_SCTP_ASCONF:
1340 bytes_left = sm->chunk_length;
1341 param = sm->sctpchnk.Asconf;
1345 if (bytes_left >= SN_MIN_PARAM_SIZE)
1346 param_size = SCTP_SIZE32(ntohs(param->param_length));
1348 param_size = bytes_left+1; /* force skip loop */
1350 if ((assoc->state == SN_ID) && ((sm->msg == SN_SCTP_INIT) || (bytes_left < SN_MIN_PARAM_SIZE))) {/* add pkt address */
1351 G_Addr = (struct sctp_GlobalAddress *) sn_malloc(sizeof(struct sctp_GlobalAddress));
1352 if (G_Addr == NULL) {/* out of resources */
1353 SN_LOG(SN_LOG_EVENT,
1354 logsctperror("AddGlobalIPAddress: No resources for adding global address - revert to no tracking",
1355 sm->sctp_hdr->v_tag, 0, direction));
1356 assoc->num_Gaddr = 0; /* don't track any more for this assoc*/
1357 sysctl_track_global_addresses=0;
1360 G_Addr->g_addr = g_addr;
1361 if (!Add_Global_Address_to_List(assoc, G_Addr))
1362 SN_LOG(SN_LOG_EVENT,
1363 logsctperror("AddGlobalIPAddress: Address already in list",
1364 sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction));
1367 /* step through parameters */
1368 while((bytes_left >= param_size) && (bytes_left >= sizeof(struct sctp_ipv4addr_param))) {
1369 if (assoc->num_Gaddr >= sysctl_track_global_addresses) {
1370 SN_LOG(SN_LOG_EVENT,
1371 logsctperror("AddGlobalIPAddress: Maximum Number of addresses reached",
1372 sm->sctp_hdr->v_tag, sysctl_track_global_addresses, direction));
1375 switch (ntohs(param->param_type)) {
1376 case SCTP_ADD_IP_ADDRESS:
1377 /* skip to address parameter - leave param_size so bytes left will be calculated properly*/
1378 param = (struct sctp_paramhdr *) &((struct sctp_asconf_addrv4_param *) param)->addrp;
1380 case SCTP_IPV4_ADDRESS:
1381 ipv4_param = (struct sctp_ipv4addr_param *) param;
1382 /* add addresses to association */
1383 G_Addr = (struct sctp_GlobalAddress *) sn_malloc(sizeof(struct sctp_GlobalAddress));
1384 if (G_Addr == NULL) {/* out of resources */
1385 SN_LOG(SN_LOG_EVENT,
1386 logsctperror("AddGlobalIPAddress: No resources for adding global address - revert to no tracking",
1387 sm->sctp_hdr->v_tag, 0, direction));
1388 assoc->num_Gaddr = 0; /* don't track any more for this assoc*/
1389 sysctl_track_global_addresses=0;
1394 if ((sm->msg == SN_SCTP_ASCONF) && (ipv4_param->addr == INADDR_ANY)) { /* use packet address */
1395 G_Addr->g_addr = g_addr;
1396 if (!Add_Global_Address_to_List(assoc, G_Addr))
1397 SN_LOG(SN_LOG_EVENT,
1398 logsctperror("AddGlobalIPAddress: Address already in list",
1399 sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction));
1400 return; /*shouldn't be any other addresses if the zero address is given*/
1402 G_Addr->g_addr.s_addr = ipv4_param->addr;
1403 if (!Add_Global_Address_to_List(assoc, G_Addr))
1404 SN_LOG(SN_LOG_EVENT,
1405 logsctperror("AddGlobalIPAddress: Address already in list",
1406 sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction));
1410 bytes_left -= param_size;
1411 if (bytes_left < SN_MIN_PARAM_SIZE)
1414 param = SN_SCTP_NEXTPARAM(param);
1415 param_size = SCTP_SIZE32(ntohs(param->param_length));
1416 if (++param_count > sysctl_param_proc_limit) {
1417 SN_LOG(SN_LOG_EVENT,
1418 logsctperror("Parameter parse limit exceeded (AddGlobalIPAddress)",
1419 sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction));
1420 break; /* limit exceeded*/
1423 if (addr_param_count == 0) {
1424 SN_LOG(SN_LOG_DETAIL,
1425 logsctperror("AddGlobalIPAddress: no address parameters to add",
1426 sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction));
1431 * @brief Add_Global_Address_to_List
1433 * Adds a global IP address to an associations address list, if it is not
1434 * already there. The first address added us usually the packet's address, and
1435 * is most likely to be used, so it is added at the beginning. Subsequent
1436 * addresses are added after this one.
1438 * @param assoc Pointer to the association this SCTP Message belongs to
1439 * @param G_addr Pointer to the global address to add
1441 * @return 1 - success | 0 - fail
1443 static int Add_Global_Address_to_List(struct sctp_nat_assoc *assoc, struct sctp_GlobalAddress *G_addr)
1445 struct sctp_GlobalAddress *iter_G_Addr = NULL, *first_G_Addr = NULL;
1446 first_G_Addr = LIST_FIRST(&(assoc->Gaddr));
1447 if (first_G_Addr == NULL) {
1448 LIST_INSERT_HEAD(&(assoc->Gaddr), G_addr, list_Gaddr); /* add new address to beginning of list*/
1450 LIST_FOREACH(iter_G_Addr, &(assoc->Gaddr), list_Gaddr) {
1451 if (G_addr->g_addr.s_addr == iter_G_Addr->g_addr.s_addr)
1452 return (0); /* already exists, so don't add */
1454 LIST_INSERT_AFTER(first_G_Addr, G_addr, list_Gaddr); /* add address to end of list*/
1457 return (1); /* success */
1460 /** @ingroup packet_parser
1461 * @brief RmGlobalIPAddresses from DelIP packets
1463 * RmGlobalIPAddresses scans an ASCONF chunk for DelIP parameters to remove the
1464 * given Global IP addresses from the association. It will not delete the
1465 * the address if it is a list of one address.
1468 * @param sm Pointer to sctp message information
1469 * @param assoc Pointer to the association this SCTP Message belongs to
1470 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1474 RmGlobalIPAddresses(struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc, int direction)
1476 struct sctp_asconf_addrv4_param *asconf_ipv4_param;
1477 struct sctp_paramhdr *param;
1478 struct sctp_GlobalAddress *G_Addr, *G_Addr_tmp;
1479 struct in_addr g_addr;
1484 if (direction == SN_TO_GLOBAL)
1485 g_addr = sm->ip_hdr->ip_dst;
1487 g_addr = sm->ip_hdr->ip_src;
1489 bytes_left = sm->chunk_length;
1491 param = sm->sctpchnk.Asconf;
1492 if (bytes_left >= SN_MIN_PARAM_SIZE) {
1493 param_size = SCTP_SIZE32(ntohs(param->param_length));
1495 SN_LOG(SN_LOG_EVENT,
1496 logsctperror("RmGlobalIPAddress: truncated packet - cannot remove IP addresses",
1497 sm->sctp_hdr->v_tag, sysctl_track_global_addresses, direction));
1501 /* step through Asconf parameters */
1502 while((bytes_left >= param_size) && (bytes_left >= sizeof(struct sctp_ipv4addr_param))) {
1503 if (ntohs(param->param_type) == SCTP_DEL_IP_ADDRESS) {
1504 asconf_ipv4_param = (struct sctp_asconf_addrv4_param *) param;
1505 if (asconf_ipv4_param->addrp.addr == INADDR_ANY) { /* remove all bar pkt address */
1506 LIST_FOREACH_SAFE(G_Addr, &(assoc->Gaddr), list_Gaddr, G_Addr_tmp) {
1507 if (G_Addr->g_addr.s_addr != sm->ip_hdr->ip_src.s_addr) {
1508 if (assoc->num_Gaddr > 1) { /* only delete if more than one */
1509 LIST_REMOVE(G_Addr, list_Gaddr);
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));
1519 return; /*shouldn't be any other addresses if the zero address is given*/
1521 LIST_FOREACH_SAFE(G_Addr, &(assoc->Gaddr), list_Gaddr, G_Addr_tmp) {
1522 if (G_Addr->g_addr.s_addr == asconf_ipv4_param->addrp.addr) {
1523 if (assoc->num_Gaddr > 1) { /* only delete if more than one */
1524 LIST_REMOVE(G_Addr, list_Gaddr);
1527 break; /* Since add only adds new addresses, there should be no double entries */
1529 SN_LOG(SN_LOG_EVENT,
1530 logsctperror("RmGlobalIPAddress: Request to remove last IP address (didn't)",
1531 sm->sctp_hdr->v_tag, assoc->num_Gaddr, direction));
1537 bytes_left -= param_size;
1538 if (bytes_left == 0) return;
1539 else if (bytes_left < SN_MIN_PARAM_SIZE) {
1540 SN_LOG(SN_LOG_EVENT,
1541 logsctperror("RmGlobalIPAddress: truncated packet - may not have removed all IP addresses",
1542 sm->sctp_hdr->v_tag, sysctl_track_global_addresses, direction));
1546 param = SN_SCTP_NEXTPARAM(param);
1547 param_size = SCTP_SIZE32(ntohs(param->param_length));
1548 if (++param_count > sysctl_param_proc_limit) {
1549 SN_LOG(SN_LOG_EVENT,
1550 logsctperror("Parameter parse limit exceeded (RmGlobalIPAddress)",
1551 sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction));
1552 return; /* limit exceeded*/
1557 /** @ingroup packet_parser
1558 * @brief Check that ASCONF was successful
1560 * Each ASCONF configuration parameter carries a correlation ID which should be
1561 * matched with an ASCONFack. This is difficult for a NAT, since every
1562 * association could potentially have a number of outstanding ASCONF
1563 * configuration parameters, which should only be activated on receipt of the
1566 * Currently we only look for an ACK when the NAT is setting up a new
1567 * association (ie AddIP for a connection that the NAT does not know about
1568 * because the original Init went through a public interface or another NAT)
1569 * Since there is currently no connection on this path, there should be no other
1570 * ASCONF configuration parameters outstanding, so we presume that if there is
1571 * an ACK that it is responding to the AddIP and activate the new association.
1573 * @param la Pointer to the relevant libalias instance
1574 * @param sm Pointer to sctp message information
1575 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1577 * @return 1 - success | 0 - fail
1580 IsASCONFack(struct libalias *la, struct sctp_nat_msg *sm, int direction)
1582 struct sctp_paramhdr *param;
1588 param = sm->sctpchnk.Asconf;
1589 param_size = SCTP_SIZE32(ntohs(param->param_length));
1590 if (param_size == 8)
1591 return (1); /*success - default acknowledgement of everything */
1593 bytes_left = sm->chunk_length;
1594 if (bytes_left < param_size)
1595 return (0); /* not found */
1596 /* step through Asconf parameters */
1597 while(bytes_left >= SN_ASCONFACK_PARAM_SIZE) {
1598 if (ntohs(param->param_type) == SCTP_SUCCESS_REPORT)
1599 return (1); /* success - but can't match correlation IDs - should only be one */
1600 /* check others just in case */
1601 bytes_left -= param_size;
1602 if (bytes_left >= SN_MIN_PARAM_SIZE) {
1603 param = SN_SCTP_NEXTPARAM(param);
1607 param_size = SCTP_SIZE32(ntohs(param->param_length));
1608 if (bytes_left < param_size) return (0);
1610 if (++param_count > sysctl_param_proc_limit) {
1611 SN_LOG(SN_LOG_EVENT,
1612 logsctperror("Parameter parse limit exceeded (IsASCONFack)",
1613 sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction));
1614 return (0); /* not found limit exceeded*/
1617 return (0); /* not success */
1620 /** @ingroup packet_parser
1621 * @brief Check to see if ASCONF contains an Add IP or Del IP parameter
1623 * IsADDorDEL scans an ASCONF packet to see if it contains an AddIP or DelIP
1626 * @param la Pointer to the relevant libalias instance
1627 * @param sm Pointer to sctp message information
1628 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1630 * @return SCTP_ADD_IP_ADDRESS | SCTP_DEL_IP_ADDRESS | 0 - fail
1633 IsADDorDEL(struct libalias *la, struct sctp_nat_msg *sm, int direction)
1635 struct sctp_paramhdr *param;
1641 param = sm->sctpchnk.Asconf;
1642 param_size = SCTP_SIZE32(ntohs(param->param_length));
1644 bytes_left = sm->chunk_length;
1645 if (bytes_left < param_size)
1646 return (0); /* not found */
1647 /* step through Asconf parameters */
1648 while(bytes_left >= SN_ASCONFACK_PARAM_SIZE) {
1649 if (ntohs(param->param_type) == SCTP_ADD_IP_ADDRESS)
1650 return (SCTP_ADD_IP_ADDRESS);
1651 else if (ntohs(param->param_type) == SCTP_DEL_IP_ADDRESS)
1652 return (SCTP_DEL_IP_ADDRESS);
1653 /* check others just in case */
1654 bytes_left -= param_size;
1655 if (bytes_left >= SN_MIN_PARAM_SIZE) {
1656 param = SN_SCTP_NEXTPARAM(param);
1658 return (0); /*Neither found */
1660 param_size = SCTP_SIZE32(ntohs(param->param_length));
1661 if (bytes_left < param_size) return (0);
1663 if (++param_count > sysctl_param_proc_limit) {
1664 SN_LOG(SN_LOG_EVENT,
1665 logsctperror("Parameter parse limit exceeded IsADDorDEL)",
1666 sm->sctp_hdr->v_tag, sysctl_param_proc_limit, direction));
1667 return (0); /* not found limit exceeded*/
1670 return (0); /*Neither found */
1673 /* ----------------------------------------------------------------------
1674 * STATE MACHINE CODE
1675 * ----------------------------------------------------------------------
1677 /** @addtogroup state_machine
1679 * The SCTP NAT State Machine functions will:
1680 * - Process an already parsed packet
1681 * - Use the existing NAT Hash Tables
1682 * - Determine the next state for the association
1683 * - Update the NAT Hash Tables and Timer Queues
1684 * - Return the appropriate action to take with the packet
1686 /** @ingroup state_machine
1687 * @brief Process SCTP message
1689 * This function is the base state machine. It calls the processing engine for
1692 * @param la Pointer to the relevant libalias instance
1693 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1694 * @param sm Pointer to sctp message information
1695 * @param assoc Pointer to the association this SCTP Message belongs to
1697 * @return SN_DROP_PKT | SN_NAT_PKT | SN_REPLY_ABORT | SN_REPLY_ERROR | SN_PROCESSING_ERROR
1700 ProcessSctpMsg(struct libalias *la, int direction, struct sctp_nat_msg *sm, struct sctp_nat_assoc *assoc)
1704 switch (assoc->state) {
1705 case SN_ID: /* Idle */
1706 rtnval = ID_process(la, direction, assoc, sm);
1707 if (rtnval != SN_NAT_PKT) {
1708 assoc->state = SN_RM;/* Mark for removal*/
1711 case SN_INi: /* Initialising - Init */
1712 return (INi_process(la, direction, assoc, sm));
1713 case SN_INa: /* Initialising - AddIP */
1714 return (INa_process(la, direction, assoc, sm));
1715 case SN_UP: /* Association UP */
1716 return (UP_process(la, direction, assoc, sm));
1717 case SN_CL: /* Association Closing */
1718 return (CL_process(la, direction, assoc, sm));
1720 return (SN_PROCESSING_ERROR);
1723 /** @ingroup state_machine
1724 * @brief Process SCTP message while in the Idle state
1726 * This function looks for an Incoming INIT or AddIP message.
1728 * All other SCTP messages are invalid when in SN_ID, and are dropped.
1730 * @param la Pointer to the relevant libalias instance
1731 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1732 * @param sm Pointer to sctp message information
1733 * @param assoc Pointer to the association this SCTP Message belongs to
1735 * @return SN_NAT_PKT | SN_DROP_PKT | SN_REPLY_ABORT | SN_REPLY_ERROR
1738 ID_process(struct libalias *la, int direction, struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm)
1741 case SN_SCTP_ASCONF: /* a packet containing an ASCONF chunk with ADDIP */
1742 if (!sysctl_accept_global_ootb_addip && (direction == SN_TO_LOCAL))
1743 return (SN_DROP_PKT);
1744 /* if this Asconf packet does not contain the Vtag parameters it is of no use in Idle state */
1745 if (!GetAsconfVtags(la, sm, &(assoc->l_vtag), &(assoc->g_vtag), direction))
1746 return (SN_DROP_PKT);
1748 case SN_SCTP_INIT: /* a packet containing an INIT chunk or an ASCONF AddIP */
1749 if (sysctl_track_global_addresses)
1750 AddGlobalIPAddresses(sm, assoc, direction);
1751 switch (direction) {
1753 assoc->l_addr = sm->ip_hdr->ip_src;
1754 assoc->a_addr = FindAliasAddress(la, assoc->l_addr);
1755 assoc->l_port = sm->sctp_hdr->src_port;
1756 assoc->g_port = sm->sctp_hdr->dest_port;
1757 if (sm->msg == SN_SCTP_INIT)
1758 assoc->g_vtag = sm->sctpchnk.Init->initiate_tag;
1759 if (AddSctpAssocGlobal(la, assoc)) /* DB clash *///**** need to add dst address
1760 return ((sm->msg == SN_SCTP_INIT) ? SN_REPLY_ABORT : SN_REPLY_ERROR);
1761 if (sm->msg == SN_SCTP_ASCONF) {
1762 if (AddSctpAssocLocal(la, assoc, sm->ip_hdr->ip_dst)) /* DB clash */
1763 return (SN_REPLY_ERROR);
1764 assoc->TableRegister |= SN_WAIT_TOLOCAL; /* wait for tolocal ack */
1768 assoc->l_addr = FindSctpRedirectAddress(la, sm);
1769 assoc->a_addr = sm->ip_hdr->ip_dst;
1770 assoc->l_port = sm->sctp_hdr->dest_port;
1771 assoc->g_port = sm->sctp_hdr->src_port;
1772 if (sm->msg == SN_SCTP_INIT)
1773 assoc->l_vtag = sm->sctpchnk.Init->initiate_tag;
1774 if (AddSctpAssocLocal(la, assoc, sm->ip_hdr->ip_src)) /* DB clash */
1775 return ((sm->msg == SN_SCTP_INIT) ? SN_REPLY_ABORT : SN_REPLY_ERROR);
1776 if (sm->msg == SN_SCTP_ASCONF) {
1777 if (AddSctpAssocGlobal(la, assoc)) /* DB clash */ //**** need to add src address
1778 return (SN_REPLY_ERROR);
1779 assoc->TableRegister |= SN_WAIT_TOGLOBAL; /* wait for toglobal ack */
1783 assoc->state = (sm->msg == SN_SCTP_INIT) ? SN_INi : SN_INa;
1784 assoc->exp = SN_I_T(la);
1785 sctp_AddTimeOut(la,assoc);
1786 return (SN_NAT_PKT);
1787 default: /* Any other type of SCTP message is not valid in Idle */
1788 return (SN_DROP_PKT);
1790 return (SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */
1793 /** @ingroup state_machine
1794 * @brief Process SCTP message while waiting for an INIT-ACK message
1796 * Only an INIT-ACK, resent INIT, or an ABORT SCTP packet are valid in this
1797 * state, all other packets are dropped.
1799 * @param la Pointer to the relevant libalias instance
1800 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1801 * @param sm Pointer to sctp message information
1802 * @param assoc Pointer to the association this SCTP Message belongs to
1804 * @return SN_NAT_PKT | SN_DROP_PKT | SN_REPLY_ABORT
1807 INi_process(struct libalias *la, int direction, struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm)
1810 case SN_SCTP_INIT: /* a packet containing a retransmitted INIT chunk */
1811 sctp_ResetTimeOut(la, assoc, SN_I_T(la));
1812 return (SN_NAT_PKT);
1813 case SN_SCTP_INITACK: /* a packet containing an INIT-ACK chunk */
1814 switch (direction) {
1816 if (assoc->num_Gaddr) /*If tracking global addresses for this association */
1817 AddGlobalIPAddresses(sm, assoc, direction);
1818 assoc->l_vtag = sm->sctpchnk.Init->initiate_tag;
1819 if (AddSctpAssocLocal(la, assoc, sm->ip_hdr->ip_src)) { /* DB clash */
1820 assoc->state = SN_RM;/* Mark for removal*/
1821 return (SN_SEND_ABORT);
1825 assoc->l_addr = sm->ip_hdr->ip_src; // Only if not set in Init! *
1826 assoc->g_vtag = sm->sctpchnk.Init->initiate_tag;
1827 if (AddSctpAssocGlobal(la, assoc)) { /* DB clash */
1828 assoc->state = SN_RM;/* Mark for removal*/
1829 return (SN_SEND_ABORT);
1833 assoc->state = SN_UP;/* association established for NAT */
1834 sctp_ResetTimeOut(la,assoc, SN_U_T(la));
1835 return (SN_NAT_PKT);
1836 case SN_SCTP_ABORT: /* a packet containing an ABORT chunk */
1837 assoc->state = SN_RM;/* Mark for removal*/
1838 return (SN_NAT_PKT);
1840 return (SN_DROP_PKT);
1842 return (SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */
1845 /** @ingroup state_machine
1846 * @brief Process SCTP message while waiting for an AddIp-ACK message
1848 * Only an AddIP-ACK, resent AddIP, or an ABORT message are valid, all other
1849 * SCTP packets are dropped
1851 * @param la Pointer to the relevant libalias instance
1852 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1853 * @param sm Pointer to sctp message information
1854 * @param assoc Pointer to the association this SCTP Message belongs to
1856 * @return SN_NAT_PKT | SN_DROP_PKT
1859 INa_process(struct libalias *la, int direction,struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm)
1862 case SN_SCTP_ASCONF: /* a packet containing an ASCONF chunk*/
1863 sctp_ResetTimeOut(la,assoc, SN_I_T(la));
1864 return (SN_NAT_PKT);
1865 case SN_SCTP_ASCONFACK: /* a packet containing an ASCONF chunk with a ADDIP-ACK */
1866 switch (direction) {
1868 if (!(assoc->TableRegister & SN_WAIT_TOLOCAL)) /* wrong direction */
1869 return (SN_DROP_PKT);
1872 if (!(assoc->TableRegister & SN_WAIT_TOGLOBAL)) /* wrong direction */
1873 return (SN_DROP_PKT);
1875 if (IsASCONFack(la,sm,direction)) {
1876 assoc->TableRegister &= SN_BOTH_TBL; /* remove wait flags */
1877 assoc->state = SN_UP; /* association established for NAT */
1878 sctp_ResetTimeOut(la,assoc, SN_U_T(la));
1879 return (SN_NAT_PKT);
1881 assoc->state = SN_RM;/* Mark for removal*/
1882 return (SN_NAT_PKT);
1884 case SN_SCTP_ABORT: /* a packet containing an ABORT chunk */
1885 assoc->state = SN_RM;/* Mark for removal*/
1886 return (SN_NAT_PKT);
1888 return (SN_DROP_PKT);
1890 return (SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */
1893 /** @ingroup state_machine
1894 * @brief Process SCTP messages while association is UP redirecting packets
1896 * While in the SN_UP state, all packets for the particular association
1897 * are passed. Only a SHUT-ACK or an ABORT will cause a change of state.
1899 * @param la Pointer to the relevant libalias instance
1900 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1901 * @param sm Pointer to sctp message information
1902 * @param assoc Pointer to the association this SCTP Message belongs to
1904 * @return SN_NAT_PKT | SN_DROP_PKT
1907 UP_process(struct libalias *la, int direction, struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm)
1910 case SN_SCTP_SHUTACK: /* a packet containing a SHUTDOWN-ACK chunk */
1911 assoc->state = SN_CL;
1912 sctp_ResetTimeOut(la,assoc, SN_C_T(la));
1913 return (SN_NAT_PKT);
1914 case SN_SCTP_ABORT: /* a packet containing an ABORT chunk */
1915 assoc->state = SN_RM;/* Mark for removal*/
1916 return (SN_NAT_PKT);
1917 case SN_SCTP_ASCONF: /* a packet containing an ASCONF chunk*/
1918 if ((direction == SN_TO_LOCAL) && assoc->num_Gaddr) /*If tracking global addresses for this association & from global side */
1919 switch (IsADDorDEL(la,sm,direction)) {
1920 case SCTP_ADD_IP_ADDRESS:
1921 AddGlobalIPAddresses(sm, assoc, direction);
1923 case SCTP_DEL_IP_ADDRESS:
1924 RmGlobalIPAddresses(sm, assoc, direction);
1926 } /* fall through to default */
1928 sctp_ResetTimeOut(la,assoc, SN_U_T(la));
1929 return (SN_NAT_PKT); /* forward packet */
1931 return (SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */
1934 /** @ingroup state_machine
1935 * @brief Process SCTP message while association is in the process of closing
1937 * This function waits for a SHUT-COMP to close the association. Depending on
1938 * the setting of sysctl_holddown_timer it may not remove the association
1939 * immediately, but leave it up until SN_X_T(la). Only SHUT-COMP, SHUT-ACK, and
1940 * ABORT packets are permitted in this state. All other packets are dropped.
1942 * @param la Pointer to the relevant libalias instance
1943 * @param direction SN_TO_LOCAL | SN_TO_GLOBAL
1944 * @param sm Pointer to sctp message information
1945 * @param assoc Pointer to the association this SCTP Message belongs to
1947 * @return SN_NAT_PKT | SN_DROP_PKT
1950 CL_process(struct libalias *la, int direction,struct sctp_nat_assoc *assoc, struct sctp_nat_msg *sm)
1953 case SN_SCTP_SHUTCOMP: /* a packet containing a SHUTDOWN-COMPLETE chunk */
1954 assoc->state = SN_CL; /* Stay in Close state until timeout */
1955 if (sysctl_holddown_timer > 0)
1956 sctp_ResetTimeOut(la, assoc, SN_X_T(la));/* allow to stay open for Tbit packets*/
1958 assoc->state = SN_RM;/* Mark for removal*/
1959 return (SN_NAT_PKT);
1960 case SN_SCTP_SHUTACK: /* a packet containing a SHUTDOWN-ACK chunk */
1961 assoc->state = SN_CL; /* Stay in Close state until timeout */
1962 sctp_ResetTimeOut(la, assoc, SN_C_T(la));
1963 return (SN_NAT_PKT);
1964 case SN_SCTP_ABORT: /* a packet containing an ABORT chunk */
1965 assoc->state = SN_RM;/* Mark for removal*/
1966 return (SN_NAT_PKT);
1968 return (SN_DROP_PKT);
1970 return (SN_DROP_PKT);/* shouldn't get here very bad: log, drop and hope for the best */
1973 /* ----------------------------------------------------------------------
1975 * ----------------------------------------------------------------------
1977 /** @addtogroup Hash
1979 * The Hash functions facilitate searching the NAT Hash Tables for associations
1980 * as well as adding/removing associations from the table(s).
1983 * @brief Find the SCTP association given the local address, port and vtag
1985 * Searches the local look-up table for the association entry matching the
1986 * provided local <address:ports:vtag> tuple
1988 * @param la Pointer to the relevant libalias instance
1989 * @param l_addr local address
1990 * @param g_addr global address
1991 * @param l_vtag local Vtag
1992 * @param l_port local Port
1993 * @param g_port global Port
1995 * @return pointer to association or NULL
1997 static struct sctp_nat_assoc*
1998 FindSctpLocal(struct libalias *la, struct in_addr l_addr, struct in_addr g_addr, uint32_t l_vtag, uint16_t l_port, uint16_t g_port)
2001 struct sctp_nat_assoc *assoc = NULL;
2002 struct sctp_GlobalAddress *G_Addr = NULL;
2004 if (l_vtag != 0) { /* an init packet, vtag==0 */
2005 i = SN_TABLE_HASH(l_vtag, l_port, la->sctpNatTableSize);
2006 LIST_FOREACH(assoc, &la->sctpTableLocal[i], list_L) {
2007 if ((assoc->l_vtag == l_vtag) && (assoc->l_port == l_port) && (assoc->g_port == g_port)\
2008 && (assoc->l_addr.s_addr == l_addr.s_addr)) {
2009 if (assoc->num_Gaddr) {
2010 LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
2011 if (G_Addr->g_addr.s_addr == g_addr.s_addr)
2024 * @brief Check for Global Clash
2026 * Searches the global look-up table for the association entry matching the
2027 * provided global <(addresses):ports:vtag> tuple
2029 * @param la Pointer to the relevant libalias instance
2030 * @param Cassoc association being checked for a clash
2032 * @return pointer to association or NULL
2034 static struct sctp_nat_assoc*
2035 FindSctpGlobalClash(struct libalias *la, struct sctp_nat_assoc *Cassoc)
2038 struct sctp_nat_assoc *assoc = NULL;
2039 struct sctp_GlobalAddress *G_Addr = NULL;
2040 struct sctp_GlobalAddress *G_AddrC = NULL;
2042 if (Cassoc->g_vtag != 0) { /* an init packet, vtag==0 */
2043 i = SN_TABLE_HASH(Cassoc->g_vtag, Cassoc->g_port, la->sctpNatTableSize);
2044 LIST_FOREACH(assoc, &la->sctpTableGlobal[i], list_G) {
2045 if ((assoc->g_vtag == Cassoc->g_vtag) && (assoc->g_port == Cassoc->g_port) && (assoc->l_port == Cassoc->l_port)) {
2046 if (assoc->num_Gaddr) {
2047 LIST_FOREACH(G_AddrC, &(Cassoc->Gaddr), list_Gaddr) {
2048 LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
2049 if (G_Addr->g_addr.s_addr == G_AddrC->g_addr.s_addr)
2063 * @brief Find the SCTP association given the global port and vtag
2065 * Searches the global look-up table for the association entry matching the
2066 * provided global <address:ports:vtag> tuple
2068 * If all but the global address match it sets partial_match to 1 to indicate a
2069 * partial match. If the NAT is tracking global IP addresses for this
2070 * association, the NAT may respond with an ERRORM to request the missing
2071 * address to be added.
2073 * @param la Pointer to the relevant libalias instance
2074 * @param g_addr global address
2075 * @param g_vtag global vtag
2076 * @param g_port global port
2077 * @param l_port local port
2079 * @return pointer to association or NULL
2081 static struct sctp_nat_assoc*
2082 FindSctpGlobal(struct libalias *la, struct in_addr g_addr, uint32_t g_vtag, uint16_t g_port, uint16_t l_port, int *partial_match)
2085 struct sctp_nat_assoc *assoc = NULL;
2086 struct sctp_GlobalAddress *G_Addr = NULL;
2089 if (g_vtag != 0) { /* an init packet, vtag==0 */
2090 i = SN_TABLE_HASH(g_vtag, g_port, la->sctpNatTableSize);
2091 LIST_FOREACH(assoc, &la->sctpTableGlobal[i], list_G) {
2092 if ((assoc->g_vtag == g_vtag) && (assoc->g_port == g_port) && (assoc->l_port == l_port)) {
2094 if (assoc->num_Gaddr) {
2095 LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
2096 if (G_Addr->g_addr.s_addr == g_addr.s_addr)
2109 * @brief Find the SCTP association for a T-Flag message (given the global port and local vtag)
2111 * Searches the local look-up table for a unique association entry matching the
2112 * provided global port and local vtag information
2114 * @param la Pointer to the relevant libalias instance
2115 * @param g_addr global address
2116 * @param l_vtag local Vtag
2117 * @param g_port global Port
2118 * @param l_port local Port
2120 * @return pointer to association or NULL
2122 static struct sctp_nat_assoc*
2123 FindSctpLocalT(struct libalias *la, struct in_addr g_addr, uint32_t l_vtag, uint16_t g_port, uint16_t l_port)
2126 struct sctp_nat_assoc *assoc = NULL, *lastmatch = NULL;
2127 struct sctp_GlobalAddress *G_Addr = NULL;
2130 if (l_vtag != 0) { /* an init packet, vtag==0 */
2131 i = SN_TABLE_HASH(l_vtag, g_port, la->sctpNatTableSize);
2132 LIST_FOREACH(assoc, &la->sctpTableGlobal[i], list_G) {
2133 if ((assoc->g_vtag == l_vtag) && (assoc->g_port == g_port) && (assoc->l_port == l_port)) {
2134 if (assoc->num_Gaddr) {
2135 LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
2136 if (G_Addr->g_addr.s_addr == g_addr.s_addr)
2137 return (assoc); /* full match */
2140 if (++cnt > 1) return (NULL);
2146 /* If there is more than one match we do not know which local address to send to */
2147 return (cnt ? lastmatch : NULL);
2151 * @brief Find the SCTP association for a T-Flag message (given the local port and global vtag)
2153 * Searches the global look-up table for a unique association entry matching the
2154 * provided local port and global vtag information
2156 * @param la Pointer to the relevant libalias instance
2157 * @param g_addr global address
2158 * @param g_vtag global vtag
2159 * @param l_port local port
2160 * @param g_port global port
2162 * @return pointer to association or NULL
2164 static struct sctp_nat_assoc*
2165 FindSctpGlobalT(struct libalias *la, struct in_addr g_addr, uint32_t g_vtag, uint16_t l_port, uint16_t g_port)
2168 struct sctp_nat_assoc *assoc = NULL;
2169 struct sctp_GlobalAddress *G_Addr = NULL;
2171 if (g_vtag != 0) { /* an init packet, vtag==0 */
2172 i = SN_TABLE_HASH(g_vtag, l_port, la->sctpNatTableSize);
2173 LIST_FOREACH(assoc, &la->sctpTableLocal[i], list_L) {
2174 if ((assoc->l_vtag == g_vtag) && (assoc->l_port == l_port) && (assoc->g_port == g_port)) {
2175 if (assoc->num_Gaddr) {
2176 LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
2177 if (G_Addr->g_addr.s_addr == g_addr.s_addr)
2190 * @brief Add the sctp association information to the local look up table
2192 * Searches the local look-up table for an existing association with the same
2193 * details. If a match exists and is ONLY in the local look-up table then this
2194 * is a repeated INIT packet, we need to remove this association from the
2195 * look-up table and add the new association
2197 * The new association is added to the head of the list and state is updated
2199 * @param la Pointer to the relevant libalias instance
2200 * @param assoc pointer to sctp association
2201 * @param g_addr global address
2203 * @return SN_ADD_OK | SN_ADD_CLASH
2206 AddSctpAssocLocal(struct libalias *la, struct sctp_nat_assoc *assoc, struct in_addr g_addr)
2208 struct sctp_nat_assoc *found;
2210 LIBALIAS_LOCK_ASSERT(la);
2211 found = FindSctpLocal(la, assoc->l_addr, g_addr, assoc->l_vtag, assoc->l_port, assoc->g_port);
2213 * Note that if a different global address initiated this Init,
2214 * ie it wasn't resent as presumed:
2215 * - the local receiver if receiving it for the first time will establish
2216 * an association with the new global host
2217 * - if receiving an init from a different global address after sending a
2218 * lost initack it will send an initack to the new global host, the first
2219 * association attempt will then be blocked if retried.
2221 if (found != NULL) {
2222 if ((found->TableRegister == SN_LOCAL_TBL) && (found->g_port == assoc->g_port)) { /* resent message */
2223 RmSctpAssoc(la, found);
2224 sctp_RmTimeOut(la, found);
2225 freeGlobalAddressList(found);
2228 return (SN_ADD_CLASH);
2231 LIST_INSERT_HEAD(&la->sctpTableLocal[SN_TABLE_HASH(assoc->l_vtag, assoc->l_port, la->sctpNatTableSize)],
2233 assoc->TableRegister |= SN_LOCAL_TBL;
2234 la->sctpLinkCount++; //increment link count
2236 if (assoc->TableRegister == SN_BOTH_TBL) {
2237 /* libalias log -- controlled by libalias */
2238 if (la->packetAliasMode & PKT_ALIAS_LOG)
2239 SctpShowAliasStats(la);
2241 SN_LOG(SN_LOG_INFO, logsctpassoc(assoc, "^"));
2248 * @brief Add the sctp association information to the global look up table
2250 * Searches the global look-up table for an existing association with the same
2251 * details. If a match exists and is ONLY in the global look-up table then this
2252 * is a repeated INIT packet, we need to remove this association from the
2253 * look-up table and add the new association
2255 * The new association is added to the head of the list and state is updated
2257 * @param la Pointer to the relevant libalias instance
2258 * @param assoc pointer to sctp association
2260 * @return SN_ADD_OK | SN_ADD_CLASH
2263 AddSctpAssocGlobal(struct libalias *la, struct sctp_nat_assoc *assoc)
2265 struct sctp_nat_assoc *found;
2267 LIBALIAS_LOCK_ASSERT(la);
2268 found = FindSctpGlobalClash(la, assoc);
2269 if (found != NULL) {
2270 if ((found->TableRegister == SN_GLOBAL_TBL) && \
2271 (found->l_addr.s_addr == assoc->l_addr.s_addr) && (found->l_port == assoc->l_port)) { /* resent message */
2272 RmSctpAssoc(la, found);
2273 sctp_RmTimeOut(la, found);
2274 freeGlobalAddressList(found);
2277 return (SN_ADD_CLASH);
2280 LIST_INSERT_HEAD(&la->sctpTableGlobal[SN_TABLE_HASH(assoc->g_vtag, assoc->g_port, la->sctpNatTableSize)],
2282 assoc->TableRegister |= SN_GLOBAL_TBL;
2283 la->sctpLinkCount++; //increment link count
2285 if (assoc->TableRegister == SN_BOTH_TBL) {
2286 /* libalias log -- controlled by libalias */
2287 if (la->packetAliasMode & PKT_ALIAS_LOG)
2288 SctpShowAliasStats(la);
2290 SN_LOG(SN_LOG_INFO, logsctpassoc(assoc, "^"));
2297 * @brief Remove the sctp association information from the look up table
2299 * For each of the two (local/global) look-up tables, remove the association
2300 * from that table IF it has been registered in that table.
2302 * NOTE: The calling code is responsible for freeing memory allocated to the
2303 * association structure itself
2305 * NOTE: The association is NOT removed from the timer queue
2307 * @param la Pointer to the relevant libalias instance
2308 * @param assoc pointer to sctp association
2311 RmSctpAssoc(struct libalias *la, struct sctp_nat_assoc *assoc)
2313 // struct sctp_nat_assoc *found;
2314 if (assoc == NULL) {
2315 /* very bad, log and die*/
2317 logsctperror("ERROR: alias_sctp:RmSctpAssoc(NULL)\n", 0, 0, SN_TO_NODIR));
2320 /* log if association is fully up and now closing */
2321 if (assoc->TableRegister == SN_BOTH_TBL) {
2322 SN_LOG(SN_LOG_INFO, logsctpassoc(assoc, "$"));
2324 LIBALIAS_LOCK_ASSERT(la);
2325 if (assoc->TableRegister & SN_LOCAL_TBL) {
2326 assoc->TableRegister ^= SN_LOCAL_TBL;
2327 la->sctpLinkCount--; //decrement link count
2328 LIST_REMOVE(assoc, list_L);
2331 if (assoc->TableRegister & SN_GLOBAL_TBL) {
2332 assoc->TableRegister ^= SN_GLOBAL_TBL;
2333 la->sctpLinkCount--; //decrement link count
2334 LIST_REMOVE(assoc, list_G);
2336 // sn_free(assoc); //Don't remove now, remove if needed later
2337 /* libalias logging -- controlled by libalias log definition */
2338 if (la->packetAliasMode & PKT_ALIAS_LOG)
2339 SctpShowAliasStats(la);
2344 * @brief free the Global Address List memory
2346 * freeGlobalAddressList deletes all global IP addresses in an associations
2347 * global IP address list.
2351 static void freeGlobalAddressList(struct sctp_nat_assoc *assoc)
2353 struct sctp_GlobalAddress *gaddr1=NULL,*gaddr2=NULL;
2354 /*free global address list*/
2355 gaddr1 = LIST_FIRST(&(assoc->Gaddr));
2356 while (gaddr1 != NULL) {
2357 gaddr2 = LIST_NEXT(gaddr1, list_Gaddr);
2362 /* ----------------------------------------------------------------------
2364 * ----------------------------------------------------------------------
2366 /** @addtogroup Timer
2368 * The timer queue management functions are designed to operate efficiently with
2369 * a minimum of interaction with the queues.
2371 * Once a timeout is set in the queue it will not be altered in the queue unless
2372 * it has to be changed to a shorter time (usually only for aborts and closing).
2373 * On a queue timeout, the real expiry time is checked, and if not leq than the
2374 * timeout it is requeued (O(1)) at its later time. This is especially important
2375 * for normal packets sent during an association. When a timer expires, it is
2376 * updated to its new expiration time if necessary, or processed as a
2377 * timeout. This means that while in UP state, the timing queue is only altered
2378 * every U_T (every few minutes) for a particular association.
2381 * @brief Add an association timeout to the timer queue
2383 * Determine the location in the queue to add the timeout and insert the
2384 * association into the list at that queue position
2390 sctp_AddTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc)
2393 LIBALIAS_LOCK_ASSERT(la);
2394 add_loc = assoc->exp - la->sctpNatTimer.loc_time + la->sctpNatTimer.cur_loc;
2395 if (add_loc >= SN_TIMER_QUEUE_SIZE)
2396 add_loc -= SN_TIMER_QUEUE_SIZE;
2397 LIST_INSERT_HEAD(&la->sctpNatTimer.TimerQ[add_loc], assoc, timer_Q);
2398 assoc->exp_loc = add_loc;
2402 * @brief Remove an association from timer queue
2404 * This is an O(1) operation to remove the association pointer from its
2405 * current position in the timer queue
2407 * @param la Pointer to the relevant libalias instance
2408 * @param assoc pointer to sctp association
2411 sctp_RmTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc)
2413 LIBALIAS_LOCK_ASSERT(la);
2414 LIST_REMOVE(assoc, timer_Q);/* Note this is O(1) */
2419 * @brief Reset timer in timer queue
2421 * Reset the actual timeout for the specified association. If it is earlier than
2422 * the existing timeout, then remove and re-install the association into the
2425 * @param la Pointer to the relevant libalias instance
2426 * @param assoc pointer to sctp association
2427 * @param newexp New expiration time
2430 sctp_ResetTimeOut(struct libalias *la, struct sctp_nat_assoc *assoc, int newexp)
2432 if (newexp < assoc->exp) {
2433 sctp_RmTimeOut(la, assoc);
2434 assoc->exp = newexp;
2435 sctp_AddTimeOut(la, assoc);
2437 assoc->exp = newexp;
2442 * @brief Check timer Q against current time
2444 * Loop through each entry in the timer queue since the last time we processed
2445 * the timer queue until now (the current time). For each association in the
2446 * event list, we remove it from that position in the timer queue and check if
2447 * it has really expired. If so we:
2448 * - Log the timer expiry
2449 * - Remove the association from the NAT tables
2450 * - Release the memory used by the association
2452 * If the timer hasn't really expired we place the association into its new
2453 * correct position in the timer queue.
2455 * @param la Pointer to the relevant libalias instance
2458 sctp_CheckTimers(struct libalias *la)
2460 struct sctp_nat_assoc *assoc;
2462 LIBALIAS_LOCK_ASSERT(la);
2463 while(la->timeStamp >= la->sctpNatTimer.loc_time) {
2464 while (!LIST_EMPTY(&la->sctpNatTimer.TimerQ[la->sctpNatTimer.cur_loc])) {
2465 assoc = LIST_FIRST(&la->sctpNatTimer.TimerQ[la->sctpNatTimer.cur_loc]);
2466 //SLIST_REMOVE_HEAD(&la->sctpNatTimer.TimerQ[la->sctpNatTimer.cur_loc], timer_Q);
2467 LIST_REMOVE(assoc, timer_Q);
2468 if (la->timeStamp >= assoc->exp) { /* state expired */
2469 SN_LOG(((assoc->state == SN_CL) ? (SN_LOG_DEBUG) : (SN_LOG_INFO)),
2470 logsctperror("Timer Expired", assoc->g_vtag, assoc->state, SN_TO_NODIR));
2471 RmSctpAssoc(la, assoc);
2472 freeGlobalAddressList(assoc);
2474 } else {/* state not expired, reschedule timer*/
2475 sctp_AddTimeOut(la, assoc);
2478 /* Goto next location in the timer queue*/
2479 ++la->sctpNatTimer.loc_time;
2480 if (++la->sctpNatTimer.cur_loc >= SN_TIMER_QUEUE_SIZE)
2481 la->sctpNatTimer.cur_loc = 0;
2485 /* ----------------------------------------------------------------------
2487 * ----------------------------------------------------------------------
2489 /** @addtogroup Logging
2491 * The logging functions provide logging of different items ranging from logging
2492 * a simple message, through logging an association details to logging the
2493 * current state of the NAT tables
2495 /** @ingroup Logging
2496 * @brief Log sctp nat errors
2498 * @param errormsg Error message to be logged
2499 * @param vtag Current Vtag
2500 * @param error Error number
2501 * @param direction Direction of packet
2504 logsctperror(char* errormsg, uint32_t vtag, int error, int direction)
2507 switch (direction) {
2518 SctpAliasLog("->%c %s (vt=%u) %d\n", dir, errormsg, ntohl(vtag), error);
2521 /** @ingroup Logging
2522 * @brief Log what the parser parsed
2524 * @param direction Direction of packet
2525 * @param sm Pointer to sctp message information
2528 logsctpparse(int direction, struct sctp_nat_msg *sm)
2530 char *ploc, *pstate;
2531 switch (direction) {
2533 ploc = "TO_LOCAL -";
2536 ploc = "TO_GLOBAL -";
2545 case SN_SCTP_INITACK:
2551 case SN_SCTP_SHUTACK:
2554 case SN_SCTP_SHUTCOMP:
2555 pstate = "ShutComp";
2557 case SN_SCTP_ASCONF:
2560 case SN_SCTP_ASCONFACK:
2561 pstate = "AsconfAck";
2567 pstate = "***ERROR***";
2570 SctpAliasLog("Parsed: %s %s\n", ploc, pstate);
2573 /** @ingroup Logging
2574 * @brief Log an SCTP association's details
2576 * @param assoc pointer to sctp association
2577 * @param s Character that indicates the state of processing for this packet
2579 static void logsctpassoc(struct sctp_nat_assoc *assoc, char* s)
2581 struct sctp_GlobalAddress *G_Addr = NULL;
2583 char addrbuf[INET_ADDRSTRLEN];
2585 switch (assoc->state) {
2608 SctpAliasLog("%sAssoc: %s exp=%u la=%s lv=%u lp=%u gv=%u gp=%u tbl=%d\n",
2609 s, sp, assoc->exp, inet_ntoa_r(assoc->l_addr, addrbuf),
2610 ntohl(assoc->l_vtag), ntohs(assoc->l_port),
2611 ntohl(assoc->g_vtag), ntohs(assoc->g_port),
2612 assoc->TableRegister);
2613 /* list global addresses */
2614 LIST_FOREACH(G_Addr, &(assoc->Gaddr), list_Gaddr) {
2615 SctpAliasLog("\t\tga=%s\n",
2616 inet_ntoa_r(G_Addr->g_addr, addrbuf));
2620 /** @ingroup Logging
2621 * @brief Output Global table to log
2623 * @param la Pointer to the relevant libalias instance
2625 static void logSctpGlobal(struct libalias *la)
2628 struct sctp_nat_assoc *assoc = NULL;
2630 SctpAliasLog("G->\n");
2631 for (i=0; i < la->sctpNatTableSize; i++) {
2632 LIST_FOREACH(assoc, &la->sctpTableGlobal[i], list_G) {
2633 logsctpassoc(assoc, " ");
2638 /** @ingroup Logging
2639 * @brief Output Local table to log
2641 * @param la Pointer to the relevant libalias instance
2643 static void logSctpLocal(struct libalias *la)
2646 struct sctp_nat_assoc *assoc = NULL;
2648 SctpAliasLog("L->\n");
2649 for (i=0; i < la->sctpNatTableSize; i++) {
2650 LIST_FOREACH(assoc, &la->sctpTableLocal[i], list_L) {
2651 logsctpassoc(assoc, " ");
2656 /** @ingroup Logging
2657 * @brief Output timer queue to log
2659 * @param la Pointer to the relevant libalias instance
2661 static void logTimerQ(struct libalias *la)
2663 static char buf[50];
2665 struct sctp_nat_assoc *assoc = NULL;
2667 SctpAliasLog("t->\n");
2668 for (i=0; i < SN_TIMER_QUEUE_SIZE; i++) {
2669 LIST_FOREACH(assoc, &la->sctpNatTimer.TimerQ[i], timer_Q) {
2670 snprintf(buf, 50, " l=%u ",i);
2671 //SctpAliasLog(la->logDesc," l=%d ",i);
2672 logsctpassoc(assoc, buf);
2677 /** @ingroup Logging
2678 * @brief Sctp NAT logging function
2680 * This function is based on a similar function in alias_db.c
2682 * @param str/stream logging descriptor
2683 * @param format printf type string
2687 SctpAliasLog(const char *format, ...)
2689 char buffer[LIBALIAS_BUF_SIZE];
2691 va_start(ap, format);
2692 vsnprintf(buffer, LIBALIAS_BUF_SIZE, format, ap);
2694 log(LOG_SECURITY | LOG_INFO,
2695 "alias_sctp: %s", buffer);
2699 SctpAliasLog(FILE *stream, const char *format, ...)
2703 va_start(ap, format);
2704 vfprintf(stream, format, ap);