mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-10-30 19:43:52 +00:00 
			
		
		
		
	Compare commits
	
		
			29 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | fbe05d8b1f | ||
|  | 832bcdf631 | ||
|  | 73f9c02f49 | ||
|  | 6a25a61142 | ||
|  | 1dbbed169a | ||
|  | a8f27abe12 | ||
|  | ca2aec0235 | ||
|  | e827831514 | ||
|  | 923d60bb12 | ||
|  | f2bf8dc8c8 | ||
|  | 055ded74de | ||
|  | 3ff71284fa | ||
|  | cc0b97e197 | ||
|  | 8c69e29820 | ||
|  | 843d9038ce | ||
|  | d071a30238 | ||
|  | 3ab8ca4d84 | ||
|  | 23f4048b57 | ||
|  | 401b740ccd | ||
|  | a468b0f57f | ||
|  | b0cfa7272e | ||
|  | 683e05f60b | ||
|  | 16b637bf1b | ||
|  | 2698540c1e | ||
|  | d2f5e69d3e | ||
|  | ce64f18587 | ||
|  | 667fa59b0c | ||
|  | 5a6220f43b | ||
|  | 7c6dd3c2c3 | 
| @@ -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 | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ arguments: | |||||||
| *-h, --help*:: | *-h, --help*:: | ||||||
| 	Print a short help message about the supported options | 	Print a short help message about the supported options | ||||||
| *-V, --version*:: | *-V, --version*:: | ||||||
| 	Print the compile-time version number of the OsmoBTS program | 	Print the compile-time version number of the program | ||||||
| *-D, --daemonize*:: | *-D, --daemonize*:: | ||||||
| 	Fork the process as a daemon into background. | 	Fork the process as a daemon into background. | ||||||
| *-c, --config-file 'CONFIGFILE'*:: | *-c, --config-file 'CONFIGFILE'*:: | ||||||
|   | |||||||
| @@ -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); | ||||||
|   | |||||||
| @@ -29,6 +29,9 @@ void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci, | |||||||
| 			      uint32_t event_success, uint32_t event_failure, | 			      uint32_t event_success, uint32_t event_failure, | ||||||
| 			      void *notify_data); | 			      void *notify_data); | ||||||
|  |  | ||||||
|  | void osmo_mgcpc_ep_cancel_notify(struct osmo_mgcpc_ep *ep, struct osmo_fsm_inst *notify); | ||||||
|  | struct osmo_mgcpc_ep *osmo_mgcpc_ep_ci_ep(struct osmo_mgcpc_ep_ci *ci); | ||||||
|  |  | ||||||
| /*! Dispatch a DLCX for the given connection. | /*! Dispatch a DLCX for the given connection. | ||||||
|  * \param ci  Connection identifier as obtained from osmo_mgcpc_ep_ci_add(). |  * \param ci  Connection identifier as obtained from osmo_mgcpc_ep_ci_add(). | ||||||
|  */ |  */ | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| #!/usr/bin/env python | #!/usr/bin/env python3 | ||||||
|  |  | ||||||
| # (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com> | # (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com> | ||||||
| # This program is free software: you can redistribute it and/or modify | # This program is free software: you can redistribute it and/or modify | ||||||
|   | |||||||
| @@ -337,49 +337,35 @@ 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; |  | ||||||
| 	} else if (strstr(line, "rtpmap")) { |  | ||||||
| 		if (sscanf(line, "a=rtpmap:%d %63s", &pt, codec_resp) == 2) { |  | ||||||
| 			/* The MGW may assign an own payload type in the |  | ||||||
| 			 * response if the choosen codec falls into the IANA |  | ||||||
| 			 * assigned dynamic payload type range (96-127). |  | ||||||
| 			 * Normally the MGW should obey the 3gpp payload type |  | ||||||
| 			 * assignments, which are fixed, so we likely wont see |  | ||||||
| 			 * anything unexpected here. In order to be sure that |  | ||||||
| 			 * we will now check the codec string and if the result |  | ||||||
| 			 * does not match to what is IANA / 3gpp assigned, we |  | ||||||
| 			 * will create an entry in the ptmap table so we can |  | ||||||
| 			 * lookup later what has been assigned. */ |  | ||||||
| 			codec = map_str_to_codec(codec_resp); |  | ||||||
| 			if (codec != pt) { |  | ||||||
| 				if (r->ptmap_len < ARRAY_SIZE(r->ptmap)) { |  | ||||||
| 					r->ptmap[r->ptmap_len].pt = pt; |  | ||||||
| 					r->ptmap[r->ptmap_len].codec = codec; |  | ||||||
| 					r->ptmap_len++; |  | ||||||
| 				} else |  | ||||||
| 					goto response_parse_failure_rtpmap; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 		} else |  | ||||||
| 			goto response_parse_failure_rtpmap; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
|  |  | ||||||
| response_parse_failure_ptime: |  | ||||||
| 			LOGP(DLMGCP, LOGL_ERROR, | 			LOGP(DLMGCP, LOGL_ERROR, | ||||||
| 			     "Failed to parse SDP parameter, invalid ptime (%s)\n", line); | 			     "Failed to parse SDP parameter, invalid ptime (%s)\n", line); | ||||||
| 			return -EINVAL; | 			return -EINVAL; | ||||||
| response_parse_failure_rtpmap: | 		} | ||||||
|  | 	} else if (osmo_str_startswith(line, A_RTPMAP)) { | ||||||
|  | 		if (sscanf(line, A_RTPMAP "%d %63s", &pt, codec_resp) != 2) { | ||||||
| 			LOGP(DLMGCP, LOGL_ERROR, | 			LOGP(DLMGCP, LOGL_ERROR, | ||||||
| 	     "Failed to parse SDP parameter, invalid rtpmap (%s)\n", line); | 			     "Failed to parse SDP parameter, invalid rtpmap: %s\n", osmo_quote_str(line, -1)); | ||||||
| 			return -EINVAL; | 			return -EINVAL; | ||||||
| 		} | 		} | ||||||
|  | 		if (r->ptmap_len >= ARRAY_SIZE(r->ptmap)) { | ||||||
|  | 			LOGP(DLMGCP, LOGL_ERROR, "No more space in ptmap array (len=%u)\n", r->ptmap_len); | ||||||
|  | 			return -ENOSPC; | ||||||
|  | 		} | ||||||
|  | 		codec = map_str_to_codec(codec_resp); | ||||||
|  | 		r->ptmap[r->ptmap_len].pt = pt; | ||||||
|  | 		r->ptmap[r->ptmap_len].codec = codec; | ||||||
|  | 		r->ptmap_len++; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* Parse a line like "c=IN IP4 10.11.12.13" */ | /* Parse a line like "c=IN IP4 10.11.12.13" */ | ||||||
| static int mgcp_parse_audio_ip(struct mgcp_response *r, const char *line) | static int mgcp_parse_audio_ip(struct mgcp_response *r, const char *line) | ||||||
| @@ -480,9 +466,8 @@ int mgcp_response_parse_params(struct mgcp_response *r) | |||||||
| 	/* Find beginning of the parameter (SDP) section */ | 	/* Find beginning of the parameter (SDP) section */ | ||||||
| 	data_ptr = mgcp_find_section_end(data); | 	data_ptr = mgcp_find_section_end(data); | ||||||
| 	if (!data_ptr) { | 	if (!data_ptr) { | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, | 		LOGP(DLMGCP, LOGL_DEBUG, "MGCP response contains no SDP parameters\n"); | ||||||
| 		     "MGCP response: cannot find start of SDP parameters\n"); | 		rc = 0; | ||||||
| 		rc = -EINVAL; |  | ||||||
| 		goto exit; | 		goto exit; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -73,6 +73,14 @@ enum osmo_mgcpc_ep_fsm_event { | |||||||
|  |  | ||||||
| static struct osmo_fsm osmo_mgcpc_ep_fsm; | static struct osmo_fsm osmo_mgcpc_ep_fsm; | ||||||
|  |  | ||||||
|  | struct fsm_notify { | ||||||
|  | 	struct llist_head entry; | ||||||
|  | 	struct osmo_fsm_inst *fi; | ||||||
|  | 	uint32_t success; | ||||||
|  | 	uint32_t failure; | ||||||
|  | 	void *data; | ||||||
|  | }; | ||||||
|  |  | ||||||
| /*! One connection on an endpoint, corresponding to a connection identifier (CI) as returned by the MGW. | /*! One connection on an endpoint, corresponding to a connection identifier (CI) as returned by the MGW. | ||||||
|  * An endpoint has a fixed number of slots of these, which may or may not be in use. |  * An endpoint has a fixed number of slots of these, which may or may not be in use. | ||||||
|  */ |  */ | ||||||
| @@ -87,10 +95,7 @@ struct osmo_mgcpc_ep_ci { | |||||||
| 	bool sent; | 	bool sent; | ||||||
| 	enum mgcp_verb verb; | 	enum mgcp_verb verb; | ||||||
| 	struct mgcp_conn_peer verb_info; | 	struct mgcp_conn_peer verb_info; | ||||||
| 	struct osmo_fsm_inst *notify; | 	struct fsm_notify notify; | ||||||
| 	uint32_t notify_success; |  | ||||||
| 	uint32_t notify_failure; |  | ||||||
| 	void *notify_data; |  | ||||||
|  |  | ||||||
| 	bool got_port_info; | 	bool got_port_info; | ||||||
| 	struct mgcp_conn_peer rtp_info; | 	struct mgcp_conn_peer rtp_info; | ||||||
| @@ -118,6 +123,10 @@ struct osmo_mgcpc_ep { | |||||||
| 	/*! Endpoint connection slots. Note that each connection has its own set of FSM event numbers to signal success | 	/*! Endpoint connection slots. Note that each connection has its own set of FSM event numbers to signal success | ||||||
| 	 * and failure, depending on its index within this array. See CI_EV_SUCCESS and CI_EV_FAILURE. */ | 	 * and failure, depending on its index within this array. See CI_EV_SUCCESS and CI_EV_FAILURE. */ | ||||||
| 	struct osmo_mgcpc_ep_ci ci[USABLE_CI]; | 	struct osmo_mgcpc_ep_ci ci[USABLE_CI]; | ||||||
|  |  | ||||||
|  | 	/*! Internal use: if a function keeps an fsm_notify for later dispatch while already clearing or re-using the | ||||||
|  | 	 * ci[], the fsm_notify should be kept here to also get canceled by osmo_mgcpc_ep_cancel_notify(). */ | ||||||
|  | 	struct llist_head background_notify; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const struct value_string osmo_mgcp_verb_names[] = { | const struct value_string osmo_mgcp_verb_names[] = { | ||||||
| @@ -246,6 +255,9 @@ struct osmo_mgcpc_ep *osmo_mgcpc_ep_fi_mgwep(struct osmo_fsm_inst *fi) | |||||||
|  * MGCP messages to set up the endpoint will be sent on the given mgcp_client, as soon as the first |  * MGCP messages to set up the endpoint will be sent on the given mgcp_client, as soon as the first | ||||||
|  * osmo_mgcpc_ep_ci_request() is invoked. |  * osmo_mgcpc_ep_ci_request() is invoked. | ||||||
|  * |  * | ||||||
|  |  * IMPORTANT: To avoid use-after-free problems, using this FSM requires use of deferred FSM deallocation using | ||||||
|  |  * osmo_fsm_set_dealloc_ctx(), e.g. using osmo_select_main_ctx(OTC_SELECT) with osmo_select_main_ctx() as main loop. | ||||||
|  |  * | ||||||
|  * A typical sequence of events would be: |  * A typical sequence of events would be: | ||||||
|  * |  * | ||||||
|  *    ep = osmo_mgcpc_ep_alloc(..., mgcp_client_rtpbridge_wildcard(client)); |  *    ep = osmo_mgcpc_ep_alloc(..., mgcp_client_rtpbridge_wildcard(client)); | ||||||
| @@ -296,6 +308,7 @@ struct osmo_mgcpc_ep *osmo_mgcpc_ep_alloc(struct osmo_fsm_inst *parent, uint32_t | |||||||
| 		.fi = fi, | 		.fi = fi, | ||||||
| 		.T_defs = T_defs, | 		.T_defs = T_defs, | ||||||
| 	}; | 	}; | ||||||
|  | 	INIT_LLIST_HEAD(&ep->background_notify); | ||||||
| 	fi->priv = ep; | 	fi->priv = ep; | ||||||
|  |  | ||||||
| 	va_start(ap, endpoint_str_fmt); | 	va_start(ap, endpoint_str_fmt); | ||||||
| @@ -354,24 +367,53 @@ static bool osmo_mgcpc_ep_fsm_check_state_chg_after_response(struct osmo_fsm_ins | |||||||
|  |  | ||||||
| static void on_failure(struct osmo_mgcpc_ep_ci *ci) | static void on_failure(struct osmo_mgcpc_ep_ci *ci) | ||||||
| { | { | ||||||
| 	struct osmo_fsm_inst *notify = ci->notify; | 	struct osmo_mgcpc_ep *ep = ci->ep; | ||||||
| 	uint32_t notify_failure = ci->notify_failure; | 	struct fsm_notify notify; | ||||||
| 	void *notify_data = ci->notify_data; | 	int i; | ||||||
|  |  | ||||||
| 	if (!ci->occupied) | 	if (!ci->occupied) | ||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
|  | 	/* When dispatching an event for this CI, the user may decide to trigger the next request for this conn right | ||||||
|  | 	 * away. So we must be ready with a cleared *ci. Store the notify separately and clear before dispatching. */ | ||||||
|  | 	notify = ci->notify; | ||||||
|  | 	/* Register the planned notification in ep->background_notify so we also catch any osmo_mgcpc_ep_cancel_notify() | ||||||
|  | 	 * that might be triggered between clearing the ci and actually dispatching the event. */ | ||||||
|  | 	llist_add(¬ify.entry, &ep->background_notify); | ||||||
|  |  | ||||||
| 	*ci = (struct osmo_mgcpc_ep_ci){ | 	*ci = (struct osmo_mgcpc_ep_ci){ | ||||||
| 		.ep = ci->ep, | 		.ep = ci->ep, | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
|  | 	/* An MGCP failure typically means the endpoint becomes unusable, cancel all pending request (except DLCX). | ||||||
|  | 	 * Particularly, if two CRCX were scheduled and the first fails, we must no longer dispatch the second CRCX. */ | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(ep->ci); i++) { | ||||||
|  | 		struct osmo_mgcpc_ep_ci *other_ci = &ep->ci[i]; | ||||||
|  | 		if (other_ci == ci) | ||||||
|  | 			continue; | ||||||
|  | 		if (!other_ci->occupied) | ||||||
|  | 			continue; | ||||||
|  | 		if (!other_ci->pending) | ||||||
|  | 			continue; | ||||||
|  | 		if (other_ci->sent) | ||||||
|  | 			continue; | ||||||
|  | 		if (other_ci->verb == MGCP_VERB_DLCX) | ||||||
|  | 			continue; | ||||||
|  | 		/* Just clear the pending request, don't fire more events than below. */ | ||||||
|  | 		other_ci->pending = false; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	/* If this check has terminated the FSM instance, don't fire any more events to prevent use-after-free problems. | 	/* If this check has terminated the FSM instance, don't fire any more events to prevent use-after-free problems. | ||||||
| 	 * The endpoint FSM does dispatch a term event to its parent, and everything should be cleaned like that. */ | 	 * The endpoint FSM does dispatch a term event to its parent, and everything should be cleaned like that. */ | ||||||
| 	if (!osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi)) | 	if (!osmo_mgcpc_ep_fsm_check_state_chg_after_response(ep->fi)) { | ||||||
|  | 		/* The ep has deallocated, no need to llist_del(¬ify.entry) here. */ | ||||||
| 		return; | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if (notify) | 	if (notify.fi) | ||||||
| 		osmo_fsm_inst_dispatch(notify, notify_failure, notify_data); | 		osmo_fsm_inst_dispatch(notify.fi, notify.failure, notify.data); | ||||||
|  |  | ||||||
|  | 	llist_del(¬ify.entry); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int update_endpoint_name(struct osmo_mgcpc_ep_ci *ci, const char *new_endpoint_name) | static int update_endpoint_name(struct osmo_mgcpc_ep_ci *ci, const char *new_endpoint_name) | ||||||
| @@ -452,10 +494,10 @@ static void on_success(struct osmo_mgcpc_ep_ci *ci, void *data) | |||||||
| 	LOG_CI(ci, LOGL_DEBUG, "received successful response to %s: RTP=%s%s\n", | 	LOG_CI(ci, LOGL_DEBUG, "received successful response to %s: RTP=%s%s\n", | ||||||
| 	       osmo_mgcp_verb_name(ci->verb), | 	       osmo_mgcp_verb_name(ci->verb), | ||||||
| 	       mgcp_conn_peer_name(ci->got_port_info? &ci->rtp_info : NULL), | 	       mgcp_conn_peer_name(ci->got_port_info? &ci->rtp_info : NULL), | ||||||
| 	       ci->notify ? "" : " (not sending a notification)"); | 	       ci->notify.fi ? "" : " (not sending a notification)"); | ||||||
|  |  | ||||||
| 	if (ci->notify) | 	if (ci->notify.fi) | ||||||
| 		osmo_fsm_inst_dispatch(ci->notify, ci->notify_success, ci->notify_data); | 		osmo_fsm_inst_dispatch(ci->notify.fi, ci->notify.success, ci->notify.data); | ||||||
|  |  | ||||||
| 	osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi); | 	osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi); | ||||||
| } | } | ||||||
| @@ -516,6 +558,11 @@ static const struct osmo_tdef_state_timeout osmo_mgcpc_ep_fsm_timeouts[32] = { | |||||||
| 				     ((struct osmo_mgcpc_ep*)fi->priv)->T_defs, 5) | 				     ((struct osmo_mgcpc_ep*)fi->priv)->T_defs, 5) | ||||||
|  |  | ||||||
| /*! Dispatch an actual CRCX/MDCX/DLCX message for this connection. | /*! Dispatch an actual CRCX/MDCX/DLCX message for this connection. | ||||||
|  |  * | ||||||
|  |  * If the 'notify' instance deallocates before it received a notification of event_success or event_failure, | ||||||
|  |  * osmo_mgcpc_ep_ci_cancel_notify() or osmo_mgcpc_ep_cancel_notify() must be called. It is not harmful to cancel | ||||||
|  |  * notification after an event has been received. | ||||||
|  |  * | ||||||
|  * \param ci  Connection identifier as obtained from osmo_mgcpc_ep_ci_add(). |  * \param ci  Connection identifier as obtained from osmo_mgcpc_ep_ci_add(). | ||||||
|  * \param verb  MGCP operation to dispatch. |  * \param verb  MGCP operation to dispatch. | ||||||
|  * \param verb_info  Parameters for the MGCP operation. |  * \param verb_info  Parameters for the MGCP operation. | ||||||
| @@ -564,16 +611,18 @@ void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci, | |||||||
| 		.occupied = true, | 		.occupied = true, | ||||||
| 		/* .pending = true follows below */ | 		/* .pending = true follows below */ | ||||||
| 		.verb = verb, | 		.verb = verb, | ||||||
| 		.notify = notify, | 		.notify = { | ||||||
| 		.notify_success = event_success, | 			.fi = notify, | ||||||
| 		.notify_failure = event_failure, | 			.success = event_success, | ||||||
| 		.notify_data = notify_data, | 			.failure = event_failure, | ||||||
|  | 			.data = notify_data, | ||||||
|  | 		} | ||||||
| 	}; | 	}; | ||||||
| 	osmo_strlcpy(cleared_ci.label, ci->label, sizeof(cleared_ci.label)); | 	osmo_strlcpy(cleared_ci.label, ci->label, sizeof(cleared_ci.label)); | ||||||
| 	osmo_strlcpy(cleared_ci.mgcp_ci_str, ci->mgcp_ci_str, sizeof(cleared_ci.mgcp_ci_str)); | 	osmo_strlcpy(cleared_ci.mgcp_ci_str, ci->mgcp_ci_str, sizeof(cleared_ci.mgcp_ci_str)); | ||||||
| 	*ci = cleared_ci; | 	*ci = cleared_ci; | ||||||
|  |  | ||||||
| 	LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify)); | 	LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify.fi)); | ||||||
|  |  | ||||||
| 	if (verb_info) | 	if (verb_info) | ||||||
| 		ci->verb_info = *verb_info; | 		ci->verb_info = *verb_info; | ||||||
| @@ -631,10 +680,39 @@ dispatch_error: | |||||||
| 		osmo_fsm_inst_dispatch(notify, event_failure, notify_data); | 		osmo_fsm_inst_dispatch(notify, event_failure, notify_data); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /*! No longer notify for any state changes for any conns of this endpoint. | ||||||
|  |  * Useful if the notify instance passed to osmo_mgcpc_ep_ci_request() is about to deallocate. | ||||||
|  |  * \param ep  The endpoint FSM instance. | ||||||
|  |  * \param notify  Which target to cancel notification for, if NULL cancel all notifications. */ | ||||||
|  | void osmo_mgcpc_ep_cancel_notify(struct osmo_mgcpc_ep *ep, struct osmo_fsm_inst *notify) | ||||||
|  | { | ||||||
|  | 	struct fsm_notify *n; | ||||||
|  | 	int i; | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(ep->ci); i++) { | ||||||
|  | 		struct osmo_mgcpc_ep_ci *ci = &ep->ci[i]; | ||||||
|  | 		if (!notify || ci->notify.fi == notify) | ||||||
|  | 			ci->notify.fi = NULL; | ||||||
|  | 	} | ||||||
|  | 	llist_for_each_entry(n, &ep->background_notify, entry) { | ||||||
|  | 		if (!notify || n->fi == notify) | ||||||
|  | 			n->fi = NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Return the osmo_mgcpc_ep that this conn belongs to. */ | ||||||
|  | struct osmo_mgcpc_ep *osmo_mgcpc_ep_ci_ep(struct osmo_mgcpc_ep_ci *conn) | ||||||
|  | { | ||||||
|  | 	if (!conn) | ||||||
|  | 		return NULL; | ||||||
|  | 	return conn->ep; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int send_verb(struct osmo_mgcpc_ep_ci *ci) | static int send_verb(struct osmo_mgcpc_ep_ci *ci) | ||||||
| { | { | ||||||
| 	int rc; | 	int rc; | ||||||
| 	struct osmo_mgcpc_ep *ep = ci->ep; | 	struct osmo_mgcpc_ep *ep = ci->ep; | ||||||
|  | 	struct fsm_notify notify; | ||||||
|  |  | ||||||
| 	if (!ci->occupied || !ci->pending || ci->sent) | 	if (!ci->occupied || !ci->pending || ci->sent) | ||||||
| 		return 0; | 		return 0; | ||||||
| @@ -673,11 +751,14 @@ static int send_verb(struct osmo_mgcpc_ep_ci *ci) | |||||||
| 		       osmo_mgcp_verb_name(ci->verb), ci->mgcp_ci_str); | 		       osmo_mgcp_verb_name(ci->verb), ci->mgcp_ci_str); | ||||||
| 		/* The way this is designed, we actually need to forget all about the ci right away. */ | 		/* The way this is designed, we actually need to forget all about the ci right away. */ | ||||||
| 		mgcp_conn_delete(ci->mgcp_client_fi); | 		mgcp_conn_delete(ci->mgcp_client_fi); | ||||||
| 		if (ci->notify) | 		notify = ci->notify; | ||||||
| 			osmo_fsm_inst_dispatch(ci->notify, ci->notify_success, ci->notify_data); |  | ||||||
| 		*ci = (struct osmo_mgcpc_ep_ci){ | 		*ci = (struct osmo_mgcpc_ep_ci){ | ||||||
| 			.ep = ep, | 			.ep = ep, | ||||||
| 		}; | 		}; | ||||||
|  | 		/* When dispatching an event for this CI, the user may decide to trigger the next request for this conn | ||||||
|  | 		 * right away. So we must be ready with a cleared *ci. */ | ||||||
|  | 		if (notify.fi) | ||||||
|  | 			osmo_fsm_inst_dispatch(notify.fi, notify.success, notify.data); | ||||||
| 		break; | 		break; | ||||||
|  |  | ||||||
| 	default: | 	default: | ||||||
| @@ -692,6 +773,7 @@ void osmo_mgcpc_ep_clear(struct osmo_mgcpc_ep *ep) | |||||||
| { | { | ||||||
| 	if (!ep) | 	if (!ep) | ||||||
| 		return; | 		return; | ||||||
|  | 	osmo_mgcpc_ep_cancel_notify(ep, NULL); | ||||||
| 	osmo_fsm_inst_term(ep->fi, OSMO_FSM_TERM_REGULAR, 0); | 	osmo_fsm_inst_term(ep->fi, OSMO_FSM_TERM_REGULAR, 0); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -525,7 +525,9 @@ static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca | |||||||
| 		LOGPFSML(fi, LOGL_ERROR, | 		LOGPFSML(fi, LOGL_ERROR, | ||||||
| 			 "MGW/DLCX: abrupt FSM termination with connections still present, sending unconditional DLCX...\n"); | 			 "MGW/DLCX: abrupt FSM termination with connections still present, sending unconditional DLCX...\n"); | ||||||
| 		msg = make_dlcx_msg(mgcp_ctx); | 		msg = make_dlcx_msg(mgcp_ctx); | ||||||
| 		OSMO_ASSERT(msg); | 		if (!msg) | ||||||
|  | 			LOGPFSML(fi, LOGL_ERROR, "MGW/DLCX: Error composing DLCX message\n"); | ||||||
|  | 		else | ||||||
| 			mgcp_client_tx(mgcp, msg, NULL, NULL); | 			mgcp_client_tx(mgcp, msg, NULL, NULL); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -604,7 +606,6 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm | |||||||
| 				       uint32_t parent_term_evt, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer) | 				       uint32_t parent_term_evt, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer) | ||||||
| { | { | ||||||
| 	struct mgcp_ctx *mgcp_ctx; | 	struct mgcp_ctx *mgcp_ctx; | ||||||
| 	static bool fsm_registered = false; |  | ||||||
| 	struct osmo_fsm_inst *fi; | 	struct osmo_fsm_inst *fi; | ||||||
| 	struct in_addr ip_test; | 	struct in_addr ip_test; | ||||||
|  |  | ||||||
| @@ -616,12 +617,6 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm | |||||||
| 	if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0) | 	if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0) | ||||||
| 		return NULL; | 		return NULL; | ||||||
|  |  | ||||||
| 	/* Register the fsm description (if not already done) */ |  | ||||||
| 	if (fsm_registered == false) { |  | ||||||
| 		osmo_fsm_register(&fsm_mgcp_client); |  | ||||||
| 		fsm_registered = true; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* Allocate and configure a new fsm instance */ | 	/* Allocate and configure a new fsm instance */ | ||||||
| 	fi = osmo_fsm_inst_alloc_child(&fsm_mgcp_client, parent_fi, parent_term_evt); | 	fi = osmo_fsm_inst_alloc_child(&fsm_mgcp_client, parent_fi, parent_term_evt); | ||||||
| 	OSMO_ASSERT(fi); | 	OSMO_ASSERT(fi); | ||||||
| @@ -706,6 +701,9 @@ void mgcp_conn_delete(struct osmo_fsm_inst *fi) | |||||||
|  |  | ||||||
| 	OSMO_ASSERT(mgcp_ctx); | 	OSMO_ASSERT(mgcp_ctx); | ||||||
|  |  | ||||||
|  | 	if (fi->proc.terminating) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
| 	/* Unlink FSM from parent */ | 	/* Unlink FSM from parent */ | ||||||
| 	osmo_fsm_inst_unlink_parent(fi, NULL); | 	osmo_fsm_inst_unlink_parent(fi, NULL); | ||||||
|  |  | ||||||
| @@ -743,3 +741,8 @@ const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info) | |||||||
| 		return "empty"; | 		return "empty"; | ||||||
| 	return buf; | 	return buf; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static __attribute__((constructor)) void osmo_mgcp_client_fsm_init() | ||||||
|  | { | ||||||
|  | 	OSMO_ASSERT(osmo_fsm_register(&fsm_mgcp_client) == 0); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -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,11 +249,16 @@ 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; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	/* Copy over optional codec parameters */ | 	/* Copy over optional codec parameters */ | ||||||
| 	if (param) { | 	if (param) { | ||||||
| @@ -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)); | ||||||
|  | 		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_in = rtp_hdr->payload_type; | ||||||
| 		pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in); | 		pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in); | ||||||
| 		if (pt_out < 0) | 		if (pt_out < 0) | ||||||
| 			return -EINVAL; | 			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); | ||||||
| @@ -640,8 +669,9 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, | |||||||
| /* There are different dialects used to format and transfer voice data. When | /* There are different dialects used to format and transfer voice data. When | ||||||
|  * 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) |  * Return 0 on sucess, negative on errors like invalid data length. */ | ||||||
|  | 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 +679,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 +702,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 +722,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); | ||||||
| @@ -735,31 +776,35 @@ static bool amr_oa_bwe_convert_indicated(struct mgcp_rtp_codec *codec) | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| /* Check if a given RTP with AMR payload for octet-aligned mode */ | /* Return whether an RTP packet with AMR payload is in octet-aligned mode. | ||||||
| static bool amr_oa_check(char *data, int len) |  * Return 0 if in bandwidth-efficient mode, 1 for octet-aligned mode, and negative if the RTP data is invalid. */ | ||||||
|  | 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 +822,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 +834,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 +858,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 +883,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 +924,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 +934,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 +944,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 +971,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 +982,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 +1074,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 +1082,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 +1131,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 +1157,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 +1181,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 +1205,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 +1240,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 +1262,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 +1345,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 +1435,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); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -919,7 +919,7 @@ mgcp_header_done: | |||||||
| 	endp->callid = talloc_strdup(tcfg->endpoints, callid); | 	endp->callid = talloc_strdup(tcfg->endpoints, callid); | ||||||
|  |  | ||||||
| 	snprintf(conn_name, sizeof(conn_name), "%s", callid); | 	snprintf(conn_name, sizeof(conn_name), "%s", callid); | ||||||
| 	_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP, conn_name); | 	_conn = mgcp_conn_alloc(tcfg->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name); | ||||||
| 	if (!_conn) { | 	if (!_conn) { | ||||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||||
| 			 "CRCX: unable to allocate RTP connection\n"); | 			 "CRCX: unable to allocate RTP connection\n"); | ||||||
|   | |||||||
| @@ -132,6 +132,10 @@ static void handle_options(int argc, char **argv) | |||||||
| 			break; | 			break; | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
|  | 	if (argc > optind) { | ||||||
|  | 		fprintf(stderr, "Unsupported positional arguments on command line\n"); | ||||||
|  | 		exit(2); | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Callback function to be called when the RSIP ("Reset in Progress") mgcp | /* Callback function to be called when the RSIP ("Reset in Progress") mgcp | ||||||
| @@ -244,6 +248,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> | ||||||
| @@ -641,6 +642,13 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp) | |||||||
| 	return real_clock_gettime(clk_id, tp); | 	return real_clock_gettime(clk_id, tp); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void mgcp_endpoints_release(struct mgcp_trunk_config *trunk) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 	for (i = 1; i < trunk->number_endpoints; i++) | ||||||
|  | 		mgcp_endp_release(&trunk->endpoints[i]); | ||||||
|  | } | ||||||
|  |  | ||||||
| #define CONN_UNMODIFIED (0x1000) | #define CONN_UNMODIFIED (0x1000) | ||||||
|  |  | ||||||
| static void test_values(void) | static void test_values(void) | ||||||
| @@ -742,6 +750,7 @@ static void test_messages(void) | |||||||
| { | { | ||||||
| 	struct mgcp_config *cfg; | 	struct mgcp_config *cfg; | ||||||
| 	struct mgcp_endpoint *endp; | 	struct mgcp_endpoint *endp; | ||||||
|  | 	struct mgcp_trunk_config *trunk2; | ||||||
| 	int i; | 	int i; | ||||||
| 	struct mgcp_conn_rtp *conn = NULL; | 	struct mgcp_conn_rtp *conn = NULL; | ||||||
| 	char last_conn_id[256]; | 	char last_conn_id[256]; | ||||||
| @@ -755,7 +764,8 @@ static void test_messages(void) | |||||||
|  |  | ||||||
| 	memset(last_conn_id, 0, sizeof(last_conn_id)); | 	memset(last_conn_id, 0, sizeof(last_conn_id)); | ||||||
|  |  | ||||||
| 	mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); | 	trunk2 = mgcp_trunk_alloc(cfg, 1); | ||||||
|  | 	mgcp_endpoints_allocate(trunk2); | ||||||
|  |  | ||||||
| 	for (i = 0; i < ARRAY_SIZE(tests); i++) { | 	for (i = 0; i < ARRAY_SIZE(tests); i++) { | ||||||
| 		const struct mgcp_test *t = &tests[i]; | 		const struct mgcp_test *t = &tests[i]; | ||||||
| @@ -873,12 +883,15 @@ static void test_messages(void) | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	mgcp_endpoints_release(trunk2); | ||||||
|  | 	mgcp_endpoints_release(&cfg->trunk); | ||||||
| 	talloc_free(cfg); | 	talloc_free(cfg); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void test_retransmission(void) | static void test_retransmission(void) | ||||||
| { | { | ||||||
| 	struct mgcp_config *cfg; | 	struct mgcp_config *cfg; | ||||||
|  | 	struct mgcp_trunk_config *trunk2; | ||||||
| 	int i; | 	int i; | ||||||
| 	char last_conn_id[256]; | 	char last_conn_id[256]; | ||||||
| 	int rc; | 	int rc; | ||||||
| @@ -890,7 +903,8 @@ static void test_retransmission(void) | |||||||
|  |  | ||||||
| 	memset(last_conn_id, 0, sizeof(last_conn_id)); | 	memset(last_conn_id, 0, sizeof(last_conn_id)); | ||||||
|  |  | ||||||
| 	mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); | 	trunk2 = mgcp_trunk_alloc(cfg, 1); | ||||||
|  | 	mgcp_endpoints_allocate(trunk2); | ||||||
|  |  | ||||||
| 	for (i = 0; i < ARRAY_SIZE(retransmit); i++) { | 	for (i = 0; i < ARRAY_SIZE(retransmit); i++) { | ||||||
| 		const struct mgcp_test *t = &retransmit[i]; | 		const struct mgcp_test *t = &retransmit[i]; | ||||||
| @@ -930,6 +944,8 @@ static void test_retransmission(void) | |||||||
| 		msgb_free(msg); | 		msgb_free(msg); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	mgcp_endpoints_release(trunk2); | ||||||
|  | 	mgcp_endpoints_release(&cfg->trunk); | ||||||
| 	talloc_free(cfg); | 	talloc_free(cfg); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -943,6 +959,7 @@ static int rqnt_cb(struct mgcp_endpoint *endp, char _tone) | |||||||
| static void test_rqnt_cb(void) | static void test_rqnt_cb(void) | ||||||
| { | { | ||||||
| 	struct mgcp_config *cfg; | 	struct mgcp_config *cfg; | ||||||
|  | 	struct mgcp_trunk_config *trunk2; | ||||||
| 	struct msgb *inp, *msg; | 	struct msgb *inp, *msg; | ||||||
| 	char conn_id[256]; | 	char conn_id[256]; | ||||||
|  |  | ||||||
| @@ -952,7 +969,8 @@ static void test_rqnt_cb(void) | |||||||
| 	cfg->trunk.vty_number_endpoints = 64; | 	cfg->trunk.vty_number_endpoints = 64; | ||||||
| 	mgcp_endpoints_allocate(&cfg->trunk); | 	mgcp_endpoints_allocate(&cfg->trunk); | ||||||
|  |  | ||||||
| 	mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); | 	trunk2 = mgcp_trunk_alloc(cfg, 1); | ||||||
|  | 	mgcp_endpoints_allocate(trunk2); | ||||||
|  |  | ||||||
| 	inp = create_msg(CRCX, NULL); | 	inp = create_msg(CRCX, NULL); | ||||||
| 	msg = mgcp_handle_message(cfg, inp); | 	msg = mgcp_handle_message(cfg, inp); | ||||||
| @@ -981,6 +999,8 @@ static void test_rqnt_cb(void) | |||||||
| 	inp = create_msg(DLCX, conn_id); | 	inp = create_msg(DLCX, conn_id); | ||||||
| 	msgb_free(mgcp_handle_message(cfg, inp)); | 	msgb_free(mgcp_handle_message(cfg, inp)); | ||||||
| 	msgb_free(inp); | 	msgb_free(inp); | ||||||
|  | 	mgcp_endpoints_release(trunk2); | ||||||
|  | 	mgcp_endpoints_release(&cfg->trunk); | ||||||
| 	talloc_free(cfg); | 	talloc_free(cfg); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1237,7 +1257,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 +1269,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 +1316,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 +1353,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; | ||||||
| @@ -1342,12 +1364,12 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) | |||||||
| static void test_multilple_codec(void) | static void test_multilple_codec(void) | ||||||
| { | { | ||||||
| 	struct mgcp_config *cfg; | 	struct mgcp_config *cfg; | ||||||
|  | 	struct mgcp_trunk_config *trunk2; | ||||||
| 	struct mgcp_endpoint *endp; | 	struct mgcp_endpoint *endp; | ||||||
| 	struct msgb *inp, *resp; | 	struct msgb *inp, *resp; | ||||||
| 	struct in_addr addr; | 	struct in_addr addr; | ||||||
| 	struct mgcp_conn_rtp *conn = NULL; | 	struct mgcp_conn_rtp *conn = NULL; | ||||||
| 	char conn_id[256]; | 	char conn_id[256]; | ||||||
| 	int i; |  | ||||||
|  |  | ||||||
| 	printf("Testing multiple payload types\n"); | 	printf("Testing multiple payload types\n"); | ||||||
|  |  | ||||||
| @@ -1355,7 +1377,9 @@ static void test_multilple_codec(void) | |||||||
| 	cfg->trunk.vty_number_endpoints = 64; | 	cfg->trunk.vty_number_endpoints = 64; | ||||||
| 	mgcp_endpoints_allocate(&cfg->trunk); | 	mgcp_endpoints_allocate(&cfg->trunk); | ||||||
| 	cfg->policy_cb = mgcp_test_policy_cb; | 	cfg->policy_cb = mgcp_test_policy_cb; | ||||||
| 	mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); |  | ||||||
|  | 	trunk2 = mgcp_trunk_alloc(cfg, 1); | ||||||
|  | 	mgcp_endpoints_allocate(trunk2); | ||||||
|  |  | ||||||
| 	/* Allocate endpoint 1@mgw with two codecs */ | 	/* Allocate endpoint 1@mgw with two codecs */ | ||||||
| 	last_endpoint = -1; | 	last_endpoint = -1; | ||||||
| @@ -1481,9 +1505,8 @@ static void test_multilple_codec(void) | |||||||
| 	OSMO_ASSERT(conn); | 	OSMO_ASSERT(conn); | ||||||
| 	OSMO_ASSERT(conn->end.codec->payload_type == 0); | 	OSMO_ASSERT(conn->end.codec->payload_type == 0); | ||||||
|  |  | ||||||
| 	for (i = 1; i < cfg->trunk.number_endpoints; i++) | 	mgcp_endpoints_release(trunk2); | ||||||
| 		mgcp_endp_release(&cfg->trunk.endpoints[i]); | 	mgcp_endpoints_release(&cfg->trunk); | ||||||
|  |  | ||||||
| 	talloc_free(cfg); | 	talloc_free(cfg); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1532,12 +1555,13 @@ static void test_no_cycle(void) | |||||||
| 	OSMO_ASSERT(conn->state.stats.cycles == UINT16_MAX + 1); | 	OSMO_ASSERT(conn->state.stats.cycles == UINT16_MAX + 1); | ||||||
| 	OSMO_ASSERT(conn->state.stats.max_seq == 0); | 	OSMO_ASSERT(conn->state.stats.max_seq == 0); | ||||||
|  |  | ||||||
| 	mgcp_endp_release(endp); | 	mgcp_endpoints_release(&cfg->trunk); | ||||||
| 	talloc_free(cfg); | 	talloc_free(cfg); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void test_no_name(void) | static void test_no_name(void) | ||||||
| { | { | ||||||
|  | 	struct mgcp_trunk_config *trunk2; | ||||||
| 	struct mgcp_config *cfg; | 	struct mgcp_config *cfg; | ||||||
| 	struct msgb *inp, *msg; | 	struct msgb *inp, *msg; | ||||||
|  |  | ||||||
| @@ -1550,7 +1574,8 @@ static void test_no_name(void) | |||||||
|  |  | ||||||
| 	cfg->policy_cb = mgcp_test_policy_cb; | 	cfg->policy_cb = mgcp_test_policy_cb; | ||||||
|  |  | ||||||
| 	mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); | 	trunk2 = mgcp_trunk_alloc(cfg, 1); | ||||||
|  | 	mgcp_endpoints_allocate(trunk2); | ||||||
|  |  | ||||||
| 	inp = create_msg(CRCX, NULL); | 	inp = create_msg(CRCX, NULL); | ||||||
| 	msg = mgcp_handle_message(cfg, inp); | 	msg = mgcp_handle_message(cfg, inp); | ||||||
| @@ -1563,7 +1588,8 @@ static void test_no_name(void) | |||||||
| 	msgb_free(inp); | 	msgb_free(inp); | ||||||
| 	msgb_free(msg); | 	msgb_free(msg); | ||||||
|  |  | ||||||
| 	mgcp_endp_release(&cfg->trunk.endpoints[1]); | 	mgcp_endpoints_release(trunk2); | ||||||
|  | 	mgcp_endpoints_release(&cfg->trunk); | ||||||
| 	talloc_free(cfg); | 	talloc_free(cfg); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1711,98 +1737,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, | ||||||
|  | 	.amr_octet_aligned = true, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | 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[] = { | ||||||
| 	{ | 	{ | ||||||
| 	c->rate = 8000; | 		.descr = "same order, but differing payload type numbers", | ||||||
| 	c->channels = 1; | 		.codecs = { | ||||||
| 	c->frame_duration_num = 23; | 			{ | ||||||
| 	c->frame_duration_den = 42; | 				{ 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 | ||||||
|   | |||||||
| @@ -360,7 +360,7 @@ struct sdp_section_start_test { | |||||||
| static struct sdp_section_start_test sdp_section_start_tests[] = { | static struct sdp_section_start_test sdp_section_start_tests[] = { | ||||||
| 	{ | 	{ | ||||||
| 		.body = "", | 		.body = "", | ||||||
| 		.expect_rc = -EINVAL, | 		.expect_rc = 0, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		.body = "\n\n", | 		.body = "\n\n", | ||||||
| @@ -399,19 +399,19 @@ static struct sdp_section_start_test sdp_section_start_tests[] = { | |||||||
| 		.body = "some mgcp header data\r\nand header params" | 		.body = "some mgcp header data\r\nand header params" | ||||||
| 			"\n\r\n" | 			"\n\r\n" | ||||||
| 			"m=audio 23\r\n", | 			"m=audio 23\r\n", | ||||||
| 		.expect_rc = -EINVAL, | 		.expect_rc = 0, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		.body = "some mgcp header data\r\nand header params" | 		.body = "some mgcp header data\r\nand header params" | ||||||
| 			"\r\n\r" | 			"\r\n\r" | ||||||
| 			"m=audio 23\r\n", | 			"m=audio 23\r\n", | ||||||
| 		.expect_rc = -EINVAL, | 		.expect_rc = 0, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		.body = "some mgcp header data\r\nand header params" | 		.body = "some mgcp header data\r\nand header params" | ||||||
| 			"\n\r\r" | 			"\n\r\r" | ||||||
| 			"m=audio 23\r\n", | 			"m=audio 23\r\n", | ||||||
| 		.expect_rc = -EINVAL, | 		.expect_rc = 0, | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,8 +17,9 @@ test_mgcp_client_cancel() done | |||||||
|  |  | ||||||
| test_sdp_section_start() test [0]: | test_sdp_section_start() test [0]: | ||||||
| body: "" | body: "" | ||||||
| DLMGCP MGCP response: cannot find start of SDP parameters | DLMGCP MGCP response contains no SDP parameters | ||||||
| got rc=-22 | got rc=0 | ||||||
|  | got audio_port=0 | ||||||
|  |  | ||||||
| test_sdp_section_start() test [1]: | test_sdp_section_start() test [1]: | ||||||
| body: "\n\n" | body: "\n\n" | ||||||
| @@ -52,18 +53,21 @@ got audio_port=23 | |||||||
|  |  | ||||||
| test_sdp_section_start() test [7]: | test_sdp_section_start() test [7]: | ||||||
| body: "some mgcp header data\r\nand header params\n\r\nm=audio 23\r\n" | body: "some mgcp header data\r\nand header params\n\r\nm=audio 23\r\n" | ||||||
| DLMGCP MGCP response: cannot find start of SDP parameters | DLMGCP MGCP response contains no SDP parameters | ||||||
| got rc=-22 | got rc=0 | ||||||
|  | got audio_port=0 | ||||||
|  |  | ||||||
| test_sdp_section_start() test [8]: | test_sdp_section_start() test [8]: | ||||||
| body: "some mgcp header data\r\nand header params\r\n\rm=audio 23\r\n" | body: "some mgcp header data\r\nand header params\r\n\rm=audio 23\r\n" | ||||||
| DLMGCP MGCP response: cannot find start of SDP parameters | DLMGCP MGCP response contains no SDP parameters | ||||||
| got rc=-22 | got rc=0 | ||||||
|  | got audio_port=0 | ||||||
|  |  | ||||||
| test_sdp_section_start() test [9]: | test_sdp_section_start() test [9]: | ||||||
| body: "some mgcp header data\r\nand header params\n\r\rm=audio 23\r\n" | body: "some mgcp header data\r\nand header params\n\r\rm=audio 23\r\n" | ||||||
| DLMGCP MGCP response: cannot find start of SDP parameters | DLMGCP MGCP response contains no SDP parameters | ||||||
| got rc=-22 | got rc=0 | ||||||
|  | got audio_port=0 | ||||||
| DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2 | DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2 | ||||||
| DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100 | DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100 | ||||||
| DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2 | DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2 | ||||||
|   | |||||||
| @@ -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