sctp_eNB_task.c 27.2 KB
Newer Older
1
/*******************************************************************************
gauthier's avatar
GPLv3    
gauthier committed
2
3
    OpenAirInterface
    Copyright(c) 1999 - 2014 Eurecom
4

gauthier's avatar
GPLv3    
gauthier committed
5
6
7
8
    OpenAirInterface is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
9
10


gauthier's avatar
GPLv3    
gauthier committed
11
12
13
14
    OpenAirInterface is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
15

gauthier's avatar
GPLv3    
gauthier committed
16
17
18
19
    You should have received a copy of the GNU General Public License
    along with OpenAirInterface.The full GNU General Public License is
   included in this distribution in the file called "COPYING". If not,
   see <http://www.gnu.org/licenses/>.
20
21

  Contact Information
gauthier's avatar
GPLv3    
gauthier committed
22
23
24
25
26
  OpenAirInterface Admin: openair_admin@eurecom.fr
  OpenAirInterface Tech : openair_tech@eurecom.fr
  OpenAirInterface Dev  : openair4g-devel@eurecom.fr

  Address      : Eurecom, Compus SophiaTech 450, route des chappes, 06451 Biot, France.
27

gauthier's avatar
GPLv3    
gauthier committed
28
 *******************************************************************************/
29
30
31
32
33

#include <pthread.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
34
35
#include <unistd.h>
#include <fcntl.h>
36
37
38

#include <sys/types.h>
#include <sys/socket.h>
gauthier's avatar
gauthier committed
39
40
41
42
43
#include <netdb.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <sys/ioctl.h>
#include <net/if.h>
44
45
46
47
48
49
50
51
52
53
54

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

#include <arpa/inet.h>

#include "assertions.h"
#include "queue.h"

#include "intertask_interface.h"

55
#include "sctp_default_values.h"
56
57
58
#include "sctp_common.h"
#include "sctp_eNB_itti_messaging.h"

gauthier's avatar
   
gauthier committed
59
60
61
62
63
64
65
66
67
/* 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)


68
enum sctp_connection_type_e {
69
70
71
  SCTP_TYPE_CLIENT,
  SCTP_TYPE_SERVER,
  SCTP_TYPE_MAX
72
73
};

gauthier's avatar
   
gauthier committed
74
typedef struct sctp_cnx_list_elm_s {
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
  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
gauthier's avatar
   
gauthier committed
95
} sctp_cnx_list_elm_t;
96
97
98
99
100


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

gauthier's avatar
   
gauthier committed
101

102
103
104
struct sctp_cnx_list_elm_s *sctp_get_cnx(int32_t assoc_id, int sd)
{
  struct sctp_cnx_list_elm_s *elm;
105

106
107
108
109
110
111
112
113
114
  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;
      }
115
    }
116
  }
117

118
  return NULL;
119
120
}

gauthier's avatar
gauthier committed
121
122
void
sctp_handle_new_association_req(
123
124
125
  const instance_t instance,
  const task_id_t requestor,
  const sctp_new_association_req_t * const sctp_new_association_req_p)
126
{
127
128
  int                           sd;
  int32_t                       assoc_id = 0;
129

130
  struct sctp_event_subscribe   events;
131

132
133
  struct sctp_cnx_list_elm_s   *sctp_cnx = NULL;
  enum sctp_connection_type_e   connection_type = SCTP_TYPE_CLIENT;
134

135
136
137
138
139
140
141
142
143
  struct ifreq                  ifr;
  struct ifaddrs               *ifaddr, *ifa;
  int                           family, s;
  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);
144

145
  /* Create new socket with IPv6 affinity */
gauthier's avatar
   
gauthier committed
146
#ifdef NO_VIRTUAL_MACHINE
147
148
149
150

  // 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
gauthier's avatar
typo    
gauthier committed
151
  // with AF_INET (the working IPv4 address can be the last in the list...)
152
  if ((sd = socket(AF_INET6, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
gauthier's avatar
   
gauthier committed
153
#else
154

155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
  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_IN_STREAMS, SCTP_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 */
  memset((void *)&events, 1, sizeof(struct sctp_event_subscribe));

  if (setsockopt(sd, IPPROTO_SCTP, SCTP_EVENTS, &events,
                 sizeof(struct sctp_event_subscribe)) < 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);
203

204
205
206
207
208
      if (s > 0 ) {
        if (((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr == in.s_addr) {
#if 0
          memset(&ifr, 0, sizeof(ifr));
          snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s",ifa->ifa_name);
209

210
211
212
213
214
215
216
          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);
          }
217

thomasl's avatar
thomasl committed
218
#else
219
220
221
222
223
224
225
226
227
228
229
230
231
          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));
          }

thomasl's avatar
thomasl committed
232
#endif
233
          break;
thomasl's avatar
thomasl committed
234

235
236
237
238
239
        }
      }
    } else if (sctp_new_association_req_p->local_address.ipv6 && family == AF_INET6) {
      // compare address
      s = inet_pton(AF_INET6,
gauthier's avatar
gauthier committed
240
241
                    sctp_new_association_req_p->local_address.ipv6_address,
                    &in6);
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257

      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;
gauthier's avatar
gauthier committed
258
        }
