mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-10-31 12:03:50 +00:00 
			
		
		
		
	Compare commits
	
		
			25 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | a02faff1ac | ||
|  | ce055d5ac4 | ||
|  | 452f2ba5bd | ||
|  | 0b6faa4521 | ||
|  | 1c69fb0f14 | ||
|  | 069dd16b67 | ||
|  | e144275757 | ||
|  | e0058b7207 | ||
|  | bb3ccdea1a | ||
|  | 2799ff9bb9 | ||
|  | fed5feafd3 | ||
|  | 1de5ed6f97 | ||
|  | 80751d850b | ||
|  | 2c40164ff0 | ||
|  | e308202285 | ||
|  | b4a067c9fb | ||
|  | 8029b12146 | ||
|  | ebb05c1f90 | ||
|  | bc3f3b40fe | ||
|  | fae09f4562 | ||
|  | 7c0fe31697 | ||
|  | 98aef217c6 | ||
|  | e03e34f8bb | ||
|  | ee6958c9a8 | ||
|  | fbcf4a6f6c | 
							
								
								
									
										445
									
								
								.clang-format
									
									
									
									
									
								
							
							
						
						
									
										445
									
								
								.clang-format
									
									
									
									
									
								
							| @@ -64,451 +64,6 @@ DisableFormat: false | |||||||
| ExperimentalAutoDetectBinPacking: false | ExperimentalAutoDetectBinPacking: false | ||||||
| #FixNamespaceComments: false # Unknown to clang-format-4.0 | #FixNamespaceComments: false # Unknown to clang-format-4.0 | ||||||
|  |  | ||||||
| # Taken from: |  | ||||||
| #   git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \ |  | ||||||
| #   | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$,  - '\1'," \ |  | ||||||
| #   | sort | uniq |  | ||||||
| ForEachMacros: |  | ||||||
|   - 'apei_estatus_for_each_section' |  | ||||||
|   - 'ata_for_each_dev' |  | ||||||
|   - 'ata_for_each_link' |  | ||||||
|   - '__ata_qc_for_each' |  | ||||||
|   - 'ata_qc_for_each' |  | ||||||
|   - 'ata_qc_for_each_raw' |  | ||||||
|   - 'ata_qc_for_each_with_internal' |  | ||||||
|   - 'ax25_for_each' |  | ||||||
|   - 'ax25_uid_for_each' |  | ||||||
|   - '__bio_for_each_bvec' |  | ||||||
|   - 'bio_for_each_bvec' |  | ||||||
|   - 'bio_for_each_bvec_all' |  | ||||||
|   - 'bio_for_each_integrity_vec' |  | ||||||
|   - '__bio_for_each_segment' |  | ||||||
|   - 'bio_for_each_segment' |  | ||||||
|   - 'bio_for_each_segment_all' |  | ||||||
|   - 'bio_list_for_each' |  | ||||||
|   - 'bip_for_each_vec' |  | ||||||
|   - 'bitmap_for_each_clear_region' |  | ||||||
|   - 'bitmap_for_each_set_region' |  | ||||||
|   - 'blkg_for_each_descendant_post' |  | ||||||
|   - 'blkg_for_each_descendant_pre' |  | ||||||
|   - 'blk_queue_for_each_rl' |  | ||||||
|   - 'bond_for_each_slave' |  | ||||||
|   - 'bond_for_each_slave_rcu' |  | ||||||
|   - 'bpf_for_each_spilled_reg' |  | ||||||
|   - 'btree_for_each_safe128' |  | ||||||
|   - 'btree_for_each_safe32' |  | ||||||
|   - 'btree_for_each_safe64' |  | ||||||
|   - 'btree_for_each_safel' |  | ||||||
|   - 'card_for_each_dev' |  | ||||||
|   - 'cgroup_taskset_for_each' |  | ||||||
|   - 'cgroup_taskset_for_each_leader' |  | ||||||
|   - 'cpufreq_for_each_entry' |  | ||||||
|   - 'cpufreq_for_each_entry_idx' |  | ||||||
|   - 'cpufreq_for_each_valid_entry' |  | ||||||
|   - 'cpufreq_for_each_valid_entry_idx' |  | ||||||
|   - 'css_for_each_child' |  | ||||||
|   - 'css_for_each_descendant_post' |  | ||||||
|   - 'css_for_each_descendant_pre' |  | ||||||
|   - 'device_for_each_child_node' |  | ||||||
|   - 'displayid_iter_for_each' |  | ||||||
|   - 'dma_fence_chain_for_each' |  | ||||||
|   - 'do_for_each_ftrace_op' |  | ||||||
|   - 'drm_atomic_crtc_for_each_plane' |  | ||||||
|   - 'drm_atomic_crtc_state_for_each_plane' |  | ||||||
|   - 'drm_atomic_crtc_state_for_each_plane_state' |  | ||||||
|   - 'drm_atomic_for_each_plane_damage' |  | ||||||
|   - 'drm_client_for_each_connector_iter' |  | ||||||
|   - 'drm_client_for_each_modeset' |  | ||||||
|   - 'drm_connector_for_each_possible_encoder' |  | ||||||
|   - 'drm_for_each_bridge_in_chain' |  | ||||||
|   - 'drm_for_each_connector_iter' |  | ||||||
|   - 'drm_for_each_crtc' |  | ||||||
|   - 'drm_for_each_crtc_reverse' |  | ||||||
|   - 'drm_for_each_encoder' |  | ||||||
|   - 'drm_for_each_encoder_mask' |  | ||||||
|   - 'drm_for_each_fb' |  | ||||||
|   - 'drm_for_each_legacy_plane' |  | ||||||
|   - 'drm_for_each_plane' |  | ||||||
|   - 'drm_for_each_plane_mask' |  | ||||||
|   - 'drm_for_each_privobj' |  | ||||||
|   - 'drm_mm_for_each_hole' |  | ||||||
|   - 'drm_mm_for_each_node' |  | ||||||
|   - 'drm_mm_for_each_node_in_range' |  | ||||||
|   - 'drm_mm_for_each_node_safe' |  | ||||||
|   - 'flow_action_for_each' |  | ||||||
|   - 'for_each_acpi_dev_match' |  | ||||||
|   - 'for_each_active_dev_scope' |  | ||||||
|   - 'for_each_active_drhd_unit' |  | ||||||
|   - 'for_each_active_iommu' |  | ||||||
|   - 'for_each_aggr_pgid' |  | ||||||
|   - 'for_each_available_child_of_node' |  | ||||||
|   - 'for_each_bio' |  | ||||||
|   - 'for_each_board_func_rsrc' |  | ||||||
|   - 'for_each_bvec' |  | ||||||
|   - 'for_each_card_auxs' |  | ||||||
|   - 'for_each_card_auxs_safe' |  | ||||||
|   - 'for_each_card_components' |  | ||||||
|   - 'for_each_card_dapms' |  | ||||||
|   - 'for_each_card_pre_auxs' |  | ||||||
|   - 'for_each_card_prelinks' |  | ||||||
|   - 'for_each_card_rtds' |  | ||||||
|   - 'for_each_card_rtds_safe' |  | ||||||
|   - 'for_each_card_widgets' |  | ||||||
|   - 'for_each_card_widgets_safe' |  | ||||||
|   - 'for_each_cgroup_storage_type' |  | ||||||
|   - 'for_each_child_of_node' |  | ||||||
|   - 'for_each_clear_bit' |  | ||||||
|   - 'for_each_clear_bit_from' |  | ||||||
|   - 'for_each_cmsghdr' |  | ||||||
|   - 'for_each_compatible_node' |  | ||||||
|   - 'for_each_component_dais' |  | ||||||
|   - 'for_each_component_dais_safe' |  | ||||||
|   - 'for_each_comp_order' |  | ||||||
|   - 'for_each_console' |  | ||||||
|   - 'for_each_cpu' |  | ||||||
|   - 'for_each_cpu_and' |  | ||||||
|   - 'for_each_cpu_not' |  | ||||||
|   - 'for_each_cpu_wrap' |  | ||||||
|   - 'for_each_dapm_widgets' |  | ||||||
|   - 'for_each_dev_addr' |  | ||||||
|   - 'for_each_dev_scope' |  | ||||||
|   - 'for_each_dma_cap_mask' |  | ||||||
|   - 'for_each_dpcm_be' |  | ||||||
|   - 'for_each_dpcm_be_rollback' |  | ||||||
|   - 'for_each_dpcm_be_safe' |  | ||||||
|   - 'for_each_dpcm_fe' |  | ||||||
|   - 'for_each_drhd_unit' |  | ||||||
|   - 'for_each_dss_dev' |  | ||||||
|   - 'for_each_dtpm_table' |  | ||||||
|   - 'for_each_efi_memory_desc' |  | ||||||
|   - 'for_each_efi_memory_desc_in_map' |  | ||||||
|   - 'for_each_element' |  | ||||||
|   - 'for_each_element_extid' |  | ||||||
|   - 'for_each_element_id' |  | ||||||
|   - 'for_each_endpoint_of_node' |  | ||||||
|   - 'for_each_evictable_lru' |  | ||||||
|   - 'for_each_fib6_node_rt_rcu' |  | ||||||
|   - 'for_each_fib6_walker_rt' |  | ||||||
|   - 'for_each_free_mem_pfn_range_in_zone' |  | ||||||
|   - 'for_each_free_mem_pfn_range_in_zone_from' |  | ||||||
|   - 'for_each_free_mem_range' |  | ||||||
|   - 'for_each_free_mem_range_reverse' |  | ||||||
|   - 'for_each_func_rsrc' |  | ||||||
|   - 'for_each_hstate' |  | ||||||
|   - 'for_each_if' |  | ||||||
|   - 'for_each_iommu' |  | ||||||
|   - 'for_each_ip_tunnel_rcu' |  | ||||||
|   - 'for_each_irq_nr' |  | ||||||
|   - 'for_each_link_codecs' |  | ||||||
|   - 'for_each_link_cpus' |  | ||||||
|   - 'for_each_link_platforms' |  | ||||||
|   - 'for_each_lru' |  | ||||||
|   - 'for_each_matching_node' |  | ||||||
|   - 'for_each_matching_node_and_match' |  | ||||||
|   - 'for_each_member' |  | ||||||
|   - 'for_each_memcg_cache_index' |  | ||||||
|   - 'for_each_mem_pfn_range' |  | ||||||
|   - '__for_each_mem_range' |  | ||||||
|   - 'for_each_mem_range' |  | ||||||
|   - '__for_each_mem_range_rev' |  | ||||||
|   - 'for_each_mem_range_rev' |  | ||||||
|   - 'for_each_mem_region' |  | ||||||
|   - 'for_each_migratetype_order' |  | ||||||
|   - 'for_each_msi_entry' |  | ||||||
|   - 'for_each_msi_entry_safe' |  | ||||||
|   - 'for_each_msi_vector' |  | ||||||
|   - 'for_each_net' |  | ||||||
|   - 'for_each_net_continue_reverse' |  | ||||||
|   - 'for_each_netdev' |  | ||||||
|   - 'for_each_netdev_continue' |  | ||||||
|   - 'for_each_netdev_continue_rcu' |  | ||||||
|   - 'for_each_netdev_continue_reverse' |  | ||||||
|   - 'for_each_netdev_feature' |  | ||||||
|   - 'for_each_netdev_in_bond_rcu' |  | ||||||
|   - 'for_each_netdev_rcu' |  | ||||||
|   - 'for_each_netdev_reverse' |  | ||||||
|   - 'for_each_netdev_safe' |  | ||||||
|   - 'for_each_net_rcu' |  | ||||||
|   - 'for_each_new_connector_in_state' |  | ||||||
|   - 'for_each_new_crtc_in_state' |  | ||||||
|   - 'for_each_new_mst_mgr_in_state' |  | ||||||
|   - 'for_each_new_plane_in_state' |  | ||||||
|   - 'for_each_new_private_obj_in_state' |  | ||||||
|   - 'for_each_node' |  | ||||||
|   - 'for_each_node_by_name' |  | ||||||
|   - 'for_each_node_by_type' |  | ||||||
|   - 'for_each_node_mask' |  | ||||||
|   - 'for_each_node_state' |  | ||||||
|   - 'for_each_node_with_cpus' |  | ||||||
|   - 'for_each_node_with_property' |  | ||||||
|   - 'for_each_nonreserved_multicast_dest_pgid' |  | ||||||
|   - 'for_each_of_allnodes' |  | ||||||
|   - 'for_each_of_allnodes_from' |  | ||||||
|   - 'for_each_of_cpu_node' |  | ||||||
|   - 'for_each_of_pci_range' |  | ||||||
|   - 'for_each_old_connector_in_state' |  | ||||||
|   - 'for_each_old_crtc_in_state' |  | ||||||
|   - 'for_each_old_mst_mgr_in_state' |  | ||||||
|   - 'for_each_oldnew_connector_in_state' |  | ||||||
|   - 'for_each_oldnew_crtc_in_state' |  | ||||||
|   - 'for_each_oldnew_mst_mgr_in_state' |  | ||||||
|   - 'for_each_oldnew_plane_in_state' |  | ||||||
|   - 'for_each_oldnew_plane_in_state_reverse' |  | ||||||
|   - 'for_each_oldnew_private_obj_in_state' |  | ||||||
|   - 'for_each_old_plane_in_state' |  | ||||||
|   - 'for_each_old_private_obj_in_state' |  | ||||||
|   - 'for_each_online_cpu' |  | ||||||
|   - 'for_each_online_node' |  | ||||||
|   - 'for_each_online_pgdat' |  | ||||||
|   - 'for_each_pci_bridge' |  | ||||||
|   - 'for_each_pci_dev' |  | ||||||
|   - 'for_each_pci_msi_entry' |  | ||||||
|   - 'for_each_pcm_streams' |  | ||||||
|   - 'for_each_physmem_range' |  | ||||||
|   - 'for_each_populated_zone' |  | ||||||
|   - 'for_each_possible_cpu' |  | ||||||
|   - 'for_each_present_cpu' |  | ||||||
|   - 'for_each_prime_number' |  | ||||||
|   - 'for_each_prime_number_from' |  | ||||||
|   - 'for_each_process' |  | ||||||
|   - 'for_each_process_thread' |  | ||||||
|   - 'for_each_prop_codec_conf' |  | ||||||
|   - 'for_each_prop_dai_codec' |  | ||||||
|   - 'for_each_prop_dai_cpu' |  | ||||||
|   - 'for_each_prop_dlc_codecs' |  | ||||||
|   - 'for_each_prop_dlc_cpus' |  | ||||||
|   - 'for_each_prop_dlc_platforms' |  | ||||||
|   - 'for_each_property_of_node' |  | ||||||
|   - 'for_each_registered_fb' |  | ||||||
|   - 'for_each_requested_gpio' |  | ||||||
|   - 'for_each_requested_gpio_in_range' |  | ||||||
|   - 'for_each_reserved_mem_range' |  | ||||||
|   - 'for_each_reserved_mem_region' |  | ||||||
|   - 'for_each_rtd_codec_dais' |  | ||||||
|   - 'for_each_rtd_components' |  | ||||||
|   - 'for_each_rtd_cpu_dais' |  | ||||||
|   - 'for_each_rtd_dais' |  | ||||||
|   - 'for_each_set_bit' |  | ||||||
|   - 'for_each_set_bit_from' |  | ||||||
|   - 'for_each_set_clump8' |  | ||||||
|   - 'for_each_sg' |  | ||||||
|   - 'for_each_sg_dma_page' |  | ||||||
|   - 'for_each_sg_page' |  | ||||||
|   - 'for_each_sgtable_dma_page' |  | ||||||
|   - 'for_each_sgtable_dma_sg' |  | ||||||
|   - 'for_each_sgtable_page' |  | ||||||
|   - 'for_each_sgtable_sg' |  | ||||||
|   - 'for_each_sibling_event' |  | ||||||
|   - 'for_each_subelement' |  | ||||||
|   - 'for_each_subelement_extid' |  | ||||||
|   - 'for_each_subelement_id' |  | ||||||
|   - '__for_each_thread' |  | ||||||
|   - 'for_each_thread' |  | ||||||
|   - 'for_each_unicast_dest_pgid' |  | ||||||
|   - 'for_each_vsi' |  | ||||||
|   - 'for_each_wakeup_source' |  | ||||||
|   - 'for_each_zone' |  | ||||||
|   - 'for_each_zone_zonelist' |  | ||||||
|   - 'for_each_zone_zonelist_nodemask' |  | ||||||
|   - 'fwnode_for_each_available_child_node' |  | ||||||
|   - 'fwnode_for_each_child_node' |  | ||||||
|   - 'fwnode_graph_for_each_endpoint' |  | ||||||
|   - 'gadget_for_each_ep' |  | ||||||
|   - 'genradix_for_each' |  | ||||||
|   - 'genradix_for_each_from' |  | ||||||
|   - 'hash_for_each' |  | ||||||
|   - 'hash_for_each_possible' |  | ||||||
|   - 'hash_for_each_possible_rcu' |  | ||||||
|   - 'hash_for_each_possible_rcu_notrace' |  | ||||||
|   - 'hash_for_each_possible_safe' |  | ||||||
|   - 'hash_for_each_rcu' |  | ||||||
|   - 'hash_for_each_safe' |  | ||||||
|   - 'hctx_for_each_ctx' |  | ||||||
|   - 'hlist_bl_for_each_entry' |  | ||||||
|   - 'hlist_bl_for_each_entry_rcu' |  | ||||||
|   - 'hlist_bl_for_each_entry_safe' |  | ||||||
|   - 'hlist_for_each' |  | ||||||
|   - 'hlist_for_each_entry' |  | ||||||
|   - 'hlist_for_each_entry_continue' |  | ||||||
|   - 'hlist_for_each_entry_continue_rcu' |  | ||||||
|   - 'hlist_for_each_entry_continue_rcu_bh' |  | ||||||
|   - 'hlist_for_each_entry_from' |  | ||||||
|   - 'hlist_for_each_entry_from_rcu' |  | ||||||
|   - 'hlist_for_each_entry_rcu' |  | ||||||
|   - 'hlist_for_each_entry_rcu_bh' |  | ||||||
|   - 'hlist_for_each_entry_rcu_notrace' |  | ||||||
|   - 'hlist_for_each_entry_safe' |  | ||||||
|   - 'hlist_for_each_entry_srcu' |  | ||||||
|   - '__hlist_for_each_rcu' |  | ||||||
|   - 'hlist_for_each_safe' |  | ||||||
|   - 'hlist_nulls_for_each_entry' |  | ||||||
|   - 'hlist_nulls_for_each_entry_from' |  | ||||||
|   - 'hlist_nulls_for_each_entry_rcu' |  | ||||||
|   - 'hlist_nulls_for_each_entry_safe' |  | ||||||
|   - 'i3c_bus_for_each_i2cdev' |  | ||||||
|   - 'i3c_bus_for_each_i3cdev' |  | ||||||
|   - 'ide_host_for_each_port' |  | ||||||
|   - 'ide_port_for_each_dev' |  | ||||||
|   - 'ide_port_for_each_present_dev' |  | ||||||
|   - 'idr_for_each_entry' |  | ||||||
|   - 'idr_for_each_entry_continue' |  | ||||||
|   - 'idr_for_each_entry_continue_ul' |  | ||||||
|   - 'idr_for_each_entry_ul' |  | ||||||
|   - 'in_dev_for_each_ifa_rcu' |  | ||||||
|   - 'in_dev_for_each_ifa_rtnl' |  | ||||||
|   - 'inet_bind_bucket_for_each' |  | ||||||
|   - 'inet_lhash2_for_each_icsk_rcu' |  | ||||||
|   - 'key_for_each' |  | ||||||
|   - 'key_for_each_safe' |  | ||||||
|   - 'klp_for_each_func' |  | ||||||
|   - 'klp_for_each_func_safe' |  | ||||||
|   - 'klp_for_each_func_static' |  | ||||||
|   - 'klp_for_each_object' |  | ||||||
|   - 'klp_for_each_object_safe' |  | ||||||
|   - 'klp_for_each_object_static' |  | ||||||
|   - 'kunit_suite_for_each_test_case' |  | ||||||
|   - 'kvm_for_each_memslot' |  | ||||||
|   - 'kvm_for_each_vcpu' |  | ||||||
|   - 'list_for_each' |  | ||||||
|   - 'list_for_each_codec' |  | ||||||
|   - 'list_for_each_codec_safe' |  | ||||||
|   - 'list_for_each_continue' |  | ||||||
|   - 'list_for_each_entry' |  | ||||||
|   - 'list_for_each_entry_continue' |  | ||||||
|   - 'list_for_each_entry_continue_rcu' |  | ||||||
|   - 'list_for_each_entry_continue_reverse' |  | ||||||
|   - 'list_for_each_entry_from' |  | ||||||
|   - 'list_for_each_entry_from_rcu' |  | ||||||
|   - 'list_for_each_entry_from_reverse' |  | ||||||
|   - 'list_for_each_entry_lockless' |  | ||||||
|   - 'list_for_each_entry_rcu' |  | ||||||
|   - 'list_for_each_entry_reverse' |  | ||||||
|   - 'list_for_each_entry_safe' |  | ||||||
|   - 'list_for_each_entry_safe_continue' |  | ||||||
|   - 'list_for_each_entry_safe_from' |  | ||||||
|   - 'list_for_each_entry_safe_reverse' |  | ||||||
|   - 'list_for_each_entry_srcu' |  | ||||||
|   - 'list_for_each_prev' |  | ||||||
|   - 'list_for_each_prev_safe' |  | ||||||
|   - 'list_for_each_safe' |  | ||||||
|   - 'llist_for_each' |  | ||||||
|   - 'llist_for_each_entry' |  | ||||||
|   - 'llist_for_each_entry_safe' |  | ||||||
|   - 'llist_for_each_safe' |  | ||||||
|   - 'mci_for_each_dimm' |  | ||||||
|   - 'media_device_for_each_entity' |  | ||||||
|   - 'media_device_for_each_intf' |  | ||||||
|   - 'media_device_for_each_link' |  | ||||||
|   - 'media_device_for_each_pad' |  | ||||||
|   - 'nanddev_io_for_each_page' |  | ||||||
|   - 'netdev_for_each_lower_dev' |  | ||||||
|   - 'netdev_for_each_lower_private' |  | ||||||
|   - 'netdev_for_each_lower_private_rcu' |  | ||||||
|   - 'netdev_for_each_mc_addr' |  | ||||||
|   - 'netdev_for_each_uc_addr' |  | ||||||
|   - 'netdev_for_each_upper_dev_rcu' |  | ||||||
|   - 'netdev_hw_addr_list_for_each' |  | ||||||
|   - 'nft_rule_for_each_expr' |  | ||||||
|   - 'nla_for_each_attr' |  | ||||||
|   - 'nla_for_each_nested' |  | ||||||
|   - 'nlmsg_for_each_attr' |  | ||||||
|   - 'nlmsg_for_each_msg' |  | ||||||
|   - 'nr_neigh_for_each' |  | ||||||
|   - 'nr_neigh_for_each_safe' |  | ||||||
|   - 'nr_node_for_each' |  | ||||||
|   - 'nr_node_for_each_safe' |  | ||||||
|   - 'of_for_each_phandle' |  | ||||||
|   - 'of_property_for_each_string' |  | ||||||
|   - 'of_property_for_each_u32' |  | ||||||
|   - 'pci_bus_for_each_resource' |  | ||||||
|   - 'pcl_for_each_chunk' |  | ||||||
|   - 'pcl_for_each_segment' |  | ||||||
|   - 'pcm_for_each_format' |  | ||||||
|   - 'ping_portaddr_for_each_entry' |  | ||||||
|   - 'plist_for_each' |  | ||||||
|   - 'plist_for_each_continue' |  | ||||||
|   - 'plist_for_each_entry' |  | ||||||
|   - 'plist_for_each_entry_continue' |  | ||||||
|   - 'plist_for_each_entry_safe' |  | ||||||
|   - 'plist_for_each_safe' |  | ||||||
|   - 'pnp_for_each_card' |  | ||||||
|   - 'pnp_for_each_dev' |  | ||||||
|   - 'protocol_for_each_card' |  | ||||||
|   - 'protocol_for_each_dev' |  | ||||||
|   - 'queue_for_each_hw_ctx' |  | ||||||
|   - 'radix_tree_for_each_slot' |  | ||||||
|   - 'radix_tree_for_each_tagged' |  | ||||||
|   - 'rb_for_each' |  | ||||||
|   - 'rbtree_postorder_for_each_entry_safe' |  | ||||||
|   - 'rdma_for_each_block' |  | ||||||
|   - 'rdma_for_each_port' |  | ||||||
|   - 'rdma_umem_for_each_dma_block' |  | ||||||
|   - 'resource_list_for_each_entry' |  | ||||||
|   - 'resource_list_for_each_entry_safe' |  | ||||||
|   - 'rhl_for_each_entry_rcu' |  | ||||||
|   - 'rhl_for_each_rcu' |  | ||||||
|   - 'rht_for_each' |  | ||||||
|   - 'rht_for_each_entry' |  | ||||||
|   - 'rht_for_each_entry_from' |  | ||||||
|   - 'rht_for_each_entry_rcu' |  | ||||||
|   - 'rht_for_each_entry_rcu_from' |  | ||||||
|   - 'rht_for_each_entry_safe' |  | ||||||
|   - 'rht_for_each_from' |  | ||||||
|   - 'rht_for_each_rcu' |  | ||||||
|   - 'rht_for_each_rcu_from' |  | ||||||
|   - '__rq_for_each_bio' |  | ||||||
|   - 'rq_for_each_bvec' |  | ||||||
|   - 'rq_for_each_segment' |  | ||||||
|   - 'scsi_for_each_prot_sg' |  | ||||||
|   - 'scsi_for_each_sg' |  | ||||||
|   - 'sctp_for_each_hentry' |  | ||||||
|   - 'sctp_skb_for_each' |  | ||||||
|   - 'shdma_for_each_chan' |  | ||||||
|   - '__shost_for_each_device' |  | ||||||
|   - 'shost_for_each_device' |  | ||||||
|   - 'sk_for_each' |  | ||||||
|   - 'sk_for_each_bound' |  | ||||||
|   - 'sk_for_each_entry_offset_rcu' |  | ||||||
|   - 'sk_for_each_from' |  | ||||||
|   - 'sk_for_each_rcu' |  | ||||||
|   - 'sk_for_each_safe' |  | ||||||
|   - 'sk_nulls_for_each' |  | ||||||
|   - 'sk_nulls_for_each_from' |  | ||||||
|   - 'sk_nulls_for_each_rcu' |  | ||||||
|   - 'snd_array_for_each' |  | ||||||
|   - 'snd_pcm_group_for_each_entry' |  | ||||||
|   - 'snd_soc_dapm_widget_for_each_path' |  | ||||||
|   - 'snd_soc_dapm_widget_for_each_path_safe' |  | ||||||
|   - 'snd_soc_dapm_widget_for_each_sink_path' |  | ||||||
|   - 'snd_soc_dapm_widget_for_each_source_path' |  | ||||||
|   - 'tb_property_for_each' |  | ||||||
|   - 'tcf_exts_for_each_action' |  | ||||||
|   - 'udp_portaddr_for_each_entry' |  | ||||||
|   - 'udp_portaddr_for_each_entry_rcu' |  | ||||||
|   - 'usb_hub_for_each_child' |  | ||||||
|   - 'v4l2_device_for_each_subdev' |  | ||||||
|   - 'v4l2_m2m_for_each_dst_buf' |  | ||||||
|   - 'v4l2_m2m_for_each_dst_buf_safe' |  | ||||||
|   - 'v4l2_m2m_for_each_src_buf' |  | ||||||
|   - 'v4l2_m2m_for_each_src_buf_safe' |  | ||||||
|   - 'virtio_device_for_each_vq' |  | ||||||
|   - 'while_for_each_ftrace_op' |  | ||||||
|   - 'xa_for_each' |  | ||||||
|   - 'xa_for_each_marked' |  | ||||||
|   - 'xa_for_each_range' |  | ||||||
|   - 'xa_for_each_start' |  | ||||||
|   - 'xas_for_each' |  | ||||||
|   - 'xas_for_each_conflict' |  | ||||||
|   - 'xas_for_each_marked' |  | ||||||
|   - 'xbc_array_for_each_value' |  | ||||||
|   - 'xbc_for_each_key_value' |  | ||||||
|   - 'xbc_node_for_each_array_value' |  | ||||||
|   - 'xbc_node_for_each_child' |  | ||||||
|   - 'xbc_node_for_each_key_value' |  | ||||||
|   - 'zorro_for_each_dev' |  | ||||||
|   - 'for_each_line' |  | ||||||
|   - 'for_each_non_empty_line' |  | ||||||
|  |  | ||||||
| #IncludeBlocks: Preserve # Unknown to clang-format-5.0 | #IncludeBlocks: Preserve # Unknown to clang-format-5.0 | ||||||
| IncludeCategories: | IncludeCategories: | ||||||
|   - Regex: '.*' |   - Regex: '.*' | ||||||
|   | |||||||
| @@ -24,3 +24,4 @@ | |||||||
| # If any interfaces have been removed or changed since the last public release, a=0. | # If any interfaces have been removed or changed since the last public release, a=0. | ||||||
| # | # | ||||||
| #library		what		description / commit summary line | #library		what		description / commit summary line | ||||||
|  | libosmogsm              >1.6.0          use of iuup.h (libosmocore.git Ib21cee2e30bf83dff4e167f79541796007af9845) | ||||||
|   | |||||||
| @@ -15,10 +15,6 @@ | |||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * GNU General Public License for more details. |  * GNU General Public License for more details. | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU General Public License along |  | ||||||
|  * with this program; if not, write to the Free Software Foundation, Inc., |  | ||||||
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |  | ||||||
|  */ |  */ | ||||||
| """ | """ | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							| @@ -1,10 +1,3 @@ | |||||||
| osmo-mgw (1.9.1) unstable; urgency=medium |  | ||||||
|  |  | ||||||
|   [ Vadim Yanitskiy ] |  | ||||||
|   * libosmo-mgcp: e1: fix memleaks in e1_recv_cb() |  | ||||||
|  |  | ||||||
|  -- Harald Welte <laforge@osmocom.org>  Mon, 18 Apr 2022 10:58:50 +0200 |  | ||||||
|  |  | ||||||
| osmo-mgw (1.9.0) unstable; urgency=medium | osmo-mgw (1.9.0) unstable; urgency=medium | ||||||
|  |  | ||||||
|   [ Harald Welte ] |   [ Harald Welte ] | ||||||
|   | |||||||
| @@ -91,6 +91,11 @@ the IP related aspects is nearly identical to the configuration of the virtual | |||||||
| trunk. However, it is important that the user assigns one of the E1 line numbers | trunk. However, it is important that the user assigns one of the E1 line numbers | ||||||
| that were configured under the e1_input node. | that were configured under the e1_input node. | ||||||
|  |  | ||||||
|  | NOTE: The endpoint name that is used on MGCP level will include the trunk number, | ||||||
|  | not the E1 line number. For simplicity (and compatibility with OsmoBSC) it is | ||||||
|  | recommended to use equal numbers for trunk and E1 line. However, if required any | ||||||
|  | E1 line can be mapped flexible on any trunk as long as the mapping is bijective. | ||||||
|  |  | ||||||
| .Example: A typical configuration with one E1 trunk | .Example: A typical configuration with one E1 trunk | ||||||
| ---- | ---- | ||||||
| e1_input | e1_input | ||||||
|   | |||||||
| @@ -107,8 +107,8 @@ We are planning to add further endpoint types for: | |||||||
|  |  | ||||||
| You can find the OsmoMGW issue tracker and wiki online at | You can find the OsmoMGW issue tracker and wiki online at | ||||||
|  |  | ||||||
| - https://osmocom.org/projects/osmomgw | - https://osmocom.org/projects/osmo-mgw | ||||||
| - https://osmocom.org/projects/osmomgw/wiki | - https://osmocom.org/projects/osmo-mgw/wiki | ||||||
|  |  | ||||||
| RFC 3435 for MGCP is located at | RFC 3435 for MGCP is located at | ||||||
|  |  | ||||||
|   | |||||||
| @@ -13,4 +13,5 @@ noinst_HEADERS = \ | |||||||
| 	mgcp_e1.h \ | 	mgcp_e1.h \ | ||||||
| 	mgcp_network.h \ | 	mgcp_network.h \ | ||||||
| 	mgcp_protocol.h \ | 	mgcp_protocol.h \ | ||||||
|  | 	mgcp_iuup.h \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|   | |||||||
| @@ -17,3 +17,4 @@ 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 struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn, | ||||||
| 								const char *subtype_name, unsigned int match_nr); | 								const char *subtype_name, unsigned int match_nr); | ||||||
|  | bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec); | ||||||
|   | |||||||
| @@ -28,6 +28,7 @@ | |||||||
| #include <osmocom/mgcp/osmux.h> | #include <osmocom/mgcp/osmux.h> | ||||||
| #include <osmocom/core/linuxlist.h> | #include <osmocom/core/linuxlist.h> | ||||||
| #include <osmocom/core/rate_ctr.h> | #include <osmocom/core/rate_ctr.h> | ||||||
|  | #include <osmocom/gsm/iuup.h> | ||||||
| #include <inttypes.h> | #include <inttypes.h> | ||||||
|  |  | ||||||
| #define LOGPCONN(conn, cat, level, fmt, args...) \ | #define LOGPCONN(conn, cat, level, fmt, args...) \ | ||||||
| @@ -47,6 +48,7 @@ enum mgcp_conn_rtp_type { | |||||||
| 	MGCP_RTP_DEFAULT	= 0, | 	MGCP_RTP_DEFAULT	= 0, | ||||||
| 	MGCP_OSMUX_BSC, | 	MGCP_OSMUX_BSC, | ||||||
| 	MGCP_OSMUX_BSC_NAT, | 	MGCP_OSMUX_BSC_NAT, | ||||||
|  | 	MGCP_RTP_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 | ||||||
| @@ -93,6 +95,14 @@ struct mgcp_conn_rtp { | |||||||
| 		} stats; | 		} stats; | ||||||
| 	} osmux; | 	} osmux; | ||||||
|  |  | ||||||
|  | 	struct { | ||||||
|  | 		struct osmo_iuup_instance *iui; | ||||||
|  | 		bool active_init; /* true: Send IuUP Init */ | ||||||
|  | 		int rfci_id_no_data; /* RFCI Id for RFCI NO_DATA (-1 if not available) */ | ||||||
|  | 		bool configured; | ||||||
|  | 		struct osmo_iuup_rnl_prim *init_ind; | ||||||
|  | 	} iuup; | ||||||
|  |  | ||||||
| 	struct rate_ctr_group *rate_ctr_group; | 	struct rate_ctr_group *rate_ctr_group; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -176,6 +186,12 @@ static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) { | |||||||
| 	return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT; | 	return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Was conn configured to handle Osmux? */ | ||||||
|  | static inline bool mgcp_conn_rtp_is_iuup(const struct mgcp_conn_rtp *conn) | ||||||
|  | { | ||||||
|  | 	return conn->type == MGCP_RTP_IUUP; | ||||||
|  | } | ||||||
|  |  | ||||||
| struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp, | struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp, | ||||||
| 				  enum mgcp_conn_type type, char *name); | 				  enum mgcp_conn_type type, char *name); | ||||||
| struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id); | struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id); | ||||||
|   | |||||||
							
								
								
									
										33
									
								
								include/osmocom/mgcp/mgcp_iuup.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								include/osmocom/mgcp/mgcp_iuup.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | /* IuUP connection functionalitites */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * Author: Pau Espin Pedrol | ||||||
