From be4a2b5be37d062aeafa7cd501fe77039928f5fc Mon Sep 17 00:00:00 2001
From: Cedric Roux <cedric.roux@eurecom.fr>
Date: Thu, 14 Nov 2013 16:26:11 +0000
Subject: [PATCH] - Updated eNB SCTP task to accept new associations in case of
 "server", for X2AP for example

git-svn-id: http://svn.eurecom.fr/openair4G/trunk@4405 818b1a75-f10b-46b9-bf7c-635c3b92a50f
---
 openair-cn/SCTP/sctp_eNB_itti_messaging.c |  19 ++++
 openair-cn/SCTP/sctp_eNB_itti_messaging.h |   4 +
 openair-cn/SCTP/sctp_eNB_task.c           | 109 +++++++++++++++++++++-
 openair2/COMMON/sctp_messages_def.h       |   1 +
 openair2/COMMON/sctp_messages_types.h     |  12 +++
 5 files changed, 144 insertions(+), 1 deletion(-)

diff --git a/openair-cn/SCTP/sctp_eNB_itti_messaging.c b/openair-cn/SCTP/sctp_eNB_itti_messaging.c
index ffd5b2df3f..336291be37 100644
--- a/openair-cn/SCTP/sctp_eNB_itti_messaging.c
+++ b/openair-cn/SCTP/sctp_eNB_itti_messaging.c
@@ -45,3 +45,22 @@ int sctp_itti_send_association_resp(task_id_t task_id, instance_t instance,
 
     return itti_send_msg_to_task(task_id, instance, message_p);
 }
+
+int sctp_itti_send_association_ind(task_id_t task_id, instance_t instance,
+                                   int32_t assoc_id, uint16_t port,
+                                   uint16_t out_streams, uint16_t in_streams)
+{
+    MessageDef                 *message_p;
+    sctp_new_association_ind_t *sctp_new_association_ind_p;
+
+    message_p = itti_alloc_new_message(TASK_SCTP, SCTP_NEW_ASSOCIATION_IND);
+
+    sctp_new_association_ind_p = &message_p->msg.sctp_new_association_ind;
+
+    sctp_new_association_ind_p->assoc_id    = assoc_id;
+    sctp_new_association_ind_p->port        = port;
+    sctp_new_association_ind_p->out_streams = out_streams;
+    sctp_new_association_ind_p->in_streams  = in_streams;
+
+    return itti_send_msg_to_task(task_id, instance, message_p);
+}
diff --git a/openair-cn/SCTP/sctp_eNB_itti_messaging.h b/openair-cn/SCTP/sctp_eNB_itti_messaging.h
index eba2d19933..a5c5453be9 100644
--- a/openair-cn/SCTP/sctp_eNB_itti_messaging.h
+++ b/openair-cn/SCTP/sctp_eNB_itti_messaging.h
@@ -9,4 +9,8 @@ int sctp_itti_send_association_resp(task_id_t task_id, instance_t instance,
                                     uint16_t cnx_id, enum sctp_state_e state,
                                     uint16_t out_streams, uint16_t in_streams);
 
+int sctp_itti_send_association_ind(task_id_t task_id, instance_t instance,
+                                   int32_t assoc_id, uint16_t port,
+                                   uint16_t out_streams, uint16_t in_streams);
+
 #endif /* SCTP_ITTI_MESSAGING_H_ */
diff --git a/openair-cn/SCTP/sctp_eNB_task.c b/openair-cn/SCTP/sctp_eNB_task.c
index c59a185e62..f00acfb71f 100644
--- a/openair-cn/SCTP/sctp_eNB_task.c
+++ b/openair-cn/SCTP/sctp_eNB_task.c
@@ -49,12 +49,25 @@
 #include "sctp_common.h"
 #include "sctp_eNB_itti_messaging.h"
 
+enum sctp_connection_type_e {
+    SCTP_TYPE_CLIENT,
+    SCTP_TYPE_SERVER,
+    SCTP_TYPE_MAX
+};
+
 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;
+
     /* Socket descriptor of connection */
     int sd;
 
