Codebase list osmo-mgw / 6c1c9e9
Merge tag 'upstream/1.4.0' Upstream version 1.4.0 Ruben Undheim 5 years ago
38 changed file(s) with 2202 addition(s) and 620 deletion(s). Raw diff Collapse all Expand all
3737 stamp-h1
3838 libtool
3939 ltmain.sh
40 m4/*.m4
4041
4142 # git-version-gen magic
4243 .tarball-version
3838 AC_SUBST(LIBRARY_DL)
3939
4040
41 PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
42 PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.11.0)
43 PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
44 PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.2.0)
41 PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.12.0)
42 PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.12.0)
43 PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.12.0)
44 PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.3.0)
4545
4646 AC_ARG_ENABLE(sanitize,
4747 [AS_HELP_STRING(
9191 if test -n "$v"
9292 then
9393 : # use $v
94 elif test -d ./.git \
95 && v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
94 elif
95 v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
9696 || git describe --abbrev=4 HEAD 2>/dev/null` \
9797 && case $v in
9898 [0-9]*) ;;
242242 * message.
243243 */
244244 uint16_t osmux_dummy;
245
246 /* Use a jitterbuffer on the bts-side receiver */
247 bool bts_use_jibuf;
248 /* Minimum and maximum buffer size for the jitter buffer, in ms */
249 uint32_t bts_jitter_delay_min;
250 uint32_t bts_jitter_delay_max;
245251 };
246252
247253 /* config management */
2424 #include <string.h>
2525
2626 #include <osmocom/core/select.h>
27 #include <osmocom/netif/jibuf.h>
2728
2829 #define CI_UNUSED 0
2930
197198 uint32_t octets;
198199 } stats;
199200 } osmux;
201
202 /* Jitter buffer */
203 struct osmo_jibuf* bts_jb;
204 /* Use a jitterbuffer on the bts-side receiver */
205 bool bts_use_jibuf;
206 /* Minimum and maximum buffer size for the jitter buffer, in ms */
207 uint32_t bts_jitter_delay_min;
208 uint32_t bts_jitter_delay_max;
200209 };
201210
202211 #define for_each_line(line, save) \
334343 }
335344
336345 int mgcp_msg_terminate_nul(struct msgb *msg);
346
347 /**
348 * Internal jitter buffer related
349 */
350 void mgcp_dejitter_udp_send(struct msgb *msg, void *data);
44 mgcp_stat.h \
55 mgcp_endp.h \
66 mgcp_sdp.h \
7 mgcp_codec.h \
78 debug.h \
89 $(NULL)
7373 typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
7474 struct mgcp_rtp_end *dst_end,
7575 char *data, int *len, int buf_size);
76
77 struct mgcp_conn_rtp;
78
7679 typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
77 struct mgcp_rtp_end *dst_end,
78 struct mgcp_rtp_end *src_end);
79
80 struct mgcp_conn_rtp;
80 struct mgcp_conn_rtp *conn_dst,
81 struct mgcp_conn_rtp *conn_src);
82
8183 typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
8284 int *payload_type,
8385 const char**subtype_name,
0 #pragma once
1
2 void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
3 void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
4 int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name);
5 int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
8181 /* A prefix to denote the virtual trunk (RTP on both ends) */
8282 #define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/"
8383
84 /* Maximal number of payload types / codecs that can be negotiated via SDP at
85 * at once. */
86 #define MGCP_MAX_CODECS 10
87
8488 #endif
2626 #include <osmocom/core/linuxlist.h>
2727 #include <inttypes.h>
2828
29 /* RTP connection related counters */
30 enum {
31 IN_STREAM_ERR_TSTMP_CTR,
32 OUT_STREAM_ERR_TSTMP_CTR,
33 RTP_PACKETS_RX_CTR,
34 RTP_OCTETS_RX_CTR,
35 RTP_PACKETS_TX_CTR,
36 RTP_OCTETS_TX_CTR,
37 RTP_DROPPED_PACKETS_CTR
38 };
39
2940 struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
3041 enum mgcp_conn_type type, char *name);
3142 struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
4141
4242 /*! MGCP endpoint properties */
4343 struct mgcp_endpoint_type {
44 /*!< maximum number of connections */
44 /*! maximum number of connections */
4545 int max_conns;
4646
47 /*!< callback that defines how to dispatch incoming RTP data */
47 /*! callback that defines how to dispatch incoming RTP data */
4848 mgcp_dispatch_rtp_cb dispatch_rtp_cb;
4949
50 /*!< callback that implements endpoint specific cleanup actions */
50 /*! callback that implements endpoint specific cleanup actions */
5151 mgcp_cleanup_cp cleanup_cb;
5252 };
5353
6262 /*! MGCP endpoint model */
6363 struct mgcp_endpoint {
6464
65 /*!< Call identifier string (as supplied by the call agant) */
65 /*! Call identifier string (as supplied by the call agant) */
6666 char *callid;
6767
68 /*!< Local connection options (see mgcp_intermal.h) */
68 /*! Local connection options (see mgcp_internal.h) */
6969 struct mgcp_lco local_options;
7070
71 /*!< List with connections active on this endpoint */
71 /*! List with connections active on this endpoint */
7272 struct llist_head conns;
7373
74 /*!< Backpointer to the MGW configuration */
74 /*! Backpointer to the MGW configuration */
7575 struct mgcp_config *cfg;
7676
77 /*!< Backpointer to the Trunk specific configuration */
77 /*! Backpointer to the Trunk specific configuration */
7878 struct mgcp_trunk_config *tcfg;
7979
80 /*!< Endpoint properties (see above) */
80 /*! Endpoint properties (see above) */
8181 const struct mgcp_endpoint_type *type;
8282
83 /*!< Last MGCP transmission (in case re-transmission is required) */
83 /*! Last MGCP transmission (in case re-transmission is required) */
8484 char *last_trans;
8585
86 /*!< Last MGCP response (in case re-transmission is required) */
86 /*! Last MGCP response (in case re-transmission is required) */
8787 char *last_response;
8888
89 /*!< Memorize if this endpoint was choosen by the MGW (wildcarded, true)
89 /*! Memorize if this endpoint was choosen by the MGW (wildcarded, true)
9090 * or if the user has choosen the particular endpoint explicitly. */
9191 bool wildcarded_req;
9292 };
2727 #include <osmocom/mgcp/mgcp.h>
2828 #include <osmocom/core/linuxlist.h>
2929 #include <osmocom/core/counter.h>
30 #include <osmocom/core/rate_ctr.h>
3031
3132 #define CI_UNUSED 0
3233
4445 uint32_t ssrc;
4546 uint16_t last_seq;
4647 uint32_t last_timestamp;
47 uint32_t err_ts_counter;
48 struct rate_ctr *err_ts_ctr;
4849 int32_t last_tsdelta;
4950 uint32_t last_arrival_time;
5051 };
9798
9899 /* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
99100 struct mgcp_rtp_end {
100 /* statistics */
101 struct {
102 unsigned int packets_rx;
103 unsigned int octets_rx;
104 unsigned int packets_tx;
105 unsigned int octets_tx;
106 unsigned int dropped_packets;
107 } stats;
108
109101 /* local IP address of the RTP socket */
110102 struct in_addr addr;
111103
112104 /* in network byte order */
113105 int rtp_port, rtcp_port;
114106
115 /* audio codec information */
116 struct mgcp_rtp_codec codec;
117 struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */
107 /* currently selected audio codec */
108 struct mgcp_rtp_codec *codec;
109
110 /* array with assigned audio codecs to choose from (SDP) */
111 struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
112
113 /* number of assigned audio codecs (SDP) */
114 unsigned int codecs_assigned;
118115
119116 /* per endpoint data */
120117 int frames_per_packet;
121118 uint32_t packet_duration_ms;
119 int maximum_packet_time; /* -1: not set */
122120 char *fmtp_extra;
123121 /* are we transmitting packets (1) or dropping (0) outbound packets */
124122 int output_enabled;
129127 int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
130128 /* should we perform align_rtp_timestamp_offset() (1) or not (0) */
131129 int force_aligned_timing;
132 /* FIXME: not used anymore, used to be [external] transcoding related */
133 void *rtp_process_data;
134130
135131 /* Each end has a separate socket for RTP and RTCP */
136132 struct osmo_fd rtp;
201197 uint32_t octets;
202198 } stats;
203199 } osmux;
200
201 struct rate_ctr_group *rate_ctr_group;
204202 };
205203
206204 /*! Connection type, specifies which member of the union "u" in mgcp_conn
211209
212210 /*! MGCP connection (untyped) */
213211 struct mgcp_conn {
214 /*!< list head */
212 /*! list head */
215213 struct llist_head entry;
216214
217 /*!< Backpointer to the endpoint where the conn belongs to */
215 /*! Backpointer to the endpoint where the conn belongs to */
218216 struct mgcp_endpoint *endp;
219217
220 /*!< type of the connection (union) */
218 /*! type of the connection (union) */
221219 enum mgcp_conn_type type;
222220
223 /*!< mode of the connection */
221 /*! mode of the connection */
224222 enum mgcp_connection_mode mode;
225223
226 /*!< copy of the mode to restore the original setting (VTY) */
224 /*! copy of the mode to restore the original setting (VTY) */
227225 enum mgcp_connection_mode mode_orig;
228226
229 /*!< connection id to identify the connection */
227 /*! connection id to identify the connection */
230228 char id[MGCP_CONN_ID_LENGTH];
231229
232 /*!< human readable name (vty, logging) */
230 /*! human readable name (vty, logging) */
233231 char name[256];
234232
235 /*!< union with connection description */
233 /*! union with connection description */
236234 union {
237235 struct mgcp_conn_rtp rtp;
238236 } u;
239237
240 /*!< pointer to optional private data */
238 /*! pointer to optional private data */
241239 void *priv;
242240 };
243241
279277 struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
280278 struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
281279
280 char *get_lco_identifier(const char *options);
281 int check_local_cx_options(void *ctx, const char *options);
282282 void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
283283 struct mgcp_rtp_end *rtp);
284284 uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
289289 char *data, int *len, int buf_size);
290290
291291 int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
292 struct mgcp_rtp_end *dst_end,
293 struct mgcp_rtp_end *src_end);
292 struct mgcp_conn_rtp *conn_dst,
293 struct mgcp_conn_rtp *conn_src);
294294
295295 void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
296296 int *payload_type,
2020 */
2121
2222 #pragma once
23 #include <osmocom/mgcp/mgcp_sdp.h>
2423
2524 int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
2625 struct mgcp_conn_rtp *conn,
2726 struct mgcp_parse_data *p);
2827
29 int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
30 int payload_type, const char *audio_name);
31
3228 int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
3329 const struct mgcp_conn_rtp *conn, struct msgb *sdp,
3430 const char *addr);
2929 void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn);
3030
3131 /* Exposed for test purposes only, do not use actively */
32 void calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *,
33 uint32_t *expected, int *loss);
32 void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss);
3433
3534 /* Exposed for test purposes only, do not use actively */
3635 uint32_t calc_jitter(struct mgcp_rtp_state *);
2525
2626 typedef unsigned int mgcp_trans_id_t;
2727
28 /*! Enumeration of the codec types that mgcp_client is able to handle. */
29 enum mgcp_codecs {
30 CODEC_PCMU_8000_1 = 0,
31 CODEC_GSM_8000_1 = 3,
32 CODEC_PCMA_8000_1 = 8,
33 CODEC_G729_8000_1 = 18,
34 CODEC_GSMEFR_8000_1 = 110,
35 CODEC_GSMHR_8000_1 = 111,
36 CODEC_AMR_8000_1 = 112,
37 CODEC_AMRWB_16000_1 = 113,
38 };
39 /* Note: when new codec types are added, the corresponding value strings
40 * in mgcp_client.c (codec_table) must be updated as well. Enumerations
41 * in enum mgcp_codecs must correspond to a valid payload type. However,
42 * this is an internal assumption that is made to avoid lookup tables.
43 * The API-User should not rely on this coincidence! */
44
45 /*! Structure to build a payload type map to allow the defiition custom payload
46 * types. */
47 struct ptmap {
48 /*! codec for which a payload type number should be defined */
49 enum mgcp_codecs codec;
50
51 /*! payload type number (96-127) */
52 unsigned int pt;
53 };
54
2855 struct mgcp_response_head {
2956 int response_code;
3057 mgcp_trans_id_t trans_id;
3865 struct mgcp_response_head head;
3966 uint16_t audio_port;
4067 char audio_ip[INET_ADDRSTRLEN];
68 unsigned int ptime;
69 enum mgcp_codecs codecs[MGCP_MAX_CODECS];
70 unsigned int codecs_len;
71 struct ptmap ptmap[MGCP_MAX_CODECS];
72 unsigned int ptmap_len;
4173 };
4274
4375 enum mgcp_verb {
6597 uint16_t audio_port;
6698 char *audio_ip;
6799 enum mgcp_connection_mode conn_mode;
100 unsigned int ptime;
101 enum mgcp_codecs codecs[MGCP_MAX_CODECS];
102 unsigned int codecs_len;
103 struct ptmap ptmap[MGCP_MAX_CODECS];
104 unsigned int ptmap_len;
68105 };
69106
70107 void mgcp_client_conf_init(struct mgcp_client_conf *conf);
116153 {
117154 return get_value_string(mgcp_client_connection_mode_strs, mode);
118155 }
156
157 enum mgcp_codecs map_str_to_codec(const char *str);
158 unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
159 enum mgcp_codecs codec);
160 enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
161 unsigned int pt);
1313 * identifier is supplied it is checked against the internal state to make
1414 * sure it is correct. */
1515 struct mgcp_conn_peer {
16 /*!< RTP connection IP-Address (optional, string e.g. "127.0.0.1") */
16 /*! RTP connection IP-Address (optional, string e.g. "127.0.0.1") */
1717 char addr[INET_ADDRSTRLEN];
1818
19 /*!< RTP connection IP-Port (optional) */
19 /*! RTP connection IP-Port (optional) */
2020 uint16_t port;
2121
22 /*!< RTP endpoint */
22 /*! RTP endpoint */
2323 char endpoint[MGCP_ENDPOINT_MAXLEN];
2424
25 /*!< CALL ID (unique per connection) */
25 /*! CALL ID (unique per connection) */
2626 unsigned int call_id;
27
28 /*! RTP packetization interval (optional) */
29 unsigned int ptime;
30
31 /*! RTP codec list (optional) */
32 enum mgcp_codecs codecs[MGCP_MAX_CODECS];
33
34 /*! Number of codecs in RTP codec list (optional) */
35 unsigned int codecs_len;
2736 };
2837
2938 struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
3039 uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
3140 int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
3241 void mgcp_conn_delete(struct osmo_fsm_inst *fi);
42
43 const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi);
2323
2424 # This is not at all related to the release version, but a range of supported
2525 # API versions. Read TODO_RELEASE in the source tree's root!
26 LEGACY_MGCP_LIBVERSION=0:1:0
26 LEGACY_MGCP_LIBVERSION=1:0:1
2727
2828 lib_LTLIBRARIES = \
2929 libosmo-legacy-mgcp.la \
583583 return rc;
584584 }
585585
586 void mgcp_dejitter_udp_send(struct msgb *msg, void *data)
587 {
588 struct mgcp_rtp_end *rtp_end = (struct mgcp_rtp_end *) data;
589
590 int rc = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr,
591 rtp_end->rtp_port, (char*) msg->data, msg->len);
592 if (rc != msg->len)
593 LOGP(DLMGCP, LOGL_ERROR,
594 "Failed to send data after jitter buffer: %d\n", rc);
595 msgb_free(msg);
596 }
597
598 static int enqueue_dejitter(struct osmo_jibuf *jb, struct mgcp_rtp_end *rtp_end, char *buf, int len)
599 {
600 struct msgb *msg;
601 msg = msgb_alloc(len, "mgcp-jibuf");
602 if (!msg)
603 return -1;
604
605 memcpy(msg->data, buf, len);
606 msgb_put(msg, len);
607
608 if (osmo_jibuf_enqueue(jb, msg) < 0) {
609 rtp_end->dropped_packets += 1;
610 msgb_free(msg);
611 }
612
613 return len;
614 }
615
586616 int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
587617 struct sockaddr_in *addr, char *buf, int rc)
588618 {
590620 struct mgcp_rtp_end *rtp_end;
591621 struct mgcp_rtp_state *rtp_state;
592622 int tap_idx;
623 struct osmo_jibuf *jb;
593624
594625 LOGP(DLMGCP, LOGL_DEBUG,
595626 "endpoint %x dest %s tcfg->audio_loop %d endp->conn_mode %d (== loopback: %d)\n",
611642 rtp_end = &endp->net_end;
612643 rtp_state = &endp->bts_state;
613644 tap_idx = MGCP_TAP_NET_OUT;
645 jb = endp->bts_jb;
614646 } else {
615647 rtp_end = &endp->bts_end;
616648 rtp_state = &endp->net_state;
617649 tap_idx = MGCP_TAP_BTS_OUT;
650 jb = NULL;
618651 }
619652 LOGP(DLMGCP, LOGL_DEBUG,
620653 "endpoint %x dest %s net_end %s %d %d bts_end %s %d %d rtp_end %s %d %d\n",
679712 rtp_state->patched_first_rtp_payload = true;
680713 }
681714
682 rc = mgcp_udp_send(rtp_end->rtp.fd,
683 &rtp_end->addr,
684 rtp_end->rtp_port, buf, len);
715 if (jb)
716 rc = enqueue_dejitter(jb, rtp_end, buf, len);
717 else
718 rc = mgcp_udp_send(rtp_end->rtp.fd,
719 &rtp_end->addr,
720 rtp_end->rtp_port, buf, len);
685721
686722 if (rc <= 0)
687723 return rc;
266266 {
267267 struct msgb *msg;
268268 struct osmux_hdr *osmuxh;
269 struct llist_head list;
270269 struct sockaddr_in addr;
271270 struct mgcp_config *cfg = ofd->data;
272271 uint32_t rem;
296295 endp->osmux.stats.chunks++;
297296 rem = msg->len;
298297
299 osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
300 osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
298 osmux_xfrm_output_sched(&endp->osmux.out, osmuxh);
301299 }
302300 out:
303301 msgb_free(msg);
358356 {
359357 struct msgb *msg;
360358 struct osmux_hdr *osmuxh;
361 struct llist_head list;
362359 struct sockaddr_in addr;
363360 struct mgcp_config *cfg = ofd->data;
364361 uint32_t rem;
388385 endp->osmux.stats.chunks++;
389386 rem = msg->len;
390387
391 osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
392 osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
388 osmux_xfrm_output_sched(&endp->osmux.out, osmuxh);
393389 }
394390 out:
395391 msgb_free(msg);
469465 switch (endp->cfg->role) {
470466 case MGCP_BSC_NAT:
471467 endp->type = MGCP_OSMUX_BSC_NAT;
468 osmux_xfrm_output_set_tx_cb(&endp->osmux.out,
469 scheduled_tx_net_cb, endp);
472470 break;
473471 case MGCP_BSC:
474472 endp->type = MGCP_OSMUX_BSC;
473 osmux_xfrm_output_set_tx_cb(&endp->osmux.out,
474 scheduled_tx_bts_cb, endp);
475475 break;
476476 }
477477 endp->osmux.state = OSMUX_STATE_ENABLED;
483483 {
484484 LOGP(DLMGCP, LOGL_INFO, "Releasing endpoint %u using Osmux CID %u\n",
485485 ENDPOINT_NUMBER(endp), endp->osmux.cid);
486
487 /* We are closing, we don't need pending RTP packets to be transmitted */
488 osmux_xfrm_output_set_tx_cb(&endp->osmux.out, NULL, NULL);
489 osmux_xfrm_output_flush(&endp->osmux.out);
490
486491 osmux_xfrm_input_close_circuit(endp->osmux.in, endp->osmux.cid);
487492 endp->osmux.state = OSMUX_STATE_DISABLED;
488493 endp->osmux.cid = -1;
862862 goto error2;
863863 }
864864
865 /* Apply Jiter buffer settings for this endpoint, they can be overriden by CRCX policy later */
866 endp->bts_use_jibuf = endp->cfg->bts_use_jibuf;
867 endp->bts_jitter_delay_min = endp->cfg->bts_jitter_delay_min;
868 endp->bts_jitter_delay_max = endp->cfg->bts_jitter_delay_max;
869
865870 endp->allocated = 1;
866871
867872 /* set up RTP media parameters */
897902 case MGCP_POLICY_DEFER:
898903 /* stop processing */
899904 create_transcoder(endp);
905 /* Set up jitter buffer if required after policy has updated jibuf endp values */
906 if (endp->bts_use_jibuf) {
907 endp->bts_jb = osmo_jibuf_alloc(tcfg->endpoints);
908 osmo_jibuf_set_min_delay(endp->bts_jb, endp->bts_jitter_delay_min);
909 osmo_jibuf_set_max_delay(endp->bts_jb, endp->bts_jitter_delay_max);
910 osmo_jibuf_set_dequeue_cb(endp->bts_jb, mgcp_dejitter_udp_send, &endp->net_end);
911 }
900912 return NULL;
901913 break;
902914 case MGCP_POLICY_CONT:
903915 /* just continue */
904916 break;
905917 }
918 }
919
920 /* Set up jitter buffer if required after policy has updated jibuf endp values */
921 if (endp->bts_use_jibuf) {
922 endp->bts_jb = osmo_jibuf_alloc(tcfg->endpoints);
923 osmo_jibuf_set_min_delay(endp->bts_jb, endp->bts_jitter_delay_min);
924 osmo_jibuf_set_max_delay(endp->bts_jb, endp->bts_jitter_delay_max);
925 osmo_jibuf_set_dequeue_cb(endp->bts_jb, mgcp_dejitter_udp_send, &endp->net_end);
906926 }
907927
908928 LOGP(DLMGCP, LOGL_DEBUG, "Creating endpoint on: 0x%x CI: %u port: %u/%u\n",
13721392 void mgcp_release_endp(struct mgcp_endpoint *endp)
13731393 {
13741394 LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
1395 if (endp->bts_jb)
1396 osmo_jibuf_delete(endp->bts_jb);
1397 endp->bts_jb = NULL;
13751398 endp->ci = CI_UNUSED;
13761399 endp->allocated = 0;
13771400
2828 #include <osmocom/legacy_mgcp/vty.h>
2929
3030 #include <string.h>
31 #include <inttypes.h>
3132
3233 #define RTCP_OMIT_STR "Drop RTCP packets in both directions\n"
3334 #define RTP_PATCH_STR "Modify RTP packet header in both directions\n"
163164 vty_out(vty, " osmux dummy %s%s",
164165 g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);
165166 }
167 if (g_cfg->bts_use_jibuf)
168 vty_out(vty, " bts-jitter-buffer%s", VTY_NEWLINE);
169 if (g_cfg->bts_jitter_delay_min)
170 vty_out(vty, " bts-jitter-delay-min %"PRIu32"%s", g_cfg->bts_jitter_delay_min, VTY_NEWLINE);
171 if (g_cfg->bts_jitter_delay_max)
172 vty_out(vty, " bts-jitter-delay-max %"PRIu32"%s", g_cfg->bts_jitter_delay_max, VTY_NEWLINE);
173
166174 return CMD_SUCCESS;
167175 }
168176
240248
241249 if (g_cfg->osmux)
242250 vty_out(vty, "Osmux used CID: %d%s", osmux_used_cid(), VTY_NEWLINE);
251 vty_out(vty, "Jitter Buffer by default on Uplink : %s%s",
252 g_cfg->bts_use_jibuf ? "on" : "off", VTY_NEWLINE);
253 if (g_cfg->bts_use_jibuf)
254 vty_out(vty, "Jitter Buffer delays: min=%"PRIu32" max=%"PRIu32"%s",
255 g_cfg->bts_jitter_delay_min, g_cfg->bts_jitter_delay_max, VTY_NEWLINE);
243256
244257 return CMD_SUCCESS;
245258 }
13401353 else if (strcmp(argv[0], "off") == 0)
13411354 g_cfg->osmux_dummy = 0;
13421355
1356 return CMD_SUCCESS;
1357 }
1358
1359 #define DEJITTER_STR "Uplink Jitter Buffer"
1360 DEFUN(cfg_mgcp_bts_use_jibuf,
1361 cfg_mgcp_bts_use_jibuf_cmd,
1362 "bts-jitter-buffer",
1363 DEJITTER_STR "\n")
1364 {
1365 g_cfg->bts_use_jibuf = true;
1366 return CMD_SUCCESS;
1367 }
1368
1369 DEFUN(cfg_mgcp_no_bts_use_jibuf,
1370 cfg_mgcp_no_bts_use_jibuf_cmd,
1371 "no bts-jitter-buffer",
1372 NO_STR DEJITTER_STR "\n")
1373 {
1374 g_cfg->bts_use_jibuf = false;
1375 return CMD_SUCCESS;
1376 }
1377
1378 DEFUN(cfg_mgcp_bts_jitter_delay_min,
1379 cfg_mgcp_bts_jitter_delay_min_cmd,
1380 "bts-jitter-buffer-delay-min <1-65535>",
1381 DEJITTER_STR " Minimum Delay in ms\n" "Minimum Delay in ms\n")
1382 {
1383 g_cfg->bts_jitter_delay_min = atoi(argv[0]);
1384 if (!g_cfg->bts_jitter_delay_min) {
1385 vty_out(vty, "bts-jitter-buffer-delay-min cannot be zero.%s", VTY_NEWLINE);
1386 return CMD_WARNING;
1387 }
1388 if (g_cfg->bts_jitter_delay_min && g_cfg->bts_jitter_delay_max &&
1389 g_cfg->bts_jitter_delay_min > g_cfg->bts_jitter_delay_max) {
1390 vty_out(vty, "bts-jitter-buffer-delay-min cannot be bigger than " \
1391 "bts-jitter-buffer-delay-max.%s", VTY_NEWLINE);
1392 return CMD_WARNING;
1393 }
1394 return CMD_SUCCESS;
1395 }
1396
1397 DEFUN(cfg_mgcp_bts_jitter_delay_max,
1398 cfg_mgcp_bts_jitter_delay_max_cmd,
1399 "bts-jitter-buffer-delay-max <1-65535>",
1400 DEJITTER_STR " Maximum Delay in ms\n" "Maximum Delay in ms\n")
1401 {
1402 g_cfg->bts_jitter_delay_max = atoi(argv[0]);
1403 if (!g_cfg->bts_jitter_delay_max) {
1404 vty_out(vty, "bts-jitter-buffer-delay-max cannot be zero.%s", VTY_NEWLINE);
1405 return CMD_WARNING;
1406 }
1407 if (g_cfg->bts_jitter_delay_min && g_cfg->bts_jitter_delay_max &&
1408 g_cfg->bts_jitter_delay_min > g_cfg->bts_jitter_delay_max) {
1409 vty_out(vty, "bts-jitter-buffer-delay-max cannot be smaller than " \
1410 "bts-jitter-buffer-delay-min.%s", VTY_NEWLINE);
1411 return CMD_WARNING;
1412 }
13431413 return CMD_SUCCESS;
13441414 }
13451415
14101480 install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd);
14111481 install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
14121482 install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
1483 install_element(MGCP_NODE, &cfg_mgcp_bts_use_jibuf_cmd);
1484 install_element(MGCP_NODE, &cfg_mgcp_no_bts_use_jibuf_cmd);
1485 install_element(MGCP_NODE, &cfg_mgcp_bts_jitter_delay_min_cmd);
1486 install_element(MGCP_NODE, &cfg_mgcp_bts_jitter_delay_max_cmd);
14131487
14141488
14151489 install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
3434 mgcp_vty.c \
3535 mgcp_osmux.c \
3636 mgcp_sdp.c \
37 mgcp_codec.c \
3738 mgcp_msg.c \
3839 mgcp_conn.c \
3940 mgcp_stat.c \
0 /*
1 * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
2 * (C) 2009-2014 by On-Waves
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19 #include <osmocom/mgcp/mgcp_internal.h>
20 #include <osmocom/mgcp/mgcp_endp.h>
21 #include <errno.h>
22
23 /* Helper function to dump codec information of a specified codec to a printable
24 * string, used by dump_codec_summary() */
25 static char *dump_codec(struct mgcp_rtp_codec *codec)
26 {
27 static char str[256];
28 char *pt_str;
29
30 if (codec->payload_type > 76)
31 pt_str = "DYNAMIC";
32 else if (codec->payload_type > 72)
33 pt_str = "RESERVED <!>";
34 else if (codec->payload_type != PTYPE_UNDEFINED)
35 pt_str = codec->subtype_name;
36 else
37 pt_str = "INVALID <!>";
38
39 snprintf(str, sizeof(str), "(pt:%i=%s, audio:%s subt=%s, rate=%u, ch=%i, t=%u/%u)", codec->payload_type, pt_str,
40 codec->audio_name, codec->subtype_name, codec->rate, codec->channels, codec->frame_duration_num,
41 codec->frame_duration_den);
42 return str;
43 }
44
45 /*! Dump a summary of all negotiated codecs to debug log
46 * \param[in] conn related rtp-connection. */
47 void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
48 {
49 struct mgcp_rtp_end *rtp;
50 unsigned int i;
51 struct mgcp_rtp_codec *codec;
52 struct mgcp_endpoint *endp;
53
54 rtp = &conn->end;
55 endp = conn->conn->endp;
56
57 if (rtp->codecs_assigned == 0) {
58 LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x conn:%s no codecs available\n", ENDPOINT_NUMBER(endp),
59 mgcp_conn_dump(conn->conn));
60 return;
61 }
62
63 /* Store parsed codec information */
64 for (i = 0; i < rtp->codecs_assigned; i++) {
65 codec = &rtp->codecs[i];
66
67 LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x conn:%s codecs[%u]:%s", ENDPOINT_NUMBER(endp),
68 mgcp_conn_dump(conn->conn), i, dump_codec(codec));
69
70 if (codec == rtp->codec)
71 LOGPC(DLMGCP, LOGL_DEBUG, " [selected]");
72
73 LOGPC(DLMGCP, LOGL_DEBUG, "\n");
74 }
75 }
76
77 /* Initalize or reset codec information with default data. */
78 void codec_init(struct mgcp_rtp_codec *codec)
79 {
80 if (codec->subtype_name)
81 talloc_free(codec->subtype_name);
82 if (codec->audio_name)
83 talloc_free(codec->audio_name);
84 memset(codec, 0, sizeof(*codec));
85 codec->payload_type = -1;
86 codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
87 codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
88 codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
89 codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
90 }
91
92 /*! Initalize or reset codec information with default data.
93 * \param[out] conn related rtp-connection. */
94 void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
95 {
96 memset(conn->end.codecs, 0, sizeof(conn->end.codecs));
97 conn->end.codecs_assigned = 0;
98 conn->end.codec = NULL;
99 }
100
101 /* Set members of struct mgcp_rtp_codec, extrapolate in missing information */
102 static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
103 int payload_type, const char *audio_name, unsigned int pt_offset)
104 {
105 int rate;
106 int channels;
107 char audio_codec[64];
108
109 /* Initalize the codec struct with some default data to begin with */
110 codec_init(codec);
111
112 if (payload_type != PTYPE_UNDEFINED) {
113 /* Make sure we do not get any reserved or undefined type numbers */
114 /* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
115 if (payload_type == 1 || payload_type == 2 || payload_type == 19)
116 goto error;
117 if (payload_type >= 72 && payload_type <= 76)
118 goto error;
119 if (payload_type >= 127)
120 goto error;
121
122 codec->payload_type = payload_type;
123 }
124
125 /* When no audio name is given, we are forced to use the payload
126 * type to generate the audio name. This is only possible for
127 * non dynamic payload types, which are statically defined */
128 if (!audio_name) {
129 switch (payload_type) {
130 case 0:
131 audio_name = talloc_strdup(ctx, "PCMU/8000/1");
132 break;
133 case 3:
134 audio_name = talloc_strdup(ctx, "GSM/8000/1");
135 break;
136 case 8:
137 audio_name = talloc_strdup(ctx, "PCMA/8000/1");
138 break;
139 case 18:
140 audio_name = talloc_strdup(ctx, "G729/8000/1");
141 break;
142 default:
143 /* The given payload type is not known to us, or it
144 * it is a dynamic payload type for which we do not
145 * know the audio name. We must give up here */
146 goto error;
147 }
148 }
149
150 /* Now we extract the codec subtype name, rate and channels. The latter
151 * two are optional. If they are not present we use the safe defaults
152 * above. */
153 if (strlen(audio_name) > sizeof(audio_codec))
154 goto error;
155 channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
156 rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
157 if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1)
158 goto error;
159
160 /* Note: We only accept configurations with one audio channel! */
161 if (channels != 1)
162 goto error;
163
164 codec->rate = rate;
165 codec->channels = channels;
166 codec->subtype_name = talloc_strdup(ctx, audio_codec);
167 codec->audio_name = talloc_strdup(ctx, audio_name);
168 codec->payload_type = payload_type;
169
170 if (!strcmp(audio_codec, "G729")) {
171 codec->frame_duration_num = 10;
172 codec->frame_duration_den = 1000;
173 } else {
174 codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
175 codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
176 }
177
178 /* Derive the payload type if it is unknown */
179 if (codec->payload_type == PTYPE_UNDEFINED) {
180
181 /* For the known codecs from the static range we restore
182 * the IANA or 3GPP assigned payload type number */
183 if (codec->rate == 8000 && codec->channels == 1) {
184 /* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
185 if (!strcmp(codec->subtype_name, "GSM"))
186 codec->payload_type = 3;
187 else if (!strcmp(codec->subtype_name, "PCMA"))
188 codec->payload_type = 8;
189 else if (!strcmp(codec->subtype_name, "PCMU"))
190 codec->payload_type = 0;
191 else if (!strcmp(codec->subtype_name, "G729"))
192 codec->payload_type = 18;
193
194 /* See also: 3GPP TS 48.103, chapter 5.4.2.2 RTP Payload
195 * Note: These are not fixed payload types as the IANA
196 * defined once, they still remain dymanic payload
197 * types, but with a payload type number preference. */
198 else if (!strcmp(codec->subtype_name, "GSM-EFR"))
199 codec->payload_type = 110;
200 else if (!strcmp(codec->subtype_name, "GSM-HR-08"))
201 codec->payload_type = 111;
202 else if (!strcmp(codec->subtype_name, "AMR"))
203 codec->payload_type = 112;
204 else if (!strcmp(codec->subtype_name, "AMR-WB"))
205 codec->payload_type = 113;
206 }
207
208 /* If we could not determine a payload type we assume that
209 * we are dealing with a codec from the dynamic range. We
210 * choose a fixed identifier from 96-109. (Note: normally,
211 * the dynamic payload type rante is from 96-127, but from
212 * 110 onwards 3gpp defines prefered codec types, which are
213 * also fixed, see above) */
214 if (codec->payload_type < 0) {
215 codec->payload_type = 96 + pt_offset;
216 if (codec->payload_type > 109)
217 goto error;
218 }
219 }
220
221 return 0;
222 error:
223 /* Make sure we leave a clean codec entry on error. */
224 codec_init(codec);
225 memset(codec, 0, sizeof(*codec));
226 return -EINVAL;
227 }
228
229 /*! Add codec configuration depending on payload type and/or codec name. This
230 * function uses the input parameters to extrapolate the full codec information.
231 * \param[out] codec configuration (caller provided memory).
232 * \param[out] conn related rtp-connection.
233 * \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
234 * \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
235 * \returns 0 on success, -EINVAL on failure. */
236 int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name)
237 {
238 int rc;
239
240 /* The amount of codecs we can store is limited, make sure we do not
241 * overrun this limit. */
242 if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
243 return -EINVAL;
244
245 rc = codec_set(conn->conn, &conn->end.codecs[conn->end.codecs_assigned], payload_type, audio_name,
246 conn->end.codecs_assigned);
247 if (rc != 0)
248 return -EINVAL;
249
250 conn->end.codecs_assigned++;
251
252 return 0;
253 }
254
255 /* Check if the given codec is applicable on the specified endpoint
256 * Helper function for mgcp_codec_decide() */
257 static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec)
258 {
259 char codec_name[64];
260
261 /* A codec name must be set, if not, this might mean that the codec
262 * (payload type) that was assigned is unknown to us so we must stop
263 * here. */
264 if (!codec->subtype_name)
265 return false;
266
267 /* We now extract the codec_name (letters before the /, e.g. "GSM"
268 * from the audio name that is stored in the trunk configuration.
269 * We do not compare to the full audio_name because we expect that
270 * "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the
271 * audio name of the codec is set to "GSM" */
272 if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1)
273 return false;
274
275 /* Finally we check if the subtype_name we have generated from the
276 * audio_name in the trunc struct patches the codec_name of the
277 * given codec */
278 if (strcasecmp(codec_name, codec->subtype_name) == 0)
279 return true;
280
281 /* FIXME: It is questinable that the method to pick a compatible
282 * codec can work properly. Since this useses tcfg->audio_name, as
283 * a reference, which is set to "AMR/8000" permanently.
284 * tcfg->audio_name must be updated by the first connection that
285 * has been made on an endpoint, so that the second connection
286 * can make a meaningful decision here */
287
288 return false;
289 }
290
291 /*! Decide for one suitable codec
292 * \param[in] conn related rtp-connection.
293 * \returns 0 on success, -EINVAL on failure. */
294 int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
295 {
296 struct mgcp_rtp_end *rtp;
297 unsigned int i;
298 struct mgcp_endpoint *endp;
299 bool codec_assigned = false;
300
301 endp = conn->conn->endp;
302 rtp = &conn->end;
303
304 /* This function works on the results the SDP/LCO parser has extracted
305 * from the MGCP message. The goal is to select a suitable codec for
306 * the given connection. When transcoding is available, the first codec
307 * from the codec list is taken without further checking. When
308 * transcoding is not available, then the choice must be made more
309 * carefully. Each codec in the list is checked until one is found that
310 * is rated compatible. The rating is done by the helper function
311 * is_codec_compatible(), which does the actual checking. */
312 for (i = 0; i < rtp->codecs_assigned; i++) {
313 /* When no transcoding is available, avoid codecs that would
314 * require transcoding. */
315 if (endp->tcfg->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
316 LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n",
317 rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name);
318 continue;
319 }
320
321 rtp->codec = &rtp->codecs[i];
322 codec_assigned = true;
323 break;
324 }
325
326 /* FIXME: To the reviewes: This is problematic. I do not get why we
327 * need to reset the packet_duration_ms depending on the codec
328 * selection. I thought it were all 20ms? Is this to address some
329 * cornercase. (This piece of code was in the code path before,
330 * together with the note: "TODO/XXX: Store this per codec and derive
331 * it on use" */
332 if (codec_assigned) {
333 if (rtp->maximum_packet_time >= 0
334 && rtp->maximum_packet_time * rtp->codec->frame_duration_den >
335 rtp->codec->frame_duration_num * 1500)
336 rtp->packet_duration_ms = 0;
337
338 return 0;
339 }
340
341 return -EINVAL;
342 }
2424 #include <osmocom/mgcp/mgcp_internal.h>
2525 #include <osmocom/mgcp/mgcp_common.h>
2626 #include <osmocom/mgcp/mgcp_endp.h>
27 #include <osmocom/mgcp/mgcp_sdp.h>
28 #include <osmocom/mgcp/mgcp_codec.h>
2729 #include <osmocom/gsm/gsm_utils.h>
30 #include <osmocom/core/rate_ctr.h>
2831 #include <ctype.h>
32
33 static const struct rate_ctr_desc rate_ctr_desc[] = {
34 [IN_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:in", "Inbound rtp-stream timestamp errors."},
35 [OUT_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:out", "Outbound rtp-stream timestamp errors."},
36 [RTP_PACKETS_RX_CTR] = {"rtp:packets_rx", "Inbound rtp packets."},
37 [RTP_OCTETS_RX_CTR] = {"rtp:octets_rx", "Inbound rtp octets."},
38 [RTP_PACKETS_TX_CTR] = {"rtp:packets_tx", "Outbound rtp packets."},
39 [RTP_OCTETS_TX_CTR] = {"rtp:octets_rx", "Outbound rtp octets."},
40 [RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "dropped rtp packets."}
41 };
42
43 const static struct rate_ctr_group_desc rate_ctr_group_desc = {
44 .group_name_prefix = "conn_rtp",
45 .group_description = "rtp connection statistics",
46 .class_id = 1,
47 .num_ctr = ARRAY_SIZE(rate_ctr_desc),
48 .ctr_desc = rate_ctr_desc
49 };
50
2951
3052 /* Allocate a new connection identifier. According to RFC3435, they must
3153 * be unique only within the scope of the endpoint. (Caller must provide
6688 return -1;
6789 }
6890
69 /* Reset codec state and free memory */
70 static void mgcp_rtp_codec_init(struct mgcp_rtp_codec *codec)
71 {
72 codec->payload_type = -1;
73 codec->subtype_name = NULL;
74 codec->audio_name = NULL;
75 codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
76 codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
77 codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
78 codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
79
80 /* see also mgcp_sdp.c, mgcp_set_audio_info() */
81 talloc_free(codec->subtype_name);
82 talloc_free(codec->audio_name);
83 }
84
8591 /* Initialize rtp connection struct with default values */
8692 static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
8793 {
8894 struct mgcp_rtp_end *end = &conn_rtp->end;
95 /* FIXME: Each new rate counter group requires an unique index. At the
96 * moment we generate this index using this counter, but perhaps there
97 * is a more concious way to assign the indexes. */
98 static unsigned int rate_ctr_index = 0;
8999
90100 conn_rtp->type = MGCP_RTP_DEFAULT;
91101 conn_rtp->osmux.allocated_cid = -1;
95105
96106 end->rtp.fd = -1;
97107 end->rtcp.fd = -1;
98 memset(&end->stats, 0, sizeof(end->stats));
99108 end->rtp_port = end->rtcp_port = 0;
100109 talloc_free(end->fmtp_extra);
101110 end->fmtp_extra = NULL;
104113 end->frames_per_packet = 0; /* unknown */
105114 end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
106115 end->output_enabled = 0;
107
108 mgcp_rtp_codec_init(&end->codec);
109 mgcp_rtp_codec_init(&end->alt_codec);
116 end->maximum_packet_time = -1;
117
118 conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index);
119 conn_rtp->state.in_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[IN_STREAM_ERR_TSTMP_CTR];
120 conn_rtp->state.out_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[OUT_STREAM_ERR_TSTMP_CTR];
121 rate_ctr_index++;
122
123 /* Make sure codec table is reset */
124 mgcp_codec_reset_all(conn_rtp);
110125 }
111126
112127 /* Cleanup rtp connection struct */
115130 osmux_disable_conn(conn_rtp);
116131 osmux_release_cid(conn_rtp);
117132 mgcp_free_rtp_port(&conn_rtp->end);
133 rate_ctr_group_free(conn_rtp->rate_ctr_group);
118134 }
119135
120136 /*! allocate a new connection list entry.
221221
222222 if (seq == sstate->last_seq) {
223223 if (timestamp != sstate->last_timestamp) {
224 sstate->err_ts_counter += 1;
224 rate_ctr_inc(sstate->err_ts_ctr);
225225 LOGP(DRTP, LOGL_ERROR,
226226 "The %s timestamp delta is != 0 but the sequence "
227227 "number %d is the same, "
271271 ts_alignment_error(sstate, state->packet_duration, timestamp);
272272
273273 if (timestamp_error) {
274 sstate->err_ts_counter += 1;
274 rate_ctr_inc(sstate->err_ts_ctr);
275275 LOGP(DRTP, LOGL_NOTICE,
276276 "The %s timestamp has an alignment error of %d "
277277 "on 0x%x SSRC: %u "
309309 ENDPOINT_NUMBER(endp), tsdelta,
310310 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
311311 } else {
312 tsdelta = rtp_end->codec.rate * 20 / 1000;
312 tsdelta = rtp_end->codec->rate * 20 / 1000;
313313 LOGP(DRTP, LOGL_NOTICE,
314314 "Fixed packet duration and last timestamp delta "
315315 "are not available on 0x%x, "
398398
399399 /*! dummy callback to disable transcoding (see also cfg->setup_rtp_processing_cb).
400400 * \param[in] associated endpoint
401 * \param[in] destination RTP end
402 * \param[in] source RTP end
401 * \param[in] destination RTP connnection
402 * \param[in] source RTP connection
403403 * \returns ignores input parameters, return always 0 */
404404 int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
405 struct mgcp_rtp_end *dst_end,
406 struct mgcp_rtp_end *src_end)
405 struct mgcp_conn_rtp *conn_dst,
406 struct mgcp_conn_rtp *conn_src)
407407 {
408408 LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x transcoding disabled\n",
409409 ENDPOINT_NUMBER(endp));
420420 "endpoint:0x%x conn:%s using format defaults\n",
421421 ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn));
422422
423 *payload_type = conn->end.codec.payload_type;
424 *audio_name = conn->end.codec.audio_name;
423 *payload_type = conn->end.codec->payload_type;
424 *audio_name = conn->end.codec->audio_name;
425425 *fmtp_extra = conn->end.fmtp_extra;
426426 }
427427
489489 uint16_t seq;
490490 uint32_t timestamp, ssrc;
491491 struct rtp_hdr *rtp_hdr;
492 int payload = rtp_end->codec.payload_type;
492 int payload = rtp_end->codec->payload_type;
493493
494494 if (len < sizeof(*rtp_hdr))
495495 return;
497497 rtp_hdr = (struct rtp_hdr *)data;
498498 seq = ntohs(rtp_hdr->sequence);
499499 timestamp = ntohl(rtp_hdr->timestamp);
500 arrival_time = get_current_ts(rtp_end->codec.rate);
500 arrival_time = get_current_ts(rtp_end->codec->rate);
501501 ssrc = ntohl(rtp_hdr->ssrc);
502502 transit = arrival_time - timestamp;
503503
510510 state->in_stream.last_tsdelta = 0;
511511 state->packet_duration =
512512 mgcp_rtp_packet_duration(endp, rtp_end);
513 state->out_stream = state->in_stream;
513 state->out_stream.last_seq = seq - 1;
514 state->out_stream.ssrc = state->patch.orig_ssrc = ssrc;
515 state->out_stream.last_tsdelta = 0;
514516 state->out_stream.last_timestamp = timestamp;
515517 state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */
516518 LOGP(DRTP, LOGL_INFO,
521523 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
522524 if (state->packet_duration == 0) {
523525 state->packet_duration =
524 rtp_end->codec.rate * 20 / 1000;
526 rtp_end->codec->rate * 20 / 1000;
525527 LOGP(DRTP, LOGL_NOTICE,
526528 "endpoint:0x%x fixed packet duration is not available, "
527529 "using fixed 20ms instead: %d from %s:%d\n",
677679 }
678680
679681 LOGP(DRTP, LOGL_DEBUG,
680 "endpoint:0x%x loop:%d, mode:%d ",
681 ENDPOINT_NUMBER(endp), tcfg->audio_loop, conn_src->conn->mode);
682 if (conn_src->conn->mode == MGCP_CONN_LOOPBACK)
683 LOGPC(DRTP, LOGL_DEBUG, "(loopback)\n");
684 else
685 LOGPC(DRTP, LOGL_DEBUG, "\n");
682 "endpoint:0x%x loop:%d, mode:%d%s\n",
683 ENDPOINT_NUMBER(endp), tcfg->audio_loop, conn_src->conn->mode,
684 conn_src->conn->mode == MGCP_CONN_LOOPBACK ? " (loopback)" : "");
686685
687686 /* Note: In case of loopback configuration, both, the source and the
688687 * destination will point to the same connection. */
691690 dest_name = conn_dst->conn->name;
692691
693692 if (!rtp_end->output_enabled) {
694 rtp_end->stats.dropped_packets += 1;
693 rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_DROPPED_PACKETS_CTR]);
695694 LOGP(DRTP, LOGL_DEBUG,
696695 "endpoint:0x%x output disabled, drop to %s %s "
697696 "rtp_port:%u rtcp_port:%u\n",
732731 * 'e400', or it will reject the RAB assignment. It seems to not harm other femto
733732 * cells (as long as we patch only the first RTP payload in each stream).
734733 */
735 if (!rtp_state->patched_first_rtp_payload) {
734 if (!rtp_state->patched_first_rtp_payload
735 && conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
736736 uint8_t *data = (uint8_t *) & buf[12];
737 data[0] = 0xe4;
738 data[1] = 0x00;
739 rtp_state->patched_first_rtp_payload = true;
737 if (data[0] == 0xe0) {
738 data[0] = 0xe4;
739 data[1] = 0x00;
740 rtp_state->patched_first_rtp_payload = true;
741 LOGP(DRTP, LOGL_DEBUG,
742 "endpoint:0x%x Patching over first two bytes"
743 " to fake an IuUP Initialization Ack\n",
744 ENDPOINT_NUMBER(endp));
745 }
740746 }
741747
742748 len = mgcp_udp_send(rtp_end->rtp.fd,
746752 if (len <= 0)
747753 return len;
748754
749 conn_dst->end.stats.packets_tx += 1;
750 conn_dst->end.stats.octets_tx += len;
755 rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
756 rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len);
751757
752758 nbytes += len;
753759 buflen = cont;
766772 &rtp_end->addr,
767773 rtp_end->rtcp_port, buf, len);
768774
769 conn_dst->end.stats.packets_tx += 1;
770 conn_dst->end.stats.octets_tx += len;
775 rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
776 rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len);
771777
772778 return len;
773779 }
864870 struct mgcp_endpoint *endp;
865871 endp = conn->conn->endp;
866872
873 /* Note: it is legal to create a connection but never setting a port
874 * and IP-address for outgoing data. */
875 if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0 && conn->end.rtp_port == 0) {
876 LOGP(DRTP, LOGL_DEBUG,
877 "endpoint:0x%x destination IP-address and rtp port is (not yet) known\n",
878 ENDPOINT_NUMBER(endp));
879 return -1;
880 }
881
867882 if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0) {
868883 LOGP(DRTP, LOGL_ERROR,
869884 "endpoint:0x%x destination IP-address is invalid\n",
927942 }
928943
929944 /* Increment RX statistics */
930 conn->end.stats.packets_rx += 1;
931 conn->end.stats.octets_rx += rc;
945 rate_ctr_inc(&conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]);
946 rate_ctr_add(&conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], rc);
932947
933948 /* Forward a copy of the RTP data to a debug ip/port */
934949 forward_data(fd->fd, &conn->tap_in, buf, rc);
255255 .sin_port = conn_net->end.rtp_port,
256256 };
257257
258 conn_bts->end.stats.octets_tx += msg->len;
259 conn_bts->end.stats.packets_tx++;
258 rate_ctr_inc(&conn_bts->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
259 rate_ctr_add(&conn_bts->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], msg->len);
260260
261261 /* Send RTP data to NET */
262262 /* FIXME: Get rid of conn_bts and conn_net! */
282282 .sin_port = conn_bts->end.rtp_port,
283283 };
284284
285 conn_net->end.stats.octets_tx += msg->len;
286 conn_net->end.stats.packets_tx++;
285 rate_ctr_inc(&conn_net->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
286 rate_ctr_add(&conn_net->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], msg->len);
287287
288288 /* Send RTP data to BTS */
289289 /* FIXME: Get rid of conn_bts and conn_net! */
321321 {
322322 struct msgb *msg;
323323 struct osmux_hdr *osmuxh;
324 struct llist_head list;
324 struct sockaddr_in addr;
325 struct mgcp_config *cfg = ofd->data;
326 uint32_t rem;
327 struct mgcp_conn_rtp *conn_bts = NULL;
328
329 msg = osmux_recv(ofd, &addr);
330 if (!msg)
331 return -1;
332
333 /* not any further processing dummy messages */
334 if (msg->data[0] == MGCP_DUMMY_LOAD)
335 goto out;
336
337 rem = msg->len;
338 while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
339 struct mgcp_endpoint *endp;
340
341 /* Yes, we use MGCP_DEST_NET to locate the origin */
342 endp = endpoint_lookup(cfg, osmuxh->circuit_id,
343 &addr.sin_addr, MGCP_DEST_NET);
344
345 /* FIXME: Get rid of CONN_ID_XXX! */
346 conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
347 if (!conn_bts)
348 goto out;
349
350 if (!endp) {
351 LOGP(DLMGCP, LOGL_ERROR,
352 "Cannot find an endpoint for circuit_id=%d\n",
353 osmuxh->circuit_id);
354 goto out;
355 }
356 conn_bts->osmux.stats.octets += osmux_chunk_length(msg, rem);
357 conn_bts->osmux.stats.chunks++;
358 rem = msg->len;
359
360 osmux_xfrm_output_sched(&conn_bts->osmux.out, osmuxh);
361 }
362 out:
363 msgb_free(msg);
364 return 0;
365 }
366
367 /* This is called from the bsc-nat */
368 static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
369 struct msgb *msg)
370 {
371 struct mgcp_endpoint *endp;
372 uint8_t osmux_cid;
373 struct mgcp_conn_rtp *conn_net = NULL;
374
375 if (msg->len < 1 + sizeof(osmux_cid)) {
376 LOGP(DLMGCP, LOGL_ERROR,
377 "Discarding truncated Osmux dummy load\n");
378 goto out;
379 }
380
381 LOGP(DLMGCP, LOGL_DEBUG, "Received Osmux dummy load from %s\n",
382 inet_ntoa(addr->sin_addr));
383
384 if (!cfg->osmux) {
385 LOGP(DLMGCP, LOGL_ERROR,
386 "bsc wants to use Osmux but bsc-nat did not request it\n");
387 goto out;
388 }
389
390 /* extract the osmux CID from the dummy message */
391 memcpy(&osmux_cid, &msg->data[1], sizeof(osmux_cid));
392
393 endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, MGCP_DEST_BTS);
394 if (!endp) {
395 LOGP(DLMGCP, LOGL_ERROR,
396 "Cannot find endpoint for Osmux CID %d\n", osmux_cid);
397 goto out;
398 }
399
400 conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
401 if (!conn_net)
402 goto out;
403
404 if (conn_net->osmux.state == OSMUX_STATE_ENABLED)
405 goto out;
406
407 if (osmux_enable_conn(endp, conn_net, &addr->sin_addr, addr->sin_port) < 0 ) {
408 LOGP(DLMGCP, LOGL_ERROR,
409 "Could not enable osmux in endpoint %d\n",
410 ENDPOINT_NUMBER(endp));
411 goto out;
412 }
413
414 LOGP(DLMGCP, LOGL_INFO, "Enabling osmux in endpoint %d for %s:%u\n",
415 ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr),
416 ntohs(addr->sin_port));
417 out:
418 msgb_free(msg);
419 return 0;
420 }
421
422 int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
423 {
424 struct msgb *msg;
425 struct osmux_hdr *osmuxh;
325426 struct sockaddr_in addr;
326427 struct mgcp_config *cfg = ofd->data;
327428 uint32_t rem;
333434
334435 /* not any further processing dummy messages */
335436 if (msg->data[0] == MGCP_DUMMY_LOAD)
336 goto out;
437 return osmux_handle_dummy(cfg, &addr, msg);
337438
338439 rem = msg->len;
339440 while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
340441 struct mgcp_endpoint *endp;
341442
342 /* Yes, we use MGCP_DEST_NET to locate the origin */
443 /* Yes, we use MGCP_DEST_BTS to locate the origin */
343444 endp = endpoint_lookup(cfg, osmuxh->circuit_id,
344 &addr.sin_addr, MGCP_DEST_NET);
445 &addr.sin_addr, MGCP_DEST_BTS);
345446
346447 /* FIXME: Get rid of CONN_ID_XXX! */
347448 conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
358459 conn_net->osmux.stats.chunks++;
359460 rem = msg->len;
360461
361 osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
362 osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
363 }
364 out:
365 msgb_free(msg);
366 return 0;
367 }
368
369 /* This is called from the bsc-nat */
370 static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
371 struct msgb *msg)
372 {
373 struct mgcp_endpoint *endp;
374 uint8_t osmux_cid;
375 struct mgcp_conn_rtp *conn_net = NULL;
376
377 if (msg->len < 1 + sizeof(osmux_cid)) {
378 LOGP(DLMGCP, LOGL_ERROR,
379 "Discarding truncated Osmux dummy load\n");
380 goto out;
381 }
382
383 LOGP(DLMGCP, LOGL_DEBUG, "Received Osmux dummy load from %s\n",
384 inet_ntoa(addr->sin_addr));
385
386 if (!cfg->osmux) {
387 LOGP(DLMGCP, LOGL_ERROR,
388 "bsc wants to use Osmux but bsc-nat did not request it\n");
389 goto out;
390 }
391
392 /* extract the osmux CID from the dummy message */
393 memcpy(&osmux_cid, &msg->data[1], sizeof(osmux_cid));
394
395 endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, MGCP_DEST_BTS);
396 if (!endp) {
397 LOGP(DLMGCP, LOGL_ERROR,
398 "Cannot find endpoint for Osmux CID %d\n", osmux_cid);
399 goto out;
400 }
401
402 conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
403 if (!conn_net)
404 goto out;
405
406 if (conn_net->osmux.state == OSMUX_STATE_ENABLED)
407 goto out;
408
409 if (osmux_enable_conn(endp, conn_net, &addr->sin_addr, addr->sin_port) < 0 ) {
410 LOGP(DLMGCP, LOGL_ERROR,
411 "Could not enable osmux in endpoint %d\n",
412 ENDPOINT_NUMBER(endp));
413 goto out;
414 }
415
416 LOGP(DLMGCP, LOGL_INFO, "Enabling osmux in endpoint %d for %s:%u\n",
417 ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr),
418 ntohs(addr->sin_port));
419 out:
420 msgb_free(msg);
421 return 0;
422 }
423
424 int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
425 {
426 struct msgb *msg;
427 struct osmux_hdr *osmuxh;
428 struct llist_head list;
429 struct sockaddr_in addr;
430 struct mgcp_config *cfg = ofd->data;
431 uint32_t rem;
432 struct mgcp_conn_rtp *conn_net = NULL;
433
434 msg = osmux_recv(ofd, &addr);
435 if (!msg)
436 return -1;
437
438 /* not any further processing dummy messages */
439 if (msg->data[0] == MGCP_DUMMY_LOAD)
440 return osmux_handle_dummy(cfg, &addr, msg);
441
442 rem = msg->len;
443 while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
444 struct mgcp_endpoint *endp;
445
446 /* Yes, we use MGCP_DEST_BTS to locate the origin */
447 endp = endpoint_lookup(cfg, osmuxh->circuit_id,
448 &addr.sin_addr, MGCP_DEST_BTS);
449
450 /* FIXME: Get rid of CONN_ID_XXX! */
451 conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
452 if (!conn_net)
453 goto out;
454
455 if (!endp) {
456 LOGP(DLMGCP, LOGL_ERROR,
457 "Cannot find an endpoint for circuit_id=%d\n",
458 osmuxh->circuit_id);
459 goto out;
460 }
461 conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
462 conn_net->osmux.stats.chunks++;
463 rem = msg->len;
464
465 osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
466 osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
462 osmux_xfrm_output_sched(&conn_net->osmux.out, osmuxh);
467463 }
468464 out:
469465 msgb_free(msg);
552548 switch (endp->cfg->role) {
553549 case MGCP_BSC_NAT:
554550 conn->type = MGCP_OSMUX_BSC_NAT;
551 osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
552 scheduled_tx_net_cb, endp);
555553 break;
556554 case MGCP_BSC:
557555 conn->type = MGCP_OSMUX_BSC;
556 osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
557 scheduled_tx_bts_cb, endp);
558558 break;
559559 }
560560
575575
576576 LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n",
577577 conn->conn->id, conn->osmux.cid);
578
579 /* We are closing, we don't need pending RTP packets to be transmitted */
580 osmux_xfrm_output_set_tx_cb(&conn->osmux.out, NULL, NULL);
581 osmux_xfrm_output_flush(&conn->osmux.out);
582
578583 osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
579584 conn->osmux.state = OSMUX_STATE_DISABLED;
580585 conn->osmux.cid = -1;
3939 #include <osmocom/mgcp/mgcp_msg.h>
4040 #include <osmocom/mgcp/mgcp_endp.h>
4141 #include <osmocom/mgcp/mgcp_sdp.h>
42 #include <osmocom/mgcp/mgcp_codec.h>
4243
4344 struct mgcp_request {
4445 char *name;
8788 }
8889 }
8990
90 return cfg->setup_rtp_processing_cb(endp, &conn_dst->end,
91 &conn_src->end);
91 return cfg->setup_rtp_processing_cb(endp, conn_dst, conn_src);
9292 }
9393
9494 /* array of function pointers for handling various
355355
356356 /* Try to find a free port by attempting to bind on it. Also handle the
357357 * counter that points on the next free port. Since we have a pointer
358 * to the next free port, binding should work on the first attempt,
359 * nevertheless, try at least the next 200 ports before giving up */
358 * to the next free port, binding should in work on the first attempt in
359 * general. In case of failure the next port is tryed until the whole port
360 * range is tryed once. */
360361 static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
361362 {
362363 int i;
363364 struct mgcp_rtp_end *end;
364365 struct mgcp_port_range *range;
366 unsigned int tries;
365367
366368 OSMO_ASSERT(conn);
367369 end = &conn->end;
370372 range = &endp->cfg->net_ports;
371373
372374 /* attempt to find a port */
373 for (i = 0; i < 200; ++i) {
375 tries = (range->range_end - range->range_start) / 2;
376 for (i = 0; i < tries; ++i) {
374377 int rc;
375378
376379 if (range->last_port >= range->range_end)
386389 }
387390
388391 LOGP(DLMGCP, LOGL_ERROR,
389 "Allocating a RTP/RTCP port failed 200 times 0x%x.\n",
390 ENDPOINT_NUMBER(endp));
392 "Allocating a RTP/RTCP port failed %u times 0x%x.\n",
393 tries, ENDPOINT_NUMBER(endp));
394 return -1;
395 }
396
397 /*! Helper function for check_local_cx_options() to get a pointer of the next
398 * lco option identifier
399 * \param[in] lco string
400 * \returns pointer to the beginning of the LCO identifier, NULL on failure */
401 char *get_lco_identifier(const char *options)
402 {
403 char *ptr;
404 unsigned int count = 0;
405
406 /* Jump to the end of the lco identifier */
407 ptr = strstr(options, ":");
408 if (!ptr)
409 return NULL;
410
411 /* Walk backwards until the pointer points to the beginning of the
412 * lco identifier. We know that we stand at the beginning when we
413 * are either at the beginning of the memory or see a space or
414 * comma. (this is tolerant, it will accept a:10, b:11 as well as
415 * a:10,b:11) */
416 while (1) {
417 /* Endless loop protection */
418 if (count > 10000)
419 return NULL;
420 else if (ptr < options || *ptr == ' ' || *ptr == ',') {
421 ptr++;
422 break;
423 }
424 ptr--;
425 count++;
426 }
427
428 /* Check if we got any result */
429 if (*ptr == ':')
430 return NULL;
431
432 return ptr;
433 }
434
435 /*! Check the LCO option. This function checks for multiple appearence of LCO
436 * options, which is illegal
437 * \param[in] ctx talloc context
438 * \param[in] lco string
439 * \returns 0 on success, -1 on failure */
440 int check_local_cx_options(void *ctx, const char *options)
441 {
442 int i;
443 char *options_copy;
444 char *lco_identifier;
445 char *lco_identifier_end;
446 char *next_lco_identifier;
447
448 char **lco_seen;
449 unsigned int lco_seen_n = 0;
450
451 if (!options)
452 return -1;
453
454 lco_seen =
455 (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
456 options_copy = talloc_strdup(ctx, options);
457 lco_identifier = options_copy;
458
459 do {
460 /* Move the lco_identifier pointer to the beginning of the
461 * current lco option identifier */
462 lco_identifier = get_lco_identifier(lco_identifier);
463 if (!lco_identifier)
464 goto error;
465
466 /* Look ahead to the next LCO option early, since we
467 * will parse destructively */
468 next_lco_identifier = strstr(lco_identifier + 1, ",");
469
470 /* Pinch off the end of the lco field identifier name
471 * and see if we still got something, also check if
472 * there is some value after the colon. */
473 lco_identifier_end = strstr(lco_identifier, ":");
474 if (!lco_identifier_end)
475 goto error;
476 if (*(lco_identifier_end + 1) == ' '
477 || *(lco_identifier_end + 1) == ','
478 || *(lco_identifier_end + 1) == '\0')
479 goto error;
480 *lco_identifier_end = '\0';
481 if (strlen(lco_identifier) == 0)
482 goto error;
483
484 /* Check if we have already seen the current field identifier
485 * before. If yes, we must bail, an LCO must only appear once
486 * in the LCO string */
487 for (i = 0; i < lco_seen_n; i++) {
488 if (strcmp(lco_seen[i], lco_identifier) == 0)
489 goto error;
490 }
491 lco_seen[lco_seen_n] = lco_identifier;
492 lco_seen_n++;
493
494 /* The first identifier must always be found at the beginnning
495 * of the LCO string */
496 if (lco_seen[0] != options_copy)
497 goto error;
498
499 /* Go to the next lco option */
500 lco_identifier = next_lco_identifier;
501 } while (lco_identifier);
502
503 talloc_free(lco_seen);
504 talloc_free(options_copy);
505 return 0;
506 error:
507 talloc_free(lco_seen);
508 talloc_free(options_copy);
391509 return -1;
392510 }
393511
401519 char *p_opt, *a_opt;
402520 char codec[9];
403521
522 if (!options)
523 return 0;
524 if (strlen(options) == 0)
525 return 0;
526
527 /* Make sure the encoding of the LCO is consistant before we proceed */
528 if (check_local_cx_options(ctx, options) != 0) {
529 LOGP(DLMGCP, LOGL_ERROR,
530 "local CX options: Internal inconsistency in Local Connection Options!\n");
531 return 524;
532 }
533
404534 talloc_free(lco->string);
405 talloc_free(lco->codec);
406 lco->codec = NULL;
407 lco->pkt_period_min = lco->pkt_period_max = 0;
408 lco->string = talloc_strdup(ctx, options ? options : "");
535 lco->string = talloc_strdup(ctx, options);
409536
410537 p_opt = strstr(lco->string, "p:");
411538 if (p_opt && sscanf(p_opt, "p:%d-%d",
412539 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
413540 lco->pkt_period_max = lco->pkt_period_min;
414541
542 /* FIXME: LCO also supports the negotiation of more then one codec.
543 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
544 * codec only. */
415545 a_opt = strstr(lco->string, "a:");
416 if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1)
546 if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1) {
547 talloc_free(lco->codec);
417548 lco->codec = talloc_strdup(ctx, codec);
549 }
418550
419551 LOGP(DLMGCP, LOGL_DEBUG,
420552 "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
455587 /* Get the number of frames per channel and packet */
456588 if (rtp->frames_per_packet)
457589 f = rtp->frames_per_packet;
458 else if (rtp->packet_duration_ms && rtp->codec.frame_duration_num) {
459 int den = 1000 * rtp->codec.frame_duration_num;
460 f = (rtp->packet_duration_ms * rtp->codec.frame_duration_den +
590 else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
591 int den = 1000 * rtp->codec->frame_duration_num;
592 f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
461593 den / 2)
462594 / den;
463595 }
464596
465 return rtp->codec.rate * f * rtp->codec.frame_duration_num /
466 rtp->codec.frame_duration_den;
597 return rtp->codec->rate * f * rtp->codec->frame_duration_num /
598 rtp->codec->frame_duration_den;
467599 }
468600
469601 static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
477609 }
478610
479611 return mgcp_parse_osmux_cid(line);
612 }
613
614 /* Process codec information contained in CRCX/MDCX */
615 static int handle_codec_info(struct mgcp_conn_rtp *conn,
616 struct mgcp_parse_data *p, int have_sdp, bool crcx)
617 {
618 struct mgcp_endpoint *endp = p->endp;
619 int rc;
620 char *cmd;
621
622 if (crcx)
623 cmd = "CRCX";
624 else
625 cmd = "MDCX";
626
627 /* Collect codec information */
628 if (have_sdp) {
629 /* If we have SDP, we ignore the local connection options and
630 * use only the SDP information. */
631 mgcp_codec_reset_all(conn);
632 rc = mgcp_parse_sdp_data(endp, conn, p);
633 if (rc != 0) {
634 LOGP(DLMGCP, LOGL_ERROR,
635 "%s: endpoint:%x sdp not parseable\n", cmd,
636 ENDPOINT_NUMBER(endp));
637
638 /* See also RFC 3661: Protocol error */
639 return 510;
640 }
641 } else if (endp->local_options.codec) {
642 /* When no SDP is available, we use the codec information from
643 * the local connection options (if present) */
644 mgcp_codec_reset_all(conn);
645 rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec);
646 if (rc != 0)
647 goto error;
648 }
649
650 /* Make sure we always set a sane default codec */
651 if (conn->end.codecs_assigned == 0) {
652 /* When SDP and/or LCO did not supply any codec information,
653 * than it makes sense to pick a sane default: (payload-type 0,
654 * PCMU), see also: OS#2658 */
655 mgcp_codec_reset_all(conn);
656 rc = mgcp_codec_add(conn, 0, NULL);
657 if (rc != 0)
658 goto error;
659 }
660
661 /* Make codec decision */
662 if (mgcp_codec_decide(conn) != 0)
663 goto error;
664
665 return 0;
666
667 error:
668 LOGP(DLMGCP, LOGL_ERROR,
669 "%s: endpoint:0x%x codec negotiation failure\n", cmd,
670 ENDPOINT_NUMBER(endp));
671
672 /* See also RFC 3661: Codec negotiation failure */
673 return 534;
480674 }
481675
482676 /* CRCX command handler, processes the received command */
596790 * connection ids) */
597791 endp->callid = talloc_strdup(tcfg->endpoints, callid);
598792
599 /* Extract audio codec information */
600 rc = set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
601 local_options);
602 if (rc != 0) {
603 LOGP(DLMGCP, LOGL_ERROR,
604 "CRCX: endpoint:%x inavlid local connection options!\n",
605 ENDPOINT_NUMBER(endp));
606 error_code = rc;
607 goto error2;
608 }
609
610793 snprintf(conn_name, sizeof(conn_name), "%s", callid);
611794 _conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP, conn_name);
612795 if (!_conn) {
637820 goto error2;
638821 }
639822
640 /* set up RTP media parameters */
641 if (have_sdp)
642 mgcp_parse_sdp_data(endp, conn, p);
643 else if (endp->local_options.codec)
644 mgcp_set_audio_info(p->cfg, &conn->end.codec,
645 PTYPE_UNDEFINED, endp->local_options.codec);
823 /* Set local connection options, if present */
824 if (local_options) {
825 rc = set_local_cx_options(endp->tcfg->endpoints,
826 &endp->local_options, local_options);
827 if (rc != 0) {
828 LOGP(DLMGCP, LOGL_ERROR,
829 "CRCX: endpoint:%x inavlid local connection options!\n",
830 ENDPOINT_NUMBER(endp));
831 error_code = rc;
832 goto error2;
833 }
834 }
835
836 /* Handle codec information and decide for a suitable codec */
837 rc = handle_codec_info(conn, p, have_sdp, true);
838 mgcp_codec_summary(conn);
839 if (rc) {
840 error_code = rc;
841 goto error2;
842 }
843
646844 conn->end.fmtp_extra = talloc_strdup(tcfg->endpoints,
647845 tcfg->audio_fmtp_extra);
648846
717915 error2:
718916 mgcp_endp_release(endp);
719917 LOGP(DLMGCP, LOGL_NOTICE,
720 "CRCX: endpoint:0x%x unable to create connection resource error\n",
918 "CRCX: endpoint:0x%x unable to create connection\n",
721919 ENDPOINT_NUMBER(endp));
722920 return create_err_response(endp, error_code, "CRCX", p->trans);
723921 }
922
923
924
925
724926
725927 /* MDCX command handler, processes the received command */
726928 static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
8131015 } else
8141016 conn->conn->mode = conn->conn->mode_orig;
8151017
816 if (have_sdp)
817 mgcp_parse_sdp_data(endp, conn, p);
818
819 rc = set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
820 local_options);
821 if (rc != 0) {
822 LOGP(DLMGCP, LOGL_ERROR,
823 "MDCX: endpoint:%x inavlid local connection options!\n",
824 ENDPOINT_NUMBER(endp));
1018 /* Set local connection options, if present */
1019 if (local_options) {
1020 rc = set_local_cx_options(endp->tcfg->endpoints,
1021 &endp->local_options, local_options);
1022 if (rc != 0) {
1023 LOGP(DLMGCP, LOGL_ERROR,
1024 "MDCX: endpoint:%x inavlid local connection options!\n",
1025 ENDPOINT_NUMBER(endp));
1026 error_code = rc;
1027 goto error3;
1028 }
1029 }
1030
1031 /* Handle codec information and decide for a suitable codec */
1032 rc = handle_codec_info(conn, p, have_sdp, false);
1033 mgcp_codec_summary(conn);
1034 if (rc) {
8251035 error_code = rc;
8261036 goto error3;
8271037 }
828
829 if (!have_sdp && endp->local_options.codec)
830 mgcp_set_audio_info(p->cfg, &conn->end.codec,
831 PTYPE_UNDEFINED, endp->local_options.codec);
8321038
8331039 /* check connection mode setting */
8341040 if (conn->conn->mode != MGCP_CONN_LOOPBACK
8401046 error_code = 527;
8411047 goto error3;
8421048 }
1049
8431050
8441051 if (setup_rtp_processing(endp, conn) != 0)
8451052 goto error3;
2424 #include <osmocom/mgcp/mgcp_internal.h>
2525 #include <osmocom/mgcp/mgcp_msg.h>
2626 #include <osmocom/mgcp/mgcp_endp.h>
27 #include <osmocom/mgcp/mgcp_codec.h>
2728
2829 #include <errno.h>
2930
31 /* A struct to store intermediate parsing results. The function
32 * mgcp_parse_sdp_data() is using it as temporary storage for parsing the SDP
33 * codec information. */
3034 struct sdp_rtp_map {
3135 /* the type */
3236 int payload_type;
3943 int channels;
4044 };
4145
42 /*! Set codec configuration depending on payload type and codec name.
43 * \param[in] ctx talloc context.
44 * \param[out] codec configuration (caller provided memory).
45 * \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
46 * \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
47 * \returns 0 on success, -1 on failure. */
48 int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
49 int payload_type, const char *audio_name)
50 {
51 int rate = codec->rate;
52 int channels = codec->channels;
53 char audio_codec[64];
54
55 talloc_free(codec->subtype_name);
56 codec->subtype_name = NULL;
57 talloc_free(codec->audio_name);
58 codec->audio_name = NULL;
59
60 if (payload_type != PTYPE_UNDEFINED)
61 codec->payload_type = payload_type;
62
63 if (!audio_name) {
64 switch (payload_type) {
65 case 0:
66 audio_name = "PCMU/8000/1";
67 break;
68 case 3:
69 audio_name = "GSM/8000/1";
70 break;
71 case 8:
72 audio_name = "PCMA/8000/1";
73 break;
74 case 18:
75 audio_name = "G729/8000/1";
76 break;
77 default:
78 /* Payload type is unknown, don't change rate and
79 * channels. */
80 /* TODO: return value? */
81 return 0;
82 }
83 }
84
85 if (sscanf(audio_name, "%63[^/]/%d/%d",
86 audio_codec, &rate, &channels) < 1)
87 return -EINVAL;
88
89 codec->rate = rate;
90 codec->channels = channels;
91 codec->subtype_name = talloc_strdup(ctx, audio_codec);
92 codec->audio_name = talloc_strdup(ctx, audio_name);
93
94 if (!strcmp(audio_codec, "G729")) {
95 codec->frame_duration_num = 10;
96 codec->frame_duration_den = 1000;
97 } else {
98 codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
99 codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
100 }
101
102 if (payload_type < 0) {
103 payload_type = 96;
104 if (rate == 8000 && channels == 1) {
105 if (!strcmp(audio_codec, "GSM"))
106 payload_type = 3;
107 else if (!strcmp(audio_codec, "PCMA"))
108 payload_type = 8;
109 else if (!strcmp(audio_codec, "PCMU"))
110 payload_type = 0;
111 else if (!strcmp(audio_codec, "G729"))
112 payload_type = 18;
113 }
114
115 codec->payload_type = payload_type;
116 }
117
118 if (channels != 1)
119 LOGP(DLMGCP, LOGL_NOTICE,
120 "Channels != 1 in SDP: '%s'\n", audio_name);
121
122 return 0;
123 }
124
46 /* Helper function to extrapolate missing codec parameters in a codec mao from
47 * an already filled in payload_type, called from: mgcp_parse_sdp_data() */
12548 static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
12649 {
12750 int i;
14871 codecs[i].rate = 8000;
14972 codecs[i].channels = 1;
15073 break;
151 }
152 }
153 }
154
74 default:
75 codecs[i].codec_name = NULL;
76 codecs[i].rate = 0;
77 codecs[i].channels = 0;
78 }
79 }
80 }
81
82 /* Helper function to update codec map information with additional data from
83 * SDP, called from: mgcp_parse_sdp_data() */
15584 static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
15685 int payload, const char *audio_name)
15786 {
16190 char audio_codec[64];
16291 int rate = -1;
16392 int channels = -1;
93
94 /* Note: We can only update payload codecs that already exist
95 * in our codec list. If we get an unexpected payload type,
96 * we just drop it */
16497 if (codecs[i].payload_type != payload)
16598 continue;
99
166100 if (sscanf(audio_name, "%63[^/]/%d/%d",
167101 audio_codec, &rate, &channels) < 1) {
168102 LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
181115 audio_name);
182116 }
183117
184 /* Check if the codec matches what is set up in the trunk config */
185 static int is_codec_compatible(const struct mgcp_endpoint *endp,
186 const struct sdp_rtp_map *codec)
187 {
188 char *codec_str;
189 char audio_codec[64];
190
191 if (!codec->codec_name)
192 return 0;
193
194 /* GSM, GSM/8000 and GSM/8000/1 should all be compatible...
195 * let's go by name first. */
196 codec_str = endp->tcfg->audio_name;
197 if (sscanf(codec_str, "%63[^/]/%*d/%*d", audio_codec) < 1)
198 return 0;
199
200 return strcasecmp(audio_codec, codec->codec_name) == 0;
118 /* Extract payload types from SDP, also check for duplicates */
119 static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
120 unsigned int codecs_len, char *sdp)
121 {
122 char *str;
123 char *str_ptr;
124 char *pt_str;
125 unsigned int pt;
126 unsigned int count = 0;
127 unsigned int i;
128
129 str = talloc_zero_size(ctx, strlen(sdp) + 1);
130 str_ptr = str;
131 strcpy(str_ptr, sdp);
132
133 str_ptr = strstr(str_ptr, "RTP/AVP ");
134 if (!str_ptr)
135 goto exit;
136
137 pt_str = strtok(str_ptr, " ");
138 if (!pt_str)
139 goto exit;
140
141 while (1) {
142 /* Do not allow excessive payload types */
143 if (count > codecs_len)
144 goto error;
145
146 pt_str = strtok(NULL, " ");
147 if (!pt_str)
148 break;
149
150 pt = atoi(pt_str);
151
152 /* Do not allow duplicate payload types */
153 for (i = 0; i < count; i++)
154 if (codecs[i].payload_type == pt)
155 goto error;
156
157 codecs[count].payload_type = pt;
158 count++;
159 }
160
161 exit:
162 talloc_free(str);
163 return count;
164 error:
165 talloc_free(str);
166 return -EINVAL;
201167 }
202168
203169 /*! Analyze SDP input string.
204170 * \param[in] endp trunk endpoint.
205171 * \param[out] conn associated rtp connection.
206172 * \param[out] caller provided memory to store the parsing results.
207 * \returns 0 on success, -1 on failure.
208173 *
209174 * Note: In conn (conn->end) the function returns the packet duration,
210 * the rtp port and the rtcp port */
175 * rtp port, rtcp port and the codec information.
176 * \returns 0 on success, -1 on failure. */
211177 int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
212 struct mgcp_conn_rtp *conn,
213 struct mgcp_parse_data *p)
214 {
215 struct sdp_rtp_map codecs[10];
216 int codecs_used = 0;
178 struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
179 {
180 struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
181 unsigned int codecs_used = 0;
217182 char *line;
218 int maxptime = -1;
219 int i;
220 int codecs_assigned = 0;
183 unsigned int i;
221184 void *tmp_ctx = talloc_new(NULL);
222185 struct mgcp_rtp_end *rtp;
223186
254217 rtp->packet_duration_ms = 0;
255218 else
256219 rtp->packet_duration_ms = ptime;
257 } else if (sscanf(line, "a=maxptime:%d", &ptime2)
258 == 1) {
259 maxptime = ptime2;
220 } else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
221 rtp->maximum_packet_time = ptime2;
260222 }
261223 break;
262224 case 'm':
263 rc = sscanf(line,
264 "m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d",
265 &port, &codecs[0].payload_type,
266 &codecs[1].payload_type,
267 &codecs[2].payload_type,
268 &codecs[3].payload_type,
269 &codecs[4].payload_type,
270 &codecs[5].payload_type,
271 &codecs[6].payload_type,
272 &codecs[7].payload_type,
273 &codecs[8].payload_type,
274 &codecs[9].payload_type);
275 if (rc >= 2) {
225 rc = sscanf(line, "m=audio %d RTP/AVP", &port);
226 if (rc == 1) {
276227 rtp->rtp_port = htons(port);
277228 rtp->rtcp_port = htons(port + 1);
278 codecs_used = rc - 1;
279 codecs_initialize(tmp_ctx, codecs, codecs_used);
280229 }
230
231 rc = pt_from_sdp(conn->conn, codecs,
232 ARRAY_SIZE(codecs), line);
233 if (rc > 0)
234 codecs_used = rc;
281235 break;
282236 case 'c':
283237
298252 break;
299253 }
300254 }
301
302 /* Now select the primary and alt_codec */
303 for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) {
304 struct mgcp_rtp_codec *codec = codecs_assigned == 0 ?
305 &rtp->codec : &rtp->alt_codec;
306
307 if (endp->tcfg->no_audio_transcoding &&
308 !is_codec_compatible(endp, &codecs[i])) {
309 LOGP(DLMGCP, LOGL_NOTICE, "Skipping codec %s\n",
310 codecs[i].codec_name);
311 continue;
312 }
313
314 mgcp_set_audio_info(p->cfg, codec,
315 codecs[i].payload_type, codecs[i].map_line);
316 codecs_assigned += 1;
317 }
318
319 if (codecs_assigned > 0) {
320 /* TODO/XXX: Store this per codec and derive it on use */
321 if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den >
322 rtp->codec.frame_duration_num * 1500) {
323 /* more than 1 frame */
324 rtp->packet_duration_ms = 0;
325 }
326
327 LOGP(DLMGCP, LOGL_NOTICE,
328 "Got media info via SDP: port %d, payload %d (%s), "
329 "duration %d, addr %s\n",
330 ntohs(rtp->rtp_port), rtp->codec.payload_type,
331 rtp->codec.subtype_name ? rtp->
332 codec.subtype_name : "unknown", rtp->packet_duration_ms,
333 inet_ntoa(rtp->addr));
255 OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
256
257 /* So far we have only set the payload type in the codec struct. Now we
258 * fill up the remaining fields of the codec description with some default
259 * information */
260 codecs_initialize(tmp_ctx, codecs, codecs_used);
261
262 /* Store parsed codec information */
263 for (i = 0; i < codecs_used; i++) {
264 rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line);
265 if (rc < 0)
266 LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
334267 }
335268
336269 talloc_free(tmp_ctx);
337 return codecs_assigned > 0;
270
271 LOGP(DLMGCP, LOGL_NOTICE,
272 "Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
273 ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
274 rtp->packet_duration_ms);
275 if (codecs_used == 0)
276 LOGPC(DLMGCP, LOGL_NOTICE, "none");
277 for (i = 0; i < codecs_used; i++) {
278 LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
279 rtp->codecs[i].payload_type,
280 rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown");
281 LOGPC(DLMGCP, LOGL_NOTICE, " ");
282 }
283 LOGPC(DLMGCP, LOGL_NOTICE, "\n");
284
285 return 0;
338286 }
339287
340288 /*! Generate SDP response string.
379327 if (rc < 0)
380328 goto buffer_too_small;
381329
382 if (audio_name && endp->tcfg->audio_send_name) {
330 /* FIXME: Check if the payload type is from the static range,
331 * if yes, omitthe a=rtpmap since it is unnecessary */
332 if (audio_name && endp->tcfg->audio_send_name && (payload_type >= 96 && payload_type <= 127)) {
383333 rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n",
384334 payload_type, audio_name);
385335
2424 #include <osmocom/mgcp/mgcp_stat.h>
2525 #include <osmocom/mgcp/mgcp_endp.h>
2626 #include <limits.h>
27 #include <inttypes.h>
2728
2829 /* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
29 void calc_loss(struct mgcp_rtp_state *state,
30 struct mgcp_rtp_end *end, uint32_t *expected,
31 int *loss)
30 void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss)
3231 {
32 struct mgcp_rtp_state *state = &conn->state;
33 struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
34
3335 *expected = state->stats.cycles + state->stats.max_seq;
3436 *expected = *expected - state->stats.base_seq + 1;
3537
4345 * Make sure the sign is correct and use the biggest
4446 * positive/negative number that fits.
4547 */
46 *loss = *expected - end->stats.packets_rx;
47 if (*expected < end->stats.packets_rx) {
48 *loss = *expected - packets_rx->current;
49 if (*expected < packets_rx->current) {
4850 if (*loss > 0)
4951 *loss = INT_MIN;
5052 } else {
6971 int ploss;
7072 int nchars;
7173
72 calc_loss(&conn->state, &conn->end, &expected, &ploss);
74 struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
75 struct rate_ctr *octets_rx = &conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR];
76 struct rate_ctr *packets_tx = &conn->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR];
77 struct rate_ctr *octets_tx = &conn->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR];
78
79 calc_loss(conn, &expected, &ploss);
7380 jitter = calc_jitter(&conn->state);
7481
7582 nchars = snprintf(str, str_len,
76 "\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
77 conn->end.stats.packets_tx, conn->end.stats.octets_tx,
78 conn->end.stats.packets_rx, conn->end.stats.octets_rx,
83 "\r\nP: PS=%" PRIu64 ", OS=%" PRIu64 ", PR=%" PRIu64 ", OR=%" PRIu64 ", PL=%d, JI=%u",
84 packets_tx->current, octets_tx->current,
85 packets_rx->current, octets_rx->current,
7986 ploss, jitter);
8087 if (nchars < 0 || nchars >= str_len)
8188 goto truncate;
8693 if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) {
8794 /* Error Counter */
8895 nchars = snprintf(str, str_len,
89 "\r\nX-Osmo-CP: EC TI=%u, TO=%u",
90 conn->state.in_stream.err_ts_counter,
91 conn->state.out_stream.err_ts_counter);
96 "\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64,
97 conn->state.in_stream.err_ts_ctr->current,
98 conn->state.out_stream.err_ts_ctr->current);
9299 if (nchars < 0 || nchars >= str_len)
93100 goto truncate;
94101
2929 #include <osmocom/mgcp/mgcp_endp.h>
3030
3131 #include <string.h>
32 #include <inttypes.h>
3233
3334 #define RTCP_OMIT_STR "Drop RTCP packets in both directions\n"
3435 #define RTP_PATCH_STR "Modify RTP packet header in both directions\n"
153154 return CMD_SUCCESS;
154155 }
155156
156 static void dump_rtp_end(struct vty *vty, struct mgcp_rtp_state *state,
157 struct mgcp_rtp_end *end)
158 {
159 struct mgcp_rtp_codec *codec = &end->codec;
157 static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
158 {
159 struct mgcp_rtp_state *state = &conn->state;
160 struct mgcp_rtp_end *end = &conn->end;
161 struct mgcp_rtp_codec *codec = end->codec;
162 struct rate_ctr *dropped_packets;
163
164 dropped_packets = &conn->rate_ctr_group->ctr[RTP_DROPPED_PACKETS_CTR];
160165
161166 vty_out(vty,
162 " Timestamp Errs: %d->%d%s"
163 " Dropped Packets: %d%s"
167 " Timestamp Errs: %" PRIu64 "->%" PRIu64 "%s"
168 " Dropped Packets: %" PRIu64 "%s"
164169 " Payload Type: %d Rate: %u Channels: %d %s"
165170 " Frame Duration: %u Frame Denominator: %u%s"
166171 " FPP: %d Packet Duration: %u%s"
167172 " FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s"
168173 " Output-Enabled: %d Force-PTIME: %d%s",
169 state->in_stream.err_ts_counter,
170 state->out_stream.err_ts_counter, VTY_NEWLINE,
171 end->stats.dropped_packets, VTY_NEWLINE,
174 state->in_stream.err_ts_ctr->current,
175 state->out_stream.err_ts_ctr->current,
176 VTY_NEWLINE,
177 dropped_packets->current, VTY_NEWLINE,
172178 codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
173179 codec->frame_duration_num, codec->frame_duration_den,
174180 VTY_NEWLINE, end->frames_per_packet, end->packet_duration_ms,
206212 * connection types (E1) as soon as
207213 * the implementation is available */
208214 if (conn->type == MGCP_CONN_TYPE_RTP) {
209 dump_rtp_end(vty, &conn->u.rtp.state,
210 &conn->u.rtp.end);
215 dump_rtp_end(vty, &conn->u.rtp);
211216 }
212217 }
213218 }
280285 return CMD_WARNING;
281286 }
282287
283 static void parse_range(struct mgcp_port_range *range, const char **argv)
284 {
285 range->range_start = atoi(argv[0]);
286 range->range_end = atoi(argv[1]);
287 range->last_port = g_cfg->net_ports.range_start;
288 }
289
290288 #define RTP_STR "RTP configuration\n"
291289 #define UDP_PORT_STR "UDP Port number\n"
292290 #define NET_START_STR "First UDP port allocated\n"
295293
296294 DEFUN(cfg_mgcp_rtp_port_range,
297295 cfg_mgcp_rtp_port_range_cmd,
298 "rtp port-range <0-65534> <0-65534>",
296 "rtp port-range <1024-65534> <1025-65535>",
299297 RTP_STR "Range of ports to use for the NET side\n"
300298 RANGE_START_STR RANGE_END_STR)
301299 {
302 parse_range(&g_cfg->net_ports, argv);
300 int start;
301 int end;
302
303 start = atoi(argv[0]);
304 end = atoi(argv[1]);
305
306 if (end < start) {
307 vty_out(vty, "range end port (%i) must be greater than the range start port (%i)!%s",
308 end, start, VTY_NEWLINE);
309 return CMD_WARNING;
310 }
311
312 if (start & 1) {
313 vty_out(vty, "range must begin at an even port number, autocorrecting port (%i) to: %i%s",
314 start, start & 0xFFFE, VTY_NEWLINE);
315 start &= 0xFFFE;
316 }
317
318 if ((end & 1) == 0) {
319 vty_out(vty, "range must end at an odd port number, autocorrecting port (%i) to: %i%s",
320 end, end | 1, VTY_NEWLINE);
321 end |= 1;
322 }
323
324 g_cfg->net_ports.range_start = start;
325 g_cfg->net_ports.range_end = end;
326 g_cfg->net_ports.last_port = g_cfg->net_ports.range_start;
327
303328 return CMD_SUCCESS;
304329 }
305330 ALIAS_DEPRECATED(cfg_mgcp_rtp_port_range,
1919
2020 # This is not at all related to the release version, but a range of supported
2121 # API versions. Read TODO_RELEASE in the source tree's root!
22 MGCP_CLIENT_LIBVERSION=3:0:0
22 MGCP_CLIENT_LIBVERSION=4:0:1
2323
2424 lib_LTLIBRARIES = \
2525 libosmo-mgcp-client.la \
3535 #include <unistd.h>
3636 #include <string.h>
3737
38 /* Codec descripton for dynamic payload types (SDP) */
39 static const struct value_string codec_table[] = {
40 { CODEC_PCMU_8000_1, "PCMU/8000/1" },
41 { CODEC_GSM_8000_1, "GSM/8000/1" },
42 { CODEC_PCMA_8000_1, "PCMA/8000/1" },
43 { CODEC_G729_8000_1, "G729/8000/1" },
44 { CODEC_GSMEFR_8000_1, "GSM-EFR/8000/1" },
45 { CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" },
46 { CODEC_AMR_8000_1, "AMR/8000/1" },
47 { CODEC_AMRWB_16000_1, "AMR-WB/16000/1" },
48 { 0, NULL },
49 };
50
51 /* Get encoding name from a full codec string e,g.
52 * ("CODEC/8000/2" => returns "CODEC") */
53 static char *extract_codec_name(const char *str)
54 {
55 static char buf[64];
56 unsigned int i;
57
58 if (!str)
59 return NULL;
60
61 /* FIXME osmo_strlcpy */
62 osmo_strlcpy(buf, str, sizeof(buf));
63
64 for (i = 0; i < strlen(buf); i++) {
65 if (buf[i] == '/')
66 buf[i] = '\0';
67 }
68
69 return buf;
70 }
71
72 /*! Map a string to a codec.
73 * \ptmap[in] str input string (e.g "GSM/8000/1", "GSM/8000" or "GSM")
74 * \returns codec that corresponds to the given string representation. */
75 enum mgcp_codecs map_str_to_codec(const char *str)
76 {
77 unsigned int i;
78 char *codec_name;
79 char str_buf[64];
80
81 osmo_strlcpy(str_buf, extract_codec_name(str), sizeof(str_buf));
82
83 for (i = 0; i < ARRAY_SIZE(codec_table); i++) {
84 codec_name = extract_codec_name(codec_table[i].str);
85 if (!codec_name)
86 continue;
87 if (strcmp(codec_name, str_buf) == 0)
88 return codec_table[i].value;
89 }
90
91 return -1;
92 }
93
94 /* Check the ptmap for illegal mappings */
95 static int check_ptmap(struct ptmap *ptmap)
96 {
97 /* Check if there are mappings that leave the IANA assigned dynamic
98 * payload type range. Under normal conditions such mappings should
99 * not occur */
100
101 /* Its ok to have a 1:1 mapping in the statically defined
102 * range, this won't hurt */
103 if (ptmap->codec == ptmap->pt)
104 return 0;
105
106 if (ptmap->codec < 96 || ptmap->codec > 127)
107 goto error;
108 if (ptmap->pt < 96 || ptmap->pt > 127)
109 goto error;
110
111 return 0;
112 error:
113 LOGP(DLMGCP, LOGL_ERROR,
114 "ptmap contains illegal mapping: codec=%u maps to pt=%u\n",
115 ptmap->codec, ptmap->pt);
116 return -1;
117 }
118
119 /*! Map a codec to a payload type.
120 * \ptmap[in] payload pointer to payload type map with specified payload types.
121 * \ptmap[in] ptmap_len length of the payload type map.
122 * \ptmap[in] codec the codec for which the payload type should be looked up.
123 * \returns assigned payload type */
124 unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
125 enum mgcp_codecs codec)
126 {
127 unsigned int i;
128
129 /*! Note: If the payload type map is empty or the codec is not found
130 * in the map, then a 1:1 mapping is performed. If the codec falls
131 * into the statically defined range or if the mapping table isself
132 * tries to map to the statically defined range, then the mapping
133 * is also ignored and a 1:1 mapping is performed instead. */
134
135 /* we may return the codec directly since enum mgcp_codecs directly
136 * corresponds to the statićally assigned payload types */
137 if (codec < 96 || codec > 127)
138 return codec;
139
140 for (i = 0; i < ptmap_len; i++) {
141 /* Skip illegal map entries */
142 if (check_ptmap(ptmap) == 0 && ptmap->codec == codec)
143 return ptmap->pt;
144 ptmap++;
145 }
146
147 /* If nothing is found, do not perform any mapping */
148 return codec;
149 }
150
151 /*! Map a payload type to a codec.
152 * \ptmap[in] payload pointer to payload type map with specified payload types.
153 * \ptmap[in] ptmap_len length of the payload type map.
154 * \ptmap[in] payload type for which the codec should be looked up.
155 * \returns codec that corresponds to the specified payload type */
156 enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
157 unsigned int pt)
158 {
159 unsigned int i;
160
161 /*! Note: If the payload type map is empty or the payload type is not
162 * found in the map, then a 1:1 mapping is performed. If the payload
163 * type falls into the statically defined range or if the mapping
164 * table isself tries to map to the statically defined range, then
165 * the mapping is also ignored and a 1:1 mapping is performed
166 * instead. */
167
168 /* See also note in map_codec_to_pt() */
169 if (pt < 96 || pt > 127)
170 return pt;
171
172 for (i = 0; i < ptmap_len; i++) {
173 if (check_ptmap(ptmap) == 0 && ptmap->pt == pt)
174 return ptmap->codec;
175 ptmap++;
176 }
177
178 /* If nothing is found, do not perform any mapping */
179 return pt;
180 }
181
38182 /*! Initalize MGCP client configuration struct with default values.
39183 * \param[out] conf Client configuration.*/
40184 void mgcp_client_conf_init(struct mgcp_client_conf *conf)
177321 return true;
178322 }
179323
180 /* Parse a line like "m=audio 16002 RTP/AVP 98" */
181 static int mgcp_parse_audio_port(struct mgcp_response *r, const char *line)
182 {
183 if (sscanf(line, "m=audio %hu",
184 &r->audio_port) != 1)
185 goto response_parse_failure;
186
324 /* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
325 static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
326 {
327 char *pt_str;
328 unsigned int pt;
329 unsigned int count = 0;
330 unsigned int i;
331
332 /* Extract port information */
333 if (sscanf(line, "m=audio %hu", &r->audio_port) != 1)
334 goto response_parse_failure_port;
187335 if (r->audio_port == 0)
188 goto response_parse_failure;
189
336 goto response_parse_failure_port;
337
338 /* Extract payload types */
339 line = strstr(line, "RTP/AVP ");
340 if (!line)
341 goto exit;
342
343 pt_str = strtok(line, " ");
344 while (1) {
345 /* Do not allow excessive payload types */
346 if (count > ARRAY_SIZE(r->codecs))
347 goto response_parse_failure_pt;
348
349 pt_str = strtok(NULL, " ");
350 if (!pt_str)
351 break;
352 pt = atoi(pt_str);
353
354 /* Do not allow duplicate payload types */
355 for (i = 0; i < count; i++)
356 if (r->codecs[i] == pt)
357 goto response_parse_failure_pt;
358
359 /* Note: The payload type we store may not necessarly match
360 * the codec types we have defined in enum mgcp_codecs. To
361 * ensure that the end result only contains codec types which
362 * match enum mgcp_codecs, we will go through afterwards and
363 * remap the affected entries with the inrofmation we learn
364 * from rtpmap */
365 r->codecs[count] = pt;
366 count++;
367 }
368
369 r->codecs_len = count;
370
371 exit:
190372 return 0;
191373
192 response_parse_failure:
374 response_parse_failure_port:
193375 LOGP(DLMGCP, LOGL_ERROR,
194 "Failed to parse MGCP response header (audio port)\n");
376 "Failed to parse SDP parameter port (%s)\n", line);
195377 return -EINVAL;
378
379 response_parse_failure_pt:
380 LOGP(DLMGCP, LOGL_ERROR,
381 "Failed to parse SDP parameter payload types (%s)\n", line);
382 return -EINVAL;
383 }
384
385 /* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
386 static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *line)
387 {
388 unsigned int pt;
389 char codec_resp[64];
390 unsigned int codec;
391
392
393 if (strstr(line, "ptime")) {
394 if (sscanf(line, "a=ptime:%u", &r->ptime) != 1)
395 goto response_parse_failure_ptime;
396 } else if (strstr(line, "rtpmap")) {
397 if (sscanf(line, "a=rtpmap:%d %63s", &pt, codec_resp) == 2) {
398 /* The MGW may assign an own payload type in the
399 * response if the choosen codec falls into the IANA
400 * assigned dynamic payload type range (96-127).
401 * Normally the MGW should obey the 3gpp payload type
402 * assignments, which are fixed, so we likely wont see
403 * anything unexpected here. In order to be sure that
404 * we will now check the codec string and if the result
405 * does not match to what is IANA / 3gpp assigned, we
406 * will create an entry in the ptmap table so we can
407 * lookup later what has been assigned. */
408 codec = map_str_to_codec(codec_resp);
409 if (codec != pt) {
410 if (r->ptmap_len < ARRAY_SIZE(r->ptmap)) {
411 r->ptmap[r->ptmap_len].pt = pt;
412 r->ptmap[r->ptmap_len].codec = codec;
413 r->ptmap_len++;
414 } else
415 goto response_parse_failure_rtpmap;
416 }
417
418 } else
419 goto response_parse_failure_rtpmap;
420 }
421
422 return 0;
423
424 response_parse_failure_ptime:
425 LOGP(DLMGCP, LOGL_ERROR,
426 "Failed to parse SDP parameter, invalid ptime (%s)\n", line);
427 return -EINVAL;
428 response_parse_failure_rtpmap:
429 LOGP(DLMGCP, LOGL_ERROR,
430 "Failed to parse SDP parameter, invalid rtpmap (%s)\n", line);
431 return -EINVAL;
196432 }
197433
198434 /* Parse a line like "c=IN IP4 10.11.12.13" */
252488 int rc;
253489 char *data;
254490 char *data_ptr;
491 int i;
255492
256493 /* Since this functions performs a destructive parsing, we create a
257494 * local copy of the body data */
276513 return -EINVAL;
277514
278515 switch (line[0]) {
516 case 'a':
517 rc = mgcp_parse_audio_ptime_rtpmap(r, line);
518 if (rc)
519 goto exit;
520 break;
279521 case 'm':
280 rc = mgcp_parse_audio_port(r, line);
522 rc = mgcp_parse_audio_port_pt(r, line);
281523 if (rc)
282524 goto exit;
283525 break;
291533 break;
292534 }
293535 }
536
537 /* See also note in mgcp_parse_audio_port_pt() */
538 for (i = 0; i < r->codecs_len; i++)
539 r->codecs[i] = map_pt_to_codec(r->ptmap, r->ptmap_len, r->codecs[i]);
294540
295541 rc = 0;
296542 exit:
8121058 #define MGCP_AUEP_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
8131059 #define MGCP_RSIP_MANDATORY 0 /* none */
8141060
1061 /* Helper function for mgcp_msg_gen(): Add LCO information to MGCP message */
1062 static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
1063 {
1064 unsigned int i;
1065 int rc = 0;
1066 const char *codec;
1067 unsigned int pt;
1068
1069 rc += msgb_printf(msg, "L:");
1070
1071 if (mgcp_msg->ptime)
1072 rc += msgb_printf(msg, " p:%u,", mgcp_msg->ptime);
1073
1074 if (mgcp_msg->codecs_len) {
1075 rc += msgb_printf(msg, " a:");
1076 for (i = 0; i < mgcp_msg->codecs_len; i++) {
1077 pt = mgcp_msg->codecs[i];
1078 codec = get_value_string_or_null(codec_table, pt);
1079
1080 /* Note: Use codec descriptors from enum mgcp_codecs
1081 * in mgcp_client only! */
1082 OSMO_ASSERT(codec);
1083 rc += msgb_printf(msg, "%s", extract_codec_name(codec));
1084 if (i < mgcp_msg->codecs_len - 1)
1085 rc += msgb_printf(msg, ";");
1086 }
1087 rc += msgb_printf(msg, ",");
1088 }
1089
1090 rc += msgb_printf(msg, " nt:IN\r\n");
1091
1092 return rc;
1093 }
1094
1095 /* Helper function for mgcp_msg_gen(): Add SDP information to MGCP message */
1096 static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_client *mgcp)
1097 {
1098 unsigned int i;
1099 int rc = 0;
1100 char local_ip[INET_ADDRSTRLEN];
1101 const char *codec;
1102 unsigned int pt;
1103
1104 /* Add separator to mark the beginning of the SDP block */
1105 rc += msgb_printf(msg, "\r\n");
1106
1107 /* Add SDP protocol version */
1108 rc += msgb_printf(msg, "v=0\r\n");
1109
1110 /* Determine local IP-Address */
1111 if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
1112 LOGP(DLMGCP, LOGL_ERROR,
1113 "Could not determine local IP-Address!\n");
1114 msgb_free(msg);
1115 return -2;
1116 }
1117
1118 /* Add owner/creator (SDP) */
1119 rc += msgb_printf(msg, "o=- %x 23 IN IP4 %s\r\n",
1120 mgcp_msg->call_id, local_ip);
1121
1122 /* Add session name (none) */
1123 rc += msgb_printf(msg, "s=-\r\n");
1124
1125 /* Add RTP address and port */
1126 if (mgcp_msg->audio_port == 0) {
1127 LOGP(DLMGCP, LOGL_ERROR,
1128 "Invalid port number, can not generate MGCP message\n");
1129 msgb_free(msg);
1130 return -2;
1131 }
1132 if (strlen(mgcp_msg->audio_ip) <= 0) {
1133 LOGP(DLMGCP, LOGL_ERROR,
1134 "Empty ip address, can not generate MGCP message\n");
1135 msgb_free(msg);
1136 return -2;
1137 }
1138 rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
1139
1140 /* Add time description, active time (SDP) */
1141 rc += msgb_printf(msg, "t=0 0\r\n");
1142
1143 rc += msgb_printf(msg, "m=audio %u RTP/AVP", mgcp_msg->audio_port);
1144 for (i = 0; i < mgcp_msg->codecs_len; i++) {
1145 pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
1146 rc += msgb_printf(msg, " %u", pt);
1147
1148 }
1149 rc += msgb_printf(msg, "\r\n");
1150
1151 for (i = 0; i < mgcp_msg->codecs_len; i++) {
1152 pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
1153
1154 /* Note: Only dynamic payload type from the range 96-127
1155 * require to be explained further via rtpmap. All others
1156 * are implcitly definedby the number in m=audio */
1157 if (pt >= 96 && pt <= 127) {
1158 codec = get_value_string_or_null(codec_table, mgcp_msg->codecs[i]);
1159
1160 /* Note: Use codec descriptors from enum mgcp_codecs
1161 * in mgcp_client only! */
1162 OSMO_ASSERT(codec);
1163
1164 rc += msgb_printf(msg, "a=rtpmap:%u %s\r\n", pt, codec);
1165 }
1166 }
1167
1168 if (mgcp_msg->ptime)
1169 rc += msgb_printf(msg, "a=ptime:%u\r\n", mgcp_msg->ptime);
1170
1171 return rc;
1172 }
1173
8151174 /*! Generate an MGCP message
8161175 * \param[in] mgcp MGCP client descriptor.
8171176 * \param[in] mgcp_msg Message description
8221181 uint32_t mandatory_mask;
8231182 struct msgb *msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
8241183 int rc = 0;
825 char local_ip[INET_ADDRSTRLEN];
1184 int rc_sdp;
1185 bool use_sdp = false;
8261186
8271187 msg->l2h = msg->data;
8281188 msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
9011261 rc += msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id);
9021262 }
9031263
904 /* Add local connection options */
905 if (mgcp_msg->verb == MGCP_VERB_CRCX)
906 rc += msgb_printf(msg, "L: p:20, a:AMR, nt:IN\r\n");
1264 /* Using SDP makes sense when a valid IP/Port combination is specifiec,
1265 * if we do not know this information yet, we fall back to LCO */
1266 if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
1267 && mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT)
1268 use_sdp = true;
1269
1270 /* Add local connection options (LCO) */
1271 if (!use_sdp
1272 && (mgcp_msg->verb == MGCP_VERB_CRCX
1273 || mgcp_msg->verb == MGCP_VERB_MDCX))
1274 rc += add_lco(msg, mgcp_msg);
9071275
9081276 /* Add mode */
9091277 if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_MODE)
9111279 msgb_printf(msg, "M: %s\r\n",
9121280 mgcp_client_cmode_name(mgcp_msg->conn_mode));
9131281
914 /* Add SDP body */
915 if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
916 && mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT) {
917
918 /* Add separator to mark the beginning of the SDP block */
919 rc += msgb_printf(msg, "\r\n");
920
921 /* Add SDP protocol version */
922 rc += msgb_printf(msg, "v=0\r\n");
923
924 /* Determine local IP-Address */
925 if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
926 LOGP(DLMGCP, LOGL_ERROR,
927 "Could not determine local IP-Address!\n");
928 msgb_free(msg);
1282 /* Add session description protocol (SDP) */
1283 if (use_sdp
1284 && (mgcp_msg->verb == MGCP_VERB_CRCX
1285 || mgcp_msg->verb == MGCP_VERB_MDCX)) {
1286 rc_sdp = add_sdp(msg, mgcp_msg, mgcp);
1287 if (rc_sdp == -2)
9291288 return NULL;
930 }
931
932 /* Add owner/creator (SDP) */
933 rc += msgb_printf(msg, "o=- %x 23 IN IP4 %s\r\n",
934 mgcp_msg->call_id, local_ip);
935
936 /* Add session name (none) */
937 rc += msgb_printf(msg, "s=-\r\n");
938
939 /* Add RTP address and port */
940 if (mgcp_msg->audio_port == 0) {
941 LOGP(DLMGCP, LOGL_ERROR,
942 "Invalid port number, can not generate MGCP message\n");
943 msgb_free(msg);
944 return NULL;
945 }
946 if (strlen(mgcp_msg->audio_ip) <= 0) {
947 LOGP(DLMGCP, LOGL_ERROR,
948 "Empty ip address, can not generate MGCP message\n");
949 msgb_free(msg);
950 return NULL;
951 }
952 rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
953
954 /* Add time description, active time (SDP) */
955 rc += msgb_printf(msg, "t=0 0\r\n");
956
957 rc +=
958 msgb_printf(msg, "m=audio %u RTP/AVP 255\r\n",
959 mgcp_msg->audio_port);
1289 else
1290 rc += rc_sdp;
9601291 }
9611292
9621293 if (rc != 0) {
111111 .verb = MGCP_VERB_CRCX,
112112 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
113113 .call_id = mgcp_ctx->conn_peer_local.call_id,
114 .conn_mode = MGCP_CONN_LOOPBACK,
114 .conn_mode = MGCP_CONN_RECV_ONLY,
115 .ptime = mgcp_ctx->conn_peer_local.ptime,
116 .codecs_len = mgcp_ctx->conn_peer_local.codecs_len
115117 };
116118 osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
119 memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
117120
118121 return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
119122 }
123126 struct mgcp_msg mgcp_msg;
124127
125128 mgcp_msg = (struct mgcp_msg) {
126 .verb = MGCP_VERB_CRCX,.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
127 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
128 MGCP_MSG_PRESENCE_AUDIO_PORT),
129 .verb = MGCP_VERB_CRCX,
130 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
131 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
132 MGCP_MSG_PRESENCE_AUDIO_PORT),
129133 .call_id = mgcp_ctx->conn_peer_local.call_id,
130134 .conn_mode = MGCP_CONN_RECV_SEND,
131135 .audio_ip = mgcp_ctx->conn_peer_local.addr,
132136 .audio_port = mgcp_ctx->conn_peer_local.port,
137 .ptime = mgcp_ctx->conn_peer_local.ptime,
138 .codecs_len = mgcp_ctx->conn_peer_local.codecs_len
133139 };
134140 osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
141 memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
135142
136143 return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
137144 }
149156 .conn_mode = MGCP_CONN_RECV_SEND,
150157 .audio_ip = mgcp_ctx->conn_peer_local.addr,
151158 .audio_port = mgcp_ctx->conn_peer_local.port,
159 .ptime = mgcp_ctx->conn_peer_local.ptime,
160 .codecs_len = mgcp_ctx->conn_peer_local.codecs_len
152161 };
153162 osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
163 memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
154164
155165 /* Note: We take the endpoint and the call_id from the remote
156166 * connection info, because we can be confident that the
212222 OSMO_ASSERT(false);
213223 break;
214224 }
225 }
226
227 /* Return the CI that the MGW allocated during CRCX response. This is purely informational for logging
228 * and identity tracking; the mgcp_conn_*() functions take care of using the right CI internally. */
229 const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi)
230 {
231 struct mgcp_ctx *mgcp_ctx = fi->priv;
232 return mgcp_ctx->conn_id;
215233 }
216234
217235 static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv)
475493 * mgcp_conn_delete() to instruct the FSM to perform a graceful exit */
476494 if (strlen(mgcp_ctx->conn_id)) {
477495 LOGPFSML(fi, LOGL_ERROR,
478 "MGW/DLCX: aprupt FSM termination with connections still present, sending unconditional DLCX...\n");
496 "MGW/DLCX: abrupt FSM termination with connections still present, sending unconditional DLCX...\n");
479497 msg = make_dlcx_msg(mgcp_ctx);
480498 OSMO_ASSERT(msg);
481499 mgcp_client_tx(mgcp, msg, NULL, NULL);
547565
548566 /*! allocate FSM, and create a new connection on the MGW.
549567 * \param[in] mgcp MGCP client descriptor.
550 * \param[in] mgcpparent_fi Parent FSM instance.
568 * \param[in] parent_fi Parent FSM instance.
551569 * \param[in] parent_term_evt Event to be sent to parent when terminating.
552570 * \param[in] parent_evt Event to be sent to parent when operation is done.
553571 * \param[in] conn_peer Connection parameters (ip, port...).
564582 OSMO_ASSERT(mgcp);
565583 OSMO_ASSERT(conn_peer);
566584
567 /* Check if IP/Port informstaion in conn info makes sense */
585 /* Check if IP/Port information in conn info makes sense */
568586 if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0)
569587 return NULL;
570588
621639 OSMO_ASSERT(fi->state != ST_DLCX_RESP);
622640
623641 /* Check if IP/Port parameters make sense */
624 if (conn_peer->port == 0)
642 if (conn_peer->port == 0) {
643 LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, port == 0\n");
625644 return -EINVAL;
626 if (inet_aton(conn_peer->addr, &ip_test) == 0)
645 }
646 if (inet_aton(conn_peer->addr, &ip_test) == 0) {
647 LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address == 0.0.0.0\n");
627648 return -EINVAL;
649 }
628650
629651 /*! The user may supply an endpoint identifier in conn_peer. The
630652 * identifier is then checked. This check is optional. Later steps do
631653 * not depend on the endpoint identifier supplied here because it is
632654 * already implicitly known from the CRCX phase. */
633 if (strlen(conn_peer->endpoint) && strcmp(conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint))
655 if (strlen(conn_peer->endpoint) && strcmp(conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint)) {
656 LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, endpoint mismatches: requested %s, should be %s\n",
657 conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint);
634658 return -EINVAL;
659 }
635660
636661 /*! Note: The call-id is implicitly known from the previous CRCX and
637662 * will not be checked even when it is set in conn_peer. */
2525 #include <osmocom/mgcp/mgcp_stat.h>
2626 #include <osmocom/mgcp/mgcp_msg.h>
2727 #include <osmocom/mgcp/mgcp_endp.h>
28 #include <osmocom/mgcp/mgcp_sdp.h>
29 #include <osmocom/mgcp/mgcp_codec.h>
2830
2931 #include <osmocom/core/application.h>
3032 #include <osmocom/core/talloc.h>
155157 "s=-\r\n" \
156158 "c=IN IP4 0.0.0.0\r\n" \
157159 "t=0 0\r\n" \
158 "m=audio 16002 RTP/AVP 96\r\n" \
159 "a=rtpmap:96 AMR\r\n" \
160 "m=audio 16002 RTP/AVP 112\r\n" \
161 "a=rtpmap:112 AMR\r\n" \
160162 "a=ptime:40\r\n"
161163
162164 #define MDCX4_PT1 \
403405 "v=0\r\n" \
404406 "o=- 1439038275 1439038275 IN IP4 192.168.181.247\r\n" \
405407 "s=-\r\nc=IN IP4 192.168.181.247\r\n" \
406 "t=0 0\r\nm=audio 29084 RTP/AVP 255 0 8 3 18 4 96 97 101\r\n" \
408 "t=0 0\r\nm=audio 29084 RTP/AVP 0 8 3 18 4 96 97 101\r\n" \
407409 "a=rtpmap:0 PCMU/8000\r\n" \
408410 "a=rtpmap:8 PCMA/8000\r\n" \
409411 "a=rtpmap:3 gsm/8000\r\n" \
424426 "I: %s\r\n" \
425427 "\r\n" \
426428 "c=IN IP4 8.8.8.8\r\n" \
427 "m=audio 16434 RTP/AVP 255\r\n"
429 "m=audio 16434 RTP/AVP 3\r\n"
430
431 #define CRCX_NO_LCO_NO_SDP \
432 "CRCX 2 6@mgw MGCP 1.0\r\n" \
433 "M: recvonly\r\n" \
434 "C: 2\r\n"
435
436 #define CRCX_NO_LCO_NO_SDP_RET \
437 "200 2 OK\r\n" \
438 "I: %s\r\n" \
439 "\r\n" \
440 "v=0\r\n" \
441 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
442 "s=-\r\n" \
443 "c=IN IP4 0.0.0.0\r\n" \
444 "t=0 0\r\n" \
445 "m=audio 16008 RTP/AVP 0\r\n" \
446 "a=ptime:20\r\n"
428447
429448 struct mgcp_test {
430449 const char *name;
461480 {"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE,.extra_fmtp =
462481 "a=fmtp:126 0/1/2"},
463482 {"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE,.extra_fmtp = "a=fmtp:126 0/1/2"},
483 {"CRCX", CRCX_NO_LCO_NO_SDP, CRCX_NO_LCO_NO_SDP_RET, 97},
464484 };
465485
466486 static const struct mgcp_test retransmit[] = {
763783 fprintf(stderr, "endpoint %d: "
764784 "payload type %d (expected %d)\n",
765785 last_endpoint,
766 conn->end.codec.payload_type, t->ptype);
786 conn->end.codec->payload_type, t->ptype);
767787
768788 if (t->ptype != PTYPE_IGNORE)
769 OSMO_ASSERT(conn->end.codec.payload_type ==
789 OSMO_ASSERT(conn->end.codec->payload_type ==
770790 t->ptype);
771791
772792 /* Reset them again for next test */
773 conn->end.codec.payload_type = PTYPE_NONE;
793 conn->end.codec->payload_type = PTYPE_NONE;
774794 }
775795 }
776796
916936 static void test_packet_loss_calc(void)
917937 {
918938 int i;
939 struct mgcp_endpoint endp;
940 struct mgcp_trunk_config trunk;
941
919942 printf("Testing packet loss calculation.\n");
943
944 memset(&endp, 0, sizeof(endp));
945 memset(&trunk, 0, sizeof(trunk));
946
947 endp.type = &ep_typeset.rtp;
948 trunk.vty_number_endpoints = 1;
949 trunk.endpoints = &endp;
950 endp.tcfg = &trunk;
951 INIT_LLIST_HEAD(&endp.conns);
920952
921953 for (i = 0; i < ARRAY_SIZE(pl_test_dat); ++i) {
922954 uint32_t expected;
923955 int loss;
924 struct mgcp_rtp_state state;
925 struct mgcp_rtp_end rtp;
926 memset(&state, 0, sizeof(state));
927 memset(&rtp, 0, sizeof(rtp));
928
929 state.stats.initialized = 1;
930 state.stats.base_seq = pl_test_dat[i].base_seq;
931 state.stats.max_seq = pl_test_dat[i].max_seq;
932 state.stats.cycles = pl_test_dat[i].cycles;
933
934 rtp.stats.packets_rx = pl_test_dat[i].packets;
935 calc_loss(&state, &rtp, &expected, &loss);
956
957 struct mgcp_conn_rtp *conn = NULL;
958 struct mgcp_conn *_conn = NULL;
959 struct mgcp_rtp_state *state;
960 struct rate_ctr *packets_rx;
961
962 _conn =
963 mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
964 "test-connection");
965 conn = mgcp_conn_get_rtp(&endp, _conn->id);
966 state = &conn->state;
967 packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
968
969 state->stats.initialized = 1;
970 state->stats.base_seq = pl_test_dat[i].base_seq;
971 state->stats.max_seq = pl_test_dat[i].max_seq;
972 state->stats.cycles = pl_test_dat[i].cycles;
973
974 packets_rx->current = pl_test_dat[i].packets;
975 calc_loss(conn, &expected, &loss);
936976
937977 if (loss != pl_test_dat[i].loss
938978 || expected != pl_test_dat[i].expected) {
941981 i, loss, pl_test_dat[i].loss, expected,
942982 pl_test_dat[i].expected);
943983 }
984
985 mgcp_conn_free_all(&endp);
944986 }
987
945988 }
946989
947990 int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os,
11281171 uint32_t last_ssrc = 0;
11291172 uint32_t last_timestamp = 0;
11301173 uint32_t last_seqno = 0;
1131 int last_in_ts_err_cnt = 0;
1132 int last_out_ts_err_cnt = 0;
1174 uint64_t last_in_ts_err_cnt = 0;
1175 uint64_t last_out_ts_err_cnt = 0;
11331176 struct mgcp_conn_rtp *conn = NULL;
11341177 struct mgcp_conn *_conn = NULL;
1178 struct rate_ctr test_ctr_in;
1179 struct rate_ctr test_ctr_out;
11351180
11361181 printf("Testing packet error detection%s%s.\n",
11371182 patch_ssrc ? ", patch SSRC" : "",
11401185 memset(&trunk, 0, sizeof(trunk));
11411186 memset(&endp, 0, sizeof(endp));
11421187 memset(&state, 0, sizeof(state));
1188
1189 memset(&test_ctr_in, 0, sizeof(test_ctr_in));
1190 memset(&test_ctr_out, 0, sizeof(test_ctr_out));
1191 state.in_stream.err_ts_ctr = &test_ctr_in;
1192 state.out_stream.err_ts_ctr = &test_ctr_out;
11431193
11441194 endp.type = &ep_typeset.rtp;
11451195
11591209
11601210 rtp = &conn->end;
11611211
1162 rtp->codec.payload_type = 98;
1212 OSMO_ASSERT(mgcp_codec_add(conn, PTYPE_UNDEFINED, "AMR/8000/1") == 0);
1213 rtp->codec = &rtp->codecs[0];
11631214
11641215 for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
11651216 struct rtp_packet_info *info = test_rtp_packets1 + i;
11851236 state.in_stream.last_tsdelta, state.in_stream.last_seq);
11861237
11871238 printf("Out TS change: %d, dTS: %d, Seq change: %d, "
1188 "TS Err change: in %+d, out %+d\n",
1239 "TS Err change: in +%u, out +%u\n",
11891240 state.out_stream.last_timestamp - last_timestamp,
11901241 state.out_stream.last_tsdelta,
11911242 state.out_stream.last_seq - last_seqno,
1192 state.in_stream.err_ts_counter - last_in_ts_err_cnt,
1193 state.out_stream.err_ts_counter - last_out_ts_err_cnt);
1243 (unsigned int) (state.in_stream.err_ts_ctr->current - last_in_ts_err_cnt),
1244 (unsigned int) (state.out_stream.err_ts_ctr->current - last_out_ts_err_cnt));
11941245
11951246 printf("Stats: Jitter = %u, Transit = %d\n",
11961247 calc_jitter(&state), state.stats.transit);
11971248
1198 last_in_ts_err_cnt = state.in_stream.err_ts_counter;
1199 last_out_ts_err_cnt = state.out_stream.err_ts_counter;
1249 last_in_ts_err_cnt = state.in_stream.err_ts_ctr->current;
1250 last_out_ts_err_cnt = state.out_stream.err_ts_ctr->current;
12001251 last_timestamp = state.out_stream.last_timestamp;
12011252 last_seqno = state.out_stream.last_seq;
12021253 }
12351286 endp = &cfg->trunk.endpoints[last_endpoint];
12361287 conn = mgcp_conn_get_rtp(endp, conn_id);
12371288 OSMO_ASSERT(conn);
1238 OSMO_ASSERT(conn->end.codec.payload_type == 18);
1239 OSMO_ASSERT(conn->end.alt_codec.payload_type == 97);
1289 OSMO_ASSERT(conn->end.codec->payload_type == 18);
12401290
12411291 /* Allocate 2@mgw with three codecs, last one ignored */
12421292 last_endpoint = -1;
12511301 endp = &cfg->trunk.endpoints[last_endpoint];
12521302 conn = mgcp_conn_get_rtp(endp, conn_id);
12531303 OSMO_ASSERT(conn);
1254 OSMO_ASSERT(conn->end.codec.payload_type == 18);
1255 OSMO_ASSERT(conn->end.alt_codec.payload_type == 97);
1256
1257 /* Allocate 3@mgw with no codecs, check for PT == -1 */
1304 OSMO_ASSERT(conn->end.codec->payload_type == 18);
1305
1306 /* Allocate 3@mgw with no codecs, check for PT == 0 */
1307 /* Note: It usually makes no sense to leave the payload type list
1308 * out. However RFC 2327 does not clearly forbid this case and
1309 * it makes and since we already decided in OS#2658 that a missing
1310 * LCO should pick a sane default codec, it makes sense to expect
1311 * the same behaviour if SDP lacks proper payload type information */
12581312 last_endpoint = -1;
12591313 inp = create_msg(CRCX_MULT_3, NULL);
12601314 resp = mgcp_handle_message(cfg, inp);
12671321 endp = &cfg->trunk.endpoints[last_endpoint];
12681322 conn = mgcp_conn_get_rtp(endp, conn_id);
12691323 OSMO_ASSERT(conn);
1270 OSMO_ASSERT(conn->end.codec.payload_type == -1);
1271 OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
1324 OSMO_ASSERT(conn->end.codec->payload_type == 0);
12721325
12731326 /* Allocate 4@mgw with a single codec */
12741327 last_endpoint = -1;
12831336 endp = &cfg->trunk.endpoints[last_endpoint];
12841337 conn = mgcp_conn_get_rtp(endp, conn_id);
12851338 OSMO_ASSERT(conn);
1286 OSMO_ASSERT(conn->end.codec.payload_type == 18);
1287 OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
1339 OSMO_ASSERT(conn->end.codec->payload_type == 18);
12881340
12891341 /* Allocate 5@mgw at select GSM.. */
12901342 last_endpoint = -1;
13021354 endp = &cfg->trunk.endpoints[last_endpoint];
13031355 conn = mgcp_conn_get_rtp(endp, conn_id);
13041356 OSMO_ASSERT(conn);
1305 OSMO_ASSERT(conn->end.codec.payload_type == 3);
1306 OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
1357 OSMO_ASSERT(conn->end.codec->payload_type == 3);
13071358
13081359 inp = create_msg(MDCX_NAT_DUMMY, conn_id);
13091360 last_endpoint = -1;
13141365 endp = &cfg->trunk.endpoints[last_endpoint];
13151366 conn = mgcp_conn_get_rtp(endp, conn_id);
13161367 OSMO_ASSERT(conn);
1317 OSMO_ASSERT(conn->end.codec.payload_type == 3);
1318 OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
1368 OSMO_ASSERT(conn->end.codec->payload_type == 3);
13191369 OSMO_ASSERT(conn->end.rtp_port == htons(16434));
13201370 memset(&addr, 0, sizeof(addr));
13211371 inet_aton("8.8.8.8", &addr);
13451395 endp = &cfg->trunk.endpoints[last_endpoint];
13461396 conn = mgcp_conn_get_rtp(endp, conn_id);
13471397 OSMO_ASSERT(conn);
1348 OSMO_ASSERT(conn->end.codec.payload_type == 255);
1349 OSMO_ASSERT(conn->end.alt_codec.payload_type == 0);
1398 OSMO_ASSERT(conn->end.codec->payload_type == 0);
13501399
13511400 talloc_free(cfg);
13521401 }
14631512 .cat = log_categories,
14641513 .num_cat = ARRAY_SIZE(log_categories),
14651514 };
1515
1516 static void test_get_lco_identifier(void)
1517 {
1518 char *test;
1519 printf("Testing get_lco_identifier()\n");
1520
1521 /* Normal case at the beginning */
1522 test = "p:10, a:PCMU";
1523 printf("%s -> %s\n", test, get_lco_identifier(test));
1524
1525 test = "p:10, a:PCMU";
1526 printf("%s -> %s\n", test, get_lco_identifier(test));
1527
1528 /* Begin parsing in the middle of the value part of
1529 * the previous LCO option value */
1530 test = "XXXX, p:10, a:PCMU";
1531 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1532
1533 test = "XXXX,p:10,a:PCMU";
1534 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1535
1536 test = "10,a:PCMU";
1537 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1538
1539 test = "10, a:PCMU";
1540 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1541
1542 test = "10,a: PCMU";
1543 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1544
1545 test = "10 ,a: PCMU";
1546 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1547
1548 /* Begin parsing right at the end of the previous LCO
1549 * option value */
1550 test = ", a:PCMU";
1551 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1552
1553 test = " a:PCMU";
1554 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1555
1556 /* Empty string, result should be (null) */
1557 test = "";
1558 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1559
1560 /* Missing colons, result should be (null) */
1561 test = "p10, aPCMU";
1562 printf("%s -> %s\n", test, get_lco_identifier(test));
1563
1564 /* Colon separated from the identifier, result should be (null) */
1565 test = "10,a :PCMU";
1566 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1567 }
1568
1569 static void test_check_local_cx_options(void *ctx)
1570 {
1571 /* Legal cases */
1572 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU") == 0);
1573 OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU") == 0);
1574 OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, p:10, IN:10") == 0);
1575 OSMO_ASSERT(check_local_cx_options(ctx, "one:AAA, two:BB, tree:C") ==
1576 0);
1577 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU") == 0);
1578 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:G726-32") == 0);
1579 OSMO_ASSERT(check_local_cx_options(ctx, "p:10-20, b:64") == 0);
1580 OSMO_ASSERT(check_local_cx_options(ctx, "b:32-64, e:off") == 0);
1581 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU;G726-32") == 0);
1582
1583 /* Illegal spaces before and after colon */
1584 OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, p :10") == -1);
1585 OSMO_ASSERT(check_local_cx_options(ctx, "a :PCMU, p:10") == -1);
1586 OSMO_ASSERT(check_local_cx_options(ctx, "p: 10, a:PCMU") == -1);
1587
1588 /* Check if multiple appearances of LCOs are rejected */
1589 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU, p:10") == -1);
1590 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU, a:PCMU, p:10") ==
1591 -1);
1592 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, p:10") == -1);
1593
1594 /* Check if empty LCO are rejected */
1595 OSMO_ASSERT(check_local_cx_options(ctx, "p: , a:PCMU") == -1);
1596 OSMO_ASSERT(check_local_cx_options(ctx, "p: , a: PCMU") == -1);
1597 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a: PCMU") == -1);
1598 OSMO_ASSERT(check_local_cx_options(ctx, "p:, a:PCMU") == -1);
1599 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:") == -1);
1600
1601 /* Garbeled beginning and ends */
1602 OSMO_ASSERT(check_local_cx_options(ctx, ":10, a:10") == -1);
1603 OSMO_ASSERT(check_local_cx_options(ctx, "10, a:PCMU") == -1);
1604 OSMO_ASSERT(check_local_cx_options(ctx, ", a:PCMU") == -1);
1605 OSMO_ASSERT(check_local_cx_options(ctx, " a:PCMU") == -1);
1606 OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU,") == -1);
1607 OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, ") == -1);
1608
1609 /* Illegal strings */
1610 OSMO_ASSERT(check_local_cx_options(ctx, " ") == -1);
1611 OSMO_ASSERT(check_local_cx_options(ctx, "") == -1);
1612 OSMO_ASSERT(check_local_cx_options(ctx, "AAA") == -1);
1613 OSMO_ASSERT(check_local_cx_options(ctx, ":,") == -1);
1614 OSMO_ASSERT(check_local_cx_options(ctx, ",:") == -1);
1615 OSMO_ASSERT(check_local_cx_options(ctx, ",,,") == -1);
1616 }
14661617
14671618 int main(int argc, char **argv)
14681619 {
14851636 test_no_cycle();
14861637 test_no_name();
14871638 test_osmux_cid();
1639 test_get_lco_identifier();
1640 test_check_local_cx_options(ctx);
14881641
14891642 OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
14901643 OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
418418 Testing CRCX
419419 creating message from statically defined input:
420420 ---------8<---------
421 CRCX 2 6@mgw MGCP 1.0
422 M: recvonly
423 C: 2
424
425 ---------8<---------
426 checking response:
427 using message with patched conn_id for comparison
428 Response matches our expectations.
429 (response does not contain a connection id)
430 Dummy packets: 2
431
432 ================================================
433 Testing CRCX
434 creating message from statically defined input:
435 ---------8<---------
421436 CRCX 2 1@mgw MGCP 1.0
422437 M: recvonly
423438 C: 2
10411056 s=-
10421057 c=IN IP4 192.168.181.247
10431058 t=0 0
1044 m=audio 29084 RTP/AVP 255 0 8 3 18 4 96 97 101
1059 m=audio 29084 RTP/AVP 0 8 3 18 4 96 97 101
10451060 a=rtpmap:0 PCMU/8000
10461061 a=rtpmap:8 PCMA/8000
10471062 a=rtpmap:3 gsm/8000
10641079 I: %s
10651080
10661081 c=IN IP4 8.8.8.8
1067 m=audio 16434 RTP/AVP 255
1082 m=audio 16434 RTP/AVP 3
10681083
10691084 ---------8<---------
10701085 creating message from statically defined input:
10791094 s=-
10801095 c=IN IP4 192.168.181.247
10811096 t=0 0
1082 m=audio 29084 RTP/AVP 255 0 8 3 18 4 96 97 101
1097 m=audio 29084 RTP/AVP 0 8 3 18 4 96 97 101
10831098 a=rtpmap:0 PCMU/8000
10841099 a=rtpmap:8 PCMA/8000
10851100 a=rtpmap:3 gsm/8000
11141129 checking response:
11151130 using message with patched conn_id for comparison
11161131 Response matches our expectations.
1132 Testing get_lco_identifier()
1133 p:10, a:PCMU -> p:10, a:PCMU
1134 p:10, a:PCMU -> p:10, a:PCMU
1135 'XXXX, p:10, a:PCMU' -> 'p:10, a:PCMU'
1136 'XXXX,p:10,a:PCMU' -> 'p:10,a:PCMU'
1137 '10,a:PCMU' -> 'a:PCMU'
1138 '10, a:PCMU' -> 'a:PCMU'
1139 '10,a: PCMU' -> 'a: PCMU'
1140 '10 ,a: PCMU' -> 'a: PCMU'
1141 ', a:PCMU' -> 'a:PCMU'
1142 ' a:PCMU' -> 'a:PCMU'
1143 '' -> '(null)'
1144 p10, aPCMU -> (null)
1145 '10,a :PCMU' -> '(null)'
11171146 Done
9494
9595 void test_response_cb(struct mgcp_response *response, void *priv)
9696 {
97 unsigned int i;
9798 OSMO_ASSERT(priv == mgcp);
9899 mgcp_response_parse_params(response);
99100
100 printf("response cb received:\n"
101 " head.response_code = %d\n"
102 " head.trans_id = %u\n"
103 " head.comment = %s\n"
104 " audio_port = %u\n"
105 " audio_ip = %s\n",
106 response->head.response_code,
107 response->head.trans_id,
108 response->head.comment,
109 response->audio_port,
110 response->audio_ip
111 );
101 printf("response cb received:\n");
102 printf(" head.response_code = %d\n", response->head.response_code);
103 printf(" head.trans_id = %u\n", response->head.trans_id);
104 printf(" head.comment = %s\n", response->head.comment);
105 printf(" audio_port = %u\n", response->audio_port);
106 printf(" audio_ip = %s\n", response->audio_ip);
107 printf(" ptime = %u\n", response->ptime);
108 printf(" codecs_len = %u\n", response->codecs_len);
109 for(i=0;i<response->codecs_len;i++)
110 printf(" codecs[%u] = %u\n", i, response->codecs[i]);
111 printf(" ptmap_len = %u\n", response->ptmap_len);
112 for(i=0;i<response->ptmap_len;i++) {
113 printf(" ptmap[%u].codec = %u\n", i, response->ptmap[i].codec);
114 printf(" ptmap[%u].pt = %u\n", i, response->ptmap[i].pt);
115 }
116
112117 }
113118
114119 mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg)
148153 "s=-\r\n"
149154 "c=IN IP4 10.9.1.120\r\n"
150155 "t=0 0\r\n"
151 "m=audio 16002 RTP/AVP 98\r\n"
152 "a=rtpmap:98 AMR/8000\r\n"
156 "m=audio 16002 RTP/AVP 110 96\r\n"
157 "a=rtpmap:110 AMR/8000\r\n"
158 "a=rtpmap:96 GSM-EFR/8000\r\n"
153159 "a=ptime:20\r\n");
154160 }
155161
165171 .audio_port = 1234,
166172 .call_id = 47,
167173 .conn_id = "11",
168 .conn_mode = MGCP_CONN_RECV_SEND
174 .conn_mode = MGCP_CONN_RECV_SEND,
175 .ptime = 20,
176 .codecs[0] = CODEC_GSM_8000_1,
177 .codecs[1] = CODEC_AMR_8000_1,
178 .codecs[2] = CODEC_GSMEFR_8000_1,
179 .codecs_len = 1,
180 .ptmap[0].codec = CODEC_GSMEFR_8000_1,
181 .ptmap[0].pt = 96,
182 .ptmap_len = 1
169183 };
170184
171185 if (mgcp)
182196 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
183197 printf("%s\n", (char *)msg->data);
184198
199 printf("Generated CRCX message (two codecs):\n");
200 mgcp_msg.verb = MGCP_VERB_CRCX;
201 mgcp_msg.presence =
202 (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
203 MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
204 mgcp_msg.codecs_len = 2;
205 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
206 mgcp_msg.codecs_len = 1;
207 printf("%s\n", (char *)msg->data);
208
209 printf("Generated CRCX message (three codecs, one with custom pt):\n");
210 mgcp_msg.verb = MGCP_VERB_CRCX;
211 mgcp_msg.presence =
212 (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
213 MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
214 mgcp_msg.codecs_len = 3;
215 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
216 mgcp_msg.codecs_len = 1;
217 printf("%s\n", (char *)msg->data);
218
185219 printf("Generated MDCX message:\n");
186220 mgcp_msg.verb = MGCP_VERB_MDCX;
187221 mgcp_msg.presence =
190224 MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
191225 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
192226 printf("%s\n", (char *)msg->data);
227
228 printf("Generated MDCX message (two codecs):\n");
229 mgcp_msg.verb = MGCP_VERB_MDCX;
230 mgcp_msg.presence =
231 (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
232 MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
233 MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
234 mgcp_msg.codecs_len = 2;
235 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
236 mgcp_msg.codecs_len = 1;
237 printf("%s\n", (char *)msg->data);
238
239 printf("Generated MDCX message (three codecs, one with custom pt):\n");
240 mgcp_msg.verb = MGCP_VERB_MDCX;
241 mgcp_msg.presence =
242 (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
243 MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
244 MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
245 mgcp_msg.codecs_len = 3;
246 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
247 mgcp_msg.codecs_len = 1;
248 printf("%s\n", (char *)msg->data);
193249
194250 printf("Generated DLCX message:\n");
195251 mgcp_msg.verb = MGCP_VERB_DLCX;
241297 .conn_mode = MGCP_CONN_RECV_SEND,
242298 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID
243299 | MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE),
300 .ptime = 20,
301 .codecs[0] = CODEC_AMR_8000_1,
302 .codecs_len = 1
244303 };
245304
246305 printf("\n%s():\n", __func__);
375434 OSMO_ASSERT(!failures);
376435 }
377436
437 static void test_map_pt_to_codec(void)
438 {
439 /* Full form */
440 OSMO_ASSERT(map_str_to_codec("PCMU/8000/1") == CODEC_PCMU_8000_1);
441 OSMO_ASSERT(map_str_to_codec("GSM/8000/1") == CODEC_GSM_8000_1);
442 OSMO_ASSERT(map_str_to_codec("PCMA/8000/1") == CODEC_PCMA_8000_1);
443 OSMO_ASSERT(map_str_to_codec("G729/8000/1") == CODEC_G729_8000_1);
444 OSMO_ASSERT(map_str_to_codec("GSM-EFR/8000/1") == CODEC_GSMEFR_8000_1);
445 OSMO_ASSERT(map_str_to_codec("GSM-HR-08/8000/1") == CODEC_GSMHR_8000_1);
446 OSMO_ASSERT(map_str_to_codec("AMR/8000/1") == CODEC_AMR_8000_1);
447 OSMO_ASSERT(map_str_to_codec("AMR-WB/16000/1") == CODEC_AMRWB_16000_1);
448
449 /* Short form */
450 OSMO_ASSERT(map_str_to_codec("GSM-EFR") == CODEC_GSMEFR_8000_1);
451 OSMO_ASSERT(map_str_to_codec("G729") == CODEC_G729_8000_1);
452 OSMO_ASSERT(map_str_to_codec("GSM-HR-08") == CODEC_GSMHR_8000_1);
453
454 /* We do not care about what is after the first delimiter */
455 OSMO_ASSERT(map_str_to_codec("AMR-WB/123///456") == CODEC_AMRWB_16000_1);
456 OSMO_ASSERT(map_str_to_codec("PCMA/asdf") == CODEC_PCMA_8000_1);
457 OSMO_ASSERT(map_str_to_codec("GSM/qwertz") == CODEC_GSM_8000_1);
458
459 /* A trailing delimiter should not hurt */
460 OSMO_ASSERT(map_str_to_codec("AMR/") == CODEC_AMR_8000_1);
461 OSMO_ASSERT(map_str_to_codec("G729/") == CODEC_G729_8000_1);
462 OSMO_ASSERT(map_str_to_codec("GSM/") == CODEC_GSM_8000_1);
463
464 /* This is expected to fail */
465 OSMO_ASSERT(map_str_to_codec("INVALID/1234/7") == -1);
466 OSMO_ASSERT(map_str_to_codec(NULL) == -1);
467 OSMO_ASSERT(map_str_to_codec("") == -1);
468 OSMO_ASSERT(map_str_to_codec("/////") == -1);
469
470 /* The buffers are 64 bytes long, check what happens with overlong
471 * strings as input (This schould still work.) */
472 OSMO_ASSERT(map_str_to_codec("AMR-WB/16000/1############################################################################################################") == CODEC_AMRWB_16000_1);
473
474 /* This should not work, as there is no delimiter after the codec
475 * name */
476 OSMO_ASSERT(map_str_to_codec("AMR-WB####################################################################################################################") == -1);
477 }
478
479 static void test_map_codec_to_pt_and_map_pt_to_codec(void)
480 {
481 struct ptmap ptmap[10];
482 unsigned int ptmap_len;
483 unsigned int i;
484
485 ptmap[0].codec = CODEC_GSMEFR_8000_1;
486 ptmap[0].pt = 96;
487 ptmap[1].codec = CODEC_GSMHR_8000_1;
488 ptmap[1].pt = 97;
489 ptmap[2].codec = CODEC_AMR_8000_1;
490 ptmap[2].pt = 98;
491 ptmap[3].codec = CODEC_AMRWB_16000_1;
492 ptmap[3].pt = 99;
493 ptmap_len = 4;
494
495 /* Mappings that are covered by the table */
496 for (i = 0; i < ptmap_len; i++)
497 printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
498 for (i = 0; i < ptmap_len; i++)
499 printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
500 printf("\n");
501
502 /* Map some codecs/payload types from the static range, result must
503 * always be a 1:1 mapping */
504 printf(" %u => %u\n", CODEC_PCMU_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMU_8000_1));
505 printf(" %u => %u\n", CODEC_GSM_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_GSM_8000_1));
506 printf(" %u => %u\n", CODEC_PCMA_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMA_8000_1));
507 printf(" %u => %u\n", CODEC_G729_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_G729_8000_1));
508 printf(" %u <= %u\n", CODEC_PCMU_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMU_8000_1));
509 printf(" %u <= %u\n", CODEC_GSM_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_GSM_8000_1));
510 printf(" %u <= %u\n", CODEC_PCMA_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMA_8000_1));
511 printf(" %u <= %u\n", CODEC_G729_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_G729_8000_1));
512 printf("\n");
513
514 /* Try to do mappings from statically defined range to danymic range and vice versa. This
515 * is illegal and should result into a 1:1 mapping */
516 ptmap[3].codec = CODEC_AMRWB_16000_1;
517 ptmap[3].pt = 2;
518 ptmap[4].codec = CODEC_PCMU_8000_1;
519 ptmap[4].pt = 100;
520 ptmap_len = 5;
521
522 /* Apply all mappings again, the illegal ones we defined should result into 1:1 mappings */
523 for (i = 0; i < ptmap_len; i++)
524 printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
525 for (i = 0; i < ptmap_len; i++)
526 printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
527 printf("\n");
528 }
529
378530 static const struct log_info_cat log_categories[] = {
379531 };
380532
402554 test_mgcp_msg();
403555 test_mgcp_client_cancel();
404556 test_sdp_section_start();
557 test_map_codec_to_pt_and_map_pt_to_codec();
558 test_map_pt_to_codec();
405559
406560 printf("Done\n");
407561 fprintf(stderr, "Done\n");
6161 body: "some mgcp header data\r\nand header params\n\r\rm=audio 23\r\n"
6262 DLMGCP MGCP response: cannot find start of SDP parameters
6363 got rc=-22
64 DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
65 DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
66 DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
67 DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
6468 Done
1717 s=-
1818 c=IN IP4 10.9.1.120
1919 t=0 0
20 m=audio 16002 RTP/AVP 98
21 a=rtpmap:98 AMR/8000
20 m=audio 16002 RTP/AVP 110 96
21 a=rtpmap:110 AMR/8000
22 a=rtpmap:96 GSM-EFR/8000
2223 a=ptime:20
2324
2425 -----
2829 head.comment = OK
2930 audio_port = 16002
3031 audio_ip = 10.9.1.120
32 ptime = 20
33 codecs_len = 2
34 codecs[0] = 112
35 codecs[1] = 110
36 ptmap_len = 2
37 ptmap[0].codec = 112
38 ptmap[0].pt = 110
39 ptmap[1].codec = 110
40 ptmap[1].pt = 96
3141
3242 Generated CRCX message:
3343 CRCX 1 23@mgw MGCP 1.0
3444 C: 2f
3545 I: 11
36 L: p:20, a:AMR, nt:IN
46 L: p:20, a:GSM, nt:IN
47 M: sendrecv
48
49 Generated CRCX message (two codecs):
50 CRCX 2 23@mgw MGCP 1.0
51 C: 2f
52 I: 11
53 L: p:20, a:GSM;AMR, nt:IN
54 M: sendrecv
55
56 Generated CRCX message (three codecs, one with custom pt):
57 CRCX 3 23@mgw MGCP 1.0
58 C: 2f
59 I: 11
60 L: p:20, a:GSM;AMR;GSM-EFR, nt:IN
3761 M: sendrecv
3862
3963 Generated MDCX message:
40 MDCX 2 23@mgw MGCP 1.0
64 MDCX 4 23@mgw MGCP 1.0
4165 C: 2f
4266 I: 11
4367 M: sendrecv
4771 s=-
4872 c=IN IP4 192.168.100.23
4973 t=0 0
50 m=audio 1234 RTP/AVP 255
74 m=audio 1234 RTP/AVP 3
75 a=ptime:20
76
77 Generated MDCX message (two codecs):
78 MDCX 5 23@mgw MGCP 1.0
79 C: 2f
80 I: 11
81 M: sendrecv
82
83 v=0
84 o=- 2f 23 IN IP4 127.0.0.1
85 s=-
86 c=IN IP4 192.168.100.23
87 t=0 0
88 m=audio 1234 RTP/AVP 3 112
89 a=rtpmap:112 AMR/8000/1
90 a=ptime:20
91
92 Generated MDCX message (three codecs, one with custom pt):
93 MDCX 6 23@mgw MGCP 1.0
94 C: 2f
95 I: 11
96 M: sendrecv
97
98 v=0
99 o=- 2f 23 IN IP4 127.0.0.1
100 s=-
101 c=IN IP4 192.168.100.23
102 t=0 0
103 m=audio 1234 RTP/AVP 3 112 96
104 a=rtpmap:112 AMR/8000/1
105 a=rtpmap:96 GSM-EFR/8000/1
106 a=ptime:20
51107
52108 Generated DLCX message:
53 DLCX 3 23@mgw MGCP 1.0
109 DLCX 7 23@mgw MGCP 1.0
54110 C: 2f
55111 I: 11
56112
57113 Generated AUEP message:
58 AUEP 4 23@mgw MGCP 1.0
114 AUEP 8 23@mgw MGCP 1.0
59115
60116 Generated RSIP message:
61 RSIP 5 23@mgw MGCP 1.0
117 RSIP 9 23@mgw MGCP 1.0
62118
63119 Overfolow test:
64120
101157 test_sdp_section_start() test [8]:
102158
103159 test_sdp_section_start() test [9]:
160 110 => 96
161 111 => 97
162 112 => 98
163 113 => 99
164 96 <= 110
165 97 <= 111
166 98 <= 112
167 99 <= 113
168
169 0 => 0
170 3 => 3
171 8 => 8
172 18 => 18
173 0 <= 0
174 3 <= 3
175 8 <= 8
176 18 <= 18
177
178 110 => 96
179 111 => 97
180 112 => 98
181 113 => 113
182 0 => 0
183 96 <= 110
184 97 <= 111
185 98 <= 112
186 2 <= 2
187 100 <= 100
188
104189 Done