|  |  * | ||||||
|  |  * 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/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <osmocom/core/msgb.h> | ||||||
|  |  | ||||||
|  | struct mgcp_conn_rtp; | ||||||
|  |  | ||||||
|  | int mgcp_conn_iuup_init(struct mgcp_conn_rtp *conn_rtp); | ||||||
|  | void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp); | ||||||
|  | int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg); | ||||||
|  | int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg); | ||||||
|  | int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp); | ||||||
| @@ -70,8 +70,6 @@ struct mgcp_rtp_state { | |||||||
| 	 * data is just re-used) */ | 	 * data is just re-used) */ | ||||||
| 	uint16_t alt_rtp_tx_sequence; | 	uint16_t alt_rtp_tx_sequence; | ||||||
| 	uint32_t alt_rtp_tx_ssrc; | 	uint32_t alt_rtp_tx_ssrc; | ||||||
|  |  | ||||||
| 	bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */ |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct mgcp_rtp_codec { | struct mgcp_rtp_codec { | ||||||
| @@ -110,8 +108,8 @@ struct mgcp_rtp_end { | |||||||
| 	uint32_t packet_duration_ms; | 	uint32_t packet_duration_ms; | ||||||
| 	int maximum_packet_time; /* -1: not set */ | 	int maximum_packet_time; /* -1: not set */ | ||||||
| 	char *fmtp_extra; | 	char *fmtp_extra; | ||||||
| 	/* are we transmitting packets (1) or dropping (0) outbound packets */ | 	/* are we transmitting packets (true) or dropping (false) outbound packets */ | ||||||
| 	int output_enabled; | 	bool output_enabled; | ||||||
| 	/* FIXME: This parameter can be set + printed, but is nowhere used! */ | 	/* FIXME: This parameter can be set + printed, but is nowhere used! */ | ||||||
| 	int force_output_ptime; | 	int force_output_ptime; | ||||||
|  |  | ||||||
| @@ -177,3 +175,8 @@ void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, | |||||||
| void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, | void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, | ||||||
| 			const uint16_t seq, const int32_t transit, | 			const uint16_t seq, const int32_t transit, | ||||||
| 			const uint32_t ssrc, const bool marker_bit); | 			const uint32_t ssrc, const bool marker_bit); | ||||||
|  |  | ||||||
|  | void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, | ||||||
|  | 				 int id, int inc); | ||||||
|  | void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg); | ||||||
|  | uint32_t mgcp_get_current_ts(unsigned codec_rate); | ||||||
|   | |||||||
							
								
								
									
										52
									
								
								include/osmocom/mgcp/mgcp_threads_queue.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								include/osmocom/mgcp/mgcp_threads_queue.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | |||||||
