sctp_eNB_task.c 42.2 KB
Newer Older
1 2 3 4 5
/*
 * 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
6
 * the OAI Public License, Version 1.1  (the "License"); you may not use this file
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * 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
 */

22 23 24 25
#include <pthread.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
26 27
#include <unistd.h>
#include <fcntl.h>
28 29 30

#include <sys/types.h>
#include <sys/socket.h>
31 32 33 34 35
#include <netdb.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <sys/ioctl.h>
#include <net/if.h>
36 37 38 39 40 41 42

#include <netinet/in.h>
#include <netinet/sctp.h>

#include <arpa/inet.h>

#include "assertions.h"
laurent's avatar
laurent committed
43
#include "common/utils/system.h"
44 45 46 47
#include "queue.h"

#include "intertask_interface.h"

48
#include "sctp_default_values.h"
49 50
#include "sctp_common.h"
#include "sctp_eNB_itti_messaging.h"
51
#include "msc.h"
52

Lionel Gauthier's avatar
 
Lionel Gauthier committed
53 54 55 56 57 58 59 60 61
/* 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)


62
enum sctp_connection_type_e {
frtabu's avatar
frtabu committed
63 64 65 66
    SCTP_TYPE_CLIENT,
    SCTP_TYPE_SERVER,
    SCTP_TYPE_MULTI_SERVER,
    SCTP_TYPE_MAX
67 68
};

Lionel Gauthier's avatar
 
Lionel Gauthier committed
69
typedef struct sctp_cnx_list_elm_s {
frtabu's avatar
frtabu committed
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
    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
Lionel Gauthier's avatar
 
Lionel Gauthier committed
90
} sctp_cnx_list_elm_t;
91 92 93 94 95


static STAILQ_HEAD(sctp_cnx_list_head, sctp_cnx_list_elm_s) sctp_cnx_list;
static uint16_t sctp_nb_cnx = 0;

96
//------------------------------------------------------------------------------
97 98
struct sctp_cnx_list_elm_s *sctp_get_cnx(int32_t assoc_id, int sd)
{
frtabu's avatar
frtabu committed
99
    struct sctp_cnx_list_elm_s *elm;
100

frtabu's avatar
frtabu committed
101 102 103 104 105 106 107 108 109 110
    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;
            }
        }
111 112
    }

frtabu's avatar
frtabu committed
113
    return NULL;
114 115
}

116 117 118 119
//------------------------------------------------------------------------------
static inline
void
sctp_eNB_accept_associations_multi(
frtabu's avatar
frtabu committed
120
    struct sctp_cnx_list_elm_s *sctp_cnx)
121
{
frtabu's avatar
frtabu committed
122 123 124 125 126
    int                    ns;
    int                    n;
    int                    flags = 0;
    socklen_t              from_len;
    struct sctp_sndrcvinfo sinfo;
127

frtabu's avatar
frtabu committed
128 129
    struct sockaddr_in addr;
    uint8_t buffer[SCTP_RECV_BUFFER_SIZE];
130

frtabu's avatar
frtabu committed
131
    DevAssert(sctp_cnx != NULL);
132

frtabu's avatar
frtabu committed
133 134 135
    memset((void *)&addr, 0, sizeof(struct sockaddr_in));
    from_len = (socklen_t)sizeof(struct sockaddr_in);
    memset((void *)&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
136

frtabu's avatar
frtabu committed
137 138 139
    n = sctp_recvmsg(sctp_cnx->sd, (void *)buffer, SCTP_RECV_BUFFER_SIZE,
                     (struct sockaddr *)&addr, &from_len,
                     &sinfo, &flags);
140

frtabu's avatar
frtabu committed
141 142 143 144 145 146 147 148 149
    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;
150 151
    }

frtabu's avatar
frtabu committed
152 153 154
    if (flags & MSG_NOTIFICATION) {
        union sctp_notification *snp;
        snp = (union sctp_notification *)buffer;
155

frtabu's avatar
frtabu committed
156 157
        SCTP_DEBUG("Received notification for sd %d, type %u\n",
                   sctp_cnx->sd, snp->sn_header.sn_type);
158

frtabu's avatar
frtabu committed
159 160 161 162
        /* 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;
163

frtabu's avatar
frtabu committed
164
            SCTP_DEBUG("Client association changed: %d\n", sctp_assoc_changed->sac_state);
165

frtabu's avatar
frtabu committed
166 167 168 169 170 171
            /* 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;
172

frtabu's avatar
frtabu committed
173
                new_cnx = calloc(1, sizeof(*sctp_cnx));
174

frtabu's avatar
frtabu committed
175
                DevAssert(new_cnx != NULL);
176

frtabu's avatar
frtabu committed
177
                new_cnx->connection_type = SCTP_TYPE_CLIENT;
178

frtabu's avatar
frtabu committed
179
                ns = sctp_peeloff(sctp_cnx->sd, sctp_assoc_changed->sac_assoc_id);
180

frtabu's avatar
frtabu committed
181 182 183 184 185 186 187
                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;
188

frtabu's avatar
frtabu committed
189 190 191 192 193 194 195
                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;
                }
196

frtabu's avatar
frtabu committed
197 198 199
                /* Insert new element at end of list */
                STAILQ_INSERT_TAIL(&sctp_cnx_list, new_cnx, entries);
                sctp_nb_cnx++;
