blob: 730eaabd3005b91db5cac2a438b8bf6cfb7fefb5 [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
///////////////////////////////////////////////////////////////////////////////
//
// Rev: <RnXnn>
// Prodnr: CNL 113 827
// Contact: http://ttcn.ericsson.se
//
///////////////////////////////////////////////////////////////////////////////
#include "GTP_Tunnel_control_PT.hh"
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h>
namespace GTP__Tunnel__PortTypes {
GTP__Tunnel__control__PT::GTP__Tunnel__control__PT(const char *par_port_name)
: GTP__Tunnel__control__PT_BASE(par_port_name)
{
daemon_fd=-1;
init_msg_buffer(&recv_buffer);
}
GTP__Tunnel__control__PT::~GTP__Tunnel__control__PT()
{
free_msg_buffer(&recv_buffer);
}
void GTP__Tunnel__control__PT::log(const char *fmt, ...)
{
if (TTCN_Logger::log_this_event(TTCN_DEBUG)) {
TTCN_Logger::begin_event(TTCN_DEBUG);
TTCN_Logger::log_event("GTP Tunnel Control test port (%s): ", get_name());
va_list args;
va_start(args, fmt);
TTCN_Logger::log_event_va_list(fmt, args);
va_end(args);
TTCN_Logger::end_event();
}
}
void GTP__Tunnel__control__PT::report_ind(GTP__Tunnel__Result__code code,const char *fmt, ... ){
GTP__Tunnel__indication ind;
va_list args;
va_start(args, fmt);
char * str=mprintf_va_list(fmt, args);
ind.result().result__code()=code;
ind.result().result__text()=str;
Free(str);
incoming_message(ind);
}
void GTP__Tunnel__control__PT::set_parameter(const char * /*parameter_name*/,
const char * /*parameter_value*/)
{
}
/*void GTP__Tunnel__control__PT::Handle_Fd_Event(int fd, boolean is_readable,
boolean is_writable, boolean is_error) {}*/
void GTP__Tunnel__control__PT::Handle_Fd_Event_Error(int fd)
{
Handle_Fd_Event_Readable(fd);
}
void GTP__Tunnel__control__PT::Handle_Fd_Event_Writable(int /*fd*/)
{
}
void GTP__Tunnel__control__PT::Handle_Fd_Event_Readable(int fd)
{
log("Handle_Fd_Event_Readable begin");
inc_buff_size(&recv_buffer,4); // we should be able to receive the msg length
int curr_pos;
int rd=read(fd,recv_buffer.msg+recv_buffer.pos,recv_buffer.size-recv_buffer.pos);
log("read returned: %d",rd);
if(rd<=0){ // Read error or the daemon closed the connection
int en=errno;
if(rd<0){
log("Read error: %d %s",en,strerror(en));
report_ind(GTP__Tunnel__Result__code::ERROR_,"Read error %d %s",en,strerror(en));
} else {
log("The daemon closed the connection");
report_ind(GTP__Tunnel__Result__code::ERROR_,"The daemon closed the connection");
}
Handler_Remove_Fd_Read(daemon_fd);
close(daemon_fd);
daemon_fd=-1;
} else { // process the message
recv_buffer.pos+=rd;
// read the msg length
curr_pos=recv_buffer.pos; // store the write position of the buffer
while(curr_pos){ // process all of the message in the buffer
recv_buffer.pos=0; // start from the beginning
int msg_len;
if(get_int(&recv_buffer,&msg_len)<0) { goto msg_error;} // read the msg length
log("msg length: %d",msg_len);
if(msg_len>curr_pos){ // we need more data
log("More data is needed. msg length %d received bytes %d",msg_len,curr_pos);
recv_buffer.pos=curr_pos;
inc_buff_size(&recv_buffer,msg_len-curr_pos); // reserve enough space in the buffer to receive data
log("Handle_Fd_Event_Readable end");
return;
}
if(msg_len<8) { goto msg_error;} // No msg type????
int msg_type;
if(get_int(&recv_buffer,&msg_type)<0) { goto msg_error;} // read the msg type
log("msg type: %d",msg_type);
switch(msg_type){
case GTP_CTRL_MSG_INIT_ACK:{
GTP__Tunnel__init__ack ia;
log("GTP_Tunnel_init_ack received");
int code;
if(get_int(&recv_buffer,&code)<0) { break;} // get the IE code
log("IE code: %d",code);
if(code!=GTP_CTRL_IE_RES_CODE){ break;} // check it
if(get_int(&recv_buffer,&code)<0) { break;} // read the result code
log("IE val: %d",code);
ia.result().result__code()=code;
str_holder str;
if(get_int(&recv_buffer,&code)<0) { break;} // get the IE code
log("IE code: %d",code);
if(code!=GTP_CTRL_IE_RES_TXT){ break;} // check it
if(get_str(&recv_buffer,&str)<0) { break;}
ia.result().result__text()=CHARSTRING(str.str_size,(const char*)str.str_begin);
log("Message decoded");
incoming_message(ia);
}
break;
case GTP_CTRL_MSG_CREATE_ACK:{
log("GTP_Tunnel_create_ack received");
GTP__Tunnel__create__ack ca;
int code;
str_holder str;
if(get_int(&recv_buffer,&code)<0) { break;} // get the IE code
log("IE code: %d",code);
if(code!=GTP_CTRL_IE_OUT_TEID){ break;} // check it
if(get_str(&recv_buffer,&str)<0) { break;}
ca.outgoing__TEID()=OCTETSTRING(str.str_size,str.str_begin);
if(get_int(&recv_buffer,&code)<0) { break;} // get the IE code
if(code==GTP_CTRL_IE_IN_TEID){
log("IE code: %d",code);
if(get_str(&recv_buffer,&str)<0) { break;}
ca.incoming__TEID()()=OCTETSTRING(str.str_size,str.str_begin);
if(get_int(&recv_buffer,&code)<0) { break;} // get the IE code
} else {
ca.incoming__TEID()=OMIT_VALUE;
}
log("IE code: %d",code);
if(code!=GTP_CTRL_IE_RES_CODE){ break;} // check it
if(get_int(&recv_buffer,&code)<0) { break;} // read the result code
log("IE val: %d",code);
ca.result().result__code()=code;
if(get_int(&recv_buffer,&code)<0) { break;} // get the IE code
log("IE code: %d",code);
if(code!=GTP_CTRL_IE_RES_TXT){ break;} // check it
if(get_str(&recv_buffer,&str)<0) { break;}
ca.result().result__text()=CHARSTRING(str.str_size,(const char*)str.str_begin);
if(get_int(&recv_buffer,&code)<0) { break;} // get the IE code
log("IE code: %d",code);
if(code!=GTP_CTRL_IE_ADDR){ break;} // check it
if(get_str(&recv_buffer,&str)<0) { break;}
ca.user__address()=OCTETSTRING(str.str_size,str.str_begin);
log("Message decoded");
incoming_message(ca);
}
break;
case GTP_CTRL_MSG_BYE_ACK:{
log("GTP_Tunnel_bye_ack received");
GTP__Tunnel__bye__ack ba=NULL_VALUE;
incoming_message(ba);
}
break;
case GTP_CTRL_MSG_INDICATION:{
log("GTP_Tunnel_indication received");
GTP__Tunnel__indication ia;
int code;
if(get_int(&recv_buffer,&code)<0) { break;} // get the IE code
log("IE code: %d",code);
if(code!=GTP_CTRL_IE_RES_CODE){ break;} // check it
if(get_int(&recv_buffer,&code)<0) { break;} // read the result code
log("IE val: %d",code);
ia.result().result__code()=code;
str_holder str;
if(get_int(&recv_buffer,&code)<0) { break;} // get the IE code
log("IE code: %d",code);
if(code!=GTP_CTRL_IE_RES_TXT){ break;} // check it
if(get_str(&recv_buffer,&str)<0) { break;}
ia.result().result__text()=CHARSTRING(str.str_size,(const char*)str.str_begin);
log("Message decoded");
incoming_message(ia);
}
break;
default:
log("Unknown message type. Ignored");
report_ind(GTP__Tunnel__Result__code::ERROR_,"Unknown message type: %d. Ignored",msg_type);
break;
}
log("Remove the processed message from the buffer.");
if(msg_len!=curr_pos){ // there is data in the buffer, move it to the beginning
memmove(recv_buffer.msg,recv_buffer.msg+msg_len,curr_pos-msg_len);
}
recv_buffer.pos=curr_pos-msg_len;
curr_pos=recv_buffer.pos;
}
}
log("Handle_Fd_Event_Readable end");
return;
msg_error:
// process the message decoding error here
log("Something is wrong with the received message. Dropped.");
report_ind(GTP__Tunnel__Result__code::ERROR_,"Something is wrong with the received message. Dropped.");
recv_buffer.pos=0;
log("Handle_Fd_Event_Readable end");
}
/*void GTP__Tunnel__control__PT::Handle_Timeout(double time_since_last_call) {}*/
void GTP__Tunnel__control__PT::user_map(const char * /*system_port*/)
{
log("Mapped.");
}
void GTP__Tunnel__control__PT::user_unmap(const char * /*system_port*/)
{
if(daemon_fd!=-1){
close(daemon_fd);
daemon_fd=-1;
}
log("Unmapped.");
}
void GTP__Tunnel__control__PT::user_start()
{
}
void GTP__Tunnel__control__PT::user_stop()
{
}
void GTP__Tunnel__control__PT::send_msg(const msg_buffer* buffer){
log("Sending message");
if(::send(daemon_fd,buffer->msg,buffer->pos,0)<0){
int en=errno;
log("Message send error %d %s",en,strerror(en));
report_ind(GTP__Tunnel__Result__code::ERROR_,"Message send error %d %s",en,strerror(en));
if(daemon_fd != -1)
{
Handler_Remove_Fd_Read(daemon_fd);
close(daemon_fd);
daemon_fd=-1;
}
} else {
log("Message sent");
}
}
void GTP__Tunnel__control__PT::outgoing_send(const GTP__Tunnel__init& send_par)
{
log("outgoing_send GTP__Tunnel__init called");
if(daemon_fd!=-1){
report_ind(GTP__Tunnel__Result__code::ERROR_,"Already connected to the daemon");
return;
}
struct sockaddr_un localAddr;
localAddr.sun_family = AF_UNIX;
localAddr.sun_path[0]='\0'; // use abstract socket
if( (!send_par.interface__name().ispresent()) || (send_par.interface__name()().lengthof()==0)){
log("No interface name was specified, using the default name: gtp_tunel_daemon");
snprintf(localAddr.sun_path+1,106,"gtp_tunel_daemon");
} else {
log("Interface name was specified, connecting to: gtp_tunel_daemon_%s",(const char*)send_par.interface__name()());
snprintf(localAddr.sun_path+1,106,"gtp_tunel_daemon_%s",(const char*)send_par.interface__name()());
}
if((daemon_fd = socket(PF_UNIX,SOCK_STREAM,0))<0) {
int en=errno;
log("Socket creation error %d %s",en,strerror(en));
report_ind(GTP__Tunnel__Result__code::ERROR_,"Socket creation error %d %s",en,strerror(en));
log("outgoing_send GTP__Tunnel__init finished");
return;
}
size_t addrLength = sizeof(localAddr.sun_family) + 1 + strlen(localAddr.sun_path+1);
log("Connecting to the daemon");
if(connect(daemon_fd, (struct sockaddr *) &localAddr, addrLength) != 0)
{
int en=errno;
log("Connect failed %d %s",en,strerror(en));
report_ind(GTP__Tunnel__Result__code::ERROR_,"Connect failed %d %s",en,strerror(en));
close(daemon_fd);
daemon_fd=-1;
} else {
Handler_Add_Fd_Read(daemon_fd);
msg_buffer msg_buff;
init_msg_buffer(&msg_buff);
msg_buff.pos+=4; // skip the length, will be filled later
int msg_len=4;
msg_len+=put_int(&msg_buff,GTP_CTRL_MSG_INIT);
if(send_par.tunnel__addres().ispresent()){
str_holder str;
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_LOCAL_IP);
str.str_begin=(const unsigned char*)(const char*)send_par.tunnel__addres()().local__ip();
str.str_size=send_par.tunnel__addres()().local__ip().lengthof();
msg_len+=put_str(&msg_buff,&str);
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_LOCAL_PORT);
msg_len+=put_int(&msg_buff,(int)send_par.tunnel__addres()().local__port());
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_REMOTE_IP);
str.str_begin=(const unsigned char*)(const char*)send_par.tunnel__addres()().remote__ip();
str.str_size=send_par.tunnel__addres()().remote__ip().lengthof();
msg_len+=put_str(&msg_buff,&str);
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_REMOTE_PORT);
msg_len+=put_int(&msg_buff,(int)send_par.tunnel__addres()().remote__port());
}
if(send_par.param__list().ispresent()){
for(int i=0;i<send_par.param__list()().lengthof();i++){
switch(send_par.param__list()()[i].get_selection()){
case GTP__Tunnel__param::ALT_set__address__mode:
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_PARAM_SET_ADDR_MODE);
msg_len+=put_int(&msg_buff,(int)send_par.param__list()()[i].set__address__mode());
break;
default:
break;
}
}
}
int cp=msg_buff.pos;
msg_buff.pos=0;
put_int(&msg_buff,msg_len);
msg_buff.pos=cp;
send_msg(&msg_buff);
free_msg_buffer(&msg_buff);
}
log("outgoing_send GTP__Tunnel__init finished");
}
void GTP__Tunnel__control__PT::outgoing_send(const GTP__Tunnel__bye& /*send_par*/)
{
if(daemon_fd==-1){
report_ind(GTP__Tunnel__Result__code::ERROR_,"There is no connection to the daemon");
return;
}
msg_buffer msg_buff;
init_msg_buffer(&msg_buff);
put_int(&msg_buff,8);
put_int(&msg_buff,GTP_CTRL_MSG_BYE);
send_msg(&msg_buff);
free_msg_buffer(&msg_buff);
}
void GTP__Tunnel__control__PT::outgoing_send(const GTP__Tunnel__create& send_par)
{
if(daemon_fd==-1){
report_ind(GTP__Tunnel__Result__code::ERROR_,"There is no connection to the daemon");
return;
}
str_holder str;
msg_buffer msg_buff;
init_msg_buffer(&msg_buff);
msg_buff.pos+=4; // skip the length, will be filled later
int msg_len=4;
msg_len+=put_int(&msg_buff,GTP_CTRL_MSG_CREATE);
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_OUT_TEID);
str.str_begin=(const unsigned char*)send_par.outgoing__TEID();
str.str_size=send_par.outgoing__TEID().lengthof();
msg_len+=put_str(&msg_buff,&str);
if(send_par.incoming__TEID().ispresent()){
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_IN_TEID);
str.str_begin=(const unsigned char*)send_par.incoming__TEID()();
str.str_size=send_par.incoming__TEID()().lengthof();
msg_len+=put_str(&msg_buff,&str);
}
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_ADDR_TYPE);
msg_len+=put_int(&msg_buff,(int)send_par.user__address__type());
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_ADDR);
str.str_begin=(const unsigned char*)send_par.user__address();
str.str_size=send_par.user__address().lengthof();
msg_len+=put_str(&msg_buff,&str);
if(send_par.filter().ispresent()){
if(send_par.filter()().proto().ispresent()){
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_FILTER_PROTO);
msg_len+=put_int(&msg_buff,(int)send_par.filter()().proto()());
}
if(send_par.filter()().local__port().ispresent()){
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_FILTER_LOCAL_PORT);
msg_len+=put_int(&msg_buff,(int)send_par.filter()().local__port()());
}
if(send_par.filter()().remote__ip().ispresent()){
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_FILTER_REMOTE_IP);
str.str_begin=(const unsigned char*)send_par.filter()().remote__ip()();
str.str_size=send_par.filter()().remote__ip()().lengthof();
msg_len+=put_str(&msg_buff,&str);
}
if(send_par.filter()().remote__port().ispresent()){
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_FILTER_REMOTE_PORT);
msg_len+=put_int(&msg_buff,(int)send_par.filter()().remote__port()());
}
}
if(send_par.tunnel__addres().ispresent()){
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_LOCAL_IP);
str.str_begin=(const unsigned char*)(const char*)send_par.tunnel__addres()().local__ip();
str.str_size=send_par.tunnel__addres()().local__ip().lengthof();
msg_len+=put_str(&msg_buff,&str);
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_LOCAL_PORT);
msg_len+=put_int(&msg_buff,(int)send_par.tunnel__addres()().local__port());
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_REMOTE_IP);
str.str_begin=(const unsigned char*)(const char*)send_par.tunnel__addres()().remote__ip();
str.str_size=send_par.tunnel__addres()().remote__ip().lengthof();
msg_len+=put_str(&msg_buff,&str);
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_REMOTE_PORT);
msg_len+=put_int(&msg_buff,(int)send_par.tunnel__addres()().remote__port());
}
int cp=msg_buff.pos;
msg_buff.pos=0;
put_int(&msg_buff,msg_len);
msg_buff.pos=cp;
send_msg(&msg_buff);
free_msg_buffer(&msg_buff);
}
void GTP__Tunnel__control__PT::outgoing_send(const GTP__Tunnel__destroy& send_par)
{
if(daemon_fd==-1){
report_ind(GTP__Tunnel__Result__code::ERROR_,"There is no connection to the daemon");
return;
}
str_holder str;
msg_buffer msg_buff;
init_msg_buffer(&msg_buff);
msg_buff.pos+=4; // skip the length, will be filled later
int msg_len=4;
msg_len+=put_int(&msg_buff,GTP_CTRL_MSG_DESTROY);
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_ADDR);
str.str_begin=(const unsigned char*)send_par.user__address();
str.str_size=send_par.user__address().lengthof();
msg_len+=put_str(&msg_buff,&str);
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_OUT_TEID);
str.str_begin=(const unsigned char*)send_par.outgoing__TEID();
str.str_size=send_par.outgoing__TEID().lengthof();
msg_len+=put_str(&msg_buff,&str);
msg_len+=put_int(&msg_buff,GTP_CTRL_IE_IN_TEID);
str.str_begin=(const unsigned char*)send_par.incoming__TEID();
str.str_size=send_par.incoming__TEID().lengthof();
msg_len+=put_str(&msg_buff,&str);
int cp=msg_buff.pos;
msg_buff.pos=0;
put_int(&msg_buff,msg_len);
msg_buff.pos=cp;
send_msg(&msg_buff);
free_msg_buffer(&msg_buff);
}
} /* end of namespace */