+    /* local port used */
+    uint16_t local_port;
+
     /* IN/OUT streams */
     uint16_t in_streams;
     uint16_t out_streams;
@@ -113,6 +126,7 @@ void sctp_handle_new_association_req(
     struct sctp_event_subscribe events;
 
     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.
@@ -166,6 +180,8 @@ void sctp_handle_new_association_req(
      * 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 +
@@ -228,10 +244,31 @@ void sctp_handle_new_association_req(
                 return;
             }
         }
+    } 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);
+
+        if (bind(sd, (struct sockaddr*)&addr6, sizeof(addr6)) < 0) {
+            SCTP_ERROR("Failed to bind the socket %d 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;
@@ -283,6 +320,72 @@ void sctp_send_data(instance_t instance, task_id_t task_id, sctp_data_req_t *sct
                sctp_cnx->assoc_id);
 }
 
+static
+inline void sctp_eNB_accept_associations(struct sctp_cnx_list_elm_s *sctp_cnx)
+{
+    int client_sd;
+
+    struct sockaddr 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, &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 = ((struct sockaddr_in6*)&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)
 {
@@ -387,7 +490,11 @@ void sctp_eNB_flush_sockets(struct epoll_event *events, int nb_events)
             continue;
         }
         SCTP_DEBUG("Found data for descriptor %d\n", events[i].data.fd);
-        sctp_eNB_read_from_socket(sctp_cnx);
+        if (sctp_cnx->connection_type == SCTP_TYPE_CLIENT) {
+            sctp_eNB_read_from_socket(sctp_cnx);
+        } else {
+            sctp_eNB_accept_associations(sctp_cnx);
+        }
     }
 }
 
diff --git a/openair2/COMMON/sctp_messages_def.h b/openair2/COMMON/sctp_messages_def.h
index c009e39638..4c3450e18d 100644
--- a/openair2/COMMON/sctp_messages_def.h
+++ b/openair2/COMMON/sctp_messages_def.h
@@ -1,5 +1,6 @@
 MESSAGE_DEF(SCTP_NEW_ASSOCIATION_REQ , MESSAGE_PRIORITY_MED, sctp_new_association_req_t          , sctp_new_association_req)
 MESSAGE_DEF(SCTP_NEW_ASSOCIATION_RESP, MESSAGE_PRIORITY_MED, sctp_new_association_resp_t         , sctp_new_association_resp)
+MESSAGE_DEF(SCTP_NEW_ASSOCIATION_IND , MESSAGE_PRIORITY_MED, sctp_new_association_ind_t          , sctp_new_association_ind)
 MESSAGE_DEF(SCTP_REGISTER_UPPER_LAYER, MESSAGE_PRIORITY_MED, sctp_listener_register_upper_layer_t, sctp_listener_register_upper_layer)
 MESSAGE_DEF(SCTP_DATA_REQ,             MESSAGE_PRIORITY_MED, sctp_data_req_t                     , sctp_data_req)
 MESSAGE_DEF(SCTP_DATA_IND,             MESSAGE_PRIORITY_MED, sctp_data_ind_t                     , sctp_data_ind)
\ No newline at end of file
diff --git a/openair2/COMMON/sctp_messages_types.h b/openair2/COMMON/sctp_messages_types.h
index 1c2dc9199c..96080a0b0a 100644
--- a/openair2/COMMON/sctp_messages_types.h
+++ b/openair2/COMMON/sctp_messages_types.h
@@ -23,6 +23,18 @@ typedef struct {
     net_ip_address_t remote_address;
 } sctp_new_association_req_t;
 
+typedef struct {
+    /* Assoc id of the new association */
+    int32_t  assoc_id;
+
+    /* The port used by remote host */
+    uint16_t port;
+
+    /* Number of streams used for this association */
+    uint16_t in_streams;
+    uint16_t out_streams;
+} sctp_new_association_ind_t;
+
 typedef struct {
     /* Upper layer connexion identifier */
     uint16_t ulp_cnx_id;
-- 
2.26.2