play_scenario_fsm.c 16.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
/*******************************************************************************
    OpenAirInterface
    Copyright(c) 1999 - 2014 Eurecom

    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.


    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.

    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/>.

  Contact Information
  OpenAirInterface Admin: openair_admin@eurecom.fr
  OpenAirInterface Tech : openair_tech@eurecom.fr
  OpenAirInterface Dev  : openair4g-devel@lists.eurecom.fr

  Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE

 *******************************************************************************/

/*
                                play_scenario_fsm.c
                                -------------------
  AUTHOR  : Lionel GAUTHIER
  COMPANY : EURECOM
  EMAIL   : Lionel.Gauthier@eurecom.fr
 */
#include <stdio.h>
38
#include <sys/time.h>
39
#include <pthread.h>
40 41 42 43 44

#include "intertask_interface.h"
#include "platform_types.h"
#include "assertions.h"
#include "play_scenario.h"
45 46 47
#include "s1ap_ies_defs.h"
#include "play_scenario_s1ap_eNB_defs.h"
#include "timer.h"
48

49
//------------------------------------------------------------------------------
50 51 52
et_scenario_t    *g_scenario  = NULL;
pthread_mutex_t   g_fsm_lock  = PTHREAD_MUTEX_INITIALIZER;
et_fsm_state_t    g_fsm_state = ET_FSM_STATE_NULL;
53
uint32_t          g_constraints = ET_BIT_MASK_MATCH_SCTP_STREAM | ET_BIT_MASK_MATCH_SCTP_SSN;
54
//------------------------------------------------------------------------------
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
int timeval_subtract (struct timeval * const result, struct timeval * const a, struct timeval * const b)
{
  struct timeval  b2;
  b2.tv_sec   = b->tv_sec;
  b2.tv_usec = b->tv_usec;

  /* Perform the carry for the later subtraction by updating y. */
  if (a->tv_usec < b2.tv_usec) {
    int nsec = (b2.tv_usec - a->tv_usec) / 1000000 + 1;
    b2.tv_usec -= 1000000 * nsec;
    b2.tv_sec += nsec;
  }
  if (a->tv_usec - b2.tv_usec > 1000000) {
    int nsec = (a->tv_usec - b2.tv_usec) / 1000000;
    b2.tv_usec += 1000000 * nsec;
    b2.tv_sec -= nsec;
  }

  /* Compute the time remaining to wait.
     tv_usec is certainly positive. */
  result->tv_sec = a->tv_sec - b2.tv_sec;
  result->tv_usec = a->tv_usec - b2.tv_usec;

  /* Return 1 if result is negative. */
  return a->tv_sec < b2.tv_sec;
}