200

frtabu's avatar
frtabu committed
201 202
                /* Add the socket to list of fd monitored by ITTI */
                itti_subscribe_event_fd(TASK_SCTP, ns);
203

frtabu's avatar
frtabu committed
204 205 206 207 208
                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;
209

frtabu's avatar
frtabu committed
210 211 212 213 214 215
            default:
                break;
            }
        }
    } else {
        SCTP_DEBUG("No notification from SCTP\n");
216 217 218 219 220 221
    }
}

//------------------------------------------------------------------------------
void
sctp_handle_new_association_req_multi(
frtabu's avatar
frtabu committed
222 223 224
    const instance_t instance,
    const task_id_t requestor,
    const sctp_new_association_req_multi_t * const sctp_new_association_req_p)
225
{
frtabu's avatar
frtabu committed
226 227
    int                           ns;
    int sd;
228

frtabu's avatar
frtabu committed
229
    int32_t                       assoc_id = 0;
230

frtabu's avatar
frtabu committed
231 232
    struct sctp_cnx_list_elm_s   *sctp_cnx = NULL;
    enum sctp_connection_type_e   connection_type = SCTP_TYPE_CLIENT;
233

frtabu's avatar
frtabu committed
234 235 236 237
    /* Prepare a new SCTP association as requested by upper layer and try to connect
     * to remote host.
     */
    DevAssert(sctp_new_association_req_p != NULL);
238

frtabu's avatar
frtabu committed
239
    sd = sctp_new_association_req_p->multi_sd;
240

frtabu's avatar
frtabu committed
241
    /* Create new socket with IPv6 affinity */
242 243
//#warning "SCTP may Force IPv4 only, here"

frtabu's avatar
frtabu committed
244 245
    /* Mark the socket as non-blocking */
    //if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0) {
246
    //SCTP_ERROR("fcntl F_SETFL O_NONBLOCK failed: %s\n",
frtabu's avatar
frtabu committed
247
    //         strerror(errno));
248 249
    //close(sd);
    //return;
frtabu's avatar
frtabu committed
250
    //}
251

frtabu's avatar
frtabu committed
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
    /* 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(1);
            }

            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++;
        }
284

frtabu's avatar
frtabu committed
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
        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(1);
            }

            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);
        }
326 327
    }

Konstantinos Alexandris's avatar
Konstantinos Alexandris committed
328 329 330 331 332 333
    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(1);
    }
334

frtabu's avatar
frtabu committed
335
    sctp_cnx = calloc(1, sizeof(*sctp_cnx));
336

frtabu's avatar
frtabu committed
337
    sctp_cnx->connection_type = connection_type;
338

frtabu's avatar
frtabu committed
339 340 341 342 343 344
    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;
345

frtabu's avatar
frtabu committed
346 347
    /* Add the socket to list of fd monitored by ITTI */
    itti_subscribe_event_fd(TASK_SCTP, ns);
348

frtabu's avatar
frtabu committed
349 350 351
    /* Insert new element at end of list */
    STAILQ_INSERT_TAIL(&sctp_cnx_list, sctp_cnx, entries);
    sctp_nb_cnx++;
352

frtabu's avatar
frtabu committed
353 354
    SCTP_DEBUG("Inserted new descriptor for sd %d in list, nb elements %u, assoc_id %d\n",
               ns, sctp_nb_cnx, assoc_id);
355 356
}

