9
0
Fork 0

Public release of the cellmgr_ng code to convert E1 to IPA SCCP

This commit is contained in:
Holger Hans Peter Freyther 2010-07-28 03:32:52 +08:00
commit 97f66e2b53
49 changed files with 8386 additions and 0 deletions

4
Makefile.am Normal file
View File

@ -0,0 +1,4 @@
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
INCLUDES = $(all_includes) -I$(top_srcdir)/include
SUBDIRS = include src tests

75
README Normal file
View File

@ -0,0 +1,75 @@
== Building the cellmgr_ng ==
=== Requirements ===
==== OpenBSC ====
The result of "make install" of OpenBSC is required. The cellmgr_ng is using
the osmocomm and sccp library provided by OpenBSC.
==== NexusWare C7 =====
The NexusWare C7 library must be available. It is used to communicate with
MTP up to Level3.
==== NexusWare Uniporte ====
The NexusWare Uniported library must be available. It is used to handle
the configuration of the MGW.
=== Configuring & Building ===
The cellmgr_ng is using autoconf. At first the configure script must be
generated, after this ./configure can be called. The configure script will
use pkg-config to find the CFLAGS and LIBS for the Osmocomm, SCCP, NexusWare
C7 and NexusWare Uniporte libraries.
The NexusWare libraries naturally do not come with pkg-config files. There
are two example files in the pkg-config directory of the cellmgr_ng that can
be changed to point to the proper paths. Alternatively the NEXUSWARE_C7_CFLAGS,
NEXUSWARE_C7_LIBS, NEXUSWARE_UNIPORTE_CFLAGS, NEXUSWARE_UNIPORTE_LIBS environment
variables can be set instead.
$ autoreconf --install --force
$ export PKG_CONFIG_PATH=/openbsc/install/lib/pkg-config:$PWD/pkgconfig
$ . /stuff/NexusWare/SETUP.SH
$ ./configure --host=ppc-linux
$ make
== Reset handling ==
=== Loss of the TCP connection to the MSC ===
* All open SCCP connections need to be closed
* All circuits allocated for voice calls need to be closed
* On reconnect the cellmgr needs to generate the reset messages
* The SCCP link is considered to be up
=== Loss of the MTP link/SLTM timeouts ===
* We will have to generate a SCCP reset to the network
* We will have to close ever voice call/mgcp and such
== Filtering ==
=== Filtering reset acks ===
For the above reset handling we filter the reset on both sides. Whenever
we connect to the MSC or lose the BSC we send it a reset.
Whenever the BSC is sending us a reset we directly respond with a reset act
=== Filtering RLSD messages ===
We are using the RLSD from the network and immediately reply with a RLC
if we don't know about this combination of src and dest reference. If we
do have a pair we set a flag and will send a RLC when we receive a RLC from
the BSC.
If we receive a RLSD from the BSC we do have a bug as we didn't respond
within a short enough timeout. We will close the connection. At which point
we will handle the above.
The reason to handle RLSD is that the BSC has a rather short timeout from
cleanup complete to wanting a RLSD and then the MSC and BSC will have some
issues...
== Header Rewriting ==
=== POI ===
We do not want to have the point code indicator in the SCCP header. Currently
we are removing this from the CR and DT1 messages.

11
cellmgr_ng.cfg Normal file
View File

@ -0,0 +1,11 @@
cellmgr
mtp dpc 1
mtp opc 2
mtp sltm once 0
! commenting the next line switches to C7 mode
udp dest ip 127.0.0.1
udp dest port 5000
udp src port 1313
udp reset 3
msc ip 127.0.0.1
msc token hey

35
configure.ac Normal file
View File

@ -0,0 +1,35 @@
dnl Process this file with autoconf to produce a configure script
AC_INIT
AM_INIT_AUTOMAKE(cellmgr_ng, 0.0.1)
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
dnl checks for programs
AC_PROG_MAKE_SET
AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_RANLIB
dnl checks for header files
AC_HEADER_STDC
dnl Check for the SNMP header
AC_CHECK_HEADERS([net-snmp/net-snmp-config.h])
dnl Checks for typedefs, structures and compiler characteristics
PKG_CHECK_MODULES([LAFORGE], [liblaf0rge1])
PKG_CHECK_MODULES([SCCP], [libsccp])
#PKG_CHECK_MODULES([NEXUSWARE_C7], [nexusware-c7])
#PKG_CHECK_MODULES([NEXUSWARE_UNIPORTE], [nexusware-uniporte])
AC_OUTPUT(
include/Makefile
include/mgcp/Makefile
include/openbsc_nat/Makefile
src/Makefile
tests/Makefile
tests/mtp/Makefile
tests/patching/Makefile
Makefile)

5
include/Makefile.am Normal file
View File

@ -0,0 +1,5 @@
noinst_HEADERS = mtp_level3.h mtp_data.h ipaccess.h thread.h mtp_pcap.h \
mgcp_ss7.h bss_patch.h write_queue.h bssap_sccp.h bsc_data.h udp_input.h \
snmp_mtp.h
SUBDIRS = mgcp openbsc_nat

131
include/bsc_data.h Normal file
View File

@ -0,0 +1,131 @@
/* Everything related to the BSC connection */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef BSC_DATA_H
#define BSC_DATA_H
#include <laf0rge1/linuxlist.h>
#include <laf0rge1/select.h>
#include <laf0rge1/timer.h>
#include <sccp/sccp.h>
#include "write_queue.h"
#include <netinet/in.h>
#include <arpa/inet.h>
struct bsc_data;
struct snmp_mtp_session;
/**
* A link to the underlying MTP2 library or such
*/
struct link_data {
union {
struct {
struct thread_notifier *notifier;
struct llist_head mtp_queue;
struct timer_list mtp_timeout;
} c7;
struct {
struct write_queue write_queue;
struct sockaddr_in remote;
struct snmp_mtp_session *session;
int reset_timeout;
} udp;
};
int pcap_fd;
struct bsc_data *bsc;
struct mtp_link *the_link;
struct timer_list link_activate;
int forced_down;
int (*start)(struct link_data *);
int (*write)(struct link_data *, struct msgb *msg);
int (*shutdown)(struct link_data *);
int (*reset)(struct link_data *data);
int (*clear_queue)(struct link_data *data);
};
struct bsc_data {
/* MSC */
char *msc_address;
struct write_queue msc_connection;
struct timer_list reconnect_timer;
int first_contact;
int msc_time;
struct timer_list msc_timeout;
int msc_ip_dscp;
int ping_time;
int pong_time;
struct timer_list ping_timeout;
struct timer_list pong_timeout;
int closing;
struct llist_head sccp_connections;
struct timer_list reset_timeout;
int reset_count;
struct timer_list start_timer;
int setup;
struct link_data link;
const char *token;
/* mgcp messgaes */
struct write_queue mgcp_agent;
};
/* bsc related functions */
void release_bsc_resources(struct bsc_data *bsc);
void bsc_link_down(struct link_data *data);
void bsc_link_up(struct link_data *data);
/* msc related functions */
int msc_init(struct bsc_data *bsc);
void msc_schedule_reconnect(struct bsc_data *bsc);
void msc_send_rlc(struct bsc_data *bsc, struct sccp_source_reference *src, struct sccp_source_reference *dest);
void msc_send_reset(struct bsc_data *bsc);
void msc_send_msg(struct bsc_data *bsc, int rc, struct sccp_parse_result *, struct msgb *msg);
void msc_clear_queue(struct bsc_data *data);
/* connection tracking and action */
void update_con_state(int rc, struct sccp_parse_result *result, struct msgb *msg, int from_msc, int sls);
unsigned int sls_for_src_ref(struct sccp_source_reference *ref);
/* c7 init */
int link_c7_init(struct link_data *data);
/* udp init */
int link_udp_init(struct link_data *data, int src_port, const char *dest_ip, int port);
/* MGCP */
void mgcp_forward(struct bsc_data *bsc, const u_int8_t *data, unsigned int length);
#endif

48
include/bss_patch.h Normal file
View File

@ -0,0 +1,48 @@
/* Patch Messages to and from the MSC */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef bss_patch_h
#define bss_patch_h
#include <laf0rge1/msgb.h>
#include <sccp/sccp.h>
#define BSS_FILTER_RESET 1
#define BSS_FILTER_RESET_ACK 2
#define BSS_FILTER_RLSD 3
#define BSS_FILTER_RLC 4
#define BSS_FILTER_CLEAR_COMPL 5
/**
* Error is < 0
* Success is == 0
* Filter is > 0
*/
int bss_patch_filter_msg(struct msgb *msg, struct sccp_parse_result *result);
/*
* Copy inpt->l2h to target->l2h but rewrite the SCCP header on the way
*/
void bss_rewrite_header_for_msc(int, struct msgb *target, struct msgb *inpt, struct sccp_parse_result *result);
int bss_rewrite_header_to_bsc(struct msgb *target, int opc, int dpc);
#endif

33
include/bssap_sccp.h Normal file
View File

@ -0,0 +1,33 @@
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef bssap_sccp_h
#define bssap_sccp_h
#include <sys/types.h>
#include <sccp/sccp_types.h>
struct msgb *create_clear_command(struct sccp_source_reference *dest_ref);
struct msgb *create_sccp_rlsd(struct sccp_source_reference *src_ref, struct sccp_source_reference *dst);
struct msgb *create_sccp_rlc(struct sccp_source_reference *src_ref, struct sccp_source_reference *dst);
struct msgb *create_sccp_refuse(struct sccp_source_reference *dest_ref);
struct msgb *create_reset();
#endif

52
include/ipaccess.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef _IPACCESS_H
#define _IPACCESS_H
#include <laf0rge1/linuxlist.h>
#define IPA_TCP_PORT_OML 3002
#define IPA_TCP_PORT_RSL 3003
struct ipaccess_head {
u_int16_t len; /* network byte order */
u_int8_t proto;
u_int8_t data[0];
} __attribute__ ((packed));
enum ipaccess_proto {
IPAC_PROTO_RSL = 0x00,
IPAC_PROTO_IPACCESS = 0xfe,
IPAC_PROTO_SCCP = 0xfd,
IPAC_PROTO_OML = 0xff,
};
enum ipaccess_msgtype {
IPAC_MSGT_PING = 0x00,
IPAC_MSGT_PONG = 0x01,
IPAC_MSGT_ID_GET = 0x04,
IPAC_MSGT_ID_RESP = 0x05,
IPAC_MSGT_ID_ACK = 0x06,
};
enum ipaccess_id_tags {
IPAC_IDTAG_SERNR = 0x00,
IPAC_IDTAG_UNITNAME = 0x01,
IPAC_IDTAG_LOCATION1 = 0x02,
IPAC_IDTAG_LOCATION2 = 0x03,
IPAC_IDTAG_EQUIPVERS = 0x04,
IPAC_IDTAG_SWVERSION = 0x05,
IPAC_IDTAG_IPADDR = 0x06,
IPAC_IDTAG_MACADDR = 0x07,
IPAC_IDTAG_UNIT = 0x08,
};
/*
* methods for parsing and sending a message
*/
int ipaccess_rcvmsg_base(struct msgb *msg, struct bsc_fd *bfd);
struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error);
void ipaccess_prepend_header(struct msgb *msg, int proto);
int ipaccess_send_id_ack(int fd);
int ipaccess_send_id_req(int fd);
#endif /* _IPACCESS_H */

1
include/mgcp/Makefile.am Normal file
View File

@ -0,0 +1 @@
noinst_HEADERS = mgcp.h mgcp_internal.h

141
include/mgcp/mgcp.h Normal file
View File

@ -0,0 +1,141 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/*
* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef OPENBSC_MGCP_H
#define OPENBSC_MGCP_H
#include <laf0rge1/msgb.h>
#include <arpa/inet.h>
#define RTP_PORT_DEFAULT 4000
/**
* Calculate the RTP audio port for the given multiplex
* and the direction. This allows a semi static endpoint
* to port calculation removing the need for the BSC
* and the MediaGateway to communicate.
*
* Port usage explained:
* base + (multiplex * 2) + 0 == local port to wait for network packets
* base + (multiplex * 2) + 1 == local port for rtcp
*
* The above port will receive packets from the BTS that need
* to be patched and forwarded to the network.
* The above port will receive packets from the network that
* need to be patched and forwarded to the BTS.
*
* We assume to have a static BTS IP address so we can differentiate
* network and BTS.
*
*/
static inline int rtp_calculate_port(int multiplex, int base)
{
return base + (multiplex * 2);
}
/*
* Handling of MGCP Endpoints and the MGCP Config
*/
struct mgcp_endpoint;
struct mgcp_config;
#define MGCP_ENDP_CRCX 1
#define MGCP_ENDP_DLCX 2
#define MGCP_ENDP_MDCX 3
/*
* what to do with the msg?
* - continue as usual?
* - reject and send a failure code?
* - defer? do not send anything
*/
#define MGCP_POLICY_CONT 4
#define MGCP_POLICY_REJECT 5
#define MGCP_POLICY_DEFER 6
typedef int (*mgcp_change)(struct mgcp_config *cfg, int endpoint, int state, int local_rtp);
typedef int (*mgcp_policy)(struct mgcp_config *cfg, int endpoint, int state, const char *transactio_id);
typedef int (*mgcp_reset)(struct mgcp_config *cfg);
struct mgcp_config {
/* common configuration */
int source_port;
char *local_ip;
char *source_addr;
char *bts_ip;
char *call_agent_addr;
/* default endpoint data */
struct in_addr bts_in;
char *audio_name;
int audio_payload;
int audio_loop;
int early_bind;
int rtp_base_port;
int endp_dscp;
/* only used in forward mode */
char *forward_ip;
int forward_port;
unsigned int last_call_id;
/* endpoint configuration */
unsigned int number_endpoints;
struct mgcp_endpoint *endpoints;
/* spec handling */
int force_realloc;
/* callback functionality */
mgcp_change change_cb;
mgcp_policy policy_cb;
mgcp_reset reset_cb;
void *data;
};
/* config management */
struct mgcp_config *mgcp_config_alloc(void);
int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg);
int mgcp_vty_init(void);
int mgcp_endpoints_allocate(struct mgcp_config *cfg);
int mgcp_bind_rtp_port(struct mgcp_endpoint *endp, int rtp_port);
void mgcp_free_endp(struct mgcp_endpoint *endp);
/*
* format helper functions
*/
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);
struct msgb *mgcp_create_response_with_data(int code, const char *msg, const char *trans, const char *data);
/* adc helper */
static inline int mgcp_timeslot_to_endpoint(int multiplex, int timeslot)
{
if (timeslot == 0)
timeslot = 1;
return timeslot + (31 * multiplex);
}
#endif

View File

@ -0,0 +1,89 @@
/* MGCP Private Data */
/*
* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef OPENBSC_MGCP_DATA_H
#define OPENBSC_MGCP_DATA_H
#include <laf0rge1/select.h>
#define CI_UNUSED 0
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
#endif
enum mgcp_connection_mode {
MGCP_CONN_NONE = 0,
MGCP_CONN_RECV_ONLY = 1,
MGCP_CONN_SEND_ONLY = 2,
MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY,
};
struct mgcp_endpoint {
int ci;
char *callid;
char *local_options;
int conn_mode;
int bts_payload_type;
int net_payload_type;
/* the local rtp port we are binding to */
int rtp_port;
/*
* RTP mangling:
* - we get RTP and RTCP to us and need to forward to the BTS
* - we get RTP and RTCP from the BTS and forward to the network
*/
struct bsc_fd local_rtp;
struct bsc_fd local_rtcp;
struct in_addr remote;
struct in_addr bts;
/* in network byte order */
int net_rtp, net_rtcp;
int bts_rtp, bts_rtcp;
/* backpointer */
struct mgcp_config *cfg;
/* statistics */
unsigned int in_bts;
unsigned int in_remote;
};
#define ENDPOINT_NUMBER(endp) abs(endp - endp->cfg->endpoints)
struct mgcp_msg_ptr {
unsigned int start;
unsigned int length;
};
int mgcp_analyze_header(struct mgcp_config *cfg, struct msgb *msg,
struct mgcp_msg_ptr *ptr, int size,
const char **transaction_id, struct mgcp_endpoint **endp);
int mgcp_send_dummy(struct mgcp_endpoint *endp);
#endif

68
include/mgcp_ss7.h Normal file
View File

@ -0,0 +1,68 @@
/* mgcp_ss7 helper coder */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef mgcp_ss7_h
#define mgcp_ss7_h
#include <laf0rge1/timer.h>
#include "write_queue.h"
#include "thread.h"
struct mgcp_ss7_endpoint;
struct mgcp_ss7 {
struct mgcp_config *cfg;
struct write_queue mgcp_fd;
struct msgb *mgcp_msg;
struct mgcp_ss7_endpoint *mgw_end;
/* timer */
struct timer_list poll_timer;
/* thread handling */
struct thread_notifier *cmd_queue;
pthread_t thread;
};
enum {
MGCP_SS7_MUTE_STATUS,
MGCP_SS7_ALLOCATE,
MGCP_SS7_DELETE,
MGCP_SS7_SHUTDOWN,
};
struct mgcp_ss7_cmd {
struct llist_head entry;
u_int8_t type;
u_int32_t port;
u_int32_t param;
};
void mgcp_ss7_exec(struct mgcp_ss7 *mgcp, u_int8_t type, u_int32_t port, u_int32_t param);
struct mgcp_ss7 *mgcp_ss7_init(int endpoints, const char *local_ip, const char *mgw_ip, int base_port, int payload);
void mgcp_ss7_reset(struct mgcp_ss7 *mgcp);
void mgcp_ss7_free(struct mgcp_ss7 *mgcp);
#endif

88
include/mtp_data.h Normal file
View File

@ -0,0 +1,88 @@
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef mtp_data_h
#define mtp_data_h
#include <laf0rge1/msgb.h>
#include <laf0rge1/timer.h>
/* MTP Level3 timers */
/* Timers for SS7 */
#define MTP_T1 12, 0
#define MTP_T2 30, 0
#define START_DELAY 8, 0
/**
* The state of the mtp_link in terms of layer3 and upwards
*/
struct mtp_link {
/* routing info.. */
int dpc, opc;
/* internal state */
/* the MTP1 link is up */
int available;
int running;
int sccp_up;
/* misc data */
u_int8_t test_ptrn[14];
int sltm_pending;
struct llist_head pending_msgs;
int sltm_once;
int was_up;
/* the associated link */
int link;
int slta_misses;
struct timer_list t1_timer;
struct timer_list t2_timer;
struct timer_list delay_timer;
};
struct mtp_link *mtp_link_alloc(void);
void mtp_link_stop(struct mtp_link *link);
void mtp_link_reset(struct mtp_link *link);
int mtp_link_data(struct mtp_link *link, struct msgb *msg);
int mtp_link_submit_sccp_data(struct mtp_link *link, int sls, const u_int8_t *data, unsigned int length);
/* one time init function */
void mtp_link_init(void);
/* to be implemented for MSU sending */
void mtp_link_submit(struct mtp_link *link, struct msgb *msg);
void mtp_link_forward_sccp(struct mtp_link *link, struct msgb *msg, int sls);
void mtp_link_restart(struct mtp_link *link);
void mtp_link_slta_recv(struct mtp_link *link);
void mtp_link_sccp_down(struct mtp_link *link);
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
#endif
#endif

164
include/mtp_level3.h Normal file
View File

@ -0,0 +1,164 @@
/* Q.701-Q.704, Q.706, Q.707 handling code */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef mtp_level3_h
#define mtp_level3_h
#include <endian.h>
#include <sys/types.h>
/*
* pssible service information octets..
*/
#define MTP_NI_NATION_NET 0x02
#define MTP_SI_MNT_SNM_MSG 0x00
#define MTP_SI_MNT_REG_MSG 0x01
#define MTP_SI_MNT_SCCP 0x03
/*
* h0 contains the group, h1 the semantic of it
*/
#define MTP_TST_MSG_GRP 0x01
#define MTP_PROHIBIT_MSG_GRP 0x04
#define MTP_TRF_RESTR_MSG_GRP 0x07
/* h1 values for different groups */
#define MTP_TST_MSG_SLTM 0x01
#define MTP_TST_MSG_SLTA 0x02
#define MTP_RESTR_MSG_ALLWED 0x01
#define MTP_PROHIBIT_MSG_SIG 0x01
#define SCCP_SST 0x03
#define SCCP_SSA 0x01
#define MTP_LINK_MASK 0x0F
#define MTP_ADDR_MASK 0x0FFF
#define MTP_APOC_MASK 0x3f
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define MTP_LINK_SLS(addr) ((addr >>28) & MTP_LINK_MASK)
#define MTP_ADDR(link, dpc, opc) \
(((dpc) & MTP_ADDR_MASK) << 0 | \
((opc) & MTP_ADDR_MASK) << 14| \
((link) & MTP_LINK_MASK) << 28)
#define MTP_MAKE_APOC(apoc) \
(apoc & 0x3fff)
#elif __BYTE_ORDER == __BIG_ENDIAN
static inline u_int32_t c_swap_32(u_int32_t in)
{
return (((in & 0x000000ff) << 24) |
((in & 0x0000ff00) << 8) |
((in & 0x00ff0000) >> 8) |
((in & 0xff000000) >> 24));
}
static inline u_int16_t c_swap_16(u_int16_t in)
{
return (((in & 0x00ff) << 8) |
(in & 0xff00) >> 8);
}
#define MTP_LINK_SLS(addr) ((c_swap_32(addr)>>28) & MTP_LINK_MASK)
#define MTP_ADDR(link, dpc, opc) \
c_swap_32(((dpc) & MTP_ADDR_MASK) << 0 | \
((opc) & MTP_ADDR_MASK) << 14| \
((link) & MTP_LINK_MASK) << 28)
#define MTP_MAKE_APOC(apoc) \
c_swap_16((apoc & 0x3fff))
#endif
/*
* not the on wire address...
*/
struct mtp_addr {
u_int16_t dpc;
u_int16_t opc;
u_int8_t link;
} __attribute__((packed));
/*
* the struct is defined in Q.704 and can be seen in the
* wireshark dissectors too
*/
struct mtp_level_3_hdr {
#if __BYTE_ORDER == __LITTLE_ENDIAN
u_int8_t ser_ind : 4,
spare : 2,
ni : 2;
#elif __BYTE_ORDER == __BIG_ENDIAN
u_int8_t ni : 2,
spare : 2,
ser_ind : 4;
#endif
u_int32_t addr;
u_int8_t data[0];
} __attribute__((packed));
struct mtp_level_3_cmn {
#if __BYTE_ORDER == __LITTLE_ENDIAN
u_int8_t h0 : 4,
h1 : 4;
#elif __BYTE_ORDER == __BIG_ENDIAN
u_int8_t h1 : 4,
h0 : 4;
#endif
} __attribute__((packed));
struct mtp_level_3_mng {
struct mtp_level_3_cmn cmn;
#if __BYTE_ORDER == __LITTLE_ENDIAN
u_int8_t spare : 4,
length : 4;
#elif __BYTE_ORDER == __BIG_ENDIAN
u_int8_t length : 4,
spare : 4;
#endif
u_int8_t data[0];
} __attribute__((packed));
struct mtp_level_3_prohib {
struct mtp_level_3_cmn cmn;
u_int16_t apoc;
} __attribute__((packed));
struct sccp_con_ctrl_prt_mgt {
u_int8_t sst;
u_int8_t assn; /* affected sub system number */
u_int16_t apoc;
#if __BYTE_ORDER == __LITTLE_ENDIAN
u_int8_t mul_ind : 2,
spare : 6;
#elif __BYTE_ORDER == __BIG_ENDIAN
u_int8_t spare : 6,
mul_ind : 2;
#endif
} __attribute__((packed));
#endif

29
include/mtp_pcap.h Normal file
View File

@ -0,0 +1,29 @@
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef mtp_pcap_h
#define mtp_pcap_h
#include <sys/types.h>
int mtp_pcap_write_header(int fd);
int mtp_pcap_write_msu(int fd, const u_int8_t *data, int length);
#endif

View File

@ -0,0 +1 @@
noinst_HEADERS = bssap.h tlv.h

339
include/openbsc_nat/bssap.h Normal file
View File

