9
0
Fork 0

mgcp: Merge OpenBSC MGCP code and update the structure to match it

* Upstream has a separation of BTS and NET side for RTP ports and
  can allocate them dynamically.
* Upstream has gained the concept of trunks. We will now have various
  trunks to connect audio things.

* We will now be able to utilize multiple trunks and have the endpoints
  used properly.
This commit is contained in:
Holger Hans Peter Freyther 2011-03-01 01:24:10 +01:00
parent f3da99ed65
commit dcc3312792
10 changed files with 2048 additions and 707 deletions

View File

@ -1,13 +1,13 @@
/* 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
* (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2011 by On-Waves
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@ -23,11 +23,15 @@
#ifndef OPENBSC_MGCP_H
#define OPENBSC_MGCP_H
#include "cellmgr_debug.h"
#include <osmocore/msgb.h>
#include <osmocore/write_queue.h>
#include <arpa/inet.h>
#define RTP_PORT_DEFAULT 4000
#define RTP_PORT_NET_DEFAULT 16000
/**
* Calculate the RTP audio port for the given multiplex
@ -59,6 +63,7 @@ static inline int rtp_calculate_port(int multiplex, int base)
*/
struct mgcp_endpoint;
struct mgcp_config;
struct mgcp_trunk_config;
#define MGCP_ENDP_CRCX 1
#define MGCP_ENDP_DLCX 2
@ -74,51 +79,113 @@ struct mgcp_config;
#define MGCP_POLICY_REJECT 5
#define MGCP_POLICY_DEFER 6
typedef int (*mgcp_realloc)(struct mgcp_config *cfg, int endpoint);
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_realloc)(struct mgcp_trunk_config *cfg, int endpoint);
typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state);
typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id);
typedef int (*mgcp_reset)(struct mgcp_config *cfg);
#define PORT_ALLOC_STATIC 0
#define PORT_ALLOC_DYNAMIC 1
/**
* This holds information on how to allocate ports
*/
struct mgcp_port_range {
int mode;
/* pre-allocated from a base? */
int base_port;
/* dynamically allocated */
int range_start;
int range_end;
int last_port;
};
struct mgcp_trunk_config {
struct llist_head entry;
struct mgcp_config *cfg;
int trunk_nr;
int trunk_type;
char *audio_name;
int audio_payload;
int audio_loop;
/* spec handling */
int force_realloc;
unsigned int number_endpoints;
struct mgcp_endpoint *endpoints;
/* Special MGW handling */
int target_trunk_start;
int trunk_base;
int endp_offset;
int vad_enabled;
int digital_inp_gain;
int digital_out_gain;
int upstr_agc_enbl;
int upstr_adp_rate;
int upstr_max_gain;
int upstr_target_lvl;
int dwnstr_agc_enbl;
int dwnstr_adp_rate;
int dwnstr_max_gain;
int dwnstr_target_lvl;
};
struct mgcp_config {
int source_port;
char *local_ip;
char *source_addr;
unsigned int number_endpoints;
char *bts_ip;
char *call_agent_addr;
struct in_addr bts_in;
char *audio_name;
int audio_payload;
int audio_loop;
int early_bind;
int rtp_base_port;
/* transcoder handling */
char *transcoder_ip;
struct in_addr transcoder_in;
int transcoder_remote_base;
struct write_queue gw_fd;
struct mgcp_port_range bts_ports;
struct mgcp_port_range net_ports;
struct mgcp_port_range transcoder_ports;
int endp_dscp;
char *forward_ip;
int forward_port;
/* spec handling */
int force_realloc;
mgcp_change change_cb;
mgcp_policy policy_cb;
mgcp_reset reset_cb;
mgcp_realloc realloc_cb;
void *data;
struct mgcp_endpoint *endpoints;
uint32_t last_call_id;
/* trunk handling */
struct mgcp_trunk_config trunk;
struct llist_head trunks;
/* only used for start with a static configuration */
int last_net_port;
int last_bts_port;
/* MGW handling */
int configure_trunks;
};
/* 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);
void mgcp_vty_set_config(struct mgcp_config *cfg);
int mgcp_endpoints_allocate(struct mgcp_config *cfg);
int mgcp_bind_rtp_port(struct mgcp_endpoint *endp, int rtp_port);
int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg);
void mgcp_free_endp(struct mgcp_endpoint *endp);
int mgcp_reset_transcoder(struct mgcp_config *cfg);
/*
* format helper functions
@ -129,9 +196,18 @@ struct msgb *mgcp_create_response_with_data(int code, const char *txt, const cha
/* adc helper */
static inline int mgcp_timeslot_to_endpoint(int multiplex, int timeslot)
{
if (timeslot == 0)
timeslot = 1;
return timeslot + (31 * multiplex);
if (timeslot == 0) {
LOGP(DMGCP, LOGL_ERROR, "Timeslot should not be 0\n");
timeslot = 255;
}
return timeslot + (32 * multiplex);
}
static inline void mgcp_endpoint_to_timeslot(int endpoint, int *multiplex, int *timeslot)
{
*multiplex = endpoint / 32;
*timeslot = endpoint % 32;
}

View File

@ -1,13 +1,13 @@
/* MGCP Private Data */
/*
* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2010 by On-Waves
* (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2011 by On-Waves
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@ -35,6 +35,11 @@ enum mgcp_connection_mode {
MGCP_CONN_LOOPBACK = 4,
};
enum mgcp_trunk_type {
MGCP_TRUNK_VIRTUAL,
MGCP_TRUNK_E1,
};
struct mgcp_rtp_state {
int initialized;
int patch;
@ -48,47 +53,82 @@ struct mgcp_rtp_state {
int32_t timestamp_offset;
};
struct mgcp_rtp_end {
/* statistics */
unsigned int packets;
struct in_addr addr;
/* in network byte order */
int rtp_port, rtcp_port;
int payload_type;
/*
* Each end has a socket...
*/
struct bsc_fd rtp;
struct bsc_fd rtcp;
int local_port;
int local_alloc;
};
enum {
MGCP_TAP_BTS_IN,
MGCP_TAP_BTS_OUT,
MGCP_TAP_NET_IN,
MGCP_TAP_NET_OUT,
/* last element */
MGCP_TAP_COUNT
};
struct mgcp_rtp_tap {
int enabled;
struct sockaddr_in forward;
};
struct mgcp_endpoint {
int allocated;
uint32_t ci;
char *callid;
char *local_options;
int conn_mode;
int orig_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;
struct mgcp_trunk_config *tcfg;
/* statistics */
unsigned int in_bts;
unsigned int in_remote;
/* port status for bts/net */
struct mgcp_rtp_end bts_end;
struct mgcp_rtp_end net_end;
/*
* For transcoding we will send from the local_port
* of trans_bts and it will arrive at trans_net from
* where we will forward it to the network.
*/
struct mgcp_rtp_end trans_bts;
struct mgcp_rtp_end trans_net;
int is_transcoded;
/* sequence bits */
struct mgcp_rtp_state net_state;
struct mgcp_rtp_state bts_state;
/* SSRC/seq/ts patching for loop */
int allow_patch;
/* tap for the endpoint */
struct mgcp_rtp_tap taps[MGCP_TAP_COUNT];
/* Special MGW handling */
unsigned int audio_port;
int block_processing;
};
#define ENDPOINT_NUMBER(endp) abs(endp - endp->cfg->endpoints)
#define ENDPOINT_NUMBER(endp) abs(endp - endp->tcfg->endpoints)
struct mgcp_msg_ptr {
unsigned int start;
@ -99,5 +139,20 @@ 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);
int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port);
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port);
int mgcp_bind_trans_bts_rtp_port(struct mgcp_endpoint *enp, int rtp_port);
int mgcp_bind_trans_net_rtp_port(struct mgcp_endpoint *enp, int rtp_port);
int mgcp_free_rtp_port(struct mgcp_rtp_end *end);
/* For transcoding we need to manage an in and an output that are connected */
static inline int endp_back_channel(int endpoint)
{
return endpoint + 60;
}
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
#endif

View File

@ -1,7 +1,7 @@
/* mgcp_ss7 helper coder */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* (C) 2010-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010-2011 by On-Waves
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
@ -29,19 +29,11 @@
#include "thread.h"
enum cellmgr_node {
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
};
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;
@ -54,19 +46,17 @@ enum {
MGCP_SS7_MUTE_STATUS,
MGCP_SS7_ALLOCATE,
MGCP_SS7_DELETE,
MGCP_SS7_SHUTDOWN,
};
struct mgcp_ss7_cmd {
struct llist_head entry;
uint8_t type;
uint32_t port;
struct mgcp_endpoint *endp;
uint32_t param;
};
void mgcp_ss7_exec(struct mgcp_ss7 *mgcp, uint8_t type, uint32_t port, uint32_t param);
void mgcp_ss7_reset(struct mgcp_ss7 *mgcp);
void mgcp_ss7_free(struct mgcp_ss7 *mgcp);
void mgcp_mgw_vty_init();
#endif

View File

@ -27,6 +27,7 @@
enum ss7_vty_node {
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
TRUNK_NODE,
CELLMGR_NODE,
SS7_NODE,
LINKSETS_NODE,

View File

@ -4,7 +4,7 @@ AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOVTY_CFLAGS)
sbin_PROGRAMS = cellmgr_ng osmo_stp mgcp_mgw
mgcp_mgw_SOURCES = mgcp_ss7.c thread.c debug.c \
mgcp_mgw_SOURCES = mgcp_ss7.c mgcp_ss7_vty.c thread.c debug.c \
mgcp/mgcp_protocol.c mgcp/mgcp_network.c mgcp/mgcp_vty.c
mgcp_mgw_LDADD = $(LAFORGE_LIBS) $(NEXUSWARE_C7_LIBS) $(NEXUSWARE_UNIPORTE_LIBS) \
$(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) -lpthread

View File

