mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-11-03 21:43:32 +00:00 
			
		
		
		
	Compare commits
	
		
			25 Commits
		
	
	
		
			1.3.0
			...
			stsp/show_
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					e3c2eaeec0 | ||
| 
						 | 
					704c4f0adf | ||
| 
						 | 
					bc0346e080 | ||
| 
						 | 
					5928dc9345 | ||
| 
						 | 
					04da5e5e98 | ||
| 
						 | 
					54b4f82f91 | ||
| 
						 | 
					3d7b58d77a | ||
| 
						 | 
					604410cd13 | ||
| 
						 | 
					b340f90c9e | ||
| 
						 | 
					3c8ccb6724 | ||
| 
						 | 
					d4e6aa42ca | ||
| 
						 | 
					e6df0e47e7 | ||
| 
						 | 
					54eb0e1204 | ||
| 
						 | 
					b38fb8911f | ||
| 
						 | 
					1b3a385b9d | ||
| 
						 | 
					dbd70c7b68 | ||
| 
						 | 
					a19547b7a1 | ||
| 
						 | 
					06823731d8 | ||
| 
						 | 
					ed1cff5ab9 | ||
| 
						 | 
					f2321b7a72 | ||
| 
						 | 
					4219904cb2 | ||
| 
						 | 
					b2753f2044 | ||
| 
						 | 
					ba61f68137 | ||
| 
						 | 
					9e1d164469 | ||
| 
						 | 
					0ec1d4e17c | 
@@ -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 */
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -5,5 +5,6 @@ noinst_HEADERS = \
 | 
			
		||||
	mgcp_stat.h \
 | 
			
		||||
	mgcp_endp.h \
 | 
			
		||||
	mgcp_sdp.h \
 | 
			
		||||
	mgcp_codec.h \
 | 
			
		||||
	debug.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								include/osmocom/mgcp/mgcp_codec.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								include/osmocom/mgcp/mgcp_codec.h
									
									
									
									
									
										Normal 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);
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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 */
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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. */
 | 
			
		||||
 
 | 
			
		||||
@@ -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 \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										343
									
								
								src/libosmo-mgcp/mgcp_codec.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										343
									
								
								src/libosmo-mgcp/mgcp_codec.c
									
									
									
									
									
										Normal 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;
 | 
			
		||||
}
 | 
			
		||||
@@ -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.
 | 
			
		||||
 
 | 
			
		||||
@@ -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",
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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'
 | 
			
		||||
 
 | 
			
		||||
@@ -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");
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user