Merge branch 'on-waves/mgcp'

This commit is contained in:
Holger Hans Peter Freyther 2010-04-07 22:54:07 +02:00
commit 91b5a31a2c
7 changed files with 166 additions and 94 deletions

View File

@ -10,7 +10,7 @@ rsip_resp = """200 321321332\r\n"""
audit_packet = """AUEP %d 13@mgw MGCP 1.0\r\n"""
crcx_packet = """CRCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n"""
dlcx_packet = """DLCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\n"""
mdcx_packet = """MDCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 258696477 0 IN IP4 172.16.1.107\r\ns=-\r\nc=IN IP4 172.16.1.107\r\nt=0 0\r\nm=audio 4400 RTP/AVP 127\r\na=rtpmap:127 GSM-EFR/8000/1\r\na=ptime:20\r\na=recvonly\r\nm=image 4402 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n"""
mdcx_packet = """MDCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 258696477 0 IN IP4 172.16.1.107\r\ns=-\r\nc=IN IP4 172.16.1.107\r\nt=0 0\r\nm=audio 6666 RTP/AVP 127\r\na=rtpmap:127 GSM-EFR/8000/1\r\na=ptime:20\r\na=recvonly\r\nm=image 4402 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n"""
def hexdump(src, length=8):
"""Recipe is from http://code.activestate.com/recipes/142812/"""
@ -25,15 +25,24 @@ def hexdump(src, length=8):
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(("127.0.0.1", MGCP_CALLAGENT_PORT))
server_socket.setblocking(0)
server_socket.setblocking(1)
def send_receive(packet):
last_ci = 1
def send_and_receive(packet):
global last_ci
server_socket.sendto(packet, ("127.0.0.1", MGCP_GATEWAY_PORT))
try:
data, addr = server_socket.recvfrom(4096)
# attempt to store the CI of the response
list = data.split("\n")
for item in list:
if item.startswith("I: "):
last_ci = int(item[3:])
print hexdump(data), addr
except socket.error:
except socket.error, e:
print e
pass
def generate_tid():
@ -42,13 +51,10 @@ def generate_tid():
i = 1
while True:
send_receive(rsip_resp)
send_receive(audit_packet)
send_receive(crcx_packet % generate_tid() )
send_receive(mdcx_packet % (generate_tid(), i))
send_receive(dlcx_packet % (generate_tid(), i))
i = i + 1
send_and_receive(audit_packet % generate_tid())
send_and_receive(crcx_packet % generate_tid() )
send_and_receive(mdcx_packet % (generate_tid(), last_ci))
send_and_receive(dlcx_packet % (generate_tid(), last_ci))
time.sleep(3)

View File

@ -77,6 +77,7 @@ struct mgcp_config;
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 {
int source_port;
@ -84,6 +85,7 @@ struct mgcp_config {
char *source_addr;
unsigned int number_endpoints;
char *bts_ip;
char *call_agent_addr;
struct in_addr bts_in;
char *audio_name;
@ -95,8 +97,12 @@ struct mgcp_config {
char *forward_ip;
int forward_port;
/* spec handling */
int force_realloc;
mgcp_change change_cb;
mgcp_policy policy_cb;
mgcp_reset reset_cb;
void *data;
struct mgcp_endpoint *endpoints;
@ -115,8 +121,15 @@ 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_rsip(void);
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

@ -57,8 +57,21 @@ struct mgcp_endpoint {
/* 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);
#endif

View File

@ -38,8 +38,11 @@
#include <openbsc/gsm_data.h>
#include <osmocore/select.h>
#include <openbsc/mgcp.h>
#include <openbsc/mgcp_internal.h>
#include <openbsc/telnet_interface.h>
#include <vty/command.h>
#include "../../bscconfig.h"
/* this is here for the vty... it will never be called */
@ -51,8 +54,9 @@ void subscr_put() { abort(); }
#warning "Make use of the rtp proxy code"
static struct bsc_fd bfd;
static int first_request = 1;
static struct mgcp_config *cfg;
static int reset_endpoints = 0;
const char *openbsc_version = "OpenBSC MGCP " PACKAGE_VERSION;
const char *openbsc_copyright =
"Copyright (C) 2009-2010 Holger Freyther and On-Waves\n"
@ -74,10 +78,10 @@ static void print_help()
printf(" -c --config-file filename The config file to use.\n");
}
static void print_version()
static void print_mgcp_version()
{
printf("%s\n\n", openbsc_version);
printf(openbsc_copyright);
printf("%s", openbsc_copyright);
}
static void handle_options(int argc, char** argv)
@ -105,7 +109,7 @@ static void handle_options(int argc, char** argv)
config_file = talloc_strdup(tall_bsc_ctx, optarg);
break;
case 'V':
print_version();
print_mgcp_version();
exit(0);
break;
default:
@ -115,12 +119,21 @@ static void handle_options(int argc, char** argv)
}
}
/* simply remember this */
static int mgcp_rsip_cb(struct mgcp_config *cfg)
{
reset_endpoints = 1;
return 0;
}
static int read_call_agent(struct bsc_fd *fd, unsigned int what)
{
struct sockaddr_in addr;
socklen_t slen = sizeof(addr);
struct msgb *msg;
struct msgb *resp;
int i;
msg = (struct msgb *) fd->data;
@ -136,18 +149,6 @@ static int read_call_agent(struct bsc_fd *fd, unsigned int what)
return -1;
}
if (first_request) {
first_request = 0;
resp = mgcp_create_rsip();
if (resp) {
sendto(bfd.fd, resp->l2h, msgb_l2len(resp), 0,
(struct sockaddr *) &addr, sizeof(addr));
msgb_free(resp);
}
return 0;
}
/* handle message now */
msg->l2h = msgb_put(msg, rc);
resp = mgcp_handle_message(cfg, msg);
@ -157,6 +158,16 @@ static int read_call_agent(struct bsc_fd *fd, unsigned int what)
sendto(bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
msgb_free(resp);
}
if (reset_endpoints) {
LOGP(DMGCP, LOGL_NOTICE, "Asked to reset endpoints.\n");
reset_endpoints = 0;
/* is checking in_addr.s_addr == INADDR_LOOPBACK making it more secure? */
for (i = 1; i < cfg->number_endpoints; ++i)
mgcp_free_endp(&cfg->endpoints[i]);
}
return 0;
}
@ -186,6 +197,8 @@ int main(int argc, char** argv)
if (rc < 0)
return rc;
/* set some callbacks */
cfg->reset_cb = mgcp_rsip_cb;
/* we need to bind a socket */
if (rc == 0) {
@ -217,11 +230,11 @@ int main(int argc, char** argv)
if (bsc_register_fd(&bfd) != 0) {
DEBUGP(DMGCP, "Failed to register the fd\n");
LOGP(DMGCP, LOGL_FATAL, "Failed to register the fd\n");
return -1;
}
DEBUGP(DMGCP, "Configured for MGCP.\n");
LOGP(DMGCP, LOGL_NOTICE, "Configured for MGCP.\n");
}
/* initialisation */
@ -235,3 +248,14 @@ int main(int argc, char** argv)
return 0;
}
struct gsm_network;
int bsc_vty_init(struct gsm_network *dummy)
{
cmd_init(1);
vty_init();
mgcp_vty_init();
return 0;
}

