Compare commits

...

25 Commits

Author SHA1 Message Date
Stefan Sperling
e3c2eaeec0 add a 'show mgcp endpoint' VTY command
Change-Id: I5330e697ec34bf215de91d44209048a8dc226d51
Related: OS#2660
2018-06-26 15:26:28 +02:00
Philipp Maier
704c4f0adf client: add features to generate and parse codec information
The current implementation does not support any way to influence the
codec that is negotiated via SDP or LCO. The client statically
negotitates AMR on an invalid payload type number. Also we ignore
any codec information in the responses.

- Add struct members to allow setting of user defined codec information.
- Add struct members to retrieve parsed codec info from responses.
- Add code to generate codec information in SDP
- Add code to parse SDP codec info in MGCP responses

Change-Id: I78e72d41b73acfcb40599a0ff4823f17c3642059
Related: OS#2728
Related: OS#3334
2018-06-23 11:39:48 +00:00
Philipp Maier
bc0346e080 mgw: clean up codec negotiation (sdp)
The codec negotiation via SDP is currently in a neglected state. Also
osmo-mgw does some kind of codec decision wile the SDP is parsed, the
result is information for one codec, even when there are multiple codecs
negotiated. This is problematic because we loose all information about
alternate codecs while we parse. This should be untangled and the
information should be presevered. Also we are not really capable
picking a default. Wehen we do not supply any codec information (not
even LCO), then we should pick a sane default codec.

- separate the codec decision from the sdp parser and concentrate
  codec related code in a separate c file
- add support for multiple codecs in one SDP negotiation
- do not initalize "magic" codec defaults during conn allocation
- do not allow invalid payload types, especially not 255. When
  someone tries to select an invalid payload type, do not fail
  hard, just pick a sane default.
- handle the codec decision in protocol.c, pick a sane default
  codec when no (valid) codec has been negotiated (no LCO, no SDP)

Change-Id: If730d022ba6bdb217ad4e20b3fbbd1114dbb4b8f
Closes: OS#2658
Related: OS#3114
Related: OS#2728
2018-06-23 11:39:44 +00:00
Neels Hofmeyr
5928dc9345 mgcp_client_fsm: improve error logging
Change-Id: I2feefaeefc2d71b64714585ef8137afbb4055b7e
2018-06-15 04:33:37 +02:00
Neels Hofmeyr
04da5e5e98 mgcp-client: add mgcp_conn_get_ci()
Return the CI string allocated by the MGW and sent back during CRCX ACK.

So far the CI that identifies one connection of an MGW endpoint is "hidden"
behind mgcp_conn_* API. This CI string is however very interesting, for
logging, to be able to correlate with MGCP messages in network traces.

For osmo-bsc, there is an upcoming mgw_endpoint_fsm that will log the CI string
using this function.

Change-Id: I0c802c0cc3fa0aae9558bd7f15aad1cb9a8b12b2
2018-06-12 21:56:12 +02:00
Philipp Maier
54b4f82f91 cosmetic: fix typo
Change-Id: I1df5ff642b3744771836dea82f9d0b4ad6749bc5
2018-06-07 07:01:06 +00:00
Philipp Maier
3d7b58d77a protocol: reject illegal lco options
At the moment osmo-mgw will accept multiple lco options. (e.g.
p:10, a:PCMU, p:10) If LCO appear multiple times, than the first
appearance of will be parsed and used, all following appearances
will be ignored. However, having multiple appearances of LCO is
illegal and affected requests should be rejected. Also osmo-mgw
should reject illegal formatted LCO strings

- make sure that multiple appearances of LCOs will be rejected
- make sure that illegal formated LCOs are rejected
- add testcases with garbeled LCO and valid LCO examples

Change-Id: Iae2fddfa5f2bcfc952f8ab217b3056694e5f7812
Closes: OS#3119
2018-06-06 16:41:04 +02:00
Philipp Maier
604410cd13 protocol: do not change LCO, when no LCO are present
In the current implementation the LCO parameters are reset. This means
that an MDCX without LCO will reset the LCO that have previously set
via CRCX. But according to RFC 3435 6.8 LocalConnectionOptions, the
LCO parameters should be preserved or left at their defaults if missing.

- Make sure LCO are retained if no LCO string is present.
- Also preserve the values of individual parameters if missing.

Change-Id: Ia0d73f61516618317dcd1d49384818fd8de27aa6
2018-06-06 10:36:48 +02:00
Philipp Maier
b340f90c9e conn: call talloc_free before setting the pointer to NULL
in mgcp_rtp_codec_init() tallo_free is called after codec->subtype_name
and codec->audio_name are set to NULL. So talloc_free() always sees
NULL-pointers and never frees anything. This may cause a memory leak.

- call talloc_free() first, then set pointers to NULL

Change-Id: I7373819c3689d34811846f6f48f27568297b26e4
2018-06-05 07:23:26 +00:00
Philipp Maier
3c8ccb6724 cosmetic: fix log output
"unable to create connection resource error" sounds a bit strange.
Lets just output "unable to create connection".

Change-Id: Ibef16b455f2e122c8e5ff95240c4d7a654c56a39
2018-06-04 10:01:16 +02:00
Harald Welte
d4e6aa42ca cosmetic: fix typo in log message: 'abrupt' instead of 'aprupt'
Change-Id: Ib4d8864baf538ec5871f42fa717eba3b7da9f48e
2018-06-02 18:07:40 +02:00
Philipp Maier
e6df0e47e7 mgcp_network: do not log destination invalid ip/port as error
It is legal to create connection without setting the destination
ip and port (this usually done later through MDCX). However, if
some other connection tries to deliver an RTP packet through a
a half open connection, then the fact that no destination ip is
set is logged as error even if it is a pretty normal situation.

- Check if destination ip and port are set to zero. If yes, we
  assume that the destination connection details are intentionally
  not set yet. Only when one value is set and the other one not,
  we log an error. Otherweise we log a message to debug.

Change-Id: If96e5a60b8ab92259d3bddaa143121893bb6c525
Related OS#3104
2018-05-29 14:09:13 +02:00
Philipp Maier
54eb0e1204 client: do not start connections in loopback mode
Starting connections in loopback bode may cause confusion at the
receiving end when the connection is switched from looback into
an actual send-receive connection. The reason for this is by this
the SSRC of the RTP stream will suddenly change. For the majority
of usecases it is not necessary to loopback the incomming packets
back to the receiver in the beginning. So lets use receive-only
as a safe default.

- use MGCP_CONN_RECV_ONLY instead of MGCP_CONN_RECV_LOOPBACK

Change-Id: I44178434ee497bc1d5e9d5f6d92c13c1a09ae241
Related: OS#3104
2018-05-29 09:55:42 +02:00
Philipp Maier
b38fb8911f protocol: Try whole port range on port allocation
The function allocate_port tryes at least 200 different ports when
a new port is allocated. Since after every allocation the port
number is incremented the allocation should be able to allocate
a port with the first attempt. However, the number 200 is an
arbitrary number and it will not cover the whole port range in
most cases.

- Make sure that in the worst case at each port in the range
  is tryed once, not only the next 200

Change-Id: Ic47f09869eaddd4aea817bb2517362883d65d029
Related: OS#2825
2018-05-25 10:04:59 +00:00
Philipp Maier
1b3a385b9d sdp: remove circular inclusion
The header file mgcp_sdp.h includes itsself.

- remove unnecessary circular inclusion

Change-Id: I816c6b922641c0b58053714244ada22a75781956
2018-05-25 11:45:06 +02:00
Philipp Maier
dbd70c7b68 sdp: remove unused alt_codec field from struct mgcp_rtp_end
The alt_codec field is not used anywhere in the code

- remove unused alt_codec field

Change-Id: I5ff2899e3e61f33eb86f284b50ad8a94a949ed16
Related: OS#3114
2018-05-25 11:07:31 +02:00
Philipp Maier
a19547b7a1 vty: clean up rtp port-range command
The VTY command that sets the RTP port range does not check if the data
entered by the user actually makes sens. Also it allwos to configur a
range that starts at 0.

- Make sure 0 can not be used as start or end of the range
- make sure the end port number is always greater then the begin
  port number
- Autocorrect uneaven port range beginnings to one port number before to
  ensure the range starts at an even port number
- Autocorrect even port range ends to the next odd port number to
  ensure the range ends at an odd port number.

Change-Id: Ib1312acba4f03f378594dbbeb4f31afd891d68d7
Related: OS#2825
2018-05-24 10:21:31 +02:00
Philipp Maier
06823731d8 mgcp_sdp: correct apidoc of mgcp_parse_sdp_data
The API documentation of mgcp_parse_sdp_data is incorrect.

- correct API documentation

Change-Id: I9906f1dd6811c7092b93d60c9348221fef68cc3e
2018-05-22 10:04:22 +00:00
Neels Hofmeyr
ed1cff5ab9 api doc: fix parameter name for mgcp_conn_create()
Change-Id: Ib6ea230c2e1918bd4e431208610b53e468e534c7
2018-05-19 23:08:35 +02:00
Pau Espin Pedrol
f2321b7a72 mgcp: switch to new osmux output APIs
Older ones are being deprecated as they may generate interleaved
packets.

Change-Id: I0705aa4dc4b02eaff4d6030795243e6720f7fddf
2018-05-19 12:06:12 +00:00
Pau Espin Pedrol
4219904cb2 mgcp: mgcp_osmux: use conn_bts when forwarding pkts from bsc_nat
This commit actually doesn't fix the entire code, since anyway osmux
conns are not supported and mgcp_conn_get_rtp() will return NULL.
However, it makes the code more logical and easier to understand once
somebody refactors the code to make it work again.

Change-Id: Ib57e12e5a36b5842c40673c236907bbcbfc390f3
2018-05-19 12:06:12 +00:00
Pau Espin Pedrol
b2753f2044 legacy-mgcp: switch to new osmux output APIs
Older ones are being deprecated as they may generate interleaved
packets.

This commit is a forward-port of openbsc.git Change-Id
I189564fc63139c15314db8975afd423c7153ea32.

Change-Id: I9b8a19e5b8d62deaa9bbb92d49d99e8c33b7e345
2018-05-19 12:06:12 +00:00
Pau Espin Pedrol
ba61f68137 legacy-mgcp: Add jitter buffer on the uplink receiver
Default usage values are defined in mgcp node, and can be per-BSC
overriden on each bsc node

This commit is a forward-port of openbsc.git Change-Id
Ibf3932adc07442fb5e9c7a06404853f9d0a20959.

Change-Id: Ie19a64ac09f9d51f2434ad0d7925610fc919a90e
2018-05-19 12:06:12 +00:00
Philipp Maier
9e1d164469 stats: use libosmocore rate counter for in/out_stream.err_ts_counter
The two counters: in_stream.err_ts_counter and out_stream.err_ts_counter
are still handcoded. To make them better accessible they should
 be replaced with libosmocore rate counters.

- replace state.in_stream.err_ts_counter with libosmocore rate counter
- replace state.out_stream.err_ts_counter with libosmocore rate counter

Change-Id: I9fbd65bf2f4d1e015a05996db4c1f7ff20be2c95
Related: OS#2517
2018-05-16 11:32:36 +02:00
Philipp Maier
0ec1d4e17c network: independently initalize state->out_stream
The struct state->out_stream.ssrc is initalized by first initalizing
state->in_stream and then copying state->in_stream over to
state->out_stream. This works as long as no pointers to other objects
are added to struct mgcp_rtp_stream_state but we may add pointers to
struct mgcp_rtp_stream_state in the future.

- Initalize out_stream and in_stream independently from each other

Change-Id: I5deb27e609448ee0b9f7034e644ae96f1e57887a
Related: OS#2517
2018-05-16 11:32:12 +02:00
30 changed files with 2005 additions and 420 deletions

View File

@@ -243,6 +243,12 @@ struct mgcp_config {
* message.
*/
uint16_t osmux_dummy;
/* Use a jitterbuffer on the bts-side receiver */
bool bts_use_jibuf;
/* Minimum and maximum buffer size for the jitter buffer, in ms */
uint32_t bts_jitter_delay_min;
uint32_t bts_jitter_delay_max;
};
/* config management */

View File

@@ -25,6 +25,7 @@
#include <string.h>
#include <osmocom/core/select.h>
#include <osmocom/netif/jibuf.h>
#define CI_UNUSED 0
@@ -198,6 +199,14 @@ struct mgcp_endpoint {
uint32_t octets;
} stats;
} osmux;
/* Jitter buffer */
struct osmo_jibuf* bts_jb;
/* Use a jitterbuffer on the bts-side receiver */
bool bts_use_jibuf;
/* Minimum and maximum buffer size for the jitter buffer, in ms */
uint32_t bts_jitter_delay_min;
uint32_t bts_jitter_delay_max;
};
#define for_each_line(line, save) \
@@ -335,3 +344,8 @@ static inline const char *mgcp_bts_src_addr(struct mgcp_endpoint *endp)
}
int mgcp_msg_terminate_nul(struct msgb *msg);
/**
* Internal jitter buffer related
*/
void mgcp_dejitter_udp_send(struct msgb *msg, void *data);

View File

@@ -5,5 +5,6 @@ noinst_HEADERS = \
mgcp_stat.h \
mgcp_endp.h \
mgcp_sdp.h \
mgcp_codec.h \
debug.h \
$(NULL)

View File

@@ -0,0 +1,6 @@
#pragma once
void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name);
int mgcp_codec_decide(struct mgcp_conn_rtp *conn);

View File

@@ -82,4 +82,8 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg)
/* A prefix to denote the virtual trunk (RTP on both ends) */
#define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/"
/* Maximal number of payload types / codecs that can be negotiated via SDP at
* at once. */
#define MGCP_MAX_CODECS 10
#endif

View File

@@ -66,7 +66,7 @@ struct mgcp_endpoint {
/*!< Call identifier string (as supplied by the call agant) */
char *callid;
/*!< Local connection options (see mgcp_intermal.h) */
/*!< Local connection options (see mgcp_internal.h) */
struct mgcp_lco local_options;
/*!< List with connections active on this endpoint */

View File

@@ -28,6 +28,7 @@
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/counter.h>
#include <osmocom/core/rate_ctr.h>
#define CI_UNUSED 0
@@ -45,7 +46,7 @@ struct mgcp_rtp_stream_state {
uint32_t ssrc;
uint16_t last_seq;
uint32_t last_timestamp;
uint32_t err_ts_counter;
struct rate_ctr *err_ts_ctr;
int32_t last_tsdelta;
uint32_t last_arrival_time;
};
@@ -113,13 +114,19 @@ struct mgcp_rtp_end {
/* in network byte order */
int rtp_port, rtcp_port;
/* audio codec information */
struct mgcp_rtp_codec codec;
struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */
/* currently selected audio codec */
struct mgcp_rtp_codec *codec;
/* array with assigned audio codecs to choose from (SDP) */
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
/* number of assigned audio codecs (SDP) */
unsigned int codecs_assigned;
/* per endpoint data */
int frames_per_packet;
uint32_t packet_duration_ms;
int maximum_packet_time; /* -1: not set */
char *fmtp_extra;
/* are we transmitting packets (1) or dropping (0) outbound packets */
int output_enabled;
@@ -202,6 +209,8 @@ struct mgcp_conn_rtp {
uint32_t octets;
} stats;
} osmux;
struct rate_ctr_group *rate_ctr_group;
};
/*! Connection type, specifies which member of the union "u" in mgcp_conn
@@ -280,6 +289,8 @@ static inline int endp_back_channel(int endpoint)
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);
char *get_lco_identifier(const char *options);
int check_local_cx_options(void *ctx, const char *options);
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
struct mgcp_rtp_end *rtp);
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,

View File

@@ -21,15 +21,11 @@
*/
#pragma once
#include <osmocom/mgcp/mgcp_sdp.h>
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn,
struct mgcp_parse_data *p);
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
int payload_type, const char *audio_name);
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
const char *addr);

