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