//------------------------------------------------------------------------------
void et_scenario_wait_rx_packet(et_packet_t * const packet)
{
  if (timer_setup (ET_FSM_STATE_WAITING_RX_EVENT_DELAY_SEC, 0, TASK_S1AP, INSTANCE_DEFAULT, TIMER_ONE_SHOT,
                   NULL, &packet->timer_id) < 0) {
    AssertFatal(0, " Can not start waiting RX event timer\n");
  }
90
  g_fsm_state = ET_FSM_STATE_WAITING_EVENT;
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
  packet->status = ET_PACKET_STATUS_SCHEDULED_FOR_RECEIVING;
}
//------------------------------------------------------------------------------
void et_scenario_schedule_tx_packet(et_packet_t * const packet)
{
  s1ap_eNB_instance_t *s1ap_eNB_instance = NULL;
  struct timeval  now                   = { .tv_sec = 0, .tv_usec = 0 };
  struct timeval  offset_last_tx_packet = { .tv_sec = 0, .tv_usec = 0 };
  struct timeval  offset_last_rx_packet = { .tv_sec = 0, .tv_usec = 0 };
  struct timeval  offset_tx_rx          = { .tv_sec = 0, .tv_usec = 0 };
  struct timeval  offset                = { .tv_sec = 0, .tv_usec = 0 };
  int             last_packet_was_tx    = 0;
  int             we_are_too_early      = 0;

  AssertFatal(NULL != packet, "packet argument is NULL");
  s1ap_eNB_instance = et_s1ap_eNB_get_instance(packet->enb_instance);
  AssertFatal(NULL != s1ap_eNB_instance, "Cannot get s1ap_eNB_instance_t for eNB instance %d", packet->enb_instance);

109 110
  LOG_D(ENB_APP, "%s\n", __FUNCTION__);
  g_fsm_state = ET_FSM_STATE_WAITING_EVENT;
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

  switch (packet->sctp_hdr.chunk_type) {
    case SCTP_CID_DATA:
      // check if we can send it now
      // TODO: BUG we have to discard in scenario all packets that cannot be processed (SACK, COOKIEs, etc)
      AssertFatal(gettimeofday(&now, NULL) == 0, "gettimeofday failed");
      timeval_subtract(&offset_last_tx_packet,&now,&g_scenario->time_last_tx_packet);
      timeval_subtract(&offset_last_rx_packet,&now,&g_scenario->time_last_rx_packet);
      last_packet_was_tx = timeval_subtract(&offset_tx_rx,&offset_last_tx_packet,&offset_last_rx_packet);
      if (last_packet_was_tx) {
        we_are_too_early = timeval_subtract(&offset,&offset_last_tx_packet,&packet->time_relative_to_last_sent_packet);
      } else {
        we_are_too_early = timeval_subtract(&offset,&offset_last_rx_packet,&packet->time_relative_to_last_received_packet);
      }
      if (we_are_too_early > 0) {
        // set timer
127 128 129
        LOG_D(ENB_APP, "Send packet num %u original frame number %u in %ld.%d sec\n",
            packet->packet_number, packet->original_frame_number, offset.tv_sec, offset.tv_usec);

130 131 132 133 134
        packet->status = ET_PACKET_STATUS_SCHEDULED_FOR_SENDING;
        if (timer_setup (offset.tv_sec, offset.tv_usec, TASK_S1AP, INSTANCE_DEFAULT, TIMER_ONE_SHOT,
                         NULL, &packet->timer_id) < 0) {
          AssertFatal(0, " Can not start TX event timer\n");
        }
135
        // Done g_fsm_state = ET_FSM_STATE_WAITING_TX_EVENT;
136
      } else {
137
        LOG_D(ENB_APP, "Send packet num %u original frame number %u immediately\n", packet->packet_number, packet->original_frame_number);
138
        // send immediately
139
        AssertFatal(0 == gettimeofday(&packet->timestamp_packet, NULL), "gettimeofday() Failed");
140 141 142 143 144 145 146
        et_s1ap_eNB_itti_send_sctp_data_req(
            packet->enb_instance,
            packet->sctp_hdr.u.data_hdr.assoc_id,
            packet->sctp_hdr.u.data_hdr.payload.binary_stream,
            packet->sctp_hdr.u.data_hdr.payload.binary_stream_allocated_size,
            packet->sctp_hdr.u.data_hdr.stream);
        packet->status = ET_PACKET_STATUS_SENT;
147
        g_fsm_state = ET_FSM_STATE_RUNNING;
148 149 150 151 152 153 154 155 156 157 158 159
      }
      break;
    case SCTP_CID_INIT:
    case SCTP_CID_INIT_ACK:
      AssertFatal(0, "Invalid case TX packet SCTP_CID_INIT or SCTP_CID_INIT_ACK");
      break;
    default:
      AssertFatal(0, "Invalid case TX packet SCTP_CID %d", packet->sctp_hdr.chunk_type);
  }
}
//------------------------------------------------------------------------------
et_fsm_state_t et_scenario_fsm_notify_event_state_running(et_event_t event)
160 161
{

162 163 164 165 166 167 168 169
  switch (event.code){
    case ET_EVENT_RX_PACKET_TIME_OUT:
      AssertFatal(0, "Event ET_EVENT_RX_PACKET_TIME_OUT not handled in FSM state ET_FSM_STATE_RUNNING");
      break;
    case ET_EVENT_TX_TIMED_PACKET:
      AssertFatal(0, "Event ET_EVENT_TX_TIMED_PACKET not handled in FSM state ET_FSM_STATE_RUNNING");
      break;
    case ET_EVENT_RX_S1AP:
170
      et_s1ap_process_rx_packet(&event.u.s1ap_data_ind);
171 172 173 174
      break;
    default:
      AssertFatal(0, "Case event %d not handled in ET_FSM_STATE_RUNNING", event.code);
  }
175
  pthread_mutex_unlock(&g_fsm_lock);
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
  return 0;
}