View File

@@ -26,6 +26,33 @@ struct mgcp_client_conf {
typedef unsigned int mgcp_trans_id_t;
/*! Enumeration of the codec types that mgcp_client is able to handle. */
enum mgcp_codecs {
CODEC_PCMU_8000_1 = 0,
CODEC_GSM_8000_1 = 3,
CODEC_PCMA_8000_1 = 8,
CODEC_G729_8000_1 = 18,
CODEC_GSMEFR_8000_1 = 110,
CODEC_GSMHR_8000_1 = 111,
CODEC_AMR_8000_1 = 112,
CODEC_AMRWB_16000_1 = 113,
};
/* Note: when new codec types are added, the corresponding value strings
* in mgcp_client.c (codec_table) must be updated as well. Enumerations
* in enum mgcp_codecs must correspond to a valid payload type. However,
* this is an internal assumption that is made to avoid lookup tables.
* The API-User should not rely on this coincidence! */
/*! Structure to build a payload type map to allow the defiition custom payload
* types. */
struct ptmap {
/*!< codec for which a payload type number should be defined */
enum mgcp_codecs codec;
/*!< payload type number (96-127) */
unsigned int pt;
};
struct mgcp_response_head {
int response_code;
mgcp_trans_id_t trans_id;
@@ -39,6 +66,11 @@ struct mgcp_response {
struct mgcp_response_head head;
uint16_t audio_port;
char audio_ip[INET_ADDRSTRLEN];
unsigned int ptime;
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
unsigned int codecs_len;
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
};
enum mgcp_verb {
@@ -66,6 +98,11 @@ struct mgcp_msg {
uint16_t audio_port;
char *audio_ip;
enum mgcp_connection_mode conn_mode;
unsigned int ptime;
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
unsigned int codecs_len;
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
};
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
@@ -117,3 +154,9 @@ static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
{
return get_value_string(mgcp_client_connection_mode_strs, mode);
}
enum mgcp_codecs map_str_to_codec(const char *str);
unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
enum mgcp_codecs codec);
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
unsigned int pt);

View File

@@ -25,9 +25,20 @@ struct mgcp_conn_peer {
/*!< CALL ID (unique per connection) */
unsigned int call_id;
/*!< RTP packetization interval (optional) */
unsigned int ptime;
/*!< RTP codec list (optional) */
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
/*!< Number of codecs in RTP codec list (optional) */
unsigned int codecs_len;
};
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
void mgcp_conn_delete(struct osmo_fsm_inst *fi);
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi);

View File

