blob: 5bd0b82353cc2f68ea75ae1de16a39bd1c24c016 [file] [log] [blame]
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2000-2018 Ericsson Telecom AB
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v2.0
// which accompanies this distribution, and is available at
// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
///////////////////////////////////////////////////////////////////////////////
//
// File: SCTP_Engine_ExternalFunctions.cc
// Description: function definition of the SCTP Engine
// Rev: <RnXnn>
// Prodnr: CNL 113 840
//
#include "SCTP_Engine_Functions.hh"
#include <map>
#include <vector>
#include <queue>
#include <stdint.h>
#include <openssl/md5.h>
#include <openssl/hmac.h>
#include <time.h>
typedef std::map<uint64_t,int> id_map_t;
typedef id_map_t::iterator id_map_it_t;
typedef std::map<uint64_t,uint64_t> ii_map_t;
typedef ii_map_t::iterator ii_map_it_t;
// Map to store assoc-id <-> (transport_id,local_port,remote_port) mapping
// The (transport_id,local_port,remote_port) concatenated into a
// 64 unsigned int value to form the key
static id_map_t id_map;
// Event scheduler types and map
// std::multimap type is used, because it stores the elements in a
// specific order
// We need to order the event by the scheduled time -> use the time as key
typedef std::multimap<double,int> event_map_t;
typedef event_map_t::iterator event_map_it_t;
static event_map_t event_map;
// We need int2int maps for every association
// - outgoing stream-id -> queue index
// - incoming stream-id -> queue index
//
// These maps are allocated at the assoc creation and freed at the assoc deletion
//
static std::vector<ii_map_t*> ii_map_vector;
static std::queue<int> ii_empty_queue; // stores the index of free/reusable elements
namespace SCTP__Engine__Functions {
//***************************************************************************//
//
// int2int map handler functions
//
//***************************************************************************//
INTEGER SCTP__int2int__new(){
int idx=-1;
if(ii_empty_queue.empty()){
// No free item, add one
idx=ii_map_vector.size();
ii_map_vector.push_back(NULL);
} else {
// get the index of the free item
idx=ii_empty_queue.front();
ii_empty_queue.pop();
}
ii_map_vector[idx]=new ii_map_t;
return idx;
}
void SCTP__int2int__delete(INTEGER const&pl_map_id){
int idx=(int)pl_map_id;
if( idx>=0 && idx<(int)ii_map_vector.size() && ii_map_vector[idx] != NULL){
// valid index
// delete the map
delete ii_map_vector[idx];
ii_map_vector[idx]=NULL;
// put the map index into the empty queue
ii_empty_queue.push(idx);
}
}
INTEGER SCTP__int2int__add(INTEGER const& pl_map_id, INTEGER const& pl_key, INTEGER const& pl_value){
int idx=(int)pl_map_id;
if( idx>=0 && idx<(int)ii_map_vector.size() && ii_map_vector[idx] != NULL){
// valid index
(*(ii_map_vector[idx]))[(uint64_t)pl_key.get_long_long_val()]=(uint64_t)pl_value.get_long_long_val();
return 0;
}
return -1;
}
INTEGER SCTP__int2int__get(INTEGER const& pl_map_id, INTEGER const& pl_key, INTEGER& pl_value){
int idx=(int)pl_map_id;
if( idx>=0 && idx<(int)ii_map_vector.size() && ii_map_vector[idx] != NULL){
// valid index
ii_map_it_t it=(ii_map_vector[idx])->find((uint64_t)pl_key.get_long_long_val());
if(it!=(ii_map_vector[idx])->end()){
// valid key
pl_value.set_long_long_val(it->second);
return 0;
}
}
return -1;
}
//***************************************************************************//
//
// assoc-id <-> (transport_id,local_port,remote_port) map handler functions
//
//***************************************************************************//
// Constructs the 64 bit key value
uint64_t get_id(const int64_t tr_id, const uint64_t loc_port, const uint64_t rem_port){
uint64_t vl_tr_id=0;
// the tr_id can be -1 => all transport => translate to all 1's
if(tr_id>=0){
vl_tr_id = tr_id;
} else {
vl_tr_id = ~vl_tr_id;
}
return (vl_tr_id << 32 ) | ((loc_port & 0xff) << 16 ) | (rem_port & 0xff);
}
INTEGER SCTP__add__id__map(INTEGER const& vl_assoc_id,
INTEGER const& vl_transport_id,
INTEGER const& vl_local_port,
INTEGER const& vl_remote_port){
uint64_t key=get_id(vl_transport_id.get_long_long_val(),(int)vl_local_port,(int)vl_remote_port);
if(id_map.find(key) == id_map.end()){
// if the key is not found, we can insert it
id_map[key]=(int)vl_assoc_id;
return 0;
}
// the key is found, return error
return -1;
}
INTEGER SCTP__del__id__map(INTEGER const& vl_transport_id,
INTEGER const& vl_local_port,
INTEGER const& vl_remote_port){
uint64_t key=get_id(vl_transport_id.get_long_long_val(),(int)vl_local_port,(int)vl_remote_port);
id_map_it_t it=id_map.find(key);
if(it != id_map.end()){
// found the key, delete it.
id_map.erase(it);
return 0; // Success
}
// the key is not found, return error
return -1;
}
INTEGER SCTP__find__id__map(INTEGER const& vl_transport_id,
INTEGER const& vl_local_port,
INTEGER const& vl_remote_port){
uint64_t key=get_id(vl_transport_id.get_long_long_val(),(int)vl_local_port,(int)vl_remote_port);
id_map_it_t it=id_map.find(key);
if(it != id_map.end()){
// found the key, return the assoc_id.
return it->second; // Success
}
// the key is not found, return error
return -1;
}
//***************************************************************************//
//
// State cookie handler functions
//
//***************************************************************************//
OCTETSTRING SCTP__gen__state__cookie(INTEGER const& pl_lifespan,
INTEGER const& pl_own_init_tag,
INTEGER const& pl_remote_init_tag,
INTEGER const& pl_tie_tag,
INTEGER const& pl_remote_tsn,
INTEGER const& pl_remote_os,
INTEGER const& pl_remote_mis,
INTEGER const& pl_remote_a_rwnd,
BOOLEAN const& pl_idata_flag,
BOOLEAN const& pl_reconf_flag,
BOOLEAN const& pl_forward_tsn_flag,
OCTETSTRING const& pl_key){
time_t tstamp_t=time(NULL);
int tstamp_i = (tstamp_t &0x7fffffff ); // to calculate the lifespan of teh cookie
// the lowest 31 bit is enough
int lifespan = (int)pl_lifespan & 0xffff;
int zero = 0;
int ones = ~0;
OCTETSTRING ret_val= int2oct(tstamp_i,4) + int2oct(lifespan,2) +
int2oct(pl_own_init_tag,4)+ int2oct(pl_remote_init_tag,4)
+ int2oct(pl_tie_tag,4)
+ int2oct(pl_remote_tsn,4)+ int2oct(pl_remote_os,2)
+ int2oct(pl_remote_mis,2)+ int2oct(pl_remote_a_rwnd,4)+
bit2oct( (pl_idata_flag?BITSTRING(1,(const unsigned char*)&ones):BITSTRING(1,(const unsigned char*)&zero))
+ (pl_reconf_flag?BITSTRING(1,(const unsigned char*)&ones):BITSTRING(1,(const unsigned char*)&zero))
+ (pl_forward_tsn_flag?BITSTRING(1,(const unsigned char*)&ones):BITSTRING(1,(const unsigned char*)&zero))
+ BITSTRING(5,(const unsigned char*)&zero) )
;
unsigned int out_length;
unsigned char output[EVP_MAX_MD_SIZE];
HMAC(EVP_md5(), (const unsigned char*)pl_key, (size_t) pl_key.lengthof(),
(const unsigned char*)ret_val, (size_t) ret_val.lengthof(),
output, &out_length);
return ret_val + OCTETSTRING(16,output);
}
INTEGER SCTP__check__state__cookie(OCTETSTRING const& pl_cookie,
OCTETSTRING const& pl_key){
if(pl_cookie.lengthof()!= 47){
// We use 46 octet length state cookie
return -1;
}
// check the MAC
unsigned int out_length;
unsigned char output[EVP_MAX_MD_SIZE];
HMAC(EVP_md5(), (const unsigned char*)pl_key, (size_t) pl_key.lengthof(),
(const unsigned char*)pl_cookie, 31,
output, &out_length);
if(substr(pl_cookie,31,16)!=OCTETSTRING(16,output)){
// MAC error
return -1;
}
// check lifespan
time_t tstamp_t=time(NULL);
unsigned int tstamp_i = (tstamp_t &0x7fffffff ); // to calculate the lifespan of teh cookie
// the lowest 31 bit is enough
unsigned int lifespan = (int)oct2int(substr(pl_cookie,4,2));
unsigned int orig_ts= (int)oct2int(substr(pl_cookie,0,4));
if( orig_ts<(tstamp_i-lifespan) ){
// expired
return 1;
}
// cookie ok
return 0;
}
//***************************************************************************//
//
// Event map handler functions
//
//***************************************************************************//
void SCTP__event__add__map(INTEGER const& pl_event_id,
FLOAT const& pl_event_time){
// Just insert it
event_map.insert(std::pair<double,int>((double)pl_event_time,(int)pl_event_id));
}
void SCTP__event__del__map(INTEGER const& pl_event_id,
FLOAT const& pl_event_time){
// find the the time,id pair.
// Note that several event can be scheduled at the same time
// so it is not enough to delete the key.
event_map_it_t it=event_map.find((double)pl_event_time);
while( (it != event_map.end()) && (it->second != (int)pl_event_id ) ){
it++;
}
// now the it point to either the event ot delete or to the end
if(it != event_map.end()){
// delete the event
event_map.erase(it);
}
}
INTEGER SCTP__event__get__map() {
// return the event id of the first event.
// the map is ordered by the event time
event_map_it_t it=event_map.begin();
if(it != event_map.end()){
return it->second; // return the event id
}
return -1; // map is empty
}
OCTETSTRING SCTP__misc__float2oct(FLOAT const& pl_in){
double fl=(double)pl_in;
return OCTETSTRING(sizeof(double),(const unsigned char*)&fl);
}
FLOAT SCTP__misc__oct2float( OCTETSTRING const& pl_in){
return FLOAT(*((float*)(const unsigned char*)pl_in));
}
}