259
      }
gauthier's avatar
gauthier committed
260
    }
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
  }

  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;
      }
gauthier's avatar
gauthier committed
295

296
297
298
      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);
gauthier's avatar
gauthier committed
299

300
301
302
303
304
305
306
307
308
309
310
      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);
311
        close(sd);
312
        return;
313
      }
314

315
316
317
      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);
318

319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
      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);
      }
340
    } else {
341
342
343
344
345
346
347
348
349
      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;
350

351
    connection_type = SCTP_TYPE_SERVER;
352

353
354
355
356
    /* 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);
357

358
359
360
361
362
    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;
363
    }
364
  }
365

366
  sctp_cnx = calloc(1, sizeof(*sctp_cnx));
367

368
  sctp_cnx->connection_type = connection_type;
369

370
371
372
373
374
375
  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;
376

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

381
382
  SCTP_DEBUG("Inserted new descriptor for sd %d in list, nb elements %u, assoc_id %d\n",
             sd, sctp_nb_cnx, assoc_id);
383
384
}

gauthier's avatar
   
gauthier committed
385
void sctp_send_data(
386
387
388
  instance_t       instance,
  task_id_t        task_id,
  sctp_data_req_t *sctp_data_req_p)
389
{
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
  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);
425
426
}

gauthier's avatar
gauthier committed
427
static int sctp_close_association(
428
429
430
  const instance_t instance,
  const task_id_t  requestor,
  sctp_close_association_t     *close_association_p)
gauthier's avatar
gauthier committed
431
432
{

433
  struct sctp_cnx_list_elm_s *sctp_cnx = NULL;
gauthier's avatar
gauthier committed
434

435
436
  DevAssert(close_association_p != NULL);
  sctp_cnx = sctp_get_cnx(close_association_p->assoc_id, 0);
gauthier's avatar
gauthier committed
437

438
439
440
441
442
443
444
445
446
447
448
449
450
  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, sctp_cnx->sd);
  }

  return 0;
gauthier's avatar
gauthier committed
451
452
}

gauthier's avatar
   
gauthier committed
453
static int sctp_create_new_listener(
454
455
456
  const instance_t instance,
  const task_id_t  requestor,
  sctp_init_t     *init_p)
gauthier's avatar
   
gauthier committed
457
{
458
459
460
461
462
463
464
465
466
467
468
469
470
471
  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;
  }
gauthier's avatar
   
gauthier committed
472

473
474
475
476
  if ((used_addresses = init_p->nb_ipv4_addr + init_p->nb_ipv6_addr) == 0) {
    SCTP_WARN("No address provided...\n");
    return -1;
  }
gauthier's avatar
   
gauthier committed
477

478
  addr = calloc(used_addresses, sizeof(struct sockaddr));
gauthier's avatar
   
gauthier committed
479

480
  SCTP_DEBUG("Creating new listen socket on port %u with\n", init_p->port);
gauthier's avatar
   
gauthier committed
481

482
483
  if (init_p->ipv4 == 1) {
    struct sockaddr_in *ip4_addr;
gauthier's avatar
   
gauthier committed
484

485
    SCTP_DEBUG("ipv4 addresses:\n");
gauthier's avatar
   
gauthier committed
486

487
488
489
490
491
492
493
    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];
gauthier's avatar
   
gauthier committed
494
    }
495
  }
gauthier's avatar
   
gauthier committed
496

497
498
  if (init_p->ipv6 == 1) {
    struct sockaddr_in6 *ip6_addr;
gauthier's avatar
   
gauthier committed
499

500
    SCTP_DEBUG("ipv6 addresses:\n");
gauthier's avatar
   
gauthier committed
501

502
503
504
505
506
    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);
gauthier's avatar
   
gauthier committed
507

508
509
510
511
512
      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]);
      }
gauthier's avatar
   
gauthier committed
513
    }
514
515
516
517
  }

  if ((sd = socket(AF_INET6, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
    SCTP_ERROR("socket: %s:%d\n", strerror(errno), errno);
gauthier's avatar
   
gauthier committed
518
    return -1;
519
  }
gauthier's avatar
   
gauthier committed
520

521
  memset((void *)&event, 1, sizeof(struct sctp_event_subscribe));
522

523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
  if (setsockopt(sd, IPPROTO_SCTP, SCTP_EVENTS, &event,
                 sizeof(struct sctp_event_subscribe)) < 0) {
    SCTP_ERROR("setsockopt: %s:%d\n", strerror(errno), errno);
    return -1;
  }

  sctp_cnx = calloc(1, sizeof(*sctp_cnx));

  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);
    return -1;
  }
553

554
555
556
557
  if (listen(sd, 5) < 0) {
    SCTP_ERROR("listen: %s:%d\n", strerror(errno), errno);
    return -1;
  }
558

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

563
564
  /* Add the socket to list of fd monitored by ITTI */
  itti_subscribe_event_fd(TASK_SCTP, sd);
565

566
567
  return sd;
err:
568

569
570
571
572
  if (sd != -1) {
    close(sd);
    sd = -1;
  }
573

574
575
  return -1;
}
576