@@ -584,6 +584,36 @@ static int mgcp_send_transcoder(struct mgcp_rtp_end *end,
return rc;
}
void mgcp_dejitter_udp_send(struct msgb *msg, void *data)
{
struct mgcp_rtp_end *rtp_end = (struct mgcp_rtp_end *) data;
int rc = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr,
rtp_end->rtp_port, (char*) msg->data, msg->len);
if (rc != msg->len)
LOGP(DLMGCP, LOGL_ERROR,
"Failed to send data after jitter buffer: %d\n", rc);
msgb_free(msg);
}
static int enqueue_dejitter(struct osmo_jibuf *jb, struct mgcp_rtp_end *rtp_end, char *buf, int len)
{
struct msgb *msg;
msg = msgb_alloc(len, "mgcp-jibuf");
if (!msg)
return -1;
memcpy(msg->data, buf, len);
msgb_put(msg, len);
if (osmo_jibuf_enqueue(jb, msg) < 0) {
rtp_end->dropped_packets += 1;
msgb_free(msg);
}
return len;
}
int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
struct sockaddr_in *addr, char *buf, int rc)
{
@@ -591,6 +621,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
struct mgcp_rtp_end *rtp_end;
struct mgcp_rtp_state *rtp_state;
int tap_idx;
struct osmo_jibuf *jb;
LOGP(DLMGCP, LOGL_DEBUG,
"endpoint %x dest %s tcfg->audio_loop %d endp->conn_mode %d (== loopback: %d)\n",
@@ -612,10 +643,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
rtp_end = &endp->net_end;
rtp_state = &endp->bts_state;
tap_idx = MGCP_TAP_NET_OUT;
jb = endp->bts_jb;
} else {
rtp_end = &endp->bts_end;
rtp_state = &endp->net_state;
tap_idx = MGCP_TAP_BTS_OUT;
jb = NULL;
}
LOGP(DLMGCP, LOGL_DEBUG,
"endpoint %x dest %s net_end %s %d %d bts_end %s %d %d rtp_end %s %d %d\n",
@@ -680,9 +713,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
rtp_state->patched_first_rtp_payload = true;
}
rc = mgcp_udp_send(rtp_end->rtp.fd,
&rtp_end->addr,
rtp_end->rtp_port, buf, len);
if (jb)
rc = enqueue_dejitter(jb, rtp_end, buf, len);
else
rc = mgcp_udp_send(rtp_end->rtp.fd,
&rtp_end->addr,
rtp_end->rtp_port, buf, len);
if (rc <= 0)
return rc;

View File

@@ -267,7 +267,6 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
{
struct msgb *msg;
struct osmux_hdr *osmuxh;
struct llist_head list;
struct sockaddr_in addr;
struct mgcp_config *cfg = ofd->data;
uint32_t rem;
@@ -297,8 +296,7 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
endp->osmux.stats.chunks++;
rem = msg->len;
osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
osmux_xfrm_output_sched(&endp->osmux.out, osmuxh);
}
out:
msgb_free(msg);
@@ -359,7 +357,6 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
{
struct msgb *msg;
struct osmux_hdr *osmuxh;
struct llist_head list;
struct sockaddr_in addr;
struct mgcp_config *cfg = ofd->data;
uint32_t rem;
@@ -389,8 +386,7 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
endp->osmux.stats.chunks++;
rem = msg->len;
osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
osmux_xfrm_output_sched(&endp->osmux.out, osmuxh);
}
out:
msgb_free(msg);
@@ -470,9 +466,13 @@ int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint
switch (endp->cfg->role) {
case MGCP_BSC_NAT:
endp->type = MGCP_OSMUX_BSC_NAT;
osmux_xfrm_output_set_tx_cb(&endp->osmux.out,
scheduled_tx_net_cb, endp);
break;
case MGCP_BSC:
endp->type = MGCP_OSMUX_BSC;
osmux_xfrm_output_set_tx_cb(&endp->osmux.out,
scheduled_tx_bts_cb, endp);
break;
}
endp->osmux.state = OSMUX_STATE_ENABLED;
@@ -484,6 +484,11 @@ void osmux_disable_endpoint(struct mgcp_endpoint *endp)
{
LOGP(DLMGCP, LOGL_INFO, "Releasing endpoint %u using Osmux CID %u\n",
ENDPOINT_NUMBER(endp), endp->osmux.cid);
/* We are closing, we don't need pending RTP packets to be transmitted */
osmux_xfrm_output_set_tx_cb(&endp->osmux.out, NULL, NULL);
osmux_xfrm_output_flush(&endp->osmux.out);
osmux_xfrm_input_close_circuit(endp->osmux.in, endp->osmux.cid);
endp->osmux.state = OSMUX_STATE_DISABLED;
endp->osmux.cid = -1;

View File

@@ -863,6 +863,11 @@ mgcp_header_done:
goto error2;
}
/* Apply Jiter buffer settings for this endpoint, they can be overriden by CRCX policy later */
endp->bts_use_jibuf = endp->cfg->bts_use_jibuf;
endp->bts_jitter_delay_min = endp->cfg->bts_jitter_delay_min;
endp->bts_jitter_delay_max = endp->cfg->bts_jitter_delay_max;
endp->allocated = 1;
/* set up RTP media parameters */
@@ -898,6 +903,13 @@ mgcp_header_done:
case MGCP_POLICY_DEFER:
/* stop processing */
create_transcoder(endp);
/* Set up jitter buffer if required after policy has updated jibuf endp values */
if (endp->bts_use_jibuf) {
endp->bts_jb = osmo_jibuf_alloc(tcfg->endpoints);
osmo_jibuf_set_min_delay(endp->bts_jb, endp->bts_jitter_delay_min);
osmo_jibuf_set_max_delay(endp->bts_jb, endp->bts_jitter_delay_max);
osmo_jibuf_set_dequeue_cb(endp->bts_jb, mgcp_dejitter_udp_send, &endp->net_end);
}
return NULL;
break;
case MGCP_POLICY_CONT:
@@ -906,6 +918,14 @@ mgcp_header_done:
}
}
/* Set up jitter buffer if required after policy has updated jibuf endp values */
if (endp->bts_use_jibuf) {
endp->bts_jb = osmo_jibuf_alloc(tcfg->endpoints);
osmo_jibuf_set_min_delay(endp->bts_jb, endp->bts_jitter_delay_min);
osmo_jibuf_set_max_delay(endp->bts_jb, endp->bts_jitter_delay_max);
osmo_jibuf_set_dequeue_cb(endp->bts_jb, mgcp_dejitter_udp_send, &endp->net_end);
}
LOGP(DLMGCP, 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);
@@ -1373,6 +1393,9 @@ int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
void mgcp_release_endp(struct mgcp_endpoint *endp)
{
LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
if (endp->bts_jb)
osmo_jibuf_delete(endp->bts_jb);
endp->bts_jb = NULL;
endp->ci = CI_UNUSED;
endp->allocated = 0;

View File

@@ -29,6 +29,7 @@
#include <osmocom/legacy_mgcp/vty.h>
#include <string.h>
#include <inttypes.h>
#define RTCP_OMIT_STR "Drop RTCP packets in both directions\n"
#define RTP_PATCH_STR "Modify RTP packet header in both directions\n"
@@ -164,6 +165,13 @@ static int config_write_mgcp(struct vty *vty)
vty_out(vty, " osmux dummy %s%s",
g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);
}
if (g_cfg->bts_use_jibuf)
vty_out(vty, " bts-jitter-buffer%s", VTY_NEWLINE);
if (g_cfg->bts_jitter_delay_min)
vty_out(vty, " bts-jitter-delay-min %"PRIu32"%s", g_cfg->bts_jitter_delay_min, VTY_NEWLINE);
if (g_cfg->bts_jitter_delay_max)
vty_out(vty, " bts-jitter-delay-max %"PRIu32"%s", g_cfg->bts_jitter_delay_max, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -241,6 +249,11 @@ DEFUN(show_mcgp, show_mgcp_cmd,
if (g_cfg->osmux)
vty_out(vty, "Osmux used CID: %d%s", osmux_used_cid(), VTY_NEWLINE);
vty_out(vty, "Jitter Buffer by default on Uplink : %s%s",
g_cfg->bts_use_jibuf ? "on" : "off", VTY_NEWLINE);
if (g_cfg->bts_use_jibuf)
vty_out(vty, "Jitter Buffer delays: min=%"PRIu32" max=%"PRIu32"%s",
g_cfg->bts_jitter_delay_min, g_cfg->bts_jitter_delay_max, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -1344,6 +1357,63 @@ DEFUN(cfg_mgcp_osmux_dummy,
return CMD_SUCCESS;
}
#define DEJITTER_STR "Uplink Jitter Buffer"
DEFUN(cfg_mgcp_bts_use_jibuf,
cfg_mgcp_bts_use_jibuf_cmd,
"bts-jitter-buffer",
DEJITTER_STR "\n")
{
g_cfg->bts_use_jibuf = true;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_no_bts_use_jibuf,
cfg_mgcp_no_bts_use_jibuf_cmd,
"no bts-jitter-buffer",
NO_STR DEJITTER_STR "\n")
{
g_cfg->bts_use_jibuf = false;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_bts_jitter_delay_min,
cfg_mgcp_bts_jitter_delay_min_cmd,
"bts-jitter-buffer-delay-min <1-65535>",
DEJITTER_STR " Minimum Delay in ms\n" "Minimum Delay in ms\n")
{
g_cfg->bts_jitter_delay_min = atoi(argv[0]);
if (!g_cfg->bts_jitter_delay_min) {
vty_out(vty, "bts-jitter-buffer-delay-min cannot be zero.%s", VTY_NEWLINE);
return CMD_WARNING;
}
if (g_cfg->bts_jitter_delay_min && g_cfg->bts_jitter_delay_max &&
g_cfg->bts_jitter_delay_min > g_cfg->bts_jitter_delay_max) {
vty_out(vty, "bts-jitter-buffer-delay-min cannot be bigger than " \
"bts-jitter-buffer-delay-max.%s", VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_bts_jitter_delay_max,
cfg_mgcp_bts_jitter_delay_max_cmd,
"bts-jitter-buffer-delay-max <1-65535>",
DEJITTER_STR " Maximum Delay in ms\n" "Maximum Delay in ms\n")
{
g_cfg->bts_jitter_delay_max = atoi(argv[0]);
if (!g_cfg->bts_jitter_delay_max) {
vty_out(vty, "bts-jitter-buffer-delay-max cannot be zero.%s", VTY_NEWLINE);
return CMD_WARNING;
}
if (g_cfg->bts_jitter_delay_min && g_cfg->bts_jitter_delay_max &&
g_cfg->bts_jitter_delay_min > g_cfg->bts_jitter_delay_max) {
vty_out(vty, "bts-jitter-buffer-delay-max cannot be smaller than " \
"bts-jitter-buffer-delay-min.%s", VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
int mgcp_vty_init(void)
{
install_element_ve(&show_mgcp_cmd);
@@ -1411,6 +1481,10 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd);
install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
install_element(MGCP_NODE, &cfg_mgcp_bts_use_jibuf_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_bts_use_jibuf_cmd);
install_element(MGCP_NODE, &cfg_mgcp_bts_jitter_delay_min_cmd);
install_element(MGCP_NODE, &cfg_mgcp_bts_jitter_delay_max_cmd);
install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);

View File

@@ -36,6 +36,150 @@
#include <unistd.h>
#include <string.h>
/* Codec descripton for dynamic payload types (SDP) */
static const struct value_string codec_table[] = {
{ CODEC_PCMU_8000_1, "PCMU/8000/1" },
{ CODEC_GSM_8000_1, "GSM/8000/1" },
{ CODEC_PCMA_8000_1, "PCMA/8000/1" },
{ CODEC_G729_8000_1, "G729/8000/1" },
{ CODEC_GSMEFR_8000_1, "GSM-EFR/8000/1" },
{ CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" },
{ CODEC_AMR_8000_1, "AMR/8000/1" },
{ CODEC_AMRWB_16000_1, "AMR-WB/16000/1" },
{ 0, NULL },
};
/* Get encoding name from a full codec string e,g.
* ("CODEC/8000/2" => returns "CODEC") */
static char *extract_codec_name(const char *str)
{
static char buf[64];
unsigned int i;
if (!str)
return NULL;
/* FIXME osmo_strlcpy */
osmo_strlcpy(buf, str, sizeof(buf));
for (i = 0; i < strlen(buf); i++) {
if (buf[i] == '/')
buf[i] = '\0';
}
return buf;
}
/*! Map a string to a codec.
* \ptmap[in] str input string (e.g "GSM/8000/1", "GSM/8000" or "GSM")
* \returns codec that corresponds to the given string representation. */
enum mgcp_codecs map_str_to_codec(const char *str)
{
unsigned int i;
char *codec_name;
char str_buf[64];
osmo_strlcpy(str_buf, extract_codec_name(str), sizeof(str_buf));
for (i = 0; i < ARRAY_SIZE(codec_table); i++) {
codec_name = extract_codec_name(codec_table[i].str);
if (!codec_name)
continue;
if (strcmp(codec_name, str_buf) == 0)
return codec_table[i].value;
}
return -1;
}
/* Check the ptmap for illegal mappings */
static int check_ptmap(struct ptmap *ptmap)
{
/* Check if there are mappings that leave the IANA assigned dynamic
* payload type range. Under normal conditions such mappings should
* not occur */
/* Its ok to have a 1:1 mapping in the statically defined
* range, this won't hurt */
if (ptmap->codec == ptmap->pt)
return 0;
if (ptmap->codec < 96 || ptmap->codec > 127)
goto error;
if (ptmap->pt < 96 || ptmap->pt > 127)
goto error;
return 0;
error:
LOGP(DLMGCP, LOGL_ERROR,
"ptmap contains illegal mapping: codec=%u maps to pt=%u\n",
ptmap->codec, ptmap->pt);
return -1;
}
/*! Map a codec to a payload type.
* \ptmap[in] payload pointer to payload type map with specified payload types.
* \ptmap[in] ptmap_len length of the payload type map.
* \ptmap[in] codec the codec for which the payload type should be looked up.
* \returns assigned payload type */
unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
enum mgcp_codecs codec)
{
unsigned int i;
/*! Note: If the payload type map is empty or the codec is not found
* in the map, then a 1:1 mapping is performed. If the codec falls
* into the statically defined range or if the mapping table isself
* tries to map to the statically defined range, then the mapping
* is also ignored and a 1:1 mapping is performed instead. */
/* we may return the codec directly since enum mgcp_codecs directly
* corresponds to the statićally assigned payload types */
if (codec < 96 || codec > 127)
return codec;
for (i = 0; i < ptmap_len; i++) {
/* Skip illegal map entries */
if (check_ptmap(ptmap) == 0 && ptmap->codec == codec)
return ptmap->pt;
ptmap++;
}
/* If nothing is found, do not perform any mapping */
return codec;
}
/*! Map a payload type to a codec.
* \ptmap[in] payload pointer to payload type map with specified payload types.
* \ptmap[in] ptmap_len length of the payload type map.
* \ptmap[in] payload type for which the codec should be looked up.
* \returns codec that corresponds to the specified payload type */
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
unsigned int pt)
{
unsigned int i;
/*! Note: If the payload type map is empty or the payload type is not
* found in the map, then a 1:1 mapping is performed. If the payload
* type falls into the statically defined range or if the mapping
* table isself tries to map to the statically defined range, then
* the mapping is also ignored and a 1:1 mapping is performed
* instead. */
/* See also note in map_codec_to_pt() */
if (pt < 96 || pt > 127)
return pt;
for (i = 0; i < ptmap_len; i++) {
if (check_ptmap(ptmap) == 0 && ptmap->pt == pt)
return ptmap->codec;
ptmap++;
}
/* If nothing is found, do not perform any mapping */
return pt;
}
/*! Initalize MGCP client configuration struct with default values.
* \param[out] conf Client configuration.*/
void mgcp_client_conf_init(struct mgcp_client_conf *conf)
@@ -178,22 +322,114 @@ static bool mgcp_line_is_valid(const char *line)
return true;
}
/* Parse a line like "m=audio 16002 RTP/AVP 98" */
static int mgcp_parse_audio_port(struct mgcp_response *r, const char *line)
/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
{
if (sscanf(line, "m=audio %hu",
&r->audio_port) != 1)
goto response_parse_failure;
char *pt_str;
unsigned int pt;
unsigned int count = 0;
unsigned int i;
/* Extract port information */
if (sscanf(line, "m=audio %hu", &r->audio_port) != 1)
goto response_parse_failure_port;
if (r->audio_port == 0)
goto response_parse_failure;
goto response_parse_failure_port;
/* Extract payload types */
line = strstr(line, "RTP/AVP ");
if (!line)
goto exit;
pt_str = strtok(line, " ");
while (1) {
/* Do not allow excessive payload types */
if (count > ARRAY_SIZE(r->codecs))
goto response_parse_failure_pt;
pt_str = strtok(NULL, " ");
if (!pt_str)
break;
pt = atoi(pt_str);
/* Do not allow duplicate payload types */
for (i = 0; i < count; i++)
if (r->codecs[i] == pt)
goto response_parse_failure_pt;
/* Note: The payload type we store may not necessarly match
* the codec types we have defined in enum mgcp_codecs. To
* ensure that the end result only contains codec types which
* match enum mgcp_codecs, we will go through afterwards and
* remap the affected entries with the inrofmation we learn
* from rtpmap */
r->codecs[count] = pt;
count++;
}
r->codecs_len = count;
exit:
return 0;
response_parse_failure:
response_parse_failure_port:
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse MGCP response header (audio port)\n");
"Failed to parse SDP parameter port (%s)\n", line);
return -EINVAL;
response_parse_failure_pt:
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse SDP parameter payload types (%s)\n", line);
return -EINVAL;
}
/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *line)
{
unsigned int pt;
char codec_resp[64];
unsigned int codec;
if (strstr(line, "ptime")) {
if (sscanf(line, "a=ptime:%u", &r->ptime) != 1)
goto response_parse_failure_ptime;
} else if (strstr(line, "rtpmap")) {
if (sscanf(line, "a=rtpmap:%d %63s", &pt, codec_resp) == 2) {
/* The MGW may assign an own payload type in the
* response if the choosen codec falls into the IANA
* assigned dynamic payload type range (96-127).
* Normally the MGW should obey the 3gpp payload type
* assignments, which are fixed, so we likely wont see
* anything unexpected here. In order to be sure that
* we will now check the codec string and if the result
* does not match to what is IANA / 3gpp assigned, we
* will create an entry in the ptmap table so we can
* lookup later what has been assigned. */
codec = map_str_to_codec(codec_resp);
if (codec != pt) {
if (r->ptmap_len < ARRAY_SIZE(r->ptmap)) {
r->ptmap[r->ptmap_len].pt = pt;
r->ptmap[r->ptmap_len].codec = codec;
r->ptmap_len++;
} else
goto response_parse_failure_rtpmap;
}
} else
goto response_parse_failure_rtpmap;
}
return 0;
response_parse_failure_ptime:
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse SDP parameter, invalid ptime (%s)\n", line);
return -EINVAL;
response_parse_failure_rtpmap:
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse SDP parameter, invalid rtpmap (%s)\n", line);
return -EINVAL;
}
/* Parse a line like "c=IN IP4 10.11.12.13" */
@@ -253,6 +489,7 @@ int mgcp_response_parse_params(struct mgcp_response *r)
int rc;
char *data;
char *data_ptr;
int i;
/* Since this functions performs a destructive parsing, we create a
* local copy of the body data */
@@ -277,8 +514,13 @@ int mgcp_response_parse_params(struct mgcp_response *r)
return -EINVAL;
switch (line[0]) {
case 'a':
rc = mgcp_parse_audio_ptime_rtpmap(r, line);
if (rc)
goto exit;
break;
case 'm':
rc = mgcp_parse_audio_port(r, line);
rc = mgcp_parse_audio_port_pt(r, line);
if (rc)
goto exit;
break;
@@ -293,6 +535,10 @@ int mgcp_response_parse_params(struct mgcp_response *r)
}
}
/* See also note in mgcp_parse_audio_port_pt() */
for (i = 0; i < r->codecs_len; i++)
r->codecs[i] = map_pt_to_codec(r->ptmap, r->ptmap_len, r->codecs[i]);
rc = 0;
exit:
talloc_free(data);
@@ -813,6 +1059,119 @@ struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
#define MGCP_AUEP_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
#define MGCP_RSIP_MANDATORY 0 /* none */
/* Helper function for mgcp_msg_gen(): Add LCO information to MGCP message */
static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
{
unsigned int i;
int rc = 0;
const char *codec;
unsigned int pt;
rc += msgb_printf(msg, "L:");
if (mgcp_msg->ptime)
rc += msgb_printf(msg, " p:%u,", mgcp_msg->ptime);
if (mgcp_msg->codecs_len) {
rc += msgb_printf(msg, " a:");
for (i = 0; i < mgcp_msg->codecs_len; i++) {
pt = mgcp_msg->codecs[i];
codec = get_value_string_or_null(codec_table, pt);
/* Note: Use codec descriptors from enum mgcp_codecs
* in mgcp_client only! */
OSMO_ASSERT(codec);
rc += msgb_printf(msg, "%s", extract_codec_name(codec));
if (i < mgcp_msg->codecs_len - 1)
rc += msgb_printf(msg, ";");
}
rc += msgb_printf(msg, ",");
}
rc += msgb_printf(msg, " nt:IN\r\n");
return rc;
}
/* Helper function for mgcp_msg_gen(): Add SDP information to MGCP message */
static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_client *mgcp)
{
unsigned int i;
int rc = 0;
char local_ip[INET_ADDRSTRLEN];
const char *codec;
unsigned int pt;
/* Add separator to mark the beginning of the SDP block */
rc += msgb_printf(msg, "\r\n");
/* Add SDP protocol version */
rc += msgb_printf(msg, "v=0\r\n");
/* Determine local IP-Address */
if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
LOGP(DLMGCP, LOGL_ERROR,
"Could not determine local IP-Address!\n");
msgb_free(msg);
return -2;
}
/* Add owner/creator (SDP) */
rc += msgb_printf(msg, "o=- %x 23 IN IP4 %s\r\n",
mgcp_msg->call_id, local_ip);
/* Add session name (none) */
rc += msgb_printf(msg, "s=-\r\n");
/* Add RTP address and port */
if (mgcp_msg->audio_port == 0) {
LOGP(DLMGCP, LOGL_ERROR,
"Invalid port number, can not generate MGCP message\n");
msgb_free(msg);
return -2;
}
if (strlen(mgcp_msg->audio_ip) <= 0) {
LOGP(DLMGCP, LOGL_ERROR,
"Empty ip address, can not generate MGCP message\n");
msgb_free(msg);
return -2;
}
rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
/* Add time description, active time (SDP) */
rc += msgb_printf(msg, "t=0 0\r\n");
rc += msgb_printf(msg, "m=audio %u RTP/AVP", mgcp_msg->audio_port);
for (i = 0; i < mgcp_msg->codecs_len; i++) {
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
rc += msgb_printf(msg, " %u", pt);
}
rc += msgb_printf(msg, "\r\n");
for (i = 0; i < mgcp_msg->codecs_len; i++) {
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
/* Note: Only dynamic payload type from the range 96-127
* require to be explained further via rtpmap. All others
* are implcitly definedby the number in m=audio */
if (pt >= 96 && pt <= 127) {
codec = get_value_string_or_null(codec_table, mgcp_msg->codecs[i]);
/* Note: Use codec descriptors from enum mgcp_codecs
* in mgcp_client only! */
OSMO_ASSERT(codec);
rc += msgb_printf(msg, "a=rtpmap:%u %s\r\n", pt, codec);
}
}
if (mgcp_msg->ptime)
rc += msgb_printf(msg, "a=ptime:%u\r\n", mgcp_msg->ptime);
return rc;
}
/*! Generate an MGCP message
* \param[in] mgcp MGCP client descriptor.
* \param[in] mgcp_msg Message description
@@ -823,7 +1182,8 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
uint32_t mandatory_mask;
struct msgb *msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
int rc = 0;
char local_ip[INET_ADDRSTRLEN];
int rc_sdp;
bool use_sdp = false;
msg->l2h = msg->data;
msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
@@ -902,9 +1262,17 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
rc += msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id);
}
/* Add local connection options */
if (mgcp_msg->verb == MGCP_VERB_CRCX)
rc += msgb_printf(msg, "L: p:20, a:AMR, nt:IN\r\n");
/* Using SDP makes sense when a valid IP/Port combination is specifiec,
* if we do not know this information yet, we fall back to LCO */
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
&& mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT)
use_sdp = true;
/* Add local connection options (LCO) */
if (!use_sdp
&& (mgcp_msg->verb == MGCP_VERB_CRCX
|| mgcp_msg->verb == MGCP_VERB_MDCX))
rc += add_lco(msg, mgcp_msg);
/* Add mode */
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_MODE)
@@ -912,52 +1280,15 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
msgb_printf(msg, "M: %s\r\n",
mgcp_client_cmode_name(mgcp_msg->conn_mode));
/* Add SDP body */
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
&& mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT) {
/* Add separator to mark the beginning of the SDP block */
rc += msgb_printf(msg, "\r\n");
/* Add SDP protocol version */
rc += msgb_printf(msg, "v=0\r\n");
/* Determine local IP-Address */
if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
LOGP(DLMGCP, LOGL_ERROR,
"Could not determine local IP-Address!\n");
msgb_free(msg);
/* Add session description protocol (SDP) */
if (use_sdp
&& (mgcp_msg->verb == MGCP_VERB_CRCX
|| mgcp_msg->verb == MGCP_VERB_MDCX)) {
rc_sdp = add_sdp(msg, mgcp_msg, mgcp);
if (rc_sdp == -2)
return NULL;
}
/* Add owner/creator (SDP) */
rc += msgb_printf(msg, "o=- %x 23 IN IP4 %s\r\n",
mgcp_msg->call_id, local_ip);
/* Add session name (none) */
rc += msgb_printf(msg, "s=-\r\n");
/* Add RTP address and port */
if (mgcp_msg->audio_port == 0) {
LOGP(DLMGCP, LOGL_ERROR,
"Invalid port number, can not generate MGCP message\n");
msgb_free(msg);
return NULL;
}
if (strlen(mgcp_msg->audio_ip) <= 0) {
LOGP(DLMGCP, LOGL_ERROR,
"Empty ip address, can not generate MGCP message\n");
msgb_free(msg);
return NULL;
}
rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
/* Add time description, active time (SDP) */
rc += msgb_printf(msg, "t=0 0\r\n");
rc +=
msgb_printf(msg, "m=audio %u RTP/AVP 255\r\n",
mgcp_msg->audio_port);
else
rc += rc_sdp;
}
if (rc != 0) {

View File

@@ -112,9 +112,12 @@ static struct msgb *make_crcx_msg_bind(struct mgcp_ctx *mgcp_ctx)
.verb = MGCP_VERB_CRCX,
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
.call_id = mgcp_ctx->conn_peer_local.call_id,
.conn_mode = MGCP_CONN_LOOPBACK,
.conn_mode = MGCP_CONN_RECV_ONLY,
.ptime = mgcp_ctx->conn_peer_local.ptime,
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len
};
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
}
@@ -124,15 +127,19 @@ static struct msgb *make_crcx_msg_bind_connect(struct mgcp_ctx *mgcp_ctx)
struct mgcp_msg mgcp_msg;
mgcp_msg = (struct mgcp_msg) {
.verb = MGCP_VERB_CRCX,.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
MGCP_MSG_PRESENCE_AUDIO_PORT),
.verb = MGCP_VERB_CRCX,
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
MGCP_MSG_PRESENCE_AUDIO_PORT),
.call_id = mgcp_ctx->conn_peer_local.call_id,
.conn_mode = MGCP_CONN_RECV_SEND,
.audio_ip = mgcp_ctx->conn_peer_local.addr,
.audio_port = mgcp_ctx->conn_peer_local.port,
.ptime = mgcp_ctx->conn_peer_local.ptime,
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len
};
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
}
@@ -150,8 +157,11 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
.conn_mode = MGCP_CONN_RECV_SEND,
.audio_ip = mgcp_ctx->conn_peer_local.addr,
.audio_port = mgcp_ctx->conn_peer_local.port,
.ptime = mgcp_ctx->conn_peer_local.ptime,
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len
};
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
/* Note: We take the endpoint and the call_id from the remote
* connection info, because we can be confident that the
@@ -215,6 +225,14 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
}
}
/* Return the CI that the MGW allocated during CRCX response. This is purely informational for logging
* and identity tracking; the mgcp_conn_*() functions take care of using the right CI internally. */
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi)
{
struct mgcp_ctx *mgcp_ctx = fi->priv;
return mgcp_ctx->conn_id;
}
static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv)
{
struct osmo_fsm_inst *fi = priv;
@@ -476,7 +494,7 @@ static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca
* mgcp_conn_delete() to instruct the FSM to perform a graceful exit */
if (strlen(mgcp_ctx->conn_id)) {
LOGPFSML(fi, LOGL_ERROR,
"MGW/DLCX: aprupt FSM termination with connections still present, sending unconditional DLCX...\n");
"MGW/DLCX: abrupt FSM termination with connections still present, sending unconditional DLCX...\n");
msg = make_dlcx_msg(mgcp_ctx);
OSMO_ASSERT(msg);
mgcp_client_tx(mgcp, msg, NULL, NULL);
@@ -548,7 +566,7 @@ static struct osmo_fsm fsm_mgcp_client = {
/*! allocate FSM, and create a new connection on the MGW.
* \param[in] mgcp MGCP client descriptor.
* \param[in] mgcpparent_fi Parent FSM instance.
* \param[in] parent_fi Parent FSM instance.
* \param[in] parent_term_evt Event to be sent to parent when terminating.
* \param[in] parent_evt Event to be sent to parent when operation is done.
* \param[in] conn_peer Connection parameters (ip, port...).
@@ -565,7 +583,7 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm
OSMO_ASSERT(mgcp);
OSMO_ASSERT(conn_peer);
/* Check if IP/Port informstaion in conn info makes sense */
/* Check if IP/Port information in conn info makes sense */
if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0)
return NULL;
@@ -622,17 +640,24 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
OSMO_ASSERT(fi->state != ST_DLCX_RESP);
/* Check if IP/Port parameters make sense */
if (conn_peer->port == 0)
if (conn_peer->port == 0) {
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, port == 0\n");
return -EINVAL;
if (inet_aton(conn_peer->addr, &ip_test) == 0)
}
if (inet_aton(conn_peer->addr, &ip_test) == 0) {
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address == 0.0.0.0\n");
return -EINVAL;
}
/*! The user may supply an endpoint identifier in conn_peer. The
* identifier is then checked. This check is optional. Later steps do
* not depend on the endpoint identifier supplied here because it is
* already implicitly known from the CRCX phase. */
if (strlen(conn_peer->endpoint) && strcmp(conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint))
if (strlen(conn_peer->endpoint) && strcmp(conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint)) {
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, endpoint mismatches: requested %s, should be %s\n",
conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint);
return -EINVAL;
}
/*! Note: The call-id is implicitly known from the previous CRCX and
* will not be checked even when it is set in conn_peer. */

