With contributions from * Sakthivel Velumani <velumani@eurecom.fr> * Robert Schmidt <robert.schmidt@openairinterface.org> Squashed commit of the following: commit e8488acbcdaef6ee1688dc233c9e48e85b0fbfc8 Merge: f09468eb135d58645a85Author: laurent <laurent Thomas> Date: Sat Jan 21 15:03:40 2023 +0100 merge develop commit f09468eb13f729c49830786150918b03ae2d7524 Author: laurent <laurent Thomas> Date: Fri Jan 20 10:43:51 2023 +0100 continue e1AP development commit 76545cfbfc7ec5031633b1b0ec599c3560c1e5a7 Author: laurent <laurent Thomas> Date: Wed Jan 18 14:18:47 2023 +0100 fix regressions after merge develop commit 7071c480a6c433eb94501c49f4a7694ba77f4f6d Merge: 569b9fc5f3214aa50595Author: laurent <laurent Thomas> Date: Mon Jan 16 15:51:50 2023 +0100 merge develop commit 569b9fc5f37b9947b722885e423572a211d658da Author: laurent <laurent Thomas> Date: Wed Jan 4 17:07:01 2023 +0100 fix srb2 addition, simplify and remove dead code, fix one race commit 9fa89151c0f9b4a16a79d0685cc04d8cf2adfa44 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Jan 4 20:02:05 2023 +0530 Bearer context release decoder and encoder commit 7011cb518ed6df015ace536e9a08b2d1865188d9 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Jan 4 13:43:58 2023 +0530 Update documentation commit 3e8899fc962cb16fc1ded49590ca5dddede54978 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Jan 4 10:14:11 2023 +0530 Fix function arguments and warnings commit 989ad183a8d66961facaa6719fa7793b9d40d5b4 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Jan 3 20:18:31 2023 +0530 Moved E1-design.md to doc/ commit 04a8633dd509d9525755c5f3abfad976a1ba704e Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Jan 3 20:13:46 2023 +0530 Make libraries lower case in CMakeLists commit 033ec174c1f48a0e745a2a2e1c367aa361f5ae76 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Jan 3 17:55:25 2023 +0530 Renamed and updated cucp cuup config files commit f76a6913352bd8b2bb73ac25f225140cf05f93f2 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Jan 3 13:31:19 2023 +0530 Removed CU-UP checks from nr-softmodem commit ae70afe44c84ac16b977b388c63e80e24803c43c Author: laurent <laurent Thomas> Date: Mon Jan 2 13:01:45 2023 +0100 fix for gcc version hat doesnt accept declarations in switch commit 6a7a01043bf1e6e0ee9ac40b4d85ced7cdf6dcf4 Author: laurent <laurent Thomas> Date: Tue Dec 27 15:59:28 2022 +0100 first functional commit standalone cu-up commit 0caa4690f9e1acbe578d4de5a82ba62f45444a12 Merge: 3619f9550e1a0c0cd1c9Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Fri Dec 23 14:21:00 2022 +0530 Merge remote-tracking branch 'origin/develop' into e1-implementation-wip commit 3619f9550e4d3e78815334150ff8443db914f738 Merge: 1bcf8d5e00cfe698eb70Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Dec 21 15:47:40 2022 +0530 Merge remote-tracking branch 'origin/develop' into e1-implementation-wip commit 1bcf8d5e00a9b9ce440d825f83ddc1b48086daa8 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Sep 13 02:10:34 2022 -0400 Fix GTP rnti to ue_id after rebase commit 23075ff8b7ef5d56df844c05e0a7100fe7bd35de Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Sep 6 00:32:12 2022 -0400 Added documentation commit 28f6e507f74d3fb069441e05da523378dee0f6ed Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Mon Sep 5 15:02:02 2022 -0400 Header cleaups and unitary simulators build fixes commit e665dbab28c49327066cff72aed22e1de7797b59 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Sep 1 17:52:07 2022 -0400 Common interface between CUCP & CUUP for E1 and non E1 modes commit 5dcc1bf59a37ed87d7d32a617cfcd663d6415757 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Sep 1 13:53:49 2022 -0400 Allocate stack where ever possible Free memory after ASN encode commit 793a5379539a3a2b6c42cd327d8cef79fe21fb55 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Sep 1 10:41:50 2022 -0400 Fixes for build issues commit 0d3564f25a0d8746b0fc550ed371ce21e08cd05a Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Aug 31 20:57:14 2022 -0400 Fixed uesoftmodem linkage error commit 34adf865820f09900a5efed693ada1ccf6f0f1ad Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Aug 31 19:31:50 2022 -0400 Removed RRC thread from CUUP commit 536741910aff90a86ba3a60612f943b412c5ab40 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Aug 31 14:21:31 2022 -0400 Add header guard in one of E1AP header commit 22e9d9b6dea5d84d259ee2dc1aaa7cb943e55b08 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Aug 31 14:00:47 2022 -0400 Fix bug in PDCP ue_id commit 4c65339b07d347003ed89d5e00f49a62e9b21483 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 30 18:04:59 2022 -0400 Linked E1AP lib to L2_NR target E1AP builds for nr-softmodem commit 014fe30ac93c84d810e0209bc7f0d3b7a1bbaee3 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 30 15:49:27 2022 -0400 Remove xer prints from e1ap and gcc warning fixes commit 71f0c9126bbb91bc595b66d57e1e91db676d3b9d Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 30 15:42:30 2022 -0400 Fixed bug in integrity protection config commit 4e393ef9528c2b14d670a341b52fc63262abf894 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 30 14:34:37 2022 -0400 GTP init with only local address IP traffic works both directions in Mono, F1 and E1 commit ec3bde2c5deb99f554afc2053e351b9f79525e96 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 30 00:16:03 2022 -0400 Fix bug in pdcp config and GTP instance UL iperf still not works. Can see packets in wireshark. commit be8df4435eeae0a0755e2ac658f33caa2a3e3c94 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Sat Aug 27 20:15:04 2022 -0400 Fixes in node config after rebase commit 6f42ff88de7f1615f87c46771fd6617a5fdfc48f Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Sat Aug 27 01:38:21 2022 -0400 Added N3 tunnel address in config params fixed bug in address length feild commit b5e132041994344ebb5c2c59ca392612a32658fd Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Fri Aug 26 20:38:47 2022 -0400 Send UL UP address via F1 UE cxt mod msg commit ecfd0fe260aee6aad78c291368c246f88b6bffcf Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Fri Aug 26 15:08:53 2022 -0400 Update GTP remote address from response message Modified GTP tunnel update function accordingly Put GTP tunnel update out of F1AP F1 split works commit 14a37e414de3d653f25ccef1a790bc5b08fc7dc2 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Sat Aug 20 01:17:36 2022 -0400 Moved GTP tunnel creation out of F1AP message sending commit cdd58ab96e84f48cc8ab8f0fe6b3133081ff02f7 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Fri Aug 19 17:40:43 2022 -0400 Moved DRB config out of RRCReconfig complete Monolithic and F1 split works commit 61d66e490a1b61aa2b6ce18d993bb90d112d6374 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Aug 11 01:30:10 2022 -0400 Allow CUUP to have RRC inst without error commit dd7ae889372bb9fdf9e96be87c28eed0d9461d7f Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Aug 11 01:29:21 2022 -0400 Added missing IEs in bearer cxt response handler commit a6f1fe5fd254ccb36a54ede5b652161cb5e542e8 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Aug 10 21:02:30 2022 -0400 Unified bearer context message transfer for E1 and non-E1 mode compiles, to be tested commit f245ace285632edcc28d669123746b9bf458bc38 Author: laurent <laurent Thomas> Date: Tue Aug 9 17:46:52 2022 +0200 remove log_i() of fatal errors commit d2a0f3c8620981f3b65a136f0169e3be9c549742 Author: laurent <laurent Thomas> Date: Fri Aug 5 15:01:39 2022 +0200 fix rnti/ue_id for ue commit 7b52c6cd9e675c28cb9d6347d122f77234ec6542 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Aug 4 19:14:46 2022 -0400 Created separate functions in PDCP for E1 mode commit 46680d2b3d221e675c3aeb675137f45318e31acb Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Aug 4 19:13:10 2022 -0400 Bug fixes in bearer context setup response commit 3d091bfd31b7aba625e4ba4b4f0f337cf2c1f6db Author: laurent <laurent Thomas> Date: Thu Aug 4 15:42:11 2022 +0200 add missing file from previous commit commit 21838572196dec947da99f350f20c4d9408c3c17 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed Aug 3 16:59:16 2022 -0400 Minor bug fixes commit 34eb73fbc6f9d33098850d62b4808e54c80bb4ca Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 2 23:16:47 2022 -0400 Added config files for testing To be renamed or removed later commit a7e9c6fb9332295ad42585eb4338a6f9ff0ae7c0 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 2 23:15:21 2022 -0400 Made rrc_gNB_get_ue_context_from_ngap_ids() non static commit 028f0407571466ff24cf72b5c3814e42c346ae81 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 2 23:12:16 2022 -0400 Completed bearer context setup cycle To be tested: Handling of bearer context setup response and subsequent UE context modifition msg to F1AP task commit 4481d11324b13efa73ab0b5f4f156012485661ae Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Aug 2 23:01:50 2022 -0400 Fixed E1AP PPID (wireshark fixed) commit 7a22573f3e7e16960e3310609dea2783ddf404a2 Author: laurent <laurent Thomas> Date: Fri Jul 29 13:03:31 2022 +0200 fix compile issues, simplify gtp send function commit 0209011aa75a16e4c9c1eb36ebcf4fe0ff93aba2 Author: laurent <laurent Thomas> Date: Thu Jul 28 11:51:18 2022 +0200 restore compilation capability, gtp replacement of rnti by ueid commit ce3b886fd7f11322a8d37b924d1752e6a6adf777 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Fri Jul 22 08:35:31 2022 -0400 Included missing IEs in bearer cxt setup message commit 1208ae0d6fe4b25341c9036dde111fd4d00fd6e9 Author: Cedric Roux <cedric.roux@eurecom.fr> Date: Tue Jul 19 10:53:35 2022 +0200 replace rnti by ue_id in pdcp some sdap and gtp files had to be touched, the work is not finished in there some changes in nr_pdcp_oai_api.c simply assume ue_id is indeed rnti (will functions in this file be used by cu-up?) commit e67d1c88c786267458194e4de516919d0b5d1002 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Jul 19 10:08:39 2022 +0200 Replace RNTI with UE ID in GTP files (half done. Laurent to change ctxt_t) commit 7a141e8e941e5c2624a47da7c764531c03b8f617 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Jul 19 10:04:15 2022 +0200 Added RRC handler for bearer cxt setup in CUUP commit fe2106676bf9144347f30969a1bfd351136f536a Author: laurent <laurent Thomas> Date: Mon Jul 11 13:30:30 2022 +0200 fix race in startup procedure (config before starting threads that use the config) commit 3197baab60e53459928da30cea7a4e0514a074e3 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Sun Jul 10 11:30:31 2022 +0200 Fixed bugs in bearer creation commit 26be61167dec9068e572e08a21ddc96a0f2a7731 Author: laurent <laurent Thomas> Date: Sun Jul 10 10:50:57 2022 +0200 fix a race, add asn1c debug method commit 26750d1818aa206c4c56ada6dbed9bb66dfe3811 Author: laurent <laurent Thomas> Date: Wed Jul 6 22:34:10 2022 +0200 e1ap bearer setup encoding invalid commit d092788aa3f8229eb5c938a41534dfb318955135 Author: laurent <laurent Thomas> Date: Wed Jul 6 14:18:39 2022 +0200 fix regression with F1, some basic cleaning in NGAP commit 41a135734a33b284808d4c5049c431dbc0ff1c46 Author: laurent <laurent Thomas> Date: Wed Jul 6 11:24:52 2022 +0200 fix some ngap regressions commit 460acd840dd58fd46a995219cb9da8fda103668e Author: laurent <laurent Thomas> Date: Tue Jul 5 21:19:13 2022 +0200 build, run in F1 mode until UE connect but pdu session still fails commit 7c067095df287cc7a542be53e9a1471c11ec5af8 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Sat Jun 25 22:45:46 2022 +0530 Populate PDU and DRB paramenters from NGAP commit c4ada56a8c31b4c07fc1498e15b0954d9b1ad2f6 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue Jun 7 22:16:58 2022 +0530 setup resp decoding success commit a2f41597c7438934a203ccf3cced35b6b65e8cee Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Sat Jun 4 13:50:55 2022 +0530 Fixed bugs in E1 setup procedure E1 setup request decoding successful commit 6106207ba74c74579b5f375d572930a609abe3a9 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu May 26 20:13:14 2022 +0530 Fixing build issues commit 67235fac01aaca4c17da13bf658144ffe6bd12df Author: Robert Schmidt <robert.schmidt@openairinterface.org> Date: Mon May 23 12:04:22 2022 +0200 Make nr-softmodem depend on E1AP module commit 391cee938e8250c355b6b914e747f2004c795817 Author: Robert Schmidt <robert.schmidt@openairinterface.org> Date: Mon May 23 11:57:15 2022 +0200 Fix ITTI errors in E1AP target commit db4146e4a0432b7e3759d71efbeb450754c1f0d3 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Wed May 18 06:59:03 2022 +0530 CP UP task created commit 3058d2fc445acf5681a09f70409023718294d093 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue May 10 08:12:19 2022 +0530 Bearer context setup response message commit b911e9db2a4d0a35eb9a449ef02acefff30b558f Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Tue May 3 16:52:49 2022 +0530 Bearer context setup c structs commit d0f4d5f3539803253b9a995a90a47b3d825ea0ca Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Apr 28 02:21:50 2022 +0530 Bearer context setup message commit 489a5358fa0e199314254561a2d52c1a93f7803e Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Thu Apr 21 10:11:03 2022 +0530 Started with CUUP task commit b416061f5a237cbabbe09c6d81fe92df9e33fd72 Author: Sakthivel Velumani <velumani@eurecom.fr> Date: Fri Apr 15 18:46:27 2022 +0530 Setup request and response messages commit faca4b97dba92be857bd8442193365fc2c8ed122 Author: Robert Schmidt <robert.schmidt@openairinterface.org> Date: Mon Apr 11 15:01:48 2022 +0200 Fixup for cmake execution commit ecfd9f7608a5820ae85278939e9bbaf7f1245dd6 Author: Robert Schmidt <robert.schmidt@eurecom.fr> Date: Tue Nov 24 21:39:20 2020 +0100 Move CMakeLists.txt into root commit 9f41f7c52b3c7a185e5e34bb1591b0eb66459837 Author: Robert Schmidt <robert.schmidt@openairinterface.org> Date: Tue Apr 12 12:07:18 2022 +0200 Remove hwlat refs in build_oai: targets don't exist anymore commit e666cf5eedc67695febd33de1397331d6b7b2320 Author: Robert Schmidt <robert.schmidt@openairinterface.org> Date: Mon Apr 11 14:37:32 2022 +0200 Create CMake E1AP_RELEASE variable from E1AP_VERSION commit 1f8413361c1b99ea46ee613da035803a1387f65c Author: Robert Schmidt <robert.schmidt@openairinterface.org> Date: Sun Apr 10 14:25:38 2022 +0200 Add E1AP lib commit 7465cea49243fd6eb9869035b18860dface57098 Author: Robert Schmidt <robert.schmidt@openairinterface.org> Date: Sun Apr 10 14:48:40 2022 +0200 Add E1AP Grammar and build ASN.1 lib at build time commit cde22f83032a199387ae1eae6b702f44ed154f3e Author: Robert Schmidt <robert.schmidt@openairinterface.org> Date: Fri Apr 22 13:42:51 2022 +0200 Accept higher vers. nums in make_version/MAKE_VERSION commit d5127b23608240374a439d1d9c2ff6d53c4b527c Author: Robert Schmidt <robert.schmidt@eurecom.fr> Date: Tue Nov 24 21:39:20 2020 +0100 Move CMakeLists.txt into root
1191 lines
42 KiB
C
1191 lines
42 KiB
C
/*
|
|
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The OpenAirInterface Software Alliance licenses this file to You under
|
|
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.openairinterface.org/?page_id=698
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*-------------------------------------------------------------------------------
|
|
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
|
* contact@openairinterface.org
|
|
*/
|
|
|
|
#include <pthread.h>
|
|
#include <stdint.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netdb.h>
|
|
#include <arpa/inet.h>
|
|
#include <ifaddrs.h>
|
|
#include <sys/ioctl.h>
|
|
#include <net/if.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/sctp.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include "assertions.h"
|
|
#include "common/utils/system.h"
|
|
#include "queue.h"
|
|
|
|
#include "intertask_interface.h"
|
|
|
|
#include "sctp_default_values.h"
|
|
#include "sctp_common.h"
|
|
#include "sctp_eNB_itti_messaging.h"
|
|
|
|
/* Used to format an uint32_t containing an ipv4 address */
|
|
#define IPV4_ADDR "%u.%u.%u.%u"
|
|
#define IPV4_ADDR_FORMAT(aDDRESS) \
|
|
(uint8_t)((aDDRESS) & 0x000000ff), \
|
|
(uint8_t)(((aDDRESS) & 0x0000ff00) >> 8 ), \
|
|
(uint8_t)(((aDDRESS) & 0x00ff0000) >> 16), \
|
|
(uint8_t)(((aDDRESS) & 0xff000000) >> 24)
|
|
|
|
|
|
enum sctp_connection_type_e {
|
|
SCTP_TYPE_CLIENT,
|
|
SCTP_TYPE_SERVER,
|
|
SCTP_TYPE_MULTI_SERVER,
|
|
SCTP_TYPE_MAX
|
|
};
|
|
|
|
typedef struct sctp_cnx_list_elm_s {
|
|
STAILQ_ENTRY(sctp_cnx_list_elm_s) entries;
|
|
|
|
/* Type of this association
|
|
*/
|
|
enum sctp_connection_type_e connection_type;
|
|
|
|
int sd; ///< Socket descriptor of connection */
|
|
uint16_t local_port;
|
|
uint16_t in_streams; ///< Number of input streams negociated for this connection
|
|
uint16_t out_streams; ///< Number of output streams negotiated for this connection
|
|
uint16_t ppid; ///< Payload protocol Identifier
|
|
int32_t assoc_id; ///< SCTP association id for the connection (note4debug host byte order)
|
|
uint32_t messages_recv; ///< Number of messages received on this connection
|
|
uint32_t messages_sent; ///< Number of messages sent on this connection
|
|
task_id_t task_id; ///< Task id of the task who asked for this connection
|
|
instance_t instance; ///< Instance
|
|
uint16_t cnx_id; ///< Upper layer identifier
|
|
|
|
struct sockaddr *peer_addresses;///< A list of peer addresses for server socket
|
|
int nb_peer_addresses; ///< For server socket
|
|
} sctp_cnx_list_elm_t;
|
|
|
|
|
|
static STAILQ_HEAD(sctp_cnx_list_head, sctp_cnx_list_elm_s) sctp_cnx_list;
|
|
static uint16_t sctp_nb_cnx = 0;
|
|
|
|
//------------------------------------------------------------------------------
|
|
struct sctp_cnx_list_elm_s *sctp_get_cnx(int32_t assoc_id, int sd)
|
|
{
|
|
struct sctp_cnx_list_elm_s *elm;
|
|
|
|
STAILQ_FOREACH(elm, &sctp_cnx_list, entries) {
|
|
if (assoc_id != -1) {
|
|
if (elm->assoc_id == assoc_id) {
|
|
return elm;
|
|
}
|
|
} else {
|
|
if (elm->sd == sd) {
|
|
return elm;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static inline
|
|
void
|
|
sctp_eNB_accept_associations_multi(
|
|
struct sctp_cnx_list_elm_s *sctp_cnx)
|
|
{
|
|
int ns;
|
|
int n;
|
|
int flags = 0;
|
|
socklen_t from_len;
|
|
struct sctp_sndrcvinfo sinfo;
|
|
|
|
struct sockaddr_in addr;
|
|
uint8_t buffer[SCTP_RECV_BUFFER_SIZE];
|
|
|
|
DevAssert(sctp_cnx != NULL);
|
|
|
|
memset((void *)&addr, 0, sizeof(struct sockaddr_in));
|
|
from_len = (socklen_t)sizeof(struct sockaddr_in);
|
|
memset((void *)&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
|
|
|
|
n = sctp_recvmsg(sctp_cnx->sd, (void *)buffer, SCTP_RECV_BUFFER_SIZE,
|
|
(struct sockaddr *)&addr, &from_len,
|
|
&sinfo, &flags);
|
|
|
|
if (n < 0) {
|
|
if (errno == ENOTCONN) {
|
|
SCTP_DEBUG("Received not connected for sd %d\n", sctp_cnx->sd);
|
|
close(sctp_cnx->sd);
|
|
} else {
|
|
SCTP_DEBUG("An error occured during read\n");
|
|
SCTP_ERROR("sctp_recvmsg (fd %d, len %d ): %s:%d\n", sctp_cnx->sd, n, strerror(errno), errno);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (flags & MSG_NOTIFICATION) {
|
|
union sctp_notification *snp;
|
|
snp = (union sctp_notification *)buffer;
|
|
|
|
SCTP_DEBUG("Received notification for sd %d, type %u\n",
|
|
sctp_cnx->sd, snp->sn_header.sn_type);
|
|
|
|
/* Association has changed. */
|
|
if (SCTP_ASSOC_CHANGE == snp->sn_header.sn_type) {
|
|
struct sctp_assoc_change *sctp_assoc_changed;
|
|
sctp_assoc_changed = &snp->sn_assoc_change;
|
|
|
|
SCTP_DEBUG("Client association changed: %d\n", sctp_assoc_changed->sac_state);
|
|
|
|
/* New physical association requested by a peer */
|
|
switch (sctp_assoc_changed->sac_state) {
|
|
case SCTP_COMM_UP: {
|
|
SCTP_DEBUG("Comm up notified for sd %d, assigned assoc_id %d\n",
|
|
sctp_cnx->sd, sctp_assoc_changed->sac_assoc_id);
|
|
struct sctp_cnx_list_elm_s *new_cnx;
|
|
|
|
new_cnx = calloc(1, sizeof(*sctp_cnx));
|
|
|
|
DevAssert(new_cnx != NULL);
|
|
|
|
new_cnx->connection_type = SCTP_TYPE_CLIENT;
|
|
|
|
ns = sctp_peeloff(sctp_cnx->sd, sctp_assoc_changed->sac_assoc_id);
|
|
|
|
new_cnx->sd = ns;
|
|
new_cnx->task_id = sctp_cnx->task_id;
|
|
new_cnx->cnx_id = 0;
|
|
new_cnx->ppid = sctp_cnx->ppid;
|
|
new_cnx->instance = sctp_cnx->instance;
|
|
new_cnx->local_port = sctp_cnx->local_port;
|
|
new_cnx->assoc_id = sctp_assoc_changed->sac_assoc_id;
|
|
|
|
if (sctp_get_sockinfo(ns, &new_cnx->in_streams, &new_cnx->out_streams,
|
|
&new_cnx->assoc_id) != 0) {
|
|
SCTP_ERROR("sctp_get_sockinfo failed\n");
|
|
close(ns);
|
|
free(new_cnx);
|
|
return;
|
|
}
|
|
|
|
/* Insert new element at end of list */
|
|
STAILQ_INSERT_TAIL(&sctp_cnx_list, new_cnx, entries);
|
|
sctp_nb_cnx++;
|
|
|
|
/* Add the socket to list of fd monitored by ITTI */
|
|
itti_subscribe_event_fd(TASK_SCTP, ns);
|
|
|
|
sctp_itti_send_association_ind(new_cnx->task_id, new_cnx->instance,
|
|
new_cnx->assoc_id, new_cnx->local_port,
|
|
new_cnx->out_streams, new_cnx->in_streams);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
SCTP_DEBUG("No notification from SCTP\n");
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void
|
|
sctp_handle_new_association_req_multi(
|
|
const instance_t instance,
|
|
const task_id_t requestor,
|
|
const sctp_new_association_req_multi_t * const sctp_new_association_req_p)
|
|
{
|
|
int ns;
|
|
int sd;
|
|
|
|
int32_t assoc_id = 0;
|
|
|
|
struct sctp_cnx_list_elm_s *sctp_cnx = NULL;
|
|
enum sctp_connection_type_e connection_type = SCTP_TYPE_CLIENT;
|
|
|
|
/* Prepare a new SCTP association as requested by upper layer and try to connect
|
|
* to remote host.
|
|
*/
|
|
DevAssert(sctp_new_association_req_p != NULL);
|
|
|
|
sd = sctp_new_association_req_p->multi_sd;
|
|
|
|
/* Create new socket with IPv6 affinity */
|
|
//#warning "SCTP may Force IPv4 only, here"
|
|
|
|
/* Mark the socket as non-blocking */
|
|
//if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0) {
|
|
//SCTP_ERROR("fcntl F_SETFL O_NONBLOCK failed: %s\n",
|
|
// strerror(errno));
|
|
//close(sd);
|
|
//return;
|
|
//}
|
|
|
|
/* SOCK_STREAM socket type requires an explicit connect to the remote host
|
|
* address and port.
|
|
* Only use IPv4 for the first connection attempt
|
|
*/
|
|
if ((sctp_new_association_req_p->remote_address.ipv6 != 0) ||
|
|
(sctp_new_association_req_p->remote_address.ipv4 != 0)) {
|
|
uint8_t address_index = 0;
|
|
uint8_t used_address = sctp_new_association_req_p->remote_address.ipv6 +
|
|
sctp_new_association_req_p->remote_address.ipv4;
|
|
struct sockaddr_in addr[used_address];
|
|
|
|
memset(addr, 0, used_address * sizeof(struct sockaddr_in));
|
|
|
|
if (sctp_new_association_req_p->remote_address.ipv6 == 1) {
|
|
if (inet_pton(AF_INET6, sctp_new_association_req_p->remote_address.ipv6_address,
|
|
&addr[address_index].sin_addr.s_addr) != 1) {
|
|
SCTP_ERROR("Failed to convert ipv6 address %*s to network type\n",
|
|
(int)strlen(sctp_new_association_req_p->remote_address.ipv6_address),
|
|
sctp_new_association_req_p->remote_address.ipv6_address);
|
|
//close(sd);
|
|
//return;
|
|
exit_fun("sctp_handle_new_association_req_multi fatal: inet_pton error");
|
|
}
|
|
|
|
SCTP_DEBUG("Converted ipv6 address %*s to network type\n",
|
|
(int)strlen(sctp_new_association_req_p->remote_address.ipv6_address),
|
|
sctp_new_association_req_p->remote_address.ipv6_address);
|
|
|
|
addr[address_index].sin_family = AF_INET6;
|
|
addr[address_index].sin_port = htons(sctp_new_association_req_p->port);
|
|
address_index++;
|
|
}
|
|
|
|
if (sctp_new_association_req_p->remote_address.ipv4 == 1) {
|
|
if (inet_pton(AF_INET, sctp_new_association_req_p->remote_address.ipv4_address,
|
|
&addr[address_index].sin_addr.s_addr) != 1) {
|
|
SCTP_ERROR("Failed to convert ipv4 address %*s to network type\n",
|
|
(int)strlen(sctp_new_association_req_p->remote_address.ipv4_address),
|
|
sctp_new_association_req_p->remote_address.ipv4_address);
|
|
//close(sd);
|
|
//return;
|
|
exit_fun("sctp_handle_new_association_req_multi fatal: inet_pton error");
|
|
}
|
|
|
|
SCTP_DEBUG("Converted ipv4 address %*s to network type\n",
|
|
(int)strlen(sctp_new_association_req_p->remote_address.ipv4_address),
|
|
sctp_new_association_req_p->remote_address.ipv4_address);
|
|
|
|
addr[address_index].sin_family = AF_INET;
|
|
addr[address_index].sin_port = htons(sctp_new_association_req_p->port);
|
|
address_index++;
|
|
}
|
|
|
|
/* Connect to remote host and port */
|
|
if (sctp_connectx(sd, (struct sockaddr *)addr, 1, &assoc_id) < 0) {
|
|
/* sctp_connectx on non-blocking socket return EINPROGRESS */
|
|
if (errno != EINPROGRESS) {
|
|
SCTP_ERROR("Connect failed: %s\n", strerror(errno));
|
|
sctp_itti_send_association_resp(
|
|
requestor, instance, -1, sctp_new_association_req_p->ulp_cnx_id,
|
|
SCTP_STATE_UNREACHABLE, 0, 0);
|
|
/* Add the socket to list of fd monitored by ITTI */
|
|
//itti_unsubscribe_event_fd(TASK_SCTP, sd);
|
|
//close(sd);
|
|
return;
|
|
} else {
|
|
SCTP_DEBUG("connectx assoc_id %d in progress..., used %d addresses\n",
|
|
assoc_id, used_address);
|
|
}
|
|
} else {
|
|
SCTP_DEBUG("sctp_connectx SUCCESS, socket %d used %d addresses assoc_id %d\n",
|
|
sd,
|
|
used_address,
|
|
assoc_id);
|
|
}
|
|
}
|
|
|
|
ns = sctp_peeloff(sd, assoc_id);
|
|
if (ns == -1) {
|
|
perror("sctp_peeloff");
|
|
printf("sctp_peeloff: sd=%d assoc_id=%d\n", sd, assoc_id);
|
|
exit_fun("sctp_handle_new_association_req_multi fatal: sctp_peeloff error");
|
|
}
|
|
|
|
sctp_cnx = calloc(1, sizeof(*sctp_cnx));
|
|
|
|
sctp_cnx->connection_type = connection_type;
|
|
|
|
sctp_cnx->sd = ns;
|
|
sctp_cnx->task_id = requestor;
|
|
sctp_cnx->cnx_id = sctp_new_association_req_p->ulp_cnx_id;
|
|
sctp_cnx->ppid = sctp_new_association_req_p->ppid;
|
|
sctp_cnx->instance = instance;
|
|
sctp_cnx->assoc_id = assoc_id;
|
|
|
|
/* Add the socket to list of fd monitored by ITTI */
|
|
itti_subscribe_event_fd(TASK_SCTP, ns);
|
|
|
|
/* Insert new element at end of list */
|
|
STAILQ_INSERT_TAIL(&sctp_cnx_list, sctp_cnx, entries);
|
|
sctp_nb_cnx++;
|
|
|
|
SCTP_DEBUG("Inserted new descriptor for sd %d in list, nb elements %u, assoc_id %d\n",
|
|
ns, sctp_nb_cnx, assoc_id);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void
|
|
sctp_handle_new_association_req(
|
|
const instance_t instance,
|
|
const task_id_t requestor,
|
|
const sctp_new_association_req_t * const sctp_new_association_req_p)
|
|
{
|
|
int sd = 0;
|
|
int32_t assoc_id = 0;
|
|
|
|
struct sctp_event_subscribe events={0};
|
|
|
|
struct sctp_cnx_list_elm_s *sctp_cnx = NULL;
|
|
enum sctp_connection_type_e connection_type = SCTP_TYPE_CLIENT;
|
|
|
|
struct ifreq ifr;
|
|
struct ifaddrs *ifaddr = NULL;
|
|
struct ifaddrs *ifa = NULL;
|
|
int family = 0;
|
|
int s = 0;
|
|
struct in_addr in;
|
|
struct in6_addr in6;
|
|
/* Prepare a new SCTP association as requested by upper layer and try to connect
|
|
* to remote host.
|
|
*/
|
|
DevAssert(sctp_new_association_req_p != NULL);
|
|
|
|
/* Create new socket with IPv6 affinity */
|
|
//#warning "SCTP may Force IPv4 only, here"
|
|
#ifdef NO_VIRTUAL_MACHINE
|
|
|
|
// in init chunk appears a list of host addresses, IPv4 and IPv4 in an arbitrary (unsorted) order
|
|
// SCTP hearbeats starts with first ipv4 addresses then stop triyng with other ipv4 addresses
|
|
// if it encounters an IPv6 address in list, so we can force the building of IPv4 addresses only
|
|
// with AF_INET (the working IPv4 address can be the last in the list...)
|
|
if ((sd = socket(AF_INET6, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
|
|
#else
|
|
|
|
if ((sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
|
|
#endif
|
|
SCTP_ERROR("Socket creation failed: %s\n", strerror(errno));
|
|
return;
|
|
}
|
|
|
|
/* Add the socket to list of fd monitored by ITTI */
|
|
itti_subscribe_event_fd(TASK_SCTP, sd);
|
|
|
|
if (sctp_set_init_opt(sd,
|
|
sctp_new_association_req_p->in_streams,
|
|
sctp_new_association_req_p->out_streams,
|
|
SCTP_MAX_ATTEMPTS, SCTP_TIMEOUT) != 0) {
|
|
SCTP_ERROR("Setsockopt IPPROTO_SCTP_INITMSG failed: %s\n",
|
|
strerror(errno));
|
|
itti_unsubscribe_event_fd(TASK_SCTP, sd);
|
|
close(sd);
|
|
return;
|
|
}
|
|
|
|
/* Subscribe to all events */
|
|
events.sctp_data_io_event = 1;
|
|
events.sctp_association_event = 1;
|
|
events.sctp_address_event = 1;
|
|
events.sctp_send_failure_event = 1;
|
|
events.sctp_peer_error_event = 1;
|
|
events.sctp_shutdown_event = 1;
|
|
events.sctp_partial_delivery_event = 1;
|
|
|
|
if (setsockopt(sd, IPPROTO_SCTP, SCTP_EVENTS, &events,
|
|
8) < 0) {
|
|
SCTP_ERROR("Setsockopt IPPROTO_SCTP_EVENTS failed: %s\n",
|
|
strerror(errno));
|
|
close(sd);
|
|
return;
|
|
}
|
|
|
|
// Bind to device ... or we could bind to address also
|
|
if (getifaddrs(&ifaddr) == -1) {
|
|
SCTP_ERROR("getifaddrs failed: %s\n", strerror(errno));
|
|
close(sd);
|
|
}
|
|
|
|
/* Walk through linked list, maintaining head pointer so we
|
|
can free list later */
|
|
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
|
|
if (ifa->ifa_addr == NULL)
|
|
continue;
|
|
|
|
family = ifa->ifa_addr->sa_family;
|
|
|
|
/* For an AF_INET* interface address, display the address */
|
|
if (sctp_new_association_req_p->local_address.ipv4 && family == AF_INET) {
|
|
// compare address
|
|
s = inet_aton(sctp_new_association_req_p->local_address.ipv4_address,
|
|
&in);
|
|
|
|
if (s > 0 ) {
|
|
if (((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr == in.s_addr) {
|
|
struct sockaddr_in locaddr={0};
|
|
locaddr.sin_family = AF_INET;
|
|
locaddr.sin_port = 0;
|
|
locaddr.sin_addr.s_addr = in.s_addr;
|
|
|
|
if (sctp_bindx(sd, (struct sockaddr*)&locaddr, 1, SCTP_BINDX_ADD_ADDR) < 0) {
|
|
SCTP_ERROR("sctp_bindx SCTP_BINDX_ADD_ADDR failed: %s\n",
|
|
strerror(errno));
|
|
} else {
|
|
SCTP_DEBUG("sctp_bindx SCTP_BINDX_ADD_ADDR socket bound to : %s\n",
|
|
inet_ntoa(locaddr.sin_addr));
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
} else if (sctp_new_association_req_p->local_address.ipv6 && family == AF_INET6) {
|
|
// compare address
|
|
s = inet_pton(AF_INET6,
|
|
sctp_new_association_req_p->local_address.ipv6_address,
|
|
&in6);
|
|
|
|
if (s == 1 ) {
|
|
if (memcmp(&((struct sockaddr_in6*)ifa->ifa_addr)->sin6_addr,
|
|
&in6, sizeof(in6)) == 0) {
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifa->ifa_name);
|
|
|
|
if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
|
|
SCTP_ERROR("Setsockopt SOL_SOCKET failed: %s\n",
|
|
strerror(errno));
|
|
} else {
|
|
SCTP_DEBUG("Setsockopt SOL_SOCKET socket bound to : %s\n",
|
|
ifa->ifa_name);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
freeifaddrs(ifaddr);
|
|
|
|
/* Mark the socket as non-blocking */
|
|
if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0) {
|
|
SCTP_ERROR("fcntl F_SETFL O_NONBLOCK failed: %s\n",
|
|
strerror(errno));
|
|
close(sd);
|
|
return;
|
|
}
|
|
|
|
/* SOCK_STREAM socket type requires an explicit connect to the remote host
|
|
* address and port.
|
|
* Only use IPv4 for the first connection attempt
|
|
*/
|
|
if ((sctp_new_association_req_p->remote_address.ipv6 != 0) ||
|
|
(sctp_new_association_req_p->remote_address.ipv4 != 0)) {
|
|
uint8_t address_index = 0;
|
|
uint8_t used_address = sctp_new_association_req_p->remote_address.ipv6 +
|
|
sctp_new_association_req_p->remote_address.ipv4;
|
|
struct sockaddr_in addr[used_address];
|
|
|
|
memset(addr, 0, used_address * sizeof(struct sockaddr_in));
|
|
|
|
if (sctp_new_association_req_p->remote_address.ipv6 == 1) {
|
|
if (inet_pton(AF_INET6, sctp_new_association_req_p->remote_address.ipv6_address,
|
|
&addr[address_index].sin_addr.s_addr) != 1) {
|
|
SCTP_ERROR("Failed to convert ipv6 address %*s to network type\n",
|
|
(int)strlen(sctp_new_association_req_p->remote_address.ipv6_address),
|
|
sctp_new_association_req_p->remote_address.ipv6_address);
|
|
close(sd);
|
|
return;
|
|
}
|
|
|
|
SCTP_DEBUG("Converted ipv6 address %*s to network type\n",
|
|
(int)strlen(sctp_new_association_req_p->remote_address.ipv6_address),
|
|
sctp_new_association_req_p->remote_address.ipv6_address);
|
|
|
|
addr[address_index].sin_family = AF_INET6;
|
|
addr[address_index].sin_port = htons(sctp_new_association_req_p->port);
|
|
address_index++;
|
|
}
|
|
|
|
if (sctp_new_association_req_p->remote_address.ipv4 == 1) {
|
|
if (inet_pton(AF_INET, sctp_new_association_req_p->remote_address.ipv4_address,
|
|
&addr[address_index].sin_addr.s_addr) != 1) {
|
|
SCTP_ERROR("Failed to convert ipv4 address %*s to network type\n",
|
|
(int)strlen(sctp_new_association_req_p->remote_address.ipv4_address),
|
|
sctp_new_association_req_p->remote_address.ipv4_address);
|
|
close(sd);
|
|
return;
|
|
}
|
|
|
|
SCTP_DEBUG("Converted ipv4 address %*s to network type\n",
|
|
(int)strlen(sctp_new_association_req_p->remote_address.ipv4_address),
|
|
sctp_new_association_req_p->remote_address.ipv4_address);
|
|
|
|
addr[address_index].sin_family = AF_INET;
|
|
addr[address_index].sin_port = htons(sctp_new_association_req_p->port);
|
|
address_index++;
|
|
}
|
|
|
|
/* Connect to remote host and port */
|
|
if (sctp_connectx(sd, (struct sockaddr *)addr, 1, &assoc_id) < 0) {
|
|
/* sctp_connectx on non-blocking socket return EINPROGRESS */
|
|
if (errno != EINPROGRESS) {
|
|
SCTP_ERROR("Connect failed: %s\n", strerror(errno));
|
|
sctp_itti_send_association_resp(
|
|
requestor, instance, -1, sctp_new_association_req_p->ulp_cnx_id,
|
|
SCTP_STATE_UNREACHABLE, 0, 0);
|
|
/* Add the socket to list of fd monitored by ITTI */
|
|
itti_unsubscribe_event_fd(TASK_SCTP, sd);
|
|
close(sd);
|
|
return;
|
|
} else {
|
|
SCTP_DEBUG("connectx assoc_id %d in progress..., used %d addresses\n",
|
|
assoc_id, used_address);
|
|
}
|
|
} else {
|
|
SCTP_DEBUG("sctp_connectx SUCCESS, used %d addresses assoc_id %d\n",
|
|
used_address,
|
|
assoc_id);
|
|
}
|
|
} else {
|
|
/* No remote address provided -> only bind the socket for now.
|
|
* Connection will be accepted in the main event loop
|
|
*/
|
|
struct sockaddr_in6 addr6;
|
|
|
|
connection_type = SCTP_TYPE_SERVER;
|
|
|
|
/* For now bind to any interface */
|
|
addr6.sin6_family = AF_INET6;
|
|
addr6.sin6_addr = in6addr_any;
|
|
addr6.sin6_port = htons(sctp_new_association_req_p->port);
|
|
addr6.sin6_flowinfo = 0;
|
|
|
|
if (bind(sd, (struct sockaddr*)&addr6, sizeof(addr6)) < 0) {
|
|
SCTP_ERROR("Failed to bind the socket to address any (v4/v6): %s\n",
|
|
strerror(errno));
|
|
close(sd);
|
|
return;
|
|
}
|
|
}
|
|
|
|
sctp_cnx = calloc(1, sizeof(*sctp_cnx));
|
|
|
|
sctp_cnx->connection_type = connection_type;
|
|
|
|
sctp_cnx->sd = sd;
|
|
sctp_cnx->task_id = requestor;
|
|
sctp_cnx->cnx_id = sctp_new_association_req_p->ulp_cnx_id;
|
|
sctp_cnx->ppid = sctp_new_association_req_p->ppid;
|
|
sctp_cnx->instance = instance;
|
|
sctp_cnx->assoc_id = assoc_id;
|
|
|
|
/* Insert new element at end of list */
|
|
STAILQ_INSERT_TAIL(&sctp_cnx_list, sctp_cnx, entries);
|
|
sctp_nb_cnx++;
|
|
|
|
SCTP_DEBUG("Inserted new descriptor for sd %d in list, nb elements %u, assoc_id %d\n",
|
|
sd, sctp_nb_cnx, assoc_id);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void sctp_send_data(sctp_data_req_t *sctp_data_req_p)
|
|
{
|
|
struct sctp_cnx_list_elm_s *sctp_cnx = NULL;
|
|
|
|
DevAssert(sctp_data_req_p != NULL);
|
|
DevAssert(sctp_data_req_p->buffer != NULL);
|
|
DevAssert(sctp_data_req_p->buffer_length > 0);
|
|
|
|
sctp_cnx = sctp_get_cnx(sctp_data_req_p->assoc_id, 0);
|
|
|
|
if (sctp_cnx == NULL) {
|
|
SCTP_ERROR("Failed to find SCTP description for assoc_id %d\n",
|
|
sctp_data_req_p->assoc_id);
|
|
/* TODO: notify upper layer */
|
|
return;
|
|
}
|
|
|
|
if (sctp_data_req_p->stream >= sctp_cnx->out_streams) {
|
|
SCTP_ERROR("Requested stream (%"PRIu16") >= nb out streams (%"PRIu16")\n",
|
|
sctp_data_req_p->stream, sctp_cnx->out_streams);
|
|
return;
|
|
}
|
|
|
|
/* Send message on specified stream of the sd association
|
|
* NOTE: PPID should be defined in network order
|
|
*/
|
|
if (sctp_sendmsg(sctp_cnx->sd, sctp_data_req_p->buffer,
|
|
sctp_data_req_p->buffer_length, NULL, 0,
|
|
htonl(sctp_cnx->ppid), 0, sctp_data_req_p->stream, 0, 0) < 0) {
|
|
SCTP_ERROR("Sctp_sendmsg failed: %s\n", strerror(errno));
|
|
/* TODO: notify upper layer */
|
|
return;
|
|
}
|
|
|
|
SCTP_DEBUG("Successfully sent %u bytes on stream %d for assoc_id %u\n",
|
|
sctp_data_req_p->buffer_length, sctp_data_req_p->stream,
|
|
sctp_cnx->assoc_id);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static int sctp_close_association(sctp_close_association_t *close_association_p)
|
|
{
|
|
|
|
struct sctp_cnx_list_elm_s *sctp_cnx = NULL;
|
|
|
|
DevAssert(close_association_p != NULL);
|
|
sctp_cnx = sctp_get_cnx(close_association_p->assoc_id, 0);
|
|
|
|
if (sctp_cnx == NULL) {
|
|
SCTP_ERROR("Failed to find SCTP description for assoc_id %d\n",
|
|
close_association_p->assoc_id);
|
|
/* TODO: notify upper layer */
|
|
return -1;
|
|
} else {
|
|
close(sctp_cnx->sd);
|
|
STAILQ_REMOVE(&sctp_cnx_list, sctp_cnx, sctp_cnx_list_elm_s, entries);
|
|
SCTP_DEBUG("Removed assoc_id %u (closed socket %u)\n",
|
|
sctp_cnx->assoc_id, (unsigned int)sctp_cnx->sd);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static int sctp_create_new_listener(
|
|
const instance_t instance,
|
|
const task_id_t requestor,
|
|
sctp_init_t *init_p,
|
|
int server_type)
|
|
{
|
|
struct sctp_event_subscribe event={0};
|
|
struct sockaddr *addr = NULL;
|
|
struct sctp_cnx_list_elm_s *sctp_cnx = NULL;
|
|
uint16_t i = 0, j = 0;
|
|
int sd = 0;
|
|
int used_addresses = 0;
|
|
|
|
DevAssert(init_p != NULL);
|
|
|
|
if (init_p->ipv4 == 0 && init_p->ipv6 == 0) {
|
|
SCTP_ERROR("Illegal IP configuration upper layer should request at"
|
|
"least ipv4 and/or ipv6 config\n");
|
|
return -1;
|
|
}
|
|
|
|
if ((used_addresses = init_p->nb_ipv4_addr + init_p->nb_ipv6_addr) == 0) {
|
|
SCTP_WARN("No address provided...\n");
|
|
return -1;
|
|
}
|
|
|
|
addr = calloc(used_addresses, sizeof(struct sockaddr));
|
|
|
|
SCTP_DEBUG("Creating new listen socket on port %u with\n", init_p->port);
|
|
|
|
if (init_p->ipv4 == 1) {
|
|
struct sockaddr_in *ip4_addr;
|
|
|
|
SCTP_DEBUG("ipv4 addresses:\n");
|
|
|
|
for (i = 0; i < init_p->nb_ipv4_addr; i++) {
|
|
SCTP_DEBUG("\t- "IPV4_ADDR"\n", IPV4_ADDR_FORMAT(init_p->ipv4_address[i]));
|
|
ip4_addr = (struct sockaddr_in *)&addr[i];
|
|
ip4_addr->sin_family = AF_INET;
|
|
ip4_addr->sin_port = htons(init_p->port);
|
|
ip4_addr->sin_addr.s_addr = init_p->ipv4_address[i];
|
|
}
|
|
}
|
|
|
|
if (init_p->ipv6 == 1) {
|
|
struct sockaddr_in6 *ip6_addr;
|
|
|
|
SCTP_DEBUG("ipv6 addresses:\n");
|
|
|
|
for (j = 0; j < init_p->nb_ipv6_addr; j++) {
|
|
SCTP_DEBUG("\t- %s\n", init_p->ipv6_address[j]);
|
|
ip6_addr = (struct sockaddr_in6 *)&addr[i + j];
|
|
ip6_addr->sin6_family = AF_INET6;
|
|
ip6_addr->sin6_port = htons(init_p->port);
|
|
|
|
if (inet_pton(AF_INET6, init_p->ipv6_address[j],
|
|
ip6_addr->sin6_addr.s6_addr) <= 0) {
|
|
SCTP_WARN("Provided ipv6 address %s is not valid\n",
|
|
init_p->ipv6_address[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (server_type) {
|
|
if ((sd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0) {
|
|
SCTP_ERROR("socket: %s:%d\n", strerror(errno), errno);
|
|
free(addr);
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
if ((sd = socket(AF_INET6, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
|
|
SCTP_ERROR("socket: %s:%d\n", strerror(errno), errno);
|
|
free(addr);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
event.sctp_data_io_event = 1;
|
|
event.sctp_association_event = 1;
|
|
event.sctp_address_event = 1;
|
|
event.sctp_send_failure_event = 1;
|
|
event.sctp_peer_error_event = 1;
|
|
event.sctp_shutdown_event = 1;
|
|
event.sctp_partial_delivery_event = 1;
|
|
|
|
if (setsockopt(sd, IPPROTO_SCTP, SCTP_EVENTS, &event,
|
|
8) < 0) {
|
|
SCTP_ERROR("setsockopt: %s:%d\n", strerror(errno), errno);
|
|
if (sd != -1) {
|
|
close(sd);
|
|
sd = -1;
|
|
}
|
|
free(addr);
|
|
return -1;
|
|
}
|
|
|
|
sctp_cnx = calloc(1, sizeof(*sctp_cnx));
|
|
|
|
if (server_type) {
|
|
sctp_cnx->connection_type = SCTP_TYPE_MULTI_SERVER;
|
|
}
|
|
else {
|
|
sctp_cnx->connection_type = SCTP_TYPE_SERVER;
|
|
}
|
|
|
|
sctp_cnx->sd = sd;
|
|
sctp_cnx->local_port = init_p->port;
|
|
sctp_cnx->in_streams = 32;
|
|
sctp_cnx->out_streams = 32;
|
|
sctp_cnx->ppid = init_p->ppid;
|
|
sctp_cnx->task_id = requestor;
|
|
sctp_cnx->instance = instance;
|
|
|
|
/* Some pre-bind socket configuration */
|
|
if (sctp_set_init_opt(sd,
|
|
sctp_cnx->in_streams,
|
|
sctp_cnx->out_streams,
|
|
0,
|
|
0) < 0) {
|
|
goto err;
|
|
}
|
|
|
|
if (sctp_bindx(sd, addr, used_addresses, SCTP_BINDX_ADD_ADDR) != 0) {
|
|
SCTP_ERROR("sctp_bindx: %s:%d\n", strerror(errno), errno);
|
|
free(sctp_cnx);
|
|
sctp_cnx = NULL;
|
|
return -1;
|
|
}
|
|
|
|
if (listen(sd, 5) < 0) {
|
|
SCTP_ERROR("listen: %s:%d\n", strerror(errno), errno);
|
|
free(sctp_cnx);
|
|
sctp_cnx = NULL;
|
|
return -1;
|
|
}
|
|
SCTP_DEBUG("Created listen socket: %d\n", sd);
|
|
/* Insert new element at end of list */
|
|
STAILQ_INSERT_TAIL(&sctp_cnx_list, sctp_cnx, entries);
|
|
sctp_nb_cnx++;
|
|
|
|
/* Add the socket to list of fd monitored by ITTI */
|
|
itti_subscribe_event_fd(TASK_SCTP, sd);
|
|
|
|
return sd;
|
|
err:
|
|
|
|
if (sd != -1) {
|
|
close(sd);
|
|
sd = -1;
|
|
}
|
|
|
|
if (sctp_cnx != NULL) {
|
|
free(sctp_cnx);
|
|
sctp_cnx = NULL;
|
|
}
|
|
|
|
if (addr != NULL) {
|
|
free(addr);
|
|
addr = NULL;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static inline
|
|
void
|
|
sctp_eNB_accept_associations(
|
|
struct sctp_cnx_list_elm_s *sctp_cnx)
|
|
{
|
|
int client_sd;
|
|
struct sockaddr_in6 saddr;
|
|
socklen_t saddr_size;
|
|
|
|
DevAssert(sctp_cnx != NULL);
|
|
|
|
saddr_size = sizeof(saddr);
|
|
|
|
/* There is a new client connecting. Accept it...
|
|
*/
|
|
if ((client_sd = accept(sctp_cnx->sd, (struct sockaddr*)&saddr, &saddr_size)) < 0) {
|
|
SCTP_ERROR("[%d] accept failed: %s:%d\n", sctp_cnx->sd, strerror(errno), errno);
|
|
} else {
|
|
struct sctp_cnx_list_elm_s *new_cnx;
|
|
uint16_t port;
|
|
|
|
/* This is an ipv6 socket */
|
|
port = saddr.sin6_port;
|
|
|
|
/* Contrary to BSD, client socket does not inherit O_NONBLOCK option */
|
|
if (fcntl(client_sd, F_SETFL, O_NONBLOCK) < 0) {
|
|
SCTP_ERROR("fcntl F_SETFL O_NONBLOCK failed: %s\n",
|
|
strerror(errno));
|
|
close(client_sd);
|
|
return;
|
|
}
|
|
|
|
new_cnx = calloc(1, sizeof(*sctp_cnx));
|
|
|
|
DevAssert(new_cnx != NULL);
|
|
|
|
new_cnx->connection_type = SCTP_TYPE_CLIENT;
|
|
|
|
new_cnx->sd = client_sd;
|
|
new_cnx->task_id = sctp_cnx->task_id;
|
|
new_cnx->cnx_id = 0;
|
|
new_cnx->ppid = sctp_cnx->ppid;
|
|
new_cnx->instance = sctp_cnx->instance;
|
|
new_cnx->local_port = sctp_cnx->local_port;
|
|
|
|
if (sctp_get_sockinfo(client_sd, &new_cnx->in_streams, &new_cnx->out_streams,
|
|
&new_cnx->assoc_id) != 0) {
|
|
SCTP_ERROR("sctp_get_sockinfo failed\n");
|
|
close(client_sd);
|
|
free(new_cnx);
|
|
return;
|
|
}
|
|
|
|
/* Insert new element at end of list */
|
|
STAILQ_INSERT_TAIL(&sctp_cnx_list, new_cnx, entries);
|
|
sctp_nb_cnx++;
|
|
|
|
/* Add the socket to list of fd monitored by ITTI */
|
|
itti_subscribe_event_fd(TASK_SCTP, client_sd);
|
|
|
|
sctp_itti_send_association_ind(new_cnx->task_id, new_cnx->instance,
|
|
new_cnx->assoc_id, port,
|
|
new_cnx->out_streams, new_cnx->in_streams);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static inline
|
|
void
|
|
sctp_eNB_read_from_socket(
|
|
struct sctp_cnx_list_elm_s *sctp_cnx)
|
|
{
|
|
DevAssert(sctp_cnx != NULL);
|
|
|
|
int flags = 0;
|
|
struct sctp_sndrcvinfo sinfo={0};
|
|
uint8_t buffer[SCTP_RECV_BUFFER_SIZE];
|
|
|
|
int n = sctp_recvmsg(sctp_cnx->sd, (void *)buffer, SCTP_RECV_BUFFER_SIZE,
|
|
NULL, NULL,
|
|
&sinfo, &flags);
|
|
|
|
if (n < 0) {
|
|
if( (errno == ENOTCONN) || (errno == ECONNRESET) || (errno == ETIMEDOUT) || (errno == ECONNREFUSED) )
|
|
{
|
|
itti_unsubscribe_event_fd(TASK_SCTP, sctp_cnx->sd);
|
|
|
|
SCTP_DEBUG("Received not connected for sd %d\n", sctp_cnx->sd);
|
|
SCTP_ERROR("sctp_recvmsg (fd %d, len %d ): %s:%d\n", sctp_cnx->sd, n, strerror(errno), errno);
|
|
|
|
sctp_itti_send_association_resp(
|
|
sctp_cnx->task_id, sctp_cnx->instance, -1,
|
|
sctp_cnx->cnx_id, SCTP_STATE_UNREACHABLE, 0, 0);
|
|
|
|
close(sctp_cnx->sd);
|
|
STAILQ_REMOVE(&sctp_cnx_list, sctp_cnx, sctp_cnx_list_elm_s, entries);
|
|
sctp_nb_cnx--;
|
|
free(sctp_cnx);
|
|
} else {
|
|
SCTP_DEBUG("An error occured during read\n");
|
|
SCTP_ERROR("sctp_recvmsg (fd %d, len %d ): %s:%d\n", sctp_cnx->sd, n, strerror(errno), errno);
|
|
}
|
|
|
|
return;
|
|
} else if (n == 0) {
|
|
SCTP_DEBUG("return of sctp_recvmsg is 0...\n");
|
|
return;
|
|
}
|
|
|
|
if (!(flags & MSG_EOR)) {
|
|
SCTP_ERROR("fatal: partial SCTP messages are not handled\n");
|
|
exit_fun("fatal: partial SCTP messages are not handled" );
|
|
}
|
|
|
|
if (flags & MSG_NOTIFICATION) {
|
|
union sctp_notification *snp;
|
|
snp = (union sctp_notification *)buffer;
|
|
|
|
SCTP_DEBUG("Received notification for sd %d, type %u\n",
|
|
sctp_cnx->sd, snp->sn_header.sn_type);
|
|
|
|
/* Client deconnection */
|
|
if (SCTP_SHUTDOWN_EVENT == snp->sn_header.sn_type) {
|
|
itti_unsubscribe_event_fd(TASK_SCTP, sctp_cnx->sd);
|
|
|
|
SCTP_WARN("Received SCTP SHUTDOWN EVENT\n");
|
|
close(sctp_cnx->sd);
|
|
|
|
sctp_itti_send_association_resp(
|
|
sctp_cnx->task_id, sctp_cnx->instance, sctp_cnx->assoc_id,
|
|
sctp_cnx->cnx_id, SCTP_STATE_SHUTDOWN,
|
|
0, 0);
|
|
|
|
STAILQ_REMOVE(&sctp_cnx_list, sctp_cnx, sctp_cnx_list_elm_s, entries);
|
|
sctp_nb_cnx--;
|
|
|
|
free(sctp_cnx);
|
|
}
|
|
/* Association has changed. */
|
|
else if (SCTP_ASSOC_CHANGE == snp->sn_header.sn_type) {
|
|
struct sctp_assoc_change *sctp_assoc_changed;
|
|
sctp_assoc_changed = &snp->sn_assoc_change;
|
|
|
|
SCTP_DEBUG("Client association changed: %d\n", sctp_assoc_changed->sac_state);
|
|
|
|
/* New physical association requested by a peer */
|
|
switch (sctp_assoc_changed->sac_state) {
|
|
case SCTP_COMM_UP: {
|
|
if (sctp_get_peeraddresses(sctp_cnx->sd, NULL, NULL) != 0) {
|
|
/* TODO Failure -> notify upper layer */
|
|
} else {
|
|
sctp_get_sockinfo(sctp_cnx->sd, &sctp_cnx->in_streams,
|
|
&sctp_cnx->out_streams, &sctp_cnx->assoc_id);
|
|
}
|
|
|
|
SCTP_DEBUG("Comm up notified for sd %d, assigned assoc_id %d\n",
|
|
sctp_cnx->sd, sctp_cnx->assoc_id);
|
|
|
|
sctp_itti_send_association_resp(
|
|
sctp_cnx->task_id, sctp_cnx->instance, sctp_cnx->assoc_id,
|
|
sctp_cnx->cnx_id, SCTP_STATE_ESTABLISHED,
|
|
sctp_cnx->out_streams, sctp_cnx->in_streams);
|
|
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if ( sctp_assoc_changed->sac_state == SCTP_SHUTDOWN_COMP)
|
|
SCTP_WARN("SCTP_ASSOC_CHANGE to SSCTP_SHUTDOWN_COMP\n");
|
|
if ( sctp_assoc_changed->sac_state == SCTP_RESTART)
|
|
SCTP_WARN("SCTP_ASSOC_CHANGE to SCTP_RESTART\n");
|
|
if ( sctp_assoc_changed->sac_state == SCTP_CANT_STR_ASSOC)
|
|
SCTP_ERROR("SCTP_ASSOC_CHANGE to SCTP_CANT_STR_ASSOC\n");
|
|
if ( sctp_assoc_changed->sac_state == SCTP_COMM_LOST)
|
|
SCTP_ERROR("SCTP_ASSOC_CHANGE to SCTP_COMM_LOST\n");
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
sctp_cnx->messages_recv++;
|
|
|
|
if (ntohl(sinfo.sinfo_ppid) != sctp_cnx->ppid) {
|
|
/* Mismatch in Payload Protocol Identifier,
|
|
* may be we received unsollicited traffic from stack other than S1AP.
|
|
*/
|
|
SCTP_ERROR("Received data from peer with unsollicited PPID %d, expecting %d\n",
|
|
ntohl(sinfo.sinfo_ppid),
|
|
sctp_cnx->ppid);
|
|
}
|
|
|
|
SCTP_DEBUG("[%d][%d] Msg of length %d received, on stream %d, PPID %d\n",
|
|
sinfo.sinfo_assoc_id, sctp_cnx->sd, n,
|
|
sinfo.sinfo_stream, ntohl(sinfo.sinfo_ppid));
|
|
|
|
sctp_itti_send_new_message_ind(sctp_cnx->task_id,
|
|
sctp_cnx->instance,
|
|
sinfo.sinfo_assoc_id,
|
|
buffer,
|
|
n,
|
|
sinfo.sinfo_stream);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
static sctp_eNB_flush_sockets(
|
|
struct epoll_event *events, int nb_events)
|
|
{
|
|
int i;
|
|
struct sctp_cnx_list_elm_s *sctp_cnx = NULL;
|
|
|
|
for (i = 0; i < nb_events; i++) {
|
|
sctp_cnx = sctp_get_cnx(-1, events[i].data.fd);
|
|
|
|
if (sctp_cnx == NULL) {
|
|
continue;
|
|
}
|
|
|
|
SCTP_DEBUG("Found data for descriptor %d\n", events[i].data.fd);
|
|
|
|
if (sctp_cnx->connection_type == SCTP_TYPE_CLIENT) {
|
|
sctp_eNB_read_from_socket(sctp_cnx);
|
|
}
|
|
else if (sctp_cnx->connection_type == SCTP_TYPE_MULTI_SERVER) {
|
|
sctp_eNB_accept_associations_multi(sctp_cnx);
|
|
}
|
|
else {
|
|
sctp_eNB_accept_associations(sctp_cnx);
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void sctp_eNB_init(void)
|
|
{
|
|
SCTP_DEBUG("Starting SCTP layer\n");
|
|
|
|
STAILQ_INIT(&sctp_cnx_list);
|
|
|
|
itti_mark_task_ready(TASK_SCTP);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void sctp_eNB_process_itti_msg()
|
|
{
|
|
int nb_events;
|
|
MessageDef *received_msg = NULL;
|
|
int result;
|
|
|
|
itti_receive_msg(TASK_SCTP, &received_msg);
|
|
|
|
/* Check if there is a packet to handle */
|
|
if (received_msg != NULL) {
|
|
LOG_D(SCTP,"Received message %d:%s\n",
|
|
ITTI_MSG_ID(received_msg), ITTI_MSG_NAME(received_msg));
|
|
switch (ITTI_MSG_ID(received_msg)) {
|
|
case SCTP_INIT_MSG: {
|
|
if (sctp_create_new_listener(
|
|
ITTI_MSG_DESTINATION_INSTANCE(received_msg),
|
|
ITTI_MSG_ORIGIN_ID(received_msg),
|
|
&received_msg->ittiMsg.sctp_init,0) < 0) {
|
|
/* SCTP socket creation or bind failed... */
|
|
SCTP_ERROR("Failed to create new SCTP listener\n");
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SCTP_INIT_MSG_MULTI_REQ: {
|
|
int multi_sd = sctp_create_new_listener(
|
|
ITTI_MSG_DESTINATION_INSTANCE(received_msg),
|
|
ITTI_MSG_ORIGIN_ID(received_msg),
|
|
&received_msg->ittiMsg.sctp_init_multi,1);
|
|
/* We received a new connection request */
|
|
if (multi_sd < 0) {
|
|
/* SCTP socket creation or bind failed... */
|
|
SCTP_ERROR("Failed to create new SCTP listener\n");
|
|
}
|
|
sctp_itti_send_init_msg_multi_cnf(
|
|
ITTI_MSG_ORIGIN_ID(received_msg),
|
|
ITTI_MSG_DESTINATION_INSTANCE(received_msg),
|
|
multi_sd);
|
|
}
|
|
break;
|
|
|
|
case SCTP_NEW_ASSOCIATION_REQ: {
|
|
sctp_handle_new_association_req(ITTI_MSG_DESTINATION_INSTANCE(received_msg),
|
|
ITTI_MSG_ORIGIN_ID(received_msg),
|
|
&received_msg->ittiMsg.sctp_new_association_req);
|
|
}
|
|
break;
|
|
|
|
case SCTP_NEW_ASSOCIATION_REQ_MULTI: {
|
|
sctp_handle_new_association_req_multi(ITTI_MSG_DESTINATION_INSTANCE(received_msg),
|
|
ITTI_MSG_ORIGIN_ID(received_msg),
|
|
&received_msg->ittiMsg.sctp_new_association_req_multi);
|
|
}
|
|
break;
|
|
|
|
case SCTP_CLOSE_ASSOCIATION:
|
|
sctp_close_association(&received_msg->ittiMsg.sctp_close_association);
|
|
break;
|
|
|
|
case TERMINATE_MESSAGE:
|
|
SCTP_WARN("*** Exiting SCTP thread\n");
|
|
itti_exit_task();
|
|
break;
|
|
|
|
case SCTP_DATA_REQ: {
|
|
sctp_send_data(&received_msg->ittiMsg.sctp_data_req);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
SCTP_ERROR("Received unhandled message %d:%s\n",
|
|
ITTI_MSG_ID(received_msg), ITTI_MSG_NAME(received_msg));
|
|
break;
|
|
}
|
|
|
|
result = itti_free(ITTI_MSG_ORIGIN_ID(received_msg), received_msg);
|
|
AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
|
|
received_msg = NULL;
|
|
}
|
|
struct epoll_event events[20];
|
|
nb_events = itti_get_events(TASK_SCTP, events, 20);
|
|
/* Now handle notifications for other sockets */
|
|
sctp_eNB_flush_sockets(events, nb_events);
|
|
|
|
return;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void *sctp_eNB_task(void *arg)
|
|
{
|
|
sctp_eNB_init();
|
|
|
|
while (1) {
|
|
sctp_eNB_process_itti_msg();
|
|
}
|
|
|
|
return NULL;
|
|
}
|