View File

@ -25,6 +25,7 @@
#include <string.h>
#include <unistd.h>
#include <endian.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
@ -90,6 +91,9 @@ static void patch_payload(int payload, char *data, int len)
if (len < sizeof(*rtp_hdr))
return;
if (payload < 0)
return;
rtp_hdr = (struct rtp_hdr *) data;
rtp_hdr->payload_type = payload;
}
@ -119,16 +123,14 @@ static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
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\n",
ENDPOINT_NUMBER(endp));
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_ERROR, "Unknown message on endpoint: 0x%x\n", ENDPOINT_NUMBER(endp));
if (endp->ci == CI_UNUSED)
return -1;
}
/*
* Figure out where to forward it to. This code assumes that we
@ -146,7 +148,9 @@ static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
/* 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) {
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 {
@ -159,6 +163,12 @@ static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
}
}
/* do this before the loop handling */
if (dest == DEST_NETWORK)
++endp->in_bts;
else
++endp->in_remote;
/* dispatch */
if (cfg->audio_loop)
dest = !dest;

View File

@ -80,11 +80,6 @@ enum mgcp_connection_mode {
}
struct mgcp_msg_ptr {
unsigned int start;
unsigned int length;
};
struct mgcp_request {
char *name;
struct msgb *(*handle_request) (struct mgcp_config *cfg, struct msgb *msg);
@ -98,6 +93,7 @@ static struct msgb *handle_audit_endpoint(struct mgcp_config *cfg, struct msgb *
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)
{
@ -119,12 +115,6 @@ static int generate_call_id(struct mgcp_config *cfg)
return cfg->last_call_id;
}
/* FIXIME/TODO: need to have a list of pending transactions and check that */
static unsigned int generate_transaction_id()
{
return abs(rand());
}
/*
* array of function pointers for handling various
* messages. In the future this might be binary sorted
@ -135,6 +125,9 @@ static const struct mgcp_request mgcp_requests [] = {
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)
@ -194,23 +187,6 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
return mgcp_create_response_with_data(200, msg, trans_id, sdp_record);
}
/* send a static record */
struct msgb *mgcp_create_rsip(void)
{
struct msgb *msg;
int len;
msg = mgcp_msgb_alloc();
if (!msg)
return NULL;
len = snprintf((char *) msg->data, 2048,
"RSIP %u *@mgw MGCP 1.0\n"
"RM: restart\n", generate_transaction_id());
msg->l2h = msgb_put(msg, len);
return msg;
}
/*
* handle incoming messages:
* - this can be a command (four letters, space, transaction id)
@ -221,25 +197,25 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
int code;
struct msgb *resp = NULL;
if (msg->len < 4) {
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->data[0], "%3d %*s", &code) == 1) {
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->data[0], 4) == 0) {
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->data[0]);
LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
}
}
@ -296,9 +272,9 @@ static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, const char *
return &cfg->endpoints[gw];
}
static int 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_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;
@ -334,8 +310,11 @@ static int analyze_header(struct mgcp_config *cfg, struct msgb *msg,
}
*transaction_id = (const char *)&msg->l3h[ptr[0].start];
*endp = find_endpoint(cfg, (const char *)&msg->l3h[ptr[1].start]);
return *endp == NULL;
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,
@ -369,7 +348,7 @@ static struct msgb *handle_audit_endpoint(struct mgcp_config *cfg, struct msgb *
const char *trans_id;
struct mgcp_endpoint *endp;
found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
if (found != 0)
response = 500;
else
@ -402,13 +381,19 @@ static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
int error_code = 500;
int port;
found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
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) {
LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n", ENDPOINT_NUMBER(endp));
return create_response(500, "CRCX", trans_id);
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: */
@ -501,7 +486,7 @@ static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg)
struct mgcp_endpoint *endp;
int error_code = 500;
found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
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);
@ -614,7 +599,7 @@ static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg)
struct mgcp_endpoint *endp;
int error_code = 500;
found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
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);
@ -678,6 +663,13 @@ error3:
return create_response(error_code, "DLCX", trans_id);
}
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;
@ -722,7 +714,7 @@ int mgcp_endpoints_allocate(struct mgcp_config *cfg)
void mgcp_free_endp(struct mgcp_endpoint *endp)
{
LOGP(DMGCP, LOGL_NOTICE, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
LOGP(DMGCP, LOGL_DEBUG, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
endp->ci= CI_UNUSED;
if (endp->callid) {
@ -732,7 +724,7 @@ void mgcp_free_endp(struct mgcp_endpoint *endp)
if (endp->local_options) {
talloc_free(endp->local_options);
endp->callid = NULL;
endp->local_options = NULL;
}
if (!endp->cfg->early_bind) {
@ -742,4 +734,7 @@ void mgcp_free_endp(struct mgcp_endpoint *endp)
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));
}

View File

@ -63,6 +63,8 @@ static int config_write_mgcp(struct vty *vty)
vty_out(vty, " forward audio ip %s%s", g_cfg->forward_ip, VTY_NEWLINE);
if (g_cfg->forward_port != 0)
vty_out(vty, " forward audio port %d%s", g_cfg->forward_port, VTY_NEWLINE);
if (g_cfg->call_agent_addr)
vty_out(vty, " call agent ip %s%s", g_cfg->call_agent_addr, VTY_NEWLINE);
return CMD_SUCCESS;
}
@ -75,10 +77,12 @@ DEFUN(show_mcgp, show_mgcp_cmd, "show mgcp",
vty_out(vty, "MGCP is up and running with %u endpoints:%s", g_cfg->number_endpoints - 1, VTY_NEWLINE);
for (i = 1; i < g_cfg->number_endpoints; ++i) {
struct mgcp_endpoint *endp = &g_cfg->endpoints[i];
vty_out(vty, " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u%s",
vty_out(vty, " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u on %s traffic in :%u/%u%s",
i, endp->ci,
ntohs(endp->net_rtp), ntohs(endp->net_rtcp),
ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp), VTY_NEWLINE);
ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp),
inet_ntoa(endp->bts), endp->in_bts, endp->in_remote,
VTY_NEWLINE);
}
return CMD_SUCCESS;
@ -237,6 +241,17 @@ DEFUN(cfg_mgcp_forward_port,
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_agent_addr,
cfg_mgcp_agent_addr_cmd,
"call agent ip IP",
"Set the address of the call agent.")
{
if (g_cfg->call_agent_addr)
talloc_free(g_cfg->call_agent_addr);
g_cfg->call_agent_addr = talloc_strdup(g_cfg, argv[0]);
return CMD_SUCCESS;
}
int mgcp_vty_init(void)
{
install_element(VIEW_NODE, &show_mgcp_cmd);
@ -256,6 +271,7 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd);
install_element(MGCP_NODE, &cfg_mgcp_forward_ip_cmd);
install_element(MGCP_NODE, &cfg_mgcp_forward_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_agent_addr_cmd);
return 0;
}
@ -274,6 +290,11 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg)
if (!g_cfg->bts_ip)
fprintf(stderr, "No BTS ip address specified. This will allow everyone to connect.\n");
if (!g_cfg->source_addr) {
fprintf(stderr, "You need to specify a bind address.\n");
return -1;
}
if (mgcp_endpoints_allocate(g_cfg) != 0) {
fprintf(stderr, "Failed to allocate endpoints: %d. Quitting.\n", g_cfg->number_endpoints);
return -1;
@ -327,13 +348,3 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg)
return !!g_cfg->forward_ip;
}
struct gsm_network;
int bsc_vty_init(struct gsm_network *dummy)
{
cmd_init(1);
vty_init();
mgcp_vty_init();
return 0;
}