View File

@@ -35,6 +35,7 @@ libosmo_mgcp_a_SOURCES = \
mgcp_vty.c \
mgcp_osmux.c \
mgcp_sdp.c \
mgcp_codec.c \
mgcp_msg.c \
mgcp_conn.c \
mgcp_stat.c \

View File

@@ -0,0 +1,343 @@
/*
* (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2014 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 <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <errno.h>
/* Helper function to dump codec information of a specified codec to a printable
* string, used by dump_codec_summary() */
static char *dump_codec(struct mgcp_rtp_codec *codec)
{
static char str[256];
char *pt_str;
if (codec->payload_type > 76)
pt_str = "DYNAMIC";
else if (codec->payload_type > 72)
pt_str = "RESERVED <!>";
else if (codec->payload_type != PTYPE_UNDEFINED)
pt_str = codec->subtype_name;
else
pt_str = "INVALID <!>";
snprintf(str, sizeof(str), "(pt:%i=%s, audio:%s subt=%s, rate=%u, ch=%i, t=%u/%u)", codec->payload_type, pt_str,
codec->audio_name, codec->subtype_name, codec->rate, codec->channels, codec->frame_duration_num,
codec->frame_duration_den);
return str;
}
/*! Dump a summary of all negotiated codecs to debug log
* \param[in] conn related rtp-connection. */
void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
{
struct mgcp_rtp_end *rtp;
unsigned int i;
struct mgcp_rtp_codec *codec;
struct mgcp_endpoint *endp;
rtp = &conn->end;
endp = conn->conn->endp;
if (rtp->codecs_assigned == 0) {
LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x conn:%s no codecs available\n", ENDPOINT_NUMBER(endp),
mgcp_conn_dump(conn->conn));
return;
}
/* Store parsed codec information */
for (i = 0; i < rtp->codecs_assigned; i++) {
codec = &rtp->codecs[i];
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x conn:%s codecs[%u]:%s", ENDPOINT_NUMBER(endp),
mgcp_conn_dump(conn->conn), i, dump_codec(codec));
if (codec == rtp->codec)
LOGPC(DLMGCP, LOGL_DEBUG, " [selected]");
LOGPC(DLMGCP, LOGL_DEBUG, "\n");
}
}
/* Initalize or reset codec information with default data. */
void codec_init(struct mgcp_rtp_codec *codec)
{
if (codec->subtype_name)
talloc_free(codec->subtype_name);
if (codec->audio_name)
talloc_free(codec->audio_name);
memset(codec, 0, sizeof(*codec));
codec->payload_type = -1;
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
}
/*! Initalize or reset codec information with default data.
* \param[out] conn related rtp-connection. */
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
{
memset(conn->end.codecs, 0, sizeof(conn->end.codecs));
conn->end.codecs_assigned = 0;
conn->end.codec = NULL;
}
/* Set members of struct mgcp_rtp_codec, extrapolate in missing information */
static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
int payload_type, const char *audio_name, unsigned int pt_offset)
{
int rate;
int channels;
char audio_codec[64];
/* Initalize the codec struct with some default data to begin with */
codec_init(codec);
if (payload_type != PTYPE_UNDEFINED) {
/* Make sure we do not get any reserved or undefined type numbers */
/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
if (payload_type == 1 || payload_type == 2 || payload_type == 19)
goto error;
if (payload_type >= 72 && payload_type <= 76)
goto error;
if (payload_type >= 127)
goto error;
codec->payload_type = payload_type;
}
/* When no audio name is given, we are forced to use the payload
* type to generate the audio name. This is only possible for
* non dynamic payload types, which are statically defined */
if (!audio_name) {
switch (payload_type) {
case 0:
audio_name = talloc_strdup(ctx, "PCMU/8000/1");
break;
case 3:
audio_name = talloc_strdup(ctx, "GSM/8000/1");
break;
case 8:
audio_name = talloc_strdup(ctx, "PCMA/8000/1");
break;
case 18:
audio_name = talloc_strdup(ctx, "G729/8000/1");
break;
default:
/* The given payload type is not known to us, or it
* it is a dynamic payload type for which we do not
* know the audio name. We must give up here */
goto error;
}
}
/* Now we extract the codec subtype name, rate and channels. The latter
* two are optional. If they are not present we use the safe defaults
* above. */
if (strlen(audio_name) > sizeof(audio_codec))
goto error;
channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1)
goto error;
/* Note: We only accept configurations with one audio channel! */
if (channels != 1)
goto error;
codec->rate = rate;
codec->channels = channels;
codec->subtype_name = talloc_strdup(ctx, audio_codec);
codec->audio_name = talloc_strdup(ctx, audio_name);
codec->payload_type = payload_type;
if (!strcmp(audio_codec, "G729")) {
codec->frame_duration_num = 10;
codec->frame_duration_den = 1000;
} else {
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
}
/* Derive the payload type if it is unknown */
if (codec->payload_type == PTYPE_UNDEFINED) {
/* For the known codecs from the static range we restore
* the IANA or 3GPP assigned payload type number */
if (codec->rate == 8000 && codec->channels == 1) {
/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
if (!strcmp(codec->subtype_name, "GSM"))
codec->payload_type = 3;
else if (!strcmp(codec->subtype_name, "PCMA"))
codec->payload_type = 8;
else if (!strcmp(codec->subtype_name, "PCMU"))
codec->payload_type = 0;
else if (!strcmp(codec->subtype_name, "G729"))
codec->payload_type = 18;
/* See also: 3GPP TS 48.103, chapter 5.4.2.2 RTP Payload
* Note: These are not fixed payload types as the IANA
* defined once, they still remain dymanic payload
* types, but with a payload type number preference. */
else if (!strcmp(codec->subtype_name, "GSM-EFR"))
codec->payload_type = 110;
else if (!strcmp(codec->subtype_name, "GSM-HR-08"))
codec->payload_type = 111;
else if (!strcmp(codec->subtype_name, "AMR"))
codec->payload_type = 112;
else if (!strcmp(codec->subtype_name, "AMR-WB"))
codec->payload_type = 113;
}
/* If we could not determine a payload type we assume that
* we are dealing with a codec from the dynamic range. We
* choose a fixed identifier from 96-109. (Note: normally,
* the dynamic payload type rante is from 96-127, but from
* 110 onwards 3gpp defines prefered codec types, which are
* also fixed, see above) */
if (codec->payload_type < 0) {
codec->payload_type = 96 + pt_offset;
if (codec->payload_type > 109)
goto error;
}
}
return 0;
error:
/* Make sure we leave a clean codec entry on error. */
codec_init(codec);
memset(codec, 0, sizeof(*codec));
return -EINVAL;
}
/*! Add codec configuration depending on payload type and/or codec name. This
* function uses the input parameters to extrapolate the full codec information.
* \param[out] codec configuration (caller provided memory).
* \param[out] conn related rtp-connection.
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
* \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
* \returns 0 on success, -EINVAL on failure. */
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name)
{
int rc;
/* The amount of codecs we can store is limited, make sure we do not
* overrun this limit. */
if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
return -EINVAL;
rc = codec_set(conn->conn, &conn->end.codecs[conn->end.codecs_assigned], payload_type, audio_name,
conn->end.codecs_assigned);
if (rc != 0)
return -EINVAL;
conn->end.codecs_assigned++;
return 0;
}
/* Check if the given codec is applicable on the specified endpoint
* Helper function for mgcp_codec_decide() */
static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec)
{
char codec_name[64];
/* A codec name must be set, if not, this might mean that the codec
* (payload type) that was assigned is unknown to us so we must stop
* here. */
if (!codec->subtype_name)
return false;
/* We now extract the codec_name (letters before the /, e.g. "GSM"
* from the audio name that is stored in the trunk configuration.
* We do not compare to the full audio_name because we expect that
* "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the
* audio name of the codec is set to "GSM" */
if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1)
return false;
/* Finally we check if the subtype_name we have generated from the
* audio_name in the trunc struct patches the codec_name of the
* given codec */
if (strcasecmp(codec_name, codec->subtype_name) == 0)
return true;
/* FIXME: It is questinable that the method to pick a compatible
* codec can work properly. Since this useses tcfg->audio_name, as
* a reference, which is set to "AMR/8000" permanently.
* tcfg->audio_name must be updated by the first connection that
* has been made on an endpoint, so that the second connection
* can make a meaningful decision here */
return false;
}
/*! Decide for one suitable codec
* \param[in] conn related rtp-connection.
* \returns 0 on success, -EINVAL on failure. */
int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
{
struct mgcp_rtp_end *rtp;
unsigned int i;
struct mgcp_endpoint *endp;
bool codec_assigned = false;
endp = conn->conn->endp;
rtp = &conn->end;
/* This function works on the results the SDP/LCO parser has extracted
* from the MGCP message. The goal is to select a suitable codec for
* the given connection. When transcoding is available, the first codec
* from the codec list is taken without further checking. When
* transcoding is not available, then the choice must be made more
* carefully. Each codec in the list is checked until one is found that
* is rated compatible. The rating is done by the helper function
* is_codec_compatible(), which does the actual checking. */
for (i = 0; i < rtp->codecs_assigned; i++) {
/* When no transcoding is available, avoid codecs that would
* require transcoding. */
if (endp->tcfg->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n",
rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name);
continue;
}
rtp->codec = &rtp->codecs[i];
codec_assigned = true;
break;
}
/* FIXME: To the reviewes: This is problematic. I do not get why we
* need to reset the packet_duration_ms depending on the codec
* selection. I thought it were all 20ms? Is this to address some
* cornercase. (This piece of code was in the code path before,
* together with the note: "TODO/XXX: Store this per codec and derive
* it on use" */
if (codec_assigned) {
if (rtp->maximum_packet_time >= 0
&& rtp->maximum_packet_time * rtp->codec->frame_duration_den >
rtp->codec->frame_duration_num * 1500)
rtp->packet_duration_ms = 0;
return 0;
}
return -EINVAL;
}

View File