|  | /* | ||||||
|  |  * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * Author: Eric Wild | ||||||
|  |  * | ||||||
|  |  * 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 <stdatomic.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  |  | ||||||
|  | struct spsc { | ||||||
|  | 	atomic_uint readptr; | ||||||
|  | 	atomic_uint writeptr; | ||||||
|  |  | ||||||
|  | 	int efd_r, efd_w; /* eventfds used to block/notify readers/writers */ | ||||||
|  |  | ||||||
|  | 	int count; | ||||||
|  | 	int size_per_buf; | ||||||
|  |  | ||||||
|  | 	void *buf; /* buffer size count*size_per_buf */ | ||||||
|  | 	uintptr_t data[0]; /* count sized array of pointers to size_per_buf chunks in buf array*/ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct qchan { | ||||||
|  | 	struct spsc *a; | ||||||
|  | 	struct spsc *b; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | bool spsc_push(struct spsc *q, void *elem); | ||||||
|  | bool spsc_pop(struct spsc *q, void *elem); | ||||||
|  | ssize_t spsc_prep_pop(struct spsc *q); | ||||||
|  | int spsc_get_a_rdfd(struct qchan *q); | ||||||
|  |  | ||||||
|  | struct qchan spsc_chan_init(void *talloc_ctx, unsigned int count, unsigned int size_per_buf); | ||||||
|  | struct qchan spsc_chan_init_ex(void *talloc_ctx, unsigned int count, unsigned int size_per_buf, bool blockr_a, | ||||||
|  | 			       bool blockw_a, bool blockr_b, bool blockw_b); | ||||||
|  | void spsc_chan_close(struct qchan *q); | ||||||
| @@ -49,6 +49,7 @@ enum mgcp_codecs { | |||||||
| 	CODEC_GSMHR_8000_1 = 111, | 	CODEC_GSMHR_8000_1 = 111, | ||||||
| 	CODEC_AMR_8000_1 = 112, | 	CODEC_AMR_8000_1 = 112, | ||||||
| 	CODEC_AMRWB_16000_1 = 113, | 	CODEC_AMRWB_16000_1 = 113, | ||||||
|  | 	CODEC_IUFP = 96, | ||||||
| }; | }; | ||||||
| /* Note: when new codec types are added, the corresponding value strings | /* Note: when new codec types are added, the corresponding value strings | ||||||
|  * in mgcp_client.c (codec_table) must be updated as well. Enumerations |  * in mgcp_client.c (codec_table) must be updated as well. Enumerations | ||||||
|   | |||||||
| @@ -1,74 +0,0 @@ | |||||||
| # =========================================================================== |  | ||||||
| #   http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html |  | ||||||
| # =========================================================================== |  | ||||||
| # |  | ||||||
| # SYNOPSIS |  | ||||||
| # |  | ||||||
| #   AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) |  | ||||||
| # |  | ||||||
| # DESCRIPTION |  | ||||||
| # |  | ||||||
| #   Check whether the given FLAG works with the current language's compiler |  | ||||||
| #   or gives an error.  (Warnings, however, are ignored) |  | ||||||
| # |  | ||||||
| #   ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on |  | ||||||
| #   success/failure. |  | ||||||
| # |  | ||||||
| #   If EXTRA-FLAGS is defined, it is added to the current language's default |  | ||||||
| #   flags (e.g. CFLAGS) when the check is done.  The check is thus made with |  | ||||||
| #   the flags: "CFLAGS EXTRA-FLAGS FLAG".  This can for example be used to |  | ||||||
| #   force the compiler to issue an error when a bad flag is given. |  | ||||||
| # |  | ||||||
| #   INPUT gives an alternative input source to AC_COMPILE_IFELSE. |  | ||||||
| # |  | ||||||
| #   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this |  | ||||||
| #   macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. |  | ||||||
| # |  | ||||||
| # LICENSE |  | ||||||
| # |  | ||||||
| #   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de> |  | ||||||
| #   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com> |  | ||||||
| # |  | ||||||
| #   This program is free software: you can redistribute it and/or modify it |  | ||||||
| #   under the terms of the GNU 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 General |  | ||||||
| #   Public License for more details. |  | ||||||
| # |  | ||||||
| #   You should have received a copy of the GNU General Public License along |  | ||||||
| #   with this program. If not, see <http://www.gnu.org/licenses/>. |  | ||||||
| # |  | ||||||
| #   As a special exception, the respective Autoconf Macro's copyright owner |  | ||||||
| #   gives unlimited permission to copy, distribute and modify the configure |  | ||||||
| #   scripts that are the output of Autoconf when processing the Macro. You |  | ||||||
| #   need not follow the terms of the GNU General Public License when using |  | ||||||
| #   or distributing such scripts, even though portions of the text of the |  | ||||||
| #   Macro appear in them. The GNU General Public License (GPL) does govern |  | ||||||
| #   all other use of the material that constitutes the Autoconf Macro. |  | ||||||
| # |  | ||||||
| #   This special exception to the GPL applies to versions of the Autoconf |  | ||||||
| #   Macro released by the Autoconf Archive. When you make and distribute a |  | ||||||
| #   modified version of the Autoconf Macro, you may extend this special |  | ||||||
| #   exception to the GPL to apply to your modified version as well. |  | ||||||
|  |  | ||||||
| #serial 4 |  | ||||||
|  |  | ||||||
| AC_DEFUN([AX_CHECK_COMPILE_FLAG], |  | ||||||
| [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF |  | ||||||
| AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl |  | ||||||
| AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ |  | ||||||
|   ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS |  | ||||||
|   _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" |  | ||||||
|   AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], |  | ||||||
|     [AS_VAR_SET(CACHEVAR,[yes])], |  | ||||||
|     [AS_VAR_SET(CACHEVAR,[no])]) |  | ||||||
|   _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) |  | ||||||
| AS_VAR_IF(CACHEVAR,yes, |  | ||||||
|   [m4_default([$2], :)], |  | ||||||
|   [m4_default([$3], :)]) |  | ||||||
| AS_VAR_POPDEF([CACHEVAR])dnl |  | ||||||
| ])dnl AX_CHECK_COMPILE_FLAGS |  | ||||||
| @@ -59,6 +59,7 @@ const struct value_string osmo_mgcpc_codec_names[] = { | |||||||
| 	{ CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" }, | 	{ CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" }, | ||||||
| 	{ CODEC_AMR_8000_1, "AMR/8000/1" }, | 	{ CODEC_AMR_8000_1, "AMR/8000/1" }, | ||||||
| 	{ CODEC_AMRWB_16000_1, "AMR-WB/16000/1" }, | 	{ CODEC_AMRWB_16000_1, "AMR-WB/16000/1" }, | ||||||
|  | 	{ CODEC_IUFP, "VND.3GPP.IUFP/16000" }, | ||||||
| 	{ 0, NULL }, | 	{ 0, NULL }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -48,4 +48,5 @@ libosmo_mgcp_a_SOURCES = \ | |||||||
| 	mgcp_ctrl.c \ | 	mgcp_ctrl.c \ | ||||||
| 	mgcp_ratectr.c \ | 	mgcp_ratectr.c \ | ||||||
| 	mgcp_e1.c \ | 	mgcp_e1.c \ | ||||||
|  | 	mgcp_iuup.c \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|   | |||||||
| @@ -14,10 +14,6 @@ | |||||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  *  GNU General Public License for more details. |  *  GNU General Public License for more details. | ||||||
|  * |  | ||||||
|  *  You should have received a copy of the GNU General Public License |  | ||||||
|  *  along with this program; if not, write to the Free Software |  | ||||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA |  | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| static inline int val_seg(int val) | static inline int val_seg(int val) | ||||||
|   | |||||||
| @@ -355,7 +355,7 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn) | |||||||
|  * |  * | ||||||
|  * https://tools.ietf.org/html/rfc4867 |  * https://tools.ietf.org/html/rfc4867 | ||||||
|  */ |  */ | ||||||
| static bool amr_is_octet_aligned(const struct mgcp_rtp_codec *codec) | bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec) | ||||||
| { | { | ||||||
| 	if (!codec->param_present) | 	if (!codec->param_present) | ||||||
| 		return false; | 		return false; | ||||||
| @@ -378,10 +378,10 @@ static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *c | |||||||
| 		return false; | 		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)) | 	/* Note: AMR allows to set the RTP payload format to octet-aligned or bandwith-efficient (octet-aligned=0) | ||||||
| 			return false; | 	 * via SDP. This difference concerns payload format only, but not the actual codec. It is not a difference | ||||||
| 	} | 	 * within the meaning of this function. */ | ||||||
|  |  | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| @@ -417,7 +417,7 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp | |||||||
| 	if (!codec_src) | 	if (!codec_src) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
|  |  | ||||||
| 	/* Use the codec infrmation from the source and try to find the | 	/* Use the codec information from the source and try to find the | ||||||
| 	 * equivalent of it on the destination side */ | 	 * equivalent of it on the destination side */ | ||||||
| 	codecs_assigned = rtp_dst->codecs_assigned; | 	codecs_assigned = rtp_dst->codecs_assigned; | ||||||
| 	OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS); | 	OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS); | ||||||
|   | |||||||
| @@ -30,6 +30,8 @@ | |||||||
| #include <osmocom/mgcp/mgcp_trunk.h> | #include <osmocom/mgcp/mgcp_trunk.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_iuup.h> | ||||||
|  |  | ||||||
| #include <osmocom/gsm/gsm_utils.h> | #include <osmocom/gsm/gsm_utils.h> | ||||||
| #include <osmocom/core/rate_ctr.h> | #include <osmocom/core/rate_ctr.h> | ||||||
| #include <osmocom/core/timer.h> | #include <osmocom/core/timer.h> | ||||||
| @@ -108,7 +110,7 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn * | |||||||
| 	/* Set default values */ | 	/* Set default values */ | ||||||
| 	end->frames_per_packet = 0;	/* unknown */ | 	end->frames_per_packet = 0;	/* unknown */ | ||||||
| 	end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS; | 	end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS; | ||||||
| 	end->output_enabled = 0; | 	end->output_enabled = false; | ||||||
| 	end->maximum_packet_time = -1; | 	end->maximum_packet_time = -1; | ||||||
|  |  | ||||||
| 	conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++); | 	conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++); | ||||||
| @@ -129,6 +131,8 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp) | |||||||
| { | { | ||||||
| 	if (mgcp_conn_rtp_is_osmux(conn_rtp)) | 	if (mgcp_conn_rtp_is_osmux(conn_rtp)) | ||||||
| 		conn_osmux_disable(conn_rtp); | 		conn_osmux_disable(conn_rtp); | ||||||
|  | 	if (mgcp_conn_rtp_is_iuup(conn_rtp)) | ||||||
|  | 		mgcp_conn_iuup_cleanup(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); | 	mgcp_codec_reset_all(conn_rtp); | ||||||
| @@ -325,6 +329,11 @@ void mgcp_conn_free_oldest(struct mgcp_endpoint *endp) | |||||||
|  |  | ||||||
| /*! free all connections at once. | /*! free all connections at once. | ||||||
|  *  \param[in] endp associated endpoint */ |  *  \param[in] endp associated endpoint */ | ||||||
|  | #if defined(__has_attribute) | ||||||
|  | #if __has_attribute(no_sanitize) | ||||||
|  | __attribute__((no_sanitize("undefined"))) /* ubsan detects a misaligned load */ | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
| void mgcp_conn_free_all(struct mgcp_endpoint *endp) | void mgcp_conn_free_all(struct mgcp_endpoint *endp) | ||||||
| { | { | ||||||
| 	struct mgcp_conn *conn; | 	struct mgcp_conn *conn; | ||||||
|   | |||||||
							
								
								
									
										745
									
								
								src/libosmo-mgcp/mgcp_iuup.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										745
									
								
								src/libosmo-mgcp/mgcp_iuup.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,745 @@ | |||||||
|  | /* | ||||||
|  |  * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||||
|  |  * All rights not specifically granted under this license are reserved. | ||||||
|  |  * | ||||||
|  |  * Author: Pau Espin Pedrol | ||||||
|  |  * | ||||||
|  |  * 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. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <stdint.h> | ||||||
|  |  | ||||||
|  | #include <osmocom/core/byteswap.h> | ||||||
|  |  | ||||||
|  | #include <osmocom/gsm/iuup.h> | ||||||
|  |  | ||||||
|  | #include <osmocom/netif/rtp.h> | ||||||
|  | #include <osmocom/netif/amr.h> | ||||||
|  |  | ||||||
|  | #include <osmocom/mgcp/mgcp_conn.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_iuup.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_endp.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_codec.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_network.h> | ||||||
|  | #include <osmocom/mgcp/debug.h> | ||||||
|  |  | ||||||
|  | #define MGW_IUUP_MSGB_SIZE 4096 | ||||||
|  |  | ||||||
|  | static const struct osmo_iuup_rnl_config def_configure_req = { | ||||||
|  | 	.transparent = false, | ||||||
|  | 	.active = true, | ||||||
|  | 	.supported_versions_mask = 0x0003, | ||||||
|  | 	.num_rfci = 0, | ||||||
|  | 	.num_subflows = 0, | ||||||
|  | 	.IPTIs_present = false, | ||||||
|  | 	.t_init = { .t_ms = IUUP_TIMER_INIT_T_DEFAULT, .n_max = IUUP_TIMER_INIT_N_DEFAULT }, | ||||||
|  | 	.t_ta = { .t_ms = IUUP_TIMER_TA_T_DEFAULT, .n_max = IUUP_TIMER_TA_N_DEFAULT }, | ||||||
|  | 	.t_rc = { .t_ms = IUUP_TIMER_RC_T_DEFAULT, .n_max = IUUP_TIMER_RC_N_DEFAULT }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Find a destination connection. */ | ||||||
|  | static struct mgcp_conn *_find_dst_conn(struct mgcp_conn *conn) | ||||||
|  | { | ||||||
|  | 	/* NOTE: This code path runs every time an RTP packet is received. The | ||||||
|  | 	 * function mgcp_find_dst_conn() we use to determine the detination | ||||||
|  | 	 * connection will iterate the connection list inside the endpoint. | ||||||
|  | 	 * Since list iterations are quite costly, we will figure out the | ||||||
|  | 	 * destination only once and use the optional private data pointer of | ||||||
|  | 	 * the connection to cache the destination connection pointer. */ | ||||||
|  |  | ||||||
|  | 	struct mgcp_conn *conn_dst; | ||||||
|  | 	if (!conn->priv) { | ||||||
|  | 		conn_dst = mgcp_find_dst_conn(conn); | ||||||
|  | 		conn->priv = conn_dst; | ||||||
|  | 	} else { | ||||||
|  | 		conn_dst = (struct mgcp_conn *)conn->priv; | ||||||
|  | 	} | ||||||
|  | 	return conn_dst; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Find RFCI containing all 0 sizes, -1 if not found. irp is an Initialization.ind prim */ | ||||||
|  | static int _find_rfci_no_data(struct osmo_iuup_rnl_prim *irp) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 	uint8_t rfci_cnt = 0; | ||||||
|  | 	/* Find RFCI containing NO_DATA: */ | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) { | ||||||
|  | 		struct osmo_iuup_rfci *rfci = &irp->u.status.u.initialization.rfci[i]; | ||||||
|  | 		int j; | ||||||
|  | 		bool is_no_data; | ||||||
|  | 		if (!rfci->used) | ||||||
|  | 			continue; | ||||||
|  | 		rfci_cnt++; | ||||||
|  |  | ||||||
|  | 		is_no_data = true; | ||||||
|  | 		for (j = 0; j < irp->u.status.u.initialization.num_subflows; j++) { | ||||||
|  | 			if (rfci->subflow_sizes[j]) { | ||||||
|  | 				is_no_data = false; | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if (is_no_data) | ||||||
|  | 			return rfci->id; | ||||||
|  |  | ||||||
|  | 		/* early loop termination: */ | ||||||
|  | 		if (rfci_cnt == irp->u.status.u.initialization.num_subflows) | ||||||
|  | 			break; | ||||||
|  | 	} | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Lookup RFCI to use for specific AMR codec type. -1 if none found */ | ||||||
|  | static int8_t _conn_iuup_amr_ft_2_rfci(struct mgcp_conn_rtp *conn_rtp, uint8_t ft) | ||||||
|  | { | ||||||
|  | 	int8_t i; | ||||||
|  | 	uint8_t rfci_cnt = 0; | ||||||
|  | 	unsigned match_bytes = (unsigned)osmo_amr_bytes(ft); | ||||||
|  | 	struct osmo_iuup_rnl_prim *irp = conn_rtp->iuup.init_ind; | ||||||
|  | 	OSMO_ASSERT(irp); | ||||||
|  |  | ||||||
|  | 	/* TODO: cache this somehow */ | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) { | ||||||
|  | 		struct osmo_iuup_rfci *rfci = &irp->u.status.u.initialization.rfci[i]; | ||||||
|  | 		int j; | ||||||
|  | 		unsigned num_bits; | ||||||
|  | 		if (!rfci->used) | ||||||
|  | 			continue; | ||||||
|  | 		rfci_cnt++; | ||||||
|  |  | ||||||
|  | 		num_bits = 0; | ||||||
|  | 		for (j = 0; j < irp->u.status.u.initialization.num_subflows; j++) | ||||||
|  | 			num_bits += rfci->subflow_sizes[j]; | ||||||
|  | 		if (match_bytes == (num_bits + 7)/8) | ||||||
|  | 			return rfci->id; | ||||||
|  |  | ||||||
|  | 		/* early loop termination: */ | ||||||
|  | 		if (rfci_cnt == irp->u.status.u.initialization.num_subflows) | ||||||
|  | 			break; | ||||||
|  | 	} | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Helper function to configure IuUP layer FSM as Init-Passive, based on default config */ | ||||||
|  | static int _conn_iuup_configure_as_passive(struct mgcp_conn_rtp *conn_rtp) | ||||||
|  | { | ||||||
|  | 	struct osmo_iuup_rnl_prim *irp; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	conn_rtp->iuup.active_init = false; | ||||||
|  |  | ||||||
|  | 	/* Tx CONFIG.req */ | ||||||
|  | 	irp = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE); | ||||||
|  | 	irp->u.config = def_configure_req; | ||||||
|  | 	irp->u.config.active = conn_rtp->iuup.active_init; | ||||||
|  | 	if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp)) == 0) | ||||||
|  | 		conn_rtp->iuup.configured = true; | ||||||
|  | 	else | ||||||
|  | 		LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed configuring IuUP layer\n"); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Helper function to configure IuUP layer FSM as Init-Active, based on received | ||||||
|  |  * RNL Status-Init primitive from the sister IuUP connection we will bridge to. */ | ||||||
|  | static int _conn_iuup_configure_as_active(struct mgcp_conn_rtp *conn_rtp, struct osmo_iuup_rnl_prim *init_ind) | ||||||
|  | { | ||||||
|  | 	struct osmo_iuup_rnl_prim *irp = init_ind; | ||||||
|  | 	struct osmo_iuup_rnl_prim *irp2; | ||||||
|  | 	struct msgb *msg; | ||||||
|  | 	bool prev_output_enabled; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	conn_rtp->iuup.active_init = true; | ||||||
|  |  | ||||||
|  | 	/* Find RFCI containing NO_DATA: */ | ||||||
|  | 	conn_rtp->iuup.rfci_id_no_data = _find_rfci_no_data(init_ind); | ||||||
|  |  | ||||||
|  | 	/* Copy over the rfci_id_no_data, since we reuse the same subflow set: */ | ||||||
|  | 	msg = msgb_copy_c(conn_rtp->conn, irp->oph.msg, "iuup-init-copy"); | ||||||
|  | 	conn_rtp->iuup.init_ind = (struct osmo_iuup_rnl_prim *)msgb_data(msg); | ||||||
|  | 	conn_rtp->iuup.init_ind->oph.msg = msg; | ||||||
|  |  | ||||||
|  | 	/* Tx CONFIG.req */ | ||||||
|  | 	irp2 = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE); | ||||||
|  | 	irp2->u.config.transparent = false; | ||||||
|  | 	irp2->u.config.active = conn_rtp->iuup.active_init; | ||||||
|  | 	irp2->u.config.data_pdu_type = irp->u.status.u.initialization.data_pdu_type; | ||||||
|  | 	irp2->u.config.supported_versions_mask = def_configure_req.supported_versions_mask; | ||||||
|  | 	irp2->u.config.num_rfci = irp->u.status.u.initialization.num_rfci; | ||||||
|  | 	irp2->u.config.num_subflows = irp->u.status.u.initialization.num_subflows; | ||||||
|  | 	irp2->u.config.IPTIs_present = irp->u.status.u.initialization.IPTIs_present; | ||||||
|  | 	memcpy(irp2->u.config.rfci, irp->u.status.u.initialization.rfci, sizeof(irp2->u.config.rfci)); | ||||||
|  | 	irp2->u.config.t_init = def_configure_req.t_init; | ||||||
|  | 	irp2->u.config.t_ta = def_configure_req.t_ta; | ||||||
|  | 	irp2->u.config.t_rc = def_configure_req.t_rc; | ||||||
|  |  | ||||||
|  | 	/* We need to force allowance of RTP containing Init-ACK back: */ | ||||||
|  | 	prev_output_enabled = conn_rtp->end.output_enabled; | ||||||
|  | 	conn_rtp->end.output_enabled = true; | ||||||
|  |  | ||||||
|  | 	if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp2)) == 0) | ||||||
|  | 		conn_rtp->iuup.configured = true; | ||||||
|  | 	else | ||||||
|  | 		LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed configuring IuUP layer\n"); | ||||||
|  |  | ||||||
|  | 	conn_rtp->end.output_enabled = prev_output_enabled; | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Helper function to push an RTP+IuUP pkt up to the IuUP layer FSM through the | ||||||
|  |  * TNL primitive interface. */ | ||||||
|  | static int _conn_iuup_rtp_pl_up(struct mgcp_conn_rtp *conn_rtp, struct msgb *msg) | ||||||
|  | { | ||||||
|  | 	/* Send RTP payload (IuUP) up the stack: */ | ||||||
|  | 	struct osmo_iuup_tnl_prim *itp; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	msg->l2h = msgb_data(msg) + sizeof(struct rtp_hdr); | ||||||
|  |  | ||||||
|  | 	itp = osmo_iuup_tnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, MGW_IUUP_MSGB_SIZE); | ||||||
|  | 	itp->oph.msg->l2h = msgb_put(itp->oph.msg, msgb_l2len(msg)); | ||||||
|  | 	memcpy(itp->oph.msg->l2h, msgb_l2(msg), msgb_l2len(msg)); | ||||||
|  | 	if ((rc = osmo_iuup_tnl_prim_up(conn_rtp->iuup.iui, itp)) != 0) { | ||||||
|  | 		LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed passing IuUP-Init to IuUP layer\n"); | ||||||
|  | 	} | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int check_rtp_iuup(const struct mgcp_conn_rtp *conn_rtp, struct msgb *msg) | ||||||
|  | { | ||||||
|  | 	size_t min_size = sizeof(struct rtp_hdr); | ||||||
|  | 	/* Check there's at least 2 bytes of RTP payload (IuUP header). This is | ||||||
|  | 	 ** mainly to avoid 0-byte payload copy cases */ | ||||||
|  | 	if (msgb_length(msg) < sizeof(struct rtp_hdr) + 2) { | ||||||
|  | 		LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "RTP-IuUP packet too short (%u < %zu)\n", | ||||||
|  | 			     msgb_length(msg), min_size); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Bridge received IuUP packet in conn_rtp_src to conn_rtp_dst, an IuUP sister | ||||||
|  |  * conn in the endpoint. The function takes ownsership of the irp */ | ||||||
|  | static int bridge_iuup_to_iuup_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mgcp_conn_rtp *conn_rtp_dst, struct osmo_iuup_rnl_prim *irp) | ||||||
|  | { | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	/* If we are not configured and we received bridged data, it means | ||||||
|  | 	 * conn_rtp_src is already configured and INITed, and we can infer | ||||||
|  | 	 * conn_rtp_src is Init-passive (RNC side), so conn_rtp_dst needs to be | ||||||
|  | 	 * configured as INIT-active: */ | ||||||
|  | 	if (!conn_rtp_dst->iuup.configured) { | ||||||
|  | 		OSMO_ASSERT(conn_rtp_src->iuup.init_ind); | ||||||
|  | 		rc = _conn_iuup_configure_as_active(conn_rtp_dst, conn_rtp_src->iuup.init_ind); | ||||||
|  | 		if (rc < 0) { | ||||||
|  | 			msgb_free(irp->oph.msg); | ||||||
|  | 			return rc; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* We simply forward the msg, without freeing it: */ | ||||||
|  | 	talloc_steal(conn_rtp_dst->conn, irp->oph.msg); | ||||||
|  | 	irp->oph.operation = PRIM_OP_REQUEST; | ||||||
|  | 	if ((rc = osmo_iuup_rnl_prim_down(conn_rtp_dst->iuup.iui, irp)) != 0) | ||||||
|  | 		LOG_CONN_RTP(conn_rtp_dst, LOGL_ERROR, "Failed Tx data down to IuUP layer\n"); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Bridge received IuUP packet in conn_rtp_src to conn_rtp_dst, an RTP (no IuUP) | ||||||
|  |  * sister conn in the endpoint. The function takes ownsership of the irp */ | ||||||
|  | static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mgcp_conn_rtp *conn_rtp_dst, struct osmo_iuup_rnl_prim *irp) | ||||||
|  | { | ||||||
|  | 	/* FIXME: We probably need transcoding here?! Or at least look up AMR modes and translate to related RFCI */ | ||||||
|  | 	uint8_t frame_nr = irp->u.data.frame_nr; | ||||||
|  | 	uint8_t fqc = irp->u.data.fqc; | ||||||
|  | 	struct msgb *msg = irp->oph.msg; | ||||||
|  | 	ssize_t amr_length = 0; | ||||||
|  | 	int ft; | ||||||
|  | 	uint8_t *amr_data; | ||||||
|  | 	struct rtp_hdr *rtp_hdr; | ||||||
|  | 	struct amr_hdr *amr_hdr; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	ft = osmo_amr_bytes_to_ft(msgb_l3len(msg)); | ||||||
|  | 	if (ft < 0) { | ||||||
|  | 		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR, | ||||||
|  | 			 "Unknown AMR format for size %u\n", msgb_l3len(msg)); | ||||||
|  | 		msgb_free(msg); | ||||||
|  | 		return ft; | ||||||
|  | 	} | ||||||
|  | 	msgb_pull_to_l3(msg); | ||||||
|  | 	LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR: ft %d, len %d\n", ft, msgb_l3len(msg)); | ||||||
|  |  | ||||||
|  | 	if (mgcp_codec_amr_is_octet_aligned(conn_rtp_dst->end.codec)) { | ||||||
|  | 		amr_hdr = (struct amr_hdr *) msgb_push(msg, sizeof(struct amr_hdr)); | ||||||
|  | 		amr_hdr->cmr = 15; /* no change */ | ||||||
|  | 		amr_hdr->f = 0; | ||||||
|  | 		amr_hdr->q = !fqc; | ||||||
|  | 		amr_hdr->ft = ft & 0xff; | ||||||
|  | 		amr_hdr->pad1 = 0; | ||||||
|  | 		amr_hdr->pad2 = 0; | ||||||
|  | 	} else { | ||||||
|  | 		OSMO_ASSERT(msgb_tailroom(msg) >= 2); | ||||||
|  | 		msgb_put(msg, 2); | ||||||
|  | 		osmo_amr_iuup_to_bwe(msgb_data(msg), msgb_length(msg) - 2, msgb_length(msg) + 2); | ||||||
|  | 		/* fill bwe header */ | ||||||
|  | 		amr_data = msgb_data(msg); | ||||||
|  | 		/* CMR no change      | follow bit | ft (3 of 4 bits) */ | ||||||
|  | 		amr_data[0] = 15 << 4 | (0 << 3) | (ft >> 1); | ||||||
|  | 		amr_data[1] |= ((ft & 0x1) << 7) | (((!fqc) & 0x1) << 6); | ||||||
|  | 		amr_length = (osmo_amr_bits(ft) + 10 + 7) / 8; | ||||||
|  | 		msgb_trim(msg, amr_length); | ||||||
|  | 	} | ||||||
|  | 	rtp_hdr = (struct rtp_hdr *) msgb_push(msg, sizeof(*rtp_hdr)); | ||||||
|  | 	*rtp_hdr = (struct rtp_hdr){ | ||||||
|  | 		.csrc_count = 0, | ||||||
|  | 		.extension = 0, | ||||||
|  | 		.padding = 0, | ||||||
|  | 		.version = 0, | ||||||
|  | 		.payload_type = conn_rtp_dst->end.codec->payload_type, | ||||||
|  | 		.marker = 0, | ||||||
|  | 		.sequence = frame_nr, | ||||||
|  | 		.timestamp = 0, | ||||||
|  | 		.ssrc = 0 | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	rc = mgcp_send(conn_rtp_dst->conn->endp, true, NULL, msg, conn_rtp_src, conn_rtp_dst); | ||||||
|  | 	msgb_free(msg); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Handle RNL Data primitive received from the IuUP layer FSM: Bridge it to the | ||||||
|  |  * sister connection in the endpoint: */ | ||||||
|  | static int _conn_iuup_rx_rnl_data(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp) | ||||||
|  | { | ||||||
|  | 	struct mgcp_conn *conn_dst; | ||||||
|  | 	struct mgcp_conn_rtp *conn_rtp_dst; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	conn_dst = _find_dst_conn(conn_rtp_src->conn); | ||||||
|  |  | ||||||
|  | 	/* There is no destination conn, stop here */ | ||||||
|  | 	if (!conn_dst) { | ||||||
|  | 		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_DEBUG, | ||||||
|  | 			 "no connection to forward an incoming IuUP payload to\n"); | ||||||
|  | 		rc = -1; | ||||||
|  | 		goto free_ret; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* The destination conn is not an RTP/IuUP connection */ | ||||||
|  | 	if (conn_dst->type != MGCP_CONN_TYPE_RTP) { | ||||||
|  | 		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR, | ||||||
|  | 			 "unable to find suitable destination conn\n"); | ||||||
|  | 		 rc = -1; | ||||||
|  | 		goto free_ret; | ||||||
|  | 	} | ||||||
|  | 	conn_rtp_dst = &conn_dst->u.rtp; | ||||||
|  |  | ||||||
|  | 	switch (conn_rtp_dst->type) { | ||||||
|  | 	case MGCP_RTP_IUUP: | ||||||
|  | 		return bridge_iuup_to_iuup_peer(conn_rtp_src, conn_rtp_dst, irp); | ||||||
|  | 	case MGCP_RTP_DEFAULT: | ||||||
|  | 		return bridge_iuup_to_rtp_peer(conn_rtp_src, conn_rtp_dst, irp); | ||||||
|  | 	case MGCP_OSMUX_BSC: | ||||||
|  | 	case MGCP_OSMUX_BSC_NAT: | ||||||
|  | 	default: | ||||||
|  | 		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR, | ||||||
|  | 			 "Forward of IuUP payload to RTP connection type %u not supported!\n", | ||||||
|  | 			 conn_rtp_dst->type); | ||||||
|  | 		rc = 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | free_ret: | ||||||
|  | 	msgb_free(irp->oph.msg); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Handle RNL Status-Init primitive received from the IuUP layer FSM. | ||||||
|  |  * Potentially configure sister conn as IuUP Init-Active: */ | ||||||
|  | static int _conn_iuup_rx_rnl_status_init(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp) | ||||||
|  | { | ||||||
|  | 	struct mgcp_conn *conn_dst; | ||||||
|  | 	struct mgcp_conn_rtp *conn_rtp_dst; | ||||||
|  | 	int rc = 0; | ||||||
|  | 	struct msgb *msg; | ||||||
|  |  | ||||||
|  | 	if (conn_rtp_src->iuup.init_ind) { | ||||||
|  | 		/* We received more than one IuUP Initialization. It's probably | ||||||
|  | 		 * a retransmission, so simply ignore it (lower layers take care | ||||||
|  | 		 * of ACKing it). */ | ||||||
|  | 		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_INFO, | ||||||
|  | 		  "Ignoring potential IuUP Initialization retrans\n"); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	msg = msgb_copy_c(conn_rtp_src->conn, irp->oph.msg, "iuup-init-copy"); | ||||||
|  | 	conn_rtp_src->iuup.init_ind = (struct osmo_iuup_rnl_prim *)msgb_data(msg); | ||||||
|  | 	conn_rtp_src->iuup.init_ind->oph.msg = msg; | ||||||
|  |  | ||||||
|  | 	/* Find RFCI containing NO_DATA: */ | ||||||
|  | 	conn_rtp_src->iuup.rfci_id_no_data = _find_rfci_no_data(irp); | ||||||
|  |  | ||||||
|  | 	conn_dst = _find_dst_conn(conn_rtp_src->conn); | ||||||
|  | 	/* If not yet there, peer will potentially be IuUP-Initialized later | ||||||
|  | 	 * when we attempt to bridge audio towards it. See bridge_iuup_to_iuup_peer() */ | ||||||
|  | 	if (!conn_dst) | ||||||
|  | 		return 0; | ||||||
|  | 	conn_rtp_dst = &conn_dst->u.rtp; | ||||||
|  | 	if (!mgcp_conn_rtp_is_iuup(conn_rtp_dst)) | ||||||
|  | 		return 0; /* Nothing to do */ | ||||||
|  |  | ||||||
|  | 	/* We received IuUP parameters on the peer (RNC), Init actively this conn (against CN): */ | ||||||
|  | 	if (!conn_rtp_dst->iuup.configured) | ||||||
|  | 		rc = _conn_iuup_configure_as_active(conn_rtp_dst, irp); | ||||||
|  |  | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Handle RNL Status primitives received from the IuUP layer FSM: */ | ||||||
|  | static int _conn_iuup_rx_rnl_status(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp) | ||||||
|  | { | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	switch (irp->u.status.procedure) { | ||||||
|  | 	case IUUP_PROC_INIT: | ||||||
|  | 		rc = _conn_iuup_rx_rnl_status_init(conn_rtp_src, irp); | ||||||
|  | 		break; | ||||||
|  | 	case IUUP_PROC_RATE_CTRL: | ||||||
|  | 	case IUUP_PROC_TIME_ALIGN: | ||||||
|  | 	case IUUP_PROC_ERR_EVENT: | ||||||
|  | 	default: | ||||||
|  | 		LOG_CONN_RTP(conn_rtp_src, LOGL_ERROR, | ||||||
|  | 			     "Received IuUP RNL STATUS procedure type %u not handled\n", | ||||||
|  | 			     irp->u.status.procedure); | ||||||
|  | 		rc = 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Received RNL primitive from the IuUP layer FSM containing IuUP Status or | ||||||
|  |  * data. Continue pushing it up the stack, either IuUP Status or Data: */ | ||||||
|  | static int _conn_iuup_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx) | ||||||
|  | { | ||||||
|  | 	struct mgcp_conn_rtp *conn_rtp_src = ctx; | ||||||
|  | 	struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph; | ||||||
|  | 	struct msgb *msg = oph->msg; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	switch (OSMO_PRIM_HDR(&irp->oph)) { | ||||||
|  | 	case OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION): | ||||||
|  | 		/* we pass ownsership of msg here: */ | ||||||
|  | 		rc = _conn_iuup_rx_rnl_data(conn_rtp_src, irp); | ||||||
|  | 		break; | ||||||
|  | 	case OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION): | ||||||
|  | 		rc = _conn_iuup_rx_rnl_status(conn_rtp_src, irp); | ||||||
|  | 		msgb_free(msg); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		msgb_free(msg); | ||||||
|  | 		OSMO_ASSERT(false); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Send |RTP+IuUP| data down the stack of the specified destination connection. | ||||||
|  |  *  \param[in] endp associated endpoint (for configuration, logging). | ||||||
|  |  *  \param[in] buf buffer that contains the |RTP+IuUP| data. | ||||||
|  |  *  \param[in] len length of the buffer that contains the |RTP+IuUP| data. | ||||||
|  |  *  \param[in] conn_src associated source connection. | ||||||
|  |  *  \param[in] conn_dst associated destination connection. | ||||||
|  |  *  \returns 0 on success, -1 on ERROR. */ | ||||||
|  | static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg, | ||||||
|  | 		   struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst) | ||||||
|  | { | ||||||
|  | 	/*! When no destination connection is available (e.g. when only one | ||||||
|  | 	 *  connection in loopback mode exists), then the source connection | ||||||
|  | 	 *  shall be specified as destination connection */ | ||||||
|  |  | ||||||
|  | 	struct mgcp_rtp_end *rtp_end; | ||||||
|  | 	struct mgcp_rtp_state *rtp_state; | ||||||
|  | 	char ipbuf[INET6_ADDRSTRLEN]; | ||||||
|  | 	struct rtp_hdr *hdr = (struct rtp_hdr *)msgb_data(msg); | ||||||
|  | 	int buflen = msgb_length(msg); | ||||||
|  | 	char *dest_name; | ||||||
|  | 	int len; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(conn_src); | ||||||
|  | 	OSMO_ASSERT(conn_dst); | ||||||
|  |  | ||||||
|  | 	LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering IuUP packet...\n"); | ||||||
|  |  | ||||||
|  | 	/* Note: In case of loopback configuration, both, the source and the | ||||||
|  | 	 * destination will point to the same connection. */ | ||||||
|  | 	rtp_end = &conn_dst->end; | ||||||
|  | 	rtp_state = &conn_src->state; | ||||||
|  | 	dest_name = conn_dst->conn->name; | ||||||
|  |  | ||||||
|  | 	/* Ensure we have an alternative SSRC in case we need it, see also | ||||||
|  | 	 * gen_rtp_header() */ | ||||||
|  | 	if (rtp_state->alt_rtp_tx_ssrc == 0) | ||||||
|  | 		rtp_state->alt_rtp_tx_ssrc = rand(); | ||||||
|  |  | ||||||
|  | 	if (!rtp_end->output_enabled) { | ||||||
|  | 		rtpconn_rate_ctr_add(conn_dst, endp, RTP_DROPPED_PACKETS_CTR, 1); | ||||||
|  | 		LOGPENDP(endp, DRTP, LOGL_DEBUG, | ||||||
|  | 			 "output disabled, drop to %s %s " | ||||||
|  | 			 "rtp_port:%u rtcp_port:%u\n", | ||||||
|  | 			 dest_name, | ||||||
|  | 			 osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf), | ||||||
|  | 			 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port) | ||||||
|  | 			); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Specs say, in IuUP, the RTP seqnum and timestamp should actually be | ||||||
|  | 	 * ignored by the receiver, but still it's useful for debug purposes | ||||||
|  | 	 * to set it. Moreover, it seems ip.access nano3g produces much worse | ||||||
|  | 	 * audio output on the air side if timestamp is not set properly. */ | ||||||
|  | 	hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate)); | ||||||
|  | 	hdr->sequence = osmo_htons(rtp_state->alt_rtp_tx_sequence); | ||||||
|  | 	hdr->ssrc = rtp_state->alt_rtp_tx_ssrc; | ||||||
|  |  | ||||||
|  | 	LOGPENDP(endp, DRTP, LOGL_DEBUG, | ||||||
|  | 		 "process/send IuUP to %s %s rtp_port:%u rtcp_port:%u\n", | ||||||
|  | 		 dest_name, osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf), | ||||||
|  | 		 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)); | ||||||
|  |  | ||||||
|  | 	/* Forward a copy of the RTP data to a debug ip/port */ | ||||||
|  | 	forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out, | ||||||
|  | 		     msg); | ||||||
|  |  | ||||||
|  | 	len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port, | ||||||
|  | 			    (char *)hdr, buflen); | ||||||
|  |  | ||||||
|  | 	if (len <= 0) | ||||||
|  | 		return len; | ||||||
|  |  | ||||||
|  | 	rtpconn_rate_ctr_add(conn_dst, endp, RTP_PACKETS_TX_CTR, 1); | ||||||
|  | 	rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len); | ||||||
|  | 	rtp_state->alt_rtp_tx_sequence++; | ||||||
|  |  | ||||||
|  | 	return len; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Received TNL primitive from IuUP layer FSM, transmit it further down to the | ||||||
|  |  * socket towards destination peer. */ | ||||||
|  | static int _conn_iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx) | ||||||
|  | { | ||||||
|  | 	struct mgcp_conn_rtp *conn_rtp_dst = ctx; | ||||||
|  | 	struct mgcp_conn *conn_dst = conn_rtp_dst->conn; | ||||||
|  | 	struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph; | ||||||
|  | 	struct mgcp_conn *conn_src; | ||||||
|  | 	struct msgb *msg; | ||||||
|  | 	struct rtp_hdr *rtph; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST)); | ||||||
|  |  | ||||||
|  | 	msg = oph->msg; | ||||||
|  | 	talloc_steal(conn_rtp_dst->conn, msg); | ||||||
|  |  | ||||||
|  | 	msgb_pull_to_l2(msg); | ||||||
|  | 	rtph = (struct rtp_hdr *)msgb_push(msg, sizeof(*rtph)); | ||||||
|  | 	/* TODO: fill rtph properly: */ | ||||||
|  | 	*rtph = (struct rtp_hdr){ | ||||||
|  | 		.csrc_count = 0, | ||||||
|  | 		.extension = 0, | ||||||
|  | 		.padding = 0, | ||||||
|  | 		.version = 2, | ||||||
|  | 		.payload_type = conn_rtp_dst->end.codec->payload_type, | ||||||
|  | 		.marker = 0, | ||||||
|  | 		.sequence = 0, | ||||||
|  | 		.timestamp = 0, | ||||||
|  | 		.ssrc = 0 | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	/* The destination of the destination conn is the source conn, right? */ | ||||||
|  | 	conn_src = _find_dst_conn(conn_dst); | ||||||
|  | 	if (!conn_src) { | ||||||
|  | 		LOG_CONN_RTP(conn_rtp_dst, LOGL_NOTICE, | ||||||
|  | 			     "Couldn't find source conn for IuUP dst conn\n"); | ||||||
|  | 		/* If there's no sister connection we are either still | ||||||
|  | 		 * initializing (so we want to send back Init (ACK)), or we are | ||||||
|  | 		 * probably in loopback mode anyway, so use dst as src. */ | ||||||
|  | 		conn_src = conn_dst; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return mgcp_send_iuup(conn_dst->endp, msg, &conn_src->u.rtp, conn_rtp_dst); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Used to upgrade a regular RTP connection (MGCP_RTP_DEFAULT) to become a IuUP | ||||||
|  |  * connection (MGCP_RTP_IUUP) */ | ||||||
|  | int mgcp_conn_iuup_init(struct mgcp_conn_rtp *conn_rtp) | ||||||
|  | { | ||||||
|  | 	conn_rtp->type = MGCP_RTP_IUUP; | ||||||
|  | 	conn_rtp->iuup.iui = osmo_iuup_instance_alloc(conn_rtp->conn, conn_rtp->conn->id); | ||||||
|  | 	OSMO_ASSERT(conn_rtp->iuup.iui); | ||||||
|  | 	osmo_iuup_instance_set_user_prim_cb(conn_rtp->iuup.iui, _conn_iuup_user_prim_cb, conn_rtp); | ||||||
|  | 	osmo_iuup_instance_set_transport_prim_cb(conn_rtp->iuup.iui, _conn_iuup_transport_prim_cb, conn_rtp); | ||||||
|  | 	conn_rtp->iuup.rfci_id_no_data = -1; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Cleanup specific IuUP connection (MGCP_RTP_IUUP) state, allocated by mgcp_conn_iuup_init() */ | ||||||
|  | void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp) | ||||||
|  | { | ||||||
|  | 	osmo_iuup_instance_free(conn_rtp->iuup.iui); | ||||||
|  | 	conn_rtp->iuup.iui = NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Received RTP+IuUP pkt from socket of conn_rtp_src, build a TNL primitive to | ||||||
|  |  * push it further up the stack to the IuUP layer FSM to handle and/or bridge it */ | ||||||
|  | int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg) | ||||||
|  | { | ||||||
|  | 	struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg); | ||||||
|  | 	struct mgcp_conn_rtp *conn_rtp_src = mc->conn_src; | ||||||
|  | 	int rc = 0; | ||||||
|  | 	bool force_output_enabled = false; | ||||||
|  | 	bool prev_output_enabled; | ||||||
|  | 	struct osmo_sockaddr prev_rem_addr; | ||||||
|  | 	uint16_t prev_rem_rtp_port; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(mgcp_conn_rtp_is_iuup(conn_rtp_src)); | ||||||
|  |  | ||||||
|  | 	if ((rc = check_rtp_iuup(conn_rtp_src, msg)) < 0) | ||||||
|  | 		goto free_ret; | ||||||
|  |  | ||||||
|  | 	if (!conn_rtp_src->iuup.configured) { | ||||||
|  | 		/* We received the first message without sending any, the peer is the active side (RNC). */ | ||||||
|  | 		rc = _conn_iuup_configure_as_passive(conn_rtp_src); | ||||||
|  | 		if (rc < 0) | ||||||
|  | 			goto free_ret; | ||||||
|  | 		/* We need to force allowance of RTP containing Init-ACK back: */ | ||||||
|  | 		prev_output_enabled = conn_rtp_src->end.output_enabled; | ||||||
|  | 		conn_rtp_src->end.output_enabled = true; | ||||||
|  | 		force_output_enabled = true; | ||||||
|  | 		/* Fill in the peer address so that we can send Init-ACK back: */ | ||||||
|  | 		prev_rem_addr = conn_rtp_src->end.addr; | ||||||
|  | 		prev_rem_rtp_port = conn_rtp_src->end.rtp_port; | ||||||
|  | 		conn_rtp_src->end.addr = *mc->from_addr; | ||||||
|  | 		conn_rtp_src->end.rtp_port = htons(osmo_sockaddr_port(&mc->from_addr->u.sa)); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	rc = _conn_iuup_rtp_pl_up(conn_rtp_src, msg); | ||||||
|  |  | ||||||
|  | 	if (force_output_enabled) { | ||||||
|  | 		conn_rtp_src->end.output_enabled = prev_output_enabled; | ||||||
|  | 		conn_rtp_src->end.addr = prev_rem_addr; | ||||||
|  | 		conn_rtp_src->end.rtp_port = prev_rem_rtp_port; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return rc; | ||||||
|  | free_ret: | ||||||
|  | 	msgb_free(msg); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Build IuUP RNL Data primitive from msg containing an incoming RTP pkt from | ||||||
|  |  * peer and send it down the IuUP layer towards the destination as IuUP/RTP: */ | ||||||
|  | int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg) | ||||||
|  | { | ||||||
|  | 	struct osmo_iuup_rnl_prim *irp; | ||||||
|  | 	struct rtp_hdr *rtph; | ||||||
|  | 	int rc = -1; | ||||||
|  | 	int iuup_length = 0; | ||||||
|  | 	int8_t rfci; | ||||||
|  |  | ||||||
|  | 	/* Tx RNL-DATA.req */ | ||||||
|  | 	rtph = (struct rtp_hdr *)msgb_data(msg); | ||||||
|  | 	msgb_pull(msg, sizeof(*rtph)); | ||||||
|  |  | ||||||
|  | 	/* FIXME: validate amr packets */ | ||||||
|  | 	irp = osmo_iuup_rnl_prim_alloc(conn_dest_rtp->conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE); | ||||||
|  | 	irp->u.data.frame_nr = htons(rtph->sequence) % 16; | ||||||
|  |  | ||||||
|  | 	/* TODO: CMR handling & multiple frames handling */ | ||||||
|  |  | ||||||
|  | 	if (strcmp(conn_src_rtp->end.codec->subtype_name, "AMR") != 0) { | ||||||
|  | 		LOG_CONN_RTP(conn_src_rtp, LOGL_ERROR, | ||||||
|  | 			     "Bridge RTP=>IuUP: Bridging src codec %s to IuUP AMR not supported\n", | ||||||
|  | 			     conn_src_rtp->end.codec->subtype_name); | ||||||
|  | 		goto free_ret; | ||||||
|  | 	} | ||||||
|  | 	if (mgcp_codec_amr_is_octet_aligned(conn_src_rtp->end.codec)) { | ||||||
|  | 		struct amr_hdr *amr_hdr = (struct amr_hdr *) msgb_data(msg); | ||||||
|  | 		if (msgb_length(msg) < (sizeof(*amr_hdr))) { | ||||||
|  | 			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, | ||||||
|  | 				     "Bridge RTP=>IuUP: too short for AMR OA hdr (%u)\n", msgb_length(msg)); | ||||||
|  | 			goto free_ret; | ||||||
|  | 		} | ||||||
|  | 		if (amr_hdr->ft >= AMR_FT_MAX) { | ||||||
|  | 			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR OA ft=%u\n", amr_hdr->ft); | ||||||
|  | 			goto free_ret; | ||||||
|  | 		} | ||||||
|  | 		if ((rfci =  _conn_iuup_amr_ft_2_rfci(conn_dest_rtp, amr_hdr->ft)) < 0) { | ||||||
|  | 			LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR OA ft=%u\n", amr_hdr->ft); | ||||||
|  | 			goto free_ret; | ||||||
|  | 		} | ||||||
|  | 		irp->u.data.fqc = amr_hdr->q; | ||||||
|  | 		irp->u.data.rfci = rfci; | ||||||
|  | 		msgb_pull(msg, 2); | ||||||
|  | 	} else { | ||||||
|  | 		uint8_t *amr_bwe_hdr = (uint8_t *) msgb_data(msg); | ||||||
|  | 		int8_t ft; | ||||||
|  | 		if (msgb_length(msg) < 2) { | ||||||
|  | 			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, | ||||||
|  | 				     "Bridge RTP=>IuUP: too short for AMR BE hdr (%u)\n", msgb_length(msg)); | ||||||
|  | 			goto free_ret; | ||||||
|  | 		} | ||||||
|  | 		ft = ((amr_bwe_hdr[0] & 0x07) << 1) | ((amr_bwe_hdr[1] & 0x80) >> 7); | ||||||
|  | 		if (ft >= AMR_FT_MAX) { | ||||||
|  | 			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR BE ft=%u\n", ft); | ||||||
|  | 			goto free_ret; | ||||||
|  | 		} | ||||||
|  | 		if ((rfci =  _conn_iuup_amr_ft_2_rfci(conn_dest_rtp, ft)) < 0) { | ||||||
|  | 			LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR BE ft=%u\n", ft); | ||||||
|  | 			goto free_ret; | ||||||
|  | 		} | ||||||
|  | 		irp->u.data.fqc = ((amr_bwe_hdr[1] & 0x40) >> 6); | ||||||
|  | 		irp->u.data.rfci = rfci; | ||||||
|  | 		rc = iuup_length = osmo_amr_bwe_to_iuup(msgb_data(msg), msgb_length(msg)); | ||||||
|  | 		if (rc < 0) { | ||||||
|  | 			LOG_CONN_RTP(conn_dest_rtp, LOGL_ERROR, "Bridge RTP=>IuUP: Failed convert the RTP/AMR to IuUP payload\n"); | ||||||
|  | 			return rc; | ||||||
|  | 		} | ||||||
|  | 		msgb_trim(msg, iuup_length); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	irp->oph.msg->l3h = msgb_put(irp->oph.msg, msgb_length(msg)); | ||||||
|  | 	memcpy(irp->oph.msg->l3h, msgb_data(msg), msgb_length(msg)); | ||||||
|  | 	if ((rc = osmo_iuup_rnl_prim_down(conn_dest_rtp->iuup.iui, irp)) != 0) | ||||||
|  | 		LOG_CONN_RTP(conn_dest_rtp, LOGL_ERROR, "Bridge RTP=>IuUP: Failed Tx RTP payload down the IuUP layer\n"); | ||||||
|  | 	return rc; | ||||||
|  |  | ||||||
|  | free_ret: | ||||||
|  | 	msgb_free(irp->oph.msg); | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Build IuUP RNL Data primitive from msg containing dummy content and send it | ||||||
|  |  * down the IuUP layer towards the destination as IuUP/RTP: */ | ||||||
|  | int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp) | ||||||
|  | { | ||||||
|  | 	struct osmo_iuup_rnl_prim *irp; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	if (conn_rtp->iuup.rfci_id_no_data == -1) { | ||||||
|  | 		LOG_CONN_RTP(conn_rtp, LOGL_NOTICE, "No RFCI NO_DATA found, unable to send dummy packet\n"); | ||||||
|  | 		return -ENOTSUP; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	irp = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE); | ||||||
|  | 	irp->u.data.frame_nr = 0; | ||||||
|  | 	irp->u.data.fqc = IUUP_FQC_FRAME_GOOD; | ||||||
|  | 	irp->u.data.rfci = conn_rtp->iuup.rfci_id_no_data; | ||||||
|  | 	irp->oph.msg->l3h = irp->oph.msg->tail; | ||||||
|  | 	if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp)) != 0) { | ||||||
|  | 		LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed Tx RTP dummy payload down the IuUP layer\n"); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
| @@ -110,8 +110,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp, | |||||||
|  |  | ||||||
| 	/* Special handling for RTP connections */ | 	/* Special handling for RTP connections */ | ||||||
| 	if (conn->type == MGCP_CONN_TYPE_RTP) { | 	if (conn->type == MGCP_CONN_TYPE_RTP) { | ||||||
| 		conn->u.rtp.end.output_enabled = | 		conn->u.rtp.end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY); | ||||||
| 		    conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn)); | 	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn)); | ||||||
| @@ -121,7 +120,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp, | |||||||
|  |  | ||||||
| 	/* Special handling für RTP connections */ | 	/* Special handling für RTP connections */ | ||||||
| 	if (conn->type == MGCP_CONN_TYPE_RTP) { | 	if (conn->type == MGCP_CONN_TYPE_RTP) { | ||||||
| 		LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %d\n", | 		LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %u\n", | ||||||
| 			 conn->u.rtp.end.output_enabled); | 			 conn->u.rtp.end.output_enabled); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -48,7 +48,7 @@ | |||||||
| #include <osmocom/mgcp/debug.h> | #include <osmocom/mgcp/debug.h> | ||||||
| #include <osmocom/codec/codec.h> | #include <osmocom/codec/codec.h> | ||||||
| #include <osmocom/mgcp/mgcp_e1.h> | #include <osmocom/mgcp/mgcp_e1.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_iuup.h> | ||||||
|  |  | ||||||
| #define RTP_SEQ_MOD		(1 << 16) | #define RTP_SEQ_MOD		(1 << 16) | ||||||
| #define RTP_MAX_DROPOUT		3000 | #define RTP_MAX_DROPOUT		3000 | ||||||
| @@ -59,7 +59,7 @@ enum rtp_proto { | |||||||
| 	MGCP_PROTO_RTCP, | 	MGCP_PROTO_RTCP, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, | void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, | ||||||
| 				 int id, int inc) | 				 int id, int inc) | ||||||
| { | { | ||||||
| 	struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group; | 	struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group; | ||||||
| @@ -156,7 +156,7 @@ void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn) | |||||||
| /* This does not need to be a precision timestamp and | /* This does not need to be a precision timestamp and | ||||||
|  * is allowed to wrap quite fast. The returned value is |  * is allowed to wrap quite fast. The returned value is | ||||||
|  * 1/codec_rate seconds. */ |  * 1/codec_rate seconds. */ | ||||||
| static uint32_t get_current_ts(unsigned codec_rate) | uint32_t mgcp_get_current_ts(unsigned codec_rate) | ||||||
| { | { | ||||||
| 	struct timespec tp; | 	struct timespec tp; | ||||||
| 	uint64_t ret; | 	uint64_t ret; | ||||||
| @@ -529,7 +529,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp, | |||||||
| 	rtp_hdr = (struct rtp_hdr *)msgb_data(msg); | 	rtp_hdr = (struct rtp_hdr *)msgb_data(msg); | ||||||
| 	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 = mgcp_get_current_ts(rtp_end->codec->rate); | ||||||
| 	ssrc = ntohl(rtp_hdr->ssrc); | 	ssrc = ntohl(rtp_hdr->ssrc); | ||||||
| 	marker_bit = !!rtp_hdr->marker; | 	marker_bit = !!rtp_hdr->marker; | ||||||
| 	transit = arrival_time - timestamp; | 	transit = arrival_time - timestamp; | ||||||
| @@ -789,7 +789,7 @@ static int amr_oa_check(char *data, int len) | |||||||
|  |  | ||||||
| /* 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, struct msgb *msg) | void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg) | ||||||
| { | { | ||||||
| 	int rc; | 	int rc; | ||||||
|  |  | ||||||
| @@ -815,7 +815,7 @@ static void gen_rtp_header(struct msgb *msg, struct mgcp_rtp_end *rtp_end, | |||||||
|  |  | ||||||
| 	hdr->version = 2; | 	hdr->version = 2; | ||||||
| 	hdr->payload_type = rtp_end->codec->payload_type; | 	hdr->payload_type = rtp_end->codec->payload_type; | ||||||
| 	hdr->timestamp = osmo_htonl(get_current_ts(rtp_end->codec->rate)); | 	hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate)); | ||||||
| 	hdr->sequence = osmo_htons(state->alt_rtp_tx_sequence); | 	hdr->sequence = osmo_htons(state->alt_rtp_tx_sequence); | ||||||
| 	hdr->ssrc = state->alt_rtp_tx_ssrc; | 	hdr->ssrc = state->alt_rtp_tx_ssrc; | ||||||
| } | } | ||||||
| @@ -975,7 +975,7 @@ static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg) | |||||||
| 	 * the length is because we currently handle IUUP packets as RTP | 	 * the length is because we currently handle IUUP packets as RTP | ||||||
| 	 * packets, so they must pass this check, if we weould be more | 	 * packets, so they must pass this check, if we weould be more | ||||||
| 	 * strict here, we would possibly break 3G. (see also FIXME note | 	 * strict here, we would possibly break 3G. (see also FIXME note | ||||||
| 	 * below */ | 	 * below.*/ | ||||||
|  |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| @@ -1013,6 +1013,19 @@ static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg) | |||||||
| 			 "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((char*)msgb_data(msg), msgb_length(msg), conn_dst); | 		return osmux_xfrm_to_osmux((char*)msgb_data(msg), msgb_length(msg), conn_dst); | ||||||
|  | 	case MGCP_RTP_IUUP: | ||||||
|  | 		if (proto == MGCP_PROTO_RTP) { | ||||||
|  | 			LOGPENDP(endp, DRTP, LOGL_DEBUG, | ||||||
|  | 				 "endpoint type is MGCP_RTP_IUUP, " | ||||||
|  | 				 "using mgcp_conn_iuup_send_rtp() to forward data over IuUP\n"); | ||||||
|  | 			return mgcp_conn_iuup_send_rtp(conn_src, conn_dst, msg); | ||||||
|  | 		} | ||||||
|  | 		/* RTCP: we forward as usual for regular RTP connection */ | ||||||
|  | 		LOGPENDP(endp, DRTP, LOGL_DEBUG, | ||||||
|  | 			 "endpoint type is MGCP_RTP_IUUP and proto!=MGCP_PROTO_RTP, " | ||||||
|  | 			 "using mgcp_send() to forward data directly\n"); | ||||||
|  | 		return mgcp_send(endp, false, | ||||||
|  | 				 mc->from_addr, msg, conn_src, 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 | ||||||
| @@ -1073,8 +1086,11 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn) | |||||||
| 	if (check_rtp_destin(conn) != 0) | 	if (check_rtp_destin(conn) != 0) | ||||||
| 		goto failed; | 		goto failed; | ||||||
|  |  | ||||||
| 	rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr, | 	if (mgcp_conn_rtp_is_iuup(conn)) | ||||||
| 			   conn->end.rtp_port, rtp_dummy_payload, sizeof(rtp_dummy_payload)); | 		rc = mgcp_conn_iuup_send_dummy(conn); | ||||||
|  | 	else | ||||||
|  | 		rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr, conn->end.rtp_port, | ||||||
|  | 				   rtp_dummy_payload, sizeof(rtp_dummy_payload)); | ||||||
|  |  | ||||||
| 	if (rc == -1) | 	if (rc == -1) | ||||||
| 		goto failed; | 		goto failed; | ||||||
| @@ -1138,7 +1154,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr | |||||||
| 	 * course unable to patch the payload type. A situation like this | 	 * course unable to patch the payload type. A situation like this | ||||||
| 	 * 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 && conn_dst->type != MGCP_RTP_IUUP) { | ||||||
| 		rc = mgcp_patch_pt(conn_src, conn_dst, msg); | 		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, | ||||||
| @@ -1185,7 +1201,9 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr | |||||||
| 				mgcp_patch_and_count(endp, rtp_state, rtp_end, | 				mgcp_patch_and_count(endp, rtp_state, rtp_end, | ||||||
| 						     addr, msg); | 						     addr, msg); | ||||||
|  |  | ||||||
| 			if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) { | 			if (mgcp_conn_rtp_is_iuup(conn_dst) || mgcp_conn_rtp_is_iuup(conn_src)) { | ||||||
|  | 				/* the iuup code will correctly transform to the correct AMR mode */ | ||||||
|  | 			} else if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) { | ||||||
| 				rc = amr_oa_bwe_convert(endp, msg, | 				rc = amr_oa_bwe_convert(endp, msg, | ||||||
| 							conn_dst->end.codec->param.amr_octet_aligned); | 							conn_dst->end.codec->param.amr_octet_aligned); | ||||||
| 				if (rc < 0) { | 				if (rc < 0) { | ||||||
| @@ -1211,27 +1229,9 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *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_tap(rtp_end->rtp.fd, &conn_src->tap_out, | ||||||
| 				     msg); | 				     msg); | ||||||
|  |  | ||||||
| 			/* FIXME: HACK HACK HACK. See OS#2459. |  | ||||||
| 			 * 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 |  | ||||||
| 			 * cells (as long as we patch only the first RTP payload in each stream). |  | ||||||
| 			 */ |  | ||||||
| 			if (!rtp_state->patched_first_rtp_payload |  | ||||||
| 			    && conn_src->conn->mode == MGCP_CONN_LOOPBACK) { |  | ||||||
| 				uint8_t *data = msgb_data(msg) + 12; |  | ||||||
| 				if (data[0] == 0xe0) { |  | ||||||
| 					data[0] = 0xe4; |  | ||||||
| 					data[1] = 0x00; |  | ||||||
| 					rtp_state->patched_first_rtp_payload = true; |  | ||||||
| 					LOGPENDP(endp, DRTP, LOGL_DEBUG, |  | ||||||
| 						 "Patching over first two bytes" |  | ||||||
| 						 " to fake an IuUP Initialization Ack\n"); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port, | 			len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port, | ||||||
| 					    (char *)msgb_data(msg), msgb_length(msg)); | 					    (char *)msgb_data(msg), msgb_length(msg)); | ||||||
|  |  | ||||||
| @@ -1291,6 +1291,9 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg) | |||||||
| 	 *  destination connection is known the RTP packet is sent via | 	 *  destination connection is known the RTP packet is sent via | ||||||
| 	 *  the destination connection. */ | 	 *  the destination connection. */ | ||||||
|  |  | ||||||
|  | 	/* If source is IuUP, we need to handle state, forward it through specific bridge path: */ | ||||||
|  | 	if (mgcp_conn_rtp_is_iuup(conn_src) && mc->proto == MGCP_PROTO_RTP) | ||||||
|  | 		return mgcp_conn_iuup_dispatch_rtp(msg); | ||||||
|  |  | ||||||
| 	 /* Check if the connection is in loopback mode, if yes, just send the | 	 /* Check if the connection is in loopback mode, if yes, just send the | ||||||
| 	 * incoming data back to the origin */ | 	 * incoming data back to the origin */ | ||||||
| @@ -1468,7 +1471,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what) | |||||||
| 	msgb_put(msg, ret); | 	msgb_put(msg, ret); | ||||||
|  |  | ||||||
| 	LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n", | 	LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n", | ||||||
| 		     proto == MGCP_PROTO_RTP ? "RTP" : "RTPC", | 		     proto == MGCP_PROTO_RTP ? "RTP" : "RTCP", | ||||||
| 		     msgb_length(msg), osmo_sockaddr_ntop(&addr.u.sa, ipbuf), | 		     msgb_length(msg), osmo_sockaddr_ntop(&addr.u.sa, ipbuf), | ||||||
| 		     osmo_sockaddr_port(&addr.u.sa)); | 		     osmo_sockaddr_port(&addr.u.sa)); | ||||||
|  |  | ||||||
| @@ -1506,7 +1509,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what) | |||||||
| 	/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */ | 	/* 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 a copy of the RTP data to a debug ip/port */ | ||||||
| 	forward_data(fd->fd, &conn_src->tap_in, msg); | 	forward_data_tap(fd->fd, &conn_src->tap_in, msg); | ||||||
|  |  | ||||||
| 	rc = rx_rtp(msg); | 	rc = rx_rtp(msg); | ||||||
|  |  | ||||||
| @@ -1515,6 +1518,7 @@ out: | |||||||
| 	return rc; | 	return rc; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Note: This function is able to handle RTP and RTCP */ | ||||||
| static int rx_rtp(struct msgb *msg) | static int rx_rtp(struct msgb *msg) | ||||||
| { | { | ||||||
| 	struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg); | 	struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg); | ||||||
| @@ -1531,7 +1535,8 @@ static int rx_rtp(struct msgb *msg) | |||||||
| 	 * framing mode (octet-aligned vs. bandwith-efficient is explicitly | 	 * framing mode (octet-aligned vs. bandwith-efficient is explicitly | ||||||
| 	 * 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 (mc->proto == MGCP_PROTO_RTP && | ||||||
|  | 	    amr_oa_bwe_convert_indicated(conn_src->end.codec)) { | ||||||
| 		int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg)); | 		int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg)); | ||||||
| 		if (oa < 0) | 		if (oa < 0) | ||||||
| 			return -1; | 			return -1; | ||||||
|   | |||||||
| @@ -46,6 +46,7 @@ | |||||||
| #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_conn.h> | #include <osmocom/mgcp/mgcp_conn.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_iuup.h> | ||||||
|  |  | ||||||
| /* Contains the last successfully resolved endpoint name. This variable is used | /* Contains the last successfully resolved endpoint name. This variable is used | ||||||
|  * for the unit-tests to verify that the endpoint was correctly resolved. */ |  * for the unit-tests to verify that the endpoint was correctly resolved. */ | ||||||
| @@ -97,10 +98,6 @@ struct mgcp_request { | |||||||
| 	/* function pointer to the request handler */ | 	/* function pointer to the request handler */ | ||||||
| 	struct msgb *(*handle_request)(struct mgcp_request_data *data); | 	struct msgb *(*handle_request)(struct mgcp_request_data *data); | ||||||
|  |  | ||||||
| 	/* true if the request requires an endpoint, false if only a trunk |  | ||||||
| 	 * is sufficient. (corner cases, e.g. wildcarded DLCX) */ |  | ||||||
| 	bool require_endp; |  | ||||||
|  |  | ||||||
| 	/* a human readable name that describes the request */ | 	/* a human readable name that describes the request */ | ||||||
| 	char *debug_name; | 	char *debug_name; | ||||||
| }; | }; | ||||||
| @@ -112,32 +109,34 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *data); | |||||||
| static struct msgb *handle_rsip(struct mgcp_request_data *data); | static struct msgb *handle_rsip(struct mgcp_request_data *data); | ||||||
| static struct msgb *handle_noti_req(struct mgcp_request_data *data); | static struct msgb *handle_noti_req(struct mgcp_request_data *data); | ||||||
| static const struct mgcp_request mgcp_requests[] = { | static const struct mgcp_request mgcp_requests[] = { | ||||||
| 	{ .name = "AUEP", | 	{ .name = "AUEP", .handle_request = handle_audit_endpoint, .debug_name = "AuditEndpoint" }, | ||||||
| 	  .handle_request = handle_audit_endpoint, | 	{ | ||||||
| 	  .debug_name = "AuditEndpoint", | 		.name = "CRCX", | ||||||
| 	  .require_endp = true }, | 		.handle_request = handle_create_con, | ||||||
| 	{ .name = "CRCX", | 		.debug_name = "CreateConnection", | ||||||
| 	  .handle_request = handle_create_con, | 	}, | ||||||
| 	  .debug_name = "CreateConnection", | 	{ | ||||||
| 	  .require_endp = true }, | 		.name = "DLCX", | ||||||
| 	{ .name = "DLCX", | 		.handle_request = handle_delete_con, | ||||||
| 	  .handle_request = handle_delete_con, | 		.debug_name = "DeleteConnection", | ||||||
| 	  .debug_name = "DeleteConnection", | 	}, | ||||||
| 	  .require_endp = false }, | 	{ | ||||||
| 	{ .name = "MDCX", | 		.name = "MDCX", | ||||||
| 	  .handle_request = handle_modify_con, | 		.handle_request = handle_modify_con, | ||||||
| 	  .debug_name = "ModifiyConnection", | 		.debug_name = "ModifiyConnection", | ||||||
| 	  .require_endp = true }, | 	}, | ||||||
| 	{ .name = "RQNT", | 	{ | ||||||
| 	  .handle_request = handle_noti_req, | 		.name = "RQNT", | ||||||
| 	  .debug_name = "NotificationRequest", | 		.handle_request = handle_noti_req, | ||||||
| 	  .require_endp = true }, | 		.debug_name = "NotificationRequest", | ||||||
|  | 	}, | ||||||
|  |  | ||||||
| 	/* SPEC extension */ | 	/* SPEC extension */ | ||||||
| 	{ .name = "RSIP", | 	{ | ||||||
| 	  .handle_request = handle_rsip, | 		.name = "RSIP", | ||||||
| 	  .debug_name = "ReSetInProgress", | 		.handle_request = handle_rsip, | ||||||
| 	  .require_endp = true }, | 		.debug_name = "ReSetInProgress", | ||||||
|  | 	}, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* Initalize transcoder */ | /* Initalize transcoder */ | ||||||
| @@ -149,7 +148,13 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp, | |||||||
| 	struct mgcp_conn_rtp *conn_dst = conn; | 	struct mgcp_conn_rtp *conn_dst = conn; | ||||||
| 	struct mgcp_conn *_conn; | 	struct mgcp_conn *_conn; | ||||||
|  |  | ||||||
| 	if (conn->type != MGCP_RTP_DEFAULT && !mgcp_conn_rtp_is_osmux(conn)) { | 	switch (conn->type) { | ||||||
|  | 	case MGCP_RTP_DEFAULT: | ||||||
|  | 	case MGCP_OSMUX_BSC: | ||||||
|  | 	case MGCP_OSMUX_BSC_NAT: | ||||||
|  | 	case MGCP_RTP_IUUP: | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
| 		LOGPENDP(endp, DLMGCP, LOGL_NOTICE, | 		LOGPENDP(endp, DLMGCP, LOGL_NOTICE, | ||||||
| 			 "RTP-setup: Endpoint is not configured as RTP default, stopping here!\n"); | 			 "RTP-setup: Endpoint is not configured as RTP default, stopping here!\n"); | ||||||
| 		return 0; | 		return 0; | ||||||
| @@ -173,12 +178,15 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp, | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Helper function to allocate some memory for responses and retransmissions */ | /* Helper function to allocate some memory for responses and retransmissions */ | ||||||
| static struct msgb *mgcp_msgb_alloc(void) | static struct msgb *mgcp_msgb_alloc(void *ctx) | ||||||
| { | { | ||||||
| 	struct msgb *msg; | 	struct msgb *msg; | ||||||
| 	msg = msgb_alloc_headroom(4096, 128, "MGCP msg"); | 	msg = msgb_alloc_headroom_c(ctx, 4096, 128, "MGCP msg"); | ||||||
| 	if (!msg) |  | ||||||
|  | 	if (!msg) { | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n"); | 		LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n"); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return msg; | 	return msg; | ||||||
| } | } | ||||||
| @@ -186,7 +194,7 @@ static struct msgb *mgcp_msgb_alloc(void) | |||||||
| /* Helper function for do_retransmission() and create_resp() */ | /* Helper function for do_retransmission() and create_resp() */ | ||||||
| static struct msgb *create_retransmission_response(const struct mgcp_endpoint *endp) | static struct msgb *create_retransmission_response(const struct mgcp_endpoint *endp) | ||||||
| { | { | ||||||
| 	struct msgb *msg = mgcp_msgb_alloc(); | 	struct msgb *msg = mgcp_msgb_alloc(endp->trunk); | ||||||
| 	if (!msg) | 	if (!msg) | ||||||
| 		return NULL; | 		return NULL; | ||||||
|  |  | ||||||
| @@ -196,15 +204,14 @@ static struct msgb *create_retransmission_response(const struct mgcp_endpoint *e | |||||||
| 	return msg; | 	return msg; | ||||||
| } | } | ||||||
|  |  | ||||||
| static struct msgb *create_resp(struct mgcp_endpoint *endp, int code, | static struct msgb *create_resp(void *msgctx, struct mgcp_endpoint *endp, int code, const char *txt, const char *msg, | ||||||
| 				const char *txt, const char *msg, | 				const char *trans, const char *param, const char *sdp) | ||||||
| 				const char *trans, const char *param, |  | ||||||
| 				const char *sdp) |  | ||||||
| { | { | ||||||
| 	int len; | 	int len; | ||||||
| 	struct msgb *res; | 	struct msgb *res; | ||||||
|  |  | ||||||
| 	res = mgcp_msgb_alloc(); | 	OSMO_ASSERT(msgctx != 0); | ||||||
|  | 	res = mgcp_msgb_alloc(msgctx); | ||||||
| 	if (!res) | 	if (!res) | ||||||
| 		return NULL; | 		return NULL; | ||||||
|  |  | ||||||
| @@ -236,26 +243,22 @@ static struct msgb *create_resp(struct mgcp_endpoint *endp, int code, | |||||||
| 	return res; | 	return res; | ||||||
| } | } | ||||||
|  |  | ||||||
| static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp, | static struct msgb *create_ok_resp_with_param(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg, | ||||||
| 					      int code, const char *msg, | 					      const char *trans, const char *param) | ||||||
| 					      const char *trans, |  | ||||||
| 					      const char *param) |  | ||||||
| { | { | ||||||
| 	return create_resp(endp, code, " OK", msg, trans, param, NULL); | 	return create_resp(msgctx, endp, code, " OK", msg, trans, param, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| static struct msgb *create_ok_response(struct mgcp_endpoint *endp, | static struct msgb *create_ok_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg, | ||||||
| 				       int code, const char *msg, |  | ||||||
| 				       const char *trans) | 				       const char *trans) | ||||||
| { | { | ||||||
| 	return create_ok_resp_with_param(endp, code, msg, trans, NULL); | 	return create_ok_resp_with_param(msgctx, endp, code, msg, trans, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| static struct msgb *create_err_response(struct mgcp_endpoint *endp, | static struct msgb *create_err_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg, | ||||||
| 					int code, const char *msg, |  | ||||||
| 					const char *trans) | 					const char *trans) | ||||||
| { | { | ||||||
| 	return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL); | 	return create_resp(msgctx, endp, code, " FAIL", msg, trans, NULL, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Format MGCP response string (with SDP attached) */ | /* Format MGCP response string (with SDP attached) */ | ||||||
| @@ -278,7 +281,7 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, | |||||||
| 	int rc; | 	int rc; | ||||||
| 	struct msgb *result; | 	struct msgb *result; | ||||||
|  |  | ||||||
| 	sdp = msgb_alloc_headroom(4096, 128, "sdp record"); | 	sdp = msgb_alloc_headroom_c(endp->trunk, 4096, 128, "sdp record"); | ||||||
| 	if (!sdp) | 	if (!sdp) | ||||||
| 		return NULL; | 		return NULL; | ||||||
|  |  | ||||||
| @@ -309,7 +312,7 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, | |||||||
| 	rc = mgcp_write_response_sdp(endp, conn, sdp, addr); | 	rc = mgcp_write_response_sdp(endp, conn, sdp, addr); | ||||||
| 	if (rc < 0) | 	if (rc < 0) | ||||||
| 		goto error; | 		goto error; | ||||||
| 	result = create_resp(endp, 200, " OK", msg, trans_id, NULL, (char*) sdp->data); | 	result = create_resp(endp->trunk, endp, 200, " OK", msg, trans_id, NULL, (char *)sdp->data); | ||||||
| 	msgb_free(sdp); | 	msgb_free(sdp); | ||||||
| 	return result; | 	return result; | ||||||
| error: | error: | ||||||
| @@ -376,7 +379,7 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) | |||||||
| 	if (rc < 0) { | 	if (rc < 0) { | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, "%s: failed to parse MCGP message\n", rq.name); | 		LOGP(DLMGCP, LOGL_ERROR, "%s: failed to parse MCGP message\n", rq.name); | ||||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE)); | 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE)); | ||||||
| 		return create_err_response(NULL, -rc, rq.name, "000000"); | 		return create_err_response(cfg, NULL, -rc, rq.name, "000000"); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */ | 	/* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */ | ||||||
| @@ -397,7 +400,7 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) | |||||||
| 			if (!rq.trunk) { | 			if (!rq.trunk) { | ||||||
| 				LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n", | 				LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n", | ||||||
| 				     rq.name, pdata.epname); | 				     rq.name, pdata.epname); | ||||||
| 				return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans); | 				return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans); | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			/* If the endpoint name suggests that the request refers to a specific endpoint, then the | 			/* If the endpoint name suggests that the request refers to a specific endpoint, then the | ||||||
| @@ -405,7 +408,7 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) | |||||||
| 			LOGP(DLMGCP, LOGL_NOTICE, | 			LOGP(DLMGCP, LOGL_NOTICE, | ||||||
| 			     "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name, | 			     "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name, | ||||||
| 			     pdata.epname, -rq.mgcp_cause); | 			     pdata.epname, -rq.mgcp_cause); | ||||||
| 			return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans); | 			return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans); | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name)); | 		osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name)); | ||||||
| @@ -422,15 +425,6 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) | |||||||
| 	/* Find an appropriate handler for the current request and execute it */ | 	/* Find an appropriate handler for the current request and execute it */ | ||||||
| 	for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) { | 	for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) { | ||||||
| 		if (strcmp(mgcp_requests[i].name, rq.name) == 0) { | 		if (strcmp(mgcp_requests[i].name, rq.name) == 0) { | ||||||
| 			/* Check if the request requires and endpoint, if yes, check if we have it, otherwise don't |  | ||||||
| 			 * execute the request handler. */ |  | ||||||
| 			if (mgcp_requests[i].require_endp && !rq.endp) { |  | ||||||
| 				LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 				     "%s: the request handler \"%s\" requires an endpoint resource for \"%s\", which is not available -- abort\n", |  | ||||||
| 				     rq.name, mgcp_requests[i].debug_name, pdata.epname); |  | ||||||
| 				return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			/* Execute request handler */ | 			/* Execute request handler */ | ||||||
| 			if (rq.endp) | 			if (rq.endp) | ||||||
| 				LOGP(DLMGCP, LOGL_INFO, | 				LOGP(DLMGCP, LOGL_INFO, | ||||||
| @@ -461,7 +455,12 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) | |||||||
| static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq) | static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq) | ||||||
| { | { | ||||||
| 	LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n"); | 	LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n"); | ||||||
| 	return create_ok_response(rq->endp, 200, "AUEP", rq->pdata->trans); | 	if (!rq->endp || !mgcp_endp_avail(rq->endp)) { | ||||||
|  | 		LOGPENDP(rq->endp, DLMGCP, LOGL_ERROR, "AUEP: selected endpoint not available!\n"); | ||||||
|  | 		return create_err_response(rq->trunk, NULL, 501, "AUEP", rq->pdata->trans); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return create_ok_response(rq->trunk, rq->endp, 200, "AUEP", rq->pdata->trans); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Try to find a free port by attempting to bind on it. Also handle the | /* Try to find a free port by attempting to bind on it. Also handle the | ||||||
| @@ -855,11 +854,18 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq) | |||||||
|  |  | ||||||
| 	LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n"); | 	LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n"); | ||||||
|  |  | ||||||
|  | 	/* we must have a free ep */ | ||||||
|  | 	if (!endp) { | ||||||
|  | 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL)); | ||||||
|  | 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, "CRCX: no free endpoints available!\n"); | ||||||
|  | 		return create_err_response(rq->trunk, NULL, 403, "CRCX", pdata->trans); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if (!mgcp_endp_avail(endp)) { | 	if (!mgcp_endp_avail(endp)) { | ||||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL)); | 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL)); | ||||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||||
| 			 "CRCX: selected endpoint not available!\n"); | 			 "CRCX: selected endpoint not available!\n"); | ||||||
| 		return create_err_response(NULL, 501, "CRCX", pdata->trans); | 		return create_err_response(rq->trunk, NULL, 501, "CRCX", pdata->trans); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/* parse CallID C: and LocalParameters L: */ | 	/* parse CallID C: and LocalParameters L: */ | ||||||
| @@ -879,7 +885,7 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq) | |||||||
| 			 * together with a CRCX, the MGW will assign the | 			 * together with a CRCX, the MGW will assign the | ||||||
| 			 * connection identifier by itself on CRCX */ | 			 * connection identifier by itself on CRCX */ | ||||||
| 			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION)); | 			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION)); | ||||||
| 			return create_err_response(NULL, 523, "CRCX", pdata->trans); | 			return create_err_response(rq->trunk, NULL, 523, "CRCX", pdata->trans); | ||||||
| 			break; | 			break; | ||||||
| 		case 'M': | 		case 'M': | ||||||
| 			mode = (const char *)line + 3; | 			mode = (const char *)line + 3; | ||||||
| @@ -905,7 +911,7 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq) | |||||||
| 			LOGPENDP(endp, DLMGCP, LOGL_NOTICE, | 			LOGPENDP(endp, DLMGCP, LOGL_NOTICE, | ||||||
| 				 "CRCX: unhandled option: '%c'/%d\n", *line, *line); | 				 "CRCX: unhandled option: '%c'/%d\n", *line, *line); | ||||||
| 			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM)); | 			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM)); | ||||||
| 			return create_err_response(NULL, 539, "CRCX", pdata->trans); | 			return create_err_response(rq->trunk, NULL, 539, "CRCX", pdata->trans); | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -916,14 +922,14 @@ mgcp_header_done: | |||||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||||
| 			 "CRCX: insufficient parameters, missing callid\n"); | 			 "CRCX: insufficient parameters, missing callid\n"); | ||||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID)); | 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID)); | ||||||
| 		return create_err_response(endp, 516, "CRCX", pdata->trans); | 		return create_err_response(endp, endp, 516, "CRCX", pdata->trans); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (!mode) { | 	if (!mode) { | ||||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||||
| 			 "CRCX: insufficient parameters, missing mode\n"); | 			 "CRCX: insufficient parameters, missing mode\n"); | ||||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE)); | 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE)); | ||||||
| 		return create_err_response(endp, 517, "CRCX", pdata->trans); | 		return create_err_response(endp, endp, 517, "CRCX", pdata->trans); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/* Check if we are able to accept the creation of another connection */ | 	/* Check if we are able to accept the creation of another connection */ | ||||||
| @@ -940,7 +946,7 @@ mgcp_header_done: | |||||||
| 			/* There is no more room for a connection, leave | 			/* There is no more room for a connection, leave | ||||||
| 			 * everything as it is and return with an error */ | 			 * everything as it is and return with an error */ | ||||||
| 			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED)); | 			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED)); | ||||||
| 			return create_err_response(endp, 540, "CRCX", pdata->trans); | 			return create_err_response(endp, endp, 540, "CRCX", pdata->trans); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -958,7 +964,7 @@ mgcp_header_done: | |||||||
| 			/* This is not our call, leave everything as it is and | 			/* This is not our call, leave everything as it is and | ||||||
| 			 * return with an error. */ | 			 * return with an error. */ | ||||||
| 			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID)); | 			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID)); | ||||||
| 			return create_err_response(endp, 400, "CRCX", pdata->trans); | 			return create_err_response(endp, endp, 400, "CRCX", pdata->trans); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -969,7 +975,7 @@ mgcp_header_done: | |||||||
| 		rc = mgcp_endp_claim(endp, callid); | 		rc = mgcp_endp_claim(endp, callid); | ||||||
| 		if (rc != 0) { | 		if (rc != 0) { | ||||||
| 			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM)); | 			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM)); | ||||||
| 			return create_err_response(endp, 502, "CRCX", pdata->trans); | 			return create_err_response(endp, endp, 502, "CRCX", pdata->trans); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -1029,6 +1035,11 @@ mgcp_header_done: | |||||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION)); | 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION)); | ||||||
| 		goto error2; | 		goto error2; | ||||||
| 	} | 	} | ||||||
|  | 	/* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */ | ||||||
|  | 	/* TODO: "codec" probably needs to be moved from endp to conn */ | ||||||
|  | 	if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0) { | ||||||
|  | 		rc = mgcp_conn_iuup_init(conn); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	conn->end.fmtp_extra = talloc_strdup(trunk->endpoints, | 	conn->end.fmtp_extra = talloc_strdup(trunk->endpoints, | ||||||
| 					     trunk->audio_fmtp_extra); | 					     trunk->audio_fmtp_extra); | ||||||
| @@ -1088,7 +1099,7 @@ error2: | |||||||
| 	mgcp_endp_release(endp); | 	mgcp_endp_release(endp); | ||||||
| 	LOGPENDP(endp, DLMGCP, LOGL_NOTICE, | 	LOGPENDP(endp, DLMGCP, LOGL_NOTICE, | ||||||
| 		 "CRCX: unable to create connection\n"); | 		 "CRCX: unable to create connection\n"); | ||||||
| 	return create_err_response(endp, error_code, "CRCX", pdata->trans); | 	return create_err_response(endp, endp, error_code, "CRCX", pdata->trans); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* MDCX command handler, processes the received command */ | /* MDCX command handler, processes the received command */ | ||||||
| @@ -1112,26 +1123,24 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq) | |||||||
|  |  | ||||||
| 	LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n"); | 	LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n"); | ||||||
|  |  | ||||||
| 	if (!mgcp_endp_avail(endp)) { |  | ||||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL)); |  | ||||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, |  | ||||||
| 			 "MDCX: selected endpoint not available!\n"); |  | ||||||
| 		return create_err_response(NULL, 501, "MDCX", pdata->trans); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* Prohibit wildcarded requests */ | 	/* Prohibit wildcarded requests */ | ||||||
| 	if (rq->wildcarded) { | 	if (rq->wildcarded) { | ||||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||||
| 			 "MDCX: wildcarded endpoint names not supported.\n"); | 			 "MDCX: wildcarded endpoint names not supported.\n"); | ||||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD)); | 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD)); | ||||||
| 		return create_err_response(endp, 507, "MDCX", pdata->trans); | 		return create_err_response(rq->trunk, endp, 507, "MDCX", pdata->trans); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if (!endp || !mgcp_endp_avail(endp)) { | ||||||
|  | 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL)); | ||||||
|  | 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, "MDCX: selected endpoint not available!\n"); | ||||||
|  | 		return create_err_response(rq->trunk, NULL, 501, "MDCX", pdata->trans); | ||||||
|  | 	} | ||||||
| 	if (llist_count(&endp->conns) <= 0) { | 	if (llist_count(&endp->conns) <= 0) { | ||||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||||
| 			 "MDCX: endpoint is not holding a connection.\n"); | 			 "MDCX: endpoint is not holding a connection.\n"); | ||||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN)); | 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN)); | ||||||
| 		return create_err_response(endp, 400, "MDCX", pdata->trans); | 		return create_err_response(endp, endp, 400, "MDCX", pdata->trans); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for_each_line(line, pdata->save) { | 	for_each_line(line, pdata->save) { | ||||||
| @@ -1181,7 +1190,7 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq) | |||||||
| 				 "MDCX: Unhandled MGCP option: '%c'/%d\n", | 				 "MDCX: Unhandled MGCP option: '%c'/%d\n", | ||||||
| 				 line[0], line[0]); | 				 line[0], line[0]); | ||||||
| 			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM)); | 			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM)); | ||||||
| 			return create_err_response(NULL, 539, "MDCX", pdata->trans); | 			return create_err_response(rq->trunk, NULL, 539, "MDCX", pdata->trans); | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -1191,13 +1200,13 @@ mgcp_header_done: | |||||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||||
| 			 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n"); | 			 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n"); | ||||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID)); | 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID)); | ||||||
| 		return create_err_response(endp, 515, "MDCX", pdata->trans); | 		return create_err_response(endp, endp, 515, "MDCX", pdata->trans); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	conn = mgcp_conn_get_rtp(endp, conn_id); | 	conn = mgcp_conn_get_rtp(endp, conn_id); | ||||||
| 	if (!conn) { | 	if (!conn) { | ||||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND)); | 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND)); | ||||||
| 		return create_err_response(endp, 400, "MDCX", pdata->trans); | 		return create_err_response(endp, endp, 400, "MDCX", pdata->trans); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	mgcp_conn_watchdog_kick(conn->conn); | 	mgcp_conn_watchdog_kick(conn->conn); | ||||||
| @@ -1304,7 +1313,7 @@ mgcp_header_done: | |||||||
| 	mgcp_endp_update(endp); | 	mgcp_endp_update(endp); | ||||||
| 	return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false); | 	return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false); | ||||||
| error3: | error3: | ||||||
| 	return create_err_response(endp, error_code, "MDCX", pdata->trans); | 	return create_err_response(endp, endp, error_code, "MDCX", pdata->trans); | ||||||
|  |  | ||||||
| out_silent: | out_silent: | ||||||
| 	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n"); | 	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n"); | ||||||
| @@ -1335,14 +1344,27 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq) | |||||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL)); | 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL)); | ||||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||||
| 			 "DLCX: selected endpoint not available!\n"); | 			 "DLCX: selected endpoint not available!\n"); | ||||||
| 		return create_err_response(NULL, 501, "DLCX", pdata->trans); | 		return create_err_response(rq->trunk, NULL, 501, "DLCX", pdata->trans); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (endp && !rq->wildcarded && llist_empty(&endp->conns)) { | 	if (endp && !rq->wildcarded && llist_empty(&endp->conns)) { | ||||||
| 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, | ||||||
| 			 "DLCX: endpoint is not holding a connection.\n"); | 			 "DLCX: endpoint is not holding a connection.\n"); | ||||||
| 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN)); | 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN)); | ||||||
| 		return create_err_response(endp, 515, "DLCX", pdata->trans); | 		return create_err_response(endp, endp, 515, "DLCX", pdata->trans); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Handle wildcarded DLCX that refers to the whole trunk. This means | ||||||
|  | 	 * that we walk over all endpoints on the trunk in order to drop all | ||||||
|  | 	 * connections on the trunk. (see also RFC3435 Annex F.7) */ | ||||||
|  | 	if (rq->wildcarded) { | ||||||
|  | 		int num_conns = 0; | ||||||
|  | 		for (i = 0; i < trunk->number_endpoints; i++) { | ||||||
|  | 			num_conns += llist_count(&trunk->endpoints[i]->conns); | ||||||
|  | 			mgcp_endp_release(trunk->endpoints[i]); | ||||||
|  | 		} | ||||||
|  | 		rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns); | ||||||
|  | 		return create_ok_response(trunk, NULL, 200, "DLCX", pdata->trans); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for_each_line(line, pdata->save) { | 	for_each_line(line, pdata->save) { | ||||||
| @@ -1357,7 +1379,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq) | |||||||
| 				LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE, | 				LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE, | ||||||
| 					  "cannot handle requests with call-id (C) without endpoint -- abort!"); | 					  "cannot handle requests with call-id (C) without endpoint -- abort!"); | ||||||
| 				rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM)); | 				rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM)); | ||||||
| 				return create_err_response(NULL, 539, "DLCX", pdata->trans); | 				return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans); | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if (mgcp_verify_call_id(endp, line + 3) != 0) { | 			if (mgcp_verify_call_id(endp, line + 3) != 0) { | ||||||
| @@ -1373,7 +1395,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq) | |||||||
| 				LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE, | 				LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE, | ||||||
| 					  "cannot handle requests with conn-id (I) without endpoint -- abort!"); | 					  "cannot handle requests with conn-id (I) without endpoint -- abort!"); | ||||||
| 				rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM)); | 				rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM)); | ||||||
| 				return create_err_response(NULL, 539, "DLCX", pdata->trans); | 				return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans); | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			conn_id = (const char *)line + 3; | 			conn_id = (const char *)line + 3; | ||||||
| @@ -1389,24 +1411,11 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq) | |||||||
| 			LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n", | 			LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n", | ||||||
| 				 line[0], line[0]); | 				 line[0], line[0]); | ||||||
| 			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM)); | 			rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM)); | ||||||
| 			return create_err_response(NULL, 539, "DLCX", pdata->trans); | 			return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans); | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/* Handle wildcarded DLCX that refers to the whole trunk. This means |  | ||||||
| 	 * that we walk over all endpoints on the trunk in order to drop all |  | ||||||
| 	 * connections on the trunk. (see also RFC3435 Annex F.7) */ |  | ||||||
| 	if (rq->wildcarded) { |  | ||||||
| 		int num_conns = 0; |  | ||||||
| 		for (i = 0; i < trunk->number_endpoints; i++) { |  | ||||||
| 			num_conns += llist_count(&trunk->endpoints[i]->conns); |  | ||||||
| 			mgcp_endp_release(trunk->endpoints[i]); |  | ||||||
| 		} |  | ||||||
| 		rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns); |  | ||||||
| 		return create_ok_response(NULL, 200, "DLCX", pdata->trans); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* The logic does not permit to go past this point without having the | 	/* The logic does not permit to go past this point without having the | ||||||
| 	 * the endp pointer populated. */ | 	 * the endp pointer populated. */ | ||||||
| 	OSMO_ASSERT(endp); | 	OSMO_ASSERT(endp); | ||||||
| @@ -1429,7 +1438,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq) | |||||||
| 		/* Note: In this case we do not return any statistics, | 		/* Note: In this case we do not return any statistics, | ||||||
| 		 * as we assume that the client is not interested in | 		 * as we assume that the client is not interested in | ||||||
| 		 * this case. */ | 		 * this case. */ | ||||||
| 		return create_ok_response(endp, 200, "DLCX", pdata->trans); | 		return create_ok_response(endp, endp, 200, "DLCX", pdata->trans); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/* Find the connection */ | 	/* Find the connection */ | ||||||
| @@ -1458,10 +1467,10 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq) | |||||||
| 	rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS)); | 	rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS)); | ||||||
| 	if (silent) | 	if (silent) | ||||||
| 		goto out_silent; | 		goto out_silent; | ||||||
| 	return create_ok_resp_with_param(endp, 250, "DLCX", pdata->trans, stats); | 	return create_ok_resp_with_param(endp, endp, 250, "DLCX", pdata->trans, stats); | ||||||
|  |  | ||||||
| error3: | error3: | ||||||
| 	return create_err_response(endp, error_code, "DLCX", pdata->trans); | 	return create_err_response(endp, endp, error_code, "DLCX", pdata->trans); | ||||||
|  |  | ||||||
| out_silent: | out_silent: | ||||||
| 	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n"); | 	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n"); | ||||||
| @@ -1516,14 +1525,13 @@ static struct msgb *handle_noti_req(struct mgcp_request_data *rq) | |||||||
|  |  | ||||||
| 	/* we didn't see a signal request with a tone */ | 	/* we didn't see a signal request with a tone */ | ||||||
| 	if (tone == CHAR_MAX) | 	if (tone == CHAR_MAX) | ||||||
| 		return create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans); | 		return create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans); | ||||||
|  |  | ||||||
| 	if (rq->pdata->cfg->rqnt_cb) | 	if (rq->pdata->cfg->rqnt_cb) | ||||||
| 		res = rq->pdata->cfg->rqnt_cb(rq->endp, tone); | 		res = rq->pdata->cfg->rqnt_cb(rq->endp, tone); | ||||||
|  |  | ||||||
| 	return res == 0 ? | 	return res == 0 ? create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans) : | ||||||
| 	    create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans) : | 				create_err_response(rq->endp, rq->endp, res, "RQNT", rq->pdata->trans); | ||||||
| 	    create_err_response(rq->endp, res, "RQNT", rq->pdata->trans); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Connection keepalive timer, will take care that dummy packets are send | /* Connection keepalive timer, will take care that dummy packets are send | ||||||
|   | |||||||
| @@ -98,7 +98,7 @@ static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used) | |||||||
| /* Helper function to update codec map information with additional data from | /* Helper function to update codec map information with additional data from | ||||||
|  * SDP, called from: mgcp_parse_sdp_data() */ |  * SDP, called from: mgcp_parse_sdp_data() */ | ||||||
| static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, | static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, | ||||||
| 			  int payload, const char *audio_name) | 			  int payload_type, const char *audio_name) | ||||||
| { | { | ||||||
| 	int i; | 	int i; | ||||||
|  |  | ||||||
| @@ -110,7 +110,7 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, | |||||||
| 		/* Note: We can only update payload codecs that already exist | 		/* Note: We can only update payload codecs that already exist | ||||||
| 		 * in our codec list. If we get an unexpected payload type, | 		 * in our codec list. If we get an unexpected payload type, | ||||||
| 		 * we just drop it */ | 		 * we just drop it */ | ||||||
| 		if (codecs[i].payload_type != payload) | 		if (codecs[i].payload_type != payload_type) | ||||||
| 			continue; | 			continue; | ||||||
|  |  | ||||||
| 		if (sscanf(audio_name, "%63[^/]/%d/%d", | 		if (sscanf(audio_name, "%63[^/]/%d/%d", | ||||||
| @@ -127,7 +127,7 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, | |||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload, | 	LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload_type, | ||||||
| 	     audio_name); | 	     audio_name); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -334,7 +334,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp, | |||||||
| 	void *tmp_ctx = talloc_new(NULL); | 	void *tmp_ctx = talloc_new(NULL); | ||||||
| 	struct mgcp_rtp_end *rtp; | 	struct mgcp_rtp_end *rtp; | ||||||
|  |  | ||||||
| 	int payload; | 	int payload_type; | ||||||
| 	int ptime, ptime2 = 0; | 	int ptime, ptime2 = 0; | ||||||
| 	char audio_name[64]; | 	char audio_name[64]; | ||||||
| 	int port, rc; | 	int port, rc; | ||||||
| @@ -355,8 +355,8 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp, | |||||||
| 			/* skip these SDP attributes */ | 			/* skip these SDP attributes */ | ||||||
| 			break; | 			break; | ||||||
| 		case 'a': | 		case 'a': | ||||||
| 			if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) { | 			if (sscanf(line, "a=rtpmap:%d %63s", &payload_type, audio_name) == 2) { | ||||||
| 				codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name); | 				codecs_update(tmp_ctx, codecs, codecs_used, payload_type, audio_name); | ||||||
| 				break; | 				break; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										207
									
								
								src/libosmo-mgcp/mgcp_threads_queue.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								src/libosmo-mgcp/mgcp_threads_queue.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,207 @@ | |||||||
|  | /* | ||||||
|  |  * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * Author: Eric Wild | ||||||
|  |  * | ||||||
|  |  * 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 <inttypes.h> | ||||||
|  | #include <stdatomic.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <sys/eventfd.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <talloc.h> | ||||||
|  |  | ||||||
|  | #include <osmocom/mgcp/mgcp_threads_queue.h> | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | classic lamport circular lockfree spsc queue: | ||||||
|  | every "side" only writes its own ptr, but may read the other sides ptr | ||||||
|  |  | ||||||
|  | notify reader using eventfd as soon as element is added, reader then reads until | ||||||
|  | read fails | ||||||
|  | -> reader pops in a loop until FALSE and might get spurious events because it | ||||||
|  | read before it was notified, which is fine | ||||||
|  | -> writing pushes *the same data* in a loop until TRUE, blocks | ||||||
|  |  | ||||||
|  | shutting this down requires | ||||||
|  | 1) to stop reading and pushing | ||||||
|  | 2) ONE side to take care of the eventfds | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | static struct spsc *spsc_init(void *talloc_ctx, unsigned int count, unsigned int size_per_buf, bool blockr, bool blockw) | ||||||
|  | { | ||||||
|  | 	struct spsc *q = talloc_zero_size(talloc_ctx, sizeof(struct spsc) + sizeof(uintptr_t) * count); | ||||||
|  | 	atomic_init(&q->readptr, 0); | ||||||
|  | 	atomic_init(&q->writeptr, 0); | ||||||
|  | 	q->efd_r = eventfd(0, blockr ? 0 : EFD_NONBLOCK); | ||||||
|  | 	q->efd_w = eventfd(1, blockw ? 0 : EFD_NONBLOCK); | ||||||
|  | 	q->count = count; | ||||||
|  | 	q->size_per_buf = size_per_buf; | ||||||
|  | 	q->buf = talloc_zero_size(q, size_per_buf * count); | ||||||
|  |  | ||||||
|  | 	for (int i = 0; i < count; i++) | ||||||
|  | 		q->data[i] = (uintptr_t)q->buf + i * size_per_buf; | ||||||
|  | 	return q; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void spsc_deinit(struct spsc *q) | ||||||
|  | { | ||||||
|  | 	talloc_free(q->buf); | ||||||
|  | 	close(q->efd_r); | ||||||
|  | 	close(q->efd_w); | ||||||
|  | 	talloc_free(q); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static ssize_t spsc_check_r(struct spsc *q) | ||||||
|  | { | ||||||
|  | 	uint64_t efdr; | ||||||
|  | 	return read(q->efd_r, &efdr, sizeof(uint64_t)); | ||||||
|  | } | ||||||
|  | static ssize_t spsc_check_w(struct spsc *q) | ||||||
|  | { | ||||||
|  | 	uint64_t efdr; | ||||||
|  | 	return read(q->efd_w, &efdr, sizeof(uint64_t)); | ||||||
|  | } | ||||||
|  | static void spsc_notify_r(struct spsc *q) | ||||||
|  | { | ||||||
|  | 	uint64_t efdu = 1; | ||||||
|  | 	write(q->efd_r, &efdu, sizeof(uint64_t)); | ||||||
|  | } | ||||||
|  | static void spsc_notify_w(struct spsc *q) | ||||||
|  | { | ||||||
|  | 	uint64_t efdu = 1; | ||||||
|  | 	write(q->efd_w, &efdu, sizeof(uint64_t)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Adds element to the queue by copying the data. | ||||||
|  |  *  \param[in] q queue. | ||||||
|  |  *  \param[in] elem input buffer, must match the originally configured queue buffer size!. | ||||||
|  |  *  \returns true if queue was not full and element was successfully pushed */ | ||||||
|  | bool spsc_push(struct spsc *q, void *elem) | ||||||
|  | { | ||||||
|  | 	size_t cur_wp, cur_rp; | ||||||
|  | 	cur_wp = atomic_load_explicit(&q->writeptr, memory_order_relaxed); | ||||||
|  | 	cur_rp = atomic_load_explicit(&q->readptr, memory_order_acquire); | ||||||
|  | 	if ((cur_wp + 1) % q->count == cur_rp) { | ||||||
|  | 		spsc_check_w(q); /* blocks, ensures next (!) call succeeds */ | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 	memcpy((void *)q->data[cur_wp], elem, q->size_per_buf); | ||||||
|  | 	atomic_store_explicit(&q->writeptr, (cur_wp + 1) % q->count, memory_order_release); | ||||||
|  | 	spsc_notify_r(q); /* fine after release */ | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Reads the read-fd of the queue, which, depending on settings passed on queue creation, blocks. | ||||||
|  |  * This function can be used to deliberately wait for a non-empty queue on the read side. | ||||||
|  |  *  \param[in] q queue. | ||||||
|  |  *  \returns result of reading the fd. */ | ||||||
|  | ssize_t spsc_prep_pop(struct spsc *q) | ||||||
|  | { | ||||||
|  | 	return spsc_check_r(q); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Removes element from the queue by copying the data. | ||||||
|  |  *  \param[in] q queue. | ||||||
|  |  *  \param[in] elem output buffer, must match the originally configured queue buffer size!. | ||||||
|  |  *  \returns true if queue was not empty and element was successfully removed */ | ||||||
|  | bool spsc_pop(struct spsc *q, void *elem) | ||||||
|  | { | ||||||
|  | 	size_t cur_wp, cur_rp; | ||||||
|  | 	cur_wp = atomic_load_explicit(&q->writeptr, memory_order_acquire); | ||||||
|  | 	cur_rp = atomic_load_explicit(&q->readptr, memory_order_relaxed); | ||||||
|  |  | ||||||
|  | 	if (cur_wp == cur_rp) /* blocks via prep_pop */ | ||||||
|  | 		return false; | ||||||
|  | 	memcpy(elem, (void *)q->data[cur_rp], q->size_per_buf); | ||||||
|  | 	atomic_store_explicit(&q->readptr, (cur_rp + 1) % q->count, memory_order_release); | ||||||
|  | 	spsc_notify_w(q); | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Creates a bidirectional queue channel that consists of two queues, one in each direction, | ||||||
|  |  *  commonly referred to as a and b side. | ||||||
|  |  *  \param[in] talloc_ctx allocation context. | ||||||
|  |  *  \param[in] count number of buffers per queue. | ||||||
|  |  *  \param[in] size_per_buf size of buffers per queue. | ||||||
|  |  *  \param[in] blockr_a should reading the a-side read fd block?. | ||||||
|  |  *  \param[in] blockw_a should reading the a-side write fd block?. | ||||||
|  |  *  \param[in] blockr_b should reading the b-side read fd block?. | ||||||
|  |  *  \param[in] blockw_b should reading the b-side write fd block?. | ||||||
|  |   *  \returns queue channel */ | ||||||
|  | struct qchan spsc_chan_init_ex(void *talloc_ctx, unsigned int count, unsigned int size_per_buf, bool blockr_a, | ||||||
|  | 			       bool blockw_a, bool blockr_b, bool blockw_b) | ||||||
|  | { | ||||||
|  | 	struct qchan q; | ||||||
|  | 	q.a = spsc_init(talloc_ctx, count, size_per_buf, blockr_a, blockw_a); | ||||||
|  | 	q.b = spsc_init(talloc_ctx, count, size_per_buf, blockr_b, blockw_b); | ||||||
|  | 	return q; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Creates a bidirectional queue channel that consists of two queues, one in each direction, | ||||||
|  |  *  commonly referred to as a and b side. | ||||||
|  |  *  \param[in] talloc_ctx allocation context. | ||||||
|  |  *  \param[in] count number of buffers per queue. | ||||||
|  |  *  \param[in] size_per_buf size of buffers per queue. | ||||||
|  |  *  \returns queue channel */ | ||||||
|  | struct qchan spsc_chan_init(void *talloc_ctx, unsigned int count, unsigned int size_per_buf) | ||||||
|  | { | ||||||
|  | 	return spsc_chan_init_ex(talloc_ctx, count, size_per_buf, false, true, false, true); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Closes a bidirectional queue channel. | ||||||
|  |  *  \param[in] q queue */ | ||||||
|  | void spsc_chan_close(struct qchan *q) | ||||||
|  | { | ||||||
|  | 	spsc_deinit(q->a); | ||||||
|  | 	spsc_deinit(q->b); | ||||||
|  | 	free(q); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Gets queue channel read/write fd for a/b side according to function name. | ||||||
|  |  *  \param[in] q queue channel. | ||||||
|  |  *  \returns fd */ | ||||||
|  | int spsc_get_a_rdfd(struct qchan *q) | ||||||
|  | { | ||||||
|  | 	return q->a->efd_r; | ||||||
|  | } | ||||||
|  | /*! Gets queue channel read/write fd for a/b side according to function name. | ||||||
|  |  *  \param[in] q queue channel. | ||||||
|  |  *  \returns fd */ | ||||||
|  | int spsc_get_b_rdfd(struct qchan *q) | ||||||
|  | { | ||||||
|  | 	return q->b->efd_r; | ||||||
|  | } | ||||||
|  | /*! Gets queue channel read/write fd for a/b side according to function name. | ||||||
|  |  *  \param[in] q queue channel. | ||||||
|  |  *  \returns fd */ | ||||||
|  | int spsc_get_a_wrfd(struct qchan *q) | ||||||
|  | { | ||||||
|  | 	return q->a->efd_w; | ||||||
|  | } | ||||||
|  | /*! Gets queue channel read/write fd for a/b side according to function name. | ||||||
|  |  *  \param[in] q queue channel. | ||||||
|  |  *  \returns fd */ | ||||||
|  | int spsc_get_b_wrfd(struct qchan *q) | ||||||
|  | { | ||||||
|  | 	return q->b->efd_w; | ||||||
|  | } | ||||||
| @@ -24,7 +24,7 @@ EXTRA_DIST = \ | |||||||
| 	mgcp_test.ok \ | 	mgcp_test.ok \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|  |  | ||||||
| noinst_PROGRAMS = \ | check_PROGRAMS = \ | ||||||
| 	mgcp_test \ | 	mgcp_test \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -868,7 +868,7 @@ static void test_messages(void) | |||||||
| 					printf("Connection mode not set\n"); | 					printf("Connection mode not set\n"); | ||||||
|  |  | ||||||
| 				OSMO_ASSERT(conn->end.output_enabled | 				OSMO_ASSERT(conn->end.output_enabled | ||||||
| 					    == (conn->conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0)); | 					    == !!(conn->conn->mode & MGCP_CONN_SEND_ONLY)); | ||||||
|  |  | ||||||
| 				conn->conn->mode |= CONN_UNMODIFIED; | 				conn->conn->mode |= CONN_UNMODIFIED; | ||||||
|  |  | ||||||
| @@ -1896,16 +1896,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat | |||||||
| 		.codecs = { | 		.codecs = { | ||||||
| 			{ | 			{ | ||||||
| 				{ 111, "AMR/8000", &amr_param_octet_aligned_true, }, | 				{ 111, "AMR/8000", &amr_param_octet_aligned_true, }, | ||||||
| 				{ 112, "AMR/8000", &amr_param_octet_aligned_false, }, |  | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
| 				{ 122, "AMR/8000", &amr_param_octet_aligned_false, }, | 				{ 122, "AMR/8000", &amr_param_octet_aligned_false, }, | ||||||
| 				{ 121, "AMR/8000", &amr_param_octet_aligned_true, }, |  | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		.expect = { | 		.expect = { | ||||||
| 			{ .payload_type_map = {111, 121}, }, | 			{ .payload_type_map = {111, 122}, }, | ||||||
| 			{ .payload_type_map = {112, 122} }, |  | ||||||
| 			{ .end = true }, | 			{ .end = true }, | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| @@ -1914,15 +1911,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat | |||||||
| 		.codecs = { | 		.codecs = { | ||||||
| 			{ | 			{ | ||||||
| 				{ 111, "AMR/8000", &amr_param_octet_aligned_true, }, | 				{ 111, "AMR/8000", &amr_param_octet_aligned_true, }, | ||||||
| 				{ 112, "AMR/8000", &amr_param_octet_aligned_false, }, |  | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
| 				{ 122, "AMR/8000", &amr_param_octet_aligned_unset, }, | 				{ 122, "AMR/8000", &amr_param_octet_aligned_unset, }, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		.expect = { | 		.expect = { | ||||||
| 			{ .payload_type_map = {111, -EINVAL}, }, | 			{ .payload_type_map = {111, 122}, }, | ||||||
| 			{ .payload_type_map = {112, 122} }, |  | ||||||
| 			{ .end = true }, | 			{ .end = true }, | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| @@ -1931,15 +1926,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat | |||||||
| 		.codecs = { | 		.codecs = { | ||||||
| 			{ | 			{ | ||||||
| 				{ 111, "AMR/8000", &amr_param_octet_aligned_true, }, | 				{ 111, "AMR/8000", &amr_param_octet_aligned_true, }, | ||||||
| 				{ 112, "AMR/8000", &amr_param_octet_aligned_false, }, |  | ||||||
| 			}, | 			}, | ||||||
| 			{ | 			{ | ||||||
| 				{ 122, "AMR/8000", NULL, }, | 				{ 122, "AMR/8000", NULL, }, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		.expect = { | 		.expect = { | ||||||
| 			{ .payload_type_map = {111, -EINVAL}, }, | 			{ .payload_type_map = {111, 122}, }, | ||||||
| 			{ .payload_type_map = {112, 122} }, |  | ||||||
| 			{ .end = true }, | 			{ .end = true }, | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
|   | |||||||
| @@ -1347,32 +1347,24 @@ Testing mgcp_codec_pt_translate() | |||||||
|  - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22 |  - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22 | ||||||
|  - mgcp_codec_pt_translate(conn0, conn1, 0) -> -22 |  - mgcp_codec_pt_translate(conn0, conn1, 0) -> -22 | ||||||
|  - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22 |  - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22 | ||||||
| #4: conn1 has no codecs |  | ||||||
| #4: conn1 has no codecs | #4: conn1 has no codecs | ||||||
|  - add codecs on conn0: |  - add codecs on conn0: | ||||||
|     1:   0 PCMU/8000/1  -> rc=0 |     0: 112 AMR/8000/1 octet-aligned=1  -> rc=0 | ||||||
|     2: 111 GSM-HR-08/8000/1  -> rc=0 |     1:   0 PCMU/8000/1  -> rc=0 | ||||||
|  - add codecs on conn1: |  | ||||||
|     (none) |  | ||||||
|  - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22 |  | ||||||
|     2: 111 GSM-HR-08/8000/1  -> rc=0 |     2: 111 GSM-HR-08/8000/1  -> rc=0 | ||||||
|  - add codecs on conn1: |  - add codecs on conn1: | ||||||
|     (none) |     (none) | ||||||
|  - add codecs on conn0: |  | ||||||
|  - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22 |  - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22 | ||||||
|  - mgcp_codec_pt_translate(conn0, conn1, 0) -> -22 |  - mgcp_codec_pt_translate(conn0, conn1, 0) -> -22 | ||||||
|  - add codecs on conn1: |  - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22 | ||||||
|     0: 122 AMR/8000 octet-aligned=0  -> rc=0 | #5: test AMR with differing octet-aligned settings | ||||||
|     1: 121 AMR/8000 octet-aligned=1  -> rc=0 |  | ||||||
|  - add codecs on conn0: |  - add codecs on conn0: | ||||||
|     0: 111 AMR/8000 octet-aligned=1  -> rc=0 |     0: 111 AMR/8000 octet-aligned=1  -> rc=0 | ||||||
|  - add codecs on conn1: |  - add codecs on conn1: | ||||||
|  - mgcp_codec_pt_translate(conn1, conn0, 122) -> 112 |  | ||||||
|     0: 122 AMR/8000 octet-aligned=0  -> rc=0 |     0: 122 AMR/8000 octet-aligned=0  -> rc=0 | ||||||
|  - mgcp_codec_pt_translate(conn0, conn1, 111) -> 122 |  - mgcp_codec_pt_translate(conn0, conn1, 111) -> 122 | ||||||
|     0: 111 AMR/8000 octet-aligned=1  -> rc=0 |  - mgcp_codec_pt_translate(conn1, conn0, 122) -> 111 | ||||||
|     1: 112 AMR/8000 octet-aligned=0  -> rc=0 | #6: test AMR with missing octet-aligned settings (defaults to 0) | ||||||
|  - add codecs on conn1: |  | ||||||
|  - add codecs on conn0: |  - add codecs on conn0: | ||||||
|     0: 111 AMR/8000 octet-aligned=1  -> rc=0 |     0: 111 AMR/8000 octet-aligned=1  -> rc=0 | ||||||
|  - add codecs on conn1: |  - add codecs on conn1: | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ EXTRA_DIST = \ | |||||||
| 	mgcp_client_test.err \ | 	mgcp_client_test.err \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|  |  | ||||||
| noinst_PROGRAMS = \ | check_PROGRAMS = \ | ||||||
| 	mgcp_client_test \ | 	mgcp_client_test \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user