577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
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;
    }
608

609
    new_cnx = calloc(1, sizeof(*sctp_cnx));
610

611
    DevAssert(new_cnx != NULL);
612

613
    new_cnx->connection_type = SCTP_TYPE_CLIENT;
614

615
616
617
618
619
620
    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;
621

622
623
624
625
626
627
    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;
628
    }
629
630
631
632
633
634
635
636
637
638
639
640

    /* 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);
  }
641
642
}

gauthier's avatar
gauthier committed
643
644
645
static inline
void
sctp_eNB_read_from_socket(
646
  struct sctp_cnx_list_elm_s *sctp_cnx)
647
{
648
649
650
  int                    flags = 0, n;
  socklen_t              from_len;
  struct sctp_sndrcvinfo sinfo;
651

652
653
  struct sockaddr_in addr;
  uint8_t buffer[SCTP_RECV_BUFFER_SIZE];
654

655
  DevAssert(sctp_cnx != NULL);
656

657
658
659
  memset((void *)&addr, 0, sizeof(struct sockaddr_in));
  from_len = (socklen_t)sizeof(struct sockaddr_in);
  memset((void *)&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
660

661
662
663
  n = sctp_recvmsg(sctp_cnx->sd, (void *)buffer, SCTP_RECV_BUFFER_SIZE,
                   (struct sockaddr *)&addr, &from_len,
                   &sinfo, &flags);
664

665
666
667
  if (n < 0) {
    if (errno == ENOTCONN) {
      itti_unsubscribe_event_fd(TASK_SCTP, sctp_cnx->sd);
668

669
      SCTP_DEBUG("Received not connected for sd %d\n", sctp_cnx->sd);
670

671
672
673
674
675
676
677
678
679
680
681
      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);
682
683
    }

684
685
686
687
688
    return;
  } else if (n == 0) {
    SCTP_DEBUG("return of sctp_recvmsg is 0...\n");
    return;
  }
689

690
691
692
  if (flags & MSG_NOTIFICATION) {
    union sctp_notification *snp;
    snp = (union sctp_notification *)buffer;
693

694
695
    SCTP_DEBUG("Received notification for sd %d, type %u\n",
               sctp_cnx->sd, snp->sn_header.sn_type);
696

697
698
699
    /* Client deconnection */
    if (SCTP_SHUTDOWN_EVENT == snp->sn_header.sn_type) {
      itti_unsubscribe_event_fd(TASK_SCTP, sctp_cnx->sd);
700

701
      close(sctp_cnx->sd);
702

703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
      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);
728
729
        }

730
731
732
733
734
735
736
737
738
739
        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;
740

741
742
743
      default:
        break;
      }
744
    }
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
  } 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 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));

    sctp_itti_send_new_message_ind(sctp_cnx->task_id,
                                   sinfo.sinfo_assoc_id,
                                   buffer, n, sinfo.sinfo_stream);
  }
765
766
}

gauthier's avatar
gauthier committed
767
768
void
sctp_eNB_flush_sockets(
769
  struct epoll_event *events, int nb_events)
770
{
771
772
  int i;
  struct sctp_cnx_list_elm_s *sctp_cnx = NULL;
773

774
775
776
777
778
779
780
781
782
  if (events == NULL) {
    return;
  }

  for (i = 0; i < nb_events; i++) {
    sctp_cnx = sctp_get_cnx(-1, events[i].data.fd);

    if (sctp_cnx == NULL) {
      continue;
783
784
    }

785
786
787
788
789
790
    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 {
      sctp_eNB_accept_associations(sctp_cnx);
791
    }
792
  }
793
794
}

gauthier's avatar
   
gauthier committed
795

796
797
void *sctp_eNB_task(void *arg)
{
798
799
800
801
802
803
804
805
806
807
808
809
810
  int                 nb_events;
  struct epoll_event *events;
  MessageDef         *received_msg = NULL;
  int                 result;

  SCTP_DEBUG("Starting SCTP layer\n");

  STAILQ_INIT(&sctp_cnx_list);

  itti_mark_task_ready(TASK_SCTP);

  while (1) {
    itti_receive_msg(TASK_SCTP, &received_msg);
811

812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
    /* Check if there is a packet to handle */
    if (received_msg != NULL) {
      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) {
          /* SCTP socket creation or bind failed... */
          SCTP_ERROR("Failed to create new SCTP listener\n");
        }
      }
      break;

      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:
        itti_exit_task();
        break;

      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);
      }
      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);
      }
      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;
862
    }
863
864
865
866
867
868
869

    nb_events = itti_get_events(TASK_SCTP, &events);
    /* Now handle notifications for other sockets */
    sctp_eNB_flush_sockets(events, nb_events);
  }

  return NULL;
870
}