@ -2,13 +2,13 @@
/* The protocol implementation */
/*
* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2010 by On-Waves
* (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2011 by On-Waves
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@ -24,23 +24,28 @@
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <endian.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <osmocore/msgb.h>
#include <osmocore/select.h>
#include <mgcp/mgcp.h>
#include <mgcp/mgcp_internal.h>
#include <cellmgr_debug.h>
#include <osmocore/msgb.h>
#include <osmocore/talloc.h>
#include <osmocore/select.h>
#warning "Make use of the rtp proxy code"
/* attempt to determine byte order */
#include <sys/types.h>
#include <sys/param.h>
#include <limits.h>
#ifndef __BYTE_ORDER
#error "__BYTE_ORDER should be defined by someone"
#endif
/* according to rtp_proxy.c RFC 3550 */
struct rtp_hdr {
#if __BYTE_ORDER == __LITTLE_ENDIAN
@ -91,11 +96,12 @@ 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);
return udp_send(endp->net_end.rtp.fd, &endp->net_end.addr,
endp->net_end.rtp_port, buf, 1);
}
static void patch_and_count(struct mgcp_rtp_state *state, int payload, char *data, int len)
static void patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
int payload, struct sockaddr_in *addr, char *data, int len)
{
uint16_t seq;
uint32_t timestamp;
@ -117,9 +123,11 @@ static void patch_and_count(struct mgcp_rtp_state *state, int payload, char *dat
state->ssrc = rtp_hdr->ssrc;
state->seq_offset = (state->seq_no + 1) - seq;
state->timestamp_offset = state->last_timestamp - timestamp;
state->patch = 1;
LOGP(DMGCP, LOGL_NOTICE, "The SSRC changed... SSRC: %u offset: %d\n",
state->ssrc, state->seq_offset);
state->patch = endp->allow_patch;
LOGP(DMGCP, LOGL_NOTICE,
"The SSRC changed on 0x%x SSRC: %u offset: %d from %s:%d in %d\n",
ENDPOINT_NUMBER(endp), state->ssrc, state->seq_offset,
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode);
}
/* apply the offset and store it back to the packet */
@ -146,87 +154,50 @@ static void patch_and_count(struct mgcp_rtp_state *state, int payload, char *dat
}
/*
* 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.
* The below code is for dispatching. We have a dedicated port for
* the data coming from the net and one to discover the BTS.
*/
static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
static int forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf, int len)
{
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)
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));
if (!tap->enabled)
return 0;
}
/* do this before the loop handling */
if (dest == DEST_NETWORK)
++endp->in_bts;
else
++endp->in_remote;
return sendto(fd, buf, len, 0,
(struct sockaddr *)&tap->forward, sizeof(tap->forward));
}
static int send_transcoder(struct mgcp_rtp_end *end, struct mgcp_config *cfg,
int is_rtp, const char *buf, int len)
{
int rc;
int port;
struct sockaddr_in addr;
port = is_rtp ? end->rtp_port : end->rtcp_port;
addr.sin_family = AF_INET;
addr.sin_addr = cfg->transcoder_in;
addr.sin_port = port;
rc = sendto(is_rtp ?
end->rtp.fd :
end->rtcp.fd, buf, len, 0,
(struct sockaddr *) &addr, sizeof(addr));
if (rc != len)
LOGP(DMGCP, LOGL_ERROR,
"Failed to send data to the transcoder: %s\n",
strerror(errno));
return rc;
}
static int send_to(struct mgcp_endpoint *endp, int dest, int is_rtp,
struct sockaddr_in *addr, char *buf, int rc)
{
struct mgcp_trunk_config *tcfg = endp->tcfg;
/* For loop toggle the destination and then dispatch. */
if (cfg->audio_loop)
if (tcfg->audio_loop)
dest = !dest;
/* Loop based on the conn_mode, maybe undoing the above */
@ -234,22 +205,239 @@ static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
dest = !dest;
if (dest == DEST_NETWORK) {
if (proto == PROTO_RTP)
patch_and_count(&endp->bts_state,
endp->net_payload_type, buf, rc);
return udp_send(fd->fd, &endp->remote,
proto == PROTO_RTP ? endp->net_rtp : endp->net_rtcp,
buf, rc);
if (is_rtp) {
patch_and_count(endp, &endp->bts_state,
endp->net_end.payload_type,
addr, buf, rc);
forward_data(endp->net_end.rtp.fd,
&endp->taps[MGCP_TAP_NET_OUT], buf, rc);
return udp_send(endp->net_end.rtp.fd, &endp->net_end.addr,
endp->net_end.rtp_port, buf, rc);
} else {
return udp_send(endp->net_end.rtcp.fd, &endp->net_end.addr,
endp->net_end.rtcp_port, buf, rc);
}
} else {
if (proto == PROTO_RTP)
patch_and_count(&endp->net_state,
endp->bts_payload_type, buf, rc);
return udp_send(fd->fd, &endp->bts,
proto == PROTO_RTP ? endp->bts_rtp : endp->bts_rtcp,
buf, rc);
if (is_rtp) {
patch_and_count(endp, &endp->net_state,
endp->bts_end.payload_type,
addr, buf, rc);
forward_data(endp->bts_end.rtp.fd,
&endp->taps[MGCP_TAP_BTS_OUT], buf, rc);
return udp_send(endp->bts_end.rtp.fd, &endp->bts_end.addr,
endp->bts_end.rtp_port, buf, rc);
} else {
return udp_send(endp->bts_end.rtcp.fd, &endp->bts_end.addr,
endp->bts_end.rtcp_port, buf, rc);
}
}
}
static int recevice_from(struct mgcp_endpoint *endp, int fd, struct sockaddr_in *addr,
char *buf, int bufsize)
{
int rc;
socklen_t slen = sizeof(*addr);
rc = recvfrom(fd, buf, bufsize, 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->allocated)
return -1;
#warning "Slight spec violation. With connection mode recvonly we should attempt to forward."
return rc;
}
static int rtp_data_net(struct bsc_fd *fd, unsigned int what)
{
char buf[4096];
struct sockaddr_in addr;
struct mgcp_endpoint *endp;
int rc, proto;
endp = (struct mgcp_endpoint *) fd->data;
rc = recevice_from(endp, fd->fd, &addr, buf, sizeof(buf));
if (rc <= 0)
return -1;
if (memcmp(&addr.sin_addr, &endp->net_end.addr, sizeof(addr.sin_addr)) != 0) {
LOGP(DMGCP, LOGL_ERROR,
"Data from wrong address %s on 0x%x\n",
inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(endp));
return -1;
}
if (endp->net_end.rtp_port != addr.sin_port &&
endp->net_end.rtcp_port != addr.sin_port) {
LOGP(DMGCP, LOGL_ERROR,
"Data from wrong source port %d on 0x%x\n",
ntohs(addr.sin_port), ENDPOINT_NUMBER(endp));
return -1;
}
/* throw away the dummy message */
if (rc == 1 && buf[0] == DUMMY_LOAD) {
LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from network on 0x%x\n",
ENDPOINT_NUMBER(endp));
return 0;
}
proto = fd == &endp->net_end.rtp ? PROTO_RTP : PROTO_RTCP;
endp->net_end.packets += 1;
forward_data(fd->fd, &endp->taps[MGCP_TAP_NET_IN], buf, rc);
if (endp->is_transcoded)
return send_transcoder(&endp->trans_net, endp->cfg, proto == PROTO_RTP, &buf[0], rc);
else
return send_to(endp, DEST_BTS, proto == PROTO_RTP, &addr, &buf[0], rc);
}
static void discover_bts(struct mgcp_endpoint *endp, int proto, struct sockaddr_in *addr)
{
struct mgcp_config *cfg = endp->cfg;
if (proto == PROTO_RTP && endp->bts_end.rtp_port == 0) {
if (!cfg->bts_ip ||
memcmp(&addr->sin_addr,
&cfg->bts_in, sizeof(cfg->bts_in)) == 0 ||
memcmp(&addr->sin_addr,
&endp->bts_end.addr, sizeof(endp->bts_end.addr)) == 0) {
endp->bts_end.rtp_port = addr->sin_port;
endp->bts_end.addr = 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_end.rtp_port),
ntohs(endp->bts_end.rtcp_port), inet_ntoa(addr->sin_addr));
}
} else if (proto == PROTO_RTCP && endp->bts_end.rtcp_port == 0) {
if (memcmp(&endp->bts_end.addr, &addr->sin_addr,
sizeof(endp->bts_end.addr)) == 0) {
endp->bts_end.rtcp_port = addr->sin_port;
}
}
}
static int rtp_data_bts(struct bsc_fd *fd, unsigned int what)
{
char buf[4096];
struct sockaddr_in addr;
struct mgcp_endpoint *endp;
struct mgcp_config *cfg;
int rc, proto;
endp = (struct mgcp_endpoint *) fd->data;
cfg = endp->cfg;
rc = recevice_from(endp, fd->fd, &addr, buf, sizeof(buf));
if (rc <= 0)
return -1;
proto = fd == &endp->bts_end.rtp ? PROTO_RTP : PROTO_RTCP;
/* We have no idea who called us, maybe it is the BTS. */
/* it was the BTS... */
discover_bts(endp, proto, &addr);
if (memcmp(&endp->bts_end.addr, &addr.sin_addr, sizeof(addr.sin_addr)) != 0) {
LOGP(DMGCP, LOGL_ERROR,
"Data from wrong bts %s on 0x%x\n",
inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(endp));
return -1;
}
if (endp->bts_end.rtp_port != addr.sin_port &&
endp->bts_end.rtcp_port != addr.sin_port) {
LOGP(DMGCP, LOGL_ERROR,
"Data from wrong bts source port %d on 0x%x\n",
ntohs(addr.sin_port), ENDPOINT_NUMBER(endp));
return -1;
}
/* throw away the dummy message */
if (rc == 1 && buf[0] == DUMMY_LOAD) {
LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from bts on 0x%x\n",
ENDPOINT_NUMBER(endp));
return 0;
}
/* do this before the loop handling */
endp->bts_end.packets += 1;
forward_data(fd->fd, &endp->taps[MGCP_TAP_BTS_IN], buf, rc);
if (endp->is_transcoded)
return send_transcoder(&endp->trans_bts, endp->cfg, proto == PROTO_RTP, &buf[0], rc);
else
return send_to(endp, DEST_NETWORK, proto == PROTO_RTP, &addr, &buf[0], rc);
}
static int rtp_data_transcoder(struct mgcp_rtp_end *end, struct mgcp_endpoint *_endp,
int dest, struct bsc_fd *fd)
{
char buf[4096];
struct sockaddr_in addr;
struct mgcp_config *cfg;
int rc, proto;
cfg = _endp->cfg;
rc = recevice_from(_endp, fd->fd, &addr, buf, sizeof(buf));
if (rc <= 0)
return -1;
proto = fd == &end->rtp ? PROTO_RTP : PROTO_RTCP;
if (memcmp(&addr.sin_addr, &cfg->transcoder_in, sizeof(addr.sin_addr)) != 0) {
LOGP(DMGCP, LOGL_ERROR,
"Data not coming from transcoder dest: %d %s on 0x%x\n",
dest, inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(_endp));
return -1;
}
if (end->rtp_port != addr.sin_port &&
end->rtcp_port != addr.sin_port) {
LOGP(DMGCP, LOGL_ERROR,
"Data from wrong transcoder dest %d source port %d on 0x%x\n",
dest, ntohs(addr.sin_port), ENDPOINT_NUMBER(_endp));
return -1;
}
/* throw away the dummy message */
if (rc == 1 && buf[0] == DUMMY_LOAD) {
LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from transcoder dest %d on 0x%x\n",
dest, ENDPOINT_NUMBER(_endp));
return 0;
}
end->packets += 1;
return send_to(_endp, dest, proto == PROTO_RTP, &addr, &buf[0], rc);
}
static int rtp_data_trans_net(struct bsc_fd *fd, unsigned int what)
{
struct mgcp_endpoint *endp;
endp = (struct mgcp_endpoint *) fd->data;
return rtp_data_transcoder(&endp->trans_net, endp, DEST_NETWORK, fd);
}
static int rtp_data_trans_bts(struct bsc_fd *fd, unsigned int what)
{
struct mgcp_endpoint *endp;
endp = (struct mgcp_endpoint *) fd->data;
return rtp_data_transcoder(&endp->trans_bts, endp, DEST_BTS, fd);
}
static int create_bind(const char *source_addr, struct bsc_fd *fd, int port)
{
struct sockaddr_in addr;
@ -268,6 +456,8 @@ static int create_bind(const char *source_addr, struct bsc_fd *fd, int port)
inet_aton(source_addr, &addr.sin_addr);
if (bind(fd->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
close(fd->fd);
fd->fd = -1;
return -1;
}
@ -282,59 +472,107 @@ static int set_ip_tos(int fd, int tos)
return ret != 0;
}
static int bind_rtp(struct mgcp_endpoint *endp)
static int bind_rtp(struct mgcp_config *cfg, struct mgcp_rtp_end *rtp_end, int endpno)
{
struct mgcp_config *cfg = endp->cfg;
if (create_bind(cfg->source_addr, &endp->local_rtp, endp->rtp_port) != 0) {
if (create_bind(cfg->source_addr, &rtp_end->rtp, rtp_end->local_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));
cfg->source_addr, rtp_end->local_port, endpno);
goto cleanup0;
}
if (create_bind(cfg->source_addr, &endp->local_rtcp, endp->rtp_port + 1) != 0) {
if (create_bind(cfg->source_addr, &rtp_end->rtcp, rtp_end->local_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));
cfg->source_addr, rtp_end->local_port + 1, endpno);
goto cleanup1;
}
set_ip_tos(endp->local_rtp.fd, cfg->endp_dscp);
set_ip_tos(endp->local_rtcp.fd, cfg->endp_dscp);
set_ip_tos(rtp_end->rtp.fd, cfg->endp_dscp);
set_ip_tos(rtp_end->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) {
rtp_end->rtp.when = BSC_FD_READ;
if (bsc_register_fd(&rtp_end->rtp) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to register RTP port %d on 0x%x\n",
endp->rtp_port, ENDPOINT_NUMBER(endp));
rtp_end->local_port, endpno);
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) {
rtp_end->rtcp.when = BSC_FD_READ;
if (bsc_register_fd(&rtp_end->rtcp) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to register RTCP port %d on 0x%x\n",
endp->rtp_port + 1, ENDPOINT_NUMBER(endp));
rtp_end->local_port + 1, endpno);
goto cleanup3;
}
return 0;
cleanup3:
bsc_unregister_fd(&endp->local_rtp);
bsc_unregister_fd(&rtp_end->rtp);
cleanup2:
close(endp->local_rtcp.fd);
endp->local_rtcp.fd = -1;
close(rtp_end->rtcp.fd);
rtp_end->rtcp.fd = -1;
cleanup1:
close(endp->local_rtp.fd);
endp->local_rtp.fd = -1;
close(rtp_end->rtp.fd);
rtp_end->rtp.fd = -1;
cleanup0:
return -1;
}
int mgcp_bind_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
static int int_bind(const char *port,
struct mgcp_rtp_end *end, int (*cb)(struct bsc_fd *, unsigned),
struct mgcp_endpoint *_endp, int rtp_port)
{
endp->rtp_port = rtp_port;
return bind_rtp(endp);
if (end->rtp.fd != -1 || end->rtcp.fd != -1) {
LOGP(DMGCP, LOGL_ERROR, "Previous %s was still bound on %d\n",
port, ENDPOINT_NUMBER(_endp));
mgcp_free_rtp_port(end);
}
end->local_port = rtp_port;
end->rtp.cb = cb;
end->rtp.data = _endp;
end->rtcp.data = _endp;
end->rtcp.cb = cb;
return bind_rtp(_endp->cfg, end, ENDPOINT_NUMBER(_endp));
}
int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
{
return int_bind("bts-port", &endp->bts_end,
rtp_data_bts, endp, rtp_port);
}
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
{
return int_bind("net-port", &endp->net_end,
rtp_data_net, endp, rtp_port);
}
int mgcp_bind_trans_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
{
return int_bind("trans-net", &endp->trans_net,
rtp_data_trans_net, endp, rtp_port);
}
int mgcp_bind_trans_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
{
return int_bind("trans-bts", &endp->trans_bts,
rtp_data_trans_bts, endp, rtp_port);
}
int mgcp_free_rtp_port(struct mgcp_rtp_end *end)
{
if (end->rtp.fd != -1) {
close(end->rtp.fd);
end->rtp.fd = -1;
bsc_unregister_fd(&end->rtp);
}
if (end->rtcp.fd != -1) {
close(end->rtcp.fd);
end->rtcp.fd = -1;
bsc_unregister_fd(&end->rtcp);
}
return 0;
}

View File