357
//------------------------------------------------------------------------------
Lionel Gauthier's avatar
Lionel Gauthier committed
358 359
void
sctp_handle_new_association_req(
frtabu's avatar
frtabu committed
360 361 362
    const instance_t instance,
    const task_id_t requestor,
    const sctp_new_association_req_t * const sctp_new_association_req_p)
363
{
frtabu's avatar
frtabu committed
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
    int                           sd       = 0;
    int32_t                       assoc_id = 0;

    struct sctp_event_subscribe   events;

    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 */
385
//#warning "SCTP may Force IPv4 only, here"
Lionel Gauthier's avatar
 
Lionel Gauthier committed
386
#ifdef NO_VIRTUAL_MACHINE
387

frtabu's avatar
frtabu committed
388 389 390 391 392
    // 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) {
Lionel Gauthier's avatar
 
Lionel Gauthier committed
393
#else
394

frtabu's avatar
frtabu committed
395
    if ((sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
396
#endif
frtabu's avatar
frtabu committed
397 398
        SCTP_ERROR("Socket creation failed: %s\n", strerror(errno));
        return;
399
    }
frtabu's avatar
frtabu committed
400 401 402 403 404 405 406 407 408 409 410

    /* 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);
411 412 413 414
        close(sd);
        return;
    }

frtabu's avatar
frtabu committed
415
    /* Subscribe to all events */
416 417 418 419 420 421 422
    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;
frtabu's avatar
frtabu committed
423 424

    if (setsockopt(sd, IPPROTO_SCTP, SCTP_EVENTS, &events,
425
                   8) < 0) {
frtabu's avatar
frtabu committed
426 427
        SCTP_ERROR("Setsockopt IPPROTO_SCTP_EVENTS failed: %s\n",
                   strerror(errno));
428
        close(sd);
429
        return;
frtabu's avatar
frtabu committed
430
    }
431

frtabu's avatar
frtabu committed
432 433 434 435 436
    // Bind to device ... or we could bind to address also
    if (getifaddrs(&ifaddr) == -1) {
        SCTP_ERROR("getifaddrs failed: %s\n", strerror(errno));
        close(sd);
    }
437

frtabu's avatar
frtabu committed
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
    /* 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;
                    locaddr.sin_family = AF_INET;
                    locaddr.sin_port = htons(sctp_new_association_req_p->port);
                    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;
                }
            }
        }
494 495
    }

frtabu's avatar
frtabu committed
496 497 498 499 500 501
    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));
502 503 504
        close(sd);
        return;
    }
frtabu's avatar
frtabu committed
505 506 507 508

    /* SOCK_STREAM socket type requires an explicit connect to the remote host
     * address and port.
     * Only use IPv4 for the first connection attempt
509
     */
frtabu's avatar
frtabu committed
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
    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++;
        }
537

frtabu's avatar
frtabu committed
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
        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++;
        }
556

