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