@ -0,0 +1,339 @@
/* From GSM08.08 */
#ifndef BSSAP_H
#define BSSAP_H
#include <stdlib.h>
#include <laf0rge1/msgb.h>
#include <sccp/sccp.h>
struct gsm_network;
struct bss_sccp_connection_data;
/*
* this is from GSM 03.03 CGI but is copied in GSM 08.08
* in § 3.2.2.27 for Cell Identifier List
*/
enum CELL_IDENT {
CELL_IDENT_WHOLE_GLOBAL = 0,
CELL_IDENT_LAC_AND_CI = 1,
CELL_IDENT_CI = 2,
CELL_IDENT_NO_CELL = 3,
CELL_IDENT_LAI_AND_LAC = 4,
CELL_IDENT_LAC = 5,
CELL_IDENT_BSS = 6,
CELL_IDENT_UTRAN_PLMN_LAC_RNC = 8,
CELL_IDENT_UTRAN_RNC = 9,
CELL_IDENT_UTRAN_LAC_RNC = 10,
};
/* GSM 08.06 § 6.3 */
enum BSSAP_MSG_TYPE {
BSSAP_MSG_BSS_MANAGEMENT = 0x0,
BSSAP_MSG_DTAP = 0x1,
};
struct bssmap_header {
u_int8_t type;
u_int8_t length;
} __attribute__((packed));
struct dtap_header {
u_int8_t type;
u_int8_t link_id;
u_int8_t length;
} __attribute__((packed));
enum BSS_MAP_MSG_TYPE {
BSS_MAP_MSG_RESERVED_0 = 0,
/* ASSIGNMENT MESSAGES */
BSS_MAP_MSG_ASSIGMENT_RQST = 1,
BSS_MAP_MSG_ASSIGMENT_COMPLETE = 2,
BSS_MAP_MSG_ASSIGMENT_FAILURE = 3,
/* HANDOVER MESSAGES */
BSS_MAP_MSG_HANDOVER_RQST = 16,
BSS_MAP_MSG_HANDOVER_REQUIRED = 17,
BSS_MAP_MSG_HANDOVER_RQST_ACKNOWLEDGE= 18,
BSS_MAP_MSG_HANDOVER_CMD = 19,
BSS_MAP_MSG_HANDOVER_COMPLETE = 20,
BSS_MAP_MSG_HANDOVER_SUCCEEDED = 21,
BSS_MAP_MSG_HANDOVER_FAILURE = 22,
BSS_MAP_MSG_HANDOVER_PERFORMED = 23,
BSS_MAP_MSG_HANDOVER_CANDIDATE_ENQUIRE = 24,
BSS_MAP_MSG_HANDOVER_CANDIDATE_RESPONSE = 25,
BSS_MAP_MSG_HANDOVER_REQUIRED_REJECT = 26,
BSS_MAP_MSG_HANDOVER_DETECT = 27,
/* RELEASE MESSAGES */
BSS_MAP_MSG_CLEAR_CMD = 32,
BSS_MAP_MSG_CLEAR_COMPLETE = 33,
BSS_MAP_MSG_CLEAR_RQST = 34,
BSS_MAP_MSG_RESERVED_1 = 35,
BSS_MAP_MSG_RESERVED_2 = 36,
BSS_MAP_MSG_SAPI_N_REJECT = 37,
BSS_MAP_MSG_CONFUSION = 38,
/* OTHER CONNECTION RELATED MESSAGES */
BSS_MAP_MSG_SUSPEND = 40,
BSS_MAP_MSG_RESUME = 41,
BSS_MAP_MSG_CONNECTION_ORIENTED_INFORMATION = 42,
BSS_MAP_MSG_PERFORM_LOCATION_RQST = 43,
BSS_MAP_MSG_LSA_INFORMATION = 44,
BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE = 45,
BSS_MAP_MSG_PERFORM_LOCATION_ABORT = 46,
BSS_MAP_MSG_COMMON_ID = 47,
/* GENERAL MESSAGES */
BSS_MAP_MSG_RESET = 48,
BSS_MAP_MSG_RESET_ACKNOWLEDGE = 49,
BSS_MAP_MSG_OVERLOAD = 50,
BSS_MAP_MSG_RESERVED_3 = 51,
BSS_MAP_MSG_RESET_CIRCUIT = 52,
BSS_MAP_MSG_RESET_CIRCUIT_ACKNOWLEDGE = 53,
BSS_MAP_MSG_MSC_INVOKE_TRACE = 54,
BSS_MAP_MSG_BSS_INVOKE_TRACE = 55,
BSS_MAP_MSG_CONNECTIONLESS_INFORMATION = 58,
/* TERRESTRIAL RESOURCE MESSAGES */
BSS_MAP_MSG_BLOCK = 64,
BSS_MAP_MSG_BLOCKING_ACKNOWLEDGE = 65,
BSS_MAP_MSG_UNBLOCK = 66,
BSS_MAP_MSG_UNBLOCKING_ACKNOWLEDGE = 67,
BSS_MAP_MSG_CIRCUIT_GROUP_BLOCK = 68,
BSS_MAP_MSG_CIRCUIT_GROUP_BLOCKING_ACKNOWLEDGE = 69,
BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCK = 70,
BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCKING_ACKNOWLEDGE = 71,
BSS_MAP_MSG_UNEQUIPPED_CIRCUIT = 72,
BSS_MAP_MSG_CHANGE_CIRCUIT = 78,
BSS_MAP_MSG_CHANGE_CIRCUIT_ACKNOWLEDGE = 79,
/* RADIO RESOURCE MESSAGES */
BSS_MAP_MSG_RESOURCE_RQST = 80,
BSS_MAP_MSG_RESOURCE_INDICATION = 81,
BSS_MAP_MSG_PAGING = 82,
BSS_MAP_MSG_CIPHER_MODE_CMD = 83,
BSS_MAP_MSG_CLASSMARK_UPDATE = 84,
BSS_MAP_MSG_CIPHER_MODE_COMPLETE = 85,
BSS_MAP_MSG_QUEUING_INDICATION = 86,
BSS_MAP_MSG_COMPLETE_LAYER_3 = 87,
BSS_MAP_MSG_CLASSMARK_RQST = 88,
BSS_MAP_MSG_CIPHER_MODE_REJECT = 89,
BSS_MAP_MSG_LOAD_INDICATION = 90,
/* VGCS/VBS */
BSS_MAP_MSG_VGCS_VBS_SETUP = 4,
BSS_MAP_MSG_VGCS_VBS_SETUP_ACK = 5,
BSS_MAP_MSG_VGCS_VBS_SETUP_REFUSE = 6,
BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST = 7,
BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT = 28,
BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE = 29,
BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION = 30,
BSS_MAP_MSG_UPLINK_RQST = 31,
BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE = 39,
BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION = 73,
BSS_MAP_MSG_UPLINK_RELEASE_INDICATION = 74,
BSS_MAP_MSG_UPLINK_REJECT_CMD = 75,
BSS_MAP_MSG_UPLINK_RELEASE_CMD = 76,
BSS_MAP_MSG_UPLINK_SEIZED_CMD = 77,
};
enum GSM0808_IE_CODING {
GSM0808_IE_CIRCUIT_IDENTITY_CODE = 1,
GSM0808_IE_RESERVED_0 = 2,
GSM0808_IE_RESOURCE_AVAILABLE = 3,
GSM0808_IE_CAUSE = 4,
GSM0808_IE_CELL_IDENTIFIER = 5,
GSM0808_IE_PRIORITY = 6,
GSM0808_IE_LAYER_3_HEADER_INFORMATION = 7,
GSM0808_IE_IMSI = 8,
GSM0808_IE_TMSI = 9,
GSM0808_IE_ENCRYPTION_INFORMATION = 10,
GSM0808_IE_CHANNEL_TYPE = 11,
GSM0808_IE_PERIODICITY = 12,
GSM0808_IE_EXTENDED_RESOURCE_INDICATOR = 13,
GSM0808_IE_NUMBER_OF_MSS = 14,
GSM0808_IE_RESERVED_1 = 15,
GSM0808_IE_RESERVED_2 = 16,
GSM0808_IE_RESERVED_3 = 17,
GSM0808_IE_CLASSMARK_INFORMATION_T2 = 18,
GSM0808_IE_CLASSMARK_INFORMATION_T3 = 19,
GSM0808_IE_INTERFERENCE_BAND_TO_USE = 20,
GSM0808_IE_RR_CAUSE = 21,
GSM0808_IE_RESERVED_4 = 22,
GSM0808_IE_LAYER_3_INFORMATION = 23,
GSM0808_IE_DLCI = 24,
GSM0808_IE_DOWNLINK_DTX_FLAG = 25,
GSM0808_IE_CELL_IDENTIFIER_LIST = 26,
GSM0808_IE_RESPONSE_RQST = 27,
GSM0808_IE_RESOURCE_INDICATION_METHOD = 28,
GSM0808_IE_CLASSMARK_INFORMATION_TYPE_1 = 29,
GSM0808_IE_CIRCUIT_IDENTITY_CODE_LIST = 30,
GSM0808_IE_DIAGNOSTIC = 31,
GSM0808_IE_LAYER_3_MESSAGE_CONTENTS = 32,
GSM0808_IE_CHOSEN_CHANNEL = 33,
GSM0808_IE_TOTAL_RESOURCE_ACCESSIBLE = 34,
GSM0808_IE_CIPHER_RESPONSE_MODE = 35,
GSM0808_IE_CHANNEL_NEEDED = 36,
GSM0808_IE_TRACE_TYPE = 37,
GSM0808_IE_TRIGGERID = 38,
GSM0808_IE_TRACE_REFERENCE = 39,
GSM0808_IE_TRANSACTIONID = 40,
GSM0808_IE_MOBILE_IDENTITY = 41,
GSM0808_IE_OMCID = 42,
GSM0808_IE_FORWARD_INDICATOR = 43,
GSM0808_IE_CHOSEN_ENCR_ALG = 44,
GSM0808_IE_CIRCUIT_POOL = 45,
GSM0808_IE_CIRCUIT_POOL_LIST = 46,
GSM0808_IE_TIME_INDICATION = 47,
GSM0808_IE_RESOURCE_SITUATION = 48,
GSM0808_IE_CURRENT_CHANNEL_TYPE_1 = 49,
GSM0808_IE_QUEUEING_INDICATOR = 50,
GSM0808_IE_SPEECH_VERSION = 64,
GSM0808_IE_ASSIGNMENT_REQUIREMENT = 51,
GSM0808_IE_TALKER_FLAG = 53,
GSM0808_IE_CONNECTION_RELEASE_RQSTED = 54,
GSM0808_IE_GROUP_CALL_REFERENCE = 55,
GSM0808_IE_EMLPP_PRIORITY = 56,
GSM0808_IE_CONFIG_EVO_INDI = 57,
GSM0808_IE_OLD_BSS_TO_NEW_BSS_INFORMATION = 58,
GSM0808_IE_LSA_IDENTIFIER = 59,
GSM0808_IE_LSA_IDENTIFIER_LIST = 60,
GSM0808_IE_LSA_INFORMATION = 61,
GSM0808_IE_LCS_QOS = 62,
GSM0808_IE_LSA_ACCESS_CTRL_SUPPR = 63,
GSM0808_IE_LCS_PRIORITY = 67,
GSM0808_IE_LOCATION_TYPE = 68,
GSM0808_IE_LOCATION_ESTIMATE = 69,
GSM0808_IE_POSITIONING_DATA = 70,
GSM0808_IE_LCS_CAUSE = 71,
GSM0808_IE_LCS_CLIENT_TYPE = 72,
GSM0808_IE_APDU = 73,
GSM0808_IE_NETWORK_ELEMENT_IDENTITY = 74,
GSM0808_IE_GPS_ASSISTANCE_DATA = 75,
GSM0808_IE_DECIPHERING_KEYS = 76,
GSM0808_IE_RETURN_ERROR_RQST = 77,
GSM0808_IE_RETURN_ERROR_CAUSE = 78,
GSM0808_IE_SEGMENTATION = 79,
GSM0808_IE_SERVICE_HANDOVER = 80,
GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_UMTS = 81,
GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_CDMA2000= 82,
GSM0808_IE_RESERVED_5 = 65,
GSM0808_IE_RESERVED_6 = 66,
};
enum gsm0808_cause {
GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE = 0,
GSM0808_CAUSE_RADIO_INTERFACE_FAILURE = 1,
GSM0808_CAUSE_UPLINK_QUALITY = 2,
GSM0808_CAUSE_UPLINK_STRENGTH = 3,
GSM0808_CAUSE_DOWNLINK_QUALITY = 4,
GSM0808_CAUSE_DOWNLINK_STRENGTH = 5,
GSM0808_CAUSE_DISTANCE = 6,
GSM0808_CAUSE_O_AND_M_INTERVENTION = 7,
GSM0808_CAUSE_RESPONSE_TO_MSC_INVOCATION = 8,
GSM0808_CAUSE_CALL_CONTROL = 9,
GSM0808_CAUSE_RADIO_INTERFACE_FAILURE_REVERSION = 10,
GSM0808_CAUSE_HANDOVER_SUCCESSFUL = 11,
GSM0808_CAUSE_BETTER_CELL = 12,
GSM0808_CAUSE_DIRECTED_RETRY = 13,
GSM0808_CAUSE_JOINED_GROUP_CALL_CHANNEL = 14,
GSM0808_CAUSE_TRAFFIC = 15,
GSM0808_CAUSE_EQUIPMENT_FAILURE = 32,
GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE = 33,
GSM0808_CAUSE_RQSTED_TERRESTRIAL_RESOURCE_UNAVAILABLE = 34,
GSM0808_CAUSE_CCCH_OVERLOAD = 35,
GSM0808_CAUSE_PROCESSOR_OVERLOAD = 36,
GSM0808_CAUSE_BSS_NOT_EQUIPPED = 37,
GSM0808_CAUSE_MS_NOT_EQUIPPED = 38,
GSM0808_CAUSE_INVALID_CELL = 39,
GSM0808_CAUSE_TRAFFIC_LOAD = 40,
GSM0808_CAUSE_PREEMPTION = 41,
GSM0808_CAUSE_RQSTED_TRANSCODING_RATE_ADAPTION_UNAVAILABLE = 48,
GSM0808_CAUSE_CIRCUIT_POOL_MISMATCH = 49,
GSM0808_CAUSE_SWITCH_CIRCUIT_POOL = 50,
GSM0808_CAUSE_RQSTED_SPEECH_VERSION_UNAVAILABLE = 51,
GSM0808_CAUSE_LSA_NOT_ALLOWED = 52,
GSM0808_CAUSE_CIPHERING_ALGORITHM_NOT_SUPPORTED = 64,
GSM0808_CAUSE_TERRESTRIAL_CIRCUIT_ALREADY_ALLOCATED = 80,
GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS = 81,
GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING = 82,
GSM0808_CAUSE_INCORRECT_VALUE = 83,
GSM0808_CAUSE_UNKNOWN_MESSAGE_TYPE = 84,
GSM0808_CAUSE_UNKNOWN_INFORMATION_ELEMENT = 85,
GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC = 96,
};
/* GSM 08.08 3.2.2.11 Channel Type */
enum gsm0808_chan_indicator {
GSM0808_CHAN_SPEECH = 1,
GSM0808_CHAN_DATA = 2,
GSM0808_CHAN_SIGN = 3,
};
enum gsm0808_chan_rate_type_data {
GSM0808_DATA_FULL_BM = 0x8,
GSM0808_DATA_HALF_LM = 0x9,
GSM0808_DATA_FULL_RPREF = 0xa,
GSM0808_DATA_HALF_PREF = 0xb,
GSM0808_DATA_FULL_PREF_NO_CHANGE = 0x1a,
GSM0808_DATA_HALF_PREF_NO_CHANGE = 0x1b,
GSM0808_DATA_MULTI_MASK = 0x20,
GSM0808_DATA_MULTI_MASK_NO_CHANGE = 0x30,
};
enum gsm0808_chan_rate_type_speech {
GSM0808_SPEECH_FULL_BM = 0x8,
GSM0808_SPEECH_HALF_LM = 0x9,
GSM0808_SPEECH_FULL_PREF= 0xa,
GSM0808_SPEECH_HALF_PREF= 0xb,
GSM0808_SPEECH_FULL_PREF_NO_CHANGE = 0x1a,
GSM0808_SPEECH_HALF_PREF_NO_CHANGE = 0x1b,
GSM0808_SPEECH_PERM = 0xf,
GSM0808_SPEECH_PERM_NO_CHANGE = 0x1f,
};
enum gsm0808_permitted_speech {
GSM0808_PERM_FR1 = 0x01,
GSM0808_PERM_FR2 = 0x11,
GSM0808_PERM_FR3 = 0x21,
GSM0808_PERM_HR1 = GSM0808_PERM_FR1 | 0x4,
GSM0808_PERM_HR2 = GSM0808_PERM_FR2 | 0x4,
GSM0808_PERM_HR3 = GSM0808_PERM_FR3 | 0x4,
};
int bssmap_rcvmsg_dt1(struct sccp_connection *conn, struct msgb *msg, unsigned int length);
int bssmap_rcvmsg_udt(struct gsm_network *net, struct msgb *msg, unsigned int length);
struct msgb *bssmap_create_layer3(struct msgb *msg);
struct msgb *bssmap_create_reset(void);
struct msgb *bssmap_create_clear_complete(void);
struct msgb *bssmap_create_cipher_complete(struct msgb *layer3);
struct msgb *bssmap_create_cipher_reject(u_int8_t cause);
struct msgb *bssmap_create_sapi_reject(u_int8_t link_id);
struct msgb *bssmap_create_assignment_completed(struct gsm_lchan *lchan, u_int8_t rr_cause);
struct msgb *bssmap_create_assignment_failure(u_int8_t cause, u_int8_t *rr_cause);
struct msgb *bssmap_create_classmark_update(const u_int8_t *classmark, u_int8_t length);
void gsm0808_send_assignment_failure(struct gsm_lchan *l, u_int8_t cause, u_int8_t *rr_value);
void gsm0808_send_assignment_compl(struct gsm_lchan *l, u_int8_t rr_value);
int dtap_rcvmsg(struct gsm_lchan *lchan, struct msgb *msg, unsigned int length);
struct msgb *dtap_create_msg(struct msgb *msg_l3, u_int8_t link_id);
void bsc_queue_connection_write(struct sccp_connection *conn, struct msgb *msg);
void bsc_free_queued(struct sccp_connection *conn);
void bsc_send_queued(struct sccp_connection *conn);
void bts_queue_send(struct msgb *msg, int link_id);
void bts_send_queued(struct bss_sccp_connection_data*);
void bts_free_queued(struct bss_sccp_connection_data*);
void bts_unblock_queue(struct bss_sccp_connection_data*);
const struct tlv_definition *gsm0808_att_tlvdef();
#endif

234
include/openbsc_nat/tlv.h Normal file
View File

@ -0,0 +1,234 @@
#ifndef _TLV_H
#define _TLV_H
#include <sys/types.h>
#include <string.h>
#include <laf0rge1/msgb.h>
/* Terminology / wording
tag length value (in bits)
V - - 8
LV - 8 N * 8
TLV 8 8 N * 8
TL16V 8 16 N * 8
TLV16 8 8 N * 16
TvLV 8 8/16 N * 8
*/
#define LV_GROSS_LEN(x) (x+1)
#define TLV_GROSS_LEN(x) (x+2)
#define TLV16_GROSS_LEN(x) ((2*x)+2)
#define TL16V_GROSS_LEN(x) (x+3)
#define L16TV_GROSS_LEN(x) (x+3)
#define TVLV_MAX_ONEBYTE 0x7f
static inline u_int16_t TVLV_GROSS_LEN(u_int16_t len)
{
if (len <= TVLV_MAX_ONEBYTE)
return TLV_GROSS_LEN(len);
else
return TL16V_GROSS_LEN(len);
}
/* TLV generation */
static inline u_int8_t *lv_put(u_int8_t *buf, u_int8_t len,
const u_int8_t *val)
{
*buf++ = len;
memcpy(buf, val, len);
return buf + len;
}
static inline u_int8_t *tlv_put(u_int8_t *buf, u_int8_t tag, u_int8_t len,
const u_int8_t *val)
{
*buf++ = tag;
*buf++ = len;
memcpy(buf, val, len);
return buf + len;
}
static inline u_int8_t *tlv16_put(u_int8_t *buf, u_int8_t tag, u_int8_t len,
const u_int16_t *val)
{
*buf++ = tag;
*buf++ = len;
memcpy(buf, val, len*2);
return buf + len*2;
}
static inline u_int8_t *tl16v_put(u_int8_t *buf, u_int8_t tag, u_int16_t len,
const u_int8_t *val)
{
*buf++ = tag;
*buf++ = len >> 8;
*buf++ = len & 0xff;
memcpy(buf, val, len);
return buf + len*2;
}
static inline u_int8_t *tvlv_put(u_int8_t *buf, u_int8_t tag, u_int16_t len,
const u_int8_t *val)
{
u_int8_t *ret;
if (len <= TVLV_MAX_ONEBYTE) {
ret = tlv_put(buf, tag, len, val);
buf[1] |= 0x80;
} else
ret = tl16v_put(buf, tag, len, val);
return ret;
}
static inline u_int8_t *msgb_tlv16_put(struct msgb *msg, u_int8_t tag, u_int8_t len, const u_int16_t *val)
{
u_int8_t *buf = msgb_put(msg, TLV16_GROSS_LEN(len));
return tlv16_put(buf, tag, len, val);
}
static inline u_int8_t *msgb_tl16v_put(struct msgb *msg, u_int8_t tag, u_int16_t len,
const u_int8_t *val)
{
u_int8_t *buf = msgb_put(msg, TL16V_GROSS_LEN(len));
return tl16v_put(buf, tag, len, val);
}
static inline u_int8_t *msgb_tvlv_put(struct msgb *msg, u_int8_t tag, u_int16_t len,
const u_int8_t *val)
{
u_int8_t *buf = msgb_put(msg, TVLV_GROSS_LEN(len));
return tvlv_put(buf, tag, len, val);
}
static inline u_int8_t *msgb_l16tv_put(struct msgb *msg, u_int16_t len, u_int8_t tag,
const u_int8_t *val)
{
u_int8_t *buf = msgb_put(msg, L16TV_GROSS_LEN(len));
*buf++ = len >> 8;
*buf++ = len & 0xff;
*buf++ = tag;
memcpy(buf, val, len);
return buf + len;
}
static inline u_int8_t *v_put(u_int8_t *buf, u_int8_t val)
{
*buf++ = val;
return buf;
}
static inline u_int8_t *tv_put(u_int8_t *buf, u_int8_t tag,
u_int8_t val)
{
*buf++ = tag;
*buf++ = val;
return buf;
}
/* 'val' is still in host byte order! */
static inline u_int8_t *tv16_put(u_int8_t *buf, u_int8_t tag,
u_int16_t val)
{
*buf++ = tag;
*buf++ = val >> 8;
*buf++ = val & 0xff;
return buf;
}
static inline u_int8_t *msgb_lv_put(struct msgb *msg, u_int8_t len, const u_int8_t *val)
{
u_int8_t *buf = msgb_put(msg, LV_GROSS_LEN(len));
return lv_put(buf, len, val);
}
static inline u_int8_t *msgb_tlv_put(struct msgb *msg, u_int8_t tag, u_int8_t len, const u_int8_t *val)
{
u_int8_t *buf = msgb_put(msg, TLV_GROSS_LEN(len));
return tlv_put(buf, tag, len, val);
}
static inline u_int8_t *msgb_tv_put(struct msgb *msg, u_int8_t tag, u_int8_t val)
{
u_int8_t *buf = msgb_put(msg, 2);
return tv_put(buf, tag, val);
}
static inline u_int8_t *msgb_v_put(struct msgb *msg, u_int8_t val)
{
u_int8_t *buf = msgb_put(msg, 1);
return v_put(buf, val);
}
static inline u_int8_t *msgb_tv16_put(struct msgb *msg, u_int8_t tag, u_int16_t val)
{
u_int8_t *buf = msgb_put(msg, 3);
return tv16_put(buf, tag, val);
}
static inline u_int8_t *msgb_tlv_push(struct msgb *msg, u_int8_t tag, u_int8_t len, const u_int8_t *val)
{
u_int8_t *buf = msgb_push(msg, TLV_GROSS_LEN(len));
return tlv_put(buf, tag, len, val);
}
static inline u_int8_t *msgb_tv_push(struct msgb *msg, u_int8_t tag, u_int8_t val)
{
u_int8_t *buf = msgb_push(msg, 2);
return tv_put(buf, tag, val);
}
static inline u_int8_t *msgb_tv16_push(struct msgb *msg, u_int8_t tag, u_int16_t val)
{
u_int8_t *buf = msgb_push(msg, 3);
return tv16_put(buf, tag, val);
}
/* TLV parsing */
struct tlv_p_entry {
u_int16_t len;
const u_int8_t *val;
};
enum tlv_type {
TLV_TYPE_FIXED,
TLV_TYPE_T,
TLV_TYPE_TV,
TLV_TYPE_TLV,
TLV_TYPE_TL16V,
TLV_TYPE_TvLV,
};
struct tlv_def {
enum tlv_type type;
u_int8_t fixed_len;
};
struct tlv_definition {
struct tlv_def def[0xff];
};
struct tlv_parsed {
struct tlv_p_entry lv[0xff];
};
extern struct tlv_definition tvlv_att_def;
int tlv_parse_one(u_int8_t *o_tag, u_int16_t *o_len, const u_int8_t **o_val,
const struct tlv_definition *def,
const u_int8_t *buf, int buf_len);
int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
const u_int8_t *buf, int buf_len, u_int8_t lv_tag, u_int8_t lv_tag2);
#define TLVP_PRESENT(x, y) ((x)->lv[y].val)
#define TLVP_LEN(x, y) (x)->lv[y].len
#define TLVP_VAL(x, y) (x)->lv[y].val
#endif /* _TLV_H */

39
include/snmp_mtp.h Normal file
View File

@ -0,0 +1,39 @@
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef snmp_mtp_h
#define snmp_mtp_h
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/utilities.h>
#include <net-snmp/net-snmp-includes.h>
struct snmp_mtp_session {
netsnmp_session session, *ss;
};
void snmp_mtp_start_c7_datalink(struct snmp_mtp_session *, int link_id);
void snmp_mtp_stop_c7_datalink(struct snmp_mtp_session *, int link_id);
struct snmp_mtp_session *snmp_mtp_session_create(char *host);
void snmp_mtp_deactivate(struct snmp_mtp_session *);
void snmp_mtp_activate(struct snmp_mtp_session *);
#endif

59
include/thread.h Normal file
View File

@ -0,0 +1,59 @@
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef thread_h
#define thread_h
#include <laf0rge1/linuxlist.h>
#include <laf0rge1/select.h>
#include <pthread.h>
/**
* routines for dealing with threads
*/
struct thread_notifier {
struct bsc_fd bfd;
int no_write;
int fd[2];
pthread_mutex_t guard;
struct llist_head *main_head;
struct llist_head *thread_head;
struct llist_head __head1;
struct llist_head __head2;
};
struct thread_notifier *thread_notifier_alloc();
/**
* atomically swap two llist heads. This can be used
* to have two queues of data and then swap them for
* processing.
*/
void thread_swap(struct thread_notifier *);
void thread_safe_add(struct thread_notifier *, struct llist_head *_new);
void thread_init(void);
#endif

52
include/udp_input.h Normal file
View File

@ -0,0 +1,52 @@
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/* UDP Input for the SNMP On-Waves packets */
#ifndef c7_udp_input_h
#define c7_udp_input_h
#include <stdint.h>
#include "write_queue.h"
#define UDP_FORMAT_SIMPLE_UDP 2
#define UDP_FORMAT_SIMPLE_TCP 3
#define UDP_DATA_MSU_PRIO_0 0
#define UDP_DATA_MSU_PRIO_1 1
#define UDP_DATA_MSU_PRIO_2 2
#define UDP_DATA_MSU_PRIO_3 3
#define UDP_DATA_RETR_PRIO_0 16
#define UDP_DATA_RETR_PRIO_1 17
#define UDP_DATA_RETR_PRIO_2 18
#define UDP_DATA_RETR_PRIO_3 19
#define UDP_DATA_RETR_COMPL 32
#define UDP_DATA_RETR_IMPOS 33
struct udp_data_hdr {
uint8_t format_type;
uint8_t data_type;
uint16_t data_link_index;
uint32_t user_context;
uint32_t data_length;
uint8_t data[0];
} __attribute__((packed));
#endif

48
include/write_queue.h Normal file
View File

@ -0,0 +1,48 @@
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/* Generic write queue implementation */
#ifndef write_queue_h
#define write_queue_h
#include <laf0rge1/select.h>
#include <laf0rge1/msgb.h>
struct write_queue {
struct bsc_fd bfd;
unsigned int max_length;
unsigned int current_length;
unsigned int paused;
struct llist_head msg_queue;
int (*read_cb)(struct bsc_fd *fd);
int (*write_cb)(struct bsc_fd *fd, struct msgb *msg);
};
void write_queue_init(struct write_queue *queue, int max_length);
int write_queue_enqueue(struct write_queue *queue, struct msgb *data);
int write_queue_bfd_cb(struct bsc_fd *fd, unsigned int what);
void write_queue_pause(struct write_queue *queue);
void write_queue_unpause(struct write_queue *queue);
#endif