@ -6,9 +6,9 @@
* (C) 2009-2011 by On-Waves
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@ -29,16 +29,12 @@
#include <limits.h>
#include <unistd.h>
#include <cellmgr_debug.h>
#include <mgcp/mgcp.h>
#include <mgcp/mgcp_internal.h>
#include <osmocore/msgb.h>
#include <osmocore/talloc.h>
#include <osmocore/select.h>
#include <osmocore/utils.h>
#include <mgcp/mgcp.h>
#include <mgcp/mgcp_internal.h>
/**
* Macro for tokenizing MGCP messages and SDP in one go.
@ -74,6 +70,7 @@
} \
}
static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end);
struct mgcp_request {
char *name;
@ -91,6 +88,9 @@ 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 struct msgb *handle_noti_req(struct mgcp_config *cfg, struct msgb *msg);
static void create_transcoder(struct mgcp_endpoint *endp);
static void delete_transcoder(struct mgcp_endpoint *endp);
static uint32_t generate_call_id(struct mgcp_config *cfg)
{
int i;
@ -104,8 +104,8 @@ static uint32_t generate_call_id(struct mgcp_config *cfg)
/* 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)
for (i = 1; i < cfg->trunk.number_endpoints; ++i)
if (cfg->trunk.endpoints[i].ci == cfg->last_call_id)
return generate_call_id(cfg);
return cfg->last_call_id;
@ -184,9 +184,9 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
"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);
endp->ci, addr, endp->net_end.local_port,
endp->bts_end.payload_type, endp->bts_end.payload_type,
endp->tcfg->audio_name);
return mgcp_create_response_with_data(200, " OK", msg, trans_id, sdp_record);
}
@ -270,7 +270,8 @@ static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
const char *mgcp)
{
char *rest = NULL;
int trunk, endp, mgcp_endp;
struct mgcp_trunk_config *tcfg;
int trunk, endp;
trunk = strtoul(mgcp + 6, &rest, 10);
if (rest == NULL || rest[0] != '/' || trunk < 1) {
@ -288,13 +289,23 @@ static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
if (endp == 1)
return NULL;
mgcp_endp = mgcp_timeslot_to_endpoint(trunk - 1, endp);
if (mgcp_endp < 1 || mgcp_endp >= cfg->number_endpoints) {
tcfg = mgcp_trunk_num(cfg, trunk);
if (!tcfg) {
LOGP(DMGCP, LOGL_ERROR, "The trunk %d is not declared.\n", trunk);
return NULL;
}
if (!tcfg->endpoints) {
LOGP(DMGCP, LOGL_ERROR, "Endpoints of trunk %d not allocated.\n", trunk);
return NULL;
}
if (endp < 1 || endp >= tcfg->number_endpoints) {
LOGP(DMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n", mgcp);
return NULL;
}
return &cfg->endpoints[mgcp_endp];
return &tcfg->endpoints[endp];
}
static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, const char *mgcp)
@ -306,8 +317,8 @@ static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, const char *
return find_e1_endpoint(cfg, mgcp);
} else {
gw = strtoul(mgcp, &endptr, 16);
if (gw > 0 && gw < cfg->number_endpoints && strcmp(endptr, "@mgw") == 0)
return &cfg->endpoints[gw];
if (gw > 0 && gw < cfg->trunk.number_endpoints && strcmp(endptr, "@mgw") == 0)
return &cfg->trunk.endpoints[gw];
}
LOGP(DMGCP, LOGL_ERROR, "Not able to find endpoint: '%s'\n", mgcp);
@ -399,7 +410,7 @@ static struct msgb *handle_audit_endpoint(struct mgcp_config *cfg, struct msgb *
return create_ok_response(200, "AUEP", trans_id);
}
static int parse_conn_mode(const char* msg, int *conn_mode)
static int parse_conn_mode(const char *msg, int *conn_mode)
{
int ret = 0;
if (strcmp(msg, "recvonly") == 0)
@ -418,26 +429,98 @@ static int parse_conn_mode(const char* msg, int *conn_mode)
return ret;
}
static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_rtp_end *end,
struct mgcp_port_range *range,
int (*alloc)(struct mgcp_endpoint *endp, int port))
{
int i;
if (range->mode == PORT_ALLOC_STATIC) {
end->local_alloc = PORT_ALLOC_STATIC;
return 0;
}
/* attempt to find a port */
for (i = 0; i < 200; ++i) {
int rc;
if (range->last_port >= range->range_end)
range->last_port = range->range_start;
rc = alloc(endp, range->last_port);
range->last_port += 2;
if (rc == 0) {
end->local_alloc = PORT_ALLOC_DYNAMIC;
return 0;
}
}
LOGP(DMGCP, LOGL_ERROR, "Allocating a RTP/RTCP port failed 200 times 0x%x.\n",
ENDPOINT_NUMBER(endp));
return -1;
}
static int allocate_ports(struct mgcp_endpoint *endp)
{
if (allocate_port(endp, &endp->net_end, &endp->cfg->net_ports,
mgcp_bind_net_rtp_port) != 0)
return -1;
if (allocate_port(endp, &endp->bts_end, &endp->cfg->bts_ports,
mgcp_bind_bts_rtp_port) != 0) {
mgcp_rtp_end_reset(&endp->net_end);
return -1;
}
if (endp->cfg->transcoder_ip && endp->tcfg->trunk_type == MGCP_TRUNK_VIRTUAL) {
if (allocate_port(endp, &endp->trans_net,
&endp->cfg->transcoder_ports,
mgcp_bind_trans_net_rtp_port) != 0) {
mgcp_rtp_end_reset(&endp->net_end);
mgcp_rtp_end_reset(&endp->bts_end);
return -1;
}
if (allocate_port(endp, &endp->trans_bts,
&endp->cfg->transcoder_ports,
mgcp_bind_trans_bts_rtp_port) != 0) {
mgcp_rtp_end_reset(&endp->net_end);
mgcp_rtp_end_reset(&endp->bts_end);
mgcp_rtp_end_reset(&endp->trans_net);
return -1;
}
/* remember that we have set up transcoding */
endp->is_transcoded = 1;
}
return 0;
}
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_trunk_config *tcfg;
struct mgcp_endpoint *endp;
int error_code = 500;
int port;
int error_code = 400;
found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
if (found != 0)
return create_err_response(510, "CRCX", trans_id);
if (endp->ci != CI_UNUSED) {
if (cfg->force_realloc) {
tcfg = endp->tcfg;
if (endp->allocated) {
if (tcfg->force_realloc) {
LOGP(DMGCP, LOGL_NOTICE, "Endpoint 0x%x already allocated. Forcing realloc.\n",
ENDPOINT_NUMBER(endp));
mgcp_free_endp(endp);
if (cfg->realloc_cb)
cfg->realloc_cb(cfg, ENDPOINT_NUMBER(endp));
cfg->realloc_cb(tcfg, ENDPOINT_NUMBER(endp));
} else {
LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n",
ENDPOINT_NUMBER(endp));
@ -449,11 +532,11 @@ static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
MSG_TOKENIZE_START
switch (msg->l3h[line_start]) {
case 'L':
endp->local_options = talloc_strdup(cfg->endpoints,
endp->local_options = talloc_strdup(tcfg->endpoints,
(const char *)&msg->l3h[line_start + 3]);
break;
case 'C':
endp->callid = talloc_strdup(cfg->endpoints,
endp->callid = talloc_strdup(tcfg->endpoints,
(const char *)&msg->l3h[line_start + 3]);
break;
case 'M':
@ -474,16 +557,13 @@ static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
MSG_TOKENIZE_END
/* initialize */
endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
endp->net_end.rtp_port = endp->net_end.rtcp_port = endp->bts_end.rtp_port = endp->bts_end.rtcp_port = 0;
/* set to zero until we get the info */
memset(&endp->remote, 0, sizeof(endp->remote));
memset(&endp->net_end.addr, 0, sizeof(endp->net_end.addr));
/* 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)
if (allocate_ports(endp) != 0)
goto error2;
/* assign a local call identifier or fail */
@ -491,11 +571,12 @@ static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
if (endp->ci == CI_UNUSED)
goto error2;
endp->bts_payload_type = cfg->audio_payload;
endp->allocated = 1;
endp->bts_end.payload_type = tcfg->audio_payload;
/* policy CB */
if (cfg->policy_cb) {
switch (cfg->policy_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX, trans_id)) {
switch (cfg->policy_cb(tcfg, 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));
@ -504,6 +585,7 @@ static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
break;
case MGCP_POLICY_DEFER:
/* stop processing */
create_transcoder(endp);
return NULL;
break;
case MGCP_POLICY_CONT:
@ -512,11 +594,13 @@ static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
}
}
LOGP(DMGCP, LOGL_NOTICE, "Creating endpoint on: 0x%x CI: %u port: %u\n",
ENDPOINT_NUMBER(endp), endp->ci, endp->rtp_port);
LOGP(DMGCP, LOGL_DEBUG, "Creating endpoint on: 0x%x CI: %u port: %u/%u\n",
ENDPOINT_NUMBER(endp), endp->ci,
endp->net_end.local_port, endp->bts_end.local_port);
if (cfg->change_cb)
cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX, endp->rtp_port);
cfg->change_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX);
create_transcoder(endp);
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",
@ -525,6 +609,7 @@ error:
return create_err_response(error_code, "CRCX", trans_id);
error2:
mgcp_free_endp(endp);
LOGP(DMGCP, LOGL_NOTICE, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp));
return create_err_response(error_code, "CRCX", trans_id);
}
@ -589,9 +674,9 @@ static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg)
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;
endp->net_end.rtp_port = htons(port);
endp->net_end.rtcp_port = htons(port + 1);
endp->net_end.payload_type = payload;
}
break;
}
@ -600,7 +685,7 @@ static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg)
const char *param = (const char *)&msg->l3h[line_start];
if (sscanf(param, "c=IN IP4 %15s", ipv4) == 1) {
inet_aton(ipv4, &endp->remote);
inet_aton(ipv4, &endp->net_end.addr);
}
break;
}
@ -614,7 +699,7 @@ static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg)
/* policy CB */
if (cfg->policy_cb) {
switch (cfg->policy_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX, trans_id)) {
switch (cfg->policy_cb(endp->tcfg, 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));
@ -633,10 +718,10 @@ static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg)
}
/* 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));
LOGP(DMGCP, LOGL_DEBUG, "Modified endpoint on: 0x%x Server: %s:%u\n",
ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port));
if (cfg->change_cb)
cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX, endp->rtp_port);
cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX);
if (silent)
goto out_silent;
@ -662,14 +747,14 @@ static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg)
int found, i, line_start;
const char *trans_id;
struct mgcp_endpoint *endp;
int error_code = 500;
int error_code = 400;
int silent = 0;
found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
if (found != 0)
return create_err_response(error_code, "DLCX", trans_id);
if (endp->ci == CI_UNUSED) {
if (!endp->allocated) {
LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp));
return create_err_response(400, "DLCX", trans_id);
}
@ -699,7 +784,7 @@ static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg)
/* policy CB */
if (cfg->policy_cb) {
switch (cfg->policy_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX, trans_id)) {
switch (cfg->policy_cb(endp->tcfg, 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));
@ -709,6 +794,7 @@ static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg)
break;
case MGCP_POLICY_DEFER:
/* stop processing */
delete_transcoder(endp);
return NULL;
break;
case MGCP_POLICY_CONT:
@ -718,11 +804,13 @@ static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg)
}
/* 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));
LOGP(DMGCP, LOGL_DEBUG, "Deleted endpoint on: 0x%x Server: %s:%u\n",
ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port));
delete_transcoder(endp);
mgcp_free_endp(endp);
if (cfg->change_cb)
cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX, endp->rtp_port);
cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX);
if (silent)
goto out_silent;
@ -764,13 +852,35 @@ static struct msgb *handle_noti_req(struct mgcp_config *cfg, struct msgb *msg)
if (found != 0)
return create_err_response(400, "RQNT", trans_id);
if (endp->ci == CI_UNUSED) {
if (!endp->allocated) {
LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp));
return create_err_response(400, "RQNT", trans_id);
}
return create_ok_response(200, "RQNT", trans_id);
}
static void trunk_init(struct mgcp_trunk_config *trunk)
{
trunk->trunk_type = MGCP_TRUNK_VIRTUAL;
trunk->audio_name = talloc_strdup(trunk->cfg, "AMR/8000");
trunk->audio_payload = 126;
/* MGW handling */
trunk->target_trunk_start = 1;
trunk->endp_offset = 1;
trunk->vad_enabled = 1;
trunk->digital_inp_gain = 31;
trunk->digital_out_gain = 31;
trunk->upstr_agc_enbl = 0;
trunk->upstr_adp_rate = 100;
trunk->upstr_max_gain = 46;
trunk->upstr_target_lvl = 20;
trunk->dwnstr_agc_enbl = 0;
trunk->dwnstr_adp_rate = 100;
trunk->dwnstr_max_gain = 46;
trunk->dwnstr_target_lvl = 20;
}
struct mgcp_config *mgcp_config_alloc(void)
{
struct mgcp_config *cfg;
@ -783,31 +893,95 @@ struct mgcp_config *mgcp_config_alloc(void)
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;
cfg->transcoder_remote_base = 4000;
cfg->bts_ports.base_port = RTP_PORT_DEFAULT;
cfg->net_ports.base_port = RTP_PORT_NET_DEFAULT;
/* default trunk handling */
cfg->trunk.cfg = cfg;
cfg->trunk.trunk_nr = 0;
trunk_init(&cfg->trunk);
INIT_LLIST_HEAD(&cfg->trunks);
return cfg;
}
int mgcp_endpoints_allocate(struct mgcp_config *cfg)
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr)
{
struct mgcp_trunk_config *trunk;
trunk = talloc_zero(cfg, struct mgcp_trunk_config);
if (!trunk) {
LOGP(DMGCP, LOGL_ERROR, "Failed to allocate.\n");
return NULL;
}
trunk->cfg = cfg;
trunk->trunk_type = MGCP_TRUNK_E1;
trunk->trunk_nr = nr;
trunk_init(trunk);
trunk->number_endpoints = 33;
llist_add_tail(&trunk->entry, &cfg->trunks);
return trunk;
}
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index)
{
struct mgcp_trunk_config *trunk;
llist_for_each_entry(trunk, &cfg->trunks, entry)
if (trunk->trunk_nr == index)
return trunk;
return NULL;
}
static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end)
{
if (end->local_alloc == PORT_ALLOC_DYNAMIC) {
mgcp_free_rtp_port(end);
end->local_port = 0;
}
end->packets = 0;
memset(&end->addr, 0, sizeof(end->addr));
end->rtp_port = end->rtcp_port = 0;
end->payload_type = -1;
end->local_alloc = -1;
}
static void mgcp_rtp_end_init(struct mgcp_rtp_end *end)
{
mgcp_rtp_end_reset(end);
end->rtp.fd = -1;
end->rtcp.fd = -1;
}
int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
{
int i;
/* Initialize all endpoints */
cfg->endpoints = _talloc_zero_array(cfg,
tcfg->endpoints = _talloc_zero_array(tcfg->cfg,
sizeof(struct mgcp_endpoint),
cfg->number_endpoints, "endpoints");
if (!cfg->endpoints)
tcfg->number_endpoints, "endpoints");
if (!tcfg->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;
for (i = 0; i < tcfg->number_endpoints; ++i) {
tcfg->endpoints[i].ci = CI_UNUSED;
tcfg->endpoints[i].cfg = tcfg->cfg;
tcfg->endpoints[i].tcfg = tcfg;
mgcp_rtp_end_init(&tcfg->endpoints[i].net_end);
mgcp_rtp_end_init(&tcfg->endpoints[i].bts_end);
mgcp_rtp_end_init(&tcfg->endpoints[i].trans_net);
mgcp_rtp_end_init(&tcfg->endpoints[i].trans_bts);
/* MGW code */
tcfg->endpoints[i].audio_port = UINT_MAX;
}
return 0;
@ -817,6 +991,7 @@ 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;
endp->allocated = 0;
if (endp->callid) {
talloc_free(endp->callid);
@ -828,19 +1003,121 @@ void mgcp_free_endp(struct mgcp_endpoint *endp)
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));
mgcp_rtp_end_reset(&endp->bts_end);
mgcp_rtp_end_reset(&endp->net_end);
mgcp_rtp_end_reset(&endp->trans_net);
mgcp_rtp_end_reset(&endp->trans_bts);
endp->is_transcoded = 0;
memset(&endp->net_state, 0, sizeof(endp->net_state));
memset(&endp->bts_state, 0, sizeof(endp->bts_state));
endp->conn_mode = endp->orig_mode = MGCP_CONN_NONE;
endp->allow_patch = 0;
memset(&endp->taps, 0, sizeof(endp->taps));
}
static int send_trans(struct mgcp_config *cfg, const char *buf, int len)
{
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr = cfg->transcoder_in;
addr.sin_port = htons(2427);
return sendto(cfg->gw_fd.bfd.fd, buf, len, 0,
(struct sockaddr *) &addr, sizeof(addr));
}
static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port,
const char *msg, const char *mode)
{
char buf[2096];
int len;
/* hardcoded to AMR right now, we do not know the real type at this point */
len = snprintf(buf, sizeof(buf),
"%s 42 %x@mgw MGCP 1.0\r\n"
"C: 4256\r\n"
"M: %s\r\n"
"\r\n"
"c=IN IP4 %s\r\n"
"m=audio %d RTP/AVP %d\r\n"
"a=rtpmap:%d %s\r\n",
msg, endpoint, mode, endp->cfg->source_addr,
port, endp->tcfg->audio_payload,
endp->tcfg->audio_payload, endp->tcfg->audio_name);
if (len < 0)
return;
buf[sizeof(buf) - 1] = '\0';
send_trans(endp->cfg, buf, len);
}
static void send_dlcx(struct mgcp_endpoint *endp, int endpoint)
{
char buf[2096];
int len;
len = snprintf(buf, sizeof(buf),
"DLCX 43 %x@mgw MGCP 1.0\r\n"
"C: 4256\r\n"
, endpoint);
if (len < 0)
return;
buf[sizeof(buf) - 1] = '\0';
send_trans(endp->cfg, buf, len);
}
static void create_transcoder(struct mgcp_endpoint *endp)
{
int port;
int in_endp = ENDPOINT_NUMBER(endp);
int out_endp = endp_back_channel(in_endp);
if (!endp->is_transcoded)
return;
send_msg(endp, in_endp, endp->trans_bts.local_port, "CRCX", "sendrecv");
send_msg(endp, in_endp, endp->trans_bts.local_port, "MDCX", "sendrecv");
send_msg(endp, out_endp, endp->trans_net.local_port, "CRCX", "sendrecv");
send_msg(endp, out_endp, endp->trans_net.local_port, "MDCX", "sendrecv");
port = rtp_calculate_port(in_endp, endp->cfg->transcoder_remote_base);
endp->trans_bts.rtp_port = htons(port);
endp->trans_bts.rtcp_port = htons(port + 1);
port = rtp_calculate_port(out_endp, endp->cfg->transcoder_remote_base);
endp->trans_net.rtp_port = htons(port);
endp->trans_net.rtcp_port = htons(port + 1);
}
static void delete_transcoder(struct mgcp_endpoint *endp)
{
int in_endp = ENDPOINT_NUMBER(endp);
int out_endp = endp_back_channel(in_endp);
if (!endp->is_transcoded)
return;
send_dlcx(endp, in_endp);
send_dlcx(endp, out_endp);
}
int mgcp_reset_transcoder(struct mgcp_config *cfg)
{
if (!cfg->transcoder_ip)
return 0;
static const char mgcp_reset[] = {
"RSIP 1 13@mgw MGCP 1.0\r\n"
};
return send_trans(cfg, mgcp_reset, sizeof mgcp_reset -1);
}