//------------------------------------------------------------------------------
et_fsm_state_t et_scenario_fsm_notify_event_state_waiting(et_event_t event)
{

  switch (event.code){
    case ET_EVENT_RX_PACKET_TIME_OUT:
      fprintf(stderr, "Error The following packet is not received:\n");
      et_display_packet(event.u.rx_packet_time_out);
      AssertFatal(0, "Waited packet not received");
      break;
    case ET_EVENT_RX_S1AP:
      et_s1ap_process_rx_packet(&event.u.s1ap_data_ind);
      break;
    case ET_EVENT_TX_TIMED_PACKET:
      // send immediately
194
      AssertFatal(0 == gettimeofday(&event.u.tx_timed_packet->timestamp_packet, NULL), "gettimeofday() Failed");
195 196 197 198 199 200 201
      et_s1ap_eNB_itti_send_sctp_data_req(
          event.u.tx_timed_packet->enb_instance,
          event.u.tx_timed_packet->sctp_hdr.u.data_hdr.assoc_id,
          event.u.tx_timed_packet->sctp_hdr.u.data_hdr.payload.binary_stream,
          event.u.tx_timed_packet->sctp_hdr.u.data_hdr.payload.binary_stream_allocated_size,
          event.u.tx_timed_packet->sctp_hdr.u.data_hdr.stream);
      event.u.tx_timed_packet->status = ET_PACKET_STATUS_SENT;
202
      g_fsm_state = ET_FSM_STATE_RUNNING;
203 204 205 206 207 208
      break;


    default:
      AssertFatal(0, "Case event %d not handled in ET_FSM_STATE_WAITING", event.code);
  }
209
  pthread_mutex_unlock(&g_fsm_lock);
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
  return 0;
}