6
mgcp_mgw.cfg Normal file
View File

@ -0,0 +1,6 @@
mgcp
local ip 172.18.0.20
mgw ip 172.18.0.30
rtp base 4000
sdp audio payload number 123
number endpoints 31

53
src/InitHW.c Executable file
View File

@ -0,0 +1,53 @@
#include <unistd.h>
#include <stdio.h>
#include <pti/NexusWare.h>
void InitHW(void)
{
int status,i;
/* configure the board clock */
status = PTI_SetHSCMStandalone(0,PTI_CLK_TRUNK + 1, PTI_CLK_NONE,
PTI_NETREF_2048K);
if (status < 0)
fprintf(stderr, "Error: PTI_SetClocking() = %d\n", status);
sleep(1);
/* open the port devices */
for (i=1; i<=8; i++)
{
status = PTI_SetT1Framing(i, PTI_FRAME_E1CRC, PTI_ENCODE_HDB3);
if (status < 0)
fprintf(stderr, "Error: PTI_SetFraming(%d) = %d\n", i, status);
}
/* configure PTMC */
status = PTI_SetPTMCNetref(0, PTI_PTMC_NETREF_DISABLE);
if (status < 0)
fprintf(stderr, "Error: PTI_SetPTMCNetref() = %d\n", status);
status = PTI_SetPTMCClockMode(0, PTI_PTMC_CLOCKMODE_H100);
if (status < 0)
fprintf(stderr, "Error: PTI_SetPTMCClockMode() = %d\n", status);
status = PTI_SetEnetPortState(PTI_ENET_PORT_ID_PTMC+0,
PTI_ENET_PORT_STATE_ENABLE_ALL);
if (status < 0)
fprintf(stderr, "Error: PTI_SetEnetPortState() = %d\n", status);
status = PTI_AddEnetRoute(PTI_ENET_PORT_ID_PTMC+0, PTI_ENET_PORT_ID_FRONT+0, 1);
status |= PTI_AddEnetRoute(PTI_ENET_PORT_ID_PTMC+0, PTI_ENET_PORT_ID_REAR+0, 1);
status |= PTI_AddEnetRoute(PTI_ENET_PORT_ID_PTMC+0, PTI_ENET_PORT_ID_LOCAL+0, 1);
if (status < 0)
fprintf(stderr, "Error: PTI_AddEnetRoute() = %d\n", status);
status = PTI_ConnectHSCM(PTI_HSCM_TRUNK+1,30,PTI_HSCM_DATACHAN,0,1,1);
if (status < 0)
fprintf(stderr, "Error: PTI_ConnectHSCM() = %d\n", status);
status = PTI_ConnectHSCM(PTI_HSCM_TRUNK+1, 0, PTI_HSCM_PTMC, 0, 30, 0);
status |= PTI_ConnectHSCM(PTI_HSCM_PTMC, 128, PTI_HSCM_TRUNK+1, 0, 30, 0);
if (status < 0)
fprintf(stderr, "Error: PTI_ConnectHSCM() = %d\n", status);
}

15
src/Makefile.am Normal file
View File

@ -0,0 +1,15 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS=-Wall $(LAFORGE_CFLAGS) $(SCCP_CFLAGS) \
$(NEXUSWARE_C7_CFLAGS) $(NEXUSWARE_UNIPORTE_CFLAGS)
sbin_PROGRAMS = cellmgr_ng mgcp_mgw
mgcp_mgw_SOURCES = mgcp_ss7.c write_queue.c mgcp/mgcp_protocol.c mgcp/mgcp_network.c thread.c
mgcp_mgw_LDADD = $(LAFORGE_LIBS) $(NEXUSWARE_C7_LIBS) $(NEXUSWARE_UNIPORTE_LIBS) -lvty -lpthread
cellmgr_ng_SOURCES = main.c mtp_layer3.c thread.c input/ipaccess.c pcap.c \
bss_patch.c \
openbsc_nat/bssap.c openbsc_nat/tlv_parser.c write_queue.c bssap_sccp.c \
msc_conn.c link_udp.c snmp_mtp.c
cellmgr_ng_LDADD = $(LAFORGE_LIBS) $(SCCP_LIBS) $(NEXUSWARE_C7_LIBS) \
-lpthread -lnetsnmp -lcrypto -lvty

300
src/bss_patch.c Normal file
View File

@ -0,0 +1,300 @@
/* Patch GSM 08.08 messages for the network and BS */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <bss_patch.h>
#include <string.h>
#include <openbsc_nat/bssap.h>
#include <openbsc_nat/tlv.h>
#include <laf0rge1/debug.h>
#include <sccp/sccp.h>
#include <arpa/inet.h>
static void patch_ass_rqst(struct msgb *msg, int length)
{
struct tlv_parsed tp;
u_int8_t *data, audio;
int len;
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, length - 1, 0, 0);
len = TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE);
if (len < 3)
return;
data = (u_int8_t *) TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE);
/* no speech... ignore */
if ((data[0] & 0xf) != 0x1)
return;
/* blindly assign */
data[1] = GSM0808_SPEECH_FULL_PREF;
audio = GSM0808_PERM_FR2;
if (len > 3)
audio |= 0x80;
data[2] = audio;
}
static void patch_ass_cmpl(struct msgb *msg, int length)
{
struct tlv_parsed tp;
u_int8_t *data;
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, length - 1, 0, 0);
if (!TLVP_PRESENT(&tp, GSM0808_IE_CHOSEN_CHANNEL)) {
LOGP(DMSC, LOGL_ERROR, "Chosen Channel not in the MSG.\n");
return;
}
if (!TLVP_PRESENT(&tp, GSM0808_IE_SPEECH_VERSION)) {
LOGP(DMSC, LOGL_ERROR, "Speech version not in the MSG.\n");
return;
}
/* claim to have a TCH/H with no mode indication */
data = (u_int8_t *) TLVP_VAL(&tp, GSM0808_IE_CHOSEN_CHANNEL);
data[0] = 0x09;
data = (u_int8_t *) TLVP_VAL(&tp, GSM0808_IE_SPEECH_VERSION);
data[0] = GSM0808_PERM_HR3;
}
int bss_patch_filter_msg(struct msgb *msg, struct sccp_parse_result *sccp)
{
int type;
memset(sccp, 0, sizeof(*sccp));
if (sccp_parse_header(msg, sccp) != 0) {
LOGP(DMSC, LOGL_ERROR, "Failed to parse SCCP header.\n");
return -1;
}
type = sccp_determine_msg_type(msg);
switch (type) {
case SCCP_MSG_TYPE_CR:
if (msg->l3h)
break;
return 0;
break;
case SCCP_MSG_TYPE_CC:
case SCCP_MSG_TYPE_CREF:
return 0;
break;
case SCCP_MSG_TYPE_RLC:
return BSS_FILTER_RLC;
break;
case SCCP_MSG_TYPE_RLSD:
return BSS_FILTER_RLSD;
break;
}
if (msgb_l3len(msg) < sccp->data_len) {
LOGP(DMSC, LOGL_ERROR, "Less space than there should be.\n");
return -1;
}
if (!msg->l3h || msgb_l3len(msg) < 3) {
return -1;
}
if (msg->l3h[0] != 0) {
return -1;
}
if (msgb_l3len(msg) < 2 + msg->l3h[1]) {
return -1;
}
switch (msg->l3h[2]) {
case BSS_MAP_MSG_ASSIGMENT_RQST:
msg->l3h = &msg->l3h[2];
patch_ass_rqst(msg, sccp->data_len - 2);
break;
case BSS_MAP_MSG_ASSIGMENT_COMPLETE:
msg->l3h = &msg->l3h[2];
patch_ass_cmpl(msg, sccp->data_len - 2);
break;
case BSS_MAP_MSG_RESET:
return BSS_FILTER_RESET;
break;
case BSS_MAP_MSG_RESET_ACKNOWLEDGE:
return BSS_FILTER_RESET_ACK;
break;
case BSS_MAP_MSG_CLEAR_COMPLETE:
return BSS_FILTER_CLEAR_COMPL;
break;
}
return 0;
}
static void create_cr(struct msgb *target, struct msgb *inpt, struct sccp_parse_result *sccp)
{
static const u_int32_t optional_offset =
offsetof(struct sccp_connection_request, optional_start);
unsigned int optional_length, optional_start;
struct sccp_connection_request *cr, *in_cr;
target->l2h = msgb_put(target, sizeof(*cr));
cr = (struct sccp_connection_request *) target->l2h;
in_cr = (struct sccp_connection_request *) inpt->l2h;
cr->type = in_cr->type;
cr->proto_class = in_cr->proto_class;
cr->source_local_reference = in_cr->source_local_reference;
cr->variable_called = 2;
cr->optional_start = 4;
/* called address */
target->l3h = msgb_put(target, 1 + 2);
target->l3h[0] = 2;
target->l3h[1] = 0x42;
target->l3h[2] = 254;
/*
* We need to keep the complete optional data. The SCCP parse result
* is only pointing to the data payload.
*/
optional_start = in_cr->optional_start + optional_offset;
optional_length = msgb_l2len(inpt) - optional_start;
if (optional_start + optional_length <= msgb_l2len(inpt)) {
target->l3h = msgb_put(target, optional_length);
memcpy(target->l3h, inpt->l2h + optional_start, msgb_l3len(target));
} else {
LOGP(DINP, LOGL_ERROR, "Input should at least have a byte of data.\n");
}
}
/*
* Generate a simple UDT msg. FIXME: Merge it with the SCCP code
*/
static void create_udt(struct msgb *target, struct msgb *inpt, struct sccp_parse_result *sccp)
{
struct sccp_data_unitdata *udt, *in_udt;
target->l2h = msgb_put(target, sizeof(*udt));
udt = (struct sccp_data_unitdata *) target->l2h;
in_udt = (struct sccp_data_unitdata *) inpt->l2h;
udt->type = in_udt->type;
udt->proto_class = in_udt->proto_class;
udt->variable_called = 3;
udt->variable_calling = 5;
udt->variable_data = 7;
target->l3h = msgb_put(target, 1 + 2);
target->l3h[0] = 2;
target->l3h[1] = 0x42;
target->l3h[2] = 254;
target->l3h = msgb_put(target, 1 + 2);
target->l3h[0] = 2;
target->l3h[1] = 0x42;
target->l3h[2] = 254;
target->l3h = msgb_put(target, sccp->data_len + 1);
target->l3h[0] = sccp->data_len;
memcpy(&target->l3h[1], inpt->l3h, msgb_l3len(target) - 1);
}
void bss_rewrite_header_for_msc(int rc, struct msgb *target, struct msgb *inpt, struct sccp_parse_result *sccp)
{
switch (inpt->l2h[0]) {
case SCCP_MSG_TYPE_CR:
if (rc >= 0)
create_cr(target, inpt, sccp);
else
target->l2h = msgb_put(target, 0);
break;
case SCCP_MSG_TYPE_UDT:
if (rc >= 0)
create_udt(target, inpt, sccp);
else
target->l2h = msgb_put(target, 0);
break;
default:
target->l2h = msgb_put(target, msgb_l2len(inpt));
memcpy(target->l2h, inpt->l2h, msgb_l2len(target));
break;
}
}
/* it is asssumed that the SCCP stack checked the size */
static int patch_address(u_int32_t offset, int pc, struct msgb *msg)
{
struct sccp_called_party_address *party;
u_int8_t *the_pc;
u_int8_t pc_low, pc_high;
party = (struct sccp_called_party_address *)(msg->l2h + offset + 1);
the_pc = &party->data[0];
pc_low = pc & 0xff;
pc_high = (pc >> 8) & 0xff;
the_pc[0] = pc_low;
the_pc[1] = pc_high;
return 0;
}
int bss_rewrite_header_to_bsc(struct msgb *msg, int opc, int dpc)
{
static const u_int32_t called_offset =
offsetof(struct sccp_data_unitdata, variable_called);
static const u_int32_t calling_offset =
offsetof(struct sccp_data_unitdata, variable_calling);
struct sccp_data_unitdata *udt;
struct sccp_parse_result sccp;
memset(&sccp, 0, sizeof(sccp));
if (sccp_parse_header(msg, &sccp) != 0) {
LOGP(DMSC, LOGL_ERROR, "Failed to parse SCCP header.\n");
return -1;
}
/* For now the MSC only sends the PC in UDT */
if (msg->l2h[0] != SCCP_MSG_TYPE_UDT)
return 0;
/* sanity checking */
if (sccp.called.address.point_code_indicator != 1) {
LOGP(DMSC, LOGL_ERROR, "MSC didn't send a PC in called address\n");
return -1;
}
if (sccp.calling.address.point_code_indicator != 1) {
LOGP(DMSC, LOGL_ERROR, "MSC didn't send a PC in calling address\n");
return -1;
}
/* Good thing is we can avoid most of the error checking */
udt = (struct sccp_data_unitdata *) msg->l2h;
if (patch_address(called_offset + udt->variable_called, dpc, msg) != 0)
return -1;
if (patch_address(calling_offset + udt->variable_calling, opc, msg) != 0)
return -1;
return 0;
}

150
src/bssap_sccp.c Normal file
View File

@ -0,0 +1,150 @@
/* Create GSM 08.08 messages */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <bssap_sccp.h>
#include <laf0rge1/msgb.h>
#include <laf0rge1/debug.h>
#include <openbsc_nat/bssap.h>
#include <string.h>
struct msgb *create_clear_command(struct sccp_source_reference *dest_ref)
{
struct sccp_data_form1 *form1;
struct msgb *msg;
msg = msgb_alloc_headroom(4096, 128, "clear command");
if (!msg) {
LOGP(DINP, LOGL_ERROR, "Failed to allocate clear command.\n");
return NULL;
}
msg->l2h = msgb_put(msg, sizeof(*form1));
form1 = (struct sccp_data_form1 *) msg->l2h;
form1->type = SCCP_MSG_TYPE_DT1;
form1->destination_local_reference = *dest_ref;
form1->segmenting = 0;
form1->variable_start = 1;
/* create a Clear Command Call Control msg */
msg->l3h = msgb_put(msg, 7);
msg->l3h[0] = msgb_l3len(msg) - 1;
msg->l3h[1] = BSSAP_MSG_BSS_MANAGEMENT;
msg->l3h[2] = msg->l3h[0] - 2;
msg->l3h[3] = BSS_MAP_MSG_CLEAR_CMD;
msg->l3h[4] = 4;
msg->l3h[5] = 1;
msg->l3h[6] = 0x09;
return msg;
}
struct msgb *create_sccp_rlsd(struct sccp_source_reference *src_ref,
struct sccp_source_reference *dst_ref)
{
struct sccp_connection_released *rel;
struct msgb *msg;
msg = msgb_alloc_headroom(4096, 128, "rlsd");
if (!msg) {
LOGP(DINP, LOGL_ERROR, "Failed to allocate clear command.\n");
return NULL;
}
msg->l2h = msgb_put(msg, sizeof(*rel));
rel = (struct sccp_connection_released *) msg->l2h;
rel->type = SCCP_MSG_TYPE_RLSD;
rel->release_cause = SCCP_RELEASE_CAUSE_END_USER_ORIGINATED;
rel->destination_local_reference = *dst_ref;
rel->source_local_reference = *src_ref;
return msg;
}
struct msgb *create_sccp_rlc(struct sccp_source_reference *src_ref,
struct sccp_source_reference *dst_ref)
{
struct sccp_connection_release_complete *rlc;
struct msgb *msg;
msg = msgb_alloc_headroom(4096, 128, "rlc");
if (!msg) {
LOGP(DINP, LOGL_ERROR, "Failed to allocate rlc.\n");
return NULL;
}
msg->l2h = msgb_put(msg, sizeof(*rlc));
rlc = (struct sccp_connection_release_complete *) msg->l2h;
rlc->type = SCCP_MSG_TYPE_RLC;
rlc->destination_local_reference = *dst_ref;
rlc->source_local_reference = *src_ref;
return msg;
}
struct msgb *create_sccp_refuse(struct sccp_source_reference *dest_ref)
{
struct sccp_connection_refused *ref;
struct msgb *msg;
msg = msgb_alloc_headroom(4096, 128, "rlsd");
if (!msg) {
LOGP(DINP, LOGL_ERROR, "Failed to allocate connection refuse.\n");
return NULL;
}
msg->l2h = msgb_put(msg, sizeof(*ref));
ref = (struct sccp_connection_refused *) msg->l2h;
ref->type = SCCP_MSG_TYPE_CREF;
ref->destination_local_reference = *dest_ref;
ref->cause = SCCP_REFUSAL_END_USER_ORIGINATED;
ref->optional_start = 1;
msg->l3h = msgb_put(msg, 1);
msg->l3h[0] = SCCP_PNC_END_OF_OPTIONAL;
return msg;
}
struct msgb *create_reset()
{
static const u_int8_t reset[] = {
0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe,
0x02, 0x42, 0xfe, 0x06, 0x00, 0x04, 0x30, 0x04,
0x01, 0x20
};
struct msgb *msg;
msg = msgb_alloc_headroom(4096, 128, "reset");
if (!msg) {
LOGP(DMSC, LOGL_ERROR, "Failed to allocate reset msg.\n");
return NULL;
}
msg->l2h = msgb_put(msg, sizeof(reset));
memcpy(msg->l2h, reset, msgb_l2len(msg));
return msg;
}

164
src/input/ipaccess.c Normal file
View File

@ -0,0 +1,164 @@
/* OpenBSC Abis input driver for ip.access */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <laf0rge1/select.h>
#include <laf0rge1/msgb.h>
#include <laf0rge1/talloc.h>
#include <ipaccess.h>
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
#endif
#define TS1_ALLOC_SIZE 4096
static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG };
static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK };
static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
0x01, IPAC_IDTAG_UNIT,
0x01, IPAC_IDTAG_MACADDR,
0x01, IPAC_IDTAG_LOCATION1,
0x01, IPAC_IDTAG_LOCATION2,
0x01, IPAC_IDTAG_EQUIPVERS,
0x01, IPAC_IDTAG_SWVERSION,
0x01, IPAC_IDTAG_UNITNAME,
0x01, IPAC_IDTAG_SERNR,
};
static const char *idtag_names[] = {
[IPAC_IDTAG_SERNR] = "Serial_Number",
[IPAC_IDTAG_UNITNAME] = "Unit_Name",
[IPAC_IDTAG_LOCATION1] = "Location_1",
[IPAC_IDTAG_LOCATION2] = "Location_2",
[IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
[IPAC_IDTAG_SWVERSION] = "Software_Version",
[IPAC_IDTAG_IPADDR] = "IP_Address",
[IPAC_IDTAG_MACADDR] = "MAC_Address",
[IPAC_IDTAG_UNIT] = "Unit_ID",
};
static const char *ipac_idtag_name(int tag)
{
if (tag >= ARRAY_SIZE(idtag_names))
return "unknown";
return idtag_names[tag];
}
/* send the id ack */
int ipaccess_send_id_ack(int fd)
{
return write(fd, id_ack, sizeof(id_ack));
}
int ipaccess_send_id_req(int fd)
{
return write(fd, id_req, sizeof(id_req));
}
/* base handling of the ip.access protocol */
int ipaccess_rcvmsg_base(struct msgb *msg,
struct bsc_fd *bfd)
{
u_int8_t msg_type = *(msg->l2h);
int ret = 0;
switch (msg_type) {
case IPAC_MSGT_PING:
ret = write(bfd->fd, pong, sizeof(pong));
break;
case IPAC_MSGT_PONG:
break;
case IPAC_MSGT_ID_ACK:
ret = ipaccess_send_id_ack(bfd->fd);
break;
}
return 0;
}
/*
* read one ipa message from the socket
* return NULL in case of error
*/
struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
{
struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "Abis/IP");
struct ipaccess_head *hh;
int len, ret = 0;
if (!msg) {
*error = -ENOMEM;
return NULL;
}
/* first read our 3-byte header */
hh = (struct ipaccess_head *) msg->data;
ret = recv(bfd->fd, msg->data, 3, 0);
if (ret < 0) {
msgb_free(msg);
*error = ret;
return NULL;
} else if (ret == 0) {
msgb_free(msg);
*error = ret;
return NULL;
}
msgb_put(msg, ret);
/* then read te length as specified in header */
msg->l2h = msg->data + sizeof(*hh);
len = ntohs(hh->len);
ret = recv(bfd->fd, msg->l2h, len, 0);
if (ret < len) {
msgb_free(msg);
*error = -EIO;
return NULL;
}
msgb_put(msg, ret);
return msg;
}
void ipaccess_prepend_header(struct msgb *msg, int proto)
{
struct ipaccess_head *hh;
/* prepend the ip.access header */
hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
hh->len = htons(msg->len - sizeof(*hh));
hh->proto = proto;
}

235
src/link_udp.c Normal file
View File

@ -0,0 +1,235 @@
/* Implementation of the C7 UDP link */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <bsc_data.h>
#include <udp_input.h>
#include <mtp_data.h>
#include <mtp_pcap.h>
#include <snmp_mtp.h>
#include <laf0rge1/debug.h>
#include <laf0rge1/talloc.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
static int udp_write_cb(struct bsc_fd *fd, struct msgb *msg)
{
struct link_data *link;
int rc;
link = (struct link_data *) fd->data;
LOGP(DINP, LOGL_DEBUG, "Sending MSU: %s\n", hexdump(msg->data, msg->len));
if (link->pcap_fd >= 0)
mtp_pcap_write_msu(link->pcap_fd, msg->l2h, msgb_l2len(msg));
/* the assumption is we have connected the socket to the remote */
rc = sendto(fd->fd, msg->data, msg->len, 0,
(struct sockaddr *) &link->udp.remote, sizeof(link->udp.remote));
if (rc != msg->len) {
LOGP(DINP, LOGL_ERROR, "Failed to write msg to socket: %d\n", rc);
return -1;
}
return 0;
}
static int udp_read_cb(struct bsc_fd *fd)
{
struct link_data *link;
struct udp_data_hdr *hdr;
struct msgb *msg;
int rc;
unsigned int length;
msg = msgb_alloc_headroom(4096, 128, "UDP datagram");
if (!msg) {
LOGP(DINP, LOGL_ERROR, "Failed to allocate memory.\n");
return -1;
}
link = (struct link_data *) fd->data;
rc = read(fd->fd, msg->data, 2096);
if (rc < sizeof(*hdr)) {
LOGP(DINP, LOGL_ERROR, "Failed to read at least size of the header: %d\n", rc);
rc = -1;
goto exit;
}
/* throw away data as the link is down */
if (link->the_link->available == 0) {
LOGP(DINP, LOGL_ERROR, "The link is down. Not forwarding.\n");
rc = 0;
goto exit;
}
hdr = (struct udp_data_hdr *) msgb_put(msg, sizeof(*hdr));
if (hdr->data_type == UDP_DATA_RETR_COMPL || hdr->data_type == UDP_DATA_RETR_IMPOS) {
LOGP(DINP, LOGL_ERROR, "Link retrieval done. Restarting the link.\n");
bsc_link_down(link);
bsc_link_up(link);
goto exit;
} else if (hdr->data_type > UDP_DATA_MSU_PRIO_3) {
LOGP(DINP, LOGL_ERROR, "Link failure. retrieved message.\n");
bsc_link_down(link);
goto exit;
}
length = ntohl(hdr->data_length);
if (length + sizeof(*hdr) > (unsigned int) rc) {
LOGP(DINP, LOGL_ERROR, "The MSU payload does not fit: %u + %u > %d \n",
length, sizeof(*hdr), rc);
rc = -1;
goto exit;
}
msg->l2h = msgb_put(msg, length);
LOGP(DINP, LOGL_DEBUG, "MSU data on: %p data %s.\n", link, hexdump(msg->data, msg->len));
if (link->pcap_fd >= 0)
mtp_pcap_write_msu(link->pcap_fd, msg->l2h, msgb_l2len(msg));
mtp_link_data(link->the_link, msg);
exit:
msgb_free(msg);
return rc;
}
static int udp_link_dummy(struct link_data *link)
{
/* nothing todo */
return 0;
}
static void do_start(void *_data)
{
struct link_data *link = (struct link_data *) _data;
link->forced_down = 0;
snmp_mtp_activate(link->udp.session);
bsc_link_up(link);
}
static int udp_link_reset(struct link_data *link)
{
LOGP(DINP, LOGL_NOTICE, "Will restart SLTM transmission in %d seconds.\n",
link->udp.reset_timeout);
snmp_mtp_deactivate(link->udp.session);
bsc_link_down(link);
/* restart the link in 90 seconds... to force a timeout on the BSC */
link->link_activate.cb = do_start;
link->link_activate.data = link;
bsc_schedule_timer(&link->link_activate, link->udp.reset_timeout, 0);
return 0;
}
static int udp_link_write(struct link_data *link, struct msgb *msg)
{
struct udp_data_hdr *hdr;
hdr = (struct udp_data_hdr *) msgb_push(msg, sizeof(*hdr));
hdr->format_type = UDP_FORMAT_SIMPLE_UDP;
hdr->data_type = UDP_DATA_MSU_PRIO_0;
hdr->data_link_index = htons(1);
hdr->user_context = 0;
hdr->data_length = htonl(msgb_l2len(msg));
if (write_queue_enqueue(&link->udp.write_queue, msg) != 0) {
LOGP(DINP, LOGL_ERROR, "Failed to enqueue msg.\n");
msgb_free(msg);
return -1;
}
return 0;
}
static int udp_link_start(struct link_data *link)
{
LOGP(DINP, LOGL_NOTICE, "UDP input is ready.\n");
do_start(link);
return 0;
}
int link_udp_init(struct link_data *link, int src_port, const char *remote, int remote_port)
{
struct sockaddr_in addr;
int fd;
int on;
write_queue_init(&link->udp.write_queue, 100);
/* function table */
link->shutdown = udp_link_dummy;
link->clear_queue = udp_link_dummy;
link->reset = udp_link_reset;
link->start = udp_link_start;
link->write = udp_link_write;
/* socket creation */
link->udp.write_queue.bfd.data = link;
link->udp.write_queue.bfd.when = BSC_FD_READ;
link->udp.write_queue.read_cb = udp_read_cb;
link->udp.write_queue.write_cb = udp_write_cb;
link->udp.write_queue.bfd.fd = fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
LOGP(DINP, LOGL_ERROR, "Failed to create UDP socket.\n");
return -1;
}
on = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(src_port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("Failed to bind UDP socket");
close(fd);
return -1;
}
/* now connect the socket to the remote */
memset(&link->udp.remote, 0, sizeof(link->udp.remote));
link->udp.remote.sin_family = AF_INET;
link->udp.remote.sin_port = htons(remote_port);
inet_aton(remote, &link->udp.remote.sin_addr);
if (bsc_register_fd(&link->udp.write_queue.bfd) != 0) {
LOGP(DINP, LOGL_ERROR, "Failed to register BFD.\n");
close(fd);
return -1;
}
return 0;
}

