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