frtabu's avatar
frtabu committed
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
        /* 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;
583

frtabu's avatar
frtabu committed
584
        connection_type = SCTP_TYPE_SERVER;
585

frtabu's avatar
frtabu committed
586 587 588 589
        /* 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);
590
        addr6.sin6_flowinfo = 0;
591

frtabu's avatar
frtabu committed
592 593 594 595 596 597
        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;
        }
598 599
    }

frtabu's avatar
frtabu committed
600
    sctp_cnx = calloc(1, sizeof(*sctp_cnx));
601

frtabu's avatar
frtabu committed
602
    sctp_cnx->connection_type = connection_type;
603

frtabu's avatar
frtabu committed
604 605 606 607 608 609
    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;
610

frtabu's avatar
frtabu committed
611 612 613
    /* Insert new element at end of list */
    STAILQ_INSERT_TAIL(&sctp_cnx_list, sctp_cnx, entries);
    sctp_nb_cnx++;
614

frtabu's avatar
frtabu committed
615 616
    SCTP_DEBUG("Inserted new descriptor for sd %d in list, nb elements %u, assoc_id %d\n",
               sd, sctp_nb_cnx, assoc_id);
617 618
}

frtabu's avatar
frtabu committed
619
//------------------------------------------------------------------------------
Lionel Gauthier's avatar
 
Lionel Gauthier committed
620
void sctp_send_data(
frtabu's avatar
frtabu committed
621 622 623
    instance_t       instance,
    task_id_t        task_id,
    sctp_data_req_t *sctp_data_req_p)
624
{
frtabu's avatar
frtabu committed
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
    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);
660 661
}

662
//------------------------------------------------------------------------------
663
static int sctp_close_association(
frtabu's avatar
frtabu committed
664 665 666
    const instance_t instance,
    const task_id_t  requestor,
    sctp_close_association_t     *close_association_p)
667 668
{

frtabu's avatar
frtabu committed
669
    struct sctp_cnx_list_elm_s *sctp_cnx = NULL;
670

frtabu's avatar
frtabu committed
671 672
    DevAssert(close_association_p != NULL);
    sctp_cnx = sctp_get_cnx(close_association_p->assoc_id, 0);
673

frtabu's avatar
frtabu committed
674 675 676 677 678 679 680 681 682 683 684 685 686
    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;
687 688
}

689
//------------------------------------------------------------------------------
Lionel Gauthier's avatar
 
Lionel Gauthier committed
690
static int sctp_create_new_listener(
frtabu's avatar
frtabu committed
691 692 693 694
    const instance_t instance,
    const task_id_t  requestor,
    sctp_init_t     *init_p,
    int server_type)
Lionel Gauthier's avatar
 
Lionel Gauthier committed
695
{
frtabu's avatar
frtabu committed
696 697 698 699 700 701 702 703 704 705 706 707 708 709
    struct sctp_event_subscribe   event;
    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;
    }
Lionel Gauthier's avatar
 
Lionel Gauthier committed
710

frtabu's avatar
frtabu committed
711 712 713 714
    if ((used_addresses = init_p->nb_ipv4_addr + init_p->nb_ipv6_addr) == 0) {
        SCTP_WARN("No address provided...\n");
        return -1;
    }
Lionel Gauthier's avatar
 
Lionel Gauthier committed
715

frtabu's avatar
frtabu committed
716
    addr = calloc(used_addresses, sizeof(struct sockaddr));
Lionel Gauthier's avatar
 
Lionel Gauthier committed
717

frtabu's avatar
frtabu committed
718
    SCTP_DEBUG("Creating new listen socket on port %u with\n", init_p->port);
Lionel Gauthier's avatar
 
Lionel Gauthier committed
719

frtabu's avatar
frtabu committed
720 721
    if (init_p->ipv4 == 1) {
        struct sockaddr_in *ip4_addr;
Lionel Gauthier's avatar
 
Lionel Gauthier committed
722

frtabu's avatar
frtabu committed
723
        SCTP_DEBUG("ipv4 addresses:\n");
Lionel Gauthier's avatar
 
Lionel Gauthier committed
724

frtabu's avatar
frtabu committed
725
        for (i = 0; i < init_p->nb_ipv4_addr; i++) {
726
            SCTP_DEBUG("\t- "IPV4_ADDR"\n", IPV4_ADDR_FORMAT(init_p->ipv4_address[i]));
frtabu's avatar
frtabu committed
727 728 729 730 731
            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];
        }