View File

@ -2,13 +2,13 @@
/* The protocol implementation */
/*
* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2010 by On-Waves
* (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2011 by On-Waves
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@ -23,32 +23,91 @@
#include <sys/types.h>
#include <cellmgr_debug.h>
#include <osmocore/talloc.h>
#include <mgcp/mgcp.h>
#include <mgcp/mgcp_internal.h>
#include <mgcp_ss7.h>
#include <osmocore/talloc.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/vty.h>
#include <ss7_vty.h>
#include <string.h>
#include <netdb.h>
static struct mgcp_config *g_cfg = NULL;
/* MGW changes */
extern void mgcp_write_extra(struct vty *vty, struct mgcp_config *cfg);
extern void mgcp_write_trunk_extra(struct vty *vty, struct mgcp_trunk_config *cfg);
DEFUN(ournode_exit, ournode_exit_cmd,
"exit", "Exit\n")
{
switch (vty->node) {
case TRUNK_NODE:
vty->node = MGCP_NODE;
break;
default:
vty->node = CONFIG_NODE;
break;
}
return CMD_SUCCESS;
}
DEFUN(ournode_end, ournode_end_cmd,
"end", "End the current mode\n")
{
switch (vty->node) {
case VIEW_NODE:
case ENABLE_NODE:
break;
default:
vty_config_unlock(vty);
vty->node = ENABLE_NODE;
vty->index = NULL;
vty->index_sub = NULL;
break;
}
return CMD_SUCCESS;
}
/* a talloc string replace routine */
static void bsc_replace_string(void *ctx, char **dst, const char *newstr)
{
if (*dst)
talloc_free(*dst);
*dst = talloc_strdup(ctx, newstr);
}
struct mgcp_config *g_cfg = NULL;
static struct mgcp_trunk_config *find_trunk(struct mgcp_config *cfg, int nr)
{
struct mgcp_trunk_config *trunk;
if (nr == 0)
trunk = &cfg->trunk;
else
trunk = mgcp_trunk_num(cfg, nr);
return trunk;
}
/*
* vty code for mgcp below
*/
static struct cmd_node mgcp_node = {
struct cmd_node mgcp_node = {
MGCP_NODE,
"%s(mgcp)#",
1,
};
extern void mgcp_write_extra(struct vty *vty);
struct cmd_node trunk_node = {
TRUNK_NODE,
"%s(trunk)#",
1,
};
static int config_write_mgcp(struct vty *vty)
{
vty_out(vty, "mgcp%s", VTY_NEWLINE);
@ -58,35 +117,83 @@ static int config_write_mgcp(struct vty *vty)
vty_out(vty, " mgw ip %s%s", g_cfg->bts_ip, VTY_NEWLINE);
vty_out(vty, " bind ip %s%s", g_cfg->source_addr, VTY_NEWLINE);
vty_out(vty, " bind port %u%s", g_cfg->source_port, VTY_NEWLINE);
vty_out(vty, " rtp base %u%s", g_cfg->rtp_base_port, VTY_NEWLINE);
if (g_cfg->bts_ports.mode == PORT_ALLOC_STATIC)
vty_out(vty, " rtp bts-base %u%s", g_cfg->bts_ports.base_port, VTY_NEWLINE);
else
vty_out(vty, " rtp bts-range %u %u%s",
g_cfg->bts_ports.range_start, g_cfg->bts_ports.range_end, VTY_NEWLINE);
if (g_cfg->net_ports.mode == PORT_ALLOC_STATIC)
vty_out(vty, " rtp net-base %u%s", g_cfg->net_ports.base_port, VTY_NEWLINE);
else
vty_out(vty, " rtp net-range %u %u%s",
g_cfg->net_ports.range_start, g_cfg->net_ports.range_end, VTY_NEWLINE);
vty_out(vty, " rtp ip-dscp %d%s", g_cfg->endp_dscp, VTY_NEWLINE);
if (g_cfg->audio_payload != -1)
vty_out(vty, " sdp audio payload number %d%s", g_cfg->audio_payload, VTY_NEWLINE);
if (g_cfg->audio_name)
vty_out(vty, " sdp audio payload name %s%s", g_cfg->audio_name, VTY_NEWLINE);
vty_out(vty, " loop %u%s", !!g_cfg->audio_loop, VTY_NEWLINE);
vty_out(vty, " number endpoints %u%s", g_cfg->number_endpoints - 1, VTY_NEWLINE);
mgcp_write_extra(vty);
if (g_cfg->trunk.audio_payload != -1)
vty_out(vty, " sdp audio payload number %d%s",
g_cfg->trunk.audio_payload, VTY_NEWLINE);
if (g_cfg->trunk.audio_name)
vty_out(vty, " sdp audio payload name %s%s",
g_cfg->trunk.audio_name, VTY_NEWLINE);
vty_out(vty, " loop %u%s", !!g_cfg->trunk.audio_loop, VTY_NEWLINE);
vty_out(vty, " number endpoints %u%s", g_cfg->trunk.number_endpoints - 1, VTY_NEWLINE);
if (g_cfg->call_agent_addr)
vty_out(vty, " call agent ip %s%s", g_cfg->call_agent_addr, VTY_NEWLINE);
if (g_cfg->transcoder_ip)
vty_out(vty, " transcoder-mgw %s%s", g_cfg->transcoder_ip, VTY_NEWLINE);
if (g_cfg->transcoder_ports.mode == PORT_ALLOC_STATIC)
vty_out(vty, " rtp transcoder-base %u%s", g_cfg->transcoder_ports.base_port, VTY_NEWLINE);
else
vty_out(vty, " rtp transcoder-range %u %u%s",
g_cfg->transcoder_ports.range_start, g_cfg->transcoder_ports.range_end, VTY_NEWLINE);
vty_out(vty, " transcoder-remote-base %u%s", g_cfg->transcoder_remote_base, VTY_NEWLINE);
mgcp_write_extra(vty, g_cfg);
return CMD_SUCCESS;
}
static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg)
{
int i;
vty_out(vty, "%s trunk nr %d with %d endpoints:%s",
cfg->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1",
cfg->trunk_nr, cfg->number_endpoints - 1, VTY_NEWLINE);
if (!cfg->endpoints) {
vty_out(vty, "No endpoints allocated yet.%s", VTY_NEWLINE);
return;
}
for (i = 1; i < cfg->number_endpoints; ++i) {
struct mgcp_endpoint *endp = &cfg->endpoints[i];
vty_out(vty,
" Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u on %s "
"traffic received bts: %u/%u remote: %u/%u transcoder: %u/%u%s",
i, endp->ci,
ntohs(endp->net_end.rtp_port), ntohs(endp->net_end.rtcp_port),
ntohs(endp->bts_end.rtp_port), ntohs(endp->bts_end.rtcp_port),
inet_ntoa(endp->bts_end.addr),
endp->bts_end.packets, endp->bts_state.lost_no,
endp->net_end.packets, endp->net_state.lost_no,
endp->trans_net.packets, endp->trans_bts.packets,
VTY_NEWLINE);
}
}
DEFUN(show_mcgp, show_mgcp_cmd, "show mgcp",
SHOW_STR "Display information about the MGCP Media Gateway")
{
int i;
struct mgcp_trunk_config *trunk;
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 on %s traffic received bts: %u/%u remote: %u/%u%s",
i, endp->ci,
ntohs(endp->net_rtp), ntohs(endp->net_rtcp),
ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp),
inet_ntoa(endp->bts), endp->in_bts, endp->bts_state.lost_no,
endp->in_remote, endp->net_state.lost_no,
VTY_NEWLINE);
}
dump_trunk(vty, &g_cfg->trunk);
llist_for_each_entry(trunk, &g_cfg->trunks, entry)
dump_trunk(vty, trunk);
return CMD_SUCCESS;
}
@ -115,9 +222,7 @@ DEFUN(cfg_mgcp_local_ip,
}
addr = (struct in_addr *) hosts->h_addr_list[0];
if (g_cfg->local_ip)
talloc_free(g_cfg->local_ip);
g_cfg->local_ip = talloc_strdup(g_cfg, inet_ntoa(*addr));
bsc_replace_string(g_cfg, &g_cfg->local_ip, inet_ntoa(*addr));
return CMD_SUCCESS;
}
@ -136,9 +241,7 @@ DEFUN(cfg_mgcp_mgw_ip,
}
addr = (struct in_addr *) hosts->h_addr_list[0];
if (g_cfg->bts_ip)
talloc_free(g_cfg->bts_ip);
g_cfg->bts_ip = talloc_strdup(g_cfg, inet_ntoa(*addr));
bsc_replace_string(g_cfg, &g_cfg->bts_ip, inet_ntoa(*addr));
inet_aton(g_cfg->bts_ip, &g_cfg->bts_in);
return CMD_SUCCESS;
}
@ -148,9 +251,7 @@ DEFUN(cfg_mgcp_bind_ip,
"bind ip A.B.C.D",
"Bind the MGCP to this local addr")
{
if (g_cfg->source_addr)
talloc_free(g_cfg->source_addr);
g_cfg->source_addr = talloc_strdup(g_cfg, argv[0]);
bsc_replace_string(g_cfg, &g_cfg->source_addr, argv[0]);
return CMD_SUCCESS;
}
@ -164,13 +265,88 @@ DEFUN(cfg_mgcp_bind_port,
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_rtp_base_port,
cfg_mgcp_rtp_base_port_cmd,
"rtp base <0-65534>",
"Base port to use")
DEFUN(cfg_mgcp_bind_early,
cfg_mgcp_bind_early_cmd,
"bind early (0|1)",
"Bind all RTP ports early")
{
vty_out(vty, "bind early is deprecated, remove it from the config.\n");
return CMD_WARNING;
}
static void parse_base(struct mgcp_port_range *range, const char **argv)
{
unsigned int port = atoi(argv[0]);
g_cfg->rtp_base_port = port;
range->mode = PORT_ALLOC_STATIC;
range->base_port = port;
}
static void parse_range(struct mgcp_port_range *range, const char **argv)
{
range->mode = PORT_ALLOC_DYNAMIC;
range->range_start = atoi(argv[0]);
range->range_end = atoi(argv[1]);
range->last_port = g_cfg->bts_ports.range_start;
}
DEFUN(cfg_mgcp_rtp_bts_base_port,
cfg_mgcp_rtp_bts_base_port_cmd,
"rtp bts-base <0-65534>",
"Base port to use")
{
parse_base(&g_cfg->bts_ports, argv);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_rtp_bts_range,
cfg_mgcp_rtp_bts_range_cmd,
"rtp bts-range <0-65534> <0-65534>",
"Range of ports to allocate for endpoints\n"
"Start of the range of ports\n" "End of the range of ports\n")
{
parse_range(&g_cfg->bts_ports, argv);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_rtp_net_range,
cfg_mgcp_rtp_net_range_cmd,
"rtp net-range <0-65534> <0-65534>",
"Range of ports to allocate for endpoints\n"
"Start of the range of ports\n" "End of the range of ports\n")
{
parse_range(&g_cfg->net_ports, argv);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_rtp_net_base_port,
cfg_mgcp_rtp_net_base_port_cmd,
"rtp net-base <0-65534>",
"Base port to use for network port\n" "Port\n")
{
parse_base(&g_cfg->net_ports, argv);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgcp_rtp_bts_base_port, cfg_mgcp_rtp_base_port_cmd,
"rtp base <0-65534>", "Base port to use")
DEFUN(cfg_mgcp_rtp_transcoder_range,
cfg_mgcp_rtp_transcoder_range_cmd,
"rtp transcoder-range <0-65534> <0-65534>",
"Range of ports to allocate for the transcoder\n"
"Start of the range of ports\n" "End of the range of ports\n")
{
parse_range(&g_cfg->transcoder_ports, argv);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_rtp_transcoder_base,
cfg_mgcp_rtp_transcoder_base_cmd,
"rtp transcoder-base <0-65534>",
"Base port for the transcoder range\n" "Port\n")
{
parse_base(&g_cfg->transcoder_ports, argv);
return CMD_SUCCESS;
}
@ -195,7 +371,7 @@ DEFUN(cfg_mgcp_sdp_payload_number,
"Set the audio codec to use")
{
unsigned int payload = atoi(argv[0]);
g_cfg->audio_payload = payload;
g_cfg->trunk.audio_payload = payload;
return CMD_SUCCESS;
}
@ -204,9 +380,7 @@ DEFUN(cfg_mgcp_sdp_payload_name,
"sdp audio payload name NAME",
"Set the audio name to use")
{
if (g_cfg->audio_name)
talloc_free(g_cfg->audio_name);
g_cfg->audio_name = talloc_strdup(g_cfg, argv[0]);
bsc_replace_string(g_cfg, &g_cfg->trunk.audio_name, argv[0]);
return CMD_SUCCESS;
}
@ -215,7 +389,7 @@ DEFUN(cfg_mgcp_loop,
"loop (0|1)",
"Loop the audio")
{
g_cfg->audio_loop = atoi(argv[0]);
g_cfg->trunk.audio_loop = atoi(argv[0]);
return CMD_SUCCESS;
}
@ -225,65 +399,260 @@ DEFUN(cfg_mgcp_number_endp,
"The number of endpoints to allocate. This is not dynamic.")
{
/* + 1 as we start counting at one */
g_cfg->number_endpoints = atoi(argv[0]) + 1;
g_cfg->trunk.number_endpoints = atoi(argv[0]) + 1;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_agent_addr,
cfg_mgcp_agent_addr_cmd,
"call agent ip IP",
"Set the address of the call agent.")
{
bsc_replace_string(g_cfg, &g_cfg->call_agent_addr, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_transcoder,
cfg_mgcp_transcoder_cmd,
"transcoder-mgw A.B.C.D",
"Use a MGW to detranscoder RTP\n"
"The IP address of the MGW")
{
bsc_replace_string(g_cfg, &g_cfg->transcoder_ip, argv[0]);
inet_aton(g_cfg->transcoder_ip, &g_cfg->transcoder_in);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_no_transcoder,
cfg_mgcp_no_transcoder_cmd,
NO_STR "transcoder-mgw",
"Disable the transcoding\n")
{
if (g_cfg->transcoder_ip) {
LOGP(DMGCP, LOGL_NOTICE, "Disabling transcoding on future calls.\n");
talloc_free(g_cfg->transcoder_ip);
g_cfg->transcoder_ip = NULL;
}
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_transcoder_remote_base,
cfg_mgcp_transcoder_remote_base_cmd,
"transcoder-remote-base <0-65534>",
"Set the base port for the transcoder\n" "The RTP base port on the transcoder")
{
g_cfg->transcoder_remote_base = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_trunk, cfg_mgcp_trunk_cmd,
"trunk <1-64>",
"Configure a SS7 trunk\n" "Trunk Nr\n")
{
struct mgcp_trunk_config *trunk;
int index = atoi(argv[0]);
trunk = mgcp_trunk_num(g_cfg, index);
if (!trunk)
trunk = mgcp_trunk_alloc(g_cfg, index);
if (!trunk) {
vty_out(vty, "%%Unable to allocate trunk %u.%s",
index, VTY_NEWLINE);
return CMD_WARNING;
}
vty->node = TRUNK_NODE;
vty->index = trunk;
return CMD_SUCCESS;
}
static int config_write_trunk(struct vty *vty)
{
struct mgcp_trunk_config *trunk;
llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
vty_out(vty, " trunk %d%s", trunk->trunk_nr, VTY_NEWLINE);
vty_out(vty, " sdp audio payload number %d%s",
trunk->audio_payload, VTY_NEWLINE);
vty_out(vty, " sdp audio payload name %s%s",
trunk->audio_name, VTY_NEWLINE);
vty_out(vty, " loop %d%s",
trunk->audio_loop, VTY_NEWLINE);
mgcp_write_trunk_extra(vty, trunk);
}
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_payload_number,
cfg_trunk_payload_number_cmd,
"sdp audio payload number <1-255>",
"SDP related\n" "Audio\n" "Payload\n" "Payload Number\n")
{
struct mgcp_trunk_config *trunk = vty->index;
unsigned int payload = atoi(argv[0]);
trunk->audio_payload = payload;
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_payload_name,
cfg_trunk_payload_name_cmd,
"sdp audio payload name NAME",
"SDP related\n" "Audio\n" "Payload\n" "Payload Name\n")
{
struct mgcp_trunk_config *trunk = vty->index;
bsc_replace_string(g_cfg, &trunk->audio_name, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_loop,
cfg_trunk_loop_cmd,
"loop (0|1)",
"Loop the audio")
{
struct mgcp_trunk_config *trunk = vty->index;
trunk->audio_loop = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(loop_endp,
loop_endp_cmd,
"loop-endpoint NAME (0|1)",
"Loop a given endpoint\n"
"loop-endpoint <0-64> NAME (0|1)",
"Loop a given endpoint\n" "Trunk number\n"
"The name in hex of the endpoint\n" "Disable the loop\n" "Enable the loop\n")
{
struct mgcp_trunk_config *trunk;
struct mgcp_endpoint *endp;
int endp_no = strtoul(argv[0], NULL, 16);
if (endp_no < 1 || endp_no >= g_cfg->number_endpoints) {
trunk = find_trunk(g_cfg, atoi(argv[0]));
if (!trunk) {
vty_out(vty, "%%Trunk %d not found in the config.%s",
atoi(argv[0]), VTY_NEWLINE);
return CMD_WARNING;
}
if (!trunk->endpoints) {
vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
trunk->trunk_nr, VTY_NEWLINE);
return CMD_WARNING;
}
int endp_no = strtoul(argv[1], NULL, 16);
if (endp_no < 1 || endp_no >= trunk->number_endpoints) {
vty_out(vty, "Loopback number %s/%d is invalid.%s",
argv[0], endp_no, VTY_NEWLINE);
argv[1], endp_no, VTY_NEWLINE);
return CMD_WARNING;
}
endp = &g_cfg->endpoints[endp_no];
int loop = atoi(argv[1]);
endp = &trunk->endpoints[endp_no];
int loop = atoi(argv[2]);
if (loop)
endp->conn_mode = MGCP_CONN_LOOPBACK;
else
endp->conn_mode = endp->orig_mode;
endp->allow_patch = 1;
return CMD_SUCCESS;
}
DEFUN(ournode_exit,
ournode_exit_cmd, "exit", "Exit current mode and down to previous node\n")
DEFUN(tap_call,
tap_call_cmd,
"tap-call <0-64> ENDPOINT (bts-in|bts-out|net-in|net-out) A.B.C.D <0-65534>",
"Forward data on endpoint to a different system\n" "Trunk number\n"
"The endpoint in hex\n"
"Forward the data coming from the bts\n"
"Forward the data coming from the bts leaving to the network\n"
"Forward the data coming from the net\n"
"Forward the data coming from the net leaving to the bts\n"
"destination IP of the data\n" "destination port\n")
{
switch (vty->node) {
case MGCP_NODE:
vty->node = CONFIG_NODE;
vty->index = NULL;
break;
struct mgcp_rtp_tap *tap;
struct mgcp_trunk_config *trunk;
struct mgcp_endpoint *endp;
int port = 0;
trunk = find_trunk(g_cfg, atoi(argv[0]));
if (!trunk) {
vty_out(vty, "%%Trunk %d not found in the config.%s",
atoi(argv[0]), VTY_NEWLINE);
return CMD_WARNING;
}
if (!trunk->endpoints) {
vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
trunk->trunk_nr, VTY_NEWLINE);
return CMD_WARNING;
}
int endp_no = strtoul(argv[1], NULL, 16);
if (endp_no < 1 || endp_no >= trunk->number_endpoints) {
vty_out(vty, "Endpoint number %s/%d is invalid.%s",
argv[1], endp_no, VTY_NEWLINE);
return CMD_WARNING;
}
endp = &trunk->endpoints[endp_no];
if (strcmp(argv[2], "bts-in") == 0) {
port = MGCP_TAP_BTS_IN;
} else if (strcmp(argv[2], "bts-out") == 0) {
port = MGCP_TAP_BTS_OUT;
} else if (strcmp(argv[2], "net-in") == 0) {
port = MGCP_TAP_NET_IN;
} else if (strcmp(argv[2], "net-out") == 0) {
port = MGCP_TAP_NET_OUT;
} else {
vty_out(vty, "Unknown mode... tricked vty?%s", VTY_NEWLINE);
return CMD_WARNING;
}
tap = &endp->taps[port];
memset(&tap->forward, 0, sizeof(tap->forward));
inet_aton(argv[3], &tap->forward.sin_addr);
tap->forward.sin_port = htons(atoi(argv[4]));
tap->enabled = 1;
return CMD_SUCCESS;
}
DEFUN(ournode_end,
ournode_end_cmd, "end", "End current mode and change to enable mode.")
DEFUN(free_endp, free_endp_cmd,
"free-endpoint <0-64> NUMBER",
"Free the given endpoint\n" "Trunk number\n"
"Endpoint number in hex.\n")
{
switch (vty->node) {
case VIEW_NODE:
case ENABLE_NODE:
break;
case MGCP_NODE:
vty_config_unlock(vty);
vty->node = ENABLE_NODE;
vty->index = NULL;
vty->index_sub = NULL;
break;
struct mgcp_trunk_config *trunk;
struct mgcp_endpoint *endp;
trunk = find_trunk(g_cfg, atoi(argv[0]));
if (!trunk) {
vty_out(vty, "%%Trunk %d not found in the config.%s",
atoi(argv[0]), VTY_NEWLINE);
return CMD_WARNING;
}
if (!trunk->endpoints) {
vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
trunk->trunk_nr, VTY_NEWLINE);
return CMD_WARNING;
}
int endp_no = strtoul(argv[1], NULL, 16);
if (endp_no < 1 || endp_no >= trunk->number_endpoints) {
vty_out(vty, "Endpoint number %s/%d is invalid.%s",
argv[1], endp_no, VTY_NEWLINE);
return CMD_WARNING;
}
endp = &trunk->endpoints[endp_no];
mgcp_free_endp(endp);
return CMD_SUCCESS;
}
@ -291,6 +660,8 @@ int mgcp_vty_init(void)
{
install_element_ve(&show_mgcp_cmd);
install_element(ENABLE_NODE, &loop_endp_cmd);
install_element(ENABLE_NODE, &tap_call_cmd);
install_element(ENABLE_NODE, &free_endp_cmd);
install_element(CONFIG_NODE, &cfg_mgcp_cmd);
install_node(&mgcp_node, config_write_mgcp);
@ -302,18 +673,138 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_mgw_ip_cmd);
install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd);
install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_base_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_base_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_net_base_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_range_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_net_range_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_range_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_base_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_tos_cmd);
install_element(MGCP_NODE, &cfg_mgcp_agent_addr_cmd);
install_element(MGCP_NODE, &cfg_mgcp_transcoder_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_transcoder_cmd);
install_element(MGCP_NODE, &cfg_mgcp_transcoder_remote_base_cmd);
install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd);
install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd);
install_element(MGCP_NODE, &cfg_mgcp_loop_cmd);
install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd);
install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
install_node(&trunk_node, config_write_trunk);
install_default(TRUNK_NODE);
install_element(TRUNK_NODE, &ournode_exit_cmd);
install_element(TRUNK_NODE, &ournode_end_cmd);
install_element(TRUNK_NODE, &cfg_trunk_payload_number_cmd);
install_element(TRUNK_NODE, &cfg_trunk_payload_name_cmd);
install_element(TRUNK_NODE, &cfg_trunk_loop_cmd);
return 0;
}
void mgcp_vty_set_config(struct mgcp_config *cfg)
static int allocate_trunk(struct mgcp_trunk_config *trunk)
{
g_cfg = cfg;
int i;
struct mgcp_config *cfg = trunk->cfg;
if (mgcp_endpoints_allocate(trunk) != 0) {
LOGP(DMGCP, LOGL_ERROR,
"Failed to allocate %d endpoints on trunk %d.\n",
trunk->number_endpoints, trunk->trunk_nr);
return -1;
}
/* early bind */
for (i = 1; i < trunk->number_endpoints; ++i) {
struct mgcp_endpoint *endp = &trunk->endpoints[i];
if (cfg->bts_ports.mode == PORT_ALLOC_STATIC) {
cfg->last_bts_port += 2;
if (mgcp_bind_bts_rtp_port(endp, cfg->last_bts_port) != 0) {
LOGP(DMGCP, LOGL_FATAL,
"Failed to bind: %d\n", cfg->last_bts_port);
return -1;
}
endp->bts_end.local_alloc = PORT_ALLOC_STATIC;
}
if (cfg->net_ports.mode == PORT_ALLOC_STATIC) {
cfg->last_net_port += 2;
if (mgcp_bind_net_rtp_port(endp, cfg->last_net_port) != 0) {
LOGP(DMGCP, LOGL_FATAL,
"Failed to bind: %d\n", cfg->last_net_port);
return -1;
}
endp->net_end.local_alloc = PORT_ALLOC_STATIC;
}
if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL &&
cfg->transcoder_ip && cfg->transcoder_ports.mode == PORT_ALLOC_STATIC) {
int rtp_port;
/* network side */
rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp),
cfg->transcoder_ports.base_port);
if (mgcp_bind_trans_net_rtp_port(endp, rtp_port) != 0) {
LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port);
return -1;
}
endp->trans_net.local_alloc = PORT_ALLOC_STATIC;
/* bts side */
rtp_port = rtp_calculate_port(endp_back_channel(ENDPOINT_NUMBER(endp)),
cfg->transcoder_ports.base_port);
if (mgcp_bind_trans_bts_rtp_port(endp, rtp_port) != 0) {
LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port);
return -1;
}
endp->trans_bts.local_alloc = PORT_ALLOC_STATIC;
}
}
return 0;
}
int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg)
{
int rc;
struct mgcp_trunk_config *trunk;
g_cfg = cfg;
rc = vty_read_config_file(config_file, NULL);
if (rc < 0) {
fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
return rc;
}
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;
}
/* initialize the last ports */
g_cfg->last_bts_port = rtp_calculate_port(0, g_cfg->bts_ports.base_port);
g_cfg->last_net_port = rtp_calculate_port(0, g_cfg->net_ports.base_port);
if (allocate_trunk(&g_cfg->trunk) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to initialize the virtual trunk.\n");
return -1;
}
llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
if (allocate_trunk(trunk) != 0) {
LOGP(DMGCP, LOGL_ERROR,
"Failed to initialize E1 trunk %d.\n", trunk->trunk_nr);
return -1;
}
}
return 0;
}