1079
src/main.c Normal file

File diff suppressed because it is too large Load Diff

3
src/mgcp/README Normal file
View File

@ -0,0 +1,3 @@
This is OpenBSCs MGCP implementation and is made to fit on top of the
NexusWare UniPorte library. All changes to the MGCP must be merged back
to the OpenBSC implementation in one way or another.

300
src/mgcp/mgcp_network.c Normal file
View File

@ -0,0 +1,300 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* The protocol implementation */
/*
* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <endian.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <mgcp/mgcp.h>
#include <mgcp/mgcp_internal.h>
#include <laf0rge1/debug.h>
#include <laf0rge1/msgb.h>
#include <laf0rge1/talloc.h>
#include <laf0rge1/select.h>
#warning "Make use of the rtp proxy code"
/* according to rtp_proxy.c RFC 3550 */
struct rtp_hdr {
#if __BYTE_ORDER == __LITTLE_ENDIAN
u_int8_t csrc_count:4,
extension:1,
padding:1,
version:2;
u_int8_t payload_type:7,
marker:1;
#elif __BYTE_ORDER == __BIG_ENDIAN
u_int8_t version:2,
padding:1,
extension:1,
csrc_count:4;
u_int8_t marker:1,
payload_type:7;
#endif
u_int16_t sequence;
u_int32_t timestamp;
u_int32_t ssrc;
} __attribute__((packed));
enum {
DEST_NETWORK = 0,
DEST_BTS = 1,
};
enum {
PROTO_RTP,
PROTO_RTCP,
};
#define DUMMY_LOAD 0x23
static int udp_send(int fd, struct in_addr *addr, int port, char *buf, int len)
{
struct sockaddr_in out;
out.sin_family = AF_INET;
out.sin_port = port;
memcpy(&out.sin_addr, addr, sizeof(*addr));
return sendto(fd, buf, len, 0, (struct sockaddr *)&out, sizeof(out));
}
int mgcp_send_dummy(struct mgcp_endpoint *endp)
{
static char buf[] = { DUMMY_LOAD };
return udp_send(endp->local_rtp.fd, &endp->remote,
endp->net_rtp, buf, 1);
}
static void patch_payload(int payload, char *data, int len)
{
struct rtp_hdr *rtp_hdr;
if (len < sizeof(*rtp_hdr))
return;
if (payload < 0)
return;
rtp_hdr = (struct rtp_hdr *) data;
rtp_hdr->payload_type = payload;
}
/*
* There is data coming. We will have to figure out if it
* came from the BTS or the MediaGateway of the MSC. On top
* of that we need to figure out if it was RTP or RTCP.
*
* Currently we do not communicate with the BSC so we have
* no idea where the BTS is listening for RTP and need to
* do the classic routing trick. Wait for the first packet
* from the BTS and then go ahead.
*/
static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
{
char buf[4096];
struct sockaddr_in addr;
socklen_t slen = sizeof(addr);
struct mgcp_endpoint *endp;
struct mgcp_config *cfg;
int rc, dest, proto;
endp = (struct mgcp_endpoint *) fd->data;
cfg = endp->cfg;
rc = recvfrom(fd->fd, &buf, sizeof(buf), 0,
(struct sockaddr *) &addr, &slen);
if (rc < 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to receive message on: 0x%x errno: %d/%s\n",
ENDPOINT_NUMBER(endp), errno, strerror(errno));
return -1;
}
/* do not forward aynthing... maybe there is a packet from the bts */
if (endp->ci == CI_UNUSED) {
LOGP(DMGCP, LOGL_DEBUG, "Unknown message on endpoint: 0x%x\n", ENDPOINT_NUMBER(endp));
return -1;
}
/*
* Figure out where to forward it to. This code assumes that we
* have received the Connection Modify and know who is a legitimate
* partner. According to the spec we could attempt to forward even
* after the Create Connection but we will not as we are not really
* able to tell if this is legitimate.
*/
#warning "Slight spec violation. With connection mode recvonly we should attempt to forward."
dest = memcmp(&addr.sin_addr, &endp->remote, sizeof(addr.sin_addr)) == 0 &&
(endp->net_rtp == addr.sin_port || endp->net_rtcp == addr.sin_port)
? DEST_BTS : DEST_NETWORK;
proto = fd == &endp->local_rtp ? PROTO_RTP : PROTO_RTCP;
/* We have no idea who called us, maybe it is the BTS. */
if (dest == DEST_NETWORK && (endp->bts_rtp == 0 || cfg->forward_ip)) {
/* it was the BTS... */
if (!cfg->bts_ip
|| memcmp(&addr.sin_addr, &cfg->bts_in, sizeof(cfg->bts_in)) == 0
|| memcmp(&addr.sin_addr, &endp->bts, sizeof(endp->bts)) == 0) {
if (fd == &endp->local_rtp) {
endp->bts_rtp = addr.sin_port;
} else {
endp->bts_rtcp = addr.sin_port;
}
endp->bts = addr.sin_addr;
LOGP(DMGCP, LOGL_NOTICE, "Found BTS for endpoint: 0x%x on port: %d/%d of %s\n",
ENDPOINT_NUMBER(endp), ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp),
inet_ntoa(addr.sin_addr));
}
}
/* throw away the dummy message */
if (rc == 1 && buf[0] == DUMMY_LOAD) {
LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy on 0x%x\n",
ENDPOINT_NUMBER(endp));
return 0;
}
/* do this before the loop handling */
if (dest == DEST_NETWORK)
++endp->in_bts;
else
++endp->in_remote;
/* For loop toggle the destination and then dispatch. */
if (cfg->audio_loop)
dest = !dest;
if (dest == DEST_NETWORK) {
if (proto == PROTO_RTP)
patch_payload(endp->net_payload_type, buf, rc);
return udp_send(fd->fd, &endp->remote,
proto == PROTO_RTP ? endp->net_rtp : endp->net_rtcp,
buf, rc);
} else {
if (proto == PROTO_RTP)
patch_payload(endp->bts_payload_type, buf, rc);
return udp_send(fd->fd, &endp->bts,
proto == PROTO_RTP ? endp->bts_rtp : endp->bts_rtcp,
buf, rc);
}
}
static int create_bind(const char *source_addr, struct bsc_fd *fd, int port)
{
struct sockaddr_in addr;
int on = 1;
fd->fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd->fd < 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to create UDP port.\n");
return -1;
}
setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
inet_aton(source_addr, &addr.sin_addr);
if (bind(fd->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
return -1;
}
return 0;
}
static int set_ip_tos(int fd, int tos)
{
int ret;
ret = setsockopt(fd, IPPROTO_IP, IP_TOS,
&tos, sizeof(tos));
return ret != 0;
}
static int bind_rtp(struct mgcp_endpoint *endp)
{
struct mgcp_config *cfg = endp->cfg;
if (create_bind(cfg->source_addr, &endp->local_rtp, endp->rtp_port) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to create RTP port: %s:%d on 0x%x\n",
cfg->source_addr, endp->rtp_port, ENDPOINT_NUMBER(endp));
goto cleanup0;
}
if (create_bind(cfg->source_addr, &endp->local_rtcp, endp->rtp_port + 1) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to create RTCP port: %s:%d on 0x%x\n",
cfg->source_addr, endp->rtp_port + 1, ENDPOINT_NUMBER(endp));
goto cleanup1;
}
set_ip_tos(endp->local_rtp.fd, cfg->endp_dscp);
set_ip_tos(endp->local_rtcp.fd, cfg->endp_dscp);
endp->local_rtp.cb = rtp_data_cb;
endp->local_rtp.data = endp;
endp->local_rtp.when = BSC_FD_READ;
if (bsc_register_fd(&endp->local_rtp) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to register RTP port %d on 0x%x\n",
endp->rtp_port, ENDPOINT_NUMBER(endp));
goto cleanup2;
}
endp->local_rtcp.cb = rtp_data_cb;
endp->local_rtcp.data = endp;
endp->local_rtcp.when = BSC_FD_READ;
if (bsc_register_fd(&endp->local_rtcp) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to register RTCP port %d on 0x%x\n",
endp->rtp_port + 1, ENDPOINT_NUMBER(endp));
goto cleanup3;
}
return 0;
cleanup3:
bsc_unregister_fd(&endp->local_rtp);
cleanup2:
close(endp->local_rtcp.fd);
endp->local_rtcp.fd = -1;
cleanup1:
close(endp->local_rtp.fd);
endp->local_rtp.fd = -1;
cleanup0:
return -1;
}
int mgcp_bind_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
{
endp->rtp_port = rtp_port;
return bind_rtp(endp);
}

758
src/mgcp/mgcp_protocol.c Normal file
View File