Lionel Gauthier's avatar
 
Lionel Gauthier committed
732 733
    }

frtabu's avatar
frtabu committed
734 735
    if (init_p->ipv6 == 1) {
        struct sockaddr_in6 *ip6_addr;
Lionel Gauthier's avatar
 
Lionel Gauthier committed
736

frtabu's avatar
frtabu committed
737
        SCTP_DEBUG("ipv6 addresses:\n");
Lionel Gauthier's avatar
 
Lionel Gauthier committed
738

frtabu's avatar
frtabu committed
739 740 741 742 743
        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);
Lionel Gauthier's avatar
 
Lionel Gauthier committed
744

frtabu's avatar
frtabu committed
745 746 747 748 749 750
            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]);
            }
        }
Lionel Gauthier's avatar
 
Lionel Gauthier committed
751
    }
752

frtabu's avatar
frtabu committed
753 754 755 756 757 758
    if (server_type) {
        if ((sd = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0) {
            SCTP_ERROR("socket: %s:%d\n", strerror(errno), errno);
            free(addr);
            return -1;
        }
759
    }
frtabu's avatar
frtabu committed
760 761 762 763 764 765
    else {
        if ((sd = socket(AF_INET6, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
            SCTP_ERROR("socket: %s:%d\n", strerror(errno), errno);
            free(addr);
            return -1;
        }
766
    }
Lionel Gauthier's avatar
 
Lionel Gauthier committed
767

768 769 770 771 772 773 774
    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;
775

frtabu's avatar
frtabu committed
776
    if (setsockopt(sd, IPPROTO_SCTP, SCTP_EVENTS, &event,
777
                   8) < 0) {
frtabu's avatar
frtabu committed
778
        SCTP_ERROR("setsockopt: %s:%d\n", strerror(errno), errno);
779 780 781 782
        if (sd != -1) {
            close(sd);
            sd = -1;
        }
frtabu's avatar
frtabu committed
783 784 785
        free(addr);
        return -1;
    }
786

frtabu's avatar
frtabu committed
787
    sctp_cnx = calloc(1, sizeof(*sctp_cnx));
788

frtabu's avatar
frtabu committed
789 790 791 792 793 794
    if (server_type) {
        sctp_cnx->connection_type = SCTP_TYPE_MULTI_SERVER;
    }
    else {
        sctp_cnx->connection_type = SCTP_TYPE_SERVER;
    }
795

frtabu's avatar
frtabu committed
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
    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;
    }
812

frtabu's avatar
frtabu committed
813 814
    if (sctp_bindx(sd, addr, used_addresses, SCTP_BINDX_ADD_ADDR) != 0) {
        SCTP_ERROR("sctp_bindx: %s:%d\n", strerror(errno), errno);
815 816
        free(sctp_cnx);
        sctp_cnx = NULL;
frtabu's avatar
frtabu committed
817 818 819 820 821
        return -1;
    }

    if (listen(sd, 5) < 0) {
        SCTP_ERROR("listen: %s:%d\n", strerror(errno), errno);
822 823
        free(sctp_cnx);
        sctp_cnx = NULL;
frtabu's avatar
frtabu committed
824 825 826 827 828 829
        return -1;
    }

    /* Insert new element at end of list */
    STAILQ_INSERT_TAIL(&sctp_cnx_list, sctp_cnx, entries);
    sctp_nb_cnx++;
830

frtabu's avatar
frtabu committed
831 832
    /* Add the socket to list of fd monitored by ITTI */
    itti_subscribe_event_fd(TASK_SCTP, sd);
833

frtabu's avatar
frtabu committed
834
    return sd;
835
err:
836

frtabu's avatar
frtabu committed
837 838 839 840
    if (sd != -1) {
        close(sd);
        sd = -1;
    }
841

842 843 844 845 846
    if (sctp_cnx != NULL) {
        free(sctp_cnx);
        sctp_cnx = NULL;
    }

847 848 849 850 851
    if (addr != NULL) {
        free(addr);
        addr = NULL;
    }

frtabu's avatar
frtabu committed
852
    return -1;
853
}
854

855
//------------------------------------------------------------------------------
856 857 858
static inline
void
sctp_eNB_accept_associations(
frtabu's avatar
frtabu committed
859
    struct sctp_cnx_list_elm_s *sctp_cnx)
860
{
frtabu's avatar
frtabu committed
861 862 863
    int             client_sd;
    struct sockaddr saddr;
    socklen_t       saddr_size;
864

frtabu's avatar
frtabu committed
865
    DevAssert(sctp_cnx != NULL);
866

frtabu's avatar
frtabu committed
867
    saddr_size = sizeof(saddr);
868

frtabu's avatar
frtabu committed
869 870 871 872 873 874 875
    /* 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;
876

frtabu's avatar
frtabu committed
877 878
        /* This is an ipv6 socket */
        port = ((struct sockaddr_in6*)&saddr)->sin6_port;
879

frtabu's avatar
frtabu committed
880 881 882 883 884 885 886
        /* 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;
        }
887

frtabu's avatar
frtabu committed
888
        new_cnx = calloc(1, sizeof(*sctp_cnx));
889

frtabu's avatar
frtabu committed
890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
        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;
        }
908

frtabu's avatar
frtabu committed
909 910 911 912 913 914
        /* 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);
915

frtabu's avatar
frtabu committed
916 917 918 919
        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);
    }
920 921
}

922
//------------------------------------------------------------------------------
Lionel Gauthier's avatar
Lionel Gauthier committed
923 924 925
static inline
void
sctp_eNB_read_from_socket(
frtabu's avatar
frtabu committed
926
    struct sctp_cnx_list_elm_s *sctp_cnx)
927
{
frtabu's avatar
frtabu committed
928 929 930
    int                    flags = 0, n;
    socklen_t              from_len;
    struct sctp_sndrcvinfo sinfo;
931

frtabu's avatar
frtabu committed
932 933
    struct sockaddr_in addr;
    uint8_t buffer[SCTP_RECV_BUFFER_SIZE];
934

frtabu's avatar
frtabu committed
935
    DevAssert(sctp_cnx != NULL);
936

frtabu's avatar
frtabu committed
937 938 939
    memset((void *)&addr, 0, sizeof(struct sockaddr_in));
    from_len = (socklen_t)sizeof(struct sockaddr_in);
    memset((void *)&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
940

frtabu's avatar
frtabu committed
941 942 943
    n = sctp_recvmsg(sctp_cnx->sd, (void *)buffer, SCTP_RECV_BUFFER_SIZE,
                     (struct sockaddr *)&addr, &from_len,
                     &sinfo, &flags);
944

frtabu's avatar
frtabu committed
945 946 947
    if (n < 0) {
        if (errno == ENOTCONN) {
            itti_unsubscribe_event_fd(TASK_SCTP, sctp_cnx->sd);
948

frtabu's avatar
frtabu committed
949
            SCTP_DEBUG("Received not connected for sd %d\n", sctp_cnx->sd);
950

frtabu's avatar
frtabu committed
951 952 953
            sctp_itti_send_association_resp(
                sctp_cnx->task_id, sctp_cnx->instance, -1,
                sctp_cnx->cnx_id, SCTP_STATE_UNREACHABLE, 0, 0);
954

frtabu's avatar
frtabu committed
955 956 957 958 959 960 961 962
            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);
        }
963

frtabu's avatar
frtabu committed
964 965 966 967 968
        return;
    } else if (n == 0) {
        SCTP_DEBUG("return of sctp_recvmsg is 0...\n");
        return;
    }
969

970 971 972 973 974
    if (!(flags & MSG_EOR)) {
      SCTP_ERROR("fatal: partial SCTP messages are not handled\n");
      exit(1);
    }

frtabu's avatar
frtabu committed
975 976 977
    if (flags & MSG_NOTIFICATION) {
        union sctp_notification *snp;
        snp = (union sctp_notification *)buffer;
978

frtabu's avatar
frtabu committed
979 980
        SCTP_DEBUG("Received notification for sd %d, type %u\n",
                   sctp_cnx->sd, snp->sn_header.sn_type);
981

frtabu's avatar
frtabu committed
982 983 984
        /* Client deconnection */
        if (SCTP_SHUTDOWN_EVENT == snp->sn_header.sn_type) {
            itti_unsubscribe_event_fd(TASK_SCTP, sctp_cnx->sd);
985

frtabu's avatar
frtabu committed
986
            close(sctp_cnx->sd);
987

frtabu's avatar
frtabu committed
988 989 990 991
            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);
992

frtabu's avatar
frtabu committed
993 994
            STAILQ_REMOVE(&sctp_cnx_list, sctp_cnx, sctp_cnx_list_elm_s, entries);
            sctp_nb_cnx--;
995

frtabu's avatar
frtabu committed
996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
            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:
                SCTP_WARN("unhandled: SCTP_ASSOC_CHANGE to %d\n", sctp_assoc_changed->sac_state);
                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);
1041 1042
        }

