mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-11-04 05:53:26 +00:00 
			
		
		
		
	Compare commits
	
		
			13 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					306dc09d37 | ||
| 
						 | 
					eb282efa67 | ||
| 
						 | 
					02d5c8798b | ||
| 
						 | 
					efe3f98ec7 | ||
| 
						 | 
					3ebf7e2f05 | ||
| 
						 | 
					33826246c5 | ||
| 
						 | 
					9ef0c11abd | ||
| 
						 | 
					109ff5244e | ||
| 
						 | 
					4cf5e71a4b | ||
| 
						 | 
					8c6f3141e2 | ||
| 
						 | 
					12220eea9c | ||
| 
						 | 
					536dff1bb8 | ||
| 
						 | 
					ee784f9844 | 
@@ -195,6 +195,7 @@ AC_OUTPUT(
 | 
				
			|||||||
    tests/atlocal
 | 
					    tests/atlocal
 | 
				
			||||||
    tests/mgcp_client/Makefile
 | 
					    tests/mgcp_client/Makefile
 | 
				
			||||||
    tests/mgcp/Makefile
 | 
					    tests/mgcp/Makefile
 | 
				
			||||||
 | 
					    tests/iuup/Makefile
 | 
				
			||||||
    doc/Makefile
 | 
					    doc/Makefile
 | 
				
			||||||
    doc/examples/Makefile
 | 
					    doc/examples/Makefile
 | 
				
			||||||
    doc/manuals/Makefile
 | 
					    doc/manuals/Makefile
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,4 +7,6 @@ noinst_HEADERS = \
 | 
				
			|||||||
	mgcp_sdp.h \
 | 
						mgcp_sdp.h \
 | 
				
			||||||
	mgcp_codec.h \
 | 
						mgcp_codec.h \
 | 
				
			||||||
	debug.h \
 | 
						debug.h \
 | 
				
			||||||
 | 
						iuup_cn_node.h \
 | 
				
			||||||
 | 
						iuup_protocol.h \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,6 +29,7 @@
 | 
				
			|||||||
/* Debug Areas of the code */
 | 
					/* Debug Areas of the code */
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
	DRTP,
 | 
						DRTP,
 | 
				
			||||||
 | 
						DIUUP,
 | 
				
			||||||
	Debug_LastEntry,
 | 
						Debug_LastEntry,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										47
									
								
								include/osmocom/mgcp/iuup_cn_node.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								include/osmocom/mgcp/iuup_cn_node.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
 | 
				
			||||||
 | 
					/* IuUP CN node, minimal implementation */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*                                            _____IuUP_CN_____
 | 
				
			||||||
 | 
					 *                                            |               |
 | 
				
			||||||
 | 
					 * UE <--> RNC --PDU-> osmo_iuup_cn_rx_pdu() -+->           ---+-> rx_payload()
 | 
				
			||||||
 | 
					 *          |                                 |               |
 | 
				
			||||||
 | 
					 *          |  <-PDU-- tx_msg() <-------------+--           <-+--- osmo_iuup_cn_tx_payload()
 | 
				
			||||||
 | 
					 *                                            |               |
 | 
				
			||||||
 | 
					 *                                            -----------------
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct msgb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef int (*osmo_iuup_data_cb_t)(struct msgb *msg, void *node_priv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct osmo_iuup_cn_cfg {
 | 
				
			||||||
 | 
						void *node_priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* When the IuUP peer sent a voice packet, the clean RTP without the IuUP header is fed to this
 | 
				
			||||||
 | 
						 * callback. */
 | 
				
			||||||
 | 
						osmo_iuup_data_cb_t rx_payload;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* IuUP handler requests that a PDU shall be sent to the IuUP peer (e.g. the RNC).
 | 
				
			||||||
 | 
						 * It is guaranteed that the msgb->dst pointer is preserved or copied from the msgb that
 | 
				
			||||||
 | 
						 * originated the request. */
 | 
				
			||||||
 | 
						osmo_iuup_data_cb_t tx_msg;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct osmo_iuup_cn {
 | 
				
			||||||
 | 
						struct osmo_iuup_cn_cfg cfg;
 | 
				
			||||||
 | 
						char *name;
 | 
				
			||||||
 | 
						uint8_t next_frame_nr;
 | 
				
			||||||
 | 
						int rtp_payload_type;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool osmo_iuup_cn_is_iuup_init(struct msgb *msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
 | 
				
			||||||
 | 
									       const char *name_fmt, ...);
 | 
				
			||||||
 | 
					void osmo_iuup_cn_free(struct osmo_iuup_cn *cn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *payload);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu);
 | 
				
			||||||
							
								
								
									
										117
									
								
								include/osmocom/mgcp/iuup_protocol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								include/osmocom/mgcp/iuup_protocol.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
 | 
				
			||||||
 | 
					/* IuUP protocol handling, minimal implementation */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/endian.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/msgb.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define OSMO_IUUP_HEADROOM 32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum osmo_iuup_pdu_type {
 | 
				
			||||||
 | 
						OSMO_IUUP_PDU_DATA_WITH_CRC = 0,
 | 
				
			||||||
 | 
						OSMO_IUUP_PDU_CONTROL_PROCEDURE = 14,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum osmo_iuup_acknack {
 | 
				
			||||||
 | 
						OSMO_IUUP_ACKNACK_PROCEDURE = 0,
 | 
				
			||||||
 | 
						OSMO_IUUP_ACKNACK_ACK = 1,
 | 
				
			||||||
 | 
						OSMO_IUUP_ACKNACK_NACK = 2,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum osmo_iuup_procedure {
 | 
				
			||||||
 | 
						OSMO_IUUP_PROC_INITIALIZATION = 0,
 | 
				
			||||||
 | 
						OSMO_IUUP_PROC_RATE_CONTROL = 1,
 | 
				
			||||||
 | 
						OSMO_IUUP_PROC_TIME_ALIGNMENT = 2,
 | 
				
			||||||
 | 
						OSMO_IUUP_PROC_ERROR_EVENT = 3,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum osmo_iuup_frame_good {
 | 
				
			||||||
 | 
						OSMO_IUUP_FRAME_GOOD = 0,
 | 
				
			||||||
 | 
						OSMO_IUUP_FRAME_BAD = 1,
 | 
				
			||||||
 | 
						OSMO_IUUP_FRAME_BAD_DUE_TO_RADIO = 2,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct osmo_iuup_hdr_ctrl {
 | 
				
			||||||
 | 
					#if OSMO_IS_BIG_ENDIAN
 | 
				
			||||||
 | 
						uint8_t pdu_type:4,
 | 
				
			||||||
 | 
							ack_nack:2,
 | 
				
			||||||
 | 
							frame_nr:2;
 | 
				
			||||||
 | 
						uint8_t mode_version:4,
 | 
				
			||||||
 | 
							procedure:4;
 | 
				
			||||||
 | 
						uint8_t header_crc:6,
 | 
				
			||||||
 | 
							payload_crc_hi:2;
 | 
				
			||||||
 | 
						uint8_t payload_crc_lo;
 | 
				
			||||||
 | 
						uint8_t payload[0];
 | 
				
			||||||
 | 
					#elif OSMO_IS_LITTLE_ENDIAN
 | 
				
			||||||
 | 
						uint8_t frame_nr:2,
 | 
				
			||||||
 | 
							ack_nack:2,
 | 
				
			||||||
 | 
							pdu_type:4;
 | 
				
			||||||
 | 
						uint8_t procedure:4,
 | 
				
			||||||
 | 
							mode_version:4;
 | 
				
			||||||
 | 
						uint8_t payload_crc_hi:2,
 | 
				
			||||||
 | 
							header_crc:6;
 | 
				
			||||||
 | 
						uint8_t payload_crc_lo;
 | 
				
			||||||
 | 
						uint8_t payload[0];
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					} __attribute__((packed));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					union osmo_iuup_hdr_ctrl_payload {
 | 
				
			||||||
 | 
						struct {
 | 
				
			||||||
 | 
					#if OSMO_IS_BIG_ENDIAN
 | 
				
			||||||
 | 
						uint8_t spare:3,
 | 
				
			||||||
 | 
							iptis_present:1,
 | 
				
			||||||
 | 
							subflows:3,
 | 
				
			||||||
 | 
							chain:1;
 | 
				
			||||||
 | 
					#elif OSMO_IS_LITTLE_ENDIAN
 | 
				
			||||||
 | 
						uint8_t spare:3,
 | 
				
			||||||
 | 
							iptis_present:1,
 | 
				
			||||||
 | 
							subflows:3,
 | 
				
			||||||
 | 
							chain:1;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						} initialization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct {
 | 
				
			||||||
 | 
					#if OSMO_IS_BIG_ENDIAN
 | 
				
			||||||
 | 
						uint8_t error_distance:2,
 | 
				
			||||||
 | 
							error_cause:6;
 | 
				
			||||||
 | 
					#elif OSMO_IS_LITTLE_ENDIAN
 | 
				
			||||||
 | 
						uint8_t error_cause:6,
 | 
				
			||||||
 | 
							error_distance:2;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						} error_event;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern const struct value_string osmo_iuup_error_cause_names[];
 | 
				
			||||||
 | 
					static inline const char *osmo_iuup_error_cause_name(uint8_t val)
 | 
				
			||||||
 | 
					{ return get_value_string(osmo_iuup_error_cause_names, val); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct osmo_iuup_hdr_data {
 | 
				
			||||||
 | 
					#if OSMO_IS_BIG_ENDIAN
 | 
				
			||||||
 | 
						uint8_t pdu_type:4,
 | 
				
			||||||
 | 
							frame_nr:4;
 | 
				
			||||||
 | 
						uint8_t frame_good:2,
 | 
				
			||||||
 | 
							rfci:6;
 | 
				
			||||||
 | 
						uint8_t header_crc:6,
 | 
				
			||||||
 | 
							payload_crc_hi:2;
 | 
				
			||||||
 | 
						uint8_t payload_crc_lo;
 | 
				
			||||||
 | 
					#elif OSMO_IS_LITTLE_ENDIAN
 | 
				
			||||||
 | 
						uint8_t frame_nr:4,
 | 
				
			||||||
 | 
							pdu_type:4;
 | 
				
			||||||
 | 
						uint8_t rfci:6,
 | 
				
			||||||
 | 
							frame_good:2;
 | 
				
			||||||
 | 
						uint8_t payload_crc_hi:2,
 | 
				
			||||||
 | 
							header_crc:6;
 | 
				
			||||||
 | 
						uint8_t payload_crc_lo;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						uint8_t payload[0];
 | 
				
			||||||
 | 
					} __attribute__((packed));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int osmo_iuup_classify(bool log_errors,
 | 
				
			||||||
 | 
							       const char *log_label,
 | 
				
			||||||
 | 
							       struct msgb *pdu,
 | 
				
			||||||
 | 
							       struct osmo_iuup_hdr_ctrl **is_ctrl,
 | 
				
			||||||
 | 
							       struct osmo_iuup_hdr_data **is_data);
 | 
				
			||||||
 | 
					bool osmo_iuup_is_init(struct msgb *pdu);
 | 
				
			||||||
 | 
					void osmo_iuup_make_init_ack(struct msgb *ack);
 | 
				
			||||||
 | 
					void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len);
 | 
				
			||||||
@@ -5,3 +5,5 @@ void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
 | 
				
			|||||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param);
 | 
					int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param);
 | 
				
			||||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
 | 
					int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
 | 
				
			||||||
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
 | 
					int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
 | 
				
			||||||
 | 
					const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
 | 
				
			||||||
 | 
													const char *subtype_name, unsigned int match_nr);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,15 +23,27 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/msgb.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct sockaddr_in;
 | 
					struct sockaddr_in;
 | 
				
			||||||
struct mgcp_conn;
 | 
					struct mgcp_conn;
 | 
				
			||||||
 | 
					struct mgcp_conn_rtp;
 | 
				
			||||||
struct mgcp_endpoint;
 | 
					struct mgcp_endpoint;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Callback type for RTP dispatcher functions
 | 
					struct osmo_rtp_msg_ctx {
 | 
				
			||||||
   (e.g mgcp_dispatch_rtp_bridge_cb, see below) */
 | 
						int proto;
 | 
				
			||||||
typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in *addr,
 | 
						struct mgcp_conn_rtp *conn_src;
 | 
				
			||||||
				     char *buf, unsigned int buf_size,
 | 
						struct sockaddr_in *from_addr;
 | 
				
			||||||
				     struct mgcp_conn *conn);
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define OSMO_RTP_MSG_CTX(MSGB) (*(struct osmo_rtp_msg_ctx**)&((MSGB)->dst))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LOG_ENDP(endp, level, fmt, args...) \
 | 
				
			||||||
 | 
						LOGP(DRTP, level, "%x@ " fmt, ENDPOINT_NUMBER(endp), ## args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Callback type for RTP dispatcher functions (e.g mgcp_dispatch_rtp_bridge_cb, see below).
 | 
				
			||||||
 | 
					 * The OSMO_RTP_MSG_CTX() should be set appropriately on the msg. */
 | 
				
			||||||
 | 
					typedef int (*mgcp_dispatch_rtp_cb) (struct msgb *msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Callback type for endpoint specific cleanup actions. This function
 | 
					/* Callback type for endpoint specific cleanup actions. This function
 | 
				
			||||||
 * is automatically executed when a connection is freed (see mgcp_conn_free()
 | 
					 * is automatically executed when a connection is freed (see mgcp_conn_free()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,6 +37,13 @@
 | 
				
			|||||||
#define CONN_ID_BTS "0"
 | 
					#define CONN_ID_BTS "0"
 | 
				
			||||||
#define CONN_ID_NET "1"
 | 
					#define CONN_ID_NET "1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LOG_CONN(conn, level, fmt, args...) \
 | 
				
			||||||
 | 
						LOGP(DRTP, level, "(%d@ I:%s) " fmt, \
 | 
				
			||||||
 | 
						     ENDPOINT_NUMBER((conn)->endp), (conn)->id, ## args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LOG_CONN_RTP(conn_rtp, level, fmt, args...) \
 | 
				
			||||||
 | 
						LOG_CONN(conn_rtp->conn, level, fmt, ## args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum mgcp_trunk_type {
 | 
					enum mgcp_trunk_type {
 | 
				
			||||||
	MGCP_TRUNK_VIRTUAL,
 | 
						MGCP_TRUNK_VIRTUAL,
 | 
				
			||||||
	MGCP_TRUNK_E1,
 | 
						MGCP_TRUNK_E1,
 | 
				
			||||||
@@ -204,6 +211,8 @@ struct mgcp_conn_rtp {
 | 
				
			|||||||
	} osmux;
 | 
						} osmux;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct rate_ctr_group *rate_ctr_group;
 | 
						struct rate_ctr_group *rate_ctr_group;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct osmo_iuup_cn *iuup;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
 | 
					/*! Connection type, specifies which member of the union "u" in mgcp_conn
 | 
				
			||||||
@@ -266,11 +275,10 @@ struct mgcp_parse_data {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
					int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
				
			||||||
	      char *buf, int rc, struct mgcp_conn_rtp *conn_src,
 | 
						      struct msgb *msg, struct mgcp_conn_rtp *conn_src,
 | 
				
			||||||
	      struct mgcp_conn_rtp *conn_dst);
 | 
						      struct mgcp_conn_rtp *conn_dst);
 | 
				
			||||||
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
 | 
					int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
 | 
				
			||||||
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
 | 
					int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg);
 | 
				
			||||||
				unsigned int buf_size, struct mgcp_conn *conn);
 | 
					 | 
				
			||||||
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
 | 
					void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
 | 
				
			||||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
 | 
					int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
 | 
				
			||||||
			   struct mgcp_conn_rtp *conn);
 | 
								   struct mgcp_conn_rtp *conn);
 | 
				
			||||||
@@ -350,3 +358,8 @@ LOGP(cat, level, "endpoint:0x%x " fmt, \
 | 
				
			|||||||
LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
 | 
					LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
 | 
				
			||||||
         (conn)->id, \
 | 
					         (conn)->id, \
 | 
				
			||||||
         ## args)
 | 
					         ## args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void mgcp_patch_and_count(struct mgcp_endpoint *endp,
 | 
				
			||||||
 | 
								  struct mgcp_rtp_state *state,
 | 
				
			||||||
 | 
								  struct mgcp_rtp_end *rtp_end,
 | 
				
			||||||
 | 
								  struct sockaddr_in *addr, struct msgb *msg);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -337,48 +337,34 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	unsigned int pt;
 | 
						unsigned int pt;
 | 
				
			||||||
	char codec_resp[64];
 | 
						char codec_resp[64];
 | 
				
			||||||
	unsigned int codec;
 | 
						enum mgcp_codecs codec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define A_PTIME "a=ptime:"
 | 
				
			||||||
 | 
					#define A_RTPMAP "a=rtpmap:"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (strstr(line, "ptime")) {
 | 
						if (osmo_str_startswith(line, A_PTIME)) {
 | 
				
			||||||
		if (sscanf(line, "a=ptime:%u", &r->ptime) != 1)
 | 
							if (sscanf(line, A_PTIME "%u", &r->ptime) != 1) {
 | 
				
			||||||
			goto response_parse_failure_ptime;
 | 
								LOGP(DLMGCP, LOGL_ERROR,
 | 
				
			||||||
	} else if (strstr(line, "rtpmap")) {
 | 
								     "Failed to parse SDP parameter, invalid ptime (%s)\n", line);
 | 
				
			||||||
		if (sscanf(line, "a=rtpmap:%d %63s", &pt, codec_resp) == 2) {
 | 
								return -EINVAL;
 | 
				
			||||||
			/* The MGW may assign an own payload type in the
 | 
							}
 | 
				
			||||||
			 * response if the choosen codec falls into the IANA
 | 
						} else if (osmo_str_startswith(line, A_RTPMAP)) {
 | 
				
			||||||
			 * assigned dynamic payload type range (96-127).
 | 
							if (sscanf(line, A_RTPMAP "%d %63s", &pt, codec_resp) != 2) {
 | 
				
			||||||
			 * Normally the MGW should obey the 3gpp payload type
 | 
								LOGP(DLMGCP, LOGL_ERROR,
 | 
				
			||||||
			 * assignments, which are fixed, so we likely wont see
 | 
								     "Failed to parse SDP parameter, invalid rtpmap: %s\n", osmo_quote_str(line, -1));
 | 
				
			||||||
			 * anything unexpected here. In order to be sure that
 | 
								return -EINVAL;
 | 
				
			||||||
			 * we will now check the codec string and if the result
 | 
							}
 | 
				
			||||||
			 * does not match to what is IANA / 3gpp assigned, we
 | 
							if (r->ptmap_len >= ARRAY_SIZE(r->ptmap)) {
 | 
				
			||||||
			 * will create an entry in the ptmap table so we can
 | 
								LOGP(DLMGCP, LOGL_ERROR, "No more space in ptmap array (len=%u)\n", r->ptmap_len);
 | 
				
			||||||
			 * lookup later what has been assigned. */
 | 
								return -ENOSPC;
 | 
				
			||||||
			codec = map_str_to_codec(codec_resp);
 | 
							}
 | 
				
			||||||
			if (codec != pt) {
 | 
							codec = map_str_to_codec(codec_resp);
 | 
				
			||||||
				if (r->ptmap_len < ARRAY_SIZE(r->ptmap)) {
 | 
							r->ptmap[r->ptmap_len].pt = pt;
 | 
				
			||||||
					r->ptmap[r->ptmap_len].pt = pt;
 | 
							r->ptmap[r->ptmap_len].codec = codec;
 | 
				
			||||||
					r->ptmap[r->ptmap_len].codec = codec;
 | 
							r->ptmap_len++;
 | 
				
			||||||
					r->ptmap_len++;
 | 
					 | 
				
			||||||
				} else
 | 
					 | 
				
			||||||
					goto response_parse_failure_rtpmap;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		} else
 | 
					 | 
				
			||||||
			goto response_parse_failure_rtpmap;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					 | 
				
			||||||
response_parse_failure_ptime:
 | 
					 | 
				
			||||||
	LOGP(DLMGCP, LOGL_ERROR,
 | 
					 | 
				
			||||||
	     "Failed to parse SDP parameter, invalid ptime (%s)\n", line);
 | 
					 | 
				
			||||||
	return -EINVAL;
 | 
					 | 
				
			||||||
response_parse_failure_rtpmap:
 | 
					 | 
				
			||||||
	LOGP(DLMGCP, LOGL_ERROR,
 | 
					 | 
				
			||||||
	     "Failed to parse SDP parameter, invalid rtpmap (%s)\n", line);
 | 
					 | 
				
			||||||
	return -EINVAL;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Parse a line like "c=IN IP4 10.11.12.13" */
 | 
					/* Parse a line like "c=IN IP4 10.11.12.13" */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,4 +40,6 @@ libosmo_mgcp_a_SOURCES = \
 | 
				
			|||||||
	mgcp_conn.c \
 | 
						mgcp_conn.c \
 | 
				
			||||||
	mgcp_stat.c \
 | 
						mgcp_stat.c \
 | 
				
			||||||
	mgcp_endp.c \
 | 
						mgcp_endp.c \
 | 
				
			||||||
 | 
						iuup_protocol.c \
 | 
				
			||||||
 | 
						iuup_cn_node.c \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										211
									
								
								src/libosmo-mgcp/iuup_cn_node.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								src/libosmo-mgcp/iuup_cn_node.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,211 @@
 | 
				
			|||||||
 | 
					/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
 | 
				
			||||||
 | 
					/* IuUP Core Network side protocol handling, minimal implementation */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Neels Hofmeyr <neels@hofmeyr.de>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <talloc.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <arpa/inet.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/utils.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/logging.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/msgb.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/netif/rtp.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/mgcp/iuup_cn_node.h>
 | 
				
			||||||
 | 
					#include <osmocom/mgcp/iuup_protocol.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/mgcp/debug.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LOG_IUUP_CN(cn, level, fmt, args...) \
 | 
				
			||||||
 | 
							LOGP(DIUUP, level, "(%s) " fmt, (cn)->name, ## args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
 | 
				
			||||||
 | 
									       const char *name_fmt, ...)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						va_list ap;
 | 
				
			||||||
 | 
						struct osmo_iuup_cn *cn = talloc_zero(ctx, struct osmo_iuup_cn);
 | 
				
			||||||
 | 
						OSMO_ASSERT(cn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cn->cfg = *cfg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!name_fmt)
 | 
				
			||||||
 | 
							name_fmt = "-";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						va_start(ap, name_fmt);
 | 
				
			||||||
 | 
						cn->name = talloc_vasprintf(cn, name_fmt, ap);
 | 
				
			||||||
 | 
						va_end(ap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGP(DIUUP, LOGL_INFO, "(%s) Initializing IuUP node\n", cn->name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!osmo_identifier_valid(cn->name)) {
 | 
				
			||||||
 | 
							LOGP(DIUUP, LOGL_ERROR, "Attempting to set illegal id for IuUP CN instance: %s\n",
 | 
				
			||||||
 | 
							     osmo_quote_str(cn->name, -1));
 | 
				
			||||||
 | 
							talloc_free(cn);
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return cn;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void osmo_iuup_cn_free(struct osmo_iuup_cn *cn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						talloc_free(cn);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int rx_data(struct osmo_iuup_cn *cn, struct msgb *pdu,
 | 
				
			||||||
 | 
							   struct osmo_iuup_hdr_data *hdr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Remove the IuUP bit from the middle of the buffer by writing the RTP header forward. */
 | 
				
			||||||
 | 
						/* And append AMR 12.2k header "0xf03c". - AD HOC fix */
 | 
				
			||||||
 | 
						unsigned int pre_hdr_len = ((uint8_t*)hdr) - pdu->data;
 | 
				
			||||||
 | 
						memmove(pdu->data + sizeof(*hdr) - 2, pdu->data, pre_hdr_len);
 | 
				
			||||||
 | 
						((uint8_t*)hdr)[2] = 0xf0;
 | 
				
			||||||
 | 
						((uint8_t*)hdr)[3] = 0x3c;
 | 
				
			||||||
 | 
						msgb_pull(pdu, sizeof(*hdr) - 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP stripping IuUP header from RTP data\n", cn->name);
 | 
				
			||||||
 | 
						cn->cfg.rx_payload(pdu, cn->cfg.node_priv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int tx_init_ack(struct osmo_iuup_cn *cn, struct msgb *src_pdu)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Send Initialization Ack PDU back to the sender */
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						struct msgb *ack = msgb_alloc(4096, "IuUP Initialization Ack");
 | 
				
			||||||
 | 
						OSMO_ASSERT(ack);
 | 
				
			||||||
 | 
						ack->dst = src_pdu->dst;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Just copy the RTP header that was sent... TODO: tweak some RTP values?? */
 | 
				
			||||||
 | 
						memcpy(msgb_put(ack, sizeof(struct rtp_hdr)), src_pdu->data, sizeof(struct rtp_hdr));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_iuup_make_init_ack(ack);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGP(DIUUP, LOGL_DEBUG, "(%s) Sending Initialization ACK %p\n", cn->name, cn->cfg.node_priv);
 | 
				
			||||||
 | 
						rc = cn->cfg.tx_msg(ack, cn->cfg.node_priv);
 | 
				
			||||||
 | 
						msgb_free(ack);
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int rx_control(struct osmo_iuup_cn *cn, struct msgb *pdu,
 | 
				
			||||||
 | 
							      struct osmo_iuup_hdr_ctrl *hdr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (hdr->procedure) {
 | 
				
			||||||
 | 
						case OSMO_IUUP_PROC_INITIALIZATION:
 | 
				
			||||||
 | 
							switch (hdr->ack_nack) {
 | 
				
			||||||
 | 
							case OSMO_IUUP_ACKNACK_PROCEDURE:
 | 
				
			||||||
 | 
								LOGP(DIUUP, LOGL_INFO, "(%s) Rx IuUP Initialization, sending ACK\n", cn->name);
 | 
				
			||||||
 | 
								cn->rtp_payload_type = ((struct rtp_hdr*)pdu->data)->payload_type;
 | 
				
			||||||
 | 
								return tx_init_ack(cn, pdu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								LOGP(DIUUP, LOGL_DEBUG, "(%s) Rx IuUP Initialization, unhandled ack_nack = %d\n",
 | 
				
			||||||
 | 
								     cn->name, hdr->ack_nack);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							/* Continue to log "unexpected procedure" below. */
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case OSMO_IUUP_PROC_ERROR_EVENT:
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								union osmo_iuup_hdr_ctrl_payload *p = (void*)hdr->payload;
 | 
				
			||||||
 | 
								LOGP(DIUUP, LOGL_ERROR,
 | 
				
			||||||
 | 
								     "(%s) Rx IuUP Error Event: distance=%u, cause=%u=\"%s\"\n",
 | 
				
			||||||
 | 
								     cn->name, p->error_event.error_distance, p->error_event.error_cause,
 | 
				
			||||||
 | 
								     osmo_iuup_error_cause_name(p->error_event.error_cause));
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						LOG_IUUP_CN(cn, LOGL_ERROR,
 | 
				
			||||||
 | 
							    "Rx control PDU with unexpected procedure: 0x%x acknack=0x%x\n",
 | 
				
			||||||
 | 
							    hdr->procedure, hdr->ack_nack);
 | 
				
			||||||
 | 
						return -EINVAL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Feed a received PDU to the IuUP CN node. This function takes ownership of the msgb, it must not be
 | 
				
			||||||
 | 
					 * freed by the caller. */
 | 
				
			||||||
 | 
					int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct osmo_iuup_hdr_ctrl *is_ctrl;
 | 
				
			||||||
 | 
						struct osmo_iuup_hdr_data *is_data;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = osmo_iuup_classify(true, cn->name, pdu, &is_ctrl, &is_data);
 | 
				
			||||||
 | 
						if (rc)
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (is_ctrl)
 | 
				
			||||||
 | 
							return rx_control(cn, pdu, is_ctrl);
 | 
				
			||||||
 | 
						if (is_data)
 | 
				
			||||||
 | 
							return rx_data(cn, pdu, is_data);
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint8_t next_frame_nr(struct osmo_iuup_cn *cn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint8_t frame_nr = cn->next_frame_nr;
 | 
				
			||||||
 | 
						cn->next_frame_nr = (frame_nr + 1) & 0x0f;
 | 
				
			||||||
 | 
						return frame_nr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Send this RTP packet to the IuUP peer: add IuUP header and call the tx_msg() to transmit the resulting
 | 
				
			||||||
 | 
					 * message to the IuUP peer.
 | 
				
			||||||
 | 
					 * Returns 0 on success, negative on error. */
 | 
				
			||||||
 | 
					int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct rtp_hdr *rtp_was, *rtp;
 | 
				
			||||||
 | 
						struct osmo_iuup_hdr_data *iuup_hdr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Splice an IuUP header in between RTP header and payload data */
 | 
				
			||||||
 | 
						rtp_was = (void*)pdu->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* copy the RTP header part backwards by the size needed for the IuUP header */
 | 
				
			||||||
 | 
						/* also strips 2 bytes from the front of RTP payload - AMR header - AD HOC fix */
 | 
				
			||||||
 | 
						rtp = (void*)msgb_push(pdu, sizeof(*iuup_hdr) - 2);
 | 
				
			||||||
 | 
						memmove(rtp, rtp_was, sizeof(*rtp));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* The IuUP side negotiated a payload type number to use during Initialization. The RTP packet going to the IuUP
 | 
				
			||||||
 | 
						 * peer should reflect this payload_type. This should already have happened in mgcp_patch_pt(), but can't hurt
 | 
				
			||||||
 | 
						 * to patch over it again. */
 | 
				
			||||||
 | 
						rtp->payload_type = cn->rtp_payload_type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						iuup_hdr = (void*)rtp->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*iuup_hdr = (struct osmo_iuup_hdr_data){
 | 
				
			||||||
 | 
							.pdu_type = OSMO_IUUP_PDU_DATA_WITH_CRC,
 | 
				
			||||||
 | 
							.frame_nr = next_frame_nr(cn),
 | 
				
			||||||
 | 
							.frame_good = OSMO_IUUP_FRAME_GOOD,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_iuup_set_checksums((uint8_t*)iuup_hdr, pdu->tail - (uint8_t*)iuup_hdr);
 | 
				
			||||||
 | 
						LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP inserting IuUP header in RTP data (frame nr %u)\n",
 | 
				
			||||||
 | 
						     cn->name, iuup_hdr->frame_nr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return cn->cfg.tx_msg(pdu, cn->cfg.node_priv);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										286
									
								
								src/libosmo-mgcp/iuup_protocol.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								src/libosmo-mgcp/iuup_protocol.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,286 @@
 | 
				
			|||||||
 | 
					/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
 | 
				
			||||||
 | 
					/* IuUP Core Network side protocol, minimal implementation */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Neels Hofmeyr <neels@hofmeyr.de>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					#include <osmocom/mgcp/iuup_protocol.h>
 | 
				
			||||||
 | 
					#include <osmocom/mgcp/debug.h>
 | 
				
			||||||
 | 
					#include <osmocom/netif/rtp.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Calculating two bytes of CRC is ok to do by a loop */
 | 
				
			||||||
 | 
					static uint8_t header_crc6(const uint8_t *hdr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int bit;
 | 
				
			||||||
 | 
					    /* Polynomial: D^6 + D^5 + D^3 + D^2 + D^1 + 1
 | 
				
			||||||
 | 
					     * that's 1101111 or 0x6f;
 | 
				
			||||||
 | 
					     * align its lowest bit with a uint16_t's highest bit: */
 | 
				
			||||||
 | 
					    uint32_t polynomial = 0x6f << 15; // 00110111 10000000 00000000
 | 
				
			||||||
 | 
					    uint32_t remainder = ( ((uint32_t)hdr[0]) << 8 | hdr[1] ) << 6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (bit = 15; bit >= 0; bit--)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (remainder & (0x40 << bit))
 | 
				
			||||||
 | 
					            remainder ^= polynomial;
 | 
				
			||||||
 | 
					        polynomial >>= 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return remainder;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Charles Michael Heard's CRC-10 code, from
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *      http://web.archive.org/web/20061005231950/http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/crc10.html
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * with the CRC table initialized with values computed by
 | 
				
			||||||
 | 
					 * his "gen_byte_crc10_table()" routine, rather than by calling that
 | 
				
			||||||
 | 
					 * routine at run time, and with various data type cleanups.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static const uint16_t byte_crc10_table[256] = {
 | 
				
			||||||
 | 
						0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff,
 | 
				
			||||||
 | 
						0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe,
 | 
				
			||||||
 | 
						0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce,
 | 
				
			||||||
 | 
						0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf,
 | 
				
			||||||
 | 
						0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d,
 | 
				
			||||||
 | 
						0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c,
 | 
				
			||||||
 | 
						0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac,
 | 
				
			||||||
 | 
						0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad,
 | 
				
			||||||
 | 
						0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b,
 | 
				
			||||||
 | 
						0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a,
 | 
				
			||||||
 | 
						0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a,
 | 
				
			||||||
 | 
						0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b,
 | 
				
			||||||
 | 
						0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259,
 | 
				
			||||||
 | 
						0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158,
 | 
				
			||||||
 | 
						0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268,
 | 
				
			||||||
 | 
						0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169,
 | 
				
			||||||
 | 
						0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377,
 | 
				
			||||||
 | 
						0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076,
 | 
				
			||||||
 | 
						0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346,
 | 
				
			||||||
 | 
						0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047,
 | 
				
			||||||
 | 
						0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315,
 | 
				
			||||||
 | 
						0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014,
 | 
				
			||||||
 | 
						0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324,
 | 
				
			||||||
 | 
						0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025,
 | 
				
			||||||
 | 
						0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3,
 | 
				
			||||||
 | 
						0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2,
 | 
				
			||||||
 | 
						0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382,
 | 
				
			||||||
 | 
						0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083,
 | 
				
			||||||
 | 
						0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1,
 | 
				
			||||||
 | 
						0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0,
 | 
				
			||||||
 | 
						0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0,
 | 
				
			||||||
 | 
						0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint16_t crc10(uint16_t crc10_accum, const uint8_t *payload, unsigned int payload_len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < payload_len; i++) {
 | 
				
			||||||
 | 
							crc10_accum = ((crc10_accum << 8) & 0x300)
 | 
				
			||||||
 | 
								^ byte_crc10_table[(crc10_accum >> 2) & 0xff]
 | 
				
			||||||
 | 
								^ payload[i];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return crc10_accum;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* When a payload of a multiple of bytes has run through, we need to still feed 10 bits of zeros into the
 | 
				
			||||||
 | 
					 * CRC10 to get the payload's checksum result that we can send to a peer. That can't be done with above
 | 
				
			||||||
 | 
					 * table, because it acts as if full 16 bits are fed. This stops after 10 bits. */
 | 
				
			||||||
 | 
					static uint16_t crc10_remainder(uint16_t crc10_accum)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int bit;
 | 
				
			||||||
 | 
					    /* Polynomial: D^10 + D^9 + D^5 + D^4 + D^1 + 1
 | 
				
			||||||
 | 
					     * that's 11000110011 or 0x633;
 | 
				
			||||||
 | 
					     * align its lowest bit with a 10bit value's highest bit: */
 | 
				
			||||||
 | 
					    uint32_t polynomial = 0x633 << 9; // 1100 01100110 00000000
 | 
				
			||||||
 | 
					    uint32_t remainder = ((uint32_t)crc10_accum) << 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Run on 10 bits */
 | 
				
			||||||
 | 
					    for (bit = 9; bit >= 0; bit--)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (remainder & ((1 << 10) << bit))
 | 
				
			||||||
 | 
					            remainder ^= polynomial;
 | 
				
			||||||
 | 
					        polynomial >>= 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return remainder & 0x3ff;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint16_t payload_crc10(const uint8_t *payload, unsigned int payload_len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint16_t crc10_accum = crc10(0, payload, payload_len);
 | 
				
			||||||
 | 
						return crc10_remainder(crc10_accum);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Given an IuUP PDU data block, write the correct header and payload CRC checksums at the right places.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* For both data and ctrl, the checksums and payload are at the same offset */
 | 
				
			||||||
 | 
						struct osmo_iuup_hdr_data *hdr = (void*)iuup_header_and_payload;
 | 
				
			||||||
 | 
						uint16_t crc;
 | 
				
			||||||
 | 
						unsigned int payload_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hdr->header_crc = header_crc6(iuup_header_and_payload);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						payload_len = iuup_header_and_payload + header_and_payload_len - hdr->payload;
 | 
				
			||||||
 | 
						crc = payload_crc10(hdr->payload, payload_len);
 | 
				
			||||||
 | 
						hdr->payload_crc_hi = (crc >> 8) & 0x3;
 | 
				
			||||||
 | 
						hdr->payload_crc_lo = crc & 0xff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Validate minimum message sizes, IuUP PDU type, header- and payload checksums. If it is a Control
 | 
				
			||||||
 | 
					 * Procedure PDU, return the header position in is_ctrl, if it is a Data PDU, return the header position
 | 
				
			||||||
 | 
					 * in is_data. If log_errors is true, log on DIUUP with the given log label for context. Return NULL in
 | 
				
			||||||
 | 
					 * both is_ctrl and is_data, and return a negative error code if the PDU could not be identified as a
 | 
				
			||||||
 | 
					 * valid RTP PDU containing an IuUP part. */
 | 
				
			||||||
 | 
					int osmo_iuup_classify(bool log_errors,
 | 
				
			||||||
 | 
							       const char *log_label,
 | 
				
			||||||
 | 
							       struct msgb *pdu,
 | 
				
			||||||
 | 
							       struct osmo_iuup_hdr_ctrl **is_ctrl,
 | 
				
			||||||
 | 
							       struct osmo_iuup_hdr_data **is_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct rtp_hdr *rtp = (void*)pdu->data;
 | 
				
			||||||
 | 
						struct osmo_iuup_hdr_ctrl *hdr = (void*)rtp->data;
 | 
				
			||||||
 | 
						unsigned int payload_len;
 | 
				
			||||||
 | 
						uint16_t crc_calculated;
 | 
				
			||||||
 | 
						uint16_t crc_from_peer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ERR(fmt, args...) do { \
 | 
				
			||||||
 | 
								if (log_errors) \
 | 
				
			||||||
 | 
									LOGP(DIUUP, LOGL_ERROR, "(%s) " fmt, log_label? : "-", ## args); \
 | 
				
			||||||
 | 
								return -EINVAL; \
 | 
				
			||||||
 | 
							} while (0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (is_ctrl)
 | 
				
			||||||
 | 
							*is_ctrl = NULL;
 | 
				
			||||||
 | 
						if (is_data)
 | 
				
			||||||
 | 
							*is_data = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* We need at least a header of 4 bytes. The osmo_iuup_hdr_ctrl already includes a byte of
 | 
				
			||||||
 | 
						 * payload, so use osmo_iuup_hdr_data to check the minimum here. */
 | 
				
			||||||
 | 
						if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_data)))
 | 
				
			||||||
 | 
							ERR("IuUP PDU too short: %u\n", pdu->len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Let's not validate checksums if the header type isn't sane */
 | 
				
			||||||
 | 
						switch (hdr->pdu_type) {
 | 
				
			||||||
 | 
						case OSMO_IUUP_PDU_DATA_WITH_CRC:
 | 
				
			||||||
 | 
							/* If the caller isn't interested in data PDUs, cut short here. */
 | 
				
			||||||
 | 
							if (!is_data)
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
 | 
				
			||||||
 | 
							/* If the caller isn't interested in control PDUs, cut short here. */
 | 
				
			||||||
 | 
							if (!is_ctrl)
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_ctrl)))
 | 
				
			||||||
 | 
								ERR("IuUP control PDU too short: %u\n", pdu->len);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* For both data and ctrl, the checksums and payload are at the same offset */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						crc_calculated = header_crc6((uint8_t*)hdr);
 | 
				
			||||||
 | 
						if (crc_calculated != hdr->header_crc)
 | 
				
			||||||
 | 
							ERR("IuUP PDU with invalid header CRC (peer sent 0x%x, calculated 0x%x)\n",
 | 
				
			||||||
 | 
							    hdr->header_crc, crc_calculated);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						payload_len = pdu->tail - hdr->payload;
 | 
				
			||||||
 | 
						crc_calculated = payload_crc10(hdr->payload, payload_len);
 | 
				
			||||||
 | 
						crc_from_peer = (((uint16_t)hdr->payload_crc_hi) << 8) | hdr->payload_crc_lo;
 | 
				
			||||||
 | 
						if (crc_from_peer != crc_calculated)
 | 
				
			||||||
 | 
							ERR("IuUP PDU with invalid payload CRC (peer sent 0x%x, calculated 0x%x)\n",
 | 
				
			||||||
 | 
							    crc_from_peer, crc_calculated);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (hdr->pdu_type) {
 | 
				
			||||||
 | 
						case OSMO_IUUP_PDU_DATA_WITH_CRC:
 | 
				
			||||||
 | 
							if (is_data)
 | 
				
			||||||
 | 
								*is_data = (void*)hdr;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
 | 
				
			||||||
 | 
							if (is_ctrl)
 | 
				
			||||||
 | 
								*is_ctrl = hdr;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#undef ERR
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Return true if this RTP packet contains an IuUP Initialization header (detect IuUP peer). */
 | 
				
			||||||
 | 
					bool osmo_iuup_is_init(struct msgb *pdu)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					 	struct osmo_iuup_hdr_ctrl *is_ctrl;
 | 
				
			||||||
 | 
						osmo_iuup_classify(false, NULL, pdu, &is_ctrl, NULL);
 | 
				
			||||||
 | 
						return is_ctrl
 | 
				
			||||||
 | 
							&& is_ctrl->procedure == OSMO_IUUP_PROC_INITIALIZATION
 | 
				
			||||||
 | 
							&& is_ctrl->ack_nack == OSMO_IUUP_ACKNACK_PROCEDURE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Append an IuUP Initialization ACK message */
 | 
				
			||||||
 | 
					void osmo_iuup_make_init_ack(struct msgb *ack)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Send Initialization Ack PDU back to the sender */
 | 
				
			||||||
 | 
					 	struct osmo_iuup_hdr_ctrl *hdr;
 | 
				
			||||||
 | 
						OSMO_ASSERT(ack);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hdr = (void*)msgb_put(ack, sizeof(*hdr));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*hdr = (struct osmo_iuup_hdr_ctrl){
 | 
				
			||||||
 | 
							.pdu_type = OSMO_IUUP_PDU_CONTROL_PROCEDURE,
 | 
				
			||||||
 | 
							.ack_nack = OSMO_IUUP_ACKNACK_ACK,
 | 
				
			||||||
 | 
							.procedure = OSMO_IUUP_PROC_INITIALIZATION,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_iuup_set_checksums((uint8_t*)hdr, sizeof(*hdr));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const struct value_string osmo_iuup_error_cause_names[] = {
 | 
				
			||||||
 | 
						{ 0, "CRC error of frame header" },
 | 
				
			||||||
 | 
						{ 1, "CRC error of frame payload" },
 | 
				
			||||||
 | 
						{ 2, "Unexpected frame number" },
 | 
				
			||||||
 | 
						{ 3, "Frame loss" },
 | 
				
			||||||
 | 
						{ 4, "PDU type unknown" },
 | 
				
			||||||
 | 
						{ 5, "Unknown procedure" },
 | 
				
			||||||
 | 
						{ 6, "Unknown reserved value" },
 | 
				
			||||||
 | 
						{ 7, "Unknown field" },
 | 
				
			||||||
 | 
						{ 8, "Frame too short" },
 | 
				
			||||||
 | 
						{ 9, "Missing fields" },
 | 
				
			||||||
 | 
						{ 16, "Unexpected PDU type" },
 | 
				
			||||||
 | 
						{ 17, "spare" },
 | 
				
			||||||
 | 
						{ 18, "Unexpected procedure" },
 | 
				
			||||||
 | 
						{ 19, "Unexpected RFCI" },
 | 
				
			||||||
 | 
						{ 20, "Unexpected value" },
 | 
				
			||||||
 | 
						{ 42, "Initialisation failure" },
 | 
				
			||||||
 | 
						{ 43, "Initialisation failure (network error, timer expiry)" },
 | 
				
			||||||
 | 
						{ 44, "Initialisation failure (Iu UP function error, repeated NACK)" },
 | 
				
			||||||
 | 
						{ 45, "Rate control failure" },
 | 
				
			||||||
 | 
						{ 46, "Error event failure" },
 | 
				
			||||||
 | 
						{ 47, "Time Alignment not supported" },
 | 
				
			||||||
 | 
						{ 48, "Requested Time Alignment not possible" },
 | 
				
			||||||
 | 
						{ 49, "Iu UP Mode version not supported" },
 | 
				
			||||||
 | 
						{}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -76,36 +76,61 @@ void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Initalize or reset codec information with default data. */
 | 
					/* Initalize or reset codec information with default data. */
 | 
				
			||||||
void codec_init(struct mgcp_rtp_codec *codec)
 | 
					static void codec_init(struct mgcp_rtp_codec *codec)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						*codec = (struct mgcp_rtp_codec){
 | 
				
			||||||
 | 
							.payload_type = -1,
 | 
				
			||||||
 | 
							.frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM,
 | 
				
			||||||
 | 
							.frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN,
 | 
				
			||||||
 | 
							.rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE,
 | 
				
			||||||
 | 
							.channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void codec_free(struct mgcp_rtp_codec *codec)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (codec->subtype_name)
 | 
						if (codec->subtype_name)
 | 
				
			||||||
		talloc_free(codec->subtype_name);
 | 
							talloc_free(codec->subtype_name);
 | 
				
			||||||
	if (codec->audio_name)
 | 
						if (codec->audio_name)
 | 
				
			||||||
		talloc_free(codec->audio_name);
 | 
							talloc_free(codec->audio_name);
 | 
				
			||||||
	memset(codec, 0, sizeof(*codec));
 | 
						*codec = (struct mgcp_rtp_codec){};
 | 
				
			||||||
	codec->payload_type = -1;
 | 
					 | 
				
			||||||
	codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
 | 
					 | 
				
			||||||
	codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
 | 
					 | 
				
			||||||
	codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
 | 
					 | 
				
			||||||
	codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! Initalize or reset codec information with default data.
 | 
					/*! Initalize or reset codec information with default data.
 | 
				
			||||||
 *  \param[out] conn related rtp-connection. */
 | 
					 *  \param[out] conn related rtp-connection. */
 | 
				
			||||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
 | 
					void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	memset(conn->end.codecs, 0, sizeof(conn->end.codecs));
 | 
						int i;
 | 
				
			||||||
 | 
						for (i = 0; i < conn->end.codecs_assigned; i++)
 | 
				
			||||||
 | 
							codec_free(&conn->end.codecs[i]);
 | 
				
			||||||
	conn->end.codecs_assigned = 0;
 | 
						conn->end.codecs_assigned = 0;
 | 
				
			||||||
	conn->end.codec = NULL;
 | 
						conn->end.codec = NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Set members of struct mgcp_rtp_codec, extrapolate in missing information. Param audio_name is expected in uppercase. */
 | 
					/*! Add codec configuration depending on payload type and/or codec name. This
 | 
				
			||||||
static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type, const char *audio_name,
 | 
					 *  function uses the input parameters to extrapolate the full codec information.
 | 
				
			||||||
		     unsigned int pt_offset, const struct mgcp_codec_param *param)
 | 
					 *  \param[out] codec configuration (caller provided memory).
 | 
				
			||||||
 | 
					 *  \param[out] conn related rtp-connection.
 | 
				
			||||||
 | 
					 *  \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
 | 
				
			||||||
 | 
					 *  \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
 | 
				
			||||||
 | 
					 *  \param[in] param optional codec parameters (set to NULL when unused).
 | 
				
			||||||
 | 
					 *  \returns 0 on success, -EINVAL on failure. */
 | 
				
			||||||
 | 
					int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int rate;
 | 
						int rate;
 | 
				
			||||||
	int channels;
 | 
						int channels;
 | 
				
			||||||
	char audio_codec[64];
 | 
						char audio_codec[64];
 | 
				
			||||||
 | 
						struct mgcp_rtp_codec *codec;
 | 
				
			||||||
 | 
						unsigned int pt_offset = conn->end.codecs_assigned;
 | 
				
			||||||
 | 
						void *ctx = conn->conn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* The amount of codecs we can store is limited, make sure we do not
 | 
				
			||||||
 | 
						 * overrun this limit. */
 | 
				
			||||||
 | 
						if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* First unused entry */
 | 
				
			||||||
 | 
						codec = &conn->end.codecs[conn->end.codecs_assigned];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Initalize the codec struct with some default data to begin with */
 | 
						/* Initalize the codec struct with some default data to begin with */
 | 
				
			||||||
	codec_init(codec);
 | 
						codec_init(codec);
 | 
				
			||||||
@@ -113,12 +138,13 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type,
 | 
				
			|||||||
	if (payload_type != PTYPE_UNDEFINED) {
 | 
						if (payload_type != PTYPE_UNDEFINED) {
 | 
				
			||||||
		/* Make sure we do not get any reserved or undefined type numbers */
 | 
							/* Make sure we do not get any reserved or undefined type numbers */
 | 
				
			||||||
		/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
 | 
							/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
 | 
				
			||||||
		if (payload_type == 1 || payload_type == 2 || payload_type == 19)
 | 
							if ((payload_type == 1 || payload_type == 2 || payload_type == 19)
 | 
				
			||||||
			goto error;
 | 
							    || (payload_type >= 72 && payload_type <= 76)
 | 
				
			||||||
		if (payload_type >= 72 && payload_type <= 76)
 | 
							    || (payload_type >= 127)) {
 | 
				
			||||||
			goto error;
 | 
								LOGP(DLMGCP, LOGL_ERROR, "Cannot add codec, payload type number %d is reserved\n",
 | 
				
			||||||
		if (payload_type >= 127)
 | 
								     payload_type);
 | 
				
			||||||
			goto error;
 | 
								goto error;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		codec->payload_type = payload_type;
 | 
							codec->payload_type = payload_type;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -144,6 +170,8 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type,
 | 
				
			|||||||
			/* The given payload type is not known to us, or it
 | 
								/* The given payload type is not known to us, or it
 | 
				
			||||||
			 * it is a dynamic payload type for which we do not
 | 
								 * it is a dynamic payload type for which we do not
 | 
				
			||||||
			 * know the audio name. We must give up here */
 | 
								 * know the audio name. We must give up here */
 | 
				
			||||||
 | 
								LOGP(DLMGCP, LOGL_ERROR, "No audio codec name given, and payload type %d unknown\n",
 | 
				
			||||||
 | 
								     payload_type);
 | 
				
			||||||
			goto error;
 | 
								goto error;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -151,16 +179,23 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type,
 | 
				
			|||||||
	/* Now we extract the codec subtype name, rate and channels. The latter
 | 
						/* Now we extract the codec subtype name, rate and channels. The latter
 | 
				
			||||||
	 * two are optional. If they are not present we use the safe defaults
 | 
						 * two are optional. If they are not present we use the safe defaults
 | 
				
			||||||
	 * above. */
 | 
						 * above. */
 | 
				
			||||||
	if (strlen(audio_name) > sizeof(audio_codec))
 | 
						if (strlen(audio_name) > sizeof(audio_codec)) {
 | 
				
			||||||
 | 
							LOGP(DLMGCP, LOGL_ERROR, "Audio codec too long: %s\n", osmo_quote_str(audio_name, -1));
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
 | 
						channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
 | 
				
			||||||
	rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
 | 
						rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
 | 
				
			||||||
	if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1)
 | 
						if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1) {
 | 
				
			||||||
 | 
							LOGP(DLMGCP, LOGL_ERROR, "Invalid audio codec: %s\n", osmo_quote_str(audio_name, -1));
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Note: We only accept configurations with one audio channel! */
 | 
						/* Note: We only accept configurations with one audio channel! */
 | 
				
			||||||
	if (channels != 1)
 | 
						if (channels != 1) {
 | 
				
			||||||
 | 
							LOGP(DLMGCP, LOGL_ERROR, "Cannot handle audio codec with more than one channel: %s\n",
 | 
				
			||||||
 | 
							     osmo_quote_str(audio_name, -1));
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	codec->rate = rate;
 | 
						codec->rate = rate;
 | 
				
			||||||
	codec->channels = channels;
 | 
						codec->channels = channels;
 | 
				
			||||||
@@ -178,6 +213,7 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	/* Derive the payload type if it is unknown */
 | 
						/* Derive the payload type if it is unknown */
 | 
				
			||||||
	if (codec->payload_type == PTYPE_UNDEFINED) {
 | 
						if (codec->payload_type == PTYPE_UNDEFINED) {
 | 
				
			||||||
 | 
							/* TODO: This is semi dead code, see OS#4150 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* For the known codecs from the static range we restore
 | 
							/* For the known codecs from the static range we restore
 | 
				
			||||||
		 * the IANA or 3GPP assigned payload type number */
 | 
							 * the IANA or 3GPP assigned payload type number */
 | 
				
			||||||
@@ -213,9 +249,14 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type,
 | 
				
			|||||||
		 * 110 onwards 3gpp defines prefered codec types, which are
 | 
							 * 110 onwards 3gpp defines prefered codec types, which are
 | 
				
			||||||
		 * also fixed, see above)  */
 | 
							 * also fixed, see above)  */
 | 
				
			||||||
		if (codec->payload_type < 0) {
 | 
							if (codec->payload_type < 0) {
 | 
				
			||||||
 | 
								/* FIXME: pt_offset is completely unrelated and useless here, any of those numbers may already
 | 
				
			||||||
 | 
								 * have been added to the codecs. Instead, there should be an iterator checking for an actually
 | 
				
			||||||
 | 
								 * unused dynamic payload type number. */
 | 
				
			||||||
			codec->payload_type = 96 + pt_offset;
 | 
								codec->payload_type = 96 + pt_offset;
 | 
				
			||||||
			if (codec->payload_type > 109)
 | 
								if (codec->payload_type > 109) {
 | 
				
			||||||
 | 
									LOGP(DLMGCP, LOGL_ERROR, "Ran out of payload type numbers to assign dynamically\n");
 | 
				
			||||||
				goto error;
 | 
									goto error;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -226,41 +267,14 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type,
 | 
				
			|||||||
	} else
 | 
						} else
 | 
				
			||||||
		codec->param_present = false;
 | 
							codec->param_present = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						conn->end.codecs_assigned++;
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
error:
 | 
					error:
 | 
				
			||||||
	/* Make sure we leave a clean codec entry on error. */
 | 
						/* Make sure we leave a clean codec entry on error. */
 | 
				
			||||||
	codec_init(codec);
 | 
						codec_free(codec);
 | 
				
			||||||
	memset(codec, 0, sizeof(*codec));
 | 
					 | 
				
			||||||
	return -EINVAL;
 | 
						return -EINVAL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! Add codec configuration depending on payload type and/or codec name. This
 | 
					 | 
				
			||||||
 *  function uses the input parameters to extrapolate the full codec information.
 | 
					 | 
				
			||||||
 *  \param[out] codec configuration (caller provided memory).
 | 
					 | 
				
			||||||
 *  \param[out] conn related rtp-connection.
 | 
					 | 
				
			||||||
 *  \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
 | 
					 | 
				
			||||||
 *  \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
 | 
					 | 
				
			||||||
 *  \param[in] param optional codec parameters (set to NULL when unused).
 | 
					 | 
				
			||||||
 *  \returns 0 on success, -EINVAL on failure. */
 | 
					 | 
				
			||||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* The amount of codecs we can store is limited, make sure we do not
 | 
					 | 
				
			||||||
	 * overrun this limit. */
 | 
					 | 
				
			||||||
	if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = codec_set(conn->conn, &conn->end.codecs[conn->end.codecs_assigned], payload_type, audio_name,
 | 
					 | 
				
			||||||
		       conn->end.codecs_assigned, param);
 | 
					 | 
				
			||||||
	if (rc != 0)
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	conn->end.codecs_assigned++;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Check if the given codec is applicable on the specified endpoint
 | 
					/* Check if the given codec is applicable on the specified endpoint
 | 
				
			||||||
 * Helper function for mgcp_codec_decide() */
 | 
					 * Helper function for mgcp_codec_decide() */
 | 
				
			||||||
static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec)
 | 
					static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec)
 | 
				
			||||||
@@ -350,6 +364,25 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
 | 
				
			|||||||
	return -EINVAL;
 | 
						return -EINVAL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Return true if octet-aligned is set in the given codec. Default to octet-aligned=0, i.e. bandwidth-efficient mode.
 | 
				
			||||||
 | 
					 * See RFC4867 "RTP Payload Format for AMR and AMR-WB" sections "8.1. AMR Media Type Registration" and "8.2. AMR-WB
 | 
				
			||||||
 | 
					 * Media Type Registration":
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *    octet-align: Permissible values are 0 and 1.  If 1, octet-aligned
 | 
				
			||||||
 | 
					 *                 operation SHALL be used.  If 0 or if not present,
 | 
				
			||||||
 | 
					 *                 bandwidth-efficient operation is employed.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * https://tools.ietf.org/html/rfc4867
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static bool amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!codec->param_present)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						if (!codec->param.amr_octet_aligned_present)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						return codec->param.amr_octet_aligned;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Compare two codecs, all parameters must match up, except for the payload type
 | 
					/* Compare two codecs, all parameters must match up, except for the payload type
 | 
				
			||||||
 * number. */
 | 
					 * number. */
 | 
				
			||||||
static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
 | 
					static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
 | 
				
			||||||
@@ -362,10 +395,12 @@ static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *c
 | 
				
			|||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	if (codec_a->frame_duration_den != codec_b->frame_duration_den)
 | 
						if (codec_a->frame_duration_den != codec_b->frame_duration_den)
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	if (strcmp(codec_a->audio_name, codec_b->audio_name))
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
 | 
						if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
 | 
						if (!strcmp(codec_a->subtype_name, "AMR")) {
 | 
				
			||||||
 | 
							if (amr_is_octet_aligned(codec_a) != amr_is_octet_aligned(codec_b))
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -416,3 +451,19 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return codec_dst->payload_type;
 | 
						return codec_dst->payload_type;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
 | 
				
			||||||
 | 
													const char *subtype_name, unsigned int match_nr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						for (i = 0; i < conn->end.codecs_assigned; i++) {
 | 
				
			||||||
 | 
							if (!strcmp(conn->end.codecs[i].subtype_name, subtype_name)) {
 | 
				
			||||||
 | 
								if (match_nr) {
 | 
				
			||||||
 | 
									match_nr--;
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return &conn->end.codecs[i];
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -129,6 +129,7 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
 | 
				
			|||||||
		conn_osmux_disable(conn_rtp);
 | 
							conn_osmux_disable(conn_rtp);
 | 
				
			||||||
	mgcp_free_rtp_port(&conn_rtp->end);
 | 
						mgcp_free_rtp_port(&conn_rtp->end);
 | 
				
			||||||
	rate_ctr_group_free(conn_rtp->rate_ctr_group);
 | 
						rate_ctr_group_free(conn_rtp->rate_ctr_group);
 | 
				
			||||||
 | 
						mgcp_codec_reset_all(conn_rtp);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void mgcp_conn_watchdog_cb(void *data)
 | 
					void mgcp_conn_watchdog_cb(void *data)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,6 +45,8 @@
 | 
				
			|||||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
					#include <osmocom/mgcp/mgcp_codec.h>
 | 
				
			||||||
#include <osmocom/mgcp/debug.h>
 | 
					#include <osmocom/mgcp/debug.h>
 | 
				
			||||||
#include <osmocom/codec/codec.h>
 | 
					#include <osmocom/codec/codec.h>
 | 
				
			||||||
 | 
					#include <osmocom/mgcp/iuup_cn_node.h>
 | 
				
			||||||
 | 
					#include <osmocom/mgcp/iuup_protocol.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define RTP_SEQ_MOD		(1 << 16)
 | 
					#define RTP_SEQ_MOD		(1 << 16)
 | 
				
			||||||
@@ -52,11 +54,13 @@
 | 
				
			|||||||
#define RTP_MAX_MISORDER	100
 | 
					#define RTP_MAX_MISORDER	100
 | 
				
			||||||
#define RTP_BUF_SIZE		4096
 | 
					#define RTP_BUF_SIZE		4096
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum {
 | 
					enum rtp_proto {
 | 
				
			||||||
	MGCP_PROTO_RTP,
 | 
						MGCP_PROTO_RTP,
 | 
				
			||||||
	MGCP_PROTO_RTCP,
 | 
						MGCP_PROTO_RTCP,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int rx_rtp(struct msgb *msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! Determine the local rtp bind IP-address.
 | 
					/*! Determine the local rtp bind IP-address.
 | 
				
			||||||
 *  \param[out] addr caller provided memory to store the resulting IP-Address
 | 
					 *  \param[out] addr caller provided memory to store the resulting IP-Address
 | 
				
			||||||
 *  \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters
 | 
					 *  \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters
 | 
				
			||||||
@@ -469,19 +473,43 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp,
 | 
				
			|||||||
 * Patch the payload type of an RTP packet so that it uses the payload type
 | 
					 * Patch the payload type of an RTP packet so that it uses the payload type
 | 
				
			||||||
 * that is valid for the destination connection (conn_dst) */
 | 
					 * that is valid for the destination connection (conn_dst) */
 | 
				
			||||||
static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
 | 
					static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
 | 
				
			||||||
			 struct mgcp_conn_rtp *conn_dst, char *data, int len)
 | 
								 struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rtp_hdr *rtp_hdr;
 | 
						struct rtp_hdr *rtp_hdr;
 | 
				
			||||||
	uint8_t pt_in;
 | 
						uint8_t pt_in;
 | 
				
			||||||
	int pt_out;
 | 
						int pt_out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	OSMO_ASSERT(len >= sizeof(struct rtp_hdr));
 | 
						if (msg->len < sizeof(struct rtp_hdr)) {
 | 
				
			||||||
	rtp_hdr = (struct rtp_hdr *)data;
 | 
							LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTP packet too short (%u < %zu)\n",
 | 
				
			||||||
 | 
								     msg->len, sizeof(struct rtp_hdr));
 | 
				
			||||||
	pt_in = rtp_hdr->payload_type;
 | 
					 | 
				
			||||||
	pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in);
 | 
					 | 
				
			||||||
	if (pt_out < 0)
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rtp_hdr = (struct rtp_hdr *)msg->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (conn_src->iuup) {
 | 
				
			||||||
 | 
							/* The source is an IuUP payload. We have received a dynamic payload type number on the IuUP side, and
 | 
				
			||||||
 | 
							 * towards the pure RTP side it should go out as "AMR/8000". Make sure that the payload type number in
 | 
				
			||||||
 | 
							 * the RTP packet matches the a=rtpmap:N payload type number configured for AMR. */
 | 
				
			||||||
 | 
							const struct mgcp_rtp_codec *amr_codec = mgcp_codec_pt_find_by_subtype_name(conn_dst, "AMR", 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!amr_codec) {
 | 
				
			||||||
 | 
								/* There is no AMR codec configured on the outgoing conn. */
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pt_out = amr_codec->payload_type;
 | 
				
			||||||
 | 
						} else if (conn_dst->iuup) {
 | 
				
			||||||
 | 
							/* The destination is an IuUP payload. Use whatever payload number was negotiated during IuUP
 | 
				
			||||||
 | 
							 * Initialization. */
 | 
				
			||||||
 | 
							pt_out = conn_dst->iuup->rtp_payload_type;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							/* Both sides are normal RTP payloads. Consult the rtpmap settings received by SDP. */
 | 
				
			||||||
 | 
							pt_in = rtp_hdr->payload_type;
 | 
				
			||||||
 | 
							pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in);
 | 
				
			||||||
 | 
							if (pt_out < 0)
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rtp_hdr->payload_type = (uint8_t) pt_out;
 | 
						rtp_hdr->payload_type = (uint8_t) pt_out;
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
@@ -496,7 +524,7 @@ static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
 | 
				
			|||||||
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
 | 
					void mgcp_patch_and_count(struct mgcp_endpoint *endp,
 | 
				
			||||||
			  struct mgcp_rtp_state *state,
 | 
								  struct mgcp_rtp_state *state,
 | 
				
			||||||
			  struct mgcp_rtp_end *rtp_end,
 | 
								  struct mgcp_rtp_end *rtp_end,
 | 
				
			||||||
			  struct sockaddr_in *addr, char *data, int len)
 | 
								  struct sockaddr_in *addr, struct msgb *msg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uint32_t arrival_time;
 | 
						uint32_t arrival_time;
 | 
				
			||||||
	int32_t transit;
 | 
						int32_t transit;
 | 
				
			||||||
@@ -504,11 +532,12 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
 | 
				
			|||||||
	uint32_t timestamp, ssrc;
 | 
						uint32_t timestamp, ssrc;
 | 
				
			||||||
	struct rtp_hdr *rtp_hdr;
 | 
						struct rtp_hdr *rtp_hdr;
 | 
				
			||||||
	int payload = rtp_end->codec->payload_type;
 | 
						int payload = rtp_end->codec->payload_type;
 | 
				
			||||||
 | 
						unsigned int len = msg->len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (len < sizeof(*rtp_hdr))
 | 
						if (len < sizeof(*rtp_hdr))
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rtp_hdr = (struct rtp_hdr *)data;
 | 
						rtp_hdr = (struct rtp_hdr *)msg->data;
 | 
				
			||||||
	seq = ntohs(rtp_hdr->sequence);
 | 
						seq = ntohs(rtp_hdr->sequence);
 | 
				
			||||||
	timestamp = ntohl(rtp_hdr->timestamp);
 | 
						timestamp = ntohl(rtp_hdr->timestamp);
 | 
				
			||||||
	arrival_time = get_current_ts(rtp_end->codec->rate);
 | 
						arrival_time = get_current_ts(rtp_end->codec->rate);
 | 
				
			||||||
@@ -641,7 +670,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
 | 
				
			|||||||
 * the receiving end expects GSM-HR data to be formated after RFC 5993, this
 | 
					 * the receiving end expects GSM-HR data to be formated after RFC 5993, this
 | 
				
			||||||
 * function is used to convert between RFC 5993 and TS 101318, which we normally
 | 
					 * function is used to convert between RFC 5993 and TS 101318, which we normally
 | 
				
			||||||
 * use. */
 | 
					 * use. */
 | 
				
			||||||
static void rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
 | 
					static int rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/* NOTE: *data has an overall length of RTP_BUF_SIZE, so there is
 | 
						/* NOTE: *data has an overall length of RTP_BUF_SIZE, so there is
 | 
				
			||||||
	 * plenty of space available to store the slightly larger, converted
 | 
						 * plenty of space available to store the slightly larger, converted
 | 
				
			||||||
@@ -649,7 +678,12 @@ static void rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	struct rtp_hdr *rtp_hdr;
 | 
						struct rtp_hdr *rtp_hdr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	OSMO_ASSERT(*len >= sizeof(struct rtp_hdr));
 | 
						if (*len < sizeof(struct rtp_hdr)) {
 | 
				
			||||||
 | 
							LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n",
 | 
				
			||||||
 | 
								 *len, sizeof(struct rtp_hdr));
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rtp_hdr = (struct rtp_hdr *)data;
 | 
						rtp_hdr = (struct rtp_hdr *)data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (*len == GSM_HR_BYTES + sizeof(struct rtp_hdr)) {
 | 
						if (*len == GSM_HR_BYTES + sizeof(struct rtp_hdr)) {
 | 
				
			||||||
@@ -667,7 +701,9 @@ static void rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
 | 
				
			|||||||
		 * packet. This is not supported yet. */
 | 
							 * packet. This is not supported yet. */
 | 
				
			||||||
		LOGPENDP(endp, DRTP, LOGL_ERROR,
 | 
							LOGPENDP(endp, DRTP, LOGL_ERROR,
 | 
				
			||||||
			 "cannot figure out how to convert RTP packet\n");
 | 
								 "cannot figure out how to convert RTP packet\n");
 | 
				
			||||||
 | 
							return -ENOTSUP;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* For AMR RTP two framing modes are defined RFC3267. There is a bandwith
 | 
					/* For AMR RTP two framing modes are defined RFC3267. There is a bandwith
 | 
				
			||||||
@@ -685,7 +721,11 @@ static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, char *data, int *len,
 | 
				
			|||||||
	unsigned int payload_len;
 | 
						unsigned int payload_len;
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	OSMO_ASSERT(*len >= sizeof(struct rtp_hdr));
 | 
						if (*len < sizeof(struct rtp_hdr)) {
 | 
				
			||||||
 | 
							LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n", *len, sizeof(struct rtp_hdr));
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rtp_hdr = (struct rtp_hdr *)data;
 | 
						rtp_hdr = (struct rtp_hdr *)data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	payload_len = *len - sizeof(struct rtp_hdr);
 | 
						payload_len = *len - sizeof(struct rtp_hdr);
 | 
				
			||||||
@@ -736,30 +776,33 @@ static bool amr_oa_bwe_convert_indicated(struct mgcp_rtp_codec *codec)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Check if a given RTP with AMR payload for octet-aligned mode */
 | 
					/* Check if a given RTP with AMR payload for octet-aligned mode */
 | 
				
			||||||
static bool amr_oa_check(char *data, int len)
 | 
					static int amr_oa_check(char *data, int len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rtp_hdr *rtp_hdr;
 | 
						struct rtp_hdr *rtp_hdr;
 | 
				
			||||||
	unsigned int payload_len;
 | 
						unsigned int payload_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	OSMO_ASSERT(len >= sizeof(struct rtp_hdr));
 | 
						if (len < sizeof(struct rtp_hdr))
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rtp_hdr = (struct rtp_hdr *)data;
 | 
						rtp_hdr = (struct rtp_hdr *)data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	payload_len = len - sizeof(struct rtp_hdr);
 | 
						payload_len = len - sizeof(struct rtp_hdr);
 | 
				
			||||||
 | 
						if (payload_len < sizeof(struct amr_hdr))
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return osmo_amr_is_oa(rtp_hdr->data, payload_len);
 | 
						return osmo_amr_is_oa(rtp_hdr->data, payload_len) ? 1 : 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Forward data to a debug tap. This is debug function that is intended for
 | 
					/* Forward data to a debug tap. This is debug function that is intended for
 | 
				
			||||||
 * debugging the voice traffic with tools like gstreamer */
 | 
					 * debugging the voice traffic with tools like gstreamer */
 | 
				
			||||||
static void forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf,
 | 
					static void forward_data(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
 | 
				
			||||||
			 int len)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!tap->enabled)
 | 
						if (!tap->enabled)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = sendto(fd, buf, len, 0, (struct sockaddr *)&tap->forward,
 | 
						rc = sendto(fd, msg->data, msg->len, 0, (struct sockaddr *)&tap->forward,
 | 
				
			||||||
		    sizeof(tap->forward));
 | 
							    sizeof(tap->forward));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (rc < 0)
 | 
						if (rc < 0)
 | 
				
			||||||
@@ -777,7 +820,7 @@ static void forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf,
 | 
				
			|||||||
 *  \param[in] conn_dst associated destination connection
 | 
					 *  \param[in] conn_dst associated destination connection
 | 
				
			||||||
 *  \returns 0 on success, -1 on ERROR */
 | 
					 *  \returns 0 on success, -1 on ERROR */
 | 
				
			||||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
					int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
				
			||||||
	      char *buf, int len, struct mgcp_conn_rtp *conn_src,
 | 
						      struct msgb *msg, struct mgcp_conn_rtp *conn_src,
 | 
				
			||||||
	      struct mgcp_conn_rtp *conn_dst)
 | 
						      struct mgcp_conn_rtp *conn_dst)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/*! When no destination connection is available (e.g. when only one
 | 
						/*! When no destination connection is available (e.g. when only one
 | 
				
			||||||
@@ -789,6 +832,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
				
			|||||||
	struct mgcp_rtp_state *rtp_state;
 | 
						struct mgcp_rtp_state *rtp_state;
 | 
				
			||||||
	char *dest_name;
 | 
						char *dest_name;
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
						int len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	OSMO_ASSERT(conn_src);
 | 
						OSMO_ASSERT(conn_src);
 | 
				
			||||||
	OSMO_ASSERT(conn_dst);
 | 
						OSMO_ASSERT(conn_dst);
 | 
				
			||||||
@@ -812,7 +856,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
				
			|||||||
	 * should not occur if transcoding is consequently avoided. Until
 | 
						 * should not occur if transcoding is consequently avoided. Until
 | 
				
			||||||
	 * we have transcoding support in osmo-mgw we can not resolve this. */
 | 
						 * we have transcoding support in osmo-mgw we can not resolve this. */
 | 
				
			||||||
	if (is_rtp) {
 | 
						if (is_rtp) {
 | 
				
			||||||
		rc = mgcp_patch_pt(conn_src, conn_dst, buf, len);
 | 
							rc = mgcp_patch_pt(conn_src, conn_dst, msg);
 | 
				
			||||||
		if (rc < 0) {
 | 
							if (rc < 0) {
 | 
				
			||||||
			LOGPENDP(endp, DRTP, LOGL_DEBUG,
 | 
								LOGPENDP(endp, DRTP, LOGL_DEBUG,
 | 
				
			||||||
				 "can not patch PT because no suitable egress codec was found.\n");
 | 
									 "can not patch PT because no suitable egress codec was found.\n");
 | 
				
			||||||
@@ -837,27 +881,37 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
				
			|||||||
	} else if (is_rtp) {
 | 
						} else if (is_rtp) {
 | 
				
			||||||
		int cont;
 | 
							int cont;
 | 
				
			||||||
		int nbytes = 0;
 | 
							int nbytes = 0;
 | 
				
			||||||
		int buflen = len;
 | 
							int buflen = msg->len;
 | 
				
			||||||
		do {
 | 
							do {
 | 
				
			||||||
			/* Run transcoder */
 | 
								/* Run transcoder */
 | 
				
			||||||
			cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
 | 
								cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
 | 
				
			||||||
							    buf, &buflen,
 | 
												    (char*)msg->data, &buflen,
 | 
				
			||||||
							    RTP_BUF_SIZE);
 | 
												    RTP_BUF_SIZE);
 | 
				
			||||||
			if (cont < 0)
 | 
								if (cont < 0)
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (addr)
 | 
								if (addr)
 | 
				
			||||||
				mgcp_patch_and_count(endp, rtp_state, rtp_end,
 | 
									mgcp_patch_and_count(endp, rtp_state, rtp_end,
 | 
				
			||||||
						     addr, buf, buflen);
 | 
											     addr, msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
 | 
								if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
 | 
				
			||||||
				amr_oa_bwe_convert(endp, buf, &buflen,
 | 
									rc = amr_oa_bwe_convert(endp, (char*)msg->data, &buflen,
 | 
				
			||||||
						   conn_dst->end.codec->param.amr_octet_aligned);
 | 
												conn_dst->end.codec->param.amr_octet_aligned);
 | 
				
			||||||
 | 
									if (rc < 0) {
 | 
				
			||||||
 | 
										LOGPENDP(endp, DRTP, LOGL_ERROR,
 | 
				
			||||||
 | 
											 "Error in AMR octet-aligned <-> bandwidth-efficient mode conversion\n");
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			else if (rtp_end->rfc5993_hr_convert
 | 
								else if (rtp_end->rfc5993_hr_convert
 | 
				
			||||||
			    && strcmp(conn_src->end.codec->subtype_name,
 | 
								    && strcmp(conn_src->end.codec->subtype_name,
 | 
				
			||||||
				      "GSM-HR-08") == 0)
 | 
									      "GSM-HR-08") == 0) {
 | 
				
			||||||
				rfc5993_hr_convert(endp, buf, &buflen);
 | 
									rc = rfc5993_hr_convert(endp, (char*)msg->data, &buflen);
 | 
				
			||||||
 | 
									if (rc < 0) {
 | 
				
			||||||
 | 
										LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			LOGPENDP(endp, DRTP, LOGL_DEBUG,
 | 
								LOGPENDP(endp, DRTP, LOGL_DEBUG,
 | 
				
			||||||
				 "process/send to %s %s "
 | 
									 "process/send to %s %s "
 | 
				
			||||||
@@ -868,8 +922,9 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			/* Forward a copy of the RTP data to a debug ip/port */
 | 
								/* Forward a copy of the RTP data to a debug ip/port */
 | 
				
			||||||
			forward_data(rtp_end->rtp.fd, &conn_src->tap_out,
 | 
								forward_data(rtp_end->rtp.fd, &conn_src->tap_out,
 | 
				
			||||||
				     buf, buflen);
 | 
									     msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
			/* FIXME: HACK HACK HACK. See OS#2459.
 | 
								/* FIXME: HACK HACK HACK. See OS#2459.
 | 
				
			||||||
			 * The ip.access nano3G needs the first RTP payload's first two bytes to read hex
 | 
								 * The ip.access nano3G needs the first RTP payload's first two bytes to read hex
 | 
				
			||||||
			 * 'e400', or it will reject the RAB assignment. It seems to not harm other femto
 | 
								 * 'e400', or it will reject the RAB assignment. It seems to not harm other femto
 | 
				
			||||||
@@ -877,7 +932,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
				
			|||||||
			 */
 | 
								 */
 | 
				
			||||||
			if (!rtp_state->patched_first_rtp_payload
 | 
								if (!rtp_state->patched_first_rtp_payload
 | 
				
			||||||
			    && conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
 | 
								    && conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
 | 
				
			||||||
				uint8_t *data = (uint8_t *) & buf[12];
 | 
									uint8_t *data = msg->data + 12;
 | 
				
			||||||
				if (data[0] == 0xe0) {
 | 
									if (data[0] == 0xe0) {
 | 
				
			||||||
					data[0] = 0xe4;
 | 
										data[0] = 0xe4;
 | 
				
			||||||
					data[1] = 0x00;
 | 
										data[1] = 0x00;
 | 
				
			||||||
@@ -887,10 +942,13 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
				
			|||||||
						 " to fake an IuUP Initialization Ack\n");
 | 
											 " to fake an IuUP Initialization Ack\n");
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			len = mgcp_udp_send(rtp_end->rtp.fd,
 | 
								if (conn_dst->iuup)
 | 
				
			||||||
					    &rtp_end->addr,
 | 
									len = osmo_iuup_cn_tx_payload(conn_dst->iuup, msg);
 | 
				
			||||||
					    rtp_end->rtp_port, buf, buflen);
 | 
								else
 | 
				
			||||||
 | 
									len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port,
 | 
				
			||||||
 | 
											    (char*)msg->data, msg->len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (len <= 0)
 | 
								if (len <= 0)
 | 
				
			||||||
				return len;
 | 
									return len;
 | 
				
			||||||
@@ -911,7 +969,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		len = mgcp_udp_send(rtp_end->rtcp.fd,
 | 
							len = mgcp_udp_send(rtp_end->rtcp.fd,
 | 
				
			||||||
				    &rtp_end->addr,
 | 
									    &rtp_end->addr,
 | 
				
			||||||
				    rtp_end->rtcp_port, buf, len);
 | 
									    rtp_end->rtcp_port, (char*)msg->data, msg->len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
 | 
							rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
 | 
				
			||||||
		rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len);
 | 
							rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len);
 | 
				
			||||||
@@ -922,45 +980,6 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
				
			|||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Helper function for mgcp_recv(),
 | 
					 | 
				
			||||||
   Receive one RTP Packet + Originating address from file descriptor */
 | 
					 | 
				
			||||||
static int receive_from(struct mgcp_endpoint *endp, int fd,
 | 
					 | 
				
			||||||
			struct sockaddr_in *addr, char *buf, int bufsize)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
	socklen_t slen = sizeof(*addr);
 | 
					 | 
				
			||||||
	struct sockaddr_in addr_sink;
 | 
					 | 
				
			||||||
	char buf_sink[RTP_BUF_SIZE];
 | 
					 | 
				
			||||||
	bool tossed = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!addr)
 | 
					 | 
				
			||||||
		addr = &addr_sink;
 | 
					 | 
				
			||||||
	if (!buf) {
 | 
					 | 
				
			||||||
		tossed = true;
 | 
					 | 
				
			||||||
		buf = buf_sink;
 | 
					 | 
				
			||||||
		bufsize = sizeof(buf_sink);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = recvfrom(fd, buf, bufsize, 0, (struct sockaddr *)addr, &slen);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOGPENDP(endp, DRTP, LOGL_DEBUG,
 | 
					 | 
				
			||||||
	     "receiving %u bytes length packet from %s:%u ...\n",
 | 
					 | 
				
			||||||
	     rc, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (rc < 0) {
 | 
					 | 
				
			||||||
		LOGPENDP(endp, DRTP, LOGL_ERROR,
 | 
					 | 
				
			||||||
			 "failed to receive packet, errno: %d/%s\n",
 | 
					 | 
				
			||||||
			 errno, strerror(errno));
 | 
					 | 
				
			||||||
		return -1;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (tossed) {
 | 
					 | 
				
			||||||
		LOGPENDP(endp, DRTP, LOGL_ERROR, "packet tossed\n");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return rc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Check if the origin (addr) matches the address/port data of the RTP
 | 
					/* Check if the origin (addr) matches the address/port data of the RTP
 | 
				
			||||||
 * connections. */
 | 
					 * connections. */
 | 
				
			||||||
static int check_rtp_origin(struct mgcp_conn_rtp *conn,
 | 
					static int check_rtp_origin(struct mgcp_conn_rtp *conn,
 | 
				
			||||||
@@ -1053,7 +1072,7 @@ static int check_rtp_destin(struct mgcp_conn_rtp *conn)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/* Do some basic checks to make sure that the RTCP packets we are going to
 | 
					/* Do some basic checks to make sure that the RTCP packets we are going to
 | 
				
			||||||
 * process are not complete garbage */
 | 
					 * process are not complete garbage */
 | 
				
			||||||
static int check_rtcp(char *buf, unsigned int buf_size)
 | 
					static int check_rtcp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rtcp_hdr *hdr;
 | 
						struct rtcp_hdr *hdr;
 | 
				
			||||||
	unsigned int len;
 | 
						unsigned int len;
 | 
				
			||||||
@@ -1061,33 +1080,45 @@ static int check_rtcp(char *buf, unsigned int buf_size)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	/* RTPC packets that are just a header without data do not make
 | 
						/* RTPC packets that are just a header without data do not make
 | 
				
			||||||
	 * any sense. */
 | 
						 * any sense. */
 | 
				
			||||||
	if (buf_size < sizeof(struct rtcp_hdr))
 | 
						if (msg->len < sizeof(struct rtcp_hdr)) {
 | 
				
			||||||
 | 
							LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP packet too short (%u < %zu)\n",
 | 
				
			||||||
 | 
								     msg->len, sizeof(struct rtcp_hdr));
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Make sure that the length of the received packet does not exceed
 | 
						/* Make sure that the length of the received packet does not exceed
 | 
				
			||||||
	 * the available buffer size */
 | 
						 * the available buffer size */
 | 
				
			||||||
	hdr = (struct rtcp_hdr *)buf;
 | 
						hdr = (struct rtcp_hdr *)msg->data;
 | 
				
			||||||
	len = (osmo_ntohs(hdr->length) + 1) * 4;
 | 
						len = (osmo_ntohs(hdr->length) + 1) * 4;
 | 
				
			||||||
	if (len > buf_size)
 | 
						if (len > msg->len) {
 | 
				
			||||||
 | 
							LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP header length exceeds packet size (%u > %u)\n",
 | 
				
			||||||
 | 
								     len, msg->len);
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Make sure we accept only packets that have a proper packet type set
 | 
						/* Make sure we accept only packets that have a proper packet type set
 | 
				
			||||||
	 * See also: http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
 | 
						 * See also: http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
 | 
				
			||||||
	type = hdr->type;
 | 
						type = hdr->type;
 | 
				
			||||||
	if ((type < 192 || type > 195) && (type < 200 || type > 213))
 | 
						if ((type < 192 || type > 195) && (type < 200 || type > 213)) {
 | 
				
			||||||
 | 
							LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP header: invalid type: %u\n", type);
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Do some basic checks to make sure that the RTP packets we are going to
 | 
					/* Do some basic checks to make sure that the RTP packets we are going to
 | 
				
			||||||
 * process are not complete garbage */
 | 
					 * process are not complete garbage */
 | 
				
			||||||
static int check_rtp(char *buf, unsigned int buf_size)
 | 
					static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/* RTP packets that are just a header without data do not make
 | 
						size_t min_size = sizeof(struct rtp_hdr);
 | 
				
			||||||
	 * any sense. */
 | 
						if (conn_src->iuup)
 | 
				
			||||||
	if (buf_size < sizeof(struct rtp_hdr))
 | 
							min_size += sizeof(struct osmo_iuup_hdr_data);
 | 
				
			||||||
		return -EINVAL;
 | 
						if (msg->len < min_size) {
 | 
				
			||||||
 | 
							LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTP packet too short (%u < %zu)\n",
 | 
				
			||||||
 | 
								     msg->len, min_size);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* FIXME: Add more checks, the reason why we do not check more than
 | 
						/* FIXME: Add more checks, the reason why we do not check more than
 | 
				
			||||||
	 * the length is because we currently handle IUUP packets as RTP
 | 
						 * the length is because we currently handle IUUP packets as RTP
 | 
				
			||||||
@@ -1098,86 +1129,14 @@ static int check_rtp(char *buf, unsigned int buf_size)
 | 
				
			|||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Receive RTP data from a specified source connection and dispatch it to a
 | 
					 | 
				
			||||||
 * destination connection. */
 | 
					 | 
				
			||||||
static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
 | 
					 | 
				
			||||||
		     unsigned int buf_size, struct osmo_fd *fd)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct mgcp_endpoint *endp;
 | 
					 | 
				
			||||||
	struct mgcp_conn_rtp *conn;
 | 
					 | 
				
			||||||
	struct mgcp_trunk_config *tcfg;
 | 
					 | 
				
			||||||
	int rc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	conn = (struct mgcp_conn_rtp*) fd->data;
 | 
					 | 
				
			||||||
	endp = conn->conn->endp;
 | 
					 | 
				
			||||||
	tcfg = endp->tcfg;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "receiving RTP/RTCP packet...\n");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rc = receive_from(endp, fd->fd, addr, buf, buf_size);
 | 
					 | 
				
			||||||
	if (rc <= 0)
 | 
					 | 
				
			||||||
		return -1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* FIXME: The way how we detect the protocol looks odd. We should look
 | 
					 | 
				
			||||||
	 * into the packet header. Also we should introduce a packet type
 | 
					 | 
				
			||||||
	 * MGCP_PROTO_IUUP because currently we handle IUUP packets like RTP
 | 
					 | 
				
			||||||
	 * packets which is problematic. */
 | 
					 | 
				
			||||||
	*proto = fd == &conn->end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (*proto == MGCP_PROTO_RTP) {
 | 
					 | 
				
			||||||
		if (check_rtp(buf, rc) < 0) {
 | 
					 | 
				
			||||||
			LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
 | 
					 | 
				
			||||||
				 "invalid RTP packet received -- packet tossed\n");
 | 
					 | 
				
			||||||
			return -1;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else if (*proto == MGCP_PROTO_RTCP) {
 | 
					 | 
				
			||||||
		if (check_rtcp(buf, rc) < 0) {
 | 
					 | 
				
			||||||
			LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
 | 
					 | 
				
			||||||
				 "invalid RTCP packet received -- packet tossed\n");
 | 
					 | 
				
			||||||
			return -1;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "");
 | 
					 | 
				
			||||||
	LOGPC(DRTP, LOGL_DEBUG, "receiving from %s %s %d\n",
 | 
					 | 
				
			||||||
	      conn->conn->name, inet_ntoa(addr->sin_addr),
 | 
					 | 
				
			||||||
	      ntohs(addr->sin_port));
 | 
					 | 
				
			||||||
	LOGPENDP(endp, DRTP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn->conn));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Check if the origin of the RTP packet seems plausible */
 | 
					 | 
				
			||||||
	if (tcfg->rtp_accept_all == 0) {
 | 
					 | 
				
			||||||
		if (check_rtp_origin(conn, addr) != 0)
 | 
					 | 
				
			||||||
			return -1;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Filter out dummy message */
 | 
					 | 
				
			||||||
	if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) {
 | 
					 | 
				
			||||||
		LOGPCONN(conn->conn, DRTP, LOGL_NOTICE,
 | 
					 | 
				
			||||||
			 "dummy message received\n");
 | 
					 | 
				
			||||||
		LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
 | 
					 | 
				
			||||||
			 "packet tossed\n");
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Increment RX statistics */
 | 
					 | 
				
			||||||
	rate_ctr_inc(&conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]);
 | 
					 | 
				
			||||||
	rate_ctr_add(&conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], rc);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Forward a copy of the RTP data to a debug ip/port */
 | 
					 | 
				
			||||||
	forward_data(fd->fd, &conn->tap_in, buf, rc);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return rc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Send RTP data. Possible options are standard RTP packet
 | 
					/* Send RTP data. Possible options are standard RTP packet
 | 
				
			||||||
 * transmission or trsmission via an osmux connection */
 | 
					 * transmission or trsmission via an osmux connection */
 | 
				
			||||||
static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
 | 
					static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
 | 
				
			||||||
			 unsigned int buf_size,
 | 
					 | 
				
			||||||
			 struct mgcp_conn_rtp *conn_src,
 | 
					 | 
				
			||||||
			 struct mgcp_conn_rtp *conn_dst)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct mgcp_endpoint *endp;
 | 
						enum rtp_proto proto = OSMO_RTP_MSG_CTX(msg)->proto;
 | 
				
			||||||
	endp = conn_src->conn->endp;
 | 
						struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
 | 
				
			||||||
 | 
						struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
 | 
				
			||||||
 | 
						struct mgcp_endpoint *endp = conn_src->conn->endp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	LOGPENDP(endp, DRTP, LOGL_DEBUG, "destin conn:%s\n",
 | 
						LOGPENDP(endp, DRTP, LOGL_DEBUG, "destin conn:%s\n",
 | 
				
			||||||
		 mgcp_conn_dump(conn_dst->conn));
 | 
							 mgcp_conn_dump(conn_dst->conn));
 | 
				
			||||||
@@ -1196,13 +1155,13 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
 | 
				
			|||||||
			 "endpoint type is MGCP_RTP_DEFAULT, "
 | 
								 "endpoint type is MGCP_RTP_DEFAULT, "
 | 
				
			||||||
			 "using mgcp_send() to forward data directly\n");
 | 
								 "using mgcp_send() to forward data directly\n");
 | 
				
			||||||
		return mgcp_send(endp, proto == MGCP_PROTO_RTP,
 | 
							return mgcp_send(endp, proto == MGCP_PROTO_RTP,
 | 
				
			||||||
				 addr, buf, buf_size, conn_src, conn_dst);
 | 
									 from_addr, msg, conn_src, conn_dst);
 | 
				
			||||||
	case MGCP_OSMUX_BSC_NAT:
 | 
						case MGCP_OSMUX_BSC_NAT:
 | 
				
			||||||
	case MGCP_OSMUX_BSC:
 | 
						case MGCP_OSMUX_BSC:
 | 
				
			||||||
		LOGPENDP(endp, DRTP, LOGL_DEBUG,
 | 
							LOGPENDP(endp, DRTP, LOGL_DEBUG,
 | 
				
			||||||
			 "endpoint type is MGCP_OSMUX_BSC_NAT, "
 | 
								 "endpoint type is MGCP_OSMUX_BSC_NAT, "
 | 
				
			||||||
			 "using osmux_xfrm_to_osmux() to forward data through OSMUX\n");
 | 
								 "using osmux_xfrm_to_osmux() to forward data through OSMUX\n");
 | 
				
			||||||
		return osmux_xfrm_to_osmux(buf, buf_size, conn_dst);
 | 
							return osmux_xfrm_to_osmux((char*)msg->data, msg->len, conn_dst);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* If the data has not been handled/forwarded until here, it will
 | 
						/* If the data has not been handled/forwarded until here, it will
 | 
				
			||||||
@@ -1220,10 +1179,12 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
 | 
				
			|||||||
 *  \param[in] buf_size size data length of buf
 | 
					 *  \param[in] buf_size size data length of buf
 | 
				
			||||||
 *  \param[in] conn originating connection
 | 
					 *  \param[in] conn originating connection
 | 
				
			||||||
 *  \returns 0 on success, -1 on ERROR */
 | 
					 *  \returns 0 on success, -1 on ERROR */
 | 
				
			||||||
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
 | 
					int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
 | 
				
			||||||
				unsigned int buf_size, struct mgcp_conn *conn)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
 | 
				
			||||||
 | 
						struct mgcp_conn *conn = conn_src->conn;
 | 
				
			||||||
	struct mgcp_conn *conn_dst;
 | 
						struct mgcp_conn *conn_dst;
 | 
				
			||||||
 | 
						struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*! NOTE: This callback function implements the endpoint specific
 | 
						/*! NOTE: This callback function implements the endpoint specific
 | 
				
			||||||
	 *  dispatch bahviour of an rtp bridge/proxy endpoint. It is assumed
 | 
						 *  dispatch bahviour of an rtp bridge/proxy endpoint. It is assumed
 | 
				
			||||||
@@ -1242,11 +1203,10 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
 | 
				
			|||||||
		 * address data from the UDP packet header to patch the
 | 
							 * address data from the UDP packet header to patch the
 | 
				
			||||||
		 * outgoing address in connection on the fly */
 | 
							 * outgoing address in connection on the fly */
 | 
				
			||||||
		if (conn->u.rtp.end.rtp_port == 0) {
 | 
							if (conn->u.rtp.end.rtp_port == 0) {
 | 
				
			||||||
			conn->u.rtp.end.addr = addr->sin_addr;
 | 
								conn->u.rtp.end.addr = from_addr->sin_addr;
 | 
				
			||||||
			conn->u.rtp.end.rtp_port = addr->sin_port;
 | 
								conn->u.rtp.end.rtp_port = from_addr->sin_port;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return mgcp_send_rtp(proto, addr, buf,
 | 
							return mgcp_send_rtp(conn_src, msg);
 | 
				
			||||||
				     buf_size, &conn->u.rtp, &conn->u.rtp);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Find a destination connection. */
 | 
						/* Find a destination connection. */
 | 
				
			||||||
@@ -1278,9 +1238,7 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Dispatch RTP packet to destination RTP connection */
 | 
						/* Dispatch RTP packet to destination RTP connection */
 | 
				
			||||||
	return mgcp_send_rtp(proto, addr, buf,
 | 
						return mgcp_send_rtp(&conn_dst->u.rtp, msg);
 | 
				
			||||||
			     buf_size, &conn->u.rtp, &conn_dst->u.rtp);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
 | 
					/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
 | 
				
			||||||
@@ -1302,6 +1260,76 @@ void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *co
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool is_dummy_msg(enum rtp_proto proto, struct msgb *msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return msg->len == 1 && msg->data[0] == MGCP_DUMMY_LOAD;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct pdu_ctx {
 | 
				
			||||||
 | 
						struct sockaddr_in *from_addr;
 | 
				
			||||||
 | 
						struct mgcp_conn_rtp *conn_src;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* IuUP CN node has stripped an IuUP header and forwards RTP data to distribute to the peers. */
 | 
				
			||||||
 | 
					int iuup_rx_payload(struct msgb *msg, void *node_priv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
 | 
				
			||||||
 | 
						LOG_CONN_RTP(conn_src, LOGL_DEBUG, "iuup_rx_payload(%u bytes)\n", msg->len);
 | 
				
			||||||
 | 
						return rx_rtp(msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* IuUP CN node has composed a message that contains an IuUP header and asks us to send to the IuUP peer.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int iuup_tx_msg(struct msgb *msg, void *node_priv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct in_addr zero_addr = {};
 | 
				
			||||||
 | 
						struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
 | 
				
			||||||
 | 
						struct mgcp_conn_rtp *conn_dst = node_priv;
 | 
				
			||||||
 | 
						struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
 | 
				
			||||||
 | 
						struct mgcp_rtp_end *rtp_end = &conn_dst->end;
 | 
				
			||||||
 | 
						struct in_addr to_addr = rtp_end->addr;
 | 
				
			||||||
 | 
						uint16_t to_port = rtp_end->rtp_port;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (conn_src == conn_dst
 | 
				
			||||||
 | 
						    && !memcmp(&zero_addr, &to_addr, sizeof(zero_addr)) && !to_port) {
 | 
				
			||||||
 | 
							LOG_CONN_RTP(conn_dst, LOGL_DEBUG, "iuup_tx_msg(): direct IuUP reply\n");
 | 
				
			||||||
 | 
							/* IuUP wants to send a message back to the same peer that sent an RTP package, but there
 | 
				
			||||||
 | 
							 * is no address configured for that peer yet. It is probably an IuUP Initialization ACK
 | 
				
			||||||
 | 
							 * reply. Use the sender address to send the reply.
 | 
				
			||||||
 | 
							 *
 | 
				
			||||||
 | 
							 * During 3G RAB Assignment, a 3G cell might first probe the MGW and expect an IuUP
 | 
				
			||||||
 | 
							 * Initialization ACK before it replies to the MSC with a successful RAB Assignment; only
 | 
				
			||||||
 | 
							 * after that reply does MSC officially know which RTP address+port the 3G cell wants to
 | 
				
			||||||
 | 
							 * use and can tell this MGW about it, so this "loopback" is, for some 3G cells, the only
 | 
				
			||||||
 | 
							 * chance we have to get a successful RAB Assignment done (particularly the nano3G does
 | 
				
			||||||
 | 
							 * this). */
 | 
				
			||||||
 | 
							to_addr = from_addr->sin_addr;
 | 
				
			||||||
 | 
							to_port = from_addr->sin_port;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOG_CONN_RTP(conn_dst, LOGL_DEBUG, "iuup_tx_msg(%u bytes) to %s:%u\n", msg->len,
 | 
				
			||||||
 | 
							     inet_ntoa(to_addr), ntohs(to_port));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return mgcp_udp_send(rtp_end->rtp.fd, &to_addr, to_port, (char*)msg->data, msg->len);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void iuup_init(struct mgcp_conn_rtp *conn_src)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct osmo_iuup_cn_cfg cfg = {
 | 
				
			||||||
 | 
							.node_priv = conn_src,
 | 
				
			||||||
 | 
							.rx_payload = iuup_rx_payload,
 | 
				
			||||||
 | 
							.tx_msg = iuup_tx_msg,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (conn_src->iuup) {
 | 
				
			||||||
 | 
							LOG_CONN_RTP(conn_src, LOGL_NOTICE, "Rx IuUP init, but already initialized. Ignoring.\n");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						conn_src->iuup = osmo_iuup_cn_init(conn_src->conn, &cfg, "endp_%d_conn_%s",
 | 
				
			||||||
 | 
										   ENDPOINT_NUMBER(conn_src->conn->endp), conn_src->conn->id);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Handle incoming RTP data from NET */
 | 
					/* Handle incoming RTP data from NET */
 | 
				
			||||||
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
 | 
					static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -1315,23 +1343,88 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
 | 
				
			|||||||
	struct mgcp_conn_rtp *conn_src;
 | 
						struct mgcp_conn_rtp *conn_src;
 | 
				
			||||||
	struct mgcp_endpoint *endp;
 | 
						struct mgcp_endpoint *endp;
 | 
				
			||||||
	struct sockaddr_in addr;
 | 
						struct sockaddr_in addr;
 | 
				
			||||||
 | 
						socklen_t slen = sizeof(addr);
 | 
				
			||||||
	char buf[RTP_BUF_SIZE];
 | 
						int ret;
 | 
				
			||||||
	int proto;
 | 
						enum rtp_proto proto;
 | 
				
			||||||
	int len;
 | 
						struct osmo_rtp_msg_ctx mc;
 | 
				
			||||||
 | 
						struct msgb *msg = msgb_alloc_headroom(RTP_BUF_SIZE + OSMO_IUUP_HEADROOM,
 | 
				
			||||||
 | 
										       OSMO_IUUP_HEADROOM, "RTP-rx");
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	conn_src = (struct mgcp_conn_rtp *)fd->data;
 | 
						conn_src = (struct mgcp_conn_rtp *)fd->data;
 | 
				
			||||||
	OSMO_ASSERT(conn_src);
 | 
						OSMO_ASSERT(conn_src);
 | 
				
			||||||
	endp = conn_src->conn->endp;
 | 
						endp = conn_src->conn->endp;
 | 
				
			||||||
	OSMO_ASSERT(endp);
 | 
						OSMO_ASSERT(endp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	LOGPENDP(endp, DRTP, LOGL_DEBUG, "source conn:%s\n",
 | 
						proto = (fd == &conn_src->end.rtp)? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
 | 
				
			||||||
		 mgcp_conn_dump(conn_src->conn));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Receive packet */
 | 
						ret = recvfrom(fd->fd, msg->data, msg->data_len, 0, (struct sockaddr *)&addr, &slen);
 | 
				
			||||||
	len = mgcp_recv(&proto, &addr, buf, sizeof(buf), fd);
 | 
					
 | 
				
			||||||
	if (len < 0)
 | 
						if (ret <= 0) {
 | 
				
			||||||
		return -1;
 | 
							LOG_CONN_RTP(conn_src, LOGL_ERROR, "recvfrom error: %s\n", strerror(errno));
 | 
				
			||||||
 | 
							rc = -1;
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msgb_put(msg, ret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n",
 | 
				
			||||||
 | 
							     proto == MGCP_PROTO_RTP ? "RTP" : "RTPC",
 | 
				
			||||||
 | 
							     msg->len, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((proto == MGCP_PROTO_RTP && check_rtp(conn_src, msg))
 | 
				
			||||||
 | 
						    || (proto == MGCP_PROTO_RTCP && check_rtcp(conn_src, msg))) {
 | 
				
			||||||
 | 
							/* Logging happened in the two check_ functions */
 | 
				
			||||||
 | 
							rc = -1;
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (is_dummy_msg(proto, msg)) {
 | 
				
			||||||
 | 
							LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx dummy packet (dropped)\n");
 | 
				
			||||||
 | 
							rc = 0;
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mc = (struct osmo_rtp_msg_ctx){
 | 
				
			||||||
 | 
							.proto = proto,
 | 
				
			||||||
 | 
							.conn_src = conn_src,
 | 
				
			||||||
 | 
							.from_addr = &addr,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						OSMO_RTP_MSG_CTX(msg) = &mc;
 | 
				
			||||||
 | 
						LOG_CONN_RTP(conn_src, LOGL_DEBUG, "msg ctx: %d %p %s\n",
 | 
				
			||||||
 | 
							     OSMO_RTP_MSG_CTX(msg)->proto,
 | 
				
			||||||
 | 
							     OSMO_RTP_MSG_CTX(msg)->conn_src,
 | 
				
			||||||
 | 
							     osmo_hexdump((void*)OSMO_RTP_MSG_CTX(msg)->from_addr, sizeof(struct sockaddr_in)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Increment RX statistics */
 | 
				
			||||||
 | 
						rate_ctr_inc(&conn_src->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]);
 | 
				
			||||||
 | 
						rate_ctr_add(&conn_src->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], msg->len);
 | 
				
			||||||
 | 
						/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Forward a copy of the RTP data to a debug ip/port */
 | 
				
			||||||
 | 
						forward_data(fd->fd, &conn_src->tap_in, msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (proto == MGCP_PROTO_RTP && osmo_iuup_is_init(msg))
 | 
				
			||||||
 | 
							iuup_init(conn_src);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (conn_src->iuup && proto == MGCP_PROTO_RTP)
 | 
				
			||||||
 | 
							rc = osmo_iuup_cn_rx_pdu(conn_src->iuup, msg);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							rc = rx_rtp(msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						msgb_free(msg);
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int rx_rtp(struct msgb *msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
 | 
				
			||||||
 | 
						struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
 | 
				
			||||||
 | 
						struct mgcp_conn *conn = conn_src->conn;
 | 
				
			||||||
 | 
						struct mgcp_trunk_config *tcfg = conn->endp->tcfg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx_rtp(%u bytes)\n", msg->len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mgcp_conn_watchdog_kick(conn_src->conn);
 | 
						mgcp_conn_watchdog_kick(conn_src->conn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1340,14 +1433,20 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
 | 
				
			|||||||
	 * define, then we check if the incoming payload matches that
 | 
						 * define, then we check if the incoming payload matches that
 | 
				
			||||||
	 * expectation. */
 | 
						 * expectation. */
 | 
				
			||||||
	if (amr_oa_bwe_convert_indicated(conn_src->end.codec)) {
 | 
						if (amr_oa_bwe_convert_indicated(conn_src->end.codec)) {
 | 
				
			||||||
		if (amr_oa_check(buf, len) != conn_src->end.codec->param.amr_octet_aligned)
 | 
							int oa = amr_oa_check((char*)msg->data, msg->len);
 | 
				
			||||||
 | 
							if (oa < 0)
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned)
 | 
				
			||||||
			return -1;
 | 
								return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Check if the origin of the RTP packet seems plausible */
 | 
				
			||||||
 | 
						if (!tcfg->rtp_accept_all && check_rtp_origin(conn_src, from_addr))
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Execute endpoint specific implementation that handles the
 | 
						/* Execute endpoint specific implementation that handles the
 | 
				
			||||||
	 * dispatching of the RTP data */
 | 
						 * dispatching of the RTP data */
 | 
				
			||||||
	return endp->type->dispatch_rtp_cb(proto, &addr, buf, len,
 | 
						return conn->endp->type->dispatch_rtp_cb(msg);
 | 
				
			||||||
					   conn_src->conn);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! set IP Type of Service parameter.
 | 
					/*! set IP Type of Service parameter.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -234,13 +234,8 @@ static void scheduled_from_osmux_tx_rtp_cb(struct msgb *msg, void *data)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	struct mgcp_conn_rtp *conn = data;
 | 
						struct mgcp_conn_rtp *conn = data;
 | 
				
			||||||
	struct mgcp_endpoint *endp = conn->conn->endp;
 | 
						struct mgcp_endpoint *endp = conn->conn->endp;
 | 
				
			||||||
	struct sockaddr_in addr = {
 | 
					 | 
				
			||||||
		.sin_addr = conn->end.addr,
 | 
					 | 
				
			||||||
		.sin_port = conn->end.rtp_port,
 | 
					 | 
				
			||||||
	}; /* FIXME: not set/used in cb */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						endp->type->dispatch_rtp_cb(msg);
 | 
				
			||||||
	endp->type->dispatch_rtp_cb(MGCP_PROTO_RTP, &addr, (char *)msg->data, msg->len, conn->conn);
 | 
					 | 
				
			||||||
	msgb_free(msg);
 | 
						msgb_free(msg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -244,6 +244,12 @@ static const struct log_info_cat log_categories[] = {
 | 
				
			|||||||
		  .color = "\033[1;30m",
 | 
							  .color = "\033[1;30m",
 | 
				
			||||||
		  .enabled = 1,.loglevel = LOGL_NOTICE,
 | 
							  .enabled = 1,.loglevel = LOGL_NOTICE,
 | 
				
			||||||
		  },
 | 
							  },
 | 
				
			||||||
 | 
						[DIUUP] = {
 | 
				
			||||||
 | 
							  .name = "DIUUP",
 | 
				
			||||||
 | 
							  .description = "IuUP within RTP stream handling",
 | 
				
			||||||
 | 
							  .color = "\033[1;31m",
 | 
				
			||||||
 | 
							  .enabled = 1,.loglevel = LOGL_NOTICE,
 | 
				
			||||||
 | 
							  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const struct log_info log_info = {
 | 
					const struct log_info log_info = {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
SUBDIRS = \
 | 
					SUBDIRS = \
 | 
				
			||||||
	mgcp_client \
 | 
						mgcp_client \
 | 
				
			||||||
	mgcp \
 | 
						mgcp \
 | 
				
			||||||
 | 
						iuup \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
 | 
					# The `:;' works around a Bash 3.2 bug when the output is not writeable.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										45
									
								
								tests/iuup/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								tests/iuup/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					AM_CPPFLAGS = \
 | 
				
			||||||
 | 
						$(all_includes) \
 | 
				
			||||||
 | 
						-I$(top_srcdir)/include \
 | 
				
			||||||
 | 
						-I$(top_srcdir) \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AM_CFLAGS = \
 | 
				
			||||||
 | 
						-Wall \
 | 
				
			||||||
 | 
						-ggdb3 \
 | 
				
			||||||
 | 
						$(LIBOSMOCORE_CFLAGS) \
 | 
				
			||||||
 | 
						$(LIBOSMOVTY_CFLAGS) \
 | 
				
			||||||
 | 
						$(LIBOSMOGSM_CFLAGS) \
 | 
				
			||||||
 | 
						$(LIBOSMONETIF_CFLAGS) \
 | 
				
			||||||
 | 
						$(COVERAGE_CFLAGS) \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AM_LDFLAGS = \
 | 
				
			||||||
 | 
						$(COVERAGE_LDFLAGS) \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EXTRA_DIST = \
 | 
				
			||||||
 | 
						iuup_test.ok \
 | 
				
			||||||
 | 
						iuup_test.err \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					noinst_PROGRAMS = \
 | 
				
			||||||
 | 
						iuup_test \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					iuup_test_SOURCES = \
 | 
				
			||||||
 | 
						iuup_test.c \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					iuup_test_LDADD = \
 | 
				
			||||||
 | 
						$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
 | 
				
			||||||
 | 
						$(LIBOSMOCORE_LIBS) \
 | 
				
			||||||
 | 
						$(LIBOSMOVTY_LIBS) \
 | 
				
			||||||
 | 
						$(LIBOSMOGSM_LIBS) \
 | 
				
			||||||
 | 
						$(LIBRARY_DL) \
 | 
				
			||||||
 | 
						$(LIBOSMONETIF_LIBS) \
 | 
				
			||||||
 | 
						-lm  \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					update_exp:
 | 
				
			||||||
 | 
						$(builddir)/iuup_test >$(srcdir)/iuup_test.ok 2>$(srcdir)/iuup_test.err
 | 
				
			||||||
							
								
								
									
										156
									
								
								tests/iuup/iuup_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								tests/iuup/iuup_test.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,156 @@
 | 
				
			|||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/msgb.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/application.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/logging.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/mgcp/iuup_cn_node.h>
 | 
				
			||||||
 | 
					#include <osmocom/mgcp/iuup_protocol.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void *ctx = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *dump(struct msgb *msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return osmo_hexdump_nospc(msg->data, msg->len);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct msgb *msgb_from_hex(const char *label, const char *hex)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct msgb *msg = msgb_alloc_headroom(4096 + OSMO_IUUP_HEADROOM,
 | 
				
			||||||
 | 
										       OSMO_IUUP_HEADROOM, label);
 | 
				
			||||||
 | 
						unsigned char *rc;
 | 
				
			||||||
 | 
						msg->l2h = msg->data;
 | 
				
			||||||
 | 
						rc = msgb_put(msg, osmo_hexparse(hex, msg->data, msgb_tailroom(msg)));
 | 
				
			||||||
 | 
						OSMO_ASSERT(rc == msg->l2h);
 | 
				
			||||||
 | 
						return msg;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *expect_rx_payload = NULL;
 | 
				
			||||||
 | 
					int rx_payload(struct msgb *msg, void *node_priv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						printf("rx_payload() invoked by iuup_cn!\n");
 | 
				
			||||||
 | 
						printf("        [IuUP] -RTP->\n");
 | 
				
			||||||
 | 
						printf("%s\n", dump(msg));
 | 
				
			||||||
 | 
						printf("node_priv=%p\n", node_priv);
 | 
				
			||||||
 | 
						if (!expect_rx_payload) {
 | 
				
			||||||
 | 
							printf("ERROR: did not expect rx_payload()\n");
 | 
				
			||||||
 | 
							exit(-1);
 | 
				
			||||||
 | 
						} else if (strcmp(expect_rx_payload, dump(msg))) {
 | 
				
			||||||
 | 
							printf("ERROR: mismatches expected msg %s\n", expect_rx_payload);
 | 
				
			||||||
 | 
							exit(-1);
 | 
				
			||||||
 | 
						} else
 | 
				
			||||||
 | 
							printf("ok: matches expected msg\n");
 | 
				
			||||||
 | 
						expect_rx_payload = NULL;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *expect_tx_msg = NULL;
 | 
				
			||||||
 | 
					int tx_msg(struct msgb *msg, void *node_priv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						printf("tx_msg() invoked by iuup_cn!\n");
 | 
				
			||||||
 | 
						printf(" <-PDU- [IuUP]\n");
 | 
				
			||||||
 | 
						printf("%s\n", dump(msg));
 | 
				
			||||||
 | 
						printf("node_priv=%p\n", node_priv);
 | 
				
			||||||
 | 
						if (!expect_tx_msg) {
 | 
				
			||||||
 | 
							printf("ERROR: did not expect tx_msg()\n");
 | 
				
			||||||
 | 
							exit(-1);
 | 
				
			||||||
 | 
						} else if (strcmp(expect_tx_msg, dump(msg))) {
 | 
				
			||||||
 | 
							printf("ERROR: mismatches expected msg %s\n", expect_tx_msg);
 | 
				
			||||||
 | 
							exit(-1);
 | 
				
			||||||
 | 
						} else
 | 
				
			||||||
 | 
							printf("ok: matches expected msg\n");
 | 
				
			||||||
 | 
						expect_tx_msg = NULL;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int rx_pdu(struct osmo_iuup_cn *cn, struct msgb *msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						printf(" -PDU-> [IuUP]\n");
 | 
				
			||||||
 | 
						printf("%s\n", dump(msg));
 | 
				
			||||||
 | 
						rc = osmo_iuup_cn_rx_pdu(cn, msg);
 | 
				
			||||||
 | 
						printf("rc=%d\n", rc);
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int tx_payload(struct osmo_iuup_cn *cn, struct msgb *msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						printf("        [IuUP] <-RTP-\n");
 | 
				
			||||||
 | 
						printf("%s\n", dump(msg));
 | 
				
			||||||
 | 
						rc = osmo_iuup_cn_tx_payload(cn, msg);
 | 
				
			||||||
 | 
						printf("rc=%d\n", rc);
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void test_cn_session()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						void *node_priv = (void*)0x2342;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct osmo_iuup_cn_cfg cfg = {
 | 
				
			||||||
 | 
							.node_priv = node_priv,
 | 
				
			||||||
 | 
							.rx_payload = rx_payload,
 | 
				
			||||||
 | 
							.tx_msg = tx_msg,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct osmo_iuup_cn *cn = osmo_iuup_cn_init(ctx, &cfg, __func__);
 | 
				
			||||||
 | 
						OSMO_ASSERT(cn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						printf("\nSend IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack\n");
 | 
				
			||||||
 | 
						expect_tx_msg = "8060dc5219495e3f00010111" /* RTP header */
 | 
				
			||||||
 | 
								"e4002400"; /* IuUP Init Ack */
 | 
				
			||||||
 | 
						rx_pdu(cn,
 | 
				
			||||||
 | 
						       msgb_from_hex("IuUP-Init",
 | 
				
			||||||
 | 
								     "8060dc5219495e3f00010111" /* <- RTP header */
 | 
				
			||||||
 | 
								     "e000df99" /* <- IuUP header */
 | 
				
			||||||
 | 
								     "160051673c01270000820000001710000100" /* IuUP params */));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define RTP_HEADER "8060944c6256042c00010102"
 | 
				
			||||||
 | 
					#define IUUP_HEADER "0100e2b3"
 | 
				
			||||||
 | 
					#define RTP_PAYLOAD "6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0"
 | 
				
			||||||
 | 
						printf("\nReceive payload encapsulated in IuUP. Expecting rx_payload() of just RTP packet\n");
 | 
				
			||||||
 | 
						printf("i.e. should strip away " IUUP_HEADER "\n");
 | 
				
			||||||
 | 
						expect_rx_payload = RTP_HEADER "f03c" RTP_PAYLOAD;
 | 
				
			||||||
 | 
						rx_pdu(cn,
 | 
				
			||||||
 | 
						       msgb_from_hex("IuUP-Data",
 | 
				
			||||||
 | 
								     RTP_HEADER IUUP_HEADER RTP_PAYLOAD));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						printf("\nTransmit RTP. Expecting tx_msg() with inserted IuUP header\n");
 | 
				
			||||||
 | 
						expect_tx_msg = RTP_HEADER "000002b3" RTP_PAYLOAD;
 | 
				
			||||||
 | 
						tx_payload(cn,
 | 
				
			||||||
 | 
							   msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						printf("\nMore RTP, each time the Frame Nr advances, causing a new header CRC.\n");
 | 
				
			||||||
 | 
						expect_tx_msg = RTP_HEADER "0100e2b3" RTP_PAYLOAD;
 | 
				
			||||||
 | 
						tx_payload(cn,
 | 
				
			||||||
 | 
							   msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
 | 
				
			||||||
 | 
						expect_tx_msg = RTP_HEADER "02007eb3" RTP_PAYLOAD;
 | 
				
			||||||
 | 
						tx_payload(cn,
 | 
				
			||||||
 | 
							   msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
 | 
				
			||||||
 | 
						expect_tx_msg = RTP_HEADER "03009eb3" RTP_PAYLOAD;
 | 
				
			||||||
 | 
						tx_payload(cn,
 | 
				
			||||||
 | 
							   msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						printf("All done.\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct log_info_cat log_categories[] = {
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const struct log_info log_info = {
 | 
				
			||||||
 | 
						.cat = log_categories,
 | 
				
			||||||
 | 
						.num_cat = ARRAY_SIZE(log_categories),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ctx = talloc_named_const(NULL, 0, __FILE__);
 | 
				
			||||||
 | 
						void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
 | 
				
			||||||
 | 
						osmo_init_logging2(ctx, &log_info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test_cn_session();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						talloc_free(msgb_ctx);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										0
									
								
								tests/iuup/iuup_test.err
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/iuup/iuup_test.err
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										58
									
								
								tests/iuup/iuup_test.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								tests/iuup/iuup_test.ok
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					Send IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack
 | 
				
			||||||
 | 
					 -PDU-> [IuUP]
 | 
				
			||||||
 | 
					8060dc5219495e3f00010111e000df99160051673c01270000820000001710000100
 | 
				
			||||||
 | 
					tx_msg() invoked by iuup_cn!
 | 
				
			||||||
 | 
					 <-PDU- [IuUP]
 | 
				
			||||||
 | 
					8060dc5219495e3f00010111e4002400
 | 
				
			||||||
 | 
					node_priv=0x2342
 | 
				
			||||||
 | 
					ok: matches expected msg
 | 
				
			||||||
 | 
					rc=0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Receive payload encapsulated in IuUP. Expecting rx_payload() of just RTP packet
 | 
				
			||||||
 | 
					i.e. should strip away 0100e2b3
 | 
				
			||||||
 | 
					 -PDU-> [IuUP]
 | 
				
			||||||
 | 
					8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
				
			||||||
 | 
					rx_payload() invoked by iuup_cn!
 | 
				
			||||||
 | 
					        [IuUP] -RTP->
 | 
				
			||||||
 | 
					8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
				
			||||||
 | 
					node_priv=0x2342
 | 
				
			||||||
 | 
					ok: matches expected msg
 | 
				
			||||||
 | 
					rc=0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Transmit RTP. Expecting tx_msg() with inserted IuUP header
 | 
				
			||||||
 | 
					        [IuUP] <-RTP-
 | 
				
			||||||
 | 
					8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
				
			||||||
 | 
					tx_msg() invoked by iuup_cn!
 | 
				
			||||||
 | 
					 <-PDU- [IuUP]
 | 
				
			||||||
 | 
					8060944c6256042c00010102000002b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
				
			||||||
 | 
					node_priv=0x2342
 | 
				
			||||||
 | 
					ok: matches expected msg
 | 
				
			||||||
 | 
					rc=0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					More RTP, each time the Frame Nr advances, causing a new header CRC.
 | 
				
			||||||
 | 
					        [IuUP] <-RTP-
 | 
				
			||||||
 | 
					8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
				
			||||||
 | 
					tx_msg() invoked by iuup_cn!
 | 
				
			||||||
 | 
					 <-PDU- [IuUP]
 | 
				
			||||||
 | 
					8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
				
			||||||
 | 
					node_priv=0x2342
 | 
				
			||||||
 | 
					ok: matches expected msg
 | 
				
			||||||
 | 
					rc=0
 | 
				
			||||||
 | 
					        [IuUP] <-RTP-
 | 
				
			||||||
 | 
					8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
				
			||||||
 | 
					tx_msg() invoked by iuup_cn!
 | 
				
			||||||
 | 
					 <-PDU- [IuUP]
 | 
				
			||||||
 | 
					8060944c6256042c0001010202007eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
				
			||||||
 | 
					node_priv=0x2342
 | 
				
			||||||
 | 
					ok: matches expected msg
 | 
				
			||||||
 | 
					rc=0
 | 
				
			||||||
 | 
					        [IuUP] <-RTP-
 | 
				
			||||||
 | 
					8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
				
			||||||
 | 
					tx_msg() invoked by iuup_cn!
 | 
				
			||||||
 | 
					 <-PDU- [IuUP]
 | 
				
			||||||
 | 
					8060944c6256042c0001010203009eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
 | 
				
			||||||
 | 
					node_priv=0x2342
 | 
				
			||||||
 | 
					ok: matches expected msg
 | 
				
			||||||
 | 
					rc=0
 | 
				
			||||||
 | 
					All done.
 | 
				
			||||||
@@ -28,6 +28,7 @@
 | 
				
			|||||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
					#include <osmocom/mgcp/mgcp_endp.h>
 | 
				
			||||||
#include <osmocom/mgcp/mgcp_sdp.h>
 | 
					#include <osmocom/mgcp/mgcp_sdp.h>
 | 
				
			||||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
					#include <osmocom/mgcp/mgcp_codec.h>
 | 
				
			||||||
 | 
					#include <osmocom/mgcp/mgcp_internal.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <osmocom/core/application.h>
 | 
					#include <osmocom/core/application.h>
 | 
				
			||||||
#include <osmocom/core/talloc.h>
 | 
					#include <osmocom/core/talloc.h>
 | 
				
			||||||
@@ -1237,7 +1238,7 @@ struct rtp_packet_info test_rtp_packets1[] = {
 | 
				
			|||||||
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
 | 
					void mgcp_patch_and_count(struct mgcp_endpoint *endp,
 | 
				
			||||||
			  struct mgcp_rtp_state *state,
 | 
								  struct mgcp_rtp_state *state,
 | 
				
			||||||
			  struct mgcp_rtp_end *rtp_end,
 | 
								  struct mgcp_rtp_end *rtp_end,
 | 
				
			||||||
			  struct sockaddr_in *addr, char *data, int len);
 | 
								  struct sockaddr_in *addr, struct msgb *msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void test_packet_error_detection(int patch_ssrc, int patch_ts)
 | 
					static void test_packet_error_detection(int patch_ssrc, int patch_ts)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -1249,7 +1250,6 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
 | 
				
			|||||||
	struct mgcp_rtp_state state;
 | 
						struct mgcp_rtp_state state;
 | 
				
			||||||
	struct mgcp_rtp_end *rtp;
 | 
						struct mgcp_rtp_end *rtp;
 | 
				
			||||||
	struct sockaddr_in addr = { 0 };
 | 
						struct sockaddr_in addr = { 0 };
 | 
				
			||||||
	char buffer[4096];
 | 
					 | 
				
			||||||
	uint32_t last_ssrc = 0;
 | 
						uint32_t last_ssrc = 0;
 | 
				
			||||||
	uint32_t last_timestamp = 0;
 | 
						uint32_t last_timestamp = 0;
 | 
				
			||||||
	uint32_t last_seqno = 0;
 | 
						uint32_t last_seqno = 0;
 | 
				
			||||||
@@ -1297,16 +1297,17 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
 | 
						for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
 | 
				
			||||||
		struct rtp_packet_info *info = test_rtp_packets1 + i;
 | 
							struct rtp_packet_info *info = test_rtp_packets1 + i;
 | 
				
			||||||
 | 
							struct msgb *msg = msgb_alloc(4096, __func__);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		force_monotonic_time_us = round(1000000.0 * info->txtime);
 | 
							force_monotonic_time_us = round(1000000.0 * info->txtime);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		OSMO_ASSERT(info->len <= sizeof(buffer));
 | 
							OSMO_ASSERT(info->len <= msgb_tailroom(msg));
 | 
				
			||||||
		OSMO_ASSERT(info->len >= 0);
 | 
							OSMO_ASSERT(info->len >= 0);
 | 
				
			||||||
		memmove(buffer, info->data, info->len);
 | 
							msg->l3h = msgb_put(msg, info->len);
 | 
				
			||||||
 | 
							memcpy((char*)msgb_l3(msg), info->data, info->len);
 | 
				
			||||||
		mgcp_rtp_end_config(&endp, 1, rtp);
 | 
							mgcp_rtp_end_config(&endp, 1, rtp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		mgcp_patch_and_count(&endp, &state, rtp, &addr,
 | 
							mgcp_patch_and_count(&endp, &state, rtp, &addr, msg);
 | 
				
			||||||
				     buffer, info->len);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (state.out_stream.ssrc != last_ssrc) {
 | 
							if (state.out_stream.ssrc != last_ssrc) {
 | 
				
			||||||
			printf("Output SSRC changed to %08x\n",
 | 
								printf("Output SSRC changed to %08x\n",
 | 
				
			||||||
@@ -1333,6 +1334,8 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
 | 
				
			|||||||
		last_out_ts_err_cnt = state.out_stream.err_ts_ctr->current;
 | 
							last_out_ts_err_cnt = state.out_stream.err_ts_ctr->current;
 | 
				
			||||||
		last_timestamp = state.out_stream.last_timestamp;
 | 
							last_timestamp = state.out_stream.last_timestamp;
 | 
				
			||||||
		last_seqno = state.out_stream.last_seq;
 | 
							last_seqno = state.out_stream.last_seq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							msgb_free(msg);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	force_monotonic_time_us = -1;
 | 
						force_monotonic_time_us = -1;
 | 
				
			||||||
@@ -1711,98 +1714,335 @@ static void test_check_local_cx_options(void *ctx)
 | 
				
			|||||||
	OSMO_ASSERT(check_local_cx_options(ctx, ",,,") == -1);
 | 
						OSMO_ASSERT(check_local_cx_options(ctx, ",,,") == -1);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void test_mgcp_codec_pt_translate_pars(struct mgcp_rtp_codec *c)
 | 
					static const struct mgcp_codec_param amr_param_octet_aligned_true = {
 | 
				
			||||||
{
 | 
						.amr_octet_aligned_present = true,
 | 
				
			||||||
	c->rate = 8000;
 | 
						.amr_octet_aligned = true,
 | 
				
			||||||
	c->channels = 1;
 | 
					};
 | 
				
			||||||
	c->frame_duration_num = 23;
 | 
					
 | 
				
			||||||
	c->frame_duration_den = 42;
 | 
					static const struct mgcp_codec_param amr_param_octet_aligned_false = {
 | 
				
			||||||
}
 | 
						.amr_octet_aligned_present = true,
 | 
				
			||||||
 | 
						.amr_octet_aligned = false,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct mgcp_codec_param amr_param_octet_aligned_unset = {
 | 
				
			||||||
 | 
						.amr_octet_aligned_present = false,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct testcase_mgcp_codec_pt_translate_codec {
 | 
				
			||||||
 | 
						int payload_type;
 | 
				
			||||||
 | 
						const char *audio_name;
 | 
				
			||||||
 | 
						const struct mgcp_codec_param *param;
 | 
				
			||||||
 | 
						int expect_rc;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct testcase_mgcp_codec_pt_translate_expect {
 | 
				
			||||||
 | 
						bool end;
 | 
				
			||||||
 | 
						int payload_type_map[2];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct testcase_mgcp_codec_pt_translate {
 | 
				
			||||||
 | 
						const char *descr;
 | 
				
			||||||
 | 
						/* two conns on an endpoint, each with N configured codecs */
 | 
				
			||||||
 | 
						struct testcase_mgcp_codec_pt_translate_codec codecs[2][10];
 | 
				
			||||||
 | 
						struct testcase_mgcp_codec_pt_translate_expect expect[32];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translate_cases[] = {
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.descr = "same order, but differing payload type numbers",
 | 
				
			||||||
 | 
							.codecs = {
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
 | 
				
			||||||
 | 
									{ 0, "PCMU/8000/1", NULL, },
 | 
				
			||||||
 | 
									{ 111, "GSM-HR-08/8000/1", NULL, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 96, "AMR/8000/1", &amr_param_octet_aligned_true, },
 | 
				
			||||||
 | 
									{ 0, "PCMU/8000/1", NULL, },
 | 
				
			||||||
 | 
									{ 97, "GSM-HR-08/8000/1", NULL, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.expect = {
 | 
				
			||||||
 | 
								{ .payload_type_map = {112, 96}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {0, 0}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {111, 97} },
 | 
				
			||||||
 | 
								{ .payload_type_map = {123, -EINVAL} },
 | 
				
			||||||
 | 
								{ .end = true },
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.descr = "different order and different payload type numbers",
 | 
				
			||||||
 | 
							.codecs = {
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 0, "PCMU/8000/1", NULL, },
 | 
				
			||||||
 | 
									{ 111, "GSM-HR-08/8000/1", NULL, },
 | 
				
			||||||
 | 
									{ 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 97, "GSM-HR-08/8000/1", NULL, },
 | 
				
			||||||
 | 
									{ 0, "PCMU/8000/1", NULL, },
 | 
				
			||||||
 | 
									{ 96, "AMR/8000/1", &amr_param_octet_aligned_true, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.expect = {
 | 
				
			||||||
 | 
								{ .payload_type_map = {112, 96}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {0, 0}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {111, 97} },
 | 
				
			||||||
 | 
								{ .payload_type_map = {123, -EINVAL} },
 | 
				
			||||||
 | 
								{ .end = true },
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.descr = "both sides have the same payload_type numbers assigned to differing codecs",
 | 
				
			||||||
 | 
							.codecs = {
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 0, "PCMU/8000/1", NULL, },
 | 
				
			||||||
 | 
									{ 96, "GSM-HR-08/8000/1", NULL, },
 | 
				
			||||||
 | 
									{ 97, "AMR/8000/1", &amr_param_octet_aligned_true, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 97, "GSM-HR-08/8000/1", NULL, },
 | 
				
			||||||
 | 
									{ 0, "PCMU/8000/1", NULL, },
 | 
				
			||||||
 | 
									{ 96, "AMR/8000/1", &amr_param_octet_aligned_true, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.expect = {
 | 
				
			||||||
 | 
								{ .payload_type_map = {96, 97}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {97, 96}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {0, 0}, },
 | 
				
			||||||
 | 
								{ .end = true },
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.descr = "conn0 has no codecs",
 | 
				
			||||||
 | 
							.codecs = {
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									/* no codecs */
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 96, "AMR/8000/1", &amr_param_octet_aligned_true, },
 | 
				
			||||||
 | 
									{ 0, "PCMU/8000/1", NULL, },
 | 
				
			||||||
 | 
									{ 97, "GSM-HR-08/8000/1", NULL, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.expect = {
 | 
				
			||||||
 | 
								{ .payload_type_map = {112, -EINVAL}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {0, -EINVAL}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {111, -EINVAL} },
 | 
				
			||||||
 | 
								{ .end = true },
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.descr = "conn1 has no codecs",
 | 
				
			||||||
 | 
							.codecs = {
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
 | 
				
			||||||
 | 
									{ 0, "PCMU/8000/1", NULL, },
 | 
				
			||||||
 | 
									{ 111, "GSM-HR-08/8000/1", NULL, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									/* no codecs */
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.expect = {
 | 
				
			||||||
 | 
								{ .payload_type_map = {112, -EINVAL}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {0, -EINVAL}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {111, -EINVAL} },
 | 
				
			||||||
 | 
								{ .end = true },
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.descr = "test AMR with differing octet-aligned settings",
 | 
				
			||||||
 | 
							.codecs = {
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
				
			||||||
 | 
									{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 122, "AMR/8000", &amr_param_octet_aligned_false, },
 | 
				
			||||||
 | 
									{ 121, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.expect = {
 | 
				
			||||||
 | 
								{ .payload_type_map = {111, 121}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {112, 122} },
 | 
				
			||||||
 | 
								{ .end = true },
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.descr = "test AMR with missing octet-aligned settings (defaults to 0)",
 | 
				
			||||||
 | 
							.codecs = {
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
				
			||||||
 | 
									{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 122, "AMR/8000", &amr_param_octet_aligned_unset, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.expect = {
 | 
				
			||||||
 | 
								{ .payload_type_map = {111, -EINVAL}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {112, 122} },
 | 
				
			||||||
 | 
								{ .end = true },
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.descr = "test AMR with NULL param (defaults to 0)",
 | 
				
			||||||
 | 
							.codecs = {
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
				
			||||||
 | 
									{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 122, "AMR/8000", NULL, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.expect = {
 | 
				
			||||||
 | 
								{ .payload_type_map = {111, -EINVAL}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {112, 122} },
 | 
				
			||||||
 | 
								{ .end = true },
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.descr = "match FOO/8000/1 and FOO/8000 as identical, single channel is implicit",
 | 
				
			||||||
 | 
							.codecs = {
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 0, "PCMU/8000/1", NULL, },
 | 
				
			||||||
 | 
									{ 111, "GSM-HR-08/8000/1", NULL, },
 | 
				
			||||||
 | 
									{ 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 97, "GSM-HR-08/8000", NULL, },
 | 
				
			||||||
 | 
									{ 0, "PCMU/8000", NULL, },
 | 
				
			||||||
 | 
									{ 96, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.expect = {
 | 
				
			||||||
 | 
								{ .payload_type_map = {112, 96}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {0, 0}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {111, 97} },
 | 
				
			||||||
 | 
								{ .payload_type_map = {123, -EINVAL} },
 | 
				
			||||||
 | 
								{ .end = true },
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.descr = "match FOO/8000/1 and FOO as identical, 8k and single channel are implicit",
 | 
				
			||||||
 | 
							.codecs = {
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 0, "PCMU/8000/1", NULL, },
 | 
				
			||||||
 | 
									{ 111, "GSM-HR-08/8000/1", NULL, },
 | 
				
			||||||
 | 
									{ 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 97, "GSM-HR-08", NULL, },
 | 
				
			||||||
 | 
									{ 0, "PCMU", NULL, },
 | 
				
			||||||
 | 
									{ 96, "AMR", &amr_param_octet_aligned_true, },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.expect = {
 | 
				
			||||||
 | 
								{ .payload_type_map = {112, 96}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {0, 0}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {111, 97} },
 | 
				
			||||||
 | 
								{ .payload_type_map = {123, -EINVAL} },
 | 
				
			||||||
 | 
								{ .end = true },
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							.descr = "test whether channel number matching is waterproof",
 | 
				
			||||||
 | 
							.codecs = {
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 111, "GSM-HR-08/8000", },
 | 
				
			||||||
 | 
									{ 112, "GSM-HR-08/8000/2", .expect_rc = -22},
 | 
				
			||||||
 | 
									{ 113, "GSM-HR-08/8000/3", .expect_rc = -22},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									{ 122, "GSM-HR-08/8000/2", .expect_rc = -22},
 | 
				
			||||||
 | 
									{ 121, "GSM-HR-08/8000/1", },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							.expect = {
 | 
				
			||||||
 | 
								{ .payload_type_map = {111, 121}, },
 | 
				
			||||||
 | 
								{ .payload_type_map = {112, -EINVAL} },
 | 
				
			||||||
 | 
								{ .payload_type_map = {113, -EINVAL} },
 | 
				
			||||||
 | 
								{ .end = true },
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void test_mgcp_codec_pt_translate(void)
 | 
					static void test_mgcp_codec_pt_translate(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct mgcp_conn_rtp conn_src;
 | 
						int i;
 | 
				
			||||||
	struct mgcp_conn_rtp conn_dst;
 | 
						bool ok = true;
 | 
				
			||||||
	int pt_dst;
 | 
						printf("\nTesting mgcp_codec_pt_translate()\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Setup a realistic set of codec configurations on both
 | 
						for (i = 0; i < ARRAY_SIZE(test_mgcp_codec_pt_translate_cases); i++) {
 | 
				
			||||||
	 * ends. AMR and HR will use different payload types. PCMU
 | 
							const struct testcase_mgcp_codec_pt_translate *t = &test_mgcp_codec_pt_translate_cases[i];
 | 
				
			||||||
	 * must use 0 on both ends since this is not a dynamic payload
 | 
							struct mgcp_conn_rtp conn[2] = {};
 | 
				
			||||||
	 * type */
 | 
							int rc;
 | 
				
			||||||
	test_mgcp_codec_pt_translate_pars(&conn_src.end.codecs[0]);
 | 
							int conn_i;
 | 
				
			||||||
	test_mgcp_codec_pt_translate_pars(&conn_dst.end.codecs[0]);
 | 
							int c;
 | 
				
			||||||
	test_mgcp_codec_pt_translate_pars(&conn_src.end.codecs[1]);
 | 
					 | 
				
			||||||
	test_mgcp_codec_pt_translate_pars(&conn_dst.end.codecs[1]);
 | 
					 | 
				
			||||||
	test_mgcp_codec_pt_translate_pars(&conn_src.end.codecs[2]);
 | 
					 | 
				
			||||||
	test_mgcp_codec_pt_translate_pars(&conn_dst.end.codecs[2]);
 | 
					 | 
				
			||||||
	conn_src.end.codecs[0].payload_type = 112;
 | 
					 | 
				
			||||||
	conn_dst.end.codecs[0].payload_type = 96;
 | 
					 | 
				
			||||||
	conn_src.end.codecs[1].payload_type = 0;
 | 
					 | 
				
			||||||
	conn_dst.end.codecs[1].payload_type = 0;
 | 
					 | 
				
			||||||
	conn_src.end.codecs[2].payload_type = 111;
 | 
					 | 
				
			||||||
	conn_dst.end.codecs[2].payload_type = 97;
 | 
					 | 
				
			||||||
	conn_src.end.codecs[0].audio_name = "AMR/8000/1";
 | 
					 | 
				
			||||||
	conn_dst.end.codecs[0].audio_name = "AMR/8000/1";
 | 
					 | 
				
			||||||
	conn_src.end.codecs[1].audio_name = "PCMU/8000/1";
 | 
					 | 
				
			||||||
	conn_dst.end.codecs[1].audio_name = "PCMU/8000/1";
 | 
					 | 
				
			||||||
	conn_src.end.codecs[2].audio_name = "GSM-HR-08/8000/1";
 | 
					 | 
				
			||||||
	conn_dst.end.codecs[2].audio_name = "GSM-HR-08/8000/1";
 | 
					 | 
				
			||||||
	conn_src.end.codecs[0].subtype_name = "AMR";
 | 
					 | 
				
			||||||
	conn_dst.end.codecs[0].subtype_name = "AMR";
 | 
					 | 
				
			||||||
	conn_src.end.codecs[1].subtype_name = "PCMU";
 | 
					 | 
				
			||||||
	conn_dst.end.codecs[1].subtype_name = "PCMU";
 | 
					 | 
				
			||||||
	conn_src.end.codecs[2].subtype_name = "GSM-HR-08";
 | 
					 | 
				
			||||||
	conn_dst.end.codecs[2].subtype_name = "GSM-HR-08";
 | 
					 | 
				
			||||||
	conn_src.end.codecs_assigned = 3;
 | 
					 | 
				
			||||||
	conn_dst.end.codecs_assigned = 3;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* We expect the function to find the PT we must use when we send the
 | 
							printf("#%d: %s\n", i, t->descr);
 | 
				
			||||||
	 * packet out to the destination. All we know is the context for both
 | 
					 | 
				
			||||||
	 * connections and the payload type from the source packet */
 | 
					 | 
				
			||||||
	pt_dst =
 | 
					 | 
				
			||||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
					 | 
				
			||||||
				    conn_src.end.codecs[0].payload_type);
 | 
					 | 
				
			||||||
	OSMO_ASSERT(pt_dst == conn_dst.end.codecs[0].payload_type);
 | 
					 | 
				
			||||||
	pt_dst =
 | 
					 | 
				
			||||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
					 | 
				
			||||||
				    conn_src.end.codecs[1].payload_type);
 | 
					 | 
				
			||||||
	OSMO_ASSERT(pt_dst == conn_dst.end.codecs[1].payload_type);
 | 
					 | 
				
			||||||
	pt_dst =
 | 
					 | 
				
			||||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
					 | 
				
			||||||
				    conn_src.end.codecs[2].payload_type);
 | 
					 | 
				
			||||||
	OSMO_ASSERT(pt_dst == conn_dst.end.codecs[2].payload_type);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Try some constellations that must fail */
 | 
							for (conn_i = 0; conn_i < 2; conn_i++) {
 | 
				
			||||||
	pt_dst = mgcp_codec_pt_translate(&conn_src, &conn_dst, 123);
 | 
								printf(" - add codecs on conn%d:\n", conn_i);
 | 
				
			||||||
	OSMO_ASSERT(pt_dst == -EINVAL);
 | 
								for (c = 0; c < ARRAY_SIZE(t->codecs[conn_i]); c++) {
 | 
				
			||||||
	conn_src.end.codecs_assigned = 0;
 | 
									const struct testcase_mgcp_codec_pt_translate_codec *codec = &t->codecs[conn_i][c];
 | 
				
			||||||
	conn_dst.end.codecs_assigned = 3;
 | 
									if (!codec->audio_name)
 | 
				
			||||||
	pt_dst =
 | 
										break;
 | 
				
			||||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
					
 | 
				
			||||||
				    conn_src.end.codecs[0].payload_type);
 | 
									rc = mgcp_codec_add(&conn[conn_i], codec->payload_type, codec->audio_name, codec->param);
 | 
				
			||||||
	OSMO_ASSERT(pt_dst == -EINVAL);
 | 
					
 | 
				
			||||||
	pt_dst =
 | 
									printf("   %2d: %3d %s%s  -> rc=%d\n", c, codec->payload_type, codec->audio_name,
 | 
				
			||||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
									       codec->param ?
 | 
				
			||||||
				    conn_src.end.codecs[1].payload_type);
 | 
										       (codec->param->amr_octet_aligned_present?
 | 
				
			||||||
	OSMO_ASSERT(pt_dst == -EINVAL);
 | 
												(codec->param->amr_octet_aligned ?
 | 
				
			||||||
	pt_dst =
 | 
													" octet-aligned=1" : " octet-aligned=0")
 | 
				
			||||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
												: " octet-aligned=unset")
 | 
				
			||||||
				    conn_src.end.codecs[2].payload_type);
 | 
											: "",
 | 
				
			||||||
	OSMO_ASSERT(pt_dst == -EINVAL);
 | 
											rc);
 | 
				
			||||||
	conn_src.end.codecs_assigned = 3;
 | 
									if (rc != codec->expect_rc) {
 | 
				
			||||||
	conn_dst.end.codecs_assigned = 0;
 | 
										printf("     ERROR: expected rc=%d\n", codec->expect_rc);
 | 
				
			||||||
	pt_dst =
 | 
										ok = false;
 | 
				
			||||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
									}
 | 
				
			||||||
				    conn_src.end.codecs[0].payload_type);
 | 
								}
 | 
				
			||||||
	OSMO_ASSERT(pt_dst == -EINVAL);
 | 
								if (!c)
 | 
				
			||||||
	pt_dst =
 | 
									printf("    (none)\n");
 | 
				
			||||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
							}
 | 
				
			||||||
				    conn_src.end.codecs[1].payload_type);
 | 
					
 | 
				
			||||||
	OSMO_ASSERT(pt_dst == -EINVAL);
 | 
							for (c = 0; c < ARRAY_SIZE(t->expect); c++) {
 | 
				
			||||||
	pt_dst =
 | 
								const struct testcase_mgcp_codec_pt_translate_expect *expect = &t->expect[c];
 | 
				
			||||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
								int result;
 | 
				
			||||||
				    conn_src.end.codecs[2].payload_type);
 | 
					
 | 
				
			||||||
	OSMO_ASSERT(pt_dst == -EINVAL);
 | 
								if (expect->end)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								result = mgcp_codec_pt_translate(&conn[0], &conn[1], expect->payload_type_map[0]);
 | 
				
			||||||
 | 
								printf(" - mgcp_codec_pt_translate(conn0, conn1, %d) -> %d\n",
 | 
				
			||||||
 | 
								       expect->payload_type_map[0], result);
 | 
				
			||||||
 | 
								if (result != expect->payload_type_map[1]) {
 | 
				
			||||||
 | 
									printf("     ERROR: expected -> %d\n", expect->payload_type_map[1]);
 | 
				
			||||||
 | 
									ok = false;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* If the expected result is an error, don't do reverse map test */
 | 
				
			||||||
 | 
								if (expect->payload_type_map[1] < 0)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								result = mgcp_codec_pt_translate(&conn[1], &conn[0], expect->payload_type_map[1]);
 | 
				
			||||||
 | 
								printf(" - mgcp_codec_pt_translate(conn1, conn0, %d) -> %d\n",
 | 
				
			||||||
 | 
								       expect->payload_type_map[1], result);
 | 
				
			||||||
 | 
								if (result != expect->payload_type_map[0]) {
 | 
				
			||||||
 | 
									printf("     ERROR: expected -> %d\n", expect->payload_type_map[0]);
 | 
				
			||||||
 | 
									ok = false;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (conn_i = 0; conn_i < 2; conn_i++)
 | 
				
			||||||
 | 
								mgcp_codec_reset_all(&conn[conn_i]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						OSMO_ASSERT(ok);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void test_conn_id_matching()
 | 
					void test_conn_id_matching()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1218,6 +1218,148 @@ p:10, a:PCMU -> p:10, a:PCMU
 | 
				
			|||||||
Testing get_lco_identifier()
 | 
					Testing get_lco_identifier()
 | 
				
			||||||
p:10, a:PCMU -> p:10, a:PCMU
 | 
					p:10, a:PCMU -> p:10, a:PCMU
 | 
				
			||||||
p:10, a:PCMU -> p:10, a:PCMU
 | 
					p:10, a:PCMU -> p:10, a:PCMU
 | 
				
			||||||
 | 
					'XXXX, p:10, a:PCMU' -> 'p:10, a:PCMU'
 | 
				
			||||||
 | 
					'XXXX,p:10,a:PCMU' -> 'p:10,a:PCMU'
 | 
				
			||||||
 | 
					'10,a:PCMU' -> 'a:PCMU'
 | 
				
			||||||
 | 
					'10, a:PCMU' -> 'a:PCMU'
 | 
				
			||||||
 | 
					'10,a: PCMU' -> 'a: PCMU'
 | 
				
			||||||
 | 
					'10 ,a: PCMU' -> 'a: PCMU'
 | 
				
			||||||
 | 
					', a:PCMU' -> 'a:PCMU'
 | 
				
			||||||
 | 
					' a:PCMU' -> 'a:PCMU'
 | 
				
			||||||
 | 
					'' -> '(null)'
 | 
				
			||||||
 | 
					p10, aPCMU -> (null)
 | 
				
			||||||
 | 
					'10,a :PCMU' -> '(null)'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Testing mgcp_codec_pt_translate()
 | 
				
			||||||
 | 
					#0: same order, but differing payload type numbers
 | 
				
			||||||
 | 
					 - add codecs on conn0:
 | 
				
			||||||
 | 
					    0: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
				
			||||||
 | 
					    1:   0 PCMU/8000/1  -> rc=0
 | 
				
			||||||
 | 
					    2: 111 GSM-HR-08/8000/1  -> rc=0
 | 
				
			||||||
 | 
					 - add codecs on conn1:
 | 
				
			||||||
 | 
					    0:  96 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
				
			||||||
 | 
					    1:   0 PCMU/8000/1  -> rc=0
 | 
				
			||||||
 | 
					    2:  97 GSM-HR-08/8000/1  -> rc=0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
 | 
				
			||||||
 | 
					#1: different order and different payload type numbers
 | 
				
			||||||
 | 
					 - add codecs on conn0:
 | 
				
			||||||
 | 
					    0:   0 PCMU/8000/1  -> rc=0
 | 
				
			||||||
 | 
					    1: 111 GSM-HR-08/8000/1  -> rc=0
 | 
				
			||||||
 | 
					    2: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
				
			||||||
 | 
					 - add codecs on conn1:
 | 
				
			||||||
 | 
					    0:  97 GSM-HR-08/8000/1  -> rc=0
 | 
				
			||||||
 | 
					    1:   0 PCMU/8000/1  -> rc=0
 | 
				
			||||||
 | 
					    2:  96 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
 | 
				
			||||||
 | 
					#2: both sides have the same payload_type numbers assigned to differing codecs
 | 
				
			||||||
 | 
					 - add codecs on conn0:
 | 
				
			||||||
 | 
					    0:   0 PCMU/8000/1  -> rc=0
 | 
				
			||||||
 | 
					    1:  96 GSM-HR-08/8000/1  -> rc=0
 | 
				
			||||||
 | 
					    2:  97 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
				
			||||||
 | 
					 - add codecs on conn1:
 | 
				
			||||||
 | 
					    0:  97 GSM-HR-08/8000/1  -> rc=0
 | 
				
			||||||
 | 
					    1:   0 PCMU/8000/1  -> rc=0
 | 
				
			||||||
 | 
					    2:  96 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 96) -> 97
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 96
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 97) -> 96
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 96) -> 97
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
 | 
				
			||||||
 | 
					#3: conn0 has no codecs
 | 
				
			||||||
 | 
					 - add codecs on conn0:
 | 
				
			||||||
 | 
					    (none)
 | 
				
			||||||
 | 
					 - add codecs on conn1:
 | 
				
			||||||
 | 
					    0:  96 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
				
			||||||
 | 
					    1:   0 PCMU/8000/1  -> rc=0
 | 
				
			||||||
 | 
					    2:  97 GSM-HR-08/8000/1  -> rc=0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
 | 
				
			||||||
 | 
					#4: conn1 has no codecs
 | 
				
			||||||
 | 
					 - add codecs on conn0:
 | 
				
			||||||
 | 
					    0: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
				
			||||||
 | 
					    1:   0 PCMU/8000/1  -> rc=0
 | 
				
			||||||
 | 
					    2: 111 GSM-HR-08/8000/1  -> rc=0
 | 
				
			||||||
 | 
					 - add codecs on conn1:
 | 
				
			||||||
 | 
					    (none)
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
 | 
				
			||||||
 | 
					#5: test AMR with differing octet-aligned settings
 | 
				
			||||||
 | 
					 - add codecs on conn0:
 | 
				
			||||||
 | 
					    0: 111 AMR/8000 octet-aligned=1  -> rc=0
 | 
				
			||||||
 | 
					    1: 112 AMR/8000 octet-aligned=0  -> rc=0
 | 
				
			||||||
 | 
					 - add codecs on conn1:
 | 
				
			||||||
 | 
					    0: 122 AMR/8000 octet-aligned=0  -> rc=0
 | 
				
			||||||
 | 
					    1: 121 AMR/8000 octet-aligned=1  -> rc=0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 111) -> 121
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 121) -> 111
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
 | 
				
			||||||
 | 
					#6: test AMR with missing octet-aligned settings (defaults to 0)
 | 
				
			||||||
 | 
					 - add codecs on conn0:
 | 
				
			||||||
 | 
					    0: 111 AMR/8000 octet-aligned=1  -> rc=0
 | 
				
			||||||
 | 
					    1: 112 AMR/8000 octet-aligned=0  -> rc=0
 | 
				
			||||||
 | 
					 - add codecs on conn1:
 | 
				
			||||||
 | 
					    0: 122 AMR/8000 octet-aligned=unset  -> rc=0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
 | 
				
			||||||
 | 
					#7: test AMR with NULL param (defaults to 0)
 | 
				
			||||||
 | 
					 - add codecs on conn0:
 | 
				
			||||||
 | 
					    0: 111 AMR/8000 octet-aligned=1  -> rc=0
 | 
				
			||||||
 | 
					    1: 112 AMR/8000 octet-aligned=0  -> rc=0
 | 
				
			||||||
 | 
					 - add codecs on conn1:
 | 
				
			||||||
 | 
					    0: 122 AMR/8000  -> rc=0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
 | 
				
			||||||
 | 
					#8: match FOO/8000/1 and FOO/8000 as identical, single channel is implicit
 | 
				
			||||||
 | 
					 - add codecs on conn0:
 | 
				
			||||||
 | 
					    0:   0 PCMU/8000/1  -> rc=0
 | 
				
			||||||
 | 
					    1: 111 GSM-HR-08/8000/1  -> rc=0
 | 
				
			||||||
 | 
					    2: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
				
			||||||
 | 
					 - add codecs on conn1:
 | 
				
			||||||
 | 
					    0:  97 GSM-HR-08/8000  -> rc=0
 | 
				
			||||||
 | 
					    1:   0 PCMU/8000  -> rc=0
 | 
				
			||||||
 | 
					    2:  96 AMR/8000 octet-aligned=1  -> rc=0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
 | 
				
			||||||
 | 
					#9: match FOO/8000/1 and FOO as identical, 8k and single channel are implicit
 | 
				
			||||||
 | 
					 - add codecs on conn0:
 | 
				
			||||||
 | 
					    0:   0 PCMU/8000/1  -> rc=0
 | 
				
			||||||
 | 
					    1: 111 GSM-HR-08/8000/1  -> rc=0
 | 
				
			||||||
 | 
					    2: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
				
			||||||
 | 
					 - add codecs on conn1:
 | 
				
			||||||
 | 
					    0:  97 GSM-HR-08  -> rc=0
 | 
				
			||||||
 | 
					    1:   0 PCMU  -> rc=0
 | 
				
			||||||
 | 
					    2:  96 AMR octet-aligned=1  -> rc=0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
 | 
				
			||||||
 | 
					 - mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
 | 
				
			||||||
 | 
					#10: test whether channel number matching is waterproof
 | 
				
			||||||
 - add codecs on conn0:
 | 
					 - add codecs on conn0:
 | 
				
			||||||
    0: 111 GSM-HR-08/8000  -> rc=0
 | 
					    0: 111 GSM-HR-08/8000  -> rc=0
 | 
				
			||||||
    1: 112 GSM-HR-08/8000/2  -> rc=-22
 | 
					    1: 112 GSM-HR-08/8000/2  -> rc=-22
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,3 +13,10 @@ AT_KEYWORDS([mgcp])
 | 
				
			|||||||
cat $abs_srcdir/mgcp/mgcp_test.ok > expout
 | 
					cat $abs_srcdir/mgcp/mgcp_test.ok > expout
 | 
				
			||||||
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
 | 
					AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
 | 
				
			||||||
AT_CLEANUP
 | 
					AT_CLEANUP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AT_SETUP([iuup])
 | 
				
			||||||
 | 
					AT_KEYWORDS([iuup])
 | 
				
			||||||
 | 
					cat $abs_srcdir/iuup/iuup_test.ok > expout
 | 
				
			||||||
 | 
					cat $abs_srcdir/iuup/iuup_test.err > experr
 | 
				
			||||||
 | 
					AT_CHECK([$abs_top_builddir/tests/iuup/iuup_test], [], [expout], [experr])
 | 
				
			||||||
 | 
					AT_CLEANUP
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user