@ -0,0 +1,758 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* The protocol implementation */
/*
* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <limits.h>
#include <unistd.h>
#include <laf0rge1/debug.h>
#include <laf0rge1/msgb.h>
#include <laf0rge1/talloc.h>
#include <laf0rge1/select.h>
#include <mgcp/mgcp.h>
#include <mgcp/mgcp_internal.h>
/**
* Macro for tokenizing MGCP messages and SDP in one go.
*
*/
#define MSG_TOKENIZE_START \
line_start = 0; \
for (i = 0; i < msgb_l3len(msg); ++i) { \
/* we have a line end */ \
if (msg->l3h[i] == '\n') { \
/* skip the first line */ \
if (line_start == 0) { \
line_start = i + 1; \
continue; \
} \
\
/* check if we have a proper param */ \
if (i - line_start == 1 && msg->l3h[line_start] == '\r') { \
} else if (i - line_start > 2 \
&& islower(msg->l3h[line_start]) \
&& msg->l3h[line_start + 1] == '=') { \
} else if (i - line_start < 3 \
|| msg->l3h[line_start + 1] != ':' \
|| msg->l3h[line_start + 2] != ' ') \
goto error; \
\
msg->l3h[i] = '\0'; \
if (msg->l3h[i-1] == '\r') \
msg->l3h[i-1] = '\0';
#define MSG_TOKENIZE_END \
line_start = i + 1; \
} \
}
struct mgcp_request {
char *name;
struct msgb *(*handle_request) (struct mgcp_config *cfg, struct msgb *msg);
char *debug_name;
};
#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
{ .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
static struct msgb *handle_audit_endpoint(struct mgcp_config *cfg, struct msgb *msg);
static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg);
static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg);
static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg);
static struct msgb *handle_rsip(struct mgcp_config *cfg, struct msgb *msg);
static int generate_call_id(struct mgcp_config *cfg)
{
int i;
/* use the call id */
++cfg->last_call_id;
/* handle wrap around */
if (cfg->last_call_id == CI_UNUSED)
++cfg->last_call_id;
/* callstack can only be of size number_of_endpoints */
/* verify that the call id is free, e.g. in case of overrun */
for (i = 1; i < cfg->number_endpoints; ++i)
if (cfg->endpoints[i].ci == cfg->last_call_id)
return generate_call_id(cfg);
return cfg->last_call_id;
}
/*
* array of function pointers for handling various
* messages. In the future this might be binary sorted
* for performance reasons.
*/
static const struct mgcp_request mgcp_requests [] = {
MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint")
MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
/* SPEC extension */
MGCP_REQUEST("RSIP", handle_rsip, "ReSetInProgress")
};
static struct msgb *mgcp_msgb_alloc(void)
{
struct msgb *msg;
msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
if (!msg)
LOGP(DMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
return msg;
}
struct msgb *mgcp_create_response_with_data(int code, const char *msg, const char *trans,
const char *data)
{
int len;
struct msgb *res;
res = mgcp_msgb_alloc();
if (!res)
return NULL;
if (data) {
len = snprintf((char *) res->data, 2048, "%d %s\n%s", code, trans, data);
} else {
len = snprintf((char *) res->data, 2048, "%d %s\n", code, trans);
}
res->l2h = msgb_put(res, len);
LOGP(DMGCP, LOGL_DEBUG, "Sending response: code: %d for '%s'\n", code, res->l2h);
return res;
}
static struct msgb *create_response(int code, const char *msg, const char *trans)
{
return mgcp_create_response_with_data(code, msg, trans, NULL);
}
static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
const char *msg, const char *trans_id)
{
const char *addr = endp->cfg->local_ip;
char sdp_record[4096];
if (!addr)
addr = endp->cfg->source_addr;
snprintf(sdp_record, sizeof(sdp_record) - 1,
"I: %d\n\n"
"v=0\r\n"
"c=IN IP4 %s\r\n"
"m=audio %d RTP/AVP %d\r\n"
"a=rtpmap:%d %s\r\n",
endp->ci, addr, endp->rtp_port,
endp->bts_payload_type, endp->bts_payload_type,
endp->cfg->audio_name);
return mgcp_create_response_with_data(200, msg, trans_id, sdp_record);
}
/*
* handle incoming messages:
* - this can be a command (four letters, space, transaction id)
* - or a response (three numbers, space, transaction id)
*/
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
{
int code;
struct msgb *resp = NULL;
if (msgb_l2len(msg) < 4) {
LOGP(DMGCP, LOGL_ERROR, "mgs too short: %d\n", msg->len);
return NULL;
}
/* attempt to treat it as a response */
if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
LOGP(DMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
} else {
int i, handled = 0;
msg->l3h = &msg->l2h[4];
for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i)
if (strncmp(mgcp_requests[i].name, (const char *) &msg->l2h[0], 4) == 0) {
handled = 1;
resp = mgcp_requests[i].handle_request(cfg, msg);
break;
}
if (!handled) {
LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
}
}
return resp;
}
/* string tokenizer for the poor */
static int find_msg_pointers(struct msgb *msg, struct mgcp_msg_ptr *ptrs, int ptrs_length)
{
int i, found = 0;
int whitespace = 1;
for (i = 0; i < msgb_l3len(msg) && ptrs_length > 0; ++i) {
/* if we have a space we found an end */
if (msg->l3h[i] == ' ' || msg->l3h[i] == '\r' || msg->l3h[i] == '\n') {
if (!whitespace) {
++found;
whitespace = 1;
ptrs->length = i - ptrs->start - 1;
++ptrs;
--ptrs_length;
} else {
/* skip any number of whitespace */
}
/* line end... stop */
if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n')
break;
} else if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n') {
/* line end, be done */
break;
} else if (whitespace) {
whitespace = 0;
ptrs->start = i;
}
}
if (ptrs_length == 0)
return -1;
return found;
}
static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, const char *mgcp)
{
char *endptr = NULL;
unsigned int gw = INT_MAX;
gw = strtoul(mgcp, &endptr, 16);
if (gw == 0 || gw >= cfg->number_endpoints || strcmp(endptr, "@mgw") != 0) {
LOGP(DMGCP, LOGL_ERROR, "Not able to find endpoint: '%s'\n", mgcp);
return NULL;
}
return &cfg->endpoints[gw];
}
int mgcp_analyze_header(struct mgcp_config *cfg, struct msgb *msg,
struct mgcp_msg_ptr *ptr, int size,
const char **transaction_id, struct mgcp_endpoint **endp)
{
int found;
*transaction_id = "000000";
if (size < 3) {
LOGP(DMGCP, LOGL_ERROR, "Not enough space in ptr\n");
return -1;
}
found = find_msg_pointers(msg, ptr, size);
if (found <= 3) {
LOGP(DMGCP, LOGL_ERROR, "Gateway: Not enough params. Found: %d\n", found);
return -1;
}
/*
* replace the space with \0. the main method gurantess that
* we still have + 1 for null termination
*/
msg->l3h[ptr[3].start + ptr[3].length + 1] = '\0';
msg->l3h[ptr[2].start + ptr[2].length + 1] = '\0';
msg->l3h[ptr[1].start + ptr[1].length + 1] = '\0';
msg->l3h[ptr[0].start + ptr[0].length + 1] = '\0';
if (strncmp("1.0", (const char *)&msg->l3h[ptr[3].start], 3) != 0
|| strncmp("MGCP", (const char *)&msg->l3h[ptr[2].start], 4) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Wrong MGCP version. Not handling: '%s' '%s'\n",
(const char *)&msg->l3h[ptr[3].start],
(const char *)&msg->l3h[ptr[2].start]);
return -1;
}
*transaction_id = (const char *)&msg->l3h[ptr[0].start];
if (endp) {
*endp = find_endpoint(cfg, (const char *)&msg->l3h[ptr[1].start]);
return *endp == NULL;
}
return 0;
}
static int verify_call_id(const struct mgcp_endpoint *endp,
const char *callid)
{
if (strcmp(endp->callid, callid) != 0) {
LOGP(DMGCP, LOGL_ERROR, "CallIDs does not match on 0x%x. '%s' != '%s'\n",
ENDPOINT_NUMBER(endp), endp->callid, callid);
return -1;
}
return 0;
}
static int verify_ci(const struct mgcp_endpoint *endp,
const char *ci)
{
if (atoi(ci) != endp->ci) {
LOGP(DMGCP, LOGL_ERROR, "ConnectionIdentifiers do not match on 0x%x. %d != %s\n",
ENDPOINT_NUMBER(endp), endp->ci, ci);
return -1;
}
return 0;
}
static struct msgb *handle_audit_endpoint(struct mgcp_config *cfg, struct msgb *msg)
{
struct mgcp_msg_ptr data_ptrs[6];
int found, response;
const char *trans_id;
struct mgcp_endpoint *endp;
found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
if (found != 0)
response = 500;
else
response = 200;
return create_response(response, "AUEP", trans_id);
}
static int parse_conn_mode(const char* msg, int *conn_mode)
{
int ret = 0;
if (strcmp(msg, "recvonly") == 0)
*conn_mode = MGCP_CONN_RECV_ONLY;
else if (strcmp(msg, "sendrecv") == 0)
*conn_mode = MGCP_CONN_RECV_SEND;
else {
LOGP(DMGCP, LOGL_ERROR, "Unknown connection mode: '%s'\n", msg);
ret = -1;
}
return ret;
}
static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
{
struct mgcp_msg_ptr data_ptrs[6];
int found, i, line_start;
const char *trans_id;
struct mgcp_endpoint *endp;
int error_code = 500;
int port;
found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
if (found != 0)
return create_response(500, "CRCX", trans_id);
if (endp->ci != CI_UNUSED) {
if (cfg->force_realloc) {
LOGP(DMGCP, LOGL_NOTICE, "Endpoint 0x%x already allocated. Forcing realloc.\n",
ENDPOINT_NUMBER(endp));
} else {
LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n",
ENDPOINT_NUMBER(endp));
return create_response(500, "CRCX", trans_id);
}
}
/* parse CallID C: and LocalParameters L: */
MSG_TOKENIZE_START
switch (msg->l3h[line_start]) {
case 'L':
endp->local_options = talloc_strdup(cfg->endpoints,
(const char *)&msg->l3h[line_start + 3]);
break;
case 'C':
endp->callid = talloc_strdup(cfg->endpoints,
(const char *)&msg->l3h[line_start + 3]);
break;
case 'M':
if (parse_conn_mode((const char *)&msg->l3h[line_start + 3],
&endp->conn_mode) != 0) {
error_code = 517;
goto error2;
}
break;
default:
LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
msg->l3h[line_start], msg->l3h[line_start],
ENDPOINT_NUMBER(endp));
break;
}
MSG_TOKENIZE_END
/* initialize */
endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
/* set to zero until we get the info */
memset(&endp->remote, 0, sizeof(endp->remote));
/* bind to the port now */
port = rtp_calculate_port(ENDPOINT_NUMBER(endp), cfg->rtp_base_port);
if (cfg->early_bind)
endp->rtp_port = port;
else if (mgcp_bind_rtp_port(endp, port) != 0)
goto error2;
/* assign a local call identifier or fail */
endp->ci = generate_call_id(cfg);
if (endp->ci == CI_UNUSED)
goto error2;
endp->bts_payload_type = cfg->audio_payload;
/* policy CB */
if (cfg->policy_cb) {
switch (cfg->policy_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX, trans_id)) {
case MGCP_POLICY_REJECT:
LOGP(DMGCP, LOGL_NOTICE, "CRCX rejected by policy on 0x%x\n",
ENDPOINT_NUMBER(endp));
mgcp_free_endp(endp);
return create_response(500, "CRCX", trans_id);
break;
case MGCP_POLICY_DEFER:
/* stop processing */
return NULL;
break;
case MGCP_POLICY_CONT:
/* just continue */
break;
}
}
LOGP(DMGCP, LOGL_NOTICE, "Creating endpoint on: 0x%x CI: %u port: %u\n",
ENDPOINT_NUMBER(endp), endp->ci, endp->rtp_port);
if (cfg->change_cb)
cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX, endp->rtp_port);
return create_response_with_sdp(endp, "CRCX", trans_id);
error:
LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d\n",
hexdump(msg->l3h, msgb_l3len(msg)),
ENDPOINT_NUMBER(endp), line_start, i);
return create_response(error_code, "CRCX", trans_id);
error2:
LOGP(DMGCP, LOGL_NOTICE, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp));
return create_response(error_code, "CRCX", trans_id);
}
static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg)
{
struct mgcp_msg_ptr data_ptrs[6];
int found, i, line_start;
const char *trans_id;
struct mgcp_endpoint *endp;
int error_code = 500;
int silent = 0;
found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
if (found != 0)
return create_response(error_code, "MDCX", trans_id);
if (endp->ci == CI_UNUSED) {
LOGP(DMGCP, LOGL_ERROR, "Endpoint is not holding a connection. 0x%x\n", ENDPOINT_NUMBER(endp));
return create_response(error_code, "MDCX", trans_id);
}
MSG_TOKENIZE_START
switch (msg->l3h[line_start]) {
case 'C': {
if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
goto error3;
break;
}
case 'I': {
if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
goto error3;
break;
}
case 'L':
/* skip */
break;
case 'M':
if (parse_conn_mode((const char *)&msg->l3h[line_start + 3],
&endp->conn_mode) != 0) {
error_code = 517;
goto error3;
}
break;
case 'Z':
silent = strcmp("noanswer", (const char *)&msg->l3h[line_start + 3]) == 0;
break;
case '\0':
/* SDP file begins */
break;
case 'a':
case 'o':
case 's':
case 't':
case 'v':
/* skip these SDP attributes */
break;
case 'm': {
int port;
int payload;
const char *param = (const char *)&msg->l3h[line_start];
if (sscanf(param, "m=audio %d RTP/AVP %d", &port, &payload) == 2) {
endp->net_rtp = htons(port);
endp->net_rtcp = htons(port + 1);
endp->net_payload_type = payload;
}
break;
}
case 'c': {
char ipv4[16];
const char *param = (const char *)&msg->l3h[line_start];
if (sscanf(param, "c=IN IP4 %15s", ipv4) == 1) {
inet_aton(ipv4, &endp->remote);
}
break;
}
default:
LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
msg->l3h[line_start], msg->l3h[line_start],
ENDPOINT_NUMBER(endp));
break;
}
MSG_TOKENIZE_END
/* policy CB */
if (cfg->policy_cb) {
switch (cfg->policy_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX, trans_id)) {
case MGCP_POLICY_REJECT:
LOGP(DMGCP, LOGL_NOTICE, "MDCX rejected by policy on 0x%x\n",
ENDPOINT_NUMBER(endp));
if (silent)
goto out_silent;
return create_response(500, "MDCX", trans_id);
break;
case MGCP_POLICY_DEFER:
/* stop processing */
return NULL;
break;
case MGCP_POLICY_CONT:
/* just continue */
break;
}
}
/* modify */
LOGP(DMGCP, LOGL_NOTICE, "Modified endpoint on: 0x%x Server: %s:%u\n",
ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), ntohs(endp->net_rtp));
if (cfg->change_cb)
cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX, endp->rtp_port);
if (silent)
goto out_silent;
return create_response_with_sdp(endp, "MDCX", trans_id);
error:
LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d %d\n",
hexdump(msg->l3h, msgb_l3len(msg)),
ENDPOINT_NUMBER(endp), line_start, i, msg->l3h[line_start]);
return create_response(error_code, "MDCX", trans_id);
error3:
return create_response(error_code, "MDCX", trans_id);
out_silent:
return NULL;
}
static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg)
{
struct mgcp_msg_ptr data_ptrs[6];
int found, i, line_start;
const char *trans_id;
struct mgcp_endpoint *endp;
int error_code = 500;
int silent = 0;
found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
if (found != 0)
return create_response(error_code, "DLCX", trans_id);
if (endp->ci == CI_UNUSED) {
LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp));
return create_response(error_code, "DLCX", trans_id);
}
MSG_TOKENIZE_START
switch (msg->l3h[line_start]) {
case 'C': {
if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
goto error3;
break;
}
case 'I': {
if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
goto error3;
break;
case 'Z':
silent = strcmp("noanswer", (const char *)&msg->l3h[line_start + 3]) == 0;
break;
}
default:
LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
msg->l3h[line_start], msg->l3h[line_start],
ENDPOINT_NUMBER(endp));
break;
}
MSG_TOKENIZE_END
/* policy CB */
if (cfg->policy_cb) {
switch (cfg->policy_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX, trans_id)) {
case MGCP_POLICY_REJECT:
LOGP(DMGCP, LOGL_NOTICE, "DLCX rejected by policy on 0x%x\n",
ENDPOINT_NUMBER(endp));
if (silent)
goto out_silent;
return create_response(500, "DLCX", trans_id);
break;
case MGCP_POLICY_DEFER:
/* stop processing */
return NULL;
break;
case MGCP_POLICY_CONT:
/* just continue */
break;
}
}
/* free the connection */
LOGP(DMGCP, LOGL_NOTICE, "Deleted endpoint on: 0x%x Server: %s:%u\n",
ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), ntohs(endp->net_rtp));
mgcp_free_endp(endp);
if (cfg->change_cb)
cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX, endp->rtp_port);
if (silent)
goto out_silent;
return create_response(250, "DLCX", trans_id);
error:
LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d\n",
hexdump(msg->l3h, msgb_l3len(msg)),
ENDPOINT_NUMBER(endp), line_start, i);
return create_response(error_code, "DLCX", trans_id);
error3:
return create_response(error_code, "DLCX", trans_id);
out_silent:
return NULL;
}
static struct msgb *handle_rsip(struct mgcp_config *cfg, struct msgb *msg)
{
if (cfg->reset_cb)
cfg->reset_cb(cfg);
return NULL;
}
struct mgcp_config *mgcp_config_alloc(void)
{
struct mgcp_config *cfg;
cfg = talloc_zero(NULL, struct mgcp_config);
if (!cfg) {
LOGP(DMGCP, LOGL_FATAL, "Failed to allocate config.\n");
return NULL;
}
cfg->source_port = 2427;
cfg->source_addr = talloc_strdup(cfg, "0.0.0.0");
cfg->audio_name = talloc_strdup(cfg, "GSM-EFR/8000");
cfg->audio_payload = 97;
cfg->rtp_base_port = RTP_PORT_DEFAULT;
return cfg;
}
int mgcp_endpoints_allocate(struct mgcp_config *cfg)
{
int i;
/* Initialize all endpoints */
cfg->endpoints = _talloc_zero_array(cfg,
sizeof(struct mgcp_endpoint),
cfg->number_endpoints, "endpoints");
if (!cfg->endpoints)
return -1;
for (i = 0; i < cfg->number_endpoints; ++i) {
cfg->endpoints[i].local_rtp.fd = -1;
cfg->endpoints[i].local_rtcp.fd = -1;
cfg->endpoints[i].ci = CI_UNUSED;
cfg->endpoints[i].cfg = cfg;
cfg->endpoints[i].net_payload_type = -1;
cfg->endpoints[i].bts_payload_type = -1;
}
return 0;
}
void mgcp_free_endp(struct mgcp_endpoint *endp)
{
LOGP(DMGCP, LOGL_DEBUG, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
endp->ci= CI_UNUSED;
if (endp->callid) {
talloc_free(endp->callid);
endp->callid = NULL;
}
if (endp->local_options) {
talloc_free(endp->local_options);
endp->local_options = NULL;
}
if (!endp->cfg->early_bind) {
bsc_unregister_fd(&endp->local_rtp);
bsc_unregister_fd(&endp->local_rtcp);
}
endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
endp->net_payload_type = endp->bts_payload_type = -1;
endp->in_bts = endp->in_remote = 0;
memset(&endp->remote, 0, sizeof(endp->remote));
memset(&endp->bts, 0, sizeof(endp->bts));
}

939
src/mgcp_ss7.c Normal file
View File

@ -0,0 +1,939 @@
/* Use the UniPorte library to allocate endpoints */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <mgcp_ss7.h>
#include <mgcp/mgcp.h>
#include <mgcp/mgcp_internal.h>
#include <write_queue.h>
#include <laf0rge1/debug.h>
#include <laf0rge1/select.h>
#include <laf0rge1/talloc.h>
#include <laf0rge1/timer.h>
#include <vty/command.h>
#include <vty/vty.h>
/* uniporte includes */
#ifndef NO_UNIPORTE
#include <UniPorte.h>
#include <BusMastHostApi.h>
#include <MtnSa.h>
#include <SystemLayer.h>
#include <PredefMobs.h>
#endif
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <netdb.h>
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <getopt.h>
static struct debug_target *stderr_target;
static int payload = 126;
static int number_endpoints = 32;
static char *mgw_ip = "172.18.0.30";
static int base_port = RTP_PORT_DEFAULT;
static char *local_ip = "172.18.0.20";
static char *config_file = "mgcp_mgw.cfg";
static int exit_on_failure = 0;
static int endp_dscp = 0;
#define TO_MGW_PORT(no) (no-1)
#define FROM_MGW_PORT(no) (no+1)
static struct mgcp_ss7 *s_ss7;
struct mgcp_ss7_endpoint {
unsigned int port;
int block;
};
static void mgcp_ss7_endp_free(struct mgcp_ss7* ss7, int endp);
static void mgcp_ss7_do_exec(struct mgcp_ss7 *mgcp, u_int8_t type, u_int32_t port, u_int32_t param);
static void mgcp_mgw_vty_init();
static void check_exit(int status)
{
if (exit_on_failure && status == 21) {
LOGP(DMGCP, LOGL_ERROR, "Failure detected with the MGW. Exiting.\n");
exit(-1);
}
}
#ifndef NO_UNIPORTE
static void Force_Poll( int milliseconds )
{
int timeout = 0;
unsigned long startTime;
startTime = SysLyrGetTime();
/* Loop until the specified number of milliseconds
* have elapsed.
*/
do {
MtnSaPoll();
SysLyrSleep( 20 );
} while ((SysLyrGetTime()-startTime)<(unsigned long)milliseconds);
return;
}
static char eventName[Event_TELEMETRY_DATA + 1][128] = {
{ "Event_NOT_READY" },
{ "Event_READY" },
{ "Event_ANSWER" },
{ "Event_OUTGOING_CALL" },
{ "Event_ABORT" },
{ "Event_CONNECT" },
{ "Event_DISCONNECT" },
{ "Event_MANAGED_OBJECT_GET_COMPLETE" },
{ "Event_MANAGED_OBJECT_GET_AND_CLEAR_COMPLETE" },
{ "Event_MANAGED_OBJECT_SET_COMPLETE" },
{ "Event_MANAGED_OBJECT_TRAP" },
{ "Event_PREDEF_MOB_SET_COMPLETE" },
{ "Event_PREDEF_MOB_GET_COMPLETE" },
{ "Event_USER_MOB_DEFINE_COMPLETE" },
{ "Event_USER_MOB_SET_COMPLETE" },
{ "Event_USER_MOB_GET_COMPLETE" },
{ "Event_RECEIVE_DATA" },
{ "Event_SEND_COMPLETE" },
{ "Event_TDM_CONNECT_COMPLETE" },
{ "Event_LOG" },
{ "Event_DEVICE_IN_CONTACT" },
{ "Event_DEVICE_MANAGED" },
{ "Event_DEVICE_OUT_OF_CONTACT" },
{ "Event_TELEMETRY_DATA" } };
static char stateName[PortState_END_OF_ENUM][128] = {
{ "PortState_IDLE" },
{ "PortState_SIGNALING" },
{ "PortState_INITIATING" },
{ "PortState_LINK" },
{ "PortState_TRAINING" },
{ "PortState_EC_NEGOTIATING" },
{ "PortState_DATA" },
{ "PortState_RESYNCING" },
{ "PortState_FAX" },
{ "PortState_COMMAND_ESCAPE" },
{ "PortState_TERMINATING" },
{ "PortState_VOICE" },
{ "PortState_PORT_RESET" },
{ "PortState_DSP_RESET" },
{ "PortState_ALLOCATED" },
{ "PortState_OUT_OF_SERVICE" },
{ "PortState_RECONFIGURE" },
{ "PortState_ON_HOLD" } };
static int uniporte_events(unsigned long port, EventTypeT event,
void *event_data, unsigned long event_data_length ) {
char text[128];
ManObjectInfoPtr info;
DataReceiveInfoPtr dataInfo;
int i;
ToneDetectionPtr tones;
/* Don't print output when we receive data or complete
* sending data. That would be too verbose.
*/
if (event==Event_DEVICE_MANAGED) {
MtnSaSetManObject(0, ChannelType_ETHERNET, ManObj_C_MOE_COMM_LOSS_RESET_DELAY ,
10, 0);
}
else if (event==Event_MANAGED_OBJECT_TRAP ) {
info = (ManObjectInfoPtr)event_data;
if (info->trapId == Trap_PORT_STATE_CHANGE) {
sprintf(text, "Port #%ld, Change to state %s", port, stateName[info->value]);
puts(text);
/* update the mgcp state */
int mgcp_endp = FROM_MGW_PORT(port);
if (s_ss7->mgw_end[mgcp_endp].block != 1)
fprintf(stderr, "State change on a non blocked port. ERROR.\n");
s_ss7->mgw_end[mgcp_endp].block = 0;
}
}
else if ( event == Event_MANAGED_OBJECT_SET_COMPLETE ) {
info = (ManObjectInfoPtr)event_data;
sprintf(text, "Object %d value %d status %d", info->object, info->value,
info->status );
puts(text);
check_exit(info->status);
}
else if ( ( event == Event_USER_MOB_SET_COMPLETE ) ||
( event == Event_USER_MOB_DEFINE_COMPLETE ) )
{
info = (ManObjectInfoPtr)event_data;
sprintf( text, "Mob ID %d status %d", info->MOBId, info->status );
puts(text);
check_exit(info->status);
}
else if ( event == Event_USER_MOB_GET_COMPLETE )
{
info = (ManObjectInfoPtr)event_data;
sprintf( text, "Mob ID %d status %d", info->MOBId, info->status );
puts(text);
check_exit(info->status);
}
else if (event == Event_CONNECT)
{
sprintf(text, "Port %d connected",port );
}
else if (event == Event_PREDEF_MOB_GET_COMPLETE)
{
info = (ManObjectInfoPtr)event_data;
sprintf(text, "Mob ID %d status %d", info->MOBId, info->status );
puts(text);
check_exit(info->status);
}
return( 0 );
}
static int initialize_uniporte(struct mgcp_ss7 *mgcp)
{
ProfileT profile;
unsigned long mgw_address;
int rc;
LOGP(DMGCP, LOGL_NOTICE, "Initializing MGW on %s\n", mgcp->cfg->bts_ip);
MtnSaSetEthernetOnly();
rc = MtnSaStartup(uniporte_events);
if (rc != 0)
LOGP(DMGCP, LOGL_ERROR, "Failed to startup the MGW.\n");
SysEthGetHostAddress(mgcp->cfg->bts_ip, &mgw_address);
rc = MtnSaRegisterEthernetDevice(mgw_address, 0);
if (rc != 0)
LOGP(DMGCP, LOGL_ERROR, "Failed to register ethernet.\n");
Force_Poll(2000);
MtnSaTakeOverDevice(0);
Force_Poll(2000);
MtnSaSetReceiveTraps(1);
MtnSaSetTransparent();
/* change the voice profile to AMR */
MtnSaGetProfile(ProfileType_VOICE, 0, &profile);
profile.countryCode = CountryCode_INTERNAT_ALAW;
MtnSaSetProfile(ProfileType_VOICE, 0, &profile);
if (MtnSaGetPortCount() == 0)
return -1;
return 0;
}
static void* start_uniporte(void *_ss7) {
struct llist_head blocked;
struct mgcp_ss7_cmd *cmd, *tmp;
struct mgcp_ss7 *ss7 = _ss7;
s_ss7 = ss7;
if (initialize_uniporte(ss7) != 0) {
fprintf(stderr, "Failed to create Uniporte.\n");
exit(-1);
return 0;
}
fprintf(stderr, "Created the MGCP processing thread.\n");
INIT_LLIST_HEAD(&blocked);
for (;;) {
thread_swap(ss7->cmd_queue);
start_over:
/* handle items that are currently blocked */
llist_for_each_entry_safe(cmd, tmp, &blocked, entry) {
if (ss7->mgw_end[cmd->port].block)
continue;
mgcp_ss7_do_exec(ss7, cmd->type, cmd->port, cmd->param);
llist_del(&cmd->entry);
free(cmd);
/* We might have unblocked something, make sure we operate in order */
MtnSaPoll();
goto start_over;
}
llist_for_each_entry_safe(cmd, tmp, ss7->cmd_queue->main_head, entry) {
if (ss7->mgw_end[cmd->port].block) {
llist_del(&cmd->entry);
llist_add_tail(&cmd->entry, &blocked);
continue;
}
mgcp_ss7_do_exec(ss7, cmd->type, cmd->port, cmd->param);
llist_del(&cmd->entry);
free(cmd);
/* We might have unblocked something, make sure we operate in order */
MtnSaPoll();
goto start_over;
}
Force_Poll(20);
}
return 0;
}
#endif
static void update_mute_status(int mgw_port, int conn_mode)
{
#ifndef NO_UNIPORTE
if (conn_mode == MGCP_CONN_NONE) {
MtnSaSetManObject(mgw_port, ChannelType_PORT, ManObj_C_VOICE_UPSTREAM_MUTE, 1, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT, ManObj_C_VOICE_DOWNSTREAM_MUTE, 1, 0);
} else if (conn_mode == MGCP_CONN_RECV_ONLY) {
MtnSaSetManObject(mgw_port, ChannelType_PORT, ManObj_C_VOICE_UPSTREAM_MUTE, 1, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT, ManObj_C_VOICE_DOWNSTREAM_MUTE, 0, 0);
} else if (conn_mode == MGCP_CONN_SEND_ONLY) {
MtnSaSetManObject(mgw_port, ChannelType_PORT, ManObj_C_VOICE_UPSTREAM_MUTE, 0, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT, ManObj_C_VOICE_DOWNSTREAM_MUTE, 1, 0);
} else if (conn_mode == MGCP_CONN_RECV_SEND) {
MtnSaSetManObject(mgw_port, ChannelType_PORT, ManObj_C_VOICE_UPSTREAM_MUTE, 0, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT, ManObj_C_VOICE_DOWNSTREAM_MUTE, 0, 0);
} else {
LOGP(DMGCP, LOGL_ERROR, "Unhandled conn mode: %d\n", conn_mode);
}
#endif
}
#ifndef NO_UNIPORTE
static void allocate_endp(struct mgcp_ss7 *ss7, int endp_no)
{
int mgw_port;
unsigned long mgw_address, loc_address;
struct mgcp_ss7_endpoint *mgw_endp = &ss7->mgw_end[endp_no];
struct mgcp_endpoint *mg_endp = &ss7->cfg->endpoints[endp_no];
mgw_port = TO_MGW_PORT(endp_no);
mgw_endp->port = MtnSaAllocate(mgw_port);
if (mgw_endp->port == UINT_MAX) {
fprintf(stderr, "Failed to allocate the port: %d\n", endp_no);
return;
}
/* Select AMR 5.9, Payload 98, no CRC, hardcoded */
MtnSaApplyProfile(mgw_port, ProfileType_VOICE, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_G_DATA_PATH, DataPathT_ETHERNET, 0 );
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_C_VOICE_RTP_TELEPHONE_EVENT_PT_TX, ss7->cfg->audio_payload, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_G_RTP_AMR_PAYLOAD_TYPE, ss7->cfg->audio_payload, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_G_RTP_AMR_PAYLOAD_FORMAT, RtpAmrPayloadFormat_OCTET_ALIGNED, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_G_VOICE_ENCODING, Voice_Encoding_AMR_5_90, 0);
update_mute_status(mgw_port, mg_endp->conn_mode);
/* set the addresses */
SysEthGetHostAddress(ss7->cfg->bts_ip, &mgw_address);
SysEthGetHostAddress(ss7->cfg->local_ip, &loc_address);
MtnSaSetVoIpAddresses(mgw_port,
mgw_address, mg_endp->rtp_port,
loc_address, mg_endp->rtp_port);
MtnSaConnect(mgw_port, mgw_port);
mgw_endp->block = 1;
}
#endif
static void mgcp_ss7_do_exec(struct mgcp_ss7 *mgcp, u_int8_t type, u_int32_t port, u_int32_t param)
{
#ifndef NO_UNIPORTE
struct mgcp_ss7_endpoint *mgw_endp = &mgcp->mgw_end[port];
int rc;
switch (type) {
case MGCP_SS7_MUTE_STATUS:
if (mgw_endp->port != UINT_MAX)
update_mute_status(TO_MGW_PORT(port), param);
break;
case MGCP_SS7_DELETE:
if (mgw_endp->port != UINT_MAX) {
rc = MtnSaDisconnect(mgw_endp->port);
if (rc != 0)
fprintf(stderr, "Failed to disconnect port: %u\n", mgw_endp->port);
rc = MtnSaDeallocate(mgw_endp->port);
if (rc != 0)
fprintf(stderr, "Failed to deallocate port: %u\n", mgw_endp->port);
mgw_endp->port = UINT_MAX;
mgw_endp->block = 1;
}
break;
case MGCP_SS7_ALLOCATE:
allocate_endp(mgcp, port);
break;
case MGCP_SS7_SHUTDOWN:
MtnSaShutdown();
break;
}
#endif
}
void mgcp_ss7_exec(struct mgcp_ss7 *mgcp, u_int8_t type, u_int32_t port, u_int32_t param)
{
struct mgcp_ss7_cmd *cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = type;
cmd->port = port;
cmd->param = param;
thread_safe_add(mgcp->cmd_queue, &cmd->entry);
}
static int ss7_allocate_endpoint(struct mgcp_ss7 *ss7, int endp_no, struct mgcp_ss7_endpoint *endp)
{
struct mgcp_endpoint *mg_endp;
mg_endp = &ss7->cfg->endpoints[endp_no];
mg_endp->bts_rtp = htons(mg_endp->rtp_port);
mg_endp->bts_rtcp = htons(mg_endp->rtp_port + 1);
mg_endp->bts = ss7->cfg->bts_in;
mgcp_ss7_exec(ss7, MGCP_SS7_ALLOCATE, endp_no, 0);
return MGCP_POLICY_CONT;
}
static int ss7_modify_endpoint(struct mgcp_ss7 *ss7, int endp_no, struct mgcp_ss7_endpoint *endp)
{
struct mgcp_endpoint *mg_endp;
mg_endp = &ss7->cfg->endpoints[endp_no];
mgcp_ss7_exec(ss7, MGCP_SS7_MUTE_STATUS, endp_no, mg_endp->conn_mode);
/*
* this is a bad assumption of the network. We assume
* to have the remote addr now.
*/
mgcp_send_dummy(mg_endp);
/* update the remote end */
return MGCP_POLICY_CONT;
}
static int ss7_delete_endpoint(struct mgcp_ss7 *ss7, int endp_no, struct mgcp_ss7_endpoint *endp)
{
mgcp_ss7_endp_free(ss7, endp_no);
return MGCP_POLICY_CONT;
}
static int mgcp_ss7_policy(struct mgcp_config *cfg, int endp_no, int state, const char *trans)
{
int rc;
struct mgcp_ss7 *ss7;
struct mgcp_ss7_endpoint *endp;
ss7 = (struct mgcp_ss7 *) cfg->data;
endp = &ss7->mgw_end[endp_no];
/* TODO: Make it async and wait for the port to be connected */
rc = MGCP_POLICY_REJECT;
switch (state) {
case MGCP_ENDP_CRCX:
rc = ss7_allocate_endpoint(ss7, endp_no, endp);
break;
case MGCP_ENDP_MDCX:
rc = ss7_modify_endpoint(ss7, endp_no, endp);
break;
case MGCP_ENDP_DLCX:
rc = ss7_delete_endpoint(ss7, endp_no, endp);
break;
}
return rc;
}
static void enqueue_msg(struct write_queue *queue, struct sockaddr_in *addr, struct msgb *msg)
{
struct sockaddr_in *data;
data = (struct sockaddr_in *) msgb_push(msg, sizeof(*data));
*data = *addr;
if (write_queue_enqueue(queue, msg) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to queue the message.\n");
msgb_free(msg);
}
}
static int write_call_agent(struct bsc_fd *bfd, struct msgb *msg)
{
int rc;
struct sockaddr_in *addr;
addr = (struct sockaddr_in *) msg->data;
rc = sendto(bfd->fd, msg->l2h, msgb_l2len(msg), 0,
(struct sockaddr *) addr, sizeof(*addr));
if (rc != msgb_l2len(msg))
LOGP(DMGCP, LOGL_ERROR, "Failed to write MGCP message: rc: %d errno: %d\n", rc, errno);
return rc;
}
static int read_call_agent(struct bsc_fd *fd)
{
struct sockaddr_in addr;
socklen_t slen = sizeof(addr);
struct msgb *resp;
struct mgcp_ss7 *cfg;
struct write_queue *queue;
cfg = (struct mgcp_ss7 *) fd->data;
queue = container_of(fd, struct write_queue, bfd);
/* read one less so we can use it as a \0 */
int rc = recvfrom(fd->fd, cfg->mgcp_msg->data, cfg->mgcp_msg->data_len - 1, 0,
(struct sockaddr *) &addr, &slen);
if (rc < 0) {
perror("Gateway failed to read");
return -1;
} else if (slen > sizeof(addr)) {
fprintf(stderr, "Gateway received message from outerspace: %d %d\n",
slen, sizeof(addr));
return -1;
}
/* handle message now */
cfg->mgcp_msg->l2h = msgb_put(cfg->mgcp_msg, rc);
resp = mgcp_handle_message(cfg->cfg, cfg->mgcp_msg);
msgb_reset(cfg->mgcp_msg);
if (resp)
enqueue_msg(queue, &addr, resp);
return 0;
}
static int create_socket(struct mgcp_ss7 *cfg)
{
int on;
struct sockaddr_in addr;
struct bsc_fd *bfd;
bfd = &cfg->mgcp_fd.bfd;
cfg->mgcp_fd.read_cb = read_call_agent;
cfg->mgcp_fd.write_cb = write_call_agent;
bfd->when = BSC_FD_READ;
bfd->fd = socket(AF_INET, SOCK_DGRAM, 0);
if (bfd->fd < 0) {
perror("Gateway failed to listen");
return -1;
}
on = 1;
setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(cfg->cfg->source_port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("Gateway failed to bind");
close(bfd->fd);
return -1;
}
bfd->data = cfg;
cfg->mgcp_msg = msgb_alloc(4096, "mgcp-msg");
if (!cfg->mgcp_msg) {
fprintf(stderr, "Gateway memory error.\n");
close(bfd->fd);
return -1;
}
talloc_steal(cfg, cfg->mgcp_msg);
if (bsc_register_fd(bfd) != 0) {
DEBUGP(DMGCP, "Failed to register the fd\n");
close(bfd->fd);
return -1;
}
return 0;
}
static void mgcp_ss7_endp_free(struct mgcp_ss7 *ss7, int endp)
{
mgcp_ss7_exec(ss7, MGCP_SS7_DELETE, endp, 0);
}
static int reset_cb(struct mgcp_config *cfg)
{
mgcp_ss7_reset((struct mgcp_ss7 *) cfg->data);
return 0;
}
struct mgcp_ss7 *mgcp_ss7_init(int endpoints, const char *local_ip, const char *mgw_ip, int base_port, int payload)
{
int i;
struct mgcp_ss7 *conf = talloc_zero(NULL, struct mgcp_ss7);
if (!conf)
return NULL;
write_queue_init(&conf->mgcp_fd, 30);
conf->cfg = mgcp_config_alloc();
if (!conf->cfg) {
LOGP(DMGCP, LOGL_ERROR, "Failed to allocate memory.\n");
talloc_free(conf);
return NULL;
}
/* take over the ownership */
talloc_steal(conf, conf->cfg);
conf->cfg->number_endpoints = endpoints;
conf->cfg->local_ip = talloc_strdup(conf->cfg, local_ip);
conf->cfg->bts_ip = talloc_strdup(conf->cfg, mgw_ip);
inet_aton(conf->cfg->bts_ip, &conf->cfg->bts_in);
talloc_free(conf->cfg->audio_name);
conf->cfg->audio_name = talloc_strdup(conf->cfg, "AMR/8000");
conf->cfg->audio_payload = payload;
conf->cfg->rtp_base_port = base_port;
conf->cfg->policy_cb = mgcp_ss7_policy;
conf->cfg->reset_cb = reset_cb;
conf->cfg->data = conf;
conf->cfg->endp_dscp = endp_dscp;
/* do not attempt to allocate call ids */
conf->cfg->early_bind = 1;
if (mgcp_endpoints_allocate(conf->cfg) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to allocate endpoints: %d\n", endpoints);
talloc_free(conf);
return NULL;
}
if (create_socket(conf) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to create socket.\n");
talloc_free(conf);
return NULL;
}
conf->mgw_end = _talloc_zero_array(conf, sizeof(struct mgcp_ss7_endpoint),
conf->cfg->number_endpoints, "mgw endpoints");
if (!conf->mgw_end) {
LOGP(DMGCP, LOGL_ERROR, "Failed to allocate MGW endpoint array.\n");
talloc_free(conf);
return NULL;
}
for (i = 0; i < conf->cfg->number_endpoints; ++i) {
struct mgcp_endpoint *endp;
int rtp_port;
/* initialize the MGW part */
conf->mgw_end[i].port = UINT_MAX;
/* allocate the ports */
endp = &conf->cfg->endpoints[i];
rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), conf->cfg->rtp_base_port);
if (mgcp_bind_rtp_port(endp, rtp_port) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to bind: %d\n", rtp_port);
mgcp_ss7_free(conf);
return NULL;
}
}
conf->cmd_queue = thread_notifier_alloc();
if (!conf->cmd_queue) {
LOGP(DMGCP, LOGL_ERROR, "Failed to allocate the command queue.\n");
talloc_free(conf);
return NULL;
}
#ifndef NO_UNIPORTE
conf->cmd_queue->no_write = 1;
pthread_create(&conf->thread, NULL, start_uniporte, conf);
#endif
return conf;
}
void mgcp_ss7_free(struct mgcp_ss7 *mgcp)
{
/* close everything */
mgcp_ss7_reset(mgcp);
mgcp_ss7_exec(mgcp, MGCP_SS7_SHUTDOWN, 0, 0);
close(mgcp->mgcp_fd.bfd.fd);
bsc_unregister_fd(&mgcp->mgcp_fd.bfd);
bsc_del_timer(&mgcp->poll_timer);
talloc_free(mgcp);
}
void mgcp_ss7_reset(struct mgcp_ss7 *mgcp)
{
int i;
if (!mgcp)
return;
LOGP(DMGCP, LOGL_INFO, "Resetting all endpoints.\n");
/* free UniPorted and MGCP data */
for (i = 0; i < mgcp->cfg->number_endpoints; ++i) {
mgcp_ss7_endp_free(mgcp, i);
mgcp_free_endp(&mgcp->cfg->endpoints[i]);
}
}
static void print_help()
{
printf(" Some useful help...\n");
printf(" -h This help text.\n");
printf(" -c --config=CFG. The configuration file.\n");
printf(" -e --exit-on-failure. Exit the app on MGW failure.\n");
}
static void print_usage()
{
printf("Usage: mgcp_mgw\n");
}
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"config", 1, 0, 'c'},
{"exit", 0, 0, 'e'},
{0, 0, 0, 0},
};
c = getopt_long(argc, argv, "hc:e",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
print_usage();
print_help();
exit(0);
case 'c':
config_file = optarg;
break;
case 'e':
exit_on_failure = 1;
break;
default:
fprintf(stderr, "Unknown option.\n");
break;
}
}
}
int main(int argc, char **argv)
{
struct mgcp_ss7 *mgcp;
debug_init();
stderr_target = debug_target_create_stderr();
debug_add_target(stderr_target);
/* enable filters */
debug_set_all_filter(stderr_target, 1);
debug_set_category_filter(stderr_target, DINP, 1, LOGL_INFO);
debug_set_category_filter(stderr_target, DSCCP, 1, LOGL_INFO);
debug_set_category_filter(stderr_target, DMSC, 1, LOGL_INFO);
debug_set_category_filter(stderr_target, DMGCP, 1, LOGL_INFO);
debug_set_print_timestamp(stderr_target, 1);
debug_set_use_color(stderr_target, 0);
handle_options(argc, argv);
signal(SIGPIPE, SIG_IGN);
mgcp_mgw_vty_init();
if (vty_read_config_file(config_file) < 0) {
fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
return -1;
}
printf("Creating MGCP MGW with endpoints: %d ip: %s mgw: %s rtp-base: %d payload: %d\n",
number_endpoints, local_ip, mgw_ip, base_port, payload);
mgcp = mgcp_ss7_init(number_endpoints, local_ip, mgw_ip, base_port, payload);
if (!mgcp) {
fprintf(stderr, "Failed to create MGCP\n");
exit(-1);
}
while (1) {
bsc_select_main(0);
}
return 0;
}
/* VTY code */
struct cmd_node mgcp_node = {
MGCP_NODE,
"%s(mgcp)#",
1,
};
DEFUN(cfg_mgcp,
cfg_mgcp_cmd,
"mgcp",
"Configure the MGCP")
{
vty->node = MGCP_NODE;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_local_ip,
cfg_mgcp_local_ip_cmd,
"local ip IP",
"Set the IP to be used in SDP records")
{
struct hostent *hosts;
struct in_addr *addr;
hosts = gethostbyname(argv[0]);
if (!hosts || hosts->h_length < 1 || hosts->h_addrtype != AF_INET) {
vty_out(vty, "Failed to resolve '%s'%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
addr = (struct in_addr *) hosts->h_addr_list[0];
local_ip = talloc_strdup(NULL, inet_ntoa(*addr));
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_mgw_ip,
cfg_mgcp_mgw_ip_cmd,
"mgw ip IP",
"Set the IP of the MGW for RTP forwarding")
{
struct hostent *hosts;
struct in_addr *addr;
hosts = gethostbyname(argv[0]);
if (!hosts || hosts->h_length < 1 || hosts->h_addrtype != AF_INET) {
vty_out(vty, "Failed to resolve '%s'%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
addr = (struct in_addr *) hosts->h_addr_list[0];
mgw_ip = talloc_strdup(NULL, inet_ntoa(*addr));
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_rtp_base_port,
cfg_mgcp_rtp_base_port_cmd,
"rtp base <0-65534>",
"Base port to use")
{
unsigned int port = atoi(argv[0]);
if (port > 65534) {
vty_out(vty, "%% wrong base port '%s'%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
base_port = port;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_rtp_ip_dscp,
cfg_mgcp_rtp_ip_dscp_cmd,
"rtp ip-dscp <0-255>",
"Set the IP_TOS socket attribute on the RTP/RTCP sockets.\n" "The TOS value.")
{
int dscp = atoi(argv[0]);
endp_dscp = dscp;
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgcp_rtp_ip_dscp, cfg_mgcp_rtp_ip_tos_cmd,
"rtp ip-tos <0-255>",
"Set the IP_TOS socket attribute on the RTP/RTCP sockets.\n" "The TOS value.")
DEFUN(cfg_mgcp_sdp_payload_number,
cfg_mgcp_sdp_payload_number_cmd,
"sdp audio payload number <1-255>",
"Set the audio codec to use")
{
unsigned int new_payload = atoi(argv[0]);
if (new_payload > 255) {
vty_out(vty, "%% wrong payload number '%s'%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
payload = new_payload;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_number_endp,
cfg_mgcp_number_endp_cmd,
"number endpoints <0-65534>",
"The number of endpoints to allocate. This is not dynamic.")
{
/* + 1 as we start counting at one */
number_endpoints = atoi(argv[0]) + 1;
return CMD_SUCCESS;
}
static int config_write_mgcp()
{
return CMD_SUCCESS;
}
static void mgcp_mgw_vty_init(void)
{
cmd_init(1);
vty_init();
install_element(CONFIG_NODE, &cfg_mgcp_cmd);
install_node(&mgcp_node, config_write_mgcp);
install_default(MGCP_NODE);
install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd);
install_element(MGCP_NODE, &cfg_mgcp_mgw_ip_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_base_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_tos_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd);
install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd);
install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd);
}
void subscr_put() {}
void vty_event() {}

594
src/msc_conn.c Normal file
View File

@ -0,0 +1,594 @@
/* MSC related stuff... */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <bsc_data.h>
#include <bss_patch.h>
#include <bssap_sccp.h>
#include <ipaccess.h>
#include <mtp_data.h>
#include <laf0rge1/debug.h>
#include <laf0rge1/tlv.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#define RECONNECT_TIME 10, 0
#define NAT_MUX 0xfc
static void msc_send_id_response(struct bsc_data *bsc);
static void msc_send(struct bsc_data *bsc, struct msgb *msg, int proto);
void mtp_link_slta_recv(struct mtp_link *link)
{
struct msgb *msg;
unsigned int sls;
while (!llist_empty(&link->pending_msgs)) {
msg = msgb_dequeue(&link->pending_msgs);
sls = (unsigned int) msg->l3h;
if (mtp_link_submit_sccp_data(link, sls, msg->l2h, msgb_l2len(msg)) != 0)
LOGP(DMSC, LOGL_ERROR, "Could not forward SCCP message.\n");
msgb_free(msg);
}
}
void msc_clear_queue(struct bsc_data *data)
{
struct msgb *msg;
LOGP(DMSC, LOGL_NOTICE, "Clearing the MSC to BSC queue.\n");
while (!llist_empty(&data->link.the_link->pending_msgs)) {
msg = msgb_dequeue(&data->link.the_link->pending_msgs);
msgb_free(msg);
}
}
static void close_msc(struct bsc_data *bsc)
{
struct bsc_fd *bfd = &bsc->msc_connection.bfd;
close(bfd->fd);
bsc_unregister_fd(bfd);
bfd->fd = -1;
release_bsc_resources(bsc);
bsc_del_timer(&bsc->ping_timeout);
bsc_del_timer(&bsc->pong_timeout);
}
static void msc_connect_timeout(void *_bsc_data)
{
struct bsc_data *bsc_data = _bsc_data;
LOGP(DMSC, LOGL_ERROR, "Timeout on the MSC connection.\n");
close_msc(bsc_data);
}
static void msc_pong_timeout(void *_bsc_data)
{
struct bsc_data *bsc_data = _bsc_data;
LOGP(DMSC, LOGL_ERROR, "MSC didn't respond to ping. Closing.\n");
close_msc(bsc_data);
}
static void send_ping(struct bsc_data *bsc)
{
struct msgb *msg;
msg = msgb_alloc_headroom(4096, 128, "ping");
if (!msg) {
LOGP(DMSC, LOGL_ERROR, "Failed to create PING.\n");
return;
}
msg->l2h = msgb_put(msg, 1);
msg->l2h[0] = IPAC_MSGT_PING;
msc_send(bsc, msg, IPAC_PROTO_IPACCESS);
}
static void msc_ping_timeout(void *_bsc_data)
{
struct bsc_data *bsc_data = _bsc_data;
if (bsc_data->ping_time < 0)
return;
send_ping(bsc_data);
/* send another ping in 20 seconds */
bsc_schedule_timer(&bsc_data->ping_timeout, bsc_data->ping_time, 0);
/* also start a pong timer */
bsc_schedule_timer(&bsc_data->pong_timeout, bsc_data->pong_time, 0);
}
/*
* callback with IP access data
*/
static int ipaccess_a_fd_cb(struct bsc_fd *bfd)
{
int error;
struct ipaccess_head *hh;
struct mtp_link *link;
struct bsc_data *bsc;
struct msgb *msg;
msg = ipaccess_read_msg(bfd, &error);
bsc = (struct bsc_data *) bfd->data;
if (!msg) {
if (error == 0)
fprintf(stderr, "The connection to the MSC was lost, exiting\n");
else
fprintf(stderr, "Error in the IPA stream.\n");
close_msc(bsc);
return -1;
}
LOGP(DMSC, LOGL_DEBUG, "From MSC: %s proto: %d\n", hexdump(msg->data, msg->len), msg->l2h[0]);
/* handle base message handling */
hh = (struct ipaccess_head *) msg->data;
ipaccess_rcvmsg_base(msg, bfd);
link = bsc->link.the_link;
/* initialize the networking. This includes sending a GSM08.08 message */
if (hh->proto == IPAC_PROTO_IPACCESS) {
if (bsc->first_contact) {
LOGP(DMSC, LOGL_NOTICE, "Connected to MSC. Sending reset.\n");
bsc_del_timer(&bsc->msc_timeout);
bsc->first_contact = 0;
bsc->closing = 0;
msc_send_reset(bsc);
}
if (msg->l2h[0] == IPAC_MSGT_ID_GET && bsc->token) {
msc_send_id_response(bsc);
} else if (msg->l2h[0] == IPAC_MSGT_PONG) {
bsc_del_timer(&bsc->pong_timeout);
}
} else if (hh->proto == IPAC_PROTO_SCCP) {
struct sccp_parse_result result;
int rc;
rc = bss_patch_filter_msg(msg, &result);
if (rc == BSS_FILTER_RESET_ACK) {
LOGP(DMSC, LOGL_NOTICE, "Filtering reset ack from the MSC\n");
} else if (rc == BSS_FILTER_RLSD) {
LOGP(DMSC, LOGL_DEBUG, "Filtering RLSD from the MSC\n");
update_con_state(rc, &result, msg, 1, 0);
} else if (rc == BSS_FILTER_RLC) {
/* if we receive this we have forwarded a RLSD to the network */
LOGP(DMSC, LOGL_ERROR, "RLC from the network. BAD!\n");
} else if (rc == BSS_FILTER_CLEAR_COMPL) {
LOGP(DMSC, LOGL_ERROR, "Clear Complete from the network.\n");
} else if (link->sccp_up) {
unsigned int sls;
update_con_state(rc, &result, msg, 1, 0);
sls = sls_for_src_ref(result.destination_local_reference);
/* patch a possible PC */
bss_rewrite_header_to_bsc(msg, link->opc, link->dpc);
/* we can not forward it right now */
if (link->sltm_pending) {
LOGP(DMSC, LOGL_NOTICE, "Queueing msg for pending SLTM.\n");
msg->l3h = (u_int8_t *) sls;
msgb_enqueue(&link->pending_msgs, msg);
return 0;
}
if (mtp_link_submit_sccp_data(link, sls, msg->l2h, msgb_l2len(msg)) != 0)
LOGP(DMSC, LOGL_ERROR, "Could not forward SCCP message.\n");
}
} else if (hh->proto == NAT_MUX) {
mgcp_forward(bsc, msg->l2h, msgb_l2len(msg));
} else {
LOGP(DMSC, LOGL_ERROR, "Unknown IPA proto 0x%x\n", hh->proto);
}
msgb_free(msg);
return 0;
}
static int ipaccess_write_cb(struct bsc_fd *fd, struct msgb *msg)
{
int rc;
LOGP(DMSC, LOGL_DEBUG, "Sending to MSC: %s\n", hexdump(msg->data, msg->len));
rc = write(fd->fd, msg->data, msg->len);
if (rc != msg->len)
LOGP(DMSC, LOGL_ERROR, "Could not write to MSC.\n");
return rc;
}
/* called in the case of a non blocking connect */
static int msc_connection_connect(struct bsc_fd *fd, unsigned int what)
{
int rc;
int val;
socklen_t len = sizeof(val);
struct bsc_data *bsc;
bsc = (struct bsc_data *) fd->data;
if (fd != &bsc->msc_connection.bfd) {
LOGP(DMSC, LOGL_ERROR, "This is only working with the MSC connection.\n");
return -1;
}
if ((what & BSC_FD_WRITE) == 0)
return -1;
/* check the socket state */
rc = getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &val, &len);
if (rc != 0) {
LOGP(DMSC, LOGL_ERROR, "getsockopt for the MSC socket failed.\n");
goto error;
}
if (val != 0) {
LOGP(DMSC, LOGL_ERROR, "Not connected to the MSC.\n");
goto error;
}
/* go to full operation */
fd->cb = write_queue_bfd_cb;
fd->when = BSC_FD_READ;
if (!llist_empty(&bsc->msc_connection.msg_queue))
fd->when |= BSC_FD_WRITE;
return 0;
error:
bsc_unregister_fd(fd);
close(fd->fd);
fd->fd = -1;
fd->cb = write_queue_bfd_cb;
fd->when = 0;
release_bsc_resources(bsc);
bsc_del_timer(&bsc->ping_timeout);
bsc_del_timer(&bsc->pong_timeout);
return -1;
}
static void setnonblocking(struct bsc_fd *fd)
{
int flags;
flags = fcntl(fd->fd, F_GETFL);
if (flags < 0) {
perror("fcntl get failed");
close(fd->fd);
fd->fd = -1;
return;
}
flags |= O_NONBLOCK;
flags = fcntl(fd->fd, F_SETFL, flags);
if (flags < 0) {
perror("fcntl get failed");
close(fd->fd);
fd->fd = -1;
return;
}
}
static int connect_to_msc(struct bsc_fd *fd, const char *ip, int port, int tos)
{
struct sockaddr_in sin;
int on = 1, ret;
LOGP(DMSC, LOGL_NOTICE, "Attempting to connect MSC at %s:%d\n", ip, port);
fd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd->fd < 0) {
perror("Creating TCP socket failed");
return fd->fd;
}
/* make it non blocking */
setnonblocking(fd);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
inet_aton(ip, &sin.sin_addr);
setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
ret = setsockopt(fd->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
if (ret != 0)
LOGP(DMSC, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno));
ret = setsockopt(fd->fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
if (ret != 0)
LOGP(DMSC, LOGL_ERROR, "Failed to set IP_TOS: %s\n", strerror(errno));
ret = connect(fd->fd, (struct sockaddr *) &sin, sizeof(sin));
if (ret == -1 && errno == EINPROGRESS) {
LOGP(DMSC, LOGL_ERROR, "MSC Connection in progress\n");
fd->when = BSC_FD_WRITE;
fd->cb = msc_connection_connect;
} else if (ret < 0) {
perror("Connection failed");
close(fd->fd);
fd->fd = -1;
return ret;
} else {
fd->when = BSC_FD_READ;
fd->cb = write_queue_bfd_cb;
}
ret = bsc_register_fd(fd);
if (ret < 0) {
perror("Registering the fd failed");
close(fd->fd);
fd->fd = -1;
return ret;
}
return ret;
}
static void msc_reconnect(void *_data)
{
int rc;
struct bsc_data *bsc = (struct bsc_data *) _data;
bsc_del_timer(&bsc->reconnect_timer);
bsc->first_contact = 1;
rc = connect_to_msc(&bsc->msc_connection.bfd, bsc->msc_address, 5000, bsc->msc_ip_dscp);
if (rc < 0) {
fprintf(stderr, "Opening the MSC connection failed. Trying again\n");
bsc_schedule_timer(&bsc->reconnect_timer, RECONNECT_TIME);
return;
}
bsc->msc_timeout.cb = msc_connect_timeout;
bsc->msc_timeout.data = bsc;
bsc_schedule_timer(&bsc->msc_timeout, bsc->msc_time, 0);
}
void msc_schedule_reconnect(struct bsc_data *bsc)
{
bsc_schedule_timer(&bsc->reconnect_timer, RECONNECT_TIME);
}
/*
* mgcp forwarding is below
*/
static int mgcp_do_write(struct bsc_fd *fd, struct msgb *msg)
{
int ret;
LOGP(DMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len);
ret = write(fd->fd, msg->data, msg->len);
if (ret != msg->len)
LOGP(DMGCP, LOGL_ERROR, "Failed to forward message to MGCP GW (%s).\n", strerror(errno));
return ret;
}
static int mgcp_do_read(struct bsc_fd *fd)
{
struct msgb *mgcp;
int ret;
mgcp = msgb_alloc_headroom(4096, 128, "mgcp_from_gw");
if (!mgcp) {
LOGP(DMGCP, LOGL_ERROR, "Failed to allocate MGCP message.\n");
return -1;
}
ret = read(fd->fd, mgcp->data, 4096 - 128);
if (ret <= 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno));
msgb_free(mgcp);
return -1;
} else if (ret > 4096 - 128) {
LOGP(DMGCP, LOGL_ERROR, "Too much data: %d\n", ret);
msgb_free(mgcp);
return -1;
}
mgcp->l2h = msgb_put(mgcp, ret);
msc_send(fd->data, mgcp, NAT_MUX);
return 0;
}
void mgcp_forward(struct bsc_data *bsc, const u_int8_t *data, unsigned int length)
{
struct msgb *mgcp;
if (length > 4096) {
LOGP(DMGCP, LOGL_ERROR, "Can not forward too big message.\n");
return;
}
mgcp = msgb_alloc(4096, "mgcp_to_gw");
if (!mgcp) {
LOGP(DMGCP, LOGL_ERROR, "Failed to send message.\n");
return;
}
msgb_put(mgcp, length);
memcpy(mgcp->data, data, mgcp->len);
if (write_queue_enqueue(&bsc->mgcp_agent, mgcp) != 0) {
LOGP(DMGCP, LOGL_FATAL, "Could not queue message to MGCP GW.\n");
msgb_free(mgcp);
}
}
static int mgcp_create_port(struct bsc_data *bsc)
{
int on;
struct sockaddr_in addr;
bsc->mgcp_agent.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
if (bsc->mgcp_agent.bfd.fd < 0) {
LOGP(DMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno);
return -1;
}
on = 1;
setsockopt(bsc->mgcp_agent.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
/* try to bind the socket */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_port = 0;
if (bind(bsc->mgcp_agent.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
LOGP(DMGCP, LOGL_FATAL, "Failed to bind to any port.\n");
close(bsc->mgcp_agent.bfd.fd);
bsc->mgcp_agent.bfd.fd = -1;
return -1;
}
/* connect to the remote */
addr.sin_port = htons(2427);
if (connect(bsc->mgcp_agent.bfd.fd, (struct sockaddr *) & addr, sizeof(addr)) < 0) {
LOGP(DMGCP, LOGL_FATAL, "Failed to connect to local MGCP GW. %s\n", strerror(errno));
close(bsc->mgcp_agent.bfd.fd);
bsc->mgcp_agent.bfd.fd = -1;
return -1;
}
write_queue_init(&bsc->mgcp_agent, 10);
bsc->mgcp_agent.bfd.data = bsc;
bsc->mgcp_agent.bfd.when = BSC_FD_READ;
bsc->mgcp_agent.read_cb = mgcp_do_read;
bsc->mgcp_agent.write_cb = mgcp_do_write;
if (bsc_register_fd(&bsc->mgcp_agent.bfd) != 0) {
LOGP(DMGCP, LOGL_FATAL, "Failed to register BFD\n");
close(bsc->mgcp_agent.bfd.fd);
bsc->mgcp_agent.bfd.fd = -1;
return -1;
}
return 0;
}
int msc_init(struct bsc_data *bsc)
{
write_queue_init(&bsc->msc_connection, 100);
bsc->reconnect_timer.cb = msc_reconnect;
bsc->reconnect_timer.data = bsc;
bsc->msc_connection.read_cb = ipaccess_a_fd_cb;
bsc->msc_connection.write_cb = ipaccess_write_cb;
bsc->msc_connection.bfd.data = bsc;
bsc->closing = 1;
/* handle the timeout */
bsc->ping_timeout.cb = msc_ping_timeout;
bsc->ping_timeout.data = bsc;
bsc->pong_timeout.cb = msc_pong_timeout;
bsc->pong_timeout.data = bsc;
/* create MGCP port */
if (mgcp_create_port(bsc) != 0)
return -1;
return 0;
}
static void msc_send(struct bsc_data *bsc, struct msgb *msg, int proto)
{
ipaccess_prepend_header(msg, proto);
if (write_queue_enqueue(&bsc->msc_connection, msg) != 0) {
LOGP(DMSC, LOGL_FATAL, "Failed to queue MSG for the MSC.\n");
msgb_free(msg);
return;
}
}
void msc_send_rlc(struct bsc_data *bsc,
struct sccp_source_reference *src, struct sccp_source_reference *dst)
{
struct msgb *msg;
msg = create_sccp_rlc(src, dst);
if (!msg)
return;
msc_send(bsc, msg, IPAC_PROTO_SCCP);
}
void msc_send_reset(struct bsc_data *bsc)
{
struct msgb *msg;
msg = create_reset();
if (!msg)
return;
msc_send(bsc, msg, IPAC_PROTO_SCCP);
msc_ping_timeout(bsc);
}
static void msc_send_id_response(struct bsc_data *bsc)
{
struct msgb *msg;
msg = msgb_alloc_headroom(4096, 128, "id resp");
msg->l2h = msgb_v_put(msg, IPAC_MSGT_ID_RESP);
msgb_l16tv_put(msg, strlen(bsc->token) + 1,
IPAC_IDTAG_UNITNAME, (u_int8_t *) bsc->token);
msc_send(bsc, msg, IPAC_PROTO_IPACCESS);
}
void msc_send_msg(struct bsc_data *bsc, int rc, struct sccp_parse_result *result, struct msgb *_msg)
{
struct msgb *msg;
if (bsc->msc_connection.bfd.fd < 0) {
LOGP(DMSC, LOGL_ERROR, "No connection to the MSC. dropping\n");
return;
}
msg = msgb_alloc_headroom(4096, 128, "SCCP to MSC");
if (!msg) {
LOGP(DMSC, LOGL_ERROR, "Failed to alloc MSC msg.\n");
return;
}
bss_rewrite_header_for_msc(rc, msg, _msg, result);
msc_send(bsc, msg, IPAC_PROTO_SCCP);
}

509
src/mtp_layer3.c Normal file
View File

@ -0,0 +1,509 @@
/* MTP layer3 main handling code */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <mtp_data.h>
#include <mtp_level3.h>
#include <laf0rge1/debug.h>
#include <laf0rge1/talloc.h>
#include <sccp/sccp.h>
#include <arpa/inet.h>
#include <string.h>
static void *tall_mtp_ctx = NULL;
static struct msgb *mtp_msg_alloc(struct mtp_link *link)
{
struct mtp_level_3_hdr *hdr;
struct msgb *msg = msgb_alloc_headroom(4096, 128, "mtp-msg");
if (!msg) {
LOGP(DINP, LOGL_ERROR, "Failed to allocate mtp msg\n");
return NULL;
}
msg->l2h = msgb_put(msg, sizeof(*hdr));
hdr = (struct mtp_level_3_hdr *) msg->l2h;
hdr->addr = MTP_ADDR(0x0, link->dpc, link->opc);
return msg;
}
static struct msgb *mtp_create_sltm(struct mtp_link *link)
{
const u_int8_t test_ptrn[14] = { 'G', 'S', 'M', 'M', 'M', 'S', };
struct mtp_level_3_hdr *hdr;
struct mtp_level_3_mng *mng;
struct msgb *msg = mtp_msg_alloc(link);
u_int8_t *data;
if (!msg)
return NULL;
hdr = (struct mtp_level_3_hdr *) msg->l2h;
hdr->ni = MTP_NI_NATION_NET;
hdr->ser_ind = MTP_SI_MNT_REG_MSG;
mng = (struct mtp_level_3_mng *) msgb_put(msg, sizeof(*mng));
mng->cmn.h0 = MTP_TST_MSG_GRP;
mng->cmn.h1 = MTP_TST_MSG_SLTM;
mng->length = ARRAY_SIZE(test_ptrn);
data = msgb_put(msg, ARRAY_SIZE(test_ptrn));
memcpy(data, test_ptrn, ARRAY_SIZE(test_ptrn));
/* remember the last tst ptrn... once we have some */
memcpy(link->test_ptrn, test_ptrn, ARRAY_SIZE(test_ptrn));
return msg;
}
static struct msgb *mtp_create_slta(struct mtp_link *link, struct mtp_level_3_mng *in_mng, int l3_len)
{
struct mtp_level_3_hdr *hdr;
struct mtp_level_3_mng *mng;
struct msgb *out = mtp_msg_alloc(link);
if (!out)
return NULL;
hdr = (struct mtp_level_3_hdr *) out->l2h;
hdr->ni = MTP_NI_NATION_NET;
hdr->ser_ind = MTP_SI_MNT_REG_MSG;
mng = (struct mtp_level_3_mng *) msgb_put(out, sizeof(*mng));
mng->cmn.h0 = MTP_TST_MSG_GRP;
mng->cmn.h1 = MTP_TST_MSG_SLTA;
mng->length = l3_len - 2;
msgb_put(out, mng->length);
memcpy(mng->data, in_mng->data, mng->length);
return out;
}
static struct msgb *mtp_tfp_alloc(struct mtp_link *link, int apoc)
{
struct mtp_level_3_hdr *hdr;
struct mtp_level_3_prohib *prb;
struct msgb *out = mtp_msg_alloc(link);
if (!out)
return NULL;
hdr = (struct mtp_level_3_hdr *) out->l2h;
hdr->ni = MTP_NI_NATION_NET;
hdr->ser_ind = MTP_SI_MNT_SNM_MSG;
prb = (struct mtp_level_3_prohib *) msgb_put(out, sizeof(*prb));
prb->cmn.h0 = MTP_PROHIBIT_MSG_GRP;
prb->cmn.h1 = MTP_PROHIBIT_MSG_SIG;
prb->apoc = MTP_MAKE_APOC(apoc);
return out;
}
static struct msgb *mtp_tra_alloc(struct mtp_link *link)
{
struct mtp_level_3_hdr *hdr;
struct mtp_level_3_cmn *cmn;
struct msgb *out = mtp_msg_alloc(link);
if (!out)
return NULL;
hdr = (struct mtp_level_3_hdr *) out->l2h;
hdr->ni = MTP_NI_NATION_NET;
hdr->ser_ind = MTP_SI_MNT_SNM_MSG;
cmn = (struct mtp_level_3_cmn *) msgb_put(out, sizeof(*cmn));
cmn->h0 = MTP_TRF_RESTR_MSG_GRP;
cmn->h1 = MTP_RESTR_MSG_ALLWED;
return out;
}
static struct msgb *mtp_sccp_alloc_ssa(struct mtp_link *link, int sls)
{
struct sccp_data_unitdata *udt;
struct sccp_con_ctrl_prt_mgt *prt;
struct mtp_level_3_hdr *hdr;
u_int8_t *data;
struct msgb *out = mtp_msg_alloc(link);
if (!out)
return NULL;
hdr = (struct mtp_level_3_hdr *) out->l2h;
hdr->ni = MTP_NI_NATION_NET;
hdr->ser_ind = MTP_SI_MNT_SCCP;
/* this appears to be round robin or such.. */
hdr->addr = MTP_ADDR(sls % 16, link->dpc, link->opc);
/* generate the UDT message... libsccp does not offer formating yet */
udt = (struct sccp_data_unitdata *) msgb_put(out, sizeof(*udt));
udt->type = SCCP_MSG_TYPE_UDT;
udt->proto_class = SCCP_PROTOCOL_CLASS_0;
udt->variable_called = 3;
udt->variable_calling = 5;
udt->variable_data = 7;
/* put the called and calling address. It is LV */
data = msgb_put(out, 2 + 1);
data[0] = 2;
data[1] = 0x42;
data[2] = 0x1;
data = msgb_put(out, 2 + 1);
data[0] = 2;
data[1] = 0x42;
data[2] = 0x1;
data = msgb_put(out, 1);
data[0] = sizeof(*prt);
prt = (struct sccp_con_ctrl_prt_mgt *) msgb_put(out, sizeof(*prt));
prt->sst = SCCP_SSA;
prt->assn = 254;
prt->apoc = MTP_MAKE_APOC(link->opc);
prt->mul_ind = 0;
return out;
}
void mtp_link_init(void)
{
tall_mtp_ctx = talloc_named_const(NULL, 1, "mtp-link");
}
static void mtp_send_sltm(struct mtp_link *link)
{
struct msgb *msg;
link->sltm_pending = 1;
msg = mtp_create_sltm(link);
if (!msg) {
LOGP(DINP, LOGL_ERROR, "Failed to allocate SLTM.\n");
return;
}
mtp_link_submit(link, msg);
}
static void mtp_sltm_t1_timeout(void *_link)
{
struct mtp_link *link = (struct mtp_link *) _link;
if (link->slta_misses == 0) {
LOGP(DINP, LOGL_ERROR, "No SLTM response. Retrying. Link: %p\n", link);
++link->slta_misses;
mtp_send_sltm(link);
bsc_schedule_timer(&link->t1_timer, MTP_T1);
} else {
LOGP(DINP, LOGL_ERROR, "Two missing SLTAs. Restart link: %p\n", link);
link->sccp_up = 0;
link->running = 0;
bsc_del_timer(&link->t2_timer);
mtp_link_sccp_down(link);
mtp_link_restart(link);
}
}
static void mtp_sltm_t2_timeout(void *_link)
{
struct mtp_link *link = (struct mtp_link *) _link;
if (!link->running) {
LOGP(DINP, LOGL_INFO, "Not restarting SLTM timer on link: %p\n", link);
return;
}
link->slta_misses = 0;
mtp_send_sltm(link);
bsc_schedule_timer(&link->t1_timer, MTP_T1);
if (link->sltm_once && link->was_up)
LOGP(DINP, LOGL_INFO, "Not sending SLTM again as configured.\n");
else
bsc_schedule_timer(&link->t2_timer, MTP_T2);
}
static void mtp_delayed_start(void *link)
{
mtp_sltm_t2_timeout(link);
}
struct mtp_link *mtp_link_alloc(void)
{
struct mtp_link *link;
link = talloc_zero(tall_mtp_ctx, struct mtp_link);
if (!link)
return NULL;
link->t1_timer.data = link;
link->t1_timer.cb = mtp_sltm_t1_timeout;
link->t2_timer.data = link;
link->t2_timer.cb = mtp_sltm_t2_timeout;
link->delay_timer.data = link;
link->delay_timer.cb = mtp_delayed_start;
INIT_LLIST_HEAD(&link->pending_msgs);
return link;
}
void mtp_link_stop(struct mtp_link *link)
{
bsc_del_timer(&link->t1_timer);
bsc_del_timer(&link->t2_timer);
bsc_del_timer(&link->delay_timer);
link->sccp_up = 0;
link->running = 0;
link->sltm_pending = 0;
mtp_link_sccp_down(link);
}
void mtp_link_reset(struct mtp_link *link)
{
mtp_link_stop(link);
link->running = 1;
bsc_schedule_timer(&link->delay_timer, START_DELAY);
}
static int mtp_link_sign_msg(struct mtp_link *link, struct mtp_level_3_hdr *hdr, int l3_len)
{
struct msgb *msg;
struct mtp_level_3_cmn *cmn;
if (hdr->spare != 0 || hdr->ni != MTP_NI_NATION_NET || l3_len < 1) {
LOGP(DINP, LOGL_ERROR, "Unhandled data (%d, %d, %d)\n",
hdr->spare, hdr->ni, l3_len);
return -1;
}
cmn = (struct mtp_level_3_cmn *) &hdr->data[0];
LOGP(DINP, LOGL_DEBUG, "reg msg: h0: 0x%x h1: 0x%x\n",
cmn->h0, cmn->h1);
switch (cmn->h0) {
case MTP_TRF_RESTR_MSG_GRP:
switch (cmn->h1) {
case MTP_RESTR_MSG_ALLWED:
LOGP(DINP, LOGL_INFO, "Received Restart Allowed. SST should be next: %p\n", link);
link->sccp_up = 0;
mtp_link_sccp_down(link);
msg = mtp_tfp_alloc(link, 0);
if (!msg)
return -1;
mtp_link_submit(link, msg);
msg = mtp_tra_alloc(link);
if (!msg)
return -1;
mtp_link_submit(link, msg);
return 0;
break;
}
break;
}
abort();
return -1;
}
static int mtp_link_regular_msg(struct mtp_link *link, struct mtp_level_3_hdr *hdr, int l3_len)
{
struct msgb *out;
struct mtp_level_3_mng *mng;
if (hdr->spare != 0 || hdr->ni != MTP_NI_NATION_NET || l3_len < 2) {
LOGP(DINP, LOGL_ERROR, "Unhandled data (%d, %d, %d)\n",
hdr->spare, hdr->ni, l3_len);
return -1;
}
mng = (struct mtp_level_3_mng *) &hdr->data[0];
LOGP(DINP, LOGL_DEBUG, "reg msg: h0: 0x%x h1: 0x%x\n",
mng->cmn.h0, mng->cmn.h1);
switch (mng->cmn.h0) {
case MTP_TST_MSG_GRP:
switch (mng->cmn.h1) {
case MTP_TST_MSG_SLTM:
/* simply respond to the request... */
out = mtp_create_slta(link, mng, l3_len);
if (!out)
return -1;
mtp_link_submit(link, out);
return 0;
break;
case MTP_TST_MSG_SLTA:
if (mng->length != 14) {
LOGP(DINP, LOGL_ERROR, "Wrongly sized SLTA: %u\n", mng->length);
return -1;
}
if (l3_len != 16) {
LOGP(DINP, LOGL_ERROR, "Wrongly sized SLTA: %u\n", mng->length);
return -1;
}
if (memcmp(mng->data, link->test_ptrn, sizeof(link->test_ptrn)) != 0) {
LOGP(DINP, LOGL_ERROR, "Wrong test pattern SLTA\n");
return -1;
}
/* we had a matching slta */
bsc_del_timer(&link->t1_timer);
link->sltm_pending = 0;
mtp_link_slta_recv(link);
return 0;
break;
}
break;
}
return -1;
}
static int mtp_link_sccp_data(struct mtp_link *link, struct mtp_level_3_hdr *hdr, struct msgb *msg, int l3_len)
{
struct msgb *out;
struct sccp_con_ctrl_prt_mgt *prt;
msg->l2h = &hdr->data[0];
if (msgb_l2len(msg) != l3_len) {
LOGP(DINP, LOGL_ERROR, "Size is wrong after playing with the l2h header.\n");
return -1;
}
if (link->sccp_up) {
mtp_link_forward_sccp(link, msg, MTP_LINK_SLS(hdr->addr));
return 0;
} else {
struct sccp_parse_result sccp;
memset(&sccp, 0, sizeof(sccp));
if (sccp_parse_header(msg, &sccp) != 0) {
LOGP(DINP, LOGL_ERROR, "Failed to parsed SCCP header.\n");
return -1;
}
if (sccp_determine_msg_type(msg) != SCCP_MSG_TYPE_UDT) {
LOGP(DINP, LOGL_ERROR, "Dropping sccp data: 0x%x\n",
sccp_determine_msg_type(msg));
return -1;
}
if (msgb_l3len(msg) != 5) {
LOGP(DINP, LOGL_ERROR, "SCCP UDT msg of unexpected size: %u\n",
msgb_l3len(msg));
return -1;
}
if (msg->l3h[0] != SCCP_SST) {
LOGP(DINP, LOGL_ERROR, "Expected SCCP SST but got 0x%x\n",
msg->l3h[0]);
return -1;
}
prt = (struct sccp_con_ctrl_prt_mgt *) &msg->l3h[0];
if (prt->assn != 254 || prt->apoc != MTP_MAKE_APOC(link->opc)) {
LOGP(DINP, LOGL_ERROR, "Unknown SSN/APOC assn: %u, apoc: %u/%u\n",
prt->assn, ntohs(prt->apoc), prt->apoc);
return -1;
}
out = mtp_sccp_alloc_ssa(link, MTP_LINK_SLS(hdr->addr));
if (!out)
return -1;
link->sccp_up = 1;
link->was_up = 1;
LOGP(DINP, LOGL_INFO, "SCCP is established. %p\n", link);
mtp_link_submit(link, out);
}
return 0;
}
int mtp_link_data(struct mtp_link *link, struct msgb *msg)
{
int rc = -1;
struct mtp_level_3_hdr *hdr;
int l3_len;
if (!msg->l2h || msgb_l2len(msg) < sizeof(*hdr))
return -1;
if (!link->running) {
LOGP(DINP, LOGL_ERROR, "Link is not running. Call mtp_link_reset first: %p\n", link);
return -1;
}
hdr = (struct mtp_level_3_hdr *) msg->l2h;
l3_len = msgb_l2len(msg) - sizeof(*hdr);
switch (hdr->ser_ind) {
case MTP_SI_MNT_SNM_MSG:
rc = mtp_link_sign_msg(link, hdr, l3_len);
break;
case MTP_SI_MNT_REG_MSG:
rc = mtp_link_regular_msg(link, hdr, l3_len);
break;
case MTP_SI_MNT_SCCP:
rc = mtp_link_sccp_data(link, hdr, msg, l3_len);
break;
default:
fprintf(stderr, "Unhandled: %u\n", hdr->ser_ind);
break;
}
return rc;
}
int mtp_link_submit_sccp_data(struct mtp_link *link, int sls, const u_int8_t *data, unsigned int length)
{
u_int8_t *put_ptr;
struct mtp_level_3_hdr *hdr;
struct msgb *msg;
if (!link->sccp_up) {
LOGP(DINP, LOGL_ERROR, "SCCP msg after TRA and before SSA. Dropping it.\n");
return -1;
}
msg = mtp_msg_alloc(link);
if (!msg)
return -1;
hdr = (struct mtp_level_3_hdr *) msg->l2h;
hdr->ni = MTP_NI_NATION_NET;
hdr->ser_ind = MTP_SI_MNT_SCCP;
hdr->addr = MTP_ADDR(sls % 16, link->dpc, link->opc);
/* copy the raw sccp data */
put_ptr = msgb_put(msg, length);
memcpy(put_ptr, data, length);
mtp_link_submit(link, msg);
return 0;
}

1
src/openbsc_nat/README Normal file
View File

@ -0,0 +1 @@
This is OpenBSC code. Do not patch. Fix it upstream.

65
src/openbsc_nat/bssap.c Normal file
View File

@ -0,0 +1,65 @@
/* GSM 08.08 BSSMAP handling */
/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009 by on-waves.com
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <openbsc_nat/bssap.h>
#include <openbsc_nat/tlv.h>
#include <sccp/sccp.h>
#include <arpa/inet.h>
#include <assert.h>
#define BSSMAP_MSG_SIZE 512
#define BSSMAP_MSG_HEADROOM 128
static const struct tlv_definition bss_att_tlvdef = {
.def = {
[GSM0808_IE_IMSI] = { TLV_TYPE_TLV },
[GSM0808_IE_TMSI] = { TLV_TYPE_TLV },
[GSM0808_IE_CELL_IDENTIFIER_LIST] = { TLV_TYPE_TLV },
[GSM0808_IE_CHANNEL_NEEDED] = { TLV_TYPE_TV },
[GSM0808_IE_EMLPP_PRIORITY] = { TLV_TYPE_TV },
[GSM0808_IE_CHANNEL_TYPE] = { TLV_TYPE_TLV },
[GSM0808_IE_PRIORITY] = { TLV_TYPE_TLV },
[GSM0808_IE_CIRCUIT_IDENTITY_CODE] = { TLV_TYPE_TV },
[GSM0808_IE_DOWNLINK_DTX_FLAG] = { TLV_TYPE_TV },
[GSM0808_IE_INTERFERENCE_BAND_TO_USE] = { TLV_TYPE_TV },
[GSM0808_IE_CLASSMARK_INFORMATION_T2] = { TLV_TYPE_TLV },
[GSM0808_IE_GROUP_CALL_REFERENCE] = { TLV_TYPE_TLV },
[GSM0808_IE_TALKER_FLAG] = { TLV_TYPE_T },
[GSM0808_IE_CONFIG_EVO_INDI] = { TLV_TYPE_TV },
[GSM0808_IE_LSA_ACCESS_CTRL_SUPPR] = { TLV_TYPE_TV },
[GSM0808_IE_SERVICE_HANDOVER] = { TLV_TYPE_TV},
[GSM0808_IE_ENCRYPTION_INFORMATION] = { TLV_TYPE_TLV },
[GSM0808_IE_CIPHER_RESPONSE_MODE] = { TLV_TYPE_TV },
[GSM0808_IE_SPEECH_VERSION] = { TLV_TYPE_TV },
[GSM0808_IE_CHOSEN_ENCR_ALG] = { TLV_TYPE_TV },
[GSM0808_IE_CHOSEN_CHANNEL] = { TLV_TYPE_TV },
},
};
const struct tlv_definition *gsm0808_att_tlvdef()
{
return &bss_att_tlvdef;
}

View File

@ -0,0 +1,160 @@
#include <stdio.h>
#include <openbsc_nat/tlv.h>
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
#endif
struct tlv_definition tvlv_att_def;
int tlv_dump(struct tlv_parsed *dec)
{
int i;
for (i = 0; i <= 0xff; i++) {
if (!dec->lv[i].val)
continue;
printf("T=%02x L=%d\n", i, dec->lv[i].len);
}
return 0;
}
/* o_tag: output: tag found
* o_len: output: length of the data
* o_val: output: pointer to the data
* def: input: a structure defining the valid TLV tags / configurations
* buf: input: the input data buffer to be parsed
* buf_len: input: the length of the input data buffer
*
* Also, returns the number of bytes consumed by the TLV entry
*/
int tlv_parse_one(u_int8_t *o_tag, u_int16_t *o_len, const u_int8_t **o_val,
const struct tlv_definition *def,
const u_int8_t *buf, int buf_len)
{
u_int8_t tag;
int len;
tag = *buf;
*o_tag = tag;
/* FIXME: use tables for knwon IEI */
switch (def->def[tag].type) {
case TLV_TYPE_T:
/* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
*o_val = buf;
*o_len = 0;
len = 1;
break;
case TLV_TYPE_TV:
*o_val = buf+1;
*o_len = 1;
len = 2;
break;
case TLV_TYPE_FIXED:
*o_val = buf+1;
*o_len = def->def[tag].fixed_len;
len = def->def[tag].fixed_len + 1;
break;
case TLV_TYPE_TLV:
/* GSM TS 04.07 11.2.4: Type 4 TLV */
if (buf + 1 > buf + buf_len)
return -1;
*o_val = buf+2;
*o_len = *(buf+1);
len = *o_len + 2;
if (len > buf_len)
return -2;
break;
case TLV_TYPE_TvLV:
if (*(buf+1) & 0x80) {
/* like TLV, but without highest bit of len */
if (buf + 1 > buf + buf_len)
return -1;
*o_val = buf+2;
*o_len = *(buf+1) & 0x7f;
len = *o_len + 2;
if (len > buf_len)
return -2;
break;
}
/* like TL16V, fallthrough */
case TLV_TYPE_TL16V:
if (2 > buf_len)
return -1;
*o_val = buf+3;
*o_len = *(buf+1) << 8 | *(buf+2);
len = *o_len + 3;
if (len > buf_len)
return -2;
break;
default:
return -3;
}
return len;
}
/* dec: output: a caller-allocated pointer to a struct tlv_parsed,
* def: input: a structure defining the valid TLV tags / configurations
* buf: input: the input data buffer to be parsed
* buf_len: input: the length of the input data buffer
* lv_tag: input: an initial LV tag at the start of the buffer
* lv_tag2: input: a second initial LV tag following lv_tag
*/
int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
const u_int8_t *buf, int buf_len, u_int8_t lv_tag,
u_int8_t lv_tag2)
{
int ofs = 0, num_parsed = 0;
u_int16_t len;
memset(dec, 0, sizeof(*dec));
if (lv_tag) {
if (ofs > buf_len)
return -1;
dec->lv[lv_tag].val = &buf[ofs+1];
dec->lv[lv_tag].len = buf[ofs];
len = dec->lv[lv_tag].len + 1;
if (ofs + len > buf_len)
return -2;
num_parsed++;
ofs += len;
}
if (lv_tag2) {
if (ofs > buf_len)
return -1;
dec->lv[lv_tag2].val = &buf[ofs+1];
dec->lv[lv_tag2].len = buf[ofs];
len = dec->lv[lv_tag2].len + 1;
if (ofs + len > buf_len)
return -2;
num_parsed++;
ofs += len;
}
while (ofs < buf_len) {
int rv;
u_int8_t tag;
const u_int8_t *val;
rv = tlv_parse_one(&tag, &len, &val, def,
&buf[ofs], buf_len-ofs);
if (rv < 0)
return rv;
dec->lv[tag].val = val;
dec->lv[tag].len = len;
ofs += rv;
num_parsed++;
}
//tlv_dump(dec);
return num_parsed;
}
static __attribute__((constructor)) void on_dso_load_tlv(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++)
tvlv_att_def.def[i].type = TLV_TYPE_TvLV;
}

86
src/pcap.c Normal file
View File

@ -0,0 +1,86 @@
/* PCAP code from OpenBSC done by Holger Freyther */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <mtp_pcap.h>
#include <sys/time.h>
#include <unistd.h>
#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
/*
* pcap writing of the misdn load
* pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat
*/
struct pcap_hdr {
u_int32_t magic_number;
u_int16_t version_major;
u_int16_t version_minor;
int32_t thiszone;
u_int32_t sigfigs;
u_int32_t snaplen;
u_int32_t network;
} __attribute__((packed));
struct pcaprec_hdr {
u_int32_t ts_sec;
u_int32_t ts_usec;
u_int32_t incl_len;
u_int32_t orig_len;
} __attribute__((packed));
int mtp_pcap_write_header(int fd)
{
static struct pcap_hdr hdr = {
.magic_number = 0xa1b2c3d4,
.version_major = 2,
.version_minor = 4,
.thiszone = 0,
.sigfigs = 0,
.snaplen = 65535,
.network = 141,
};
return write(fd, &hdr, sizeof(hdr));
}
int mtp_pcap_write_msu(int fd, const u_int8_t *data, int length)
{
int rc_h, rc_d;
struct timeval tv;
struct pcaprec_hdr payload_header = {
.ts_sec = 0,
.ts_usec = 0,
.incl_len = length,
.orig_len = length,
};
gettimeofday(&tv, NULL);
payload_header.ts_sec = tv.tv_sec;
payload_header.ts_usec = tv.tv_usec;
rc_h = write(fd, &payload_header, sizeof(payload_header));
rc_d = write(fd, data, length);
return rc_h == sizeof(payload_header) && rc_d == length;
}

112
src/snmp_mtp.c Normal file
View File

@ -0,0 +1,112 @@
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <snmp_mtp.h>
#include <laf0rge1/talloc.h>
static void add_pdu_var(netsnmp_pdu *pdu, const char *mib_name, int id, const char *value)
{
oid oid_name[MAX_OID_LEN];
size_t name_length;
char buf[4096];
buf[4095] = '\0';
snprintf(buf, sizeof(buf)-1, "%s.%d", mib_name, id);
name_length = MAX_OID_LEN;
if (snmp_parse_oid(buf, oid_name, &name_length) == NULL) {
snmp_perror(buf);
return;
}
if (snmp_add_var(pdu, oid_name, name_length, '=', value)) {
snmp_perror(buf);
return;
}
}
static void send_pdu(netsnmp_session *ss, netsnmp_pdu *pdu)
{
int status;
netsnmp_pdu *response;
status = snmp_synch_response(ss, pdu, &response);
if (status == STAT_ERROR) {
snmp_sess_perror("set failed", ss);
} else if (status == STAT_TIMEOUT) {
fprintf(stderr, "Timeout for SNMP.\n");
}
if (response)
snmp_free_pdu(response);
}
void snmp_mtp_start_c7_datalink(struct snmp_mtp_session *session, int link_id)
{
netsnmp_pdu *pdu;
pdu = snmp_pdu_create(SNMP_MSG_SET);
add_pdu_var(pdu, "PTI-NexusWareC7-MIB::nwc7DatalinkCommand", link_id, "nwc7DatalinkCmdPowerOn");
add_pdu_var(pdu, "PTI-NexusWareC7-MIB::nwc7Mtp2Active", link_id, "true");
send_pdu(session->ss, pdu);
}
void snmp_mtp_stop_c7_datalink(struct snmp_mtp_session *session, int link_id)
{
netsnmp_pdu *pdu;
pdu = snmp_pdu_create(SNMP_MSG_SET);
add_pdu_var(pdu, "PTI-NexusWareC7-MIB::nwc7Mtp2Active", link_id, "false");
send_pdu(session->ss, pdu);
}
struct snmp_mtp_session *snmp_mtp_session_create(char *host)
{
struct snmp_mtp_session *session = talloc_zero(NULL, struct snmp_mtp_session);
if (!session)
return NULL;
init_snmp("cellmgr_ng");
snmp_sess_init(&session->session);
session->session.peername = host;
session->session.version = SNMP_VERSION_1;
session->session.community = (unsigned char *) "private";
session->session.community_len = strlen((const char *) session->session.community);
session->ss = snmp_open(&session->session);
if (!session->ss) {
snmp_perror("create failure");
snmp_log(LOG_ERR, "Could not connect to the remote.\n");
talloc_free(session);
return NULL;
}
return session;
}
void snmp_mtp_deactivate(struct snmp_mtp_session *session)
{
snmp_mtp_stop_c7_datalink(session, 1);
}
void snmp_mtp_activate(struct snmp_mtp_session *session)
{
snmp_mtp_start_c7_datalink(session, 1);
}

100
src/thread.c Normal file
View File

@ -0,0 +1,100 @@
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <thread.h>
#include <laf0rge1/talloc.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
static void *tall_ctx_thr;
static int thread_finish(struct thread_notifier *not)
{
pthread_mutex_destroy(&not->guard);
close(not->fd[0]);
close(not->fd[1]);
return 0;
}
void thread_init(void)
{
tall_ctx_thr = talloc_named_const(NULL, 1, "threads");
}
struct thread_notifier *thread_notifier_alloc()
{
struct thread_notifier *not = talloc_zero(tall_ctx_thr, struct thread_notifier);
if (!not)
return NULL;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, not->fd) == -1) {
talloc_free(not);
return NULL;
}
if (pthread_mutex_init(&not->guard, NULL) != 0) {
close(not->fd[0]);
close(not->fd[1]);
talloc_free(not);
return NULL;
}
not->bfd.fd = not->fd[1];
INIT_LLIST_HEAD(&not->__head1);
INIT_LLIST_HEAD(&not->__head2);
not->main_head = &not->__head1;
not->thread_head = &not->__head2;
talloc_set_destructor(not, thread_finish);
return not;
}
void thread_safe_add(struct thread_notifier *not, struct llist_head *_new)
{
char c;
pthread_mutex_lock(&not->guard);
llist_add_tail(_new, not->thread_head);
if (!not->no_write && write(not->fd[0], &c, sizeof(c)) != 1) {
fprintf(stderr, "BAD NEWS. Socket write failed.\n");
}
pthread_mutex_unlock(&not->guard);
}
void thread_swap(struct thread_notifier *not)
{
pthread_mutex_lock(&not->guard);
if (not->main_head == &not->__head1) {
not->main_head = &not->__head2;
not->thread_head = &not->__head1;
} else {
not->main_head = &not->__head1;
not->thread_head = &not->__head2;
}
pthread_mutex_unlock(&not->guard);
}