frtabu's avatar
frtabu committed
1043 1044 1045
        SCTP_DEBUG("[%d][%d] Msg of length %d received from port %u, on stream %d, PPID %d\n",
                   sinfo.sinfo_assoc_id, sctp_cnx->sd, n, ntohs(addr.sin_port),
                   sinfo.sinfo_stream, ntohl(sinfo.sinfo_ppid));
1046

frtabu's avatar
frtabu committed
1047
        sctp_itti_send_new_message_ind(sctp_cnx->task_id,
1048
                                       sctp_cnx->instance,
frtabu's avatar
frtabu committed
1049
                                       sinfo.sinfo_assoc_id,
1050 1051 1052
                                       buffer,
                                       n,
                                       sinfo.sinfo_stream);
1053
    }
1054 1055
}

1056
//------------------------------------------------------------------------------
Lionel Gauthier's avatar
Lionel Gauthier committed
1057 1058
void
sctp_eNB_flush_sockets(
frtabu's avatar
frtabu committed
1059
    struct epoll_event *events, int nb_events)
1060
{
frtabu's avatar
frtabu committed
1061 1062
    int i;
    struct sctp_cnx_list_elm_s *sctp_cnx = NULL;
1063

frtabu's avatar
frtabu committed
1064 1065 1066
    if (events == NULL) {
        return;
    }
1067

frtabu's avatar
frtabu committed
1068 1069
    for (i = 0; i < nb_events; i++) {
        sctp_cnx = sctp_get_cnx(-1, events[i].data.fd);
1070

frtabu's avatar
frtabu committed
1071 1072 1073
        if (sctp_cnx == NULL) {
            continue;
        }
1074

frtabu's avatar
frtabu committed
1075
        SCTP_DEBUG("Found data for descriptor %d\n", events[i].data.fd);
1076

frtabu's avatar
frtabu committed
1077 1078 1079 1080 1081 1082 1083 1084 1085
        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);
        }
