587 lines
25 KiB
C
587 lines
25 KiB
C
/*
|
|
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The OpenAirInterface Software Alliance licenses this file to You under
|
|
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.openairinterface.org/?page_id=698
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*-------------------------------------------------------------------------------
|
|
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
|
* contact@openairinterface.org
|
|
*/
|
|
|
|
/*
|
|
play_scenario_fsm.c
|
|
-------------------
|
|
AUTHOR : Lionel GAUTHIER
|
|
COMPANY : EURECOM
|
|
EMAIL : Lionel.Gauthier@eurecom.fr
|
|
*/
|
|
#include <stdio.h>
|
|
#include <sys/time.h>
|
|
#include <pthread.h>
|
|
|
|
#include "intertask_interface.h"
|
|
#include "platform_types.h"
|
|
#include "assertions.h"
|
|
#include "play_scenario.h"
|
|
#include "play_scenario_s1ap_eNB_defs.h"
|
|
|
|
//------------------------------------------------------------------------------
|
|
extern int g_max_speed;
|
|
//------------------------------------------------------------------------------
|
|
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;
|
|
uint32_t g_constraints = ET_BIT_MASK_MATCH_SCTP_STREAM | ET_BIT_MASK_MATCH_SCTP_SSN;
|
|
//------------------------------------------------------------------------------
|
|
// it is assumed that if a time is negative tv_sec and tv_usec are both negative
|
|
void timeval_add (struct timeval *const result, const struct timeval *const a, const struct timeval *const b)
|
|
{
|
|
AssertFatal(((a->tv_sec <= 0) && (a->tv_usec <= 0)) || ((a->tv_sec >= 0) && (a->tv_usec >= 0)), " Bad time format arg a\n");
|
|
AssertFatal(((b->tv_sec <= 0) && (b->tv_usec <= 0)) || ((b->tv_sec >= 0) && (b->tv_usec >= 0)), " Bad time format arg b\n");
|
|
// may happen overflows but were are not dealing with very large timings
|
|
long long int r = a->tv_usec + b->tv_usec + (a->tv_sec + b->tv_sec) * 1000000;
|
|
result->tv_sec = r / (long long int)1000000;
|
|
result->tv_usec = r % (long long int)1000000;
|
|
|
|
if ((result != a) && (result != b)) {
|
|
LOG_D(ENB_APP, "timeval_add(%ld.%06d, %ld.%06d)=%ld.%06d\n", a->tv_sec, a->tv_usec, b->tv_sec, b->tv_usec, result->tv_sec, result->tv_usec);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// it is assumed that if a time is negative tv_sec and tv_usec are both negative
|
|
// return true if result is positive
|
|
int timeval_subtract (struct timeval *const result, struct timeval *const a, struct timeval *const b)
|
|
{
|
|
AssertFatal(((a->tv_sec <= 0) && (a->tv_usec <= 0)) || ((a->tv_sec >= 0) && (a->tv_usec >= 0)), " Bad time format arg a\n");
|
|
AssertFatal(((b->tv_sec <= 0) && (b->tv_usec <= 0)) || ((b->tv_sec >= 0) && (b->tv_usec >= 0)), " Bad time format arg b\n");
|
|
// may happen overflows but were are not dealing with very large timings
|
|
long long int r = a->tv_usec - b->tv_usec + (a->tv_sec - b->tv_sec) * 1000000;
|
|
result->tv_sec = r / (long long int)1000000;
|
|
result->tv_usec = r % (long long int)1000000;
|
|
|
|
if ((result != a) && (result != b)) {
|
|
LOG_D(ENB_APP, "timeval_subtract(%ld.%06d, %ld.%06d)=%ld.%06d\n", a->tv_sec, a->tv_usec, b->tv_sec, b->tv_usec, result->tv_sec, result->tv_usec);
|
|
}
|
|
|
|
return (result->tv_sec >= 0) && (result->tv_usec >= 0);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void et_scenario_wait_rx_packet(et_packet_t *const packet)
|
|
{
|
|
packet->status = ET_PACKET_STATUS_SCHEDULED_FOR_RECEIVING;
|
|
g_fsm_state = ET_FSM_STATE_WAITING_RX_EVENT;
|
|
|
|
if (timer_setup (ET_FSM_STATE_WAITING_RX_EVENT_DELAY_SEC, 0, TASK_S1AP, INSTANCE_DEFAULT, TIMER_ONE_SHOT,
|
|
packet, &packet->timer_id) < 0) {
|
|
AssertFatal(0, " Can not start waiting RX event timer\n");
|
|
}
|
|
|
|
g_scenario->timer_count++;
|
|
LOG_D(ENB_APP, "Waiting RX packet num %d original frame number %u\n", packet->packet_number, packet->original_frame_number);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void et_scenario_schedule_tx_packet(et_packet_t *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_rx = 0;
|
|
int we_are_too_late = 0;
|
|
int original_frame_number = -1;
|
|
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);
|
|
LOG_D(ENB_APP, "%s\n", __FUNCTION__);
|
|
g_fsm_state = ET_FSM_STATE_WAITING_TX_EVENT;
|
|
|
|
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);
|
|
LOG_D(ENB_APP, "offset_last_tx_packet=%ld.%06d\n", offset_last_tx_packet.tv_sec, offset_last_tx_packet.tv_usec);
|
|
LOG_D(ENB_APP, "offset_last_rx_packet=%ld.%06d\n", offset_last_rx_packet.tv_sec, offset_last_rx_packet.tv_usec);
|
|
last_packet_was_rx = timeval_subtract(&offset_tx_rx,&offset_last_tx_packet,&offset_last_rx_packet);
|
|
|
|
if (last_packet_was_rx) {
|
|
LOG_D(ENB_APP, "last_packet_was_rx\n");
|
|
we_are_too_late = timeval_subtract(&offset,&offset_last_rx_packet,&packet->time_relative_to_last_received_packet);
|
|
LOG_D(ENB_APP, "we_are_too_late=%d, offset=%ld.%06d\n", we_are_too_late, offset.tv_sec, offset.tv_usec);
|
|
} else {
|
|
LOG_D(ENB_APP, "last_packet_was_tx\n");
|
|
we_are_too_late = timeval_subtract(&offset,&offset_last_tx_packet,&packet->time_relative_to_last_sent_packet);
|
|
LOG_D(ENB_APP, "we_are_too_late=%d, offset=%ld.%06d\n", we_are_too_late, offset.tv_sec, offset.tv_usec);
|
|
}
|
|
|
|
if ((0 == we_are_too_late) && (0 == g_max_speed)) {
|
|
// set timer
|
|
if ((offset.tv_sec <= 0) || (offset.tv_usec <= 0)) {
|
|
offset.tv_sec = -offset.tv_sec;
|
|
offset.tv_usec = -offset.tv_usec;
|
|
}
|
|
|
|
LOG_D(ENB_APP, "Send packet num %u original frame number %u in %ld.%06d sec\n",
|
|
packet->packet_number, packet->original_frame_number, offset.tv_sec, offset.tv_usec);
|
|
packet->status = ET_PACKET_STATUS_SCHEDULED_FOR_SENDING;
|
|
|
|
if (timer_setup (offset.tv_sec, offset.tv_usec, TASK_S1AP, INSTANCE_DEFAULT, TIMER_ONE_SHOT,packet, &packet->timer_id) < 0) {
|
|
AssertFatal(0, " Can not start TX event timer\n");
|
|
}
|
|
|
|
g_scenario->timer_count++;
|
|
// Done g_fsm_state = ET_FSM_STATE_WAITING_TX_EVENT;
|
|
} else {
|
|
// send immediately
|
|
AssertFatal(0 == gettimeofday(&packet->timestamp_packet, NULL), "gettimeofday() Failed");
|
|
original_frame_number = packet->original_frame_number;
|
|
|
|
do {
|
|
g_scenario->time_last_tx_packet.tv_sec = packet->timestamp_packet.tv_sec;
|
|
g_scenario->time_last_tx_packet.tv_usec = packet->timestamp_packet.tv_usec;
|
|
LOG_D(ENB_APP, "Sending packet num %d original frame number %u immediately\n",packet->packet_number, packet->original_frame_number);
|
|
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;
|
|
g_scenario->next_packet = g_scenario->next_packet->next;
|
|
packet = packet->next;
|
|
} while ((NULL != packet) && (packet->original_frame_number == original_frame_number));
|
|
|
|
g_fsm_state = ET_FSM_STATE_RUNNING;
|
|
}
|
|
|
|
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)
|
|
{
|
|
switch (event.code) {
|
|
case ET_EVENT_TICK:
|
|
while (NULL != g_scenario->next_packet) {
|
|
LOG_D(ENB_APP, "EVENT_TICK: Considering packet num %d original frame number %u\n", g_scenario->next_packet->packet_number, g_scenario->next_packet->original_frame_number);
|
|
|
|
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) {
|
|
if (g_scenario->next_packet->status == ET_PACKET_STATUS_NONE) {
|
|
et_scenario_schedule_tx_packet(g_scenario->next_packet);
|
|
pthread_mutex_unlock(&g_fsm_lock);
|
|
et_event_t continue_event;
|
|
continue_event.code = ET_EVENT_TICK;
|
|
et_scenario_fsm_notify_event(continue_event);
|
|
return g_fsm_state;
|
|
} else if (g_scenario->next_packet->status != ET_PACKET_STATUS_SCHEDULED_FOR_SENDING) {
|
|
AssertFatal(0, "Invalid packet status %d", g_scenario->next_packet->status);
|
|
}
|
|
} else if (g_scenario->next_packet->action == ET_PACKET_ACTION_S1C_RECEIVE) {
|
|
if (g_scenario->next_packet->status == ET_PACKET_STATUS_RECEIVED) {
|
|
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);
|
|
pthread_mutex_unlock(&g_fsm_lock);
|
|
return g_fsm_state;
|
|
} 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:
|
|
LOG_D(ENB_APP, "EVENT_TICK: Ignoring packet num %d SCTP CID %s\n",
|
|
g_scenario->next_packet->packet_number,
|
|
et_chunk_type_cid2str(g_scenario->next_packet->sctp_hdr.chunk_type));
|
|
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:
|
|
LOG_D(ENB_APP, "EVENT_TICK: Ignoring packet num %d SCTP CID %s\n",
|
|
g_scenario->next_packet->packet_number,
|
|
et_chunk_type_cid2str(g_scenario->next_packet->sctp_hdr.chunk_type));
|
|
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);
|
|
g_fsm_state = ET_FSM_STATE_NULL;
|
|
pthread_mutex_unlock(&g_fsm_lock);
|
|
|
|
if (0 == g_scenario->timer_count) {
|
|
fprintf(stderr, "End of scenario: %s\n", g_scenario->name);
|
|
fflush(stderr);
|
|
fflush(stdout);
|
|
return 0;
|
|
//exit(0);
|
|
}
|
|
|
|
fprintf(stderr, "Remaining timers running: %d\n", g_scenario->timer_count);
|
|
return g_fsm_state;
|
|
break;
|
|
|
|
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:
|
|
et_s1ap_process_rx_packet(&event.u.s1ap_data_ind);
|
|
break;
|
|
|
|
default:
|
|
AssertFatal(0, "Case event %d not handled in ET_FSM_STATE_RUNNING", event.code);
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_fsm_lock);
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
et_fsm_state_t et_scenario_fsm_notify_event_state_waiting_tx(et_event_t event)
|
|
{
|
|
int rv = 0;
|
|
int original_frame_number = -1;
|
|
et_packet_t *packet = NULL;
|
|
|
|
switch (event.code) {
|
|
case ET_EVENT_TICK:
|
|
fprintf(stdout, "EVENT_TICK: waiting for tx event\n");
|
|
break;
|
|
|
|
case ET_EVENT_RX_S1AP:
|
|
rv = et_s1ap_process_rx_packet(&event.u.s1ap_data_ind);
|
|
break;
|
|
|
|
case ET_EVENT_TX_TIMED_PACKET:
|
|
// send immediately
|
|
packet = event.u.tx_timed_packet;
|
|
AssertFatal(0 == gettimeofday(&packet->timestamp_packet, NULL), "gettimeofday() Failed");
|
|
original_frame_number = packet->original_frame_number;
|
|
|
|
do {
|
|
g_scenario->time_last_tx_packet.tv_sec = packet->timestamp_packet.tv_sec;
|
|
g_scenario->time_last_tx_packet.tv_usec = packet->timestamp_packet.tv_usec;
|
|
LOG_D(ENB_APP, "Sending packet num %d original frame number %u immediately\n",packet->packet_number, packet->original_frame_number);
|
|
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;
|
|
packet = packet->next;
|
|
g_scenario->next_packet = packet;
|
|
} while ( (NULL != packet) && (packet->original_frame_number == original_frame_number));
|
|
|
|
g_fsm_state = ET_FSM_STATE_RUNNING;
|
|
break;
|
|
|
|
case ET_EVENT_RX_PACKET_TIME_OUT:
|
|
default:
|
|
AssertFatal(0, "Case event %d not handled in ET_FSM_STATE_WAITING_TX", event.code);
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_fsm_lock);
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
et_fsm_state_t et_scenario_fsm_notify_event_state_waiting_rx(et_event_t event)
|
|
{
|
|
int rv = 0;
|
|
|
|
switch (event.code) {
|
|
case ET_EVENT_TICK:
|
|
fprintf(stdout, "EVENT_TICK: waiting for rx event\n");
|
|
break;
|
|
|
|
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:
|
|
rv = et_s1ap_process_rx_packet(&event.u.s1ap_data_ind);
|
|
|
|
// waited packet
|
|
if (rv == 0) {
|
|
g_fsm_state = ET_FSM_STATE_RUNNING;
|
|
}
|
|
|
|
break;
|
|
|
|
case ET_EVENT_TX_TIMED_PACKET:
|
|
default:
|
|
AssertFatal(0, "Case event %d not handled in ET_FSM_STATE_WAITING_RX", event.code);
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_fsm_lock);
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
et_fsm_state_t et_scenario_fsm_notify_event_state_connecting_s1c(et_event_t event)
|
|
{
|
|
switch (event.code) {
|
|
case ET_EVENT_TICK:
|
|
break;
|
|
|
|
case ET_EVENT_S1C_CONNECTED:
|
|
// hack simulate we have been able to get the right timing values for STCP connect
|
|
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);
|
|
pthread_mutex_unlock(&g_fsm_lock);
|
|
et_event_t continue_event;
|
|
continue_event.code = ET_EVENT_TICK;
|
|
et_scenario_fsm_notify_event(continue_event);
|
|
return g_fsm_state;
|
|
} 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->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);
|
|
pthread_mutex_unlock(&g_fsm_lock);
|
|
return g_fsm_state;
|
|
} 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);
|
|
g_fsm_state = ET_FSM_STATE_NULL;
|
|
pthread_mutex_unlock(&g_fsm_lock);
|
|
|
|
if (0 == g_scenario->timer_count) {
|
|
fprintf(stderr, "End of scenario: %s\n", g_scenario->name);
|
|
fflush(stderr);
|
|
fflush(stdout);
|
|
exit(0);
|
|
}
|
|
|
|
fprintf(stderr, "Remaining timers running: %d\n", g_scenario->timer_count);
|
|
return g_fsm_state;
|
|
break;
|
|
|
|
default:
|
|
AssertFatal(0, "Case event %d not handled in ET_FSM_STATE_CONNECTING_S1C", event.code);
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_fsm_lock);
|
|
return 0;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
et_fsm_state_t et_scenario_fsm_notify_event_state_null(et_event_t event)
|
|
{
|
|
switch (event.code) {
|
|
case ET_EVENT_TICK:
|
|
break;
|
|
|
|
case ET_EVENT_INIT:
|
|
AssertFatal(NULL == g_scenario, "Current scenario not ended");
|
|
g_scenario = event.u.init.scenario;
|
|
g_scenario->next_packet = g_scenario->list_packet;
|
|
g_scenario->last_rx_packet = NULL;
|
|
g_scenario->last_tx_packet = NULL;
|
|
|
|
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);
|
|
pthread_mutex_unlock(&g_fsm_lock);
|
|
et_event_t continue_event;
|
|
continue_event.code = ET_EVENT_TICK;
|
|
et_scenario_fsm_notify_event(continue_event);
|
|
return g_fsm_state;
|
|
} 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->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);
|
|
pthread_mutex_unlock(&g_fsm_lock);
|
|
return g_fsm_state;
|
|
} 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:
|
|
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;
|
|
g_fsm_state = ET_FSM_STATE_CONNECTING_S1C;
|
|
AssertFatal(gettimeofday(&g_scenario->time_last_tx_packet, NULL) == 0, "gettimeofday failed");
|
|
et_eNB_app_register (g_scenario->enb_properties);
|
|
pthread_mutex_unlock(&g_fsm_lock);
|
|
return g_fsm_state;
|
|
break;
|
|
|
|
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 Useful packet found in this scenario: %s\n", g_scenario->name);
|
|
g_fsm_state = ET_FSM_STATE_NULL;
|
|
pthread_mutex_unlock(&g_fsm_lock);
|
|
return g_fsm_state;
|
|
break;
|
|
|
|
default:
|
|
AssertFatal(0, "Case event %d not handled in ET_FSM_STATE_NULL", event.code);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
et_fsm_state_t et_scenario_fsm_notify_event(et_event_t event)
|
|
{
|
|
AssertFatal((event.code >= ET_EVENT_START) && (event.code < ET_EVENT_END), "Unknown et_event_t.code %d", event.code);
|
|
pthread_mutex_lock(&g_fsm_lock);
|
|
|
|
switch (g_fsm_state) {
|
|
case ET_FSM_STATE_NULL: return et_scenario_fsm_notify_event_state_null(event); break;
|
|
|
|
case ET_FSM_STATE_CONNECTING_S1C: return et_scenario_fsm_notify_event_state_connecting_s1c(event); break;
|
|
|
|
case ET_FSM_STATE_WAITING_TX_EVENT: return et_scenario_fsm_notify_event_state_waiting_tx(event); break;
|
|
|
|
case ET_FSM_STATE_WAITING_RX_EVENT: return et_scenario_fsm_notify_event_state_waiting_rx(event); break;
|
|
|
|
case ET_FSM_STATE_RUNNING: return et_scenario_fsm_notify_event_state_running(event); break;
|
|
|
|
default:
|
|
AssertFatal(0, "Case fsm_state %d not handled", g_fsm_state);
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_fsm_lock);
|
|
return g_fsm_state;
|
|
}
|