91
src/write_queue.c Normal file
View File

@ -0,0 +1,91 @@
/* Generic write queue implementation */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <write_queue.h>
#include <laf0rge1/debug.h>
int write_queue_bfd_cb(struct bsc_fd *fd, unsigned int what)
{
struct write_queue *queue;
queue = container_of(fd, struct write_queue, bfd);
if (what & BSC_FD_READ)
queue->read_cb(fd);
if (what & BSC_FD_WRITE) {
struct msgb *msg;
fd->when &= ~BSC_FD_WRITE;
msg = msgb_dequeue(&queue->msg_queue);
if (!msg)
return -1;
--queue->current_length;
queue->write_cb(fd, msg);
msgb_free(msg);
if (!llist_empty(&queue->msg_queue))
fd->when |= BSC_FD_WRITE;
}
return 0;
}
void write_queue_init(struct write_queue *queue, int max_length)
{
queue->max_length = max_length;
queue->current_length = 0;
queue->read_cb = NULL;
queue->write_cb = NULL;
queue->bfd.cb = write_queue_bfd_cb;
queue->paused = 0;
INIT_LLIST_HEAD(&queue->msg_queue);
}
int write_queue_enqueue(struct write_queue *queue, struct msgb *data)
{
if (queue->current_length + 1 >= queue->max_length)
LOGP(DMSC, LOGL_ERROR, "The queue is full. Dropping not yet implemented.\n");
++queue->current_length;
msgb_enqueue(&queue->msg_queue, data);
if (!queue->paused)
queue->bfd.when |= BSC_FD_WRITE;
return 0;
}
void write_queue_pause(struct write_queue *queue)
{
queue->paused = 1;
queue->bfd.when &= ~BSC_FD_WRITE;
}
void write_queue_unpause(struct write_queue *queue)
{
queue->paused = 0;
if (!llist_empty(&queue->msg_queue))
queue->bfd.when |= BSC_FD_WRITE;
}