//------------------------------------------------------------------------------
et_fsm_state_t et_scenario_fsm_notify_event_state_connecting_s1c(et_event_t event)
{

  switch (event.code){
    case ET_EVENT_S1C_CONNECTED:
      // hack simulate we have been able to get the right timing values
      AssertFatal(gettimeofday(&g_scenario->time_last_tx_packet, NULL) == 0, "gettimeofday failed");
      AssertFatal(gettimeofday(&g_scenario->time_last_rx_packet, NULL) == 0, "gettimeofday failed");

      while (NULL != g_scenario->next_packet) {
        switch (g_scenario->next_packet->sctp_hdr.chunk_type) {
          case SCTP_CID_DATA :
            // no init in this scenario, may be sub-scenario
            if (g_scenario->next_packet->action == ET_PACKET_ACTION_S1C_SEND) {
              et_scenario_schedule_tx_packet(g_scenario->next_packet);
229 230
              pthread_mutex_unlock(&g_fsm_lock);
              return g_fsm_state;
231 232 233 234 235 236 237 238 239
            } else if (g_scenario->next_packet->action == ET_PACKET_ACTION_S1C_RECEIVE) {
              if (g_scenario->next_packet->status == ET_PACKET_STATUS_RECEIVED) {
                g_scenario->last_rx_packet = g_scenario->next_packet;
                g_scenario->next_packet    = g_scenario->next_packet->next;
                g_scenario->time_last_rx_packet = g_scenario->last_rx_packet->timestamp_packet;
                g_scenario->next_packet    = g_scenario->next_packet->next;

              } else if (g_scenario->next_packet->status == ET_PACKET_STATUS_NONE) {
                et_scenario_wait_rx_packet(g_scenario->next_packet);
240 241
                pthread_mutex_unlock(&g_fsm_lock);
                return g_fsm_state;
242 243 244 245 246 247 248 249 250 251 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
              } else {
                AssertFatal(0, "Invalid packet status %d", g_scenario->next_packet->status);
              }
            } else {
              AssertFatal(0, "Invalid packet action %d", g_scenario->next_packet->action);
            }
             break;

          case SCTP_CID_INIT:
          case SCTP_CID_INIT_ACK:
          case SCTP_CID_HEARTBEAT:
          case SCTP_CID_HEARTBEAT_ACK:
          case SCTP_CID_COOKIE_ECHO:
          case SCTP_CID_COOKIE_ACK:
          case SCTP_CID_ECN_ECNE:
          case SCTP_CID_ECN_CWR:
            g_scenario->next_packet->status = ET_PACKET_STATUS_NOT_TAKEN_IN_ACCOUNT;
            g_scenario->next_packet = g_scenario->next_packet->next;
            break;

          case SCTP_CID_ABORT:
          case SCTP_CID_SHUTDOWN:
          case SCTP_CID_SHUTDOWN_ACK:
          case SCTP_CID_ERROR:
          case SCTP_CID_SHUTDOWN_COMPLETE:
            AssertFatal(0, "The scenario should be cleaned (packet %s cannot be processed at this time)",
                et_chunk_type_cid2str(g_scenario->next_packet->sctp_hdr.chunk_type));
            break;

          default:
            g_scenario->next_packet->status = ET_PACKET_STATUS_NOT_TAKEN_IN_ACCOUNT;
            g_scenario->next_packet = g_scenario->next_packet->next;
        }
      }
      fprintf(stderr, "No Packet found in this scenario: %s\n", g_scenario->name);
277 278 279
      g_fsm_state = ET_FSM_STATE_NULL;
      pthread_mutex_unlock(&g_fsm_lock);
      return g_fsm_state;
280 281 282 283 284
      break;

    default:
      AssertFatal(0, "Case event %d not handled in ET_FSM_STATE_CONNECTING_S1C", event.code);
  }
285
  pthread_mutex_unlock(&g_fsm_lock);
286 287 288 289 290
  return 0;
}
//------------------------------------------------------------------------------
et_fsm_state_t et_scenario_fsm_notify_event_state_null(et_event_t event)
{
291 292 293 294
  switch (event.code){
    case ET_EVENT_INIT:
      AssertFatal(NULL == g_scenario, "Current scenario not ended");
      g_scenario = event.u.init.scenario;
295 296 297 298
      g_scenario->next_packet            = g_scenario->list_packet;
      g_scenario->last_rx_packet         = NULL;
      g_scenario->last_tx_packet         = NULL;

299 300
      while (NULL != g_scenario->next_packet) {
        switch (g_scenario->next_packet->sctp_hdr.chunk_type) {
301 302

          case SCTP_CID_DATA :
303
            // no init in this scenario, may be sub-scenario, ...
304 305
            if (g_scenario->next_packet->action == ET_PACKET_ACTION_S1C_SEND) {
              et_scenario_schedule_tx_packet(g_scenario->next_packet);
306 307
              pthread_mutex_unlock(&g_fsm_lock);
              return g_fsm_state;
308 309 310 311 312 313 314 315 316
            } else if (g_scenario->next_packet->action == ET_PACKET_ACTION_S1C_RECEIVE) {
              if (g_scenario->next_packet->status == ET_PACKET_STATUS_RECEIVED) {
                g_scenario->last_rx_packet = g_scenario->next_packet;
                g_scenario->next_packet    = g_scenario->next_packet->next;
                g_scenario->time_last_rx_packet = g_scenario->last_rx_packet->timestamp_packet;
                g_scenario->next_packet    = g_scenario->next_packet->next;

              } else if (g_scenario->next_packet->status == ET_PACKET_STATUS_NONE) {
                et_scenario_wait_rx_packet(g_scenario->next_packet);
317 318
                pthread_mutex_unlock(&g_fsm_lock);
                return g_fsm_state;
319 320 321
              } else {
                AssertFatal(0, "Invalid packet status %d", g_scenario->next_packet->status);
              }
322
            } else {
323
              AssertFatal(0, "Invalid packet action %d", g_scenario->next_packet->action);
324 325
            }
            break;
326

327 328
          case SCTP_CID_INIT:
          case SCTP_CID_INIT_ACK:
329 330 331 332 333
            g_scenario->enb_properties       = (Enb_properties_array_t *)et_enb_config_get();
            g_scenario->hash_old_ue_mme_id2ue_mme_id = hashtable_create (256,NULL,NULL);
            g_scenario->hash_mme2association_id      = hashtable_create (256,NULL,NULL);
            // Try to register each eNB
            g_scenario->registered_enb       = 0;
334 335 336 337
            g_fsm_state            = ET_FSM_STATE_CONNECTING_S1C;
            et_eNB_app_register (g_scenario->enb_properties);
            pthread_mutex_unlock(&g_fsm_lock);
            return g_fsm_state;
338
            break;
339

340 341 342 343 344 345
          case SCTP_CID_HEARTBEAT:
          case SCTP_CID_HEARTBEAT_ACK:
          case SCTP_CID_COOKIE_ECHO:
          case SCTP_CID_COOKIE_ACK:
          case SCTP_CID_ECN_ECNE:
          case SCTP_CID_ECN_CWR:
346 347
            g_scenario->next_packet->status = ET_PACKET_STATUS_NOT_TAKEN_IN_ACCOUNT;
            g_scenario->next_packet = g_scenario->next_packet->next;
348
            break;
349

350 351 352 353 354
          case SCTP_CID_ABORT:
          case SCTP_CID_SHUTDOWN:
          case SCTP_CID_SHUTDOWN_ACK:
          case SCTP_CID_ERROR:
          case SCTP_CID_SHUTDOWN_COMPLETE:
355 356
            AssertFatal(0, "The scenario should be cleaned (packet %s cannot be processed at this time)",
                et_chunk_type_cid2str(g_scenario->next_packet->sctp_hdr.chunk_type));
357 358 359
            break;

          default:
360 361
            g_scenario->next_packet->status = ET_PACKET_STATUS_NOT_TAKEN_IN_ACCOUNT;
            g_scenario->next_packet = g_scenario->next_packet->next;
362 363
        }
      }
364
      fprintf(stderr, "No Useful packet found in this scenario: %s\n", g_scenario->name);
365 366 367
      g_fsm_state = ET_FSM_STATE_NULL;
      pthread_mutex_unlock(&g_fsm_lock);
      return g_fsm_state;
368 369 370 371 372 373 374 375 376
      break;

    default:
      AssertFatal(0, "Case event %d not handled in ET_FSM_STATE_NULL", event.code);
  }
  return 0;
}

//------------------------------------------------------------------------------
377
et_fsm_state_t et_scenario_fsm_notify_event(et_event_t event)
378 379 380
{
  AssertFatal((event.code >= ET_EVENT_START) && (event.code < ET_EVENT_END), "Unknown et_event_t.code %d", event.code);

381 382
  pthread_mutex_lock(&g_fsm_lock);
  switch (g_fsm_state){
383
    case ET_FSM_STATE_NULL: return et_scenario_fsm_notify_event_state_null(event); break;
384 385 386
    case ET_FSM_STATE_CONNECTING_S1C: return et_scenario_fsm_notify_event_state_connecting_s1c(event); break;
    case ET_FSM_STATE_WAITING_EVENT: return et_scenario_fsm_notify_event_state_waiting(event); break;
    case ET_FSM_STATE_RUNNING: return et_scenario_fsm_notify_event_state_running(event); break;
387
    default:
388
      AssertFatal(0, "Case fsm_state %d not handled", g_fsm_state);
389
  }
390 391
  pthread_mutex_unlock(&g_fsm_lock);
  return g_fsm_state;
392
}