View File

@ -29,7 +29,6 @@
#include <osmocore/talloc.h>
#include <osmocore/timer.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/telnet_interface.h>
@ -57,35 +56,17 @@ static struct log_target *stderr_target;
static char *config_file = "mgcp_mgw.cfg";
static int exit_on_failure = 0;
static int s_endp_offset = 1;
#define TO_MGW_PORT(no) (no-s_endp_offset)
#define FROM_MGW_PORT(no) (no+s_endp_offset)
extern struct mgcp_config *g_cfg;
static struct mgcp_ss7 *s_ss7;
static struct mgcp_config *g_cfg;
static int s_vad_enabled = 1;
static void mgcp_ss7_endp_free(struct mgcp_endpoint *endp);
/* gain settings */
int s_digital_inp_gain = 31;
int s_digital_out_gain = 31;
int s_upstr_agc_enbl = 0;
int s_upstr_adp_rate = 100;
int s_upstr_max_gain = 46;
int s_upstr_target_lvl = 20;
int s_dwnstr_agc_enbl = 0;
int s_dwnstr_adp_rate = 100;
int s_dwnstr_max_gain = 46;
int s_dwnstr_target_lvl = 20;
struct mgcp_ss7_endpoint {
unsigned int port;
int block;
};
#ifndef NO_UNIPORTE
static void mgcp_ss7_do_exec(struct mgcp_ss7 *mgcp, uint8_t type, struct mgcp_endpoint *, uint32_t param);
static void mgcp_ss7_endp_free(struct mgcp_ss7* ss7, int endp);
static void mgcp_ss7_do_exec(struct mgcp_ss7 *mgcp, uint8_t type, uint32_t port, uint32_t param);
static void mgcp_mgw_vty_init();
/* Contains a mapping from UniPorte to the MGCP side of things */
static struct mgcp_endpoint *s_endpoints[240];
static void check_exit(int status)
{
@ -95,7 +76,7 @@ static void check_exit(int status)
}
}
#ifndef NO_UNIPORTE
static void Force_Poll( int milliseconds )
{
int timeout = 0;
@ -163,10 +144,10 @@ static int uniporte_events(unsigned long port, EventTypeT event,
char text[128];
ManObjectInfoPtr info;
DataReceiveInfoPtr dataInfo;
struct mgcp_endpoint *endp;
int i;
ToneDetectionPtr tones;
/* Don't print output when we receive data or complete
* sending data. That would be too verbose.
*/
@ -181,10 +162,20 @@ static int uniporte_events(unsigned long port, EventTypeT event,
puts(text);
/* update the mgcp state */
int mgcp_endp = FROM_MGW_PORT(port);
if (s_ss7->mgw_end[mgcp_endp].block != 1)
if (port >= ARRAY_SIZE(s_endpoints)) {
fprintf(stderr, "The port is bigger than we can manage.\n");
return 0;
}
endp = s_endpoints[port];
if (!endp) {
fprintf(stderr, "Unexpected event on port %d\n", port);
return 0;
}
if (endp->block_processing != 1)
fprintf(stderr, "State change on a non blocked port. ERROR.\n");
s_ss7->mgw_end[mgcp_endp].block = 0;
endp->block_processing = 0;
}
}
else if ( event == Event_MANAGED_OBJECT_SET_COMPLETE ) {
@ -267,8 +258,6 @@ static void* start_uniporte(void *_ss7) {
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);
@ -282,10 +271,10 @@ static void* start_uniporte(void *_ss7) {
start_over:
/* handle items that are currently blocked */
llist_for_each_entry_safe(cmd, tmp, &blocked, entry) {
if (ss7->mgw_end[cmd->port].block)
if (cmd->endp->block_processing)
continue;
mgcp_ss7_do_exec(ss7, cmd->type, cmd->port, cmd->param);
mgcp_ss7_do_exec(ss7, cmd->type, cmd->endp, cmd->param);
llist_del(&cmd->entry);
free(cmd);
@ -295,13 +284,13 @@ start_over:
}
llist_for_each_entry_safe(cmd, tmp, ss7->cmd_queue->main_head, entry) {
if (ss7->mgw_end[cmd->port].block) {
if (cmd->endp->block_processing) {
llist_del(&cmd->entry);
llist_add_tail(&cmd->entry, &blocked);
continue;
}
mgcp_ss7_do_exec(ss7, cmd->type, cmd->port, cmd->param);
mgcp_ss7_do_exec(ss7, cmd->type, cmd->endp, cmd->param);
llist_del(&cmd->entry);
free(cmd);
@ -315,11 +304,9 @@ start_over:
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);
@ -335,137 +322,145 @@ static void update_mute_status(int mgw_port, int conn_mode)
} 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)
static void allocate_endp(struct mgcp_ss7 *ss7, struct mgcp_endpoint *endp)
{
int mgw_port;
int mgw_port, timeslot, multiplex;
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);
mgcp_endpoint_to_timeslot(ENDPOINT_NUMBER(endp), &multiplex, &timeslot);
mgw_port = timeslot - endp->tcfg->endp_offset;
fprintf(stderr, "TEST: Going to use MGW: %d for MUL: %d TS: %d\n",
mgw_port, multiplex, timeslot);
endp->audio_port = MtnSaAllocate(mgw_port);
if (endp->audio_port == UINT_MAX) {
fprintf(stderr, "Failed to allocate the port: %d\n", ENDPOINT_NUMBER(endp));
return;
}
if (mgw_port != endp->audio_port) {
fprintf(stderr, "Oh... a lot of assumptions are now broken %d %d %s:%d\n",
mgw_port, endp->audio_port, __func__, __LINE__);
}
s_endpoints[endp->audio_port] = endp;
/* Gain settings, apply before switching the port to voice */
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_C_VOICE_INPUT_DIGITAL_GAIN, s_digital_inp_gain, 0);
ManObj_C_VOICE_INPUT_DIGITAL_GAIN, endp->tcfg->digital_inp_gain, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_C_VOICE_OUTPUT_DIGITAL_GAIN, s_digital_out_gain, 0);
ManObj_C_VOICE_OUTPUT_DIGITAL_GAIN, endp->tcfg->digital_out_gain, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_G_US_AGC_ENABLE, s_upstr_agc_enbl, 0);
ManObj_G_US_AGC_ENABLE, endp->tcfg->upstr_agc_enbl, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_G_DS_AGC_ENABLE, s_dwnstr_agc_enbl, 0);
ManObj_G_DS_AGC_ENABLE, endp->tcfg->dwnstr_agc_enbl, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_G_US_ADAPTATION_RATE, s_upstr_adp_rate, 0);
ManObj_G_US_ADAPTATION_RATE, endp->tcfg->upstr_adp_rate, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_G_DS_ADAPTATION_RATE, s_dwnstr_adp_rate, 0);
ManObj_G_DS_ADAPTATION_RATE, endp->tcfg->dwnstr_adp_rate, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_G_US_MAX_APPLIED_GAIN, s_upstr_max_gain, 0);
ManObj_G_US_MAX_APPLIED_GAIN, endp->tcfg->upstr_max_gain, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_G_DS_MAX_APPLIED_GAIN, s_dwnstr_max_gain, 0);
ManObj_G_DS_MAX_APPLIED_GAIN, endp->tcfg->dwnstr_max_gain, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_C_US_TARGET_LEVEL, s_upstr_target_lvl, 0);
ManObj_C_US_TARGET_LEVEL, endp->tcfg->upstr_target_lvl, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_C_US_TARGET_LEVEL, s_dwnstr_target_lvl, 0);
ManObj_C_US_TARGET_LEVEL, endp->tcfg->dwnstr_target_lvl, 0);
/* 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);
ManObj_C_VOICE_RTP_TELEPHONE_EVENT_PT_TX,
endp->tcfg->audio_payload, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_G_RTP_AMR_PAYLOAD_TYPE, ss7->cfg->audio_payload, 0);
ManObj_G_RTP_AMR_PAYLOAD_TYPE,
endp->tcfg->audio_payload, 0);
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_G_RTP_AMR_PAYLOAD_FORMAT, RtpAmrPayloadFormat_OCTET_ALIGNED, 0);
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);
MtnSaSetManObject(mgw_port, ChannelType_PORT,
ManObj_C_VOICE_VAD_CNG, s_vad_enabled, 0);
ManObj_C_VOICE_VAD_CNG, endp->tcfg->vad_enabled, 0);
update_mute_status(mgw_port, mg_endp->conn_mode);
update_mute_status(mgw_port, 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);
mgw_address, endp->bts_end.local_port,
loc_address, endp->bts_end.local_port);
MtnSaConnect(mgw_port, mgw_port);
mgw_endp->block = 1;
endp->block_processing = 1;
}
#endif
static void mgcp_ss7_do_exec(struct mgcp_ss7 *mgcp, uint8_t type, uint32_t port, uint32_t param)
static void mgcp_ss7_do_exec(struct mgcp_ss7 *mgcp, uint8_t type,
struct mgcp_endpoint *mgw_endp, uint32_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);
if (mgw_endp->audio_port != UINT_MAX)
update_mute_status(mgw_endp->audio_port, param);
break;
case MGCP_SS7_DELETE:
if (mgw_endp->port != UINT_MAX) {
rc = MtnSaDisconnect(mgw_endp->port);
if (mgw_endp->audio_port != UINT_MAX) {
rc = MtnSaDisconnect(mgw_endp->audio_port);
if (rc != 0)
fprintf(stderr, "Failed to disconnect port: %u\n", mgw_endp->port);
rc = MtnSaDeallocate(mgw_endp->port);
fprintf(stderr, "Failed to disconnect port: %u\n", mgw_endp->audio_port);
rc = MtnSaDeallocate(mgw_endp->audio_port);
if (rc != 0)
fprintf(stderr, "Failed to deallocate port: %u\n", mgw_endp->port);
mgw_endp->port = UINT_MAX;
mgw_endp->block = 1;
fprintf(stderr, "Failed to deallocate port: %u\n", mgw_endp->audio_port);
mgw_endp->audio_port = UINT_MAX;
mgw_endp->block_processing = 1;
}
break;
case MGCP_SS7_ALLOCATE:
allocate_endp(mgcp, port);
break;
case MGCP_SS7_SHUTDOWN:
MtnSaShutdown();
allocate_endp(mgcp, mgw_endp);
break;
}
#endif
}
#endif
void mgcp_ss7_exec(struct mgcp_ss7 *mgcp, uint8_t type, uint32_t port, uint32_t param)
void mgcp_ss7_exec(struct mgcp_endpoint *endp, int type, uint32_t param)
{
struct mgcp_ss7 *mgcp;
struct mgcp_ss7_cmd *cmd = malloc(sizeof(*cmd));
if (!cmd) {
LOGP(DMGCP, LOGL_ERROR, "Failed to send a command.\n");
return;
}
memset(cmd, 0, sizeof(*cmd));
cmd->type = type;
cmd->port = port;
cmd->endp = endp;
cmd->param = param;
mgcp = endp->tcfg->cfg->data;
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)
static int ss7_allocate_endpoint(struct mgcp_ss7 *ss7, struct mgcp_endpoint *mg_endp)
{
struct mgcp_endpoint *mg_endp;
mg_endp->bts_end.rtp_port = htons(mg_endp->bts_end.local_port);
mg_endp->bts_end.rtcp_port = htons(mg_endp->bts_end.local_port + 1);
mg_endp->bts_end.addr = ss7->cfg->bts_in;
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);
mgcp_ss7_exec(mg_endp, MGCP_SS7_ALLOCATE, 0);
return MGCP_POLICY_CONT;
}
static int ss7_modify_endpoint(struct mgcp_ss7 *ss7, int endp_no, struct mgcp_ss7_endpoint *endp)
static int ss7_modify_endpoint(struct mgcp_ss7 *ss7, struct mgcp_endpoint *mg_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);
mgcp_ss7_exec(mg_endp, MGCP_SS7_MUTE_STATUS, mg_endp->conn_mode);
/*
* Just assume that we have the data now.
@ -476,32 +471,41 @@ static int ss7_modify_endpoint(struct mgcp_ss7 *ss7, int endp_no, struct mgcp_ss
return MGCP_POLICY_CONT;
}
static int ss7_delete_endpoint(struct mgcp_ss7 *ss7, int endp_no, struct mgcp_ss7_endpoint *endp)
static int ss7_delete_endpoint(struct mgcp_ss7 *ss7, struct mgcp_endpoint *endp)
{
mgcp_ss7_endp_free(ss7, endp_no);
mgcp_ss7_endp_free(endp);
return MGCP_POLICY_CONT;
}
static int mgcp_ss7_policy(struct mgcp_config *cfg, int endp_no, int state, const char *trans)
static int mgcp_ss7_policy(struct mgcp_trunk_config *tcfg, int endp_no, int state, const char *trans)
{
int rc;
int multiplex, timeslot;
struct mgcp_ss7 *ss7;
struct mgcp_ss7_endpoint *endp;
struct mgcp_endpoint *endp;
ss7 = (struct mgcp_ss7 *) cfg->data;
endp = &ss7->mgw_end[endp_no];
mgcp_endpoint_to_timeslot(endp_no, &multiplex, &timeslot);
/* these endpoints are blocked */
if (timeslot == 0 || timeslot >= 0x1F) {
LOGP(DMGCP, LOGL_NOTICE, "Rejecting non voice timeslots %d\n", timeslot);
return MGCP_POLICY_REJECT;
}
endp = &tcfg->endpoints[endp_no];
ss7 = (struct mgcp_ss7 *) tcfg->cfg->data;
/* 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);
rc = ss7_allocate_endpoint(ss7, endp);
break;
case MGCP_ENDP_MDCX:
rc = ss7_modify_endpoint(ss7, endp_no, endp);
rc = ss7_modify_endpoint(ss7, endp);
break;
case MGCP_ENDP_DLCX:
rc = ss7_delete_endpoint(ss7, endp_no, endp);
rc = ss7_delete_endpoint(ss7, endp);
break;
}
@ -620,9 +624,9 @@ static int create_socket(struct mgcp_ss7 *cfg)
return 0;
}
static void mgcp_ss7_endp_free(struct mgcp_ss7 *ss7, int endp)
static void mgcp_ss7_endp_free(struct mgcp_endpoint *endp)
{
mgcp_ss7_exec(ss7, MGCP_SS7_DELETE, endp, 0);
mgcp_ss7_exec(endp, MGCP_SS7_DELETE, 0);
}
static int reset_cb(struct mgcp_config *cfg)
@ -631,25 +635,15 @@ static int reset_cb(struct mgcp_config *cfg)
return 0;
}
static int realloc_cb(struct mgcp_config *cfg, int endp)
static int realloc_cb(struct mgcp_trunk_config *tcfg, int endp_no)
{
mgcp_ss7_endp_free((struct mgcp_ss7 *) cfg->data, endp);
struct mgcp_endpoint *endp = &tcfg->endpoints[endp_no];
mgcp_ss7_endp_free(endp);
return 0;
}
static void mgcp_ss7_set_default(struct mgcp_config *cfg)
{
/* do not attempt to allocate call ids */
cfg->early_bind = 1;
talloc_free(cfg->audio_name);
cfg->audio_payload = 126;
cfg->audio_name = talloc_strdup(cfg, "AMR/8000");
}
static struct mgcp_ss7 *mgcp_ss7_init(struct mgcp_config *cfg)
{
int i;
struct mgcp_ss7 *conf = talloc_zero(NULL, struct mgcp_ss7);
if (!conf)
return NULL;
@ -665,44 +659,13 @@ static struct mgcp_ss7 *mgcp_ss7_init(struct mgcp_config *cfg)
conf->cfg->realloc_cb = realloc_cb;
conf->cfg->data = conf;
if (mgcp_endpoints_allocate(conf->cfg) != 0) {
LOGP(DMGCP, LOGL_ERROR, "Failed to allocate endpoints: %d\n",
cfg->number_endpoints - 1);
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 = 1; 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;
}
}
/* Now do the init of the trunks */
conf->cmd_queue = thread_notifier_alloc();
if (!conf->cmd_queue) {
@ -719,33 +682,30 @@ static struct mgcp_ss7 *mgcp_ss7_init(struct mgcp_config *cfg)
return conf;
}
void mgcp_ss7_free(struct mgcp_ss7 *mgcp)
static void free_trunk(struct mgcp_trunk_config *trunk)
{
/* 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);
int i;
for (i = 1; i < trunk->number_endpoints; ++i) {
struct mgcp_endpoint *endp = &trunk->endpoints[i];
mgcp_ss7_endp_free(endp);
mgcp_free_endp(endp);
}
}
void mgcp_ss7_reset(struct mgcp_ss7 *mgcp)
{
int i;
struct mgcp_trunk_config *trunk;
if (!mgcp)
return;
LOGP(DMGCP, LOGL_INFO, "Resetting all endpoints.\n");
/* free UniPorted and MGCP data */
for (i = 1; i < mgcp->cfg->number_endpoints; ++i) {
mgcp_ss7_endp_free(mgcp, i);
mgcp_free_endp(&mgcp->cfg->endpoints[i]);
}
/* free UniPorte and MGCP data */
free_trunk(&mgcp->cfg->trunk);
llist_for_each_entry(trunk, &mgcp->cfg->trunks, entry)
free_trunk(trunk);
}
static void print_help()
@ -827,10 +787,9 @@ int main(int argc, char **argv)
return -1;
}
mgcp_ss7_set_default(g_cfg);
mgcp_vty_set_config(g_cfg);
if (vty_read_config_file(config_file, NULL) < 0) {
fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
if (mgcp_parse_config(config_file, g_cfg) != 0) {
LOGP(DMGCP, LOGL_ERROR,
"Failed to parse the config file: '%s'\n", config_file);
return -1;
}
@ -838,9 +797,8 @@ int main(int argc, char **argv)
if (rc < 0)
return rc;
printf("Creating MGCP MGW with endpoints: %d ip: %s mgw: %s rtp-base: %d payload: %d\n",
g_cfg->number_endpoints - 1, g_cfg->local_ip, g_cfg->bts_ip,
g_cfg->rtp_base_port, g_cfg->audio_payload);
printf("Creating MGCP MGW ip: %s mgw: %s\n",
g_cfg->local_ip, g_cfg->bts_ip);
mgcp = mgcp_ss7_init(g_cfg);
if (!mgcp) {
@ -853,171 +811,3 @@ int main(int argc, char **argv)
return 0;
}
static struct vty_app_info vty_info = {
.name = "mgcp_ss7",
.version = "0.0.1",
.go_parent_cb = NULL,
};
void logging_vty_add_cmds(void);
DEFUN(cfg_mgcp_vad, cfg_mgcp_vad_cmd,
"vad (enabled|disabled)",
"Enable the Voice Activity Detection\n"
"Enable\n" "Disable\n")
{
if (argv[0][0] == 'e')
s_vad_enabled = 1;
else
s_vad_enabled = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_realloc, cfg_mgcp_realloc_cmd,
"force-realloc (0|1)",
"Force the reallocation of an endpoint\n"
"Disable\n" "Enable\n")
{
g_cfg->force_realloc = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_inp_dig_gain, cfg_mgcp_inp_dig_gain_cmd,
"input-digital-gain <0-62>",
"Static Digital Input Gain\n"
"Gain value")
{
s_digital_inp_gain = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_out_dig_gain, cfg_mgcp_out_dig_gain_cmd,
"outut-digital-gain <0-62>",
"Static Digital Output Gain\n"
"Gain value")
{
s_digital_out_gain = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_upstr_agc, cfg_mgcp_upstr_agc_cmd,
"upstream-automatic-gain (0|1)",
"Enable automatic gain control on upstream\n"
"Disable\n" "Enabled\n")
{
s_upstr_agc_enbl = argv[0][0] == '1';
return CMD_SUCCESS;
}
DEFUN(cfg_mgc_upstr_adp, cfg_mgcp_upstr_adp_cmd,
"upstream-adaptiton-rate <1-128>",
"Set the adaption rate in (dB/sec) * 10\n"
"Range\n")
{
s_upstr_adp_rate = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_upstr_max_gain, cfg_mgcp_upstr_max_gain_cmd,
"upstream-max-applied-gain <0-49>",
"Maximum applied gain from -31db to 18db\n"
"Gain level\n")
{
s_upstr_max_gain = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_upstr_target, cfg_mgcp_upstr_target_cmd,
"upstream-target-level <6-37>",
"Set the desired level in db\n"
"Desired lievel\n")
{
s_upstr_target_lvl = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_dwnstr_agc, cfg_mgcp_dwnstr_agc_cmd,
"downstream-automatic-gain (0|1)",
"Enable automatic gain control on downstream\n"
"Disable\n" "Enabled\n")
{
s_dwnstr_agc_enbl = argv[0][0] == '1';
return CMD_SUCCESS;
}
DEFUN(cfg_mgc_dwnstr_adp, cfg_mgcp_dwnstr_adp_cmd,
"downstream-adaptation-rate <1-128>",
"Set the adaption rate in (dB/sec) * 10\n"
"Range\n")
{
s_dwnstr_adp_rate = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_dwnstr_max_gain, cfg_mgcp_dwnstr_max_gain_cmd,
"downstream-max-applied-gain <0-49>",
"Maximum applied gain from -31db to 18db\n"
"Gain level\n")
{
s_dwnstr_max_gain = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_dwnstr_target, cfg_mgcp_dwnstr_target_cmd,
"downstream-target-level <6-37>",
"Set the desired level in db\n"
"Desired lievel\n")
{
s_dwnstr_target_lvl = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(endpoint_offset, endpoint_offset_cmd,
"endpoint-offset <-60-60>",
"Offset to the CIC map\n" "Value to set\n")
{
s_endp_offset = atoi(argv[0]);
return CMD_SUCCESS;
}
void mgcp_write_extra(struct vty *vty)
{
vty_out(vty, " force-realloc %d%s", g_cfg->force_realloc, VTY_NEWLINE);
vty_out(vty, " vad %s%s", s_vad_enabled ? "enabled" : "disabled", VTY_NEWLINE);
vty_out(vty, " input-digital-gain %d%s", s_digital_inp_gain, VTY_NEWLINE);
vty_out(vty, " output-digital-gain %d%s", s_digital_out_gain, VTY_NEWLINE);
vty_out(vty, " upstream-automatic-gain %d%s", s_upstr_agc_enbl, VTY_NEWLINE);
vty_out(vty, " upstream-adaptation-rate %d%s", s_upstr_adp_rate, VTY_NEWLINE);
vty_out(vty, " upstream-max-applied-gain %d%s", s_upstr_max_gain, VTY_NEWLINE);
vty_out(vty, " upstream-target-level %d%s", s_upstr_target_lvl, VTY_NEWLINE);
vty_out(vty, " downstream-automatic-gain %d%s", s_dwnstr_agc_enbl, VTY_NEWLINE);
vty_out(vty, " downstream-adaptation-rate %d%s", s_dwnstr_adp_rate, VTY_NEWLINE);
vty_out(vty, " downstream-max-applied-gain %d%s", s_dwnstr_max_gain, VTY_NEWLINE);
vty_out(vty, " downstream-target-level %d%s", s_dwnstr_target_lvl, VTY_NEWLINE);
vty_out(vty, " endpoint-offset %d%s", s_endp_offset, VTY_NEWLINE);
}
static void mgcp_mgw_vty_init(void)
{
cmd_init(1);
vty_init(&vty_info);
logging_vty_add_cmds();
mgcp_vty_init();
install_element(MGCP_NODE, &cfg_mgcp_vad_cmd);
install_element(MGCP_NODE, &cfg_mgcp_realloc_cmd);
install_element(MGCP_NODE, &cfg_mgcp_inp_dig_gain_cmd);
install_element(MGCP_NODE, &cfg_mgcp_out_dig_gain_cmd);
install_element(MGCP_NODE, &cfg_mgcp_upstr_agc_cmd);
install_element(MGCP_NODE, &cfg_mgcp_upstr_adp_cmd);
install_element(MGCP_NODE, &cfg_mgcp_upstr_max_gain_cmd);
install_element(MGCP_NODE, &cfg_mgcp_upstr_target_cmd);
install_element(MGCP_NODE, &cfg_mgcp_dwnstr_agc_cmd);
install_element(MGCP_NODE, &cfg_mgcp_dwnstr_adp_cmd);
install_element(MGCP_NODE, &cfg_mgcp_dwnstr_max_gain_cmd);
install_element(MGCP_NODE, &cfg_mgcp_dwnstr_target_cmd);
install_element(MGCP_NODE, &endpoint_offset_cmd);
}
const char *openbsc_copyright = "";

423
src/mgcp_ss7_vty.c Normal file
View File

@ -0,0 +1,423 @@
/* Extra vty handling for the SS7 code */
/*
* (C) 2010-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010-2011 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 Affero General Public License as published by
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <mgcp_ss7.h>
#include <ss7_vty.h>
#include <mgcp/mgcp.h>
#include <stdlib.h>
extern struct mgcp_config *g_cfg;
static struct vty_app_info vty_info = {
.name = "mgcp_ss7",
.version = "0.0.1",
.go_parent_cb = NULL,
};
void logging_vty_add_cmds(void);
DEFUN(cfg_mgcp_configure, cfg_mgcp_configure_cmd,
"configure-trunks (0|1)",
"Reconfigure the Trunk Configuration\n" "Reconfigure\n" "Keep\n")
{
g_cfg->configure_trunks = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_vad, cfg_mgcp_vad_cmd,
"vad (enabled|disabled)",
"Enable the Voice Activity Detection\n"
"Enable\n" "Disable\n")
{
if (argv[0][0] == 'e')
g_cfg->trunk.vad_enabled = 1;
else
g_cfg->trunk.vad_enabled = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_realloc, cfg_mgcp_realloc_cmd,
"force-realloc (0|1)",
"Force the reallocation of an endpoint\n"
"Disable\n" "Enable\n")
{
g_cfg->trunk.force_realloc = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_inp_dig_gain, cfg_mgcp_inp_dig_gain_cmd,
"input-digital-gain <0-62>",
"Static Digital Input Gain\n"
"Gain value")
{
g_cfg->trunk.digital_inp_gain = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_out_dig_gain, cfg_mgcp_out_dig_gain_cmd,
"outut-digital-gain <0-62>",
"Static Digital Output Gain\n"
"Gain value")
{
g_cfg->trunk.digital_out_gain = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_upstr_agc, cfg_mgcp_upstr_agc_cmd,
"upstream-automatic-gain (0|1)",
"Enable automatic gain control on upstream\n"
"Disable\n" "Enabled\n")
{
g_cfg->trunk.upstr_agc_enbl = argv[0][0] == '1';
return CMD_SUCCESS;
}
DEFUN(cfg_mgc_upstr_adp, cfg_mgcp_upstr_adp_cmd,
"upstream-adaptiton-rate <1-128>",
"Set the adaption rate in (dB/sec) * 10\n"
"Range\n")
{
g_cfg->trunk.upstr_adp_rate = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_upstr_max_gain, cfg_mgcp_upstr_max_gain_cmd,
"upstream-max-applied-gain <0-49>",
"Maximum applied gain from -31db to 18db\n"
"Gain level\n")
{
g_cfg->trunk.upstr_max_gain = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_upstr_target, cfg_mgcp_upstr_target_cmd,
"upstream-target-level <6-37>",
"Set the desired level in db\n"
"Desired lievel\n")
{
g_cfg->trunk.upstr_target_lvl = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_dwnstr_agc, cfg_mgcp_dwnstr_agc_cmd,
"downstream-automatic-gain (0|1)",
"Enable automatic gain control on downstream\n"
"Disable\n" "Enabled\n")
{
g_cfg->trunk.dwnstr_agc_enbl = argv[0][0] == '1';
return CMD_SUCCESS;
}
DEFUN(cfg_mgc_dwnstr_adp, cfg_mgcp_dwnstr_adp_cmd,
"downstream-adaptation-rate <1-128>",
"Set the adaption rate in (dB/sec) * 10\n"
"Range\n")
{
g_cfg->trunk.dwnstr_adp_rate = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_dwnstr_max_gain, cfg_mgcp_dwnstr_max_gain_cmd,
"downstream-max-applied-gain <0-49>",
"Maximum applied gain from -31db to 18db\n"
"Gain level\n")
{
g_cfg->trunk.dwnstr_max_gain = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_dwnstr_target, cfg_mgcp_dwnstr_target_cmd,
"downstream-target-level <6-37>",
"Set the desired level in db\n"
"Desired lievel\n")
{
g_cfg->trunk.dwnstr_target_lvl = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_endp_offset, cfg_mgcp_endp_offset_cmd,
"endpoint-offset <-60-60>",
"Offset to the CIC map\n" "Value to set\n")
{
g_cfg->trunk.endp_offset = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_target_trunk, cfg_mgcp_target_trunk_cmd,
"target-trunk-start <1-24>",
"Map the virtual trunk to start here\n" "Trunk Nr\n")
{
g_cfg->trunk.target_trunk_start = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_vad, cfg_trunk_vad_cmd,
"vad (enabled|disabled)",
"Enable the Voice Activity Detection\n"
"Enable\n" "Disable\n")
{
struct mgcp_trunk_config *trunk = vty->index;
if (argv[0][0] == 'e')
trunk->vad_enabled = 1;
else
trunk->vad_enabled = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_realloc, cfg_trunk_realloc_cmd,
"force-realloc (0|1)",
"Force the reallocation of an endpoint\n"
"Disable\n" "Enable\n")
{
struct mgcp_trunk_config *trunk = vty->index;
trunk->force_realloc = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_inp_dig_gain, cfg_trunk_inp_dig_gain_cmd,
"input-digital-gain <0-62>",
"Static Digital Input Gain\n"
"Gain value")
{
struct mgcp_trunk_config *trunk = vty->index;
trunk->digital_inp_gain = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_out_dig_gain, cfg_trunk_out_dig_gain_cmd,
"outut-digital-gain <0-62>",
"Static Digital Output Gain\n"
"Gain value")
{
struct mgcp_trunk_config *trunk = vty->index;
trunk->digital_out_gain = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_upstr_agc, cfg_trunk_upstr_agc_cmd,
"upstream-automatic-gain (0|1)",
"Enable automatic gain control on upstream\n"
"Disable\n" "Enabled\n")
{
struct mgcp_trunk_config *trunk = vty->index;
trunk->upstr_agc_enbl = argv[0][0] == '1';
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_upstr_adp, cfg_trunk_upstr_adp_cmd,
"upstream-adaptiton-rate <1-128>",
"Set the adaption rate in (dB/sec) * 10\n"
"Range\n")
{
struct mgcp_trunk_config *trunk = vty->index;
trunk->upstr_adp_rate = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_upstr_max_gain, cfg_trunk_upstr_max_gain_cmd,
"upstream-max-applied-gain <0-49>",
"Maximum applied gain from -31db to 18db\n"
"Gain level\n")
{
struct mgcp_trunk_config *trunk = vty->index;
trunk->upstr_max_gain = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_upstr_target, cfg_trunk_upstr_target_cmd,
"upstream-target-level <6-37>",
"Set the desired level in db\n"
"Desired lievel\n")
{
struct mgcp_trunk_config *trunk = vty->index;
trunk->upstr_target_lvl = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_dwnstr_agc, cfg_trunk_dwnstr_agc_cmd,
"downstream-automatic-gain (0|1)",
"Enable automatic gain control on downstream\n"
"Disable\n" "Enabled\n")
{
struct mgcp_trunk_config *trunk = vty->index;
trunk->dwnstr_agc_enbl = argv[0][0] == '1';
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_dwnstr_adp, cfg_trunk_dwnstr_adp_cmd,
"downstream-adaptation-rate <1-128>",
"Set the adaption rate in (dB/sec) * 10\n"
"Range\n")
{
struct mgcp_trunk_config *trunk = vty->index;
trunk->dwnstr_adp_rate = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_dwnstr_max_gain, cfg_trunk_dwnstr_max_gain_cmd,
"downstream-max-applied-gain <0-49>",
"Maximum applied gain from -31db to 18db\n"
"Gain level\n")
{
struct mgcp_trunk_config *trunk = vty->index;
trunk->dwnstr_max_gain = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_dwnstr_target, cfg_trunk_dwnstr_target_cmd,
"downstream-target-level <6-37>",
"Set the desired level in db\n"
"Desired lievel\n")
{
struct mgcp_trunk_config *trunk = vty->index;
trunk->dwnstr_target_lvl = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_endp_offset, cfg_trunk_endp_offset_cmd,
"endpoint-offset <-60-60>",
"Offset to the CIC map\n" "Value to set\n")
{
struct mgcp_trunk_config *trunk = vty->index;
trunk->endp_offset = atoi(argv[0]);
return CMD_SUCCESS;
}
void mgcp_write_extra(struct vty *vty, struct mgcp_config *cfg)
{
vty_out(vty, " configure-trunks %d%s",
cfg->configure_trunks, VTY_NEWLINE);
vty_out(vty, " force-realloc %d%s",
cfg->trunk.force_realloc, VTY_NEWLINE);
vty_out(vty, " vad %s%s",
cfg->trunk.vad_enabled ? "enabled" : "disabled", VTY_NEWLINE);
vty_out(vty, " input-digital-gain %d%s",
cfg->trunk.digital_inp_gain, VTY_NEWLINE);
vty_out(vty, " output-digital-gain %d%s",
cfg->trunk.digital_out_gain, VTY_NEWLINE);
vty_out(vty, " upstream-automatic-gain %d%s",
cfg->trunk.upstr_agc_enbl, VTY_NEWLINE);
vty_out(vty, " upstream-adaptation-rate %d%s",
cfg->trunk.upstr_adp_rate, VTY_NEWLINE);
vty_out(vty, " upstream-max-applied-gain %d%s",
cfg->trunk.upstr_max_gain, VTY_NEWLINE);
vty_out(vty, " upstream-target-level %d%s",
cfg->trunk.upstr_target_lvl, VTY_NEWLINE);
vty_out(vty, " downstream-automatic-gain %d%s",
cfg->trunk.dwnstr_agc_enbl, VTY_NEWLINE);
vty_out(vty, " downstream-adaptation-rate %d%s",
cfg->trunk.dwnstr_adp_rate, VTY_NEWLINE);
vty_out(vty, " downstream-max-applied-gain %d%s",
cfg->trunk.dwnstr_max_gain, VTY_NEWLINE);
vty_out(vty, " downstream-target-level %d%s",
cfg->trunk.dwnstr_target_lvl, VTY_NEWLINE);
vty_out(vty, " endpoint-offset %d%s",
cfg->trunk.endp_offset, VTY_NEWLINE);
vty_out(vty, " target-trunk-start %d%s",
cfg->trunk.target_trunk_start, VTY_NEWLINE);
}
void mgcp_write_trunk_extra(struct vty *vty, struct mgcp_trunk_config *trunk)
{
vty_out(vty, " force-realloc %d%s",
trunk->force_realloc, VTY_NEWLINE);
vty_out(vty, " vad %s%s",
trunk->vad_enabled ? "enabled" : "disabled", VTY_NEWLINE);
vty_out(vty, " input-digital-gain %d%s",
trunk->digital_inp_gain, VTY_NEWLINE);
vty_out(vty, " output-digital-gain %d%s",
trunk->digital_out_gain, VTY_NEWLINE);
vty_out(vty, " upstream-automatic-gain %d%s",
trunk->upstr_agc_enbl, VTY_NEWLINE);
vty_out(vty, " upstream-adaptation-rate %d%s",
trunk->upstr_adp_rate, VTY_NEWLINE);
vty_out(vty, " upstream-max-applied-gain %d%s",
trunk->upstr_max_gain, VTY_NEWLINE);
vty_out(vty, " upstream-target-level %d%s",
trunk->upstr_target_lvl, VTY_NEWLINE);
vty_out(vty, " downstream-automatic-gain %d%s",
trunk->dwnstr_agc_enbl, VTY_NEWLINE);
vty_out(vty, " downstream-adaptation-rate %d%s",
trunk->dwnstr_adp_rate, VTY_NEWLINE);
vty_out(vty, " downstream-max-applied-gain %d%s",
trunk->dwnstr_max_gain, VTY_NEWLINE);
vty_out(vty, " downstream-target-level %d%s",
trunk->dwnstr_target_lvl, VTY_NEWLINE);
vty_out(vty, " endpoint-offset %d%s",
trunk->endp_offset, VTY_NEWLINE);
}
void mgcp_mgw_vty_init(void)
{
cmd_init(1);
vty_init(&vty_info);
logging_vty_add_cmds();
mgcp_vty_init();
install_element(MGCP_NODE, &cfg_mgcp_configure_cmd);
install_element(MGCP_NODE, &cfg_mgcp_vad_cmd);
install_element(MGCP_NODE, &cfg_mgcp_realloc_cmd);
install_element(MGCP_NODE, &cfg_mgcp_inp_dig_gain_cmd);
install_element(MGCP_NODE, &cfg_mgcp_out_dig_gain_cmd);
install_element(MGCP_NODE, &cfg_mgcp_upstr_agc_cmd);
install_element(MGCP_NODE, &cfg_mgcp_upstr_adp_cmd);
install_element(MGCP_NODE, &cfg_mgcp_upstr_max_gain_cmd);
install_element(MGCP_NODE, &cfg_mgcp_upstr_target_cmd);
install_element(MGCP_NODE, &cfg_mgcp_dwnstr_agc_cmd);
install_element(MGCP_NODE, &cfg_mgcp_dwnstr_adp_cmd);
install_element(MGCP_NODE, &cfg_mgcp_dwnstr_max_gain_cmd);
install_element(MGCP_NODE, &cfg_mgcp_dwnstr_target_cmd);
install_element(MGCP_NODE, &cfg_mgcp_endp_offset_cmd);
install_element(MGCP_NODE, &cfg_mgcp_target_trunk_cmd);
install_element(TRUNK_NODE, &cfg_trunk_vad_cmd);
install_element(TRUNK_NODE, &cfg_trunk_realloc_cmd);
install_element(TRUNK_NODE, &cfg_trunk_inp_dig_gain_cmd);
install_element(TRUNK_NODE, &cfg_trunk_out_dig_gain_cmd);
install_element(TRUNK_NODE, &cfg_trunk_upstr_agc_cmd);
install_element(TRUNK_NODE, &cfg_trunk_upstr_adp_cmd);
install_element(TRUNK_NODE, &cfg_trunk_upstr_max_gain_cmd);
install_element(TRUNK_NODE, &cfg_trunk_upstr_target_cmd);
install_element(TRUNK_NODE, &cfg_trunk_dwnstr_agc_cmd);
install_element(TRUNK_NODE, &cfg_trunk_dwnstr_adp_cmd);
install_element(TRUNK_NODE, &cfg_trunk_dwnstr_max_gain_cmd);
install_element(TRUNK_NODE, &cfg_trunk_dwnstr_target_cmd);
install_element(TRUNK_NODE, &cfg_trunk_endp_offset_cmd);
}
const char *openbsc_copyright = "";