1
tests/Makefile.am Normal file
View File

@ -0,0 +1 @@
SUBDIRS = mtp patching

4
tests/mtp/Makefile.am Normal file
View File

@ -0,0 +1,4 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include -Wall
noinst_PROGRAMS = mtp_parse_test
mtp_parse_test_SOURCES = mtp_parse_test.c

628
tests/mtp/mtp_parse_test.c Normal file
View File

@ -0,0 +1,628 @@
/* MTP Layer3 parsing tests */
#include <mtp_level3.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
struct mtp_test {
const u_int8_t *input;
const u_int16_t length;
struct mtp_level_3_hdr hdr;
int has_mng;
struct mtp_level_3_mng mng;
int has_prohib;
struct mtp_level_3_prohib prohib;
};
static const unsigned char pkt1[] = {
0x81, 0x88, 0xc0, 0x16, 0x00, 0x11, 0xe0, 0x62,
0x61, 0x72, 0x62, 0x61, 0x6e, 0x6f, 0x62, 0x61,
0x72, 0x6e, 0x61, 0x62, 0x6f };
static const unsigned char pkt2[] = {
0x81, 0x5b, 0x00, 0x22, 0x00, 0x11, 0xe0, 0x41,
0x6d, 0x69, 0x74, 0x20, 0x43, 0x68, 0x61, 0x6e,
0x64, 0x72, 0x61, 0x00, 0x00 };
static const unsigned char pkt3[] = {
0x81, 0x88, 0xc0, 0x16, 0x00, 0x21, 0xe0, 0x41,
0x6d, 0x69, 0x74, 0x20, 0x43, 0x68, 0x61, 0x6e,
0x64, 0x72, 0x61, 0x00, 0x00 };
static const unsigned char pkt4[] = {
0x81, 0x5b, 0x00, 0x22, 0x00, 0x21, 0xe0, 0x62,
0x61, 0x72, 0x62, 0x61, 0x6e, 0x6f, 0x62, 0x61,
0x72, 0x6e, 0x61, 0x62, 0x6f };
static const unsigned char pkt5[] = {
0x81, 0x88, 0xc0, 0x16, 0x00, 0x21, 0xe0, 0x62,
0x61, 0x72, 0x62, 0x61, 0x6e, 0x6f, 0x62, 0x61,
0x72, 0x6e, 0x61, 0x62, 0x6f };
static const unsigned char pkt6[] = {
0x80, 0x5b, 0x00, 0x22, 0x00, 0x17 };
static const unsigned char pkt7[] = {
0x80, 0x88, 0xc0, 0x16, 0x00, 0x14, 0x56, 0x00 };
static const unsigned char pkt8[] = {
0x80, 0x88, 0xc0, 0x16, 0x00, 0x14, 0x00, 0x00 };
static const unsigned char pkt9[] = {
0x80, 0x88, 0xc0, 0x16, 0x00, 0x17 };
static const unsigned char pkt10[] = {
0x83, 0x5b, 0x00, 0x22, 0x20, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0x01, 0x04,
0xc3, 0x88, 0x00, 0x01, 0x05, 0x03, 0xfe, 0x5b,
0x00, 0x01 };
static const unsigned char pkt11[] = {
0x83, 0x88, 0xc0, 0x16, 0xd0, 0x09, 0x00, 0x03,
0x05, 0x07, 0x02, 0x42, 0x01, 0x02, 0x42, 0x01,
0x05, 0x01, 0xfe, 0x5b, 0x00, 0x00 };
static const unsigned char pkt12[] = {
0x83, 0x5b, 0x00, 0x22, 0x30, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x30,
0x04, 0x01, 0x20 };
static const unsigned char pkt13[] = {
0x83, 0x5b, 0x00, 0x22, 0x40, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x0f, 0x04, 0x01, 0x07 };
static const unsigned char pkt14[] = {
0x83, 0x5b, 0x00, 0x22, 0x50, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x10, 0x04, 0x01, 0x07 };
static const unsigned char pkt15[] = {
0x83, 0x5b, 0x00, 0x22, 0x60, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x11, 0x04, 0x01, 0x07 };
static const unsigned char pkt16[] = {
0x83, 0x5b, 0x00, 0x22, 0x70, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x12, 0x04, 0x01, 0x07 };
static const unsigned char pkt17[] = {
0x83, 0x5b, 0x00, 0x22, 0x80, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x13, 0x04, 0x01, 0x07 };
static const unsigned char pkt18[] = {
0x83, 0x5b, 0x00, 0x22, 0x90, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x14, 0x04, 0x01, 0x07 };
static const unsigned char pkt19[] = {
0x83, 0x5b, 0x00, 0x22, 0xa0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x15, 0x04, 0x01, 0x07 };
static const unsigned char pkt20[] = {
0x83, 0x5b, 0x00, 0x22, 0xb0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x16, 0x04, 0x01, 0x07 };
static const unsigned char pkt21[] = {
0x83, 0x5b, 0x00, 0x22, 0xc0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x17, 0x04, 0x01, 0x07 };
static const unsigned char pkt22[] = {
0x83, 0x5b, 0x00, 0x22, 0xd0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x18, 0x04, 0x01, 0x07 };
static const unsigned char pkt23[] = {
0x83, 0x5b, 0x00, 0x22, 0xe0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x19, 0x04, 0x01, 0x07 };
static const unsigned char pkt24[] = {
0x83, 0x5b, 0x00, 0x22, 0xf0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x1a, 0x04, 0x01, 0x07 };
static const unsigned char pkt25[] = {
0x83, 0x5b, 0x00, 0x22, 0x00, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x1b, 0x04, 0x01, 0x07 };
static const unsigned char pkt26[] = {
0x83, 0x5b, 0x00, 0x22, 0x10, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x1c, 0x04, 0x01, 0x07 };
static const unsigned char pkt27[] = {
0x83, 0x5b, 0x00, 0x22, 0x20, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x1d, 0x04, 0x01, 0x07 };
static const unsigned char pkt28[] = {
0x83, 0x5b, 0x00, 0x22, 0x30, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x1e, 0x04, 0x01, 0x07 };
static const unsigned char pkt29[] = {
0x83, 0x88, 0xc0, 0x16, 0x60, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x0f };
static const unsigned char pkt30[] = {
0x83, 0x88, 0xc0, 0x16, 0x70, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x10 };
static const unsigned char pkt31[] = {
0x83, 0x88, 0xc0, 0x16, 0x80, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x11 };
static const unsigned char pkt32[] = {
0x83, 0x88, 0xc0, 0x16, 0x90, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x12 };
static const unsigned char pkt33[] = {
0x83, 0x88, 0xc0, 0x16, 0xa0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x13 };
static const unsigned char pkt34[] = {
0x83, 0x88, 0xc0, 0x16, 0xb0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x14 };
static const unsigned char pkt35[] = {
0x83, 0x88, 0xc0, 0x16, 0xc0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x15 };
static const unsigned char pkt36[] = {
0x83, 0x88, 0xc0, 0x16, 0xd0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x16 };
static const unsigned char pkt37[] = {
0x83, 0x88, 0xc0, 0x16, 0xe0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x17 };
static const unsigned char pkt38[] = {
0x83, 0x88, 0xc0, 0x16, 0xf0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x18 };
static const unsigned char pkt39[] = {
0x83, 0x88, 0xc0, 0x16, 0x00, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x19 };
static const unsigned char pkt40[] = {
0x83, 0x88, 0xc0, 0x16, 0x10, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x1a };
static const unsigned char pkt41[] = {
0x83, 0x88, 0xc0, 0x16, 0x20, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x1b };
static const unsigned char pkt42[] = {
0x83, 0x88, 0xc0, 0x16, 0x30, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x1c };
static const unsigned char pkt43[] = {
0x83, 0x88, 0xc0, 0x16, 0x40, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x1d };
static const unsigned char pkt44[] = {
0x83, 0x88, 0xc0, 0x16, 0x50, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x1e };
static const unsigned char pkt45[] = {
0x83, 0x5b, 0x00, 0x22, 0x40, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x0f, 0x04, 0x01, 0x07 };
static const unsigned char pkt46[] = {
0x83, 0x5b, 0x00, 0x22, 0x50, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x10, 0x04, 0x01, 0x07 };
static const unsigned char pkt47[] = {
0x83, 0x5b, 0x00, 0x22, 0x60, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x11, 0x04, 0x01, 0x07 };
static const unsigned char pkt48[] = {
0x83, 0x5b, 0x00, 0x22, 0x70, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x12, 0x04, 0x01, 0x07 };
static const unsigned char pkt49[] = {
0x83, 0x5b, 0x00, 0x22, 0x80, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x13, 0x04, 0x01, 0x07 };
static const unsigned char pkt50[] = {
0x83, 0x5b, 0x00, 0x22, 0x90, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x14, 0x04, 0x01, 0x07 };
static const unsigned char pkt51[] = {
0x83, 0x5b, 0x00, 0x22, 0xa0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x15, 0x04, 0x01, 0x07 };
static const unsigned char pkt52[] = {
0x83, 0x5b, 0x00, 0x22, 0xb0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x16, 0x04, 0x01, 0x07 };
static const unsigned char pkt53[] = {
0x83, 0x5b, 0x00, 0x22, 0xc0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x17, 0x04, 0x01, 0x07 };
static const unsigned char pkt54[] = {
0x83, 0x5b, 0x00, 0x22, 0xd0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x18, 0x04, 0x01, 0x07 };
static const unsigned char pkt55[] = {
0x83, 0x5b, 0x00, 0x22, 0xe0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x19, 0x04, 0x01, 0x07 };
static const unsigned char pkt56[] = {
0x83, 0x5b, 0x00, 0x22, 0xf0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x1a, 0x04, 0x01, 0x07 };
static const unsigned char pkt57[] = {
0x83, 0x5b, 0x00, 0x22, 0x00, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x1b, 0x04, 0x01, 0x07 };
static const unsigned char pkt58[] = {
0x83, 0x5b, 0x00, 0x22, 0x10, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x1c, 0x04, 0x01, 0x07 };
static const unsigned char pkt59[] = {
0x83, 0x5b, 0x00, 0x22, 0x20, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x1d, 0x04, 0x01, 0x07 };
static const unsigned char pkt60[] = {
0x83, 0x5b, 0x00, 0x22, 0x30, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
0x01, 0x00, 0x1e, 0x04, 0x01, 0x07 };
static const unsigned char pkt61[] = {
0x83, 0x88, 0xc0, 0x16, 0x60, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x0f };
static const unsigned char pkt62[] = {
0x83, 0x88, 0xc0, 0x16, 0x70, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x10 };
static const unsigned char pkt63[] = {
0x83, 0x88, 0xc0, 0x16, 0x80, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x11 };
static const unsigned char pkt64[] = {
0x83, 0x88, 0xc0, 0x16, 0x90, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x12 };
static const unsigned char pkt65[] = {
0x83, 0x88, 0xc0, 0x16, 0xa0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x13 };
static const unsigned char pkt66[] = {
0x83, 0x88, 0xc0, 0x16, 0xb0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x14 };
static const unsigned char pkt67[] = {
0x83, 0x88, 0xc0, 0x16, 0xc0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x15 };
static const unsigned char pkt68[] = {
0x83, 0x88, 0xc0, 0x16, 0xd0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x16 };
static const unsigned char pkt69[] = {
0x83, 0x88, 0xc0, 0x16, 0xe0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x17 };
static const unsigned char pkt70[] = {
0x83, 0x88, 0xc0, 0x16, 0xf0, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x18 };
static const unsigned char pkt71[] = {
0x83, 0x88, 0xc0, 0x16, 0x00, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x19 };
static const unsigned char pkt72[] = {
0x83, 0x88, 0xc0, 0x16, 0x10, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x1a };
static const unsigned char pkt73[] = {
0x83, 0x88, 0xc0, 0x16, 0x20, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x1b };
static const unsigned char pkt74[] = {
0x83, 0x88, 0xc0, 0x16, 0x30, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x1c };
static const unsigned char pkt75[] = {
0x83, 0x88, 0xc0, 0x16, 0x40, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x1d };
static const unsigned char pkt76[] = {
0x83, 0x88, 0xc0, 0x16, 0x50, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
0x01, 0x00, 0x1e };
static const unsigned char pkt77[] = {
0x83, 0x88, 0xc0, 0x16, 0x60, 0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
0x43, 0x5b, 0x00, 0xfe, 0x03, 0x00, 0x01, 0x31 };
static struct mtp_test tests[] = {
{
.input = pkt1,
.length = sizeof(pkt1),
.hdr = {
.ni = 0x02,
.spare = 0x00,
.ser_ind = 0x01,
},
.has_mng = 1,
.mng = {
.cmn = {
.h0 = 0x01,
.h1 = 0x01,
},
.length = 14,
},
},
{
.input = pkt3,
.length = sizeof(pkt3),
.hdr = {
.ni = 0x02,
.spare = 0x00,
.ser_ind = 0x01,
},
.has_mng = 1,
.mng = {
.cmn = {
.h0 = 0x01,
.h1 = 0x02,
},
.length = 14,
},
},
{
.input = pkt7,
.length = sizeof(pkt7),
.hdr = {
.ni = 2,
.spare = 0,
.ser_ind = 0,
},
.has_prohib = 1,
.prohib = {
.cmn = {
.h0 = 0x04,
.h1 = 0x1,
},
},
}
};
static void check_hdr(const u_int8_t *data, const struct mtp_level_3_hdr *t_hdr)
{
struct mtp_level_3_hdr *hdr;
hdr = (struct mtp_level_3_hdr *) data;
if (memcmp(hdr, t_hdr, sizeof(*hdr)) == 0)
return;
if (hdr->ni != t_hdr->ni)
fprintf(stderr, "NI failed.\n");
if (hdr->spare != t_hdr->spare)
fprintf(stderr, "spare not equal\n");
if (hdr->ser_ind != t_hdr->ser_ind)
fprintf(stderr, "ser_ind not equal\n");
if (hdr->addr != t_hdr->addr)
fprintf(stderr, "routing data not equal\n");
fprintf(stderr, "FAIL: Comparing headers failed.\n");
abort();
}
static void check_mng(const u_int8_t *data, const struct mtp_level_3_mng *t_mng)
{
struct mtp_level_3_hdr *hdr = (struct mtp_level_3_hdr *) data;
struct mtp_level_3_mng *mng = (struct mtp_level_3_mng *) &hdr->data[0];
if (memcmp(mng, t_mng, sizeof(*mng)) == 0)
return;
if (mng->cmn.h0 != t_mng->cmn.h0)
fprintf(stderr, "h0 not equal.\n");
if (mng->cmn.h1 != t_mng->cmn.h1)
fprintf(stderr, "h1 not equal.\n");
if (mng->length != t_mng->length)
fprintf(stderr, "length not euqal.\n");
fprintf(stderr, "FAIL: Comparing the mtp_level_3_mng\n");
abort();
}
static void check_prohib(const u_int8_t *data, const struct mtp_level_3_prohib *t_prohib)
{
struct mtp_level_3_hdr *hdr = (struct mtp_level_3_hdr *) data;
struct mtp_level_3_prohib *prohib = (struct mtp_level_3_prohib *) &hdr->data[0];
if (memcmp(prohib, t_prohib, sizeof(*prohib)) == 0)
return;
if (prohib->cmn.h0 != t_prohib->cmn.h0)
fprintf(stderr, "h0 not equal.\n");
if (prohib->cmn.h1 != t_prohib->cmn.h1)
fprintf(stderr, "h1 not equal.\n");
if (ntohs(prohib->apoc) != t_prohib->apoc)
fprintf(stderr, "apoc not euqal.\n");
fprintf(stderr, "FAIL: Comparing the mtp_level_3_prohib\n");
abort();
}
int main(int argc, char **argv)
{
u_int32_t addr;
int i;
/* set the addresses here due big endian MTP_ADDRESS macro */
tests[0].hdr.addr = MTP_ADDR(0x00, 136, 91);
tests[1].hdr.addr = MTP_ADDR(0x00, 136, 91);
tests[2].hdr.addr = MTP_ADDR(0x00, 136, 91);
tests[2].prohib.apoc = MTP_MAKE_APOC(86);
for (i = 0; i < ARRAY_SIZE(tests); ++i) {
check_hdr(tests[i].input, &tests[i].hdr);
if (tests[i].has_mng)
check_mng(tests[i].input, &tests[i].mng);
if (tests[i].has_prohib)
check_prohib(tests[i].input, &tests[i].prohib);
}
/* check the SCCP unitdata */
{
struct sccp_con_ctrl_prt_mgt prt = {
.sst = 0x03,
.assn = 254,
.apoc = MTP_MAKE_APOC(91),
.mul_ind = 1,
};
u_int8_t data[] = { 0x03, 0xfe, 0x5b, 0x00, 0x01 };
if (memcmp(&prt, data, 5) != 0) {
u_int8_t *d = (u_int8_t *) &prt;
fprintf(stderr, "GOT: 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n",
d[0], d[1], d[2], d[3], d[4]);
abort();
}
}
/* verify decoding of the sls */
for (i = 0; i < 16; ++i) {
addr = MTP_ADDR(i, 136, 91);
if (MTP_LINK_SLS(addr) != i) {
fprintf(stderr, "0x%x/0x%x does not match 0x%x\n", addr, MTP_LINK_SLS(addr), i);
abort();
}
}
return 0;
}