@@ -25,9 +25,32 @@
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/core/rate_ctr.h>
#include <ctype.h>
enum {
IN_STREAM_ERR_TSTMP_CTR,
OUT_STREAM_ERR_TSTMP_CTR,
};
static const struct rate_ctr_desc rate_ctr_desc[] = {
[IN_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:in", "Inbound rtp-stream timestamp errors."},
[OUT_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:out", "Outbound rtp-stream timestamp errors."},
};
const static struct rate_ctr_group_desc rate_ctr_group_desc = {
.group_name_prefix = "conn_rtp",
.group_description = "rtp connection statistics",
.class_id = 1,
.num_ctr = 2,
.ctr_desc = rate_ctr_desc
};
/* Allocate a new connection identifier. According to RFC3435, they must
* be unique only within the scope of the endpoint. (Caller must provide
* memory for id) */
@@ -67,26 +90,14 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
return -1;
}
/* Reset codec state and free memory */
static void mgcp_rtp_codec_init(struct mgcp_rtp_codec *codec)
{
codec->payload_type = -1;
codec->subtype_name = NULL;
codec->audio_name = NULL;
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
/* see also mgcp_sdp.c, mgcp_set_audio_info() */
talloc_free(codec->subtype_name);
talloc_free(codec->audio_name);
}
/* Initialize rtp connection struct with default values */
static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
{
struct mgcp_rtp_end *end = &conn_rtp->end;
/* FIXME: Each new rate counter group requires an unique index. At the
* moment we generate this index using this counter, but perhaps there
* is a more concious way to assign the indexes. */
static unsigned int rate_ctr_index = 0;
conn_rtp->type = MGCP_RTP_DEFAULT;
conn_rtp->osmux.allocated_cid = -1;
@@ -105,9 +116,15 @@ static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn
end->frames_per_packet = 0; /* unknown */
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
end->output_enabled = 0;
end->maximum_packet_time = -1;
mgcp_rtp_codec_init(&end->codec);
mgcp_rtp_codec_init(&end->alt_codec);
conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index);
conn_rtp->state.in_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[IN_STREAM_ERR_TSTMP_CTR];
conn_rtp->state.out_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[OUT_STREAM_ERR_TSTMP_CTR];
rate_ctr_index++;
/* Make sure codec table is reset */
mgcp_codec_reset_all(conn_rtp);
}
/* Cleanup rtp connection struct */
@@ -116,6 +133,7 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
osmux_disable_conn(conn_rtp);
osmux_release_cid(conn_rtp);
mgcp_free_rtp_port(&conn_rtp->end);
rate_ctr_group_free(conn_rtp->rate_ctr_group);
}
/*! allocate a new connection list entry.

View File

@@ -222,7 +222,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
if (seq == sstate->last_seq) {
if (timestamp != sstate->last_timestamp) {
sstate->err_ts_counter += 1;
rate_ctr_inc(sstate->err_ts_ctr);
LOGP(DRTP, LOGL_ERROR,
"The %s timestamp delta is != 0 but the sequence "
"number %d is the same, "
@@ -272,7 +272,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
ts_alignment_error(sstate, state->packet_duration, timestamp);
if (timestamp_error) {
sstate->err_ts_counter += 1;
rate_ctr_inc(sstate->err_ts_ctr);
LOGP(DRTP, LOGL_NOTICE,
"The %s timestamp has an alignment error of %d "
"on 0x%x SSRC: %u "
@@ -310,7 +310,7 @@ static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp,
ENDPOINT_NUMBER(endp), tsdelta,
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
} else {
tsdelta = rtp_end->codec.rate * 20 / 1000;
tsdelta = rtp_end->codec->rate * 20 / 1000;
LOGP(DRTP, LOGL_NOTICE,
"Fixed packet duration and last timestamp delta "
"are not available on 0x%x, "
@@ -421,8 +421,8 @@ void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
"endpoint:0x%x conn:%s using format defaults\n",
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn));
*payload_type = conn->end.codec.payload_type;
*audio_name = conn->end.codec.audio_name;
*payload_type = conn->end.codec->payload_type;
*audio_name = conn->end.codec->audio_name;
*fmtp_extra = conn->end.fmtp_extra;
}
@@ -490,7 +490,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
uint16_t seq;
uint32_t timestamp, ssrc;
struct rtp_hdr *rtp_hdr;
int payload = rtp_end->codec.payload_type;
int payload = rtp_end->codec->payload_type;
if (len < sizeof(*rtp_hdr))
return;
@@ -498,7 +498,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
rtp_hdr = (struct rtp_hdr *)data;
seq = ntohs(rtp_hdr->sequence);
timestamp = ntohl(rtp_hdr->timestamp);
arrival_time = get_current_ts(rtp_end->codec.rate);
arrival_time = get_current_ts(rtp_end->codec->rate);
ssrc = ntohl(rtp_hdr->ssrc);
transit = arrival_time - timestamp;
@@ -511,7 +511,9 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
state->in_stream.last_tsdelta = 0;
state->packet_duration =
mgcp_rtp_packet_duration(endp, rtp_end);
state->out_stream = state->in_stream;
state->out_stream.last_seq = seq - 1;
state->out_stream.ssrc = state->patch.orig_ssrc = ssrc;
state->out_stream.last_tsdelta = 0;
state->out_stream.last_timestamp = timestamp;
state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */
LOGP(DRTP, LOGL_INFO,
@@ -522,7 +524,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
if (state->packet_duration == 0) {
state->packet_duration =
rtp_end->codec.rate * 20 / 1000;
rtp_end->codec->rate * 20 / 1000;
LOGP(DRTP, LOGL_NOTICE,
"endpoint:0x%x fixed packet duration is not available, "
"using fixed 20ms instead: %d from %s:%d\n",
@@ -865,6 +867,15 @@ static int check_rtp_destin(struct mgcp_conn_rtp *conn)
struct mgcp_endpoint *endp;
endp = conn->conn->endp;
/* Note: it is legal to create a connection but never setting a port
* and IP-address for outgoing data. */
if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0 && conn->end.rtp_port == 0) {
LOGP(DRTP, LOGL_DEBUG,
"endpoint:0x%x destination IP-address and rtp port is (not yet) known\n",
ENDPOINT_NUMBER(endp));
return -1;
}
if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0) {
LOGP(DRTP, LOGL_ERROR,
"endpoint:0x%x destination IP-address is invalid\n",

View File

@@ -322,11 +322,10 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
{
struct msgb *msg;
struct osmux_hdr *osmuxh;
struct llist_head list;
struct sockaddr_in addr;
struct mgcp_config *cfg = ofd->data;
uint32_t rem;
struct mgcp_conn_rtp *conn_net = NULL;
struct mgcp_conn_rtp *conn_bts = NULL;
msg = osmux_recv(ofd, &addr);
if (!msg)
@@ -345,8 +344,8 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
&addr.sin_addr, MGCP_DEST_NET);
/* FIXME: Get rid of CONN_ID_XXX! */
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_net)
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
if (!conn_bts)
goto out;
if (!endp) {
@@ -355,12 +354,11 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
osmuxh->circuit_id);
goto out;
}
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
conn_net->osmux.stats.chunks++;
conn_bts->osmux.stats.octets += osmux_chunk_length(msg, rem);
conn_bts->osmux.stats.chunks++;
rem = msg->len;
osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
osmux_xfrm_output_sched(&conn_bts->osmux.out, osmuxh);
}
out:
msgb_free(msg);
@@ -426,7 +424,6 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
{
struct msgb *msg;
struct osmux_hdr *osmuxh;
struct llist_head list;
struct sockaddr_in addr;
struct mgcp_config *cfg = ofd->data;
uint32_t rem;
@@ -463,8 +460,7 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
conn_net->osmux.stats.chunks++;
rem = msg->len;
osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
osmux_xfrm_output_sched(&conn_net->osmux.out, osmuxh);
}
out:
msgb_free(msg);
@@ -553,9 +549,13 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
switch (endp->cfg->role) {
case MGCP_BSC_NAT:
conn->type = MGCP_OSMUX_BSC_NAT;
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
scheduled_tx_net_cb, endp);
break;
case MGCP_BSC:
conn->type = MGCP_OSMUX_BSC;
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
scheduled_tx_bts_cb, endp);
break;
}
@@ -576,6 +576,11 @@ void osmux_disable_conn(struct mgcp_conn_rtp *conn)
LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n",
conn->conn->id, conn->osmux.cid);
/* We are closing, we don't need pending RTP packets to be transmitted */
osmux_xfrm_output_set_tx_cb(&conn->osmux.out, NULL, NULL);
osmux_xfrm_output_flush(&conn->osmux.out);
osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
conn->osmux.state = OSMUX_STATE_DISABLED;
conn->osmux.cid = -1;

View File