1086 1087 1088
    }
}

1089
//------------------------------------------------------------------------------
laurent's avatar
laurent committed
1090
void sctp_eNB_init(void)
1091
{
frtabu's avatar
frtabu committed
1092
    SCTP_DEBUG("Starting SCTP layer\n");
1093

frtabu's avatar
frtabu committed
1094
    STAILQ_INIT(&sctp_cnx_list);
1095

frtabu's avatar
frtabu committed
1096 1097
    itti_mark_task_ready(TASK_SCTP);
    MSC_START_USE();
1098

laurent's avatar
laurent committed
1099
}
1100

laurent's avatar
laurent committed
1101 1102 1103 1104 1105 1106 1107
//------------------------------------------------------------------------------
void *sctp_eNB_process_itti_msg(void *notUsed)
{
    int                 nb_events;
    struct epoll_event *events;
    MessageDef         *received_msg = NULL;
    int                 result;
1108

1109
    itti_receive_msg(TASK_SCTP, &received_msg);
1110

1111 1112
    /* Check if there is a packet to handle */
    if (received_msg != NULL) {
frtabu's avatar
frtabu committed
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124
        switch (ITTI_MSG_ID(received_msg)) {
        case SCTP_INIT_MSG: {
            SCTP_DEBUG("Received SCTP_INIT_MSG\n");

            /* We received a new connection request */
            if (sctp_create_new_listener(
                        ITTI_MESSAGE_GET_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");
            }
1125
        }
frtabu's avatar
frtabu committed
1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142
        break;

        case SCTP_INIT_MSG_MULTI_REQ: {
            int multi_sd;

            SCTP_DEBUG("Received SCTP_INIT_MSG_MULTI_REQ\n");

            multi_sd = sctp_create_new_listener(
                           ITTI_MESSAGE_GET_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(
1143 1144 1145
                ITTI_MSG_ORIGIN_ID(received_msg),
                ITTI_MESSAGE_GET_INSTANCE(received_msg),
                multi_sd);
frtabu's avatar
frtabu committed
1146
        }
1147 1148
        break;

frtabu's avatar
frtabu committed
1149 1150 1151 1152 1153
        case SCTP_NEW_ASSOCIATION_REQ: {
            sctp_handle_new_association_req(ITTI_MESSAGE_GET_INSTANCE(received_msg),
                                            ITTI_MSG_ORIGIN_ID(received_msg),
                                            &received_msg->ittiMsg.sctp_new_association_req);
        }
1154 1155
        break;

frtabu's avatar
frtabu committed
1156 1157 1158 1159 1160 1161
        case SCTP_NEW_ASSOCIATION_REQ_MULTI: {
            sctp_handle_new_association_req_multi(ITTI_MESSAGE_GET_INSTANCE(received_msg),
                                                  ITTI_MSG_ORIGIN_ID(received_msg),
                                                  &received_msg->ittiMsg.sctp_new_association_req_multi);
        }
        break;
1162

frtabu's avatar
frtabu committed
1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178
        case SCTP_CLOSE_ASSOCIATION:
            sctp_close_association(ITTI_MESSAGE_GET_INSTANCE(received_msg),
                                   ITTI_MSG_ORIGIN_ID(received_msg),
                                   &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(ITTI_MESSAGE_GET_INSTANCE(received_msg),
                           ITTI_MSG_ORIGIN_ID(received_msg),
                           &received_msg->ittiMsg.sctp_data_req);
        }
1179 1180
        break;

frtabu's avatar
frtabu committed
1181 1182 1183 1184 1185 1186 1187 1188 1189
        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;
1190
    }
1191 1192 1193 1194

    nb_events = itti_get_events(TASK_SCTP, &events);
    /* Now handle notifications for other sockets */
    sctp_eNB_flush_sockets(events, nb_events);
laurent's avatar
laurent committed
1195 1196 1197

    return NULL;
}
1198

laurent's avatar
laurent committed
1199 1200 1201
//------------------------------------------------------------------------------
void *sctp_eNB_task(void *arg)
{
frtabu's avatar
frtabu committed
1202
    sctp_eNB_init();
laurent's avatar
laurent committed
1203

frtabu's avatar
frtabu committed
1204 1205 1206
    while (1) {
        (void) sctp_eNB_process_itti_msg(NULL);
    }
1207

frtabu's avatar
frtabu committed
1208
    return NULL;
1209
}