The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netinet/libalias/alias_sctp.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 907c3ca80756730545bb550dbbbc8ce8


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.