@@ -40,6 +40,7 @@
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_codec.h>
struct mgcp_request {
char *name;
@@ -356,13 +357,15 @@ static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
/* Try to find a free port by attempting to bind on it. Also handle the
* counter that points on the next free port. Since we have a pointer
* to the next free port, binding should work on the first attempt,
* nevertheless, try at least the next 200 ports before giving up */
* to the next free port, binding should in work on the first attempt in
* general. In case of failure the next port is tryed until the whole port
* range is tryed once. */
static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
int i;
struct mgcp_rtp_end *end;
struct mgcp_port_range *range;
unsigned int tries;
OSMO_ASSERT(conn);
end = &conn->end;
@@ -371,7 +374,8 @@ static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
range = &endp->cfg->net_ports;
/* attempt to find a port */
for (i = 0; i < 200; ++i) {
tries = (range->range_end - range->range_start) / 2;
for (i = 0; i < tries; ++i) {
int rc;
if (range->last_port >= range->range_end)
@@ -387,8 +391,123 @@ static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
}
LOGP(DLMGCP, LOGL_ERROR,
"Allocating a RTP/RTCP port failed 200 times 0x%x.\n",
ENDPOINT_NUMBER(endp));
"Allocating a RTP/RTCP port failed %u times 0x%x.\n",
tries, ENDPOINT_NUMBER(endp));
return -1;
}
/*! Helper function for check_local_cx_options() to get a pointer of the next
* lco option identifier
* \param[in] lco string
* \returns pointer to the beginning of the LCO identifier, NULL on failure */
char *get_lco_identifier(const char *options)
{
char *ptr;
unsigned int count = 0;
/* Jump to the end of the lco identifier */
ptr = strstr(options, ":");
if (!ptr)
return NULL;
/* Walk backwards until the pointer points to the beginning of the
* lco identifier. We know that we stand at the beginning when we
* are either at the beginning of the memory or see a space or
* comma. (this is tolerant, it will accept a:10, b:11 as well as
* a:10,b:11) */
while (1) {
/* Endless loop protection */
if (count > 10000)
return NULL;
else if (ptr < options || *ptr == ' ' || *ptr == ',') {
ptr++;
break;
}
ptr--;
count++;
}
/* Check if we got any result */
if (*ptr == ':')
return NULL;
return ptr;
}
/*! Check the LCO option. This function checks for multiple appearence of LCO
* options, which is illegal
* \param[in] ctx talloc context
* \param[in] lco string
* \returns 0 on success, -1 on failure */
int check_local_cx_options(void *ctx, const char *options)
{
int i;
char *options_copy;
char *lco_identifier;
char *lco_identifier_end;
char *next_lco_identifier;
char **lco_seen;
unsigned int lco_seen_n = 0;
if (!options)
return -1;
lco_seen =
(char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
options_copy = talloc_strdup(ctx, options);
lco_identifier = options_copy;
do {
/* Move the lco_identifier pointer to the beginning of the
* current lco option identifier */
lco_identifier = get_lco_identifier(lco_identifier);
if (!lco_identifier)
goto error;
/* Look ahead to the next LCO option early, since we
* will parse destructively */
next_lco_identifier = strstr(lco_identifier + 1, ",");
/* Pinch off the end of the lco field identifier name
* and see if we still got something, also check if
* there is some value after the colon. */
lco_identifier_end = strstr(lco_identifier, ":");
if (!lco_identifier_end)
goto error;
if (*(lco_identifier_end + 1) == ' '
|| *(lco_identifier_end + 1) == ','
|| *(lco_identifier_end + 1) == '\0')
goto error;
*lco_identifier_end = '\0';
if (strlen(lco_identifier) == 0)
goto error;
/* Check if we have already seen the current field identifier
* before. If yes, we must bail, an LCO must only appear once
* in the LCO string */
for (i = 0; i < lco_seen_n; i++) {
if (strcmp(lco_seen[i], lco_identifier) == 0)
goto error;
}
lco_seen[lco_seen_n] = lco_identifier;
lco_seen_n++;
/* The first identifier must always be found at the beginnning
* of the LCO string */
if (lco_seen[0] != options_copy)
goto error;
/* Go to the next lco option */
lco_identifier = next_lco_identifier;
} while (lco_identifier);
talloc_free(lco_seen);
talloc_free(options_copy);
return 0;
error:
talloc_free(lco_seen);
talloc_free(options_copy);
return -1;
}
@@ -402,20 +521,34 @@ static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
char *p_opt, *a_opt;
char codec[9];
if (!options)
return 0;
if (strlen(options) == 0)
return 0;
/* Make sure the encoding of the LCO is consistant before we proceed */
if (check_local_cx_options(ctx, options) != 0) {
LOGP(DLMGCP, LOGL_ERROR,
"local CX options: Internal inconsistency in Local Connection Options!\n");
return 524;
}
talloc_free(lco->string);
talloc_free(lco->codec);
lco->codec = NULL;
lco->pkt_period_min = lco->pkt_period_max = 0;
lco->string = talloc_strdup(ctx, options ? options : "");
lco->string = talloc_strdup(ctx, options);
p_opt = strstr(lco->string, "p:");
if (p_opt && sscanf(p_opt, "p:%d-%d",
&lco->pkt_period_min, &lco->pkt_period_max) == 1)
lco->pkt_period_max = lco->pkt_period_min;
/* FIXME: LCO also supports the negotiation of more then one codec.
* (e.g. a:PCMU;G726-32) But this implementation only supports a single
* codec only. */
a_opt = strstr(lco->string, "a:");
if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1)
if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1) {
talloc_free(lco->codec);
lco->codec = talloc_strdup(ctx, codec);
}
LOGP(DLMGCP, LOGL_DEBUG,
"local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
@@ -456,15 +589,15 @@ uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
/* Get the number of frames per channel and packet */
if (rtp->frames_per_packet)
f = rtp->frames_per_packet;
else if (rtp->packet_duration_ms && rtp->codec.frame_duration_num) {
int den = 1000 * rtp->codec.frame_duration_num;
f = (rtp->packet_duration_ms * rtp->codec.frame_duration_den +
else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
int den = 1000 * rtp->codec->frame_duration_num;
f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
den / 2)
/ den;
}
return rtp->codec.rate * f * rtp->codec.frame_duration_num /
rtp->codec.frame_duration_den;
return rtp->codec->rate * f * rtp->codec->frame_duration_num /
rtp->codec->frame_duration_den;
}
static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
@@ -480,6 +613,68 @@ static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
return mgcp_parse_osmux_cid(line);
}
/* Process codec information contained in CRCX/MDCX */
static int handle_codec_info(struct mgcp_conn_rtp *conn,
struct mgcp_parse_data *p, int have_sdp, bool crcx)
{
struct mgcp_endpoint *endp = p->endp;
int rc;
char *cmd;
if (crcx)
cmd = "CRCX";
else
cmd = "MDCX";
/* Collect codec information */
if (have_sdp) {
/* If we have SDP, we ignore the local connection options and
* use only the SDP information. */
mgcp_codec_reset_all(conn);
rc = mgcp_parse_sdp_data(endp, conn, p);
if (rc != 0) {
LOGP(DLMGCP, LOGL_ERROR,
"%s: endpoint:%x sdp not parseable\n", cmd,
ENDPOINT_NUMBER(endp));
/* See also RFC 3661: Protocol error */
return 510;
}
} else if (endp->local_options.codec) {
/* When no SDP is available, we use the codec information from
* the local connection options (if present) */
mgcp_codec_reset_all(conn);
rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec);
if (rc != 0)
goto error;
}
/* Make sure we always set a sane default codec */
if (conn->end.codecs_assigned == 0) {
/* When SDP and/or LCO did not supply any codec information,
* than it makes sense to pick a sane default: (payload-type 0,
* PCMU), see also: OS#2658 */
mgcp_codec_reset_all(conn);
rc = mgcp_codec_add(conn, 0, NULL);
if (rc != 0)
goto error;
}
/* Make codec decision */
if (mgcp_codec_decide(conn) != 0)
goto error;
return 0;
error:
LOGP(DLMGCP, LOGL_ERROR,
"%s: endpoint:0x%x codec negotiation failure\n", cmd,
ENDPOINT_NUMBER(endp));
/* See also RFC 3661: Codec negotiation failure */
return 534;
}
/* CRCX command handler, processes the received command */
static struct msgb *handle_create_con(struct mgcp_parse_data *p)
{
@@ -597,17 +792,6 @@ mgcp_header_done:
* connection ids) */
endp->callid = talloc_strdup(tcfg->endpoints, callid);
/* Extract audio codec information */
rc = set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
local_options);
if (rc != 0) {
LOGP(DLMGCP, LOGL_ERROR,
"CRCX: endpoint:%x inavlid local connection options!\n",
ENDPOINT_NUMBER(endp));
error_code = rc;
goto error2;
}
snprintf(conn_name, sizeof(conn_name), "%s", callid);
_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP, conn_name);
if (!_conn) {
@@ -638,12 +822,27 @@ mgcp_header_done:
goto error2;
}
/* set up RTP media parameters */
if (have_sdp)
mgcp_parse_sdp_data(endp, conn, p);
else if (endp->local_options.codec)
mgcp_set_audio_info(p->cfg, &conn->end.codec,
PTYPE_UNDEFINED, endp->local_options.codec);
/* Set local connection options, if present */
if (local_options) {
rc = set_local_cx_options(endp->tcfg->endpoints,
&endp->local_options, local_options);
if (rc != 0) {
LOGP(DLMGCP, LOGL_ERROR,
"CRCX: endpoint:%x inavlid local connection options!\n",
ENDPOINT_NUMBER(endp));
error_code = rc;
goto error2;
}
}
/* Handle codec information and decide for a suitable codec */
rc = handle_codec_info(conn, p, have_sdp, true);
mgcp_codec_summary(conn);
if (rc) {
error_code = rc;
goto error2;
}
conn->end.fmtp_extra = talloc_strdup(tcfg->endpoints,
tcfg->audio_fmtp_extra);
@@ -718,11 +917,15 @@ mgcp_header_done:
error2:
mgcp_endp_release(endp);
LOGP(DLMGCP, LOGL_NOTICE,
"CRCX: endpoint:0x%x unable to create connection resource error\n",
"CRCX: endpoint:0x%x unable to create connection\n",
ENDPOINT_NUMBER(endp));
return create_err_response(endp, error_code, "CRCX", p->trans);
}
/* MDCX command handler, processes the received command */
static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
{
@@ -814,23 +1017,27 @@ mgcp_header_done:
} else
conn->conn->mode = conn->conn->mode_orig;
if (have_sdp)
mgcp_parse_sdp_data(endp, conn, p);
/* Set local connection options, if present */
if (local_options) {
rc = set_local_cx_options(endp->tcfg->endpoints,
&endp->local_options, local_options);
if (rc != 0) {
LOGP(DLMGCP, LOGL_ERROR,
"MDCX: endpoint:%x inavlid local connection options!\n",
ENDPOINT_NUMBER(endp));
error_code = rc;
goto error3;
}
}
rc = set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
local_options);
if (rc != 0) {
LOGP(DLMGCP, LOGL_ERROR,
"MDCX: endpoint:%x inavlid local connection options!\n",
ENDPOINT_NUMBER(endp));
/* Handle codec information and decide for a suitable codec */
rc = handle_codec_info(conn, p, have_sdp, false);
mgcp_codec_summary(conn);
if (rc) {
error_code = rc;
goto error3;
}
if (!have_sdp && endp->local_options.codec)
mgcp_set_audio_info(p->cfg, &conn->end.codec,
PTYPE_UNDEFINED, endp->local_options.codec);
/* check connection mode setting */
if (conn->conn->mode != MGCP_CONN_LOOPBACK
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
@@ -842,6 +1049,7 @@ mgcp_header_done:
goto error3;
}
if (setup_rtp_processing(endp, conn) != 0)
goto error3;

View File

@@ -25,9 +25,13 @@
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <errno.h>
/* A struct to store intermediate parsing results. The function
* mgcp_parse_sdp_data() is using it as temporary storage for parsing the SDP
* codec information. */
struct sdp_rtp_map {
/* the type */
int payload_type;
@@ -40,89 +44,8 @@ struct sdp_rtp_map {
int channels;
};
/*! Set codec configuration depending on payload type and codec name.
* \param[in] ctx talloc context.
* \param[out] codec configuration (caller provided memory).
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
* \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
* \returns 0 on success, -1 on failure. */
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
int payload_type, const char *audio_name)
{
int rate = codec->rate;
int channels = codec->channels;
char audio_codec[64];
talloc_free(codec->subtype_name);
codec->subtype_name = NULL;
talloc_free(codec->audio_name);
codec->audio_name = NULL;
if (payload_type != PTYPE_UNDEFINED)
codec->payload_type = payload_type;
if (!audio_name) {
switch (payload_type) {
case 0:
audio_name = "PCMU/8000/1";
break;
case 3:
audio_name = "GSM/8000/1";
break;
case 8:
audio_name = "PCMA/8000/1";
break;
case 18:
audio_name = "G729/8000/1";
break;
default:
/* Payload type is unknown, don't change rate and
* channels. */
/* TODO: return value? */
return 0;
}
}
if (sscanf(audio_name, "%63[^/]/%d/%d",
audio_codec, &rate, &channels) < 1)
return -EINVAL;
codec->rate = rate;
codec->channels = channels;
codec->subtype_name = talloc_strdup(ctx, audio_codec);
codec->audio_name = talloc_strdup(ctx, audio_name);
if (!strcmp(audio_codec, "G729")) {
codec->frame_duration_num = 10;
codec->frame_duration_den = 1000;
} else {
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
}
if (payload_type < 0) {
payload_type = 96;
if (rate == 8000 && channels == 1) {
if (!strcmp(audio_codec, "GSM"))
payload_type = 3;
else if (!strcmp(audio_codec, "PCMA"))
payload_type = 8;
else if (!strcmp(audio_codec, "PCMU"))
payload_type = 0;
else if (!strcmp(audio_codec, "G729"))
payload_type = 18;
}
codec->payload_type = payload_type;
}
if (channels != 1)
LOGP(DLMGCP, LOGL_NOTICE,
"Channels != 1 in SDP: '%s'\n", audio_name);
return 0;
}
/* Helper function to extrapolate missing codec parameters in a codec mao from
* an already filled in payload_type, called from: mgcp_parse_sdp_data() */
static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
{
int i;
@@ -149,10 +72,16 @@ static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
codecs[i].rate = 8000;
codecs[i].channels = 1;
break;
default:
codecs[i].codec_name = NULL;
codecs[i].rate = 0;
codecs[i].channels = 0;
}
}
}
/* Helper function to update codec map information with additional data from
* SDP, called from: mgcp_parse_sdp_data() */
static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
int payload, const char *audio_name)
{
@@ -162,8 +91,13 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
char audio_codec[64];
int rate = -1;
int channels = -1;
/* Note: We can only update payload codecs that already exist
* in our codec list. If we get an unexpected payload type,
* we just drop it */
if (codecs[i].payload_type != payload)
continue;
if (sscanf(audio_name, "%63[^/]/%d/%d",
audio_codec, &rate, &channels) < 1) {
LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
@@ -182,43 +116,72 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
audio_name);
}
/* Check if the codec matches what is set up in the trunk config */
static int is_codec_compatible(const struct mgcp_endpoint *endp,
const struct sdp_rtp_map *codec)
/* Extract payload types from SDP, also check for duplicates */
static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
unsigned int codecs_len, char *sdp)
{
char *codec_str;
char audio_codec[64];
char *str;
char *str_ptr;
char *pt_str;
unsigned int pt;
unsigned int count = 0;
unsigned int i;
if (!codec->codec_name)
return 0;
str = talloc_zero_size(ctx, strlen(sdp) + 1);
str_ptr = str;
strcpy(str_ptr, sdp);
/* GSM, GSM/8000 and GSM/8000/1 should all be compatible...
* let's go by name first. */
codec_str = endp->tcfg->audio_name;
if (sscanf(codec_str, "%63[^/]/%*d/%*d", audio_codec) < 1)
return 0;
str_ptr = strstr(str_ptr, "RTP/AVP ");
if (!str_ptr)
goto exit;
return strcasecmp(audio_codec, codec->codec_name) == 0;
pt_str = strtok(str_ptr, " ");
if (!pt_str)
goto exit;
while (1) {
/* Do not allow excessive payload types */
if (count > codecs_len)
goto error;
pt_str = strtok(NULL, " ");
if (!pt_str)
break;
pt = atoi(pt_str);
/* Do not allow duplicate payload types */
for (i = 0; i < count; i++)
if (codecs[i].payload_type == pt)
goto error;
codecs[count].payload_type = pt;
count++;
}
exit:
talloc_free(str);
return count;
error:
talloc_free(str);
return -EINVAL;
}
/*! Analyze SDP input string.
* \param[in] endp trunk endpoint.
* \param[out] conn associated rtp connection.
* \param[out] caller provided memory to store the parsing results.
* \returns 0 on success, -1 on failure.
*
* Note: In conn (conn->end) the function returns the packet duration,
* the rtp port and the rtcp port */
* rtp port, rtcp port and the codec information.
* \returns 0 on success, -1 on failure. */
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn,
struct mgcp_parse_data *p)
struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
{
struct sdp_rtp_map codecs[10];
int codecs_used = 0;
struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
unsigned int codecs_used = 0;
char *line;
int maxptime = -1;
int i;
int codecs_assigned = 0;
unsigned int i;
void *tmp_ctx = talloc_new(NULL);
struct mgcp_rtp_end *rtp;
@@ -255,30 +218,21 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
rtp->packet_duration_ms = 0;
else
rtp->packet_duration_ms = ptime;
} else if (sscanf(line, "a=maxptime:%d", &ptime2)
== 1) {
maxptime = ptime2;
} else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
rtp->maximum_packet_time = ptime2;
}
break;
case 'm':
rc = sscanf(line,
"m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d",
&port, &codecs[0].payload_type,
&codecs[1].payload_type,
&codecs[2].payload_type,
&codecs[3].payload_type,
&codecs[4].payload_type,
&codecs[5].payload_type,
&codecs[6].payload_type,
&codecs[7].payload_type,
&codecs[8].payload_type,
&codecs[9].payload_type);
if (rc >= 2) {
rc = sscanf(line, "m=audio %d RTP/AVP", &port);
if (rc == 1) {
rtp->rtp_port = htons(port);
rtp->rtcp_port = htons(port + 1);
codecs_used = rc - 1;
codecs_initialize(tmp_ctx, codecs, codecs_used);
}
rc = pt_from_sdp(conn->conn, codecs,
ARRAY_SIZE(codecs), line);
if (rc > 0)
codecs_used = rc;
break;
case 'c':
@@ -299,43 +253,37 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
break;
}
}
OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
/* Now select the primary and alt_codec */
for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) {
struct mgcp_rtp_codec *codec = codecs_assigned == 0 ?
&rtp->codec : &rtp->alt_codec;
/* So far we have only set the payload type in the codec struct. Now we
* fill up the remaining fields of the codec description with some default
* information */
codecs_initialize(tmp_ctx, codecs, codecs_used);
if (endp->tcfg->no_audio_transcoding &&
!is_codec_compatible(endp, &codecs[i])) {
LOGP(DLMGCP, LOGL_NOTICE, "Skipping codec %s\n",
codecs[i].codec_name);
continue;
}
mgcp_set_audio_info(p->cfg, codec,
codecs[i].payload_type, codecs[i].map_line);
codecs_assigned += 1;
}
if (codecs_assigned > 0) {
/* TODO/XXX: Store this per codec and derive it on use */
if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den >
rtp->codec.frame_duration_num * 1500) {
/* more than 1 frame */
rtp->packet_duration_ms = 0;
}
LOGP(DLMGCP, LOGL_NOTICE,
"Got media info via SDP: port %d, payload %d (%s), "
"duration %d, addr %s\n",
ntohs(rtp->rtp_port), rtp->codec.payload_type,
rtp->codec.subtype_name ? rtp->
codec.subtype_name : "unknown", rtp->packet_duration_ms,
inet_ntoa(rtp->addr));
/* Store parsed codec information */
for (i = 0; i < codecs_used; i++) {
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line);
if (rc < 0)
LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
}
talloc_free(tmp_ctx);
return codecs_assigned > 0;
LOGP(DLMGCP, LOGL_NOTICE,
"Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
rtp->packet_duration_ms);
if (codecs_used == 0)
LOGPC(DLMGCP, LOGL_NOTICE, "none");
for (i = 0; i < codecs_used; i++) {
LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
rtp->codecs[i].payload_type,
rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown");
LOGPC(DLMGCP, LOGL_NOTICE, " ");
}
LOGPC(DLMGCP, LOGL_NOTICE, "\n");
return 0;
}
/*! Generate SDP response string.
@@ -380,7 +328,9 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
if (rc < 0)
goto buffer_too_small;
if (audio_name && endp->tcfg->audio_send_name) {
/* FIXME: Check if the payload type is from the static range,
* if yes, omitthe a=rtpmap since it is unnecessary */
if (audio_name && endp->tcfg->audio_send_name && (payload_type >= 96 && payload_type <= 127)) {
rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n",
payload_type, audio_name);

View File

@@ -87,9 +87,9 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) {
/* Error Counter */
nchars = snprintf(str, str_len,
"\r\nX-Osmo-CP: EC TI=%u, TO=%u",
conn->state.in_stream.err_ts_counter,
conn->state.out_stream.err_ts_counter);
"\r\nX-Osmo-CP: EC TI=%lu, TO=%lu",
conn->state.in_stream.err_ts_ctr->current,
conn->state.out_stream.err_ts_ctr->current);
if (nchars < 0 || nchars >= str_len)
goto truncate;

View File