View File

@ -0,0 +1,8 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include $(LAFORGE_CFLAGS) -Wall
noinst_PROGRAMS = patching_test
patching_test_SOURCES = patching_test.c $(top_srcdir)/src/bss_patch.c \
$(top_srcdir)/src/openbsc_nat/tlv_parser.c \
$(top_srcdir)/src/openbsc_nat/bssap.c
patching_test_LDADD = $(LAFORGE_LIBS) $(SCCP_LIBS)

View File

@ -0,0 +1,279 @@
#include <bss_patch.h>
#include <laf0rge1/debug.h>
#include <stdio.h>
#include <string.h>
u_int8_t pkt125[] = {
0x06, 0x01, 0x04,
0x00, 0x00, 0x01, 0x0b, 0x00, 0x09, 0x01, 0x0b,
0x03, 0x01, 0x0b, 0x25, 0x01, 0x00, 0x02 };
u_int8_t pkt125_res[] = {
0x06, 0x01, 0x04,
0x00, 0x00, 0x01, 0x0b, 0x00, 0x09, 0x01, 0x0b,
0x03, 0x01, 0x0a, 0x11, 0x01, 0x00, 0x02 };
u_int8_t pkt28[] = {
0x06, 0x01, 0x05,
0x2b, 0x00, 0x01, 0x09, 0x00, 0x07, 0x02, 0x21,
0x08, 0x2c, 0x02, 0x40, 0x11 };
u_int8_t pkt28_res[] = {
0x06, 0x01, 0x05,
0x2b, 0x00, 0x01, 0x09, 0x00, 0x07, 0x02, 0x21,
0x09, 0x2c, 0x02, 0x40, 0x25 };
u_int8_t reset_ack[] = {
0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x0a, 0x00, 0xfe, 0x04,
0x43, 0x5c, 0x00, 0xfe, 0x03, 0x00, 0x01, 0x31 };
u_int8_t cc[] = {
0x02, 0x01, 0x04,
0x00, 0x01, 0x01, 0xb4, 0x02, 0x01, 0x00 };
struct result {
u_int8_t *input;
int inp_len;
const u_int8_t *expected;
int exp_len;
int result;
};
static struct result results[] = {
{
.input = pkt125,
.inp_len = sizeof(pkt125),
.expected = pkt125_res,
.exp_len = sizeof(pkt125),
},
{
.input = pkt28,
.inp_len = sizeof(pkt28),
.expected = pkt28_res,
.exp_len = sizeof(pkt28_res),
},
{
.input = reset_ack,
.inp_len = sizeof(reset_ack),
.expected = reset_ack,
.exp_len = sizeof(reset_ack),
.result = BSS_FILTER_RESET_ACK,
},
{
.input = cc,
.inp_len = sizeof(cc),
.expected = cc,
.exp_len = sizeof(cc),
.result = 0,
},
};
static u_int8_t udt_with_poi[] = {
0x09, 0x00, 0x03,
0x07, 0x0b, 0x04, 0x43, 0x0a, 0x00, 0xfe, 0x04,
0x43, 0x5c, 0x00, 0xfe, 0x10, 0x00, 0x0e, 0x44,
0x04, 0x01, 0x00, 0x01, 0x00, 0x01, 0x1e, 0x05,
0x1e, 0x00, 0x00, 0x00, 0x40 };
static const u_int8_t udt_without_poi[] = {
0x09, 0x00, 0x03,
0x05, 0x07, 0x02, 0x42, 0xfe,
0x02, 0x42, 0xfe,
0x10, 0x00, 0x0e, 0x44,
0x04, 0x01, 0x00, 0x01, 0x00, 0x01, 0x1e, 0x05,
0x1e, 0x00, 0x00, 0x00, 0x40 };
static u_int8_t cr_with_poi [] = {
0x01, 0x01, 0x04,
0x00, 0x02, 0x02, 0x06, 0x04, 0xc3, 0x5c, 0x00,
0xfe, 0x0f, 0x21, 0x00, 0x1f, 0x57, 0x05, 0x08,
0x00, 0x72, 0xf4, 0x80, 0x23, 0x29, 0xc3, 0x50,
0x17, 0x10, 0x05, 0x24, 0x11, 0x03, 0x33, 0x19,
0x81, 0x08, 0x29, 0x47, 0x80, 0x00, 0x00, 0x00,
0x00, 0x80, 0x21, 0x01, 0x00 };
static const u_int8_t cr_without_poi [] = {
0x01, 0x01, 0x04,
0x00, 0x02, 0x02, 0x04, 0x02, 0x42, 0xfe,
0x0f, 0x21, 0x00, 0x1f, 0x57, 0x05, 0x08,
0x00, 0x72, 0xf4, 0x80, 0x23, 0x29, 0xc3, 0x50,
0x17, 0x10, 0x05, 0x24, 0x11, 0x03, 0x33, 0x19,
0x81, 0x08, 0x29, 0x47, 0x80, 0x00, 0x00, 0x00,
0x00, 0x80, 0x21, 0x01, 0x00 };
static u_int8_t cr2_without_poi[] = {
0x01, 0x00, 0x00, 0x03, 0x02, 0x02, 0x04, 0x02,
0x42, 0xfe, 0x0f, 0x1f, 0x00, 0x1d, 0x57, 0x05,
0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x1d, 0xc3,
0x50, 0x17, 0x10, 0x05, 0x24, 0x31, 0x03, 0x50,
0x18, 0x93, 0x08, 0x29, 0x47, 0x80, 0x00, 0x00,
0x00, 0x00, 0x80, 0x00};
static struct result rewrite_results_to_msc[] = {
{
.input = udt_with_poi,
.inp_len = sizeof(udt_with_poi),
.expected = udt_without_poi,
.exp_len = sizeof(udt_without_poi),
},
{
.input = cr_with_poi,
.inp_len = sizeof(cr_with_poi),
.expected = cr_without_poi,
.exp_len = sizeof(cr_without_poi),
},
{
.input = cr2_without_poi,
.inp_len = sizeof(cr2_without_poi),
.expected = cr2_without_poi,
.exp_len = sizeof(cr2_without_poi),
},
};
u_int8_t paging_cmd[] = {
0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x0a,
0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x10,
0x00, 0x0e, 0x52, 0x08, 0x08, 0x29, 0x80, 0x10,
0x76, 0x10, 0x77, 0x46, 0x05, 0x1a, 0x01, 0x06 };
u_int8_t paging_cmd_patched[] = {
0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x02,
0x00, 0xfe, 0x04, 0x43, 0x01, 0x00, 0xfe, 0x10,
0x00, 0x0e, 0x52, 0x08, 0x08, 0x29, 0x80, 0x10,
0x76, 0x10, 0x77, 0x46, 0x05, 0x1a, 0x01, 0x06 };
static struct result rewrite_results_to_bsc[] = {
{
.input = paging_cmd,
.inp_len = sizeof(paging_cmd),
.expected = paging_cmd_patched,
.exp_len = sizeof(paging_cmd_patched),
},
};
static void test_patch_filter(void)
{
int i;
printf("Testing patching of GSM messages to the MSC.\n");
for (i = 0; i < sizeof(results)/sizeof(results[0]); ++i) {
struct sccp_parse_result result;
struct msgb *msg;
int rc;
msg = msgb_alloc_headroom(256, 8, "test");
msgb_put(msg, 1);
msg->l2h = msgb_put(msg, results[i].inp_len);
memcpy(msg->l2h, results[i].input, msgb_l2len(msg));
rc = bss_patch_filter_msg(msg, &result);
if (memcmp(msg->l2h, results[i].expected, results[i].exp_len) != 0) {
printf("Failed to patch the packet.\n");
abort();
}
if (rc != results[i].result) {
printf("Didn't get the result we wanted on %d\n", i),
abort();
}
msgb_free(msg);
}
}
static void test_rewrite_msc(void)
{
int i;
printf("Testing rewriting the SCCP header.\n");
for (i = 0; i < sizeof(rewrite_results_to_msc)/sizeof(rewrite_results_to_msc[0]); ++i) {
struct sccp_parse_result result;
struct msgb *inp;
struct msgb *outp;
int rc;
outp = msgb_alloc_headroom(256, 8, "test2");
inp = msgb_alloc_headroom(256, 8, "test1");
msgb_put(inp, 1);
inp->l2h = msgb_put(inp, rewrite_results_to_msc[i].inp_len);
memcpy(inp->l2h, rewrite_results_to_msc[i].input, msgb_l2len(inp));
rc = bss_patch_filter_msg(inp, &result);
if (rc < 0) {
printf("Failed to parse header msg: %d\n", i);
abort();
}
bss_rewrite_header_for_msc(rc, outp, inp, &result);
memset(&result, 0, sizeof(result));
rc = bss_patch_filter_msg(outp, &result);
if (rc < 0) {
printf("Patched message doesn't work: %d\n", i);
printf("hex: %s\n", hexdump(outp->l2h, msgb_l2len(outp)));
abort();
}
if (msgb_l2len(outp) != rewrite_results_to_msc[i].exp_len) {
printf("The length's don#t match on %d %u != %u\n",
i, msgb_l2len(outp), rewrite_results_to_msc[i].exp_len);
printf("hex: %s\n", hexdump(outp->l2h, msgb_l2len(outp)));
abort();
}
if (memcmp(outp->l2h, rewrite_results_to_msc[i].expected, rewrite_results_to_msc[i].exp_len) != 0) {
printf("Expected results don't match for: %d\n", i);
printf("hex: %s\n", hexdump(outp->l2h, msgb_l2len(outp)));
abort();
}
msgb_free(outp);
msgb_free(inp);
}
}
static void test_rewrite_bsc(void)
{
int i;
printf("Testing rewriting the SCCP header for BSC.\n");
for (i = 0; i < sizeof(rewrite_results_to_bsc)/sizeof(rewrite_results_to_bsc[0]); ++i) {
struct msgb *inp;
inp = msgb_alloc_headroom(256, 8, "test1");
msgb_put(inp, 1);
inp->l2h = msgb_put(inp, rewrite_results_to_bsc[i].inp_len);
memcpy(inp->l2h, rewrite_results_to_bsc[i].input, msgb_l2len(inp));
if (bss_rewrite_header_to_bsc(inp, 1, 2) != 0) {
fprintf(stderr, "Failed to rewrite header\n");
abort();
}
if (memcmp(inp->l2h, rewrite_results_to_bsc[i].expected, msgb_l2len(inp))!= 0) {
fprintf(stderr, "Content does not match\n");
printf("got: %s\n", hexdump(inp->l2h, msgb_l2len(inp)));
abort();
}
msgb_free(inp);
}
}
int main(int argc, char **argv)
{
test_patch_filter();
test_rewrite_msc();
test_rewrite_bsc();
printf("DONE!\n");
return 0;
}