@@ -157,18 +157,19 @@ static int config_write_mgcp(struct vty *vty)
static void dump_rtp_end(struct vty *vty, struct mgcp_rtp_state *state,
struct mgcp_rtp_end *end)
{
struct mgcp_rtp_codec *codec = &end->codec;
struct mgcp_rtp_codec *codec = end->codec;
vty_out(vty,
" Timestamp Errs: %d->%d%s"
" Timestamp Errs: %lu->%lu%s"
" Dropped Packets: %d%s"
" Payload Type: %d Rate: %u Channels: %d %s"
" Frame Duration: %u Frame Denominator: %u%s"
" FPP: %d Packet Duration: %u%s"
" FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s"
" Output-Enabled: %d Force-PTIME: %d%s",
state->in_stream.err_ts_counter,
state->out_stream.err_ts_counter, VTY_NEWLINE,
state->in_stream.err_ts_ctr->current,
state->out_stream.err_ts_ctr->current,
VTY_NEWLINE,
end->stats.dropped_packets, VTY_NEWLINE,
codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
codec->frame_duration_num, codec->frame_duration_den,
@@ -178,15 +179,31 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_rtp_state *state,
end->force_output_ptime, VTY_NEWLINE);
}
static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
int verbose)
static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp, int epidx, int verbose)
{
int i;
struct mgcp_conn *conn;
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);
vty_out(vty, "Endpoint %s%d:%s", MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, epidx, VTY_NEWLINE);
llist_for_each_entry(conn, &endp->conns, entry) {
vty_out(vty, " CONN: %s%s",
mgcp_conn_dump(conn), VTY_NEWLINE);
if (verbose) {
/* FIXME: Also add verbosity for other
* connection types (E1) as soon as
* the implementation is available */
if (conn->type == MGCP_CONN_TYPE_RTP) {
dump_rtp_end(vty, &conn->u.rtp.state,
&conn->u.rtp.end);
}
}
}
}
static void dump_endpoints(struct vty *vty, struct mgcp_trunk_config *cfg, int verbose)
{
int i;
if (!cfg->endpoints) {
vty_out(vty, "No endpoints allocated yet.%s", VTY_NEWLINE);
@@ -195,26 +212,22 @@ static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
for (i = 1; i < cfg->number_endpoints; ++i) {
struct mgcp_endpoint *endp = &cfg->endpoints[i];
vty_out(vty, "Endpoint 0x%.2x:%s", i, VTY_NEWLINE);
llist_for_each_entry(conn, &endp->conns, entry) {
vty_out(vty, " CONN: %s%s",
mgcp_conn_dump(conn), VTY_NEWLINE);
if (verbose) {
/* FIXME: Also add verbosity for other
* connection types (E1) as soon as
* the implementation is available */
if (conn->type == MGCP_CONN_TYPE_RTP) {
dump_rtp_end(vty, &conn->u.rtp.state,
&conn->u.rtp.end);
}
}
}
dump_endpoint(vty, endp, i, verbose);
if (i < cfg->number_endpoints - 1)
vty_out(vty, "%s", VTY_NEWLINE);
}
}
static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
int verbose)
{
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);
dump_endpoints(vty, cfg, verbose);
}
DEFUN(show_mcgp, show_mgcp_cmd,
"show mgcp [stats]",
SHOW_STR
@@ -236,6 +249,33 @@ DEFUN(show_mcgp, show_mgcp_cmd,
return CMD_SUCCESS;
}
DEFUN(show_mcgp_endpoint, show_mgcp_endpoint_cmd,
"show mgcp trunk <0-255> endpoint rtpbridge/<1-65534>",
SHOW_STR
"Display information about MGCP Media Gateway endpoint\n"
"Include Statistics\n")
{
struct mgcp_trunk_config *trunk;
int trunkidx = atoi(argv[1]);
int epidx = atoi(argv[2]);
int tidx = 0, i;
llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
if (tidx++ == trunkidx) {
for (i = 1; i < trunk->number_endpoints; ++i) {
struct mgcp_endpoint *endp = &trunk->endpoints[i];
if (i == epidx) {
dump_endpoint(vty, endp, i, true);
break;
}
}
break;
}
}
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp, cfg_mgcp_cmd, "mgcp", "Configure the MGCP")
{
vty->node = MGCP_NODE;
@@ -281,13 +321,6 @@ DEFUN(cfg_mgcp_bind_early,
return CMD_WARNING;
}
static void parse_range(struct mgcp_port_range *range, const char **argv)
{
range->range_start = atoi(argv[0]);
range->range_end = atoi(argv[1]);
range->last_port = g_cfg->net_ports.range_start;
}
#define RTP_STR "RTP configuration\n"
#define UDP_PORT_STR "UDP Port number\n"
#define NET_START_STR "First UDP port allocated\n"
@@ -296,11 +329,38 @@ static void parse_range(struct mgcp_port_range *range, const char **argv)
DEFUN(cfg_mgcp_rtp_port_range,
cfg_mgcp_rtp_port_range_cmd,
"rtp port-range <0-65534> <0-65534>",
"rtp port-range <1024-65534> <1025-65535>",
RTP_STR "Range of ports to use for the NET side\n"
RANGE_START_STR RANGE_END_STR)
{
parse_range(&g_cfg->net_ports, argv);
int start;
int end;
start = atoi(argv[0]);
end = atoi(argv[1]);
if (end < start) {
vty_out(vty, "range end port (%i) must be greater than the range start port (%i)!%s",
end, start, VTY_NEWLINE);
return CMD_WARNING;
}
if (start & 1) {
vty_out(vty, "range must begin at an even port number, autocorrecting port (%i) to: %i%s",
start, start & 0xFFFE, VTY_NEWLINE);
start &= 0xFFFE;
}
if ((end & 1) == 0) {
vty_out(vty, "range must end at an odd port number, autocorrecting port (%i) to: %i%s",
end, end | 1, VTY_NEWLINE);
end |= 1;
}
g_cfg->net_ports.range_start = start;
g_cfg->net_ports.range_end = end;
g_cfg->net_ports.last_port = g_cfg->net_ports.range_start;
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgcp_rtp_port_range,
@@ -1192,6 +1252,7 @@ DEFUN(cfg_mgcp_domain,
int mgcp_vty_init(void)
{
install_element_ve(&show_mgcp_cmd);
install_element_ve(&show_mgcp_endpoint_cmd);
install_element(ENABLE_NODE, &loop_conn_cmd);
install_element(ENABLE_NODE, &tap_rtp_cmd);
install_element(ENABLE_NODE, &free_endp_cmd);

View File

@@ -26,6 +26,8 @@
#include <osmocom/mgcp/mgcp_stat.h>
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
@@ -156,8 +158,8 @@ static void test_strline(void)
"s=-\r\n" \
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 16002 RTP/AVP 96\r\n" \
"a=rtpmap:96 AMR\r\n" \
"m=audio 16002 RTP/AVP 112\r\n" \
"a=rtpmap:112 AMR\r\n" \
"a=ptime:40\r\n"
#define MDCX4_PT1 \
@@ -404,7 +406,7 @@ static void test_strline(void)
"v=0\r\n" \
"o=- 1439038275 1439038275 IN IP4 192.168.181.247\r\n" \
"s=-\r\nc=IN IP4 192.168.181.247\r\n" \
"t=0 0\r\nm=audio 29084 RTP/AVP 255 0 8 3 18 4 96 97 101\r\n" \
"t=0 0\r\nm=audio 29084 RTP/AVP 0 8 3 18 4 96 97 101\r\n" \
"a=rtpmap:0 PCMU/8000\r\n" \
"a=rtpmap:8 PCMA/8000\r\n" \
"a=rtpmap:3 gsm/8000\r\n" \
@@ -425,7 +427,24 @@ static void test_strline(void)
"I: %s\r\n" \
"\r\n" \
"c=IN IP4 8.8.8.8\r\n" \
"m=audio 16434 RTP/AVP 255\r\n"
"m=audio 16434 RTP/AVP 3\r\n"
#define CRCX_NO_LCO_NO_SDP \
"CRCX 2 6@mgw MGCP 1.0\r\n" \
"M: recvonly\r\n" \
"C: 2\r\n"
#define CRCX_NO_LCO_NO_SDP_RET \
"200 2 OK\r\n" \
"I: %s\r\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 16008 RTP/AVP 0\r\n" \
"a=ptime:20\r\n"
struct mgcp_test {
const char *name;
@@ -462,6 +481,7 @@ static const struct mgcp_test tests[] = {
{"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE,.extra_fmtp =
"a=fmtp:126 0/1/2"},
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE,.extra_fmtp = "a=fmtp:126 0/1/2"},
{"CRCX", CRCX_NO_LCO_NO_SDP, CRCX_NO_LCO_NO_SDP_RET, 97},
};
static const struct mgcp_test retransmit[] = {
@@ -764,14 +784,14 @@ static void test_messages(void)
fprintf(stderr, "endpoint %d: "
"payload type %d (expected %d)\n",
last_endpoint,
conn->end.codec.payload_type, t->ptype);
conn->end.codec->payload_type, t->ptype);
if (t->ptype != PTYPE_IGNORE)
OSMO_ASSERT(conn->end.codec.payload_type ==
OSMO_ASSERT(conn->end.codec->payload_type ==
t->ptype);
/* Reset them again for next test */
conn->end.codec.payload_type = PTYPE_NONE;
conn->end.codec->payload_type = PTYPE_NONE;
}
}
@@ -1129,10 +1149,12 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
uint32_t last_ssrc = 0;
uint32_t last_timestamp = 0;
uint32_t last_seqno = 0;
int last_in_ts_err_cnt = 0;
int last_out_ts_err_cnt = 0;
uint64_t last_in_ts_err_cnt = 0;
uint64_t last_out_ts_err_cnt = 0;
struct mgcp_conn_rtp *conn = NULL;
struct mgcp_conn *_conn = NULL;
struct rate_ctr test_ctr_in;
struct rate_ctr test_ctr_out;
printf("Testing packet error detection%s%s.\n",
patch_ssrc ? ", patch SSRC" : "",
@@ -1142,6 +1164,11 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
memset(&endp, 0, sizeof(endp));
memset(&state, 0, sizeof(state));
memset(&test_ctr_in, 0, sizeof(test_ctr_in));
memset(&test_ctr_out, 0, sizeof(test_ctr_out));
state.in_stream.err_ts_ctr = &test_ctr_in;
state.out_stream.err_ts_ctr = &test_ctr_out;
endp.type = &ep_typeset.rtp;
trunk.vty_number_endpoints = 1;
@@ -1160,7 +1187,8 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
rtp = &conn->end;
rtp->codec.payload_type = 98;
OSMO_ASSERT(mgcp_codec_add(conn, PTYPE_UNDEFINED, "AMR/8000/1") == 0);
rtp->codec = &rtp->codecs[0];
for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
struct rtp_packet_info *info = test_rtp_packets1 + i;
@@ -1186,18 +1214,18 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
state.in_stream.last_tsdelta, state.in_stream.last_seq);
printf("Out TS change: %d, dTS: %d, Seq change: %d, "
"TS Err change: in %+d, out %+d\n",
"TS Err change: in +%u, out +%u\n",
state.out_stream.last_timestamp - last_timestamp,
state.out_stream.last_tsdelta,
state.out_stream.last_seq - last_seqno,
state.in_stream.err_ts_counter - last_in_ts_err_cnt,
state.out_stream.err_ts_counter - last_out_ts_err_cnt);
(unsigned int) (state.in_stream.err_ts_ctr->current - last_in_ts_err_cnt),
(unsigned int) (state.out_stream.err_ts_ctr->current - last_out_ts_err_cnt));
printf("Stats: Jitter = %u, Transit = %d\n",
calc_jitter(&state), state.stats.transit);
last_in_ts_err_cnt = state.in_stream.err_ts_counter;
last_out_ts_err_cnt = state.out_stream.err_ts_counter;
last_in_ts_err_cnt = state.in_stream.err_ts_ctr->current;
last_out_ts_err_cnt = state.out_stream.err_ts_ctr->current;
last_timestamp = state.out_stream.last_timestamp;
last_seqno = state.out_stream.last_seq;
}
@@ -1236,8 +1264,7 @@ static void test_multilple_codec(void)
endp = &cfg->trunk.endpoints[last_endpoint];
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec.payload_type == 18);
OSMO_ASSERT(conn->end.alt_codec.payload_type == 97);
OSMO_ASSERT(conn->end.codec->payload_type == 18);
/* Allocate 2@mgw with three codecs, last one ignored */
last_endpoint = -1;
@@ -1252,10 +1279,14 @@ static void test_multilple_codec(void)
endp = &cfg->trunk.endpoints[last_endpoint];
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec.payload_type == 18);
OSMO_ASSERT(conn->end.alt_codec.payload_type == 97);
OSMO_ASSERT(conn->end.codec->payload_type == 18);
/* Allocate 3@mgw with no codecs, check for PT == -1 */
/* Allocate 3@mgw with no codecs, check for PT == 0 */
/* Note: It usually makes no sense to leave the payload type list
* out. However RFC 2327 does not clearly forbid this case and
* it makes and since we already decided in OS#2658 that a missing
* LCO should pick a sane default codec, it makes sense to expect
* the same behaviour if SDP lacks proper payload type information */
last_endpoint = -1;
inp = create_msg(CRCX_MULT_3, NULL);
resp = mgcp_handle_message(cfg, inp);
@@ -1268,8 +1299,7 @@ static void test_multilple_codec(void)
endp = &cfg->trunk.endpoints[last_endpoint];
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec.payload_type == -1);
OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
OSMO_ASSERT(conn->end.codec->payload_type == 0);
/* Allocate 4@mgw with a single codec */
last_endpoint = -1;
@@ -1284,8 +1314,7 @@ static void test_multilple_codec(void)
endp = &cfg->trunk.endpoints[last_endpoint];
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec.payload_type == 18);
OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
OSMO_ASSERT(conn->end.codec->payload_type == 18);
/* Allocate 5@mgw at select GSM.. */
last_endpoint = -1;
@@ -1303,8 +1332,7 @@ static void test_multilple_codec(void)
endp = &cfg->trunk.endpoints[last_endpoint];
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec.payload_type == 3);
OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
OSMO_ASSERT(conn->end.codec->payload_type == 3);
inp = create_msg(MDCX_NAT_DUMMY, conn_id);
last_endpoint = -1;
@@ -1315,8 +1343,7 @@ static void test_multilple_codec(void)
endp = &cfg->trunk.endpoints[last_endpoint];
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec.payload_type == 3);
OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
OSMO_ASSERT(conn->end.codec->payload_type == 3);
OSMO_ASSERT(conn->end.rtp_port == htons(16434));
memset(&addr, 0, sizeof(addr));
inet_aton("8.8.8.8", &addr);
@@ -1346,8 +1373,7 @@ static void test_multilple_codec(void)
endp = &cfg->trunk.endpoints[last_endpoint];
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec.payload_type == 255);
OSMO_ASSERT(conn->end.alt_codec.payload_type == 0);
OSMO_ASSERT(conn->end.codec->payload_type == 0);
talloc_free(cfg);
}
@@ -1465,6 +1491,108 @@ const struct log_info log_info = {
.num_cat = ARRAY_SIZE(log_categories),
};
static void test_get_lco_identifier(void)
{
char *test;
printf("Testing get_lco_identifier()\n");
/* Normal case at the beginning */
test = "p:10, a:PCMU";
printf("%s -> %s\n", test, get_lco_identifier(test));
test = "p:10, a:PCMU";
printf("%s -> %s\n", test, get_lco_identifier(test));
/* Begin parsing in the middle of the value part of
* the previous LCO option value */
test = "XXXX, p:10, a:PCMU";
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
test = "XXXX,p:10,a:PCMU";
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
test = "10,a:PCMU";
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
test = "10, a:PCMU";
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
test = "10,a: PCMU";
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
test = "10 ,a: PCMU";
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
/* Begin parsing right at the end of the previous LCO
* option value */
test = ", a:PCMU";
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
test = " a:PCMU";
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
/* Empty string, result should be (null) */
test = "";
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
/* Missing colons, result should be (null) */
test = "p10, aPCMU";
printf("%s -> %s\n", test, get_lco_identifier(test));
/* Colon separated from the identifier, result should be (null) */
test = "10,a :PCMU";
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
}
static void test_check_local_cx_options(void *ctx)
{
/* Legal cases */
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU") == 0);
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU") == 0);
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, p:10, IN:10") == 0);
OSMO_ASSERT(check_local_cx_options(ctx, "one:AAA, two:BB, tree:C") ==
0);
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU") == 0);
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:G726-32") == 0);
OSMO_ASSERT(check_local_cx_options(ctx, "p:10-20, b:64") == 0);
OSMO_ASSERT(check_local_cx_options(ctx, "b:32-64, e:off") == 0);
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU;G726-32") == 0);
/* Illegal spaces before and after colon */
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, p :10") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, "a :PCMU, p:10") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, "p: 10, a:PCMU") == -1);
/* Check if multiple appearances of LCOs are rejected */
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU, p:10") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU, a:PCMU, p:10") ==
-1);
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, p:10") == -1);
/* Check if empty LCO are rejected */
OSMO_ASSERT(check_local_cx_options(ctx, "p: , a:PCMU") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, "p: , a: PCMU") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a: PCMU") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, "p:, a:PCMU") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:") == -1);
/* Garbeled beginning and ends */
OSMO_ASSERT(check_local_cx_options(ctx, ":10, a:10") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, "10, a:PCMU") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, ", a:PCMU") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, " a:PCMU") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU,") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, ") == -1);
/* Illegal strings */
OSMO_ASSERT(check_local_cx_options(ctx, " ") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, "") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, "AAA") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, ":,") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, ",:") == -1);
OSMO_ASSERT(check_local_cx_options(ctx, ",,,") == -1);
}
int main(int argc, char **argv)
{
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
@@ -1486,6 +1614,8 @@ int main(int argc, char **argv)
test_no_cycle();
test_no_name();
test_osmux_cid();
test_get_lco_identifier();
test_check_local_cx_options(ctx);
OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);

View File

@@ -404,6 +404,21 @@ using message as statically defined for comparison
================================================
Testing DLCX
creating message from statically defined input:
---------8<---------
DLCX 7 1@mgw MGCP 1.0
I: %s
C: 2
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response contains a connection id)
================================================
Testing CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 6@mgw MGCP 1.0
M: recvonly
C: 2
@@ -1031,7 +1046,7 @@ o=- 1439038275 1439038275 IN IP4 192.168.181.247
---------8<---------
creating message from statically defined input:
---------8<---------
---------8<---------
CRCX 259260421 5@mgw MGCP 1.0
C: 1355c6041e
L: p:20, a:GSM, nt:IN
@@ -1054,7 +1069,7 @@ C: 1355c6041e
a=rtpmap:97 iLBC/8000
a=fmtp:97 mode=30
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=fmtp:101 0-15
a=recvonly
---------8<---------
@@ -1069,7 +1084,7 @@ o=- 1439038275 1439038275 IN IP4 192.168.181.247
---------8<---------
creating message from statically defined input:
---------8<---------
---------8<---------
CRCX 259260421 5@mgw MGCP 1.0
C: 1355c6041e
L: p:20, a:GSM, nt:IN
@@ -1104,4 +1119,18 @@ a=ptime:40
M: recvonly
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:40
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
Testing get_lco_identifier()
p:10, a:PCMU -> p:10, a:PCMU
p:10, a:PCMU -> p:10, a:PCMU
'XXXX, p:10, a:PCMU' -> 'p:10, a:PCMU'

View File

@@ -95,21 +95,26 @@ static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
void test_response_cb(struct mgcp_response *response, void *priv)
{
unsigned int i;
OSMO_ASSERT(priv == mgcp);
mgcp_response_parse_params(response);
printf("response cb received:\n"
" head.response_code = %d\n"
" head.trans_id = %u\n"
" head.comment = %s\n"
" audio_port = %u\n"
" audio_ip = %s\n",
response->head.response_code,
response->head.trans_id,
response->head.comment,
response->audio_port,
response->audio_ip
);
printf("response cb received:\n");
printf(" head.response_code = %d\n", response->head.response_code);
printf(" head.trans_id = %u\n", response->head.trans_id);
printf(" head.comment = %s\n", response->head.comment);
printf(" audio_port = %u\n", response->audio_port);
printf(" audio_ip = %s\n", response->audio_ip);
printf(" ptime = %u\n", response->ptime);
printf(" codecs_len = %u\n", response->codecs_len);
for(i=0;i<response->codecs_len;i++)
printf(" codecs[%u] = %u\n", i, response->codecs[i]);
printf(" ptmap_len = %u\n", response->ptmap_len);
for(i=0;i<response->ptmap_len;i++) {
printf(" ptmap[%u].codec = %u\n", i, response->ptmap[i].codec);
printf(" ptmap[%u].pt = %u\n", i, response->ptmap[i].pt);
}
}
mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg)
@@ -149,8 +154,9 @@ void test_crcx(void)
"s=-\r\n"
"c=IN IP4 10.9.1.120\r\n"
"t=0 0\r\n"
"m=audio 16002 RTP/AVP 98\r\n"
"a=rtpmap:98 AMR/8000\r\n"
"m=audio 16002 RTP/AVP 110 96\r\n"
"a=rtpmap:110 AMR/8000\r\n"
"a=rtpmap:96 GSM-EFR/8000\r\n"
"a=ptime:20\r\n");
}
@@ -166,7 +172,15 @@ void test_mgcp_msg(void)
.audio_port = 1234,
.call_id = 47,
.conn_id = "11",
.conn_mode = MGCP_CONN_RECV_SEND
.conn_mode = MGCP_CONN_RECV_SEND,
.ptime = 20,
.codecs[0] = CODEC_GSM_8000_1,
.codecs[1] = CODEC_AMR_8000_1,
.codecs[2] = CODEC_GSMEFR_8000_1,
.codecs_len = 1,
.ptmap[0].codec = CODEC_GSMEFR_8000_1,
.ptmap[0].pt = 96,
.ptmap_len = 1
};
if (mgcp)
@@ -183,6 +197,26 @@ void test_mgcp_msg(void)
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
printf("%s\n", (char *)msg->data);
printf("Generated CRCX message (two codecs):\n");
mgcp_msg.verb = MGCP_VERB_CRCX;
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
mgcp_msg.codecs_len = 2;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated CRCX message (three codecs, one with custom pt):\n");
mgcp_msg.verb = MGCP_VERB_CRCX;
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
mgcp_msg.codecs_len = 3;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated MDCX message:\n");
mgcp_msg.verb = MGCP_VERB_MDCX;
mgcp_msg.presence =
@@ -192,6 +226,28 @@ void test_mgcp_msg(void)
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
printf("%s\n", (char *)msg->data);
printf("Generated MDCX message (two codecs):\n");
mgcp_msg.verb = MGCP_VERB_MDCX;
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
mgcp_msg.codecs_len = 2;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated MDCX message (three codecs, one with custom pt):\n");
mgcp_msg.verb = MGCP_VERB_MDCX;
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
mgcp_msg.codecs_len = 3;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated DLCX message:\n");
mgcp_msg.verb = MGCP_VERB_DLCX;
mgcp_msg.presence =
@@ -242,6 +298,9 @@ void test_mgcp_client_cancel()
.conn_mode = MGCP_CONN_RECV_SEND,
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID
| MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE),
.ptime = 20,
.codecs[0] = CODEC_AMR_8000_1,
.codecs_len = 1
};
printf("\n%s():\n", __func__);
@@ -376,6 +435,99 @@ void test_sdp_section_start()
OSMO_ASSERT(!failures);
}
static void test_map_pt_to_codec(void)
{
/* Full form */
OSMO_ASSERT(map_str_to_codec("PCMU/8000/1") == CODEC_PCMU_8000_1);
OSMO_ASSERT(map_str_to_codec("GSM/8000/1") == CODEC_GSM_8000_1);
OSMO_ASSERT(map_str_to_codec("PCMA/8000/1") == CODEC_PCMA_8000_1);
OSMO_ASSERT(map_str_to_codec("G729/8000/1") == CODEC_G729_8000_1);
OSMO_ASSERT(map_str_to_codec("GSM-EFR/8000/1") == CODEC_GSMEFR_8000_1);
OSMO_ASSERT(map_str_to_codec("GSM-HR-08/8000/1") == CODEC_GSMHR_8000_1);
OSMO_ASSERT(map_str_to_codec("AMR/8000/1") == CODEC_AMR_8000_1);
OSMO_ASSERT(map_str_to_codec("AMR-WB/16000/1") == CODEC_AMRWB_16000_1);
/* Short form */
OSMO_ASSERT(map_str_to_codec("GSM-EFR") == CODEC_GSMEFR_8000_1);
OSMO_ASSERT(map_str_to_codec("G729") == CODEC_G729_8000_1);
OSMO_ASSERT(map_str_to_codec("GSM-HR-08") == CODEC_GSMHR_8000_1);
/* We do not care about what is after the first delimiter */
OSMO_ASSERT(map_str_to_codec("AMR-WB/123///456") == CODEC_AMRWB_16000_1);
OSMO_ASSERT(map_str_to_codec("PCMA/asdf") == CODEC_PCMA_8000_1);
OSMO_ASSERT(map_str_to_codec("GSM/qwertz") == CODEC_GSM_8000_1);
/* A trailing delimiter should not hurt */
OSMO_ASSERT(map_str_to_codec("AMR/") == CODEC_AMR_8000_1);
OSMO_ASSERT(map_str_to_codec("G729/") == CODEC_G729_8000_1);
OSMO_ASSERT(map_str_to_codec("GSM/") == CODEC_GSM_8000_1);
/* This is expected to fail */
OSMO_ASSERT(map_str_to_codec("INVALID/1234/7") == -1);
OSMO_ASSERT(map_str_to_codec(NULL) == -1);
OSMO_ASSERT(map_str_to_codec("") == -1);
OSMO_ASSERT(map_str_to_codec("/////") == -1);
/* The buffers are 64 bytes long, check what happens with overlong
* strings as input (This schould still work.) */
OSMO_ASSERT(map_str_to_codec("AMR-WB/16000/1############################################################################################################") == CODEC_AMRWB_16000_1);
/* This should not work, as there is no delimiter after the codec
* name */
OSMO_ASSERT(map_str_to_codec("AMR-WB####################################################################################################################") == -1);
}
static void test_map_codec_to_pt_and_map_pt_to_codec(void)
{
struct ptmap ptmap[10];
unsigned int ptmap_len;
unsigned int i;
ptmap[0].codec = CODEC_GSMEFR_8000_1;
ptmap[0].pt = 96;
ptmap[1].codec = CODEC_GSMHR_8000_1;
ptmap[1].pt = 97;
ptmap[2].codec = CODEC_AMR_8000_1;
ptmap[2].pt = 98;
ptmap[3].codec = CODEC_AMRWB_16000_1;
ptmap[3].pt = 99;
ptmap_len = 4;
/* Mappings that are covered by the table */
for (i = 0; i < ptmap_len; i++)
printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
for (i = 0; i < ptmap_len; i++)
printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
printf("\n");
/* Map some codecs/payload types from the static range, result must
* always be a 1:1 mapping */
printf(" %u => %u\n", CODEC_PCMU_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMU_8000_1));
printf(" %u => %u\n", CODEC_GSM_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_GSM_8000_1));
printf(" %u => %u\n", CODEC_PCMA_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMA_8000_1));
printf(" %u => %u\n", CODEC_G729_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_G729_8000_1));
printf(" %u <= %u\n", CODEC_PCMU_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMU_8000_1));
printf(" %u <= %u\n", CODEC_GSM_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_GSM_8000_1));
printf(" %u <= %u\n", CODEC_PCMA_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMA_8000_1));
printf(" %u <= %u\n", CODEC_G729_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_G729_8000_1));
printf("\n");
/* Try to do mappings from statically defined range to danymic range and vice versa. This
* is illegal and should result into a 1:1 mapping */
ptmap[3].codec = CODEC_AMRWB_16000_1;
ptmap[3].pt = 2;
ptmap[4].codec = CODEC_PCMU_8000_1;
ptmap[4].pt = 100;
ptmap_len = 5;
/* Apply all mappings again, the illegal ones we defined should result into 1:1 mappings */
for (i = 0; i < ptmap_len; i++)
printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
for (i = 0; i < ptmap_len; i++)
printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
printf("\n");
}
static const struct log_info_cat log_categories[] = {
};
@@ -403,6 +555,8 @@ int main(int argc, char **argv)
test_mgcp_msg();
test_mgcp_client_cancel();
test_sdp_section_start();
test_map_codec_to_pt_and_map_pt_to_codec();
test_map_pt_to_codec();
printf("Done\n");
fprintf(stderr, "Done\n");

View File

@@ -62,4 +62,8 @@ test_sdp_section_start() test [9]:
body: "some mgcp header data\r\nand header params\n\r\rm=audio 23\r\n"
DLMGCP MGCP response: cannot find start of SDP parameters
got rc=-22
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
Done

View File

@@ -18,8 +18,9 @@ o=- 1 23 IN IP4 10.9.1.120
s=-
c=IN IP4 10.9.1.120
t=0 0
m=audio 16002 RTP/AVP 98
a=rtpmap:98 AMR/8000
m=audio 16002 RTP/AVP 110 96
a=rtpmap:110 AMR/8000
a=rtpmap:96 GSM-EFR/8000
a=ptime:20
-----
@@ -29,16 +30,39 @@ response cb received:
head.comment = OK
audio_port = 16002
audio_ip = 10.9.1.120
ptime = 20
codecs_len = 2
codecs[0] = 112
codecs[1] = 110
ptmap_len = 2
ptmap[0].codec = 112
ptmap[0].pt = 110
ptmap[1].codec = 110
ptmap[1].pt = 96
Generated CRCX message:
CRCX 1 23@mgw MGCP 1.0
C: 2f
I: 11
L: p:20, a:AMR, nt:IN
L: p:20, a:GSM, nt:IN
M: sendrecv
Generated CRCX message (two codecs):
CRCX 2 23@mgw MGCP 1.0
C: 2f
I: 11
L: p:20, a:GSM;AMR, nt:IN
M: sendrecv
Generated CRCX message (three codecs, one with custom pt):
CRCX 3 23@mgw MGCP 1.0
C: 2f
I: 11
L: p:20, a:GSM;AMR;GSM-EFR, nt:IN
M: sendrecv
Generated MDCX message:
MDCX 2 23@mgw MGCP 1.0
MDCX 4 23@mgw MGCP 1.0
C: 2f
I: 11
M: sendrecv
@@ -48,18 +72,50 @@ o=- 2f 23 IN IP4 127.0.0.1
s=-
c=IN IP4 192.168.100.23
t=0 0
m=audio 1234 RTP/AVP 255
m=audio 1234 RTP/AVP 3
a=ptime:20
Generated MDCX message (two codecs):
MDCX 5 23@mgw MGCP 1.0
C: 2f
I: 11
M: sendrecv
v=0
o=- 2f 23 IN IP4 127.0.0.1
s=-
c=IN IP4 192.168.100.23
t=0 0
m=audio 1234 RTP/AVP 3 112
a=rtpmap:112 AMR/8000/1
a=ptime:20
Generated MDCX message (three codecs, one with custom pt):
MDCX 6 23@mgw MGCP 1.0
C: 2f
I: 11
M: sendrecv
v=0
o=- 2f 23 IN IP4 127.0.0.1
s=-
c=IN IP4 192.168.100.23
t=0 0
m=audio 1234 RTP/AVP 3 112 96
a=rtpmap:112 AMR/8000/1
a=rtpmap:96 GSM-EFR/8000/1
a=ptime:20
Generated DLCX message:
DLCX 3 23@mgw MGCP 1.0
DLCX 7 23@mgw MGCP 1.0
C: 2f
I: 11
Generated AUEP message:
AUEP 4 23@mgw MGCP 1.0
AUEP 8 23@mgw MGCP 1.0
Generated RSIP message:
RSIP 5 23@mgw MGCP 1.0
RSIP 9 23@mgw MGCP 1.0
Overfolow test:
@@ -102,4 +158,33 @@ test_sdp_section_start() test [7]:
test_sdp_section_start() test [8]:
test_sdp_section_start() test [9]:
110 => 96
111 => 97
112 => 98
113 => 99
96 <= 110
97 <= 111
98 <= 112
99 <= 113
0 => 0
3 => 3
8 => 8
18 => 18
0 <= 0
3 <= 3
8 <= 8
18 <= 18
110 => 96
111 => 97
112 => 98
113 => 113
0 => 0
96 <= 110
97 <= 111
98 <= 112
2 <= 2
100 <= 100
Done