mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 08:12:01 +00:00
Compare commits
140 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9c0d105d09 | ||
|
ded02cfc29 | ||
|
0cbed362cd | ||
|
310e41cdf3 | ||
|
a55bfdc436 | ||
|
6ea8d7dac6 | ||
|
9befdeb673 | ||
|
5fb814b6b9 | ||
|
9ac9cb963d | ||
|
377d39122d | ||
|
d1e94c7ac3 | ||
|
6db3f7f1cb | ||
|
58c0b0a6f7 | ||
|
cca55247ed | ||
|
d266c374ee | ||
|
d358d71206 | ||
|
5e6661eece | ||
|
c1468ef7a5 | ||
|
24763eaa73 | ||
|
d8c2f31a87 | ||
|
01b812ea76 | ||
|
0807771b9e | ||
|
103e50a561 | ||
|
3a914e33e9 | ||
|
2172f402a5 | ||
|
8c138fe89c | ||
|
7881f7da0b | ||
|
d17a9de79e | ||
|
8e74c63e15 | ||
|
80dd576ad2 | ||
|
cb922b646f | ||
|
5d8b5b0935 | ||
|
35bd2523e2 | ||
|
f45a9b9760 | ||
|
f48cd95bbc | ||
|
7558c20973 | ||
|
6ddc79b6ed | ||
|
ff5921573a | ||
|
d4ad77d326 | ||
|
a6917fecb1 | ||
|
58877f04e3 | ||
|
d4707e2185 | ||
|
f633302c73 | ||
|
36413c011f | ||
|
833281dcb9 | ||
|
07bbd766c5 | ||
|
c7c8e649f1 | ||
|
c0e1b1aec5 | ||
|
9735d21b9d | ||
|
705565d745 | ||
|
63088e0e61 | ||
|
887c93e198 | ||
|
2ca48823b1 | ||
|
e7de137b29 | ||
|
c1ad7fd92b | ||
|
3779919551 | ||
|
8e0d101997 | ||
|
654b68d602 | ||
|
70c03f5e44 | ||
|
e56ec4a146 | ||
|
5ffd127384 | ||
|
051eb4d563 | ||
|
e740665cab | ||
|
abb9d47c90 | ||
|
2dd2b33030 | ||
|
69f15e4e95 | ||
|
b0ea7976bf | ||
|
9d939b6d5d | ||
|
de805b6772 | ||
|
cb25a0aa48 | ||
|
72eff2cf6a | ||
|
1c5fcb0791 | ||
|
48fb176fbc | ||
|
4d090d85f4 | ||
|
81c5847434 | ||
|
d48a811f6c | ||
|
b7f52b414e | ||
|
ea7aaf2eca | ||
|
432ee9d150 | ||
|
b4a59475d2 | ||
|
2177919edb | ||
|
87f114a1d0 | ||
|
928a20b540 | ||
|
b9022497d8 | ||
|
582c2bf7b0 | ||
|
663ba5c41f | ||
|
daf5bcea99 | ||
|
a29f155301 | ||
|
941e317c97 | ||
|
3fbf035923 | ||
|
21bcf6abd8 | ||
|
26e810d8a9 | ||
|
4d1a52d463 | ||
|
83df47cd7a | ||
|
c5bbb78ffe | ||
|
e0e5a772f4 | ||
|
e39ae87ea3 | ||
|
8b0a614e43 | ||
|
c61664c2b2 | ||
|
15e7e4f37d | ||
|
ab8371c6d7 | ||
|
8c777990a5 | ||
|
ec5e85b84e | ||
|
ab4e8663e1 | ||
|
a4f783b51b | ||
|
479cf76e55 | ||
|
33639166a2 | ||
|
709aad28ed | ||
|
5892b2e4db | ||
|
f1f73b8e1d | ||
|
0fdc08c551 | ||
|
5be57da075 | ||
|
147184eb06 | ||
|
cfef36837e | ||
|
c946605208 | ||
|
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
|
||||
#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
|
||||
IncludeCategories:
|
||||
- Regex: '.*'
|
||||
|
@@ -1,6 +1,6 @@
|
||||
To run the configuration parsing and output (VTY) test suite, first install
|
||||
|
||||
git://git.osmocom.org/python/osmo-python-tests
|
||||
https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests
|
||||
|
||||
and pass the following configure options here:
|
||||
|
||||
|
@@ -24,3 +24,6 @@
|
||||
# If any interfaces have been removed or changed since the last public release, a=0.
|
||||
#
|
||||
#library what description / commit summary line
|
||||
libosmo-netif >1.2.0 OSMUX_DEFAULT_PORT, osmux_xfrm_output_*, osmux_xfrm_input_*
|
||||
libosmocore >1.7.0 osmo_sockaddr_is_any()
|
||||
libmgcp-client NEW APIs mgcp_client_pool_member_...(), mgcp_client_pool_config_write()
|
17
configure.ac
17
configure.ac
@@ -44,13 +44,13 @@ AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
|
||||
AC_SUBST(LIBRARY_DLSYM)
|
||||
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.2.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.2.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.7.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.7.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.7.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.7.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.2.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.3.0)
|
||||
|
||||
CFLAGS="$CFLAGS -pthread"
|
||||
LDFLAGS="$LDFLAGS -pthread"
|
||||
@@ -79,6 +79,7 @@ AC_ARG_ENABLE(werror,
|
||||
if test x"$werror" = x"yes"
|
||||
then
|
||||
WERROR_FLAGS="-Werror"
|
||||
WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
|
||||
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
|
||||
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
|
||||
CFLAGS="$CFLAGS $WERROR_FLAGS"
|
||||
@@ -112,7 +113,7 @@ if test "x$enable_ext_tests" = "xyes" ; then
|
||||
AM_PATH_PYTHON
|
||||
AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes)
|
||||
if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
|
||||
AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.])
|
||||
AC_MSG_ERROR([Please install https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests to run the VTY/CTRL tests.])
|
||||
fi
|
||||
fi
|
||||
AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
|
||||
|
@@ -15,10 +15,6 @@
|
||||
* 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, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
@@ -29,14 +29,14 @@ BuildRequires: pkgconfig >= 0.20
|
||||
%if 0%{?suse_version}
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%endif
|
||||
BuildRequires: pkgconfig(libosmo-netif) >= 1.1.0
|
||||
BuildRequires: pkgconfig(libosmocore) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmogsm) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmocoding) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmoabis) >= 1.2.0
|
||||
BuildRequires: pkgconfig(libosmotrau) >= 1.2.0
|
||||
BuildRequires: pkgconfig(libosmo-netif) >= 1.2.0
|
||||
BuildRequires: pkgconfig(libosmocore) >= 1.7.0
|
||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.7.0
|
||||
BuildRequires: pkgconfig(libosmogsm) >= 1.7.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.7.0
|
||||
BuildRequires: pkgconfig(libosmocoding) >= 1.7.0
|
||||
BuildRequires: pkgconfig(libosmoabis) >= 1.3.0
|
||||
BuildRequires: pkgconfig(libosmotrau) >= 1.3.0
|
||||
%{?systemd_requires}
|
||||
|
||||
%description
|
||||
|
@@ -1,13 +1,18 @@
|
||||
[Unit]
|
||||
Description=Osmocom Media Gateway (MGW)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
StateDirectory=osmocom
|
||||
WorkingDirectory=%S/osmocom
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg
|
||||
RestartSec=2
|
||||
# CPU scheduling policy:
|
||||
CPUSchedulingPolicy=rr
|
||||
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
|
||||
CPUSchedulingPriority=1
|
||||
# See sched(7) for further details on real-time policies and priorities
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
41
debian/changelog
vendored
41
debian/changelog
vendored
@@ -1,15 +1,46 @@
|
||||
osmo-mgw (1.9.2) unstable; urgency=medium
|
||||
osmo-mgw (1.10.0) unstable; urgency=medium
|
||||
|
||||
* systemd: depend on networking-online.target
|
||||
[ Eric ]
|
||||
* adjust mgcp response context
|
||||
* rework message handling
|
||||
* add a lock-free bounded spsc interthread queue
|
||||
* clang-format: remove foreach macros
|
||||
* fix mgcp_conn_free_all ubsan complaints
|
||||
|
||||
-- Oliver Smith <osmith@sysmocom.de> Fri, 26 May 2023 14:57:36 +0200
|
||||
[ Philipp Maier ]
|
||||
* configuration: point out difference between trunk-nr and e1 line nr
|
||||
* mgcp_client: add new codec IUFP as VND.3GPP.IUFP
|
||||
* mgcp_codec: do not differentiate between oa and bwe when comparing codec
|
||||
* mgcp_network: do not try to convert RTCP packets
|
||||
* mgcp_network: fix typo RTPC -> RTCP
|
||||
|
||||
osmo-mgw (1.9.1) unstable; urgency=medium
|
||||
[ Oliver Smith ]
|
||||
* treewide: remove FSF address
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* mgcp_network.c: Set proper CRC Header for ACK Initialization
|
||||
* cosmetic: Rename variable payload=>payload_type
|
||||
* mgcp_network.c: Fix byte alignment of CRC Header for ACK Initialization
|
||||
* Define mgcp_rtp_end.output_enabled as bool
|
||||
* cosmetic: mgcp_codec.c: Fix typo in comment
|
||||
* Drop unneeded ax_check_compile_flag.m4
|
||||
* Make function amr_is_octet_aligned publicly available
|
||||
* Initial IuUP support using proper FSMs
|
||||
* IuUP: Support RFCI ID != RFCI Index
|
||||
* iuup: Fix caps in logging message
|
||||
* iuup: Check for IuUP Initialization retrans
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* doc/overview: fix wrong project page link
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
|
||||
* libosmo-mgcp: e1: fix memleaks in e1_recv_cb()
|
||||
|
||||
-- Harald Welte <laforge@osmocom.org> Mon, 18 Apr 2022 10:58:50 +0200
|
||||
[ Harald Welte ]
|
||||
* update git URLs (git -> https; gitea)
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 28 Jun 2022 18:50:25 +0200
|
||||
|
||||
osmo-mgw (1.9.0) unstable; urgency=medium
|
||||
|
||||
|
12
debian/control
vendored
12
debian/control
vendored
@@ -6,13 +6,13 @@ Build-Depends: debhelper (>=9),
|
||||
dh-autoreconf,
|
||||
pkg-config,
|
||||
autotools-dev,
|
||||
libosmocore-dev (>= 1.6.0),
|
||||
libosmo-netif-dev (>= 1.1.0),
|
||||
libosmo-abis-dev (>= 1.2.0),
|
||||
osmo-gsm-manuals-dev (>= 1.2.0)
|
||||
libosmocore-dev (>= 1.7.0),
|
||||
libosmo-netif-dev (>= 1.2.0),
|
||||
libosmo-abis-dev (>= 1.3.0),
|
||||
osmo-gsm-manuals-dev (>= 1.3.0)
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Git: git://git.osmocom.org/osmo-mgw.git
|
||||
Vcs-Browser: https://git.osmocom.org/osmo-mgw/
|
||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
|
||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
|
||||
Homepage: https://osmocom.org/projects/osmo-mgw
|
||||
|
||||
Package: osmo-mgw
|
||||
|
2
debian/copyright
vendored
2
debian/copyright
vendored
@@ -1,6 +1,6 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: osmo-mgw
|
||||
Source: git://git.osmocom.org/osmo-mgw
|
||||
Source: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
|
||||
|
||||
Files: *
|
||||
Copyright: 2009-2014 On-Waves
|
||||
|
@@ -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
|
||||
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
|
||||
----
|
||||
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
|
||||
|
||||
- https://osmocom.org/projects/osmomgw
|
||||
- https://osmocom.org/projects/osmomgw/wiki
|
||||
- https://osmocom.org/projects/osmo-mgw
|
||||
- https://osmocom.org/projects/osmo-mgw/wiki
|
||||
|
||||
RFC 3435 for MGCP is located at
|
||||
|
||||
|
@@ -6,11 +6,11 @@ noinst_HEADERS = \
|
||||
mgcp_endp.h \
|
||||
mgcp_sdp.h \
|
||||
mgcp_codec.h \
|
||||
mgcp_ctrl.h \
|
||||
mgcp_trunk.h \
|
||||
debug.h \
|
||||
mgcp_ratectr.h \
|
||||
mgcp_e1.h \
|
||||
mgcp_network.h \
|
||||
mgcp_protocol.h \
|
||||
mgcp_iuup.h \
|
||||
$(NULL)
|
||||
|
@@ -30,6 +30,7 @@
|
||||
enum {
|
||||
DRTP,
|
||||
DE1,
|
||||
DOSMUX,
|
||||
Debug_LastEntry,
|
||||
};
|
||||
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
@@ -156,24 +157,26 @@ struct mgcp_config {
|
||||
|
||||
enum mgcp_role role;
|
||||
|
||||
/* osmux translator: 0 means disabled, 1 means enabled */
|
||||
int osmux;
|
||||
/* addr to bind the server to */
|
||||
char osmux_addr[INET6_ADDRSTRLEN];
|
||||
/* The BSC-NAT may ask for enabling osmux on demand. This tells us if
|
||||
* the osmux socket is already initialized.
|
||||
*/
|
||||
int osmux_init;
|
||||
/* osmux batch factor: from 1 to 4 maximum */
|
||||
int osmux_batch;
|
||||
/* osmux batch size (in bytes) */
|
||||
int osmux_batch_size;
|
||||
/* osmux port */
|
||||
uint16_t osmux_port;
|
||||
/* Pad circuit with dummy messages until we see the first voice
|
||||
* message.
|
||||
*/
|
||||
uint16_t osmux_dummy;
|
||||
struct {
|
||||
/* Osmux usage policy: */
|
||||
enum osmux_usage usage;
|
||||
/* addr to bind the server to */
|
||||
char *local_addr_v4;
|
||||
char *local_addr_v6;
|
||||
/* osmux port */
|
||||
uint16_t local_port;
|
||||
/* The osmux socket is allocated on demand (1st time used).
|
||||
* This tells us if the osmux socket is already initialized. */
|
||||
bool initialized;
|
||||
/* osmux batch factor: from 1 to 4 maximum */
|
||||
int batch_factor;
|
||||
/* osmux batch size (in bytes) */
|
||||
int batch_size;
|
||||
/* Pad circuit with dummy AMR frames if no payload to transmit is available */
|
||||
bool dummy_padding;
|
||||
/* Whether peer is behind NAT (Retrieve remote addr from 1st received Osmux packet) */
|
||||
bool peer_behind_nat;
|
||||
} osmux;
|
||||
/* domain name of the media gateway */
|
||||
char domain[255+1];
|
||||
|
||||
@@ -207,4 +210,4 @@ int mgcp_send_reset_all(struct mgcp_config *cfg);
|
||||
|
||||
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint8_t dscp,
|
||||
uint8_t prio);
|
||||
int mgcp_udp_send(int fd, struct osmo_sockaddr *addr, int port, const char *buf, int len);
|
||||
int mgcp_udp_send(int fd, const struct osmo_sockaddr *addr, const char *buf, int len);
|
||||
|
@@ -17,3 +17,5 @@ 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);
|
||||
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);
|
||||
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec);
|
||||
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec);
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/gsm/iuup.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#define LOGPCONN(conn, cat, level, fmt, args...) \
|
||||
@@ -45,8 +46,8 @@ LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
|
||||
/* Specific rtp connection type (see struct mgcp_conn_rtp) */
|
||||
enum mgcp_conn_rtp_type {
|
||||
MGCP_RTP_DEFAULT = 0,
|
||||
MGCP_OSMUX_BSC,
|
||||
MGCP_OSMUX_BSC_NAT,
|
||||
MGCP_RTP_OSMUX,
|
||||
MGCP_RTP_IUUP,
|
||||
};
|
||||
|
||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
|
||||
@@ -78,22 +79,31 @@ struct mgcp_conn_rtp {
|
||||
struct {
|
||||
/* Osmux state: disabled, activating, active */
|
||||
enum osmux_state state;
|
||||
/* Is cid holding valid data? is it allocated from pool? */
|
||||
bool cid_allocated;
|
||||
/* Allocated Osmux circuit ID for this conn */
|
||||
uint8_t cid;
|
||||
/* handle to batch messages */
|
||||
/* Is local_cid holding valid data? is it allocated from pool? */
|
||||
bool local_cid_allocated;
|
||||
/* Allocated local Osmux circuit ID for this conn */
|
||||
uint8_t local_cid;
|
||||
/* Is remote_cid holding valid data? was it already received from client? */
|
||||
bool remote_cid_present;
|
||||
/* Received remote Osmux circuit ID for this conn */
|
||||
uint8_t remote_cid;
|
||||
/* handle to batch messages, shared (refcounted) among several conns */
|
||||
struct osmux_in_handle *in;
|
||||
/* handle to unbatch messages */
|
||||
struct osmux_out_handle out;
|
||||
/* statistics */
|
||||
struct {
|
||||
uint32_t chunks;
|
||||
uint32_t octets;
|
||||
} stats;
|
||||
/* handle to unbatch messages, one allocated and owned per conn */
|
||||
struct osmux_out_handle *out;
|
||||
/* statistics: */
|
||||
struct rate_ctr_group *ctrg;
|
||||
} osmux;
|
||||
|
||||
struct rate_ctr_group *rate_ctr_group;
|
||||
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 *ctrg;
|
||||
};
|
||||
|
||||
/*! MGCP connection (untyped) */
|
||||
@@ -135,12 +145,12 @@ struct mgcp_conn {
|
||||
enum {
|
||||
IN_STREAM_ERR_TSTMP_CTR,
|
||||
OUT_STREAM_ERR_TSTMP_CTR,
|
||||
RTP_PACKETS_RX_CTR,
|
||||
RTP_OCTETS_RX_CTR,
|
||||
RTP_PACKETS_TX_CTR,
|
||||
RTP_OCTETS_TX_CTR,
|
||||
RTP_DROPPED_PACKETS_CTR,
|
||||
RTP_NUM_CONNECTIONS,
|
||||
RTP_PACKETS_RX_CTR,
|
||||
RTP_OCTETS_RX_CTR,
|
||||
RTP_PACKETS_TX_CTR,
|
||||
RTP_OCTETS_TX_CTR,
|
||||
RTP_DROPPED_PACKETS_CTR,
|
||||
RTP_NUM_CONNECTIONS,
|
||||
};
|
||||
|
||||
/* RTP per-connection statistics. Instances of the corresponding rate counter group
|
||||
@@ -153,7 +163,7 @@ static const struct rate_ctr_desc mgcp_conn_rate_ctr_desc[] = {
|
||||
[RTP_OCTETS_RX_CTR] = {"rtp:octets_rx", "Inbound rtp octets."},
|
||||
[RTP_PACKETS_TX_CTR] = {"rtp:packets_tx", "Outbound rtp packets."},
|
||||
[RTP_OCTETS_TX_CTR] = {"rtp:octets_tx", "Outbound rtp octets."},
|
||||
[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "dropped rtp packets."}
|
||||
[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "Dropped rtp packets."}
|
||||
};
|
||||
|
||||
/* Aggregated RTP connection stats. These are updated when an RTP connection is freed.
|
||||
@@ -171,9 +181,55 @@ static const struct rate_ctr_desc all_rtp_conn_rate_ctr_desc[] = {
|
||||
[RTP_NUM_CONNECTIONS] = {"all_rtp:num_closed_conns", "Total number of rtp connections closed."}
|
||||
};
|
||||
|
||||
/* Osmux connection related counters */
|
||||
enum {
|
||||
OSMUX_CHUNKS_RX_CTR,
|
||||
OSMUX_OCTETS_RX_CTR,
|
||||
OSMUX_RTP_PACKETS_TX_CTR,
|
||||
OSMUX_RTP_PACKETS_TX_DROPPED_CTR,
|
||||
OSMUX_AMR_OCTETS_TX_CTR,
|
||||
/* Only available in global stats: */
|
||||
OSMUX_NUM_CONNECTIONS,
|
||||
OSMUX_PACKETS_RX_CTR,
|
||||
OSMUX_PACKETS_TX_CTR,
|
||||
OSMUX_DROPPED_PACKETS_CTR,
|
||||
};
|
||||
|
||||
/* RTP per-connection statistics. Instances of the corresponding rate counter group
|
||||
* exist for the lifetime of an RTP connection.
|
||||
* Must be kept in sync with all_rtp_conn_rate_ctr_desc below */
|
||||
static const struct rate_ctr_desc mgcp_conn_osmux_rate_ctr_desc[] = {
|
||||
[OSMUX_CHUNKS_RX_CTR] = {"osmux:chunks_rx", "Inbound Osmux chunks."},
|
||||
[OSMUX_OCTETS_RX_CTR] = {"osmux:octets_rx", "Inbound Osmux octets."},
|
||||
[OSMUX_RTP_PACKETS_TX_CTR] = {"osmux:rtp_packets_tx", "Tx outbound RTP packets to encode as Osmux."},
|
||||
[OSMUX_RTP_PACKETS_TX_DROPPED_CTR] = {"osmux:rtp_packets_tx_dropped", "Dropped Tx outbound RTP packets to encode as Osmux."},
|
||||
[OSMUX_AMR_OCTETS_TX_CTR] = {"osmux:amr_octets_tx", "Tx outbound AMD payload octets."},
|
||||
};
|
||||
|
||||
/* Aggregated Osmux connection stats. These are updated when an Osmux connection is freed.
|
||||
* Must be kept in sync with mgcp_conn_osmux_rate_ctr_desc above */
|
||||
static const struct rate_ctr_desc all_osmux_conn_rate_ctr_desc[] = {
|
||||
[OSMUX_CHUNKS_RX_CTR] = {"all_osmux:chunks_rx", "Inbound Osmux chunks."},
|
||||
[OSMUX_OCTETS_RX_CTR] = {"all_osmux:octets_rx", "Inbound Osmux octets."},
|
||||
[OSMUX_RTP_PACKETS_TX_CTR] = {"all_osmux:rtp_packets_tx", "Tx outbound RTP packets to encode as Osmux."},
|
||||
[OSMUX_RTP_PACKETS_TX_DROPPED_CTR] = {"all_osmux:rtp_packets_tx_dropped", "Dropped Tx outbound RTP packets to encode as Osmux."},
|
||||
[OSMUX_AMR_OCTETS_TX_CTR] = {"all_osmux:amr_octets_tx", "Tx outbound AMD payload octets."},
|
||||
/* These last counters below do not exist in per-connection stats, only here: */
|
||||
[OSMUX_NUM_CONNECTIONS] = {"all_osmux:num_closed_conns", "Total number of osmux connections closed."},
|
||||
[OSMUX_PACKETS_RX_CTR] = {"all_osmux:packets_rx", "Total inbound UDP/Osmux packets."},
|
||||
[OSMUX_PACKETS_TX_CTR] = {"all_osmux:packets_tx", "Total outbound UDP/Osmux packets."},
|
||||
[OSMUX_DROPPED_PACKETS_CTR] = {"all_osmux:dropped_packets", "Dropped outbound UDP/Osmux packets."}
|
||||
};
|
||||
|
||||
/* Was conn configured to handle Osmux? */
|
||||
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_RTP_OSMUX;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
|
@@ -40,8 +40,13 @@ LOGP(cat, level, "endpoint:%s " fmt, \
|
||||
endp ? endp->name : "none", \
|
||||
## args)
|
||||
|
||||
enum rtp_proto {
|
||||
MGCP_PROTO_RTP,
|
||||
MGCP_PROTO_RTCP,
|
||||
};
|
||||
|
||||
struct osmo_rtp_msg_ctx {
|
||||
int proto;
|
||||
enum rtp_proto proto;
|
||||
struct mgcp_conn_rtp *conn_src;
|
||||
struct osmo_sockaddr *from_addr;
|
||||
};
|
||||
|
@@ -1,7 +1,11 @@
|
||||
/* IuUP connection functionalitites */
|
||||
|
||||
/*
|
||||
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
|
||||
* (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
|
||||
@@ -16,9 +20,14 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg,
|
||||
const char *bind_addr, uint16_t port);
|
||||
#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);
|
@@ -9,7 +9,8 @@
|
||||
|
||||
/* The following constant defines an RTP dummy payload that is used for
|
||||
* "UDP Hole Punching" (NAT) */
|
||||
static const char rtp_dummy_payload[] = { 0x23 };
|
||||
#define MGCP_DUMMY_LOAD 0x23
|
||||
static const char rtp_dummy_payload[] = { MGCP_DUMMY_LOAD };
|
||||
|
||||
/* Check if the data in a given message buffer matches the rtp dummy payload
|
||||
* defined above */
|
||||
@@ -70,8 +71,6 @@ struct mgcp_rtp_state {
|
||||
* data is just re-used) */
|
||||
uint16_t alt_rtp_tx_sequence;
|
||||
uint32_t alt_rtp_tx_ssrc;
|
||||
|
||||
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
|
||||
};
|
||||
|
||||
struct mgcp_rtp_codec {
|
||||
@@ -94,7 +93,7 @@ struct mgcp_rtp_end {
|
||||
struct osmo_sockaddr addr;
|
||||
|
||||
/* in network byte order */
|
||||
int rtp_port, rtcp_port;
|
||||
int rtcp_port;
|
||||
|
||||
/* currently selected audio codec */
|
||||
struct mgcp_rtp_codec *codec;
|
||||
@@ -110,8 +109,8 @@ struct mgcp_rtp_end {
|
||||
uint32_t packet_duration_ms;
|
||||
int maximum_packet_time; /* -1: not set */
|
||||
char *fmtp_extra;
|
||||
/* are we transmitting packets (1) or dropping (0) outbound packets */
|
||||
int output_enabled;
|
||||
/* are we transmitting packets (true) or dropping (false) outbound packets */
|
||||
bool output_enabled;
|
||||
/* FIXME: This parameter can be set + printed, but is nowhere used! */
|
||||
int force_output_ptime;
|
||||
|
||||
@@ -158,7 +157,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *rtp_end,
|
||||
struct osmo_sockaddr *addr, struct msgb *msg);
|
||||
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
|
||||
int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
|
||||
|
||||
/* payload processing default functions */
|
||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
|
||||
@@ -177,3 +176,12 @@ 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,
|
||||
const uint16_t seq, const int32_t transit,
|
||||
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 rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
|
||||
int id);
|
||||
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg);
|
||||
uint32_t mgcp_get_current_ts(unsigned codec_rate);
|
||||
|
||||
int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg, bool target_is_oa);
|
||||
|
@@ -80,6 +80,8 @@ struct mgcp_ratectr_trunk {
|
||||
struct rate_ctr_group *mgcp_dlcx_ctr_group;
|
||||
/* Rate counter group which aggregates stats of individual RTP connections. */
|
||||
struct rate_ctr_group *all_rtp_conn_stats;
|
||||
/* Rate counter group which aggregates stats of individual Osmux connections. */
|
||||
struct rate_ctr_group *all_osmux_conn_stats;
|
||||
/* Rate counter group which contains stats for E1 events (only valid for E1 trunks) */
|
||||
struct rate_ctr_group *e1_stats;
|
||||
};
|
||||
|
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);
|
@@ -1,25 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/netif/osmux.h>
|
||||
struct mgcp_conn_rtp;
|
||||
struct mgcp_trunk;
|
||||
struct mgcp_endpoint;
|
||||
struct mgcp_conn_rtp;
|
||||
|
||||
#define OSMUX_PORT 1984
|
||||
|
||||
enum {
|
||||
OSMUX_ROLE_BSC = 0,
|
||||
OSMUX_ROLE_BSC_NAT,
|
||||
};
|
||||
|
||||
int osmux_init(int role, struct mgcp_config *cfg);
|
||||
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
||||
struct osmo_sockaddr *addr, uint16_t port);
|
||||
int osmux_init(struct mgcp_trunk *trunk);
|
||||
int osmux_init_conn(struct mgcp_conn_rtp *conn);
|
||||
int conn_osmux_enable(struct mgcp_conn_rtp *conn);
|
||||
void conn_osmux_disable(struct mgcp_conn_rtp *conn);
|
||||
int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid);
|
||||
void conn_osmux_release_cid(struct mgcp_conn_rtp *conn);
|
||||
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn);
|
||||
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
|
||||
int conn_osmux_event_rx_crcx_mdcx(struct mgcp_conn_rtp *conn);
|
||||
int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg);
|
||||
int osmux_send_dummy(struct mgcp_conn_rtp *conn);
|
||||
|
||||
void osmux_cid_pool_get(uint8_t osmux_cid);
|
||||
int osmux_cid_pool_get_next(void);
|
||||
@@ -29,10 +26,14 @@ int osmux_cid_pool_count_used(void);
|
||||
|
||||
enum osmux_state {
|
||||
OSMUX_STATE_DISABLED = 0, /* Osmux not being currently used by endp */
|
||||
OSMUX_STATE_ACTIVATING, /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref osmux_enable_endpoint. */
|
||||
OSMUX_STATE_ENABLED, /* Osmux was initialized by \ref osmux_enable_endpoint and can process frames */
|
||||
OSMUX_STATE_ACTIVATING, /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref conn_osmux_enable. */
|
||||
OSMUX_STATE_ENABLED, /* Osmux was initialized by \ref conn_osmux_enable and can process frames */
|
||||
};
|
||||
|
||||
extern const struct value_string osmux_state_strs[];
|
||||
static inline const char *osmux_state_str(enum osmux_state val)
|
||||
{ return get_value_string(osmux_state_strs, val); }
|
||||
|
||||
enum osmux_usage {
|
||||
OSMUX_USAGE_OFF = 0,
|
||||
OSMUX_USAGE_ON = 1,
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
/* See also: RFC 3435, chapter 3.5 Transmission over UDP */
|
||||
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT NULL /* INADDR(6)_ANY */
|
||||
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 2727
|
||||
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 0
|
||||
#define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
|
||||
#define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427
|
||||
|
||||
@@ -49,6 +49,7 @@ enum mgcp_codecs {
|
||||
CODEC_GSMHR_8000_1 = 111,
|
||||
CODEC_AMR_8000_1 = 112,
|
||||
CODEC_AMRWB_16000_1 = 113,
|
||||
CODEC_IUFP = 96,
|
||||
};
|
||||
/* Note: when new codec types are added, the corresponding value strings
|
||||
* in mgcp_client.c (codec_table) must be updated as well. Enumerations
|
||||
@@ -139,7 +140,7 @@ struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp);
|
||||
struct mgcp_client *mgcp_client_init(void *ctx,
|
||||
struct mgcp_client_conf *conf);
|
||||
int mgcp_client_connect(struct mgcp_client *mgcp);
|
||||
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports);
|
||||
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports) OSMO_DEPRECATED("Use mgcp_client_connect() instead");
|
||||
void mgcp_client_disconnect(struct mgcp_client *mgcp);
|
||||
|
||||
const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp);
|
||||
|
@@ -16,7 +16,7 @@ struct mgcp_client {
|
||||
mgcp_trans_id_t next_trans_id;
|
||||
struct llist_head responses_pending;
|
||||
struct llist_head inuse_endpoints;
|
||||
struct mgcp_client_pool *pool;
|
||||
struct mgcp_client_pool_member *pool_member;
|
||||
};
|
||||
|
||||
struct mgcp_inuse_endpoint {
|
||||
|
@@ -1,11 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct mgcp_client;
|
||||
struct mgcp_client_pool;
|
||||
struct mgcp_client_pool_member;
|
||||
|
||||
struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx);
|
||||
|
||||
void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool);
|
||||
int mgcp_client_pool_config_write(struct vty *vty, const char *indent);
|
||||
unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool);
|
||||
void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp_client *mgcp_client);
|
||||
|
||||
struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool);
|
||||
void mgcp_client_pool_put(struct mgcp_client *mgcp_client);
|
||||
|
||||
struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_client_pool *pool, unsigned int nr);
|
||||
struct mgcp_client *mgcp_client_pool_member_get(struct mgcp_client_pool_member *pool_member);
|
||||
bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member);
|
||||
|
@@ -1,9 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
/* Struct to handle a pool of MGWs. (Use _pool functions) */
|
||||
struct mgcp_client_pool {
|
||||
|
||||
/* A pointer to a 'single' mgcp client. This is a non-pooled MGCP client that is configured using
|
||||
* mgcp_client_vty_init() and actively registered by the API user using mgcp_client_pool_register_single() */
|
||||
struct mgcp_client *mgcp_client_single;
|
||||
|
||||
/* A list that manages the pool members (see mgcp_client_pool_member->list above) */
|
||||
struct llist_head member_list;
|
||||
|
||||
/* String to use for indentation when writing the configuration file to the VTY. This field is populated by
|
||||
* mgcp_client_pool_vty_init() */
|
||||
char *vty_indent;
|
||||
|
||||
/* VTY node specification used with this pool. This field is populated by mgcp_client_pool_vty_init() */
|
||||
struct cmd_node *vty_node;
|
||||
};
|
||||
|
||||
/* Struct to handle a member of a pool of MGWs. */
|
||||
struct mgcp_client_pool_member {
|
||||
/* Entry in llist mgcp_client_pool->pool. */
|
||||
struct llist_head list;
|
||||
|
||||
/* The pool managing this object: */
|
||||
struct mgcp_client_pool *pool;
|
||||
|
||||
/* Reference number assinged by VTY. This number is used to manage the pool from the VTY and to identify it in
|
||||
* the log. */
|
||||
unsigned int nr;
|
||||
@@ -24,22 +46,7 @@ struct mgcp_client_pool_member {
|
||||
unsigned int refcount;
|
||||
};
|
||||
|
||||
/* Struct to handle a pool of MGWs. (Use _pool functions) */
|
||||
struct mgcp_client_pool {
|
||||
|
||||
/* A pointer to a 'single' mgcp client. This is a non-pooled MGCP client that is configured using
|
||||
* mgcp_client_vty_init() and actively registered by the API user using mgcp_client_pool_register_single() */
|
||||
struct mgcp_client *mgcp_client_single;
|
||||
|
||||
/* A list that manages the pool members (see above) */
|
||||
struct llist_head pool;
|
||||
|
||||
/* String to use for indentation when writing the configuration file to the VTY. This field is populated by
|
||||
* mgcp_client_pool_vty_init() */
|
||||
char *vty_indent;
|
||||
|
||||
/* VTY node specification used with this pool. This field is populated by mgcp_client_pool_vty_init() */
|
||||
struct cmd_node *vty_node;
|
||||
};
|
||||
|
||||
struct mgcp_client_pool_member *mgcp_client_pool_member_alloc(struct mgcp_client_pool *pool, unsigned int nr);
|
||||
void mgcp_client_pool_member_free(struct mgcp_client_pool_member *pool_member);
|
||||
int mgcp_client_pool_member_reinit_client(struct mgcp_client_pool_member *pool_member);
|
||||
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member);
|
||||
|
@@ -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
|
@@ -21,7 +21,7 @@ AM_LDFLAGS = \
|
||||
|
||||
# This is not at all related to the release version, but a range of supported
|
||||
# API versions. Read TODO_RELEASE in the source tree's root!
|
||||
MGCP_CLIENT_LIBVERSION=9:0:0
|
||||
MGCP_CLIENT_LIBVERSION=10:0:1
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-mgcp-client.la \
|
||||
@@ -35,4 +35,8 @@ libosmo_mgcp_client_la_SOURCES = \
|
||||
mgcp_client_pool.c \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION)
|
||||
libosmo_mgcp_client_la_LDFLAGS = \
|
||||
$(AM_LDFLAGS) \
|
||||
-version-info $(MGCP_CLIENT_LIBVERSION) \
|
||||
-no-undefined \
|
||||
$(NULL)
|
||||
|
@@ -59,6 +59,7 @@ const struct value_string osmo_mgcpc_codec_names[] = {
|
||||
{ CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" },
|
||||
{ CODEC_AMR_8000_1, "AMR/8000/1" },
|
||||
{ CODEC_AMRWB_16000_1, "AMR-WB/16000/1" },
|
||||
{ CODEC_IUFP, "VND.3GPP.IUFP/16000" },
|
||||
{ 0, NULL },
|
||||
};
|
||||
|
||||
@@ -441,7 +442,7 @@ static int mgcp_parse_osmux_cid(const char *line)
|
||||
osmux_cid, OSMUX_CID_MAX);
|
||||
return -2;
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "MGW offered Osmux CID %u\n", osmux_cid);
|
||||
|
||||
return osmux_cid;
|
||||
}
|
||||
@@ -725,10 +726,6 @@ static int mgcp_do_read(struct osmo_fd *fd)
|
||||
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
} else if (ret > 4096 - 128) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Too much data: %s: %d\n", osmo_sock_get_name2(fd->fd), ret);
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg->l2h = msgb_put(msg, ret);
|
||||
@@ -772,7 +769,7 @@ struct mgcp_client *mgcp_client_init(void *ctx,
|
||||
|
||||
mgcp->actual.local_addr = conf->local_addr ? conf->local_addr :
|
||||
MGCP_CLIENT_LOCAL_ADDR_DEFAULT;
|
||||
mgcp->actual.local_port = conf->local_port >= 0 ? (uint16_t)conf->local_port :
|
||||
mgcp->actual.local_port = conf->local_port > 0 ? (uint16_t)conf->local_port :
|
||||
MGCP_CLIENT_LOCAL_PORT_DEFAULT;
|
||||
|
||||
mgcp->actual.remote_addr = conf->remote_addr ? conf->remote_addr :
|
||||
@@ -802,49 +799,6 @@ struct mgcp_client *mgcp_client_init(void *ctx,
|
||||
return mgcp;
|
||||
}
|
||||
|
||||
static int init_socket(struct mgcp_client *mgcp, unsigned int retry_n_ports)
|
||||
{
|
||||
int rc;
|
||||
struct osmo_wqueue *wq;
|
||||
unsigned int i;
|
||||
|
||||
wq = &mgcp->wq;
|
||||
|
||||
for (i = 0; i < retry_n_ports + 1; i++) {
|
||||
|
||||
/* Initialize socket with the currently configured port number */
|
||||
rc = osmo_sock_init2_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
|
||||
mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
|
||||
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
|
||||
if (rc > 0)
|
||||
return rc;
|
||||
|
||||
/* If there is a different port than the default port configured then we assume that the user has
|
||||
* chosen that port conciously and we will not try to resolve this by silently choosing a different
|
||||
* port. */
|
||||
if (mgcp->actual.local_port != MGCP_CLIENT_LOCAL_PORT_DEFAULT && i == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (i == retry_n_ports) {
|
||||
/* Last try failed */
|
||||
LOGPMGW(mgcp, LOGL_NOTICE, "Failed to bind to %s:%d -- check configuration!\n",
|
||||
mgcp->actual.local_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.local_port);
|
||||
if (retry_n_ports == 0)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
/* Choose a new port number to try next */
|
||||
LOGPMGW(mgcp, LOGL_NOTICE,
|
||||
"Failed to bind to %s:%d, retrying with port %d -- check configuration!\n",
|
||||
mgcp->actual.local_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.local_port,
|
||||
mgcp->actual.local_port + 1);
|
||||
mgcp->actual.local_port++;
|
||||
}
|
||||
}
|
||||
|
||||
LOGPMGW(mgcp, LOGL_FATAL, "Failed to find a port to bind on %u times -- check configuration!\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Safely ignore the MGCP response to the DLCX sent via _mgcp_client_send_dlcx() */
|
||||
static void _ignore_mgcp_response(struct mgcp_response *response, void *priv) { }
|
||||
|
||||
@@ -881,9 +835,8 @@ static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgc
|
||||
|
||||
/*! Initialize client connection (opens socket)
|
||||
* \param[in,out] mgcp MGCP client descriptor.
|
||||
* \param[in] retry_n_ports number of consecutive local ports that should be used to retry on failure.
|
||||
* \returns 0 on success, -EINVAL on error. */
|
||||
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports)
|
||||
int mgcp_client_connect(struct mgcp_client *mgcp)
|
||||
{
|
||||
struct osmo_wqueue *wq;
|
||||
int rc;
|
||||
@@ -902,7 +855,9 @@ int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports)
|
||||
|
||||
osmo_fd_setup(&wq->bfd, -1, OSMO_FD_READ, osmo_wqueue_bfd_cb, mgcp, 0);
|
||||
|
||||
rc = init_socket(mgcp, retry_n_ports);
|
||||
rc = osmo_sock_init2_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
|
||||
mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
|
||||
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
|
||||
if (rc < 0) {
|
||||
LOGPMGW(mgcp, LOGL_FATAL,
|
||||
"Failed to initialize socket %s:%u -> %s:%u for MGW: %s\n",
|
||||
@@ -928,12 +883,12 @@ error_close_fd:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! Initialize client connection (opens socket)
|
||||
/*! DEPRECATED: Initialize client connection (opens socket)
|
||||
* \param[in,out] mgcp MGCP client descriptor.
|
||||
* \returns 0 on success, -EINVAL on error. */
|
||||
int mgcp_client_connect(struct mgcp_client *mgcp)
|
||||
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports)
|
||||
{
|
||||
return mgcp_client_connect2(mgcp, 99);
|
||||
return mgcp_client_connect(mgcp);
|
||||
}
|
||||
|
||||
/*! Terminate client connection
|
||||
|
@@ -227,7 +227,7 @@ static struct value_string osmo_mgcpc_ep_fsm_event_names[33] = {};
|
||||
|
||||
static char osmo_mgcpc_ep_fsm_event_name_bufs[32][32] = {};
|
||||
|
||||
static void fill_event_names()
|
||||
static void fill_event_names(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < (ARRAY_SIZE(osmo_mgcpc_ep_fsm_event_names) - 1); i++) {
|
||||
@@ -243,7 +243,7 @@ static void fill_event_names()
|
||||
}
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init()
|
||||
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&osmo_mgcpc_ep_fsm) == 0);
|
||||
fill_event_names();
|
||||
@@ -997,7 +997,7 @@ static int osmo_mgcpc_ep_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void osmo_mgcpc_ep_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
static void osmo_mgcpc_ep_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
{
|
||||
int i;
|
||||
struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
|
||||
|
@@ -760,7 +760,7 @@ const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info)
|
||||
return buf;
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init()
|
||||
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&fsm_mgcp_client) == 0);
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <asm-generic/errno.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_internal.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_pool_internal.h>
|
||||
@@ -27,32 +28,6 @@
|
||||
#define LOGPPMGW(pool_member, level, fmt, args...) \
|
||||
LOGP(DLMGCP, level, "MGW-pool(%s) " fmt, mgcp_client_pool_member_name(pool_member), ## args)
|
||||
|
||||
/* Get a human readable name for a given pool member. */
|
||||
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member)
|
||||
{
|
||||
const struct mgcp_client *mpcp_client;
|
||||
struct mgcp_client mpcp_client_dummy;
|
||||
static char name[512];
|
||||
const char *description;
|
||||
|
||||
if (!pool_member)
|
||||
return "(null)";
|
||||
|
||||
/* It is not guranteed that a pool_member has an MGCP client. The client may not yet be initialized or the
|
||||
* initalization may have been failed. In this case we will generate a dummy MGCP client to work with. */
|
||||
if (!pool_member->client) {
|
||||
memcpy(&mpcp_client_dummy.actual, &pool_member->conf, sizeof(mpcp_client_dummy.actual));
|
||||
mpcp_client = &mpcp_client_dummy;
|
||||
} else {
|
||||
mpcp_client = pool_member->client;
|
||||
}
|
||||
|
||||
description = mgcp_client_name(mpcp_client);
|
||||
snprintf(name, sizeof(name), "%d:%s", pool_member->nr, description);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/*! Allocate MGCP client pool. This is called once on startup and before the pool is used with
|
||||
* mgcp_client_pool_vty_init(). Since the pool is linked with the VTY it must exist througout the entire runtime.
|
||||
* \param[in] talloc_ctx talloc context. */
|
||||
@@ -64,7 +39,7 @@ struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx)
|
||||
if (!pool)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&pool->pool);
|
||||
INIT_LLIST_HEAD(&pool->member_list);
|
||||
|
||||
return pool;
|
||||
}
|
||||
@@ -77,29 +52,11 @@ unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool)
|
||||
struct mgcp_client_pool_member *pool_member;
|
||||
unsigned int pool_members_initialized = 0;
|
||||
|
||||
llist_for_each_entry(pool_member, &pool->pool, list) {
|
||||
llist_for_each_entry(pool_member, &pool->member_list, list) {
|
||||
|
||||
/* Initialize client */
|
||||
pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
|
||||
if (!pool_member->client) {
|
||||
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client initialization failed\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Set backpointer so that we can detect later that this MGCP client is managed
|
||||
* by this pool. */
|
||||
pool_member->client->pool = pool;
|
||||
|
||||
/* Connect client */
|
||||
if (mgcp_client_connect2(pool_member->client, 0)) {
|
||||
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client connect failed at (%s:%u)\n",
|
||||
pool_member->conf.remote_addr, pool_member->conf.remote_port);
|
||||
talloc_free(pool_member->client);
|
||||
pool_member->client = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
pool_members_initialized++;
|
||||
if (mgcp_client_pool_member_reinit_client(pool_member) == 0)
|
||||
pool_members_initialized++;
|
||||
}
|
||||
|
||||
return pool_members_initialized;
|
||||
@@ -118,23 +75,37 @@ void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp
|
||||
pool->mgcp_client_single = mgcp_client;
|
||||
}
|
||||
|
||||
/*! Lookup the selected MGCP client config by its reference number */
|
||||
struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_client_pool *pool, unsigned int nr)
|
||||
{
|
||||
struct mgcp_client_pool_member *pool_member;
|
||||
|
||||
llist_for_each_entry(pool_member, &pool->member_list, list) {
|
||||
if (pool_member->nr == nr)
|
||||
return pool_member;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Not every pool member may have a functional MGCP client, we will run through the pool once until we meet a
|
||||
* pool member that is suitable (has a client, is not blocked, has a low load). */
|
||||
static struct mgcp_client_pool_member *mgcp_client_pool_pick(struct mgcp_client_pool *pool)
|
||||
{
|
||||
struct mgcp_client_pool_member *pool_member;
|
||||
struct mgcp_client_pool_member *pool_member_picked = NULL;
|
||||
unsigned int n_pool_members = llist_count(&pool->pool);
|
||||
unsigned int n_pool_members = 0;
|
||||
|
||||
llist_for_each_entry(pool_member, &pool->pool, list) {
|
||||
llist_for_each_entry(pool_member, &pool->member_list, list) {
|
||||
n_pool_members++;
|
||||
if (pool_member->blocked == false && pool_member->client) {
|
||||
if (!pool_member_picked)
|
||||
pool_member_picked = pool_member;
|
||||
else if (pool_member_picked->refcount > pool_member->refcount)
|
||||
pool_member_picked = pool_member;
|
||||
} else {
|
||||
LOGPPMGW(pool_member, LOGL_DEBUG, "MGW pool has %u members -- MGW %u is unusable\n", n_pool_members,
|
||||
pool_member->nr);
|
||||
LOGPPMGW(pool_member, LOGL_DEBUG, "%s -- MGW %u is unusable (blocked=%u, cli=%u)\n",
|
||||
__func__, pool_member->nr, pool_member->blocked, !!pool_member->client);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,26 +134,24 @@ struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool)
|
||||
* by the application code. */
|
||||
|
||||
/* When the pool is empty, return a single MGCP client if it is registered. */
|
||||
if (llist_empty(&pool->pool) && pool->mgcp_client_single) {
|
||||
if (llist_empty(&pool->member_list) && pool->mgcp_client_single) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "MGW pool is empty -- using (single) MGW %s\n",
|
||||
mgcp_client_name(pool->mgcp_client_single));
|
||||
return pool->mgcp_client_single;
|
||||
}
|
||||
|
||||
/* Abort when the pool is empty */
|
||||
if (llist_empty(&pool->pool)) {
|
||||
if (llist_empty(&pool->member_list)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "MGW pool is empty -- no MGW available!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Pick a suitable pool member */
|
||||
pool_member = mgcp_client_pool_pick(pool);
|
||||
if (pool_member) {
|
||||
pool_member->refcount++;
|
||||
return pool_member->client;
|
||||
}
|
||||
if (!pool_member)
|
||||
return NULL;
|
||||
|
||||
return NULL;
|
||||
return mgcp_client_pool_member_get(pool_member);
|
||||
}
|
||||
|
||||
/*! put an MGCP client back into the pool (decrement reference counter).
|
||||
@@ -194,23 +163,130 @@ struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool)
|
||||
void mgcp_client_pool_put(struct mgcp_client *mgcp_client)
|
||||
{
|
||||
struct mgcp_client_pool_member *pool_member;
|
||||
struct mgcp_client_pool *pool;
|
||||
|
||||
if (!mgcp_client)
|
||||
return;
|
||||
|
||||
if (mgcp_client->pool)
|
||||
pool = mgcp_client->pool;
|
||||
else
|
||||
if (!mgcp_client->pool_member)
|
||||
return;
|
||||
pool_member = mgcp_client->pool_member;
|
||||
|
||||
llist_for_each_entry(pool_member, &pool->pool, list) {
|
||||
if (pool_member->client == mgcp_client) {
|
||||
if (pool_member->refcount == 0) {
|
||||
LOGPPMGW(pool_member, LOGL_ERROR, "MGW pool member has invalid refcount\n");
|
||||
return;
|
||||
}
|
||||
pool_member->refcount--;
|
||||
}
|
||||
if (pool_member->refcount == 0) {
|
||||
LOGPPMGW(pool_member, LOGL_ERROR, "MGW pool member has invalid refcount\n");
|
||||
return;
|
||||
}
|
||||
pool_member->refcount--;
|
||||
}
|
||||
|
||||
/***************************
|
||||
* mgcp_client_pool_member:
|
||||
***************************/
|
||||
|
||||
/*! Allocate an mgcp_client_pool_member.
|
||||
* \param[in] pool MGCP client pool descriptor.
|
||||
* \param[in] nr Reference number of the pool member.
|
||||
*/
|
||||
struct mgcp_client_pool_member *mgcp_client_pool_member_alloc(struct mgcp_client_pool *pool, unsigned int nr)
|
||||
{
|
||||
struct mgcp_client_pool_member *pool_member;
|
||||
|
||||
pool_member = talloc_zero(pool, struct mgcp_client_pool_member);
|
||||
OSMO_ASSERT(pool_member);
|
||||
mgcp_client_conf_init(&pool_member->conf);
|
||||
pool_member->pool = pool;
|
||||
pool_member->nr = nr;
|
||||
llist_add_tail(&pool_member->list, &pool->member_list);
|
||||
return pool_member;
|
||||
}
|
||||
|
||||
/*! Free an mgcp_client_pool_member allocated through mgcp_client_pool_member_alloc().
|
||||
* \param[in] pool_member MGCP client pool descriptor.
|
||||
*
|
||||
* It also frees the associated MGCP client if present.
|
||||
*/
|
||||
void mgcp_client_pool_member_free(struct mgcp_client_pool_member *pool_member)
|
||||
{
|
||||
llist_del(&pool_member->list);
|
||||
if (pool_member->client) {
|
||||
mgcp_client_disconnect(pool_member->client);
|
||||
talloc_free(pool_member->client);
|
||||
}
|
||||
talloc_free(pool_member);
|
||||
}
|
||||
|
||||
/*! Recreate and reconnect the MGCP client associated to the pool descriptor.
|
||||
* \param[in] pool_member MGCP client pool descriptor.
|
||||
*/
|
||||
int mgcp_client_pool_member_reinit_client(struct mgcp_client_pool_member *pool_member)
|
||||
{
|
||||
/* Get rid of a possibly existing old MGCP client instance first */
|
||||
if (pool_member->client) {
|
||||
mgcp_client_disconnect(pool_member->client);
|
||||
talloc_free(pool_member->client);
|
||||
}
|
||||
|
||||
/* Initialize client */
|
||||
pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
|
||||
if (!pool_member->client) {
|
||||
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client initialization failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Set backpointer so that we can detect later that this MGCP client is managed by this pool. */
|
||||
pool_member->client->pool_member = pool_member;
|
||||
|
||||
/* Connect client */
|
||||
if (mgcp_client_connect(pool_member->client)) {
|
||||
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client connect failed at (%s:%u)\n",
|
||||
pool_member->conf.remote_addr, pool_member->conf.remote_port);
|
||||
talloc_free(pool_member->client);
|
||||
pool_member->client = NULL;
|
||||
return -ECONNABORTED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get a human readable name for a given pool member. */
|
||||
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member)
|
||||
{
|
||||
const struct mgcp_client *mpcp_client;
|
||||
struct mgcp_client mpcp_client_dummy;
|
||||
static char name[512];
|
||||
const char *description;
|
||||
|
||||
if (!pool_member)
|
||||
return "(null)";
|
||||
|
||||
/* It is not guranteed that a pool_member has an MGCP client. The client may not yet be initialized or the
|
||||
* initalization may have been failed. In this case we will generate a dummy MGCP client to work with. */
|
||||
if (!pool_member->client) {
|
||||
memcpy(&mpcp_client_dummy.actual, &pool_member->conf, sizeof(mpcp_client_dummy.actual));
|
||||
mpcp_client = &mpcp_client_dummy;
|
||||
} else {
|
||||
mpcp_client = pool_member->client;
|
||||
}
|
||||
|
||||
description = mgcp_client_name(mpcp_client);
|
||||
snprintf(name, sizeof(name), "%d:%s", pool_member->nr, description);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/*! Get the MGCP client associated with the pool reference from the pool (increment reference counter).
|
||||
* \param[in] pool_member MGCP client pool descriptor.
|
||||
* \returns MGCP client descriptor, NULL if no member was not ready.
|
||||
*/
|
||||
struct mgcp_client *mgcp_client_pool_member_get(struct mgcp_client_pool_member *pool_member)
|
||||
{
|
||||
pool_member->refcount++;
|
||||
return pool_member->client;
|
||||
}
|
||||
|
||||
/*! Get whether the MGCP client associated with the pool reference is blocked by policy.
|
||||
* \param[in] pool_member MGCP client pool descriptor.
|
||||
* \returns true if blocked, false otherwise
|
||||
*/
|
||||
bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member)
|
||||
{
|
||||
return pool_member->blocked;
|
||||
}
|
||||
|
@@ -32,10 +32,11 @@
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_internal.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_pool_internal.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_pool.h>
|
||||
|
||||
#define MGW_STR MGCP_CLIENT_MGW_STR
|
||||
|
||||
/* Only common (non-pooled) VTY connands will use this talloc context. All
|
||||
/* Only common (non-pooled) VTY commands will use this talloc context. All
|
||||
* pooled VTY commands will use the pool (global_mgcp_client_pool) as
|
||||
* talloc context. */
|
||||
static void *global_mgcp_client_ctx = NULL;
|
||||
@@ -51,13 +52,18 @@ struct mgcp_client_conf *get_mgcp_client_config(struct vty *vty)
|
||||
{
|
||||
if (global_mgcp_client_pool && vty->node == global_mgcp_client_pool->vty_node->node)
|
||||
return vty->index;
|
||||
else
|
||||
return global_mgcp_client_conf;
|
||||
|
||||
/* Global single MGCP config, deprecated: */
|
||||
vty_out(vty, "%% MGCP commands outside of 'mgw' nodes are deprecated. "
|
||||
"You should consider reading User Manual and migrating to 'mgw' node.%s",
|
||||
VTY_NEWLINE);
|
||||
|
||||
return global_mgcp_client_conf;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
|
||||
"mgw local-ip " VTY_IPV46_CMD,
|
||||
MGW_STR "local bind to connect to MGW from\n"
|
||||
"local-ip " VTY_IPV46_CMD,
|
||||
"local bind to connect to MGW from\n"
|
||||
"local bind IPv4 address\n"
|
||||
"local bind IPv6 address\n")
|
||||
{
|
||||
@@ -72,10 +78,16 @@ ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd,
|
||||
"mgcpgw local-ip A.B.C.D",
|
||||
MGW_STR "local bind to connect to MGCP gateway with\n"
|
||||
"local bind IP address\n")
|
||||
ALIAS_DEPRECATED(cfg_mgw_local_ip,
|
||||
cfg_mgw_mgw_local_ip_cmd,
|
||||
"mgw local-ip " VTY_IPV46_CMD,
|
||||
MGW_STR "local bind to connect to MGW from\n"
|
||||
"local bind IPv4 address\n"
|
||||
"local bind IPv6 address\n")
|
||||
|
||||
DEFUN(cfg_mgw_local_port, cfg_mgw_local_port_cmd,
|
||||
"mgw local-port <0-65535>",
|
||||
MGW_STR "local port to connect to MGW from\n"
|
||||
"local-port <0-65535>",
|
||||
"local port to connect to MGW from\n"
|
||||
"local bind port\n")
|
||||
{
|
||||
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
|
||||
@@ -87,10 +99,15 @@ ALIAS_DEPRECATED(cfg_mgw_local_port, cfg_mgcpgw_local_port_cmd,
|
||||
"mgcpgw local-port <0-65535>",
|
||||
MGW_STR "local bind to connect to MGCP gateway with\n"
|
||||
"local bind port\n")
|
||||
ALIAS_DEPRECATED(cfg_mgw_local_port,
|
||||
cfg_mgw_mgw_local_port_cmd,
|
||||
"mgw local-port <0-65535>",
|
||||
MGW_STR "local port to connect to MGW from\n"
|
||||
"local bind port\n")
|
||||
|
||||
DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
|
||||
"mgw remote-ip " VTY_IPV46_CMD,
|
||||
MGW_STR "remote IP address to reach the MGW at\n"
|
||||
"remote-ip " VTY_IPV46_CMD,
|
||||
"remote IP address to reach the MGW at\n"
|
||||
"remote IPv4 address\n"
|
||||
"remote IPv6 address\n")
|
||||
{
|
||||
@@ -104,10 +121,16 @@ ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
|
||||
"mgcpgw remote-ip A.B.C.D",
|
||||
MGW_STR "remote bind to connect to MGCP gateway with\n"
|
||||
"remote bind IP address\n")
|
||||
ALIAS_DEPRECATED(cfg_mgw_remote_ip,
|
||||
cfg_mgw_mgw_remote_ip_cmd,
|
||||
"mgw remote-ip " VTY_IPV46_CMD,
|
||||
MGW_STR "remote IP address to reach the MGW at\n"
|
||||
"remote IPv4 address\n"
|
||||
"remote IPv6 address\n")
|
||||
|
||||
DEFUN(cfg_mgw_remote_port, cfg_mgw_remote_port_cmd,
|
||||
"mgw remote-port <0-65535>",
|
||||
MGW_STR "remote port to reach the MGW at\n"
|
||||
"remote-port <0-65535>",
|
||||
"remote port to reach the MGW at\n"
|
||||
"remote port\n")
|
||||
{
|
||||
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
|
||||
@@ -119,8 +142,13 @@ ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
|
||||
"mgcpgw remote-port <0-65535>",
|
||||
MGW_STR "remote bind to connect to MGCP gateway with\n"
|
||||
"remote bind port\n")
|
||||
ALIAS_DEPRECATED(cfg_mgw_remote_port,
|
||||
cfg_mgw_mgw_remote_port_cmd,
|
||||
"mgw remote-port <0-65535>",
|
||||
MGW_STR "remote port to reach the MGW at\n"
|
||||
"remote port\n")
|
||||
|
||||
DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
|
||||
DEFUN_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgw_mgw_endpoint_range_cmd,
|
||||
"mgw endpoint-range <1-65534> <1-65534>",
|
||||
MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n"
|
||||
"-\n" "-\n")
|
||||
@@ -130,7 +158,7 @@ DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
|
||||
VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
|
||||
ALIAS_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
|
||||
"mgcpgw endpoint-range <1-65534> <1-65534>",
|
||||
MGW_STR "usable range of endpoint identifiers\n"
|
||||
"set first useable endpoint identifier\n"
|
||||
@@ -158,8 +186,8 @@ ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
|
||||
|
||||
DEFUN(cfg_mgw_endpoint_domain_name,
|
||||
cfg_mgw_endpoint_domain_name_cmd,
|
||||
"mgw endpoint-domain NAME",
|
||||
MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
|
||||
"endpoint-domain NAME",
|
||||
"Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
|
||||
"Domain name, should be alphanumeric.\n")
|
||||
{
|
||||
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
|
||||
@@ -172,11 +200,16 @@ DEFUN(cfg_mgw_endpoint_domain_name,
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_endpoint_domain_name,
|
||||
cfg_mgw_mgw_endpoint_domain_name_cmd,
|
||||
"mgw endpoint-domain NAME",
|
||||
MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
|
||||
"Domain name, should be alphanumeric.\n")
|
||||
|
||||
DEFUN(cfg_mgw_reset_ep_name,
|
||||
cfg_mgw_reset_ep_name_cmd,
|
||||
"mgw reset-endpoint NAME",
|
||||
MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
|
||||
"reset-endpoint NAME",
|
||||
"Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
|
||||
"e.g. 'rtpbridge/*'\n"
|
||||
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
|
||||
{
|
||||
@@ -214,11 +247,17 @@ DEFUN(cfg_mgw_reset_ep_name,
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_reset_ep_name,
|
||||
cfg_mgw_mgw_reset_ep_name_cmd,
|
||||
"mgw reset-endpoint NAME",
|
||||
MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
|
||||
"e.g. 'rtpbridge/*'\n"
|
||||
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
|
||||
|
||||
DEFUN(cfg_mgw_no_reset_ep_name,
|
||||
cfg_mgw_no_reset_ep_name_cmd,
|
||||
"no mgw reset-endpoint NAME",
|
||||
NO_STR MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
|
||||
"no reset-endpoint NAME",
|
||||
NO_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
|
||||
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
|
||||
{
|
||||
struct reset_ep *reset_ep;
|
||||
@@ -235,6 +274,11 @@ DEFUN(cfg_mgw_no_reset_ep_name,
|
||||
vty_out(vty, "%% no such endpoint name configured ('%s')%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_no_reset_ep_name,
|
||||
cfg_mgw_mgw_no_reset_ep_name_cmd,
|
||||
"no mgw reset-endpoint NAME",
|
||||
NO_STR MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
|
||||
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
|
||||
|
||||
static int config_write(struct vty *vty, const char *indent, struct mgcp_client_conf *conf)
|
||||
{
|
||||
@@ -242,33 +286,36 @@ static int config_write(struct vty *vty, const char *indent, struct mgcp_client_
|
||||
int port;
|
||||
struct reset_ep *reset_ep;
|
||||
|
||||
if (conf->description)
|
||||
/* If caller doesn't the MGW pool API (mgcp_client_pool_vty_init was never called),
|
||||
* then the "mgw" cmd prefix must be added since the old node always contained it.
|
||||
*/
|
||||
const char *mgw_prefix = global_mgcp_client_pool ? "" : "mgw ";
|
||||
|
||||
if (conf->description) /* description never had "mgw" prefix even on old node: */
|
||||
vty_out(vty, "%sdescription %s%s", indent, conf->description, VTY_NEWLINE);
|
||||
|
||||
addr = conf->local_addr;
|
||||
if (addr)
|
||||
vty_out(vty, "%smgw local-ip %s%s", indent, addr,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, "%s%slocal-ip %s%s", indent, mgw_prefix, addr, VTY_NEWLINE);
|
||||
port = conf->local_port;
|
||||
if (port >= 0)
|
||||
vty_out(vty, "%smgw local-port %u%s", indent,
|
||||
vty_out(vty, "%s%slocal-port %u%s", indent, mgw_prefix,
|
||||
(uint16_t)port, VTY_NEWLINE);
|
||||
|
||||
addr = conf->remote_addr;
|
||||
if (addr)
|
||||
vty_out(vty, "%smgw remote-ip %s%s", indent, addr,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, "%s%sremote-ip %s%s", indent, mgw_prefix, addr, VTY_NEWLINE);
|
||||
port = conf->remote_port;
|
||||
if (port >= 0)
|
||||
vty_out(vty, "%smgw remote-port %u%s", indent,
|
||||
vty_out(vty, "%s%sremote-port %u%s", indent, mgw_prefix,
|
||||
(uint16_t)port, VTY_NEWLINE);
|
||||
|
||||
if (conf->endpoint_domain_name[0])
|
||||
vty_out(vty, "%smgw endpoint-domain %s%s", indent,
|
||||
vty_out(vty, "%s%sendpoint-domain %s%s", indent, mgw_prefix,
|
||||
conf->endpoint_domain_name, VTY_NEWLINE);
|
||||
|
||||
llist_for_each_entry(reset_ep, &conf->reset_epnames, list)
|
||||
vty_out(vty, "%smgw reset-endpoint %s%s", indent, reset_ep->name, VTY_NEWLINE);
|
||||
vty_out(vty, "%s%sreset-endpoint %s%s", indent, mgw_prefix, reset_ep->name, VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -279,6 +326,11 @@ static int config_write(struct vty *vty, const char *indent, struct mgcp_client_
|
||||
* \returns CMD_SUCCESS on success, CMD_WARNING on error */
|
||||
int mgcp_client_config_write(struct vty *vty, const char *indent)
|
||||
{
|
||||
/* If caller supports MGW pool API (mgcp_client_pool_vty_init was
|
||||
* called), then skip printing any config in this node and print it when
|
||||
* the whole 'mgw' node is printed. */
|
||||
if (global_mgcp_client_pool)
|
||||
return CMD_SUCCESS;
|
||||
return config_write(vty, indent, global_mgcp_client_conf);
|
||||
}
|
||||
|
||||
@@ -286,15 +338,15 @@ static void vty_init_common(void *talloc_ctx, int node)
|
||||
{
|
||||
global_mgcp_client_ctx = talloc_ctx;
|
||||
|
||||
install_lib_element(node, &cfg_mgw_local_ip_cmd);
|
||||
install_lib_element(node, &cfg_mgw_local_port_cmd);
|
||||
install_lib_element(node, &cfg_mgw_remote_ip_cmd);
|
||||
install_lib_element(node, &cfg_mgw_remote_port_cmd);
|
||||
install_lib_element(node, &cfg_mgw_endpoint_range_cmd);
|
||||
install_lib_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
|
||||
install_lib_element(node, &cfg_mgw_endpoint_domain_name_cmd);
|
||||
install_lib_element(node, &cfg_mgw_reset_ep_name_cmd);
|
||||
install_lib_element(node, &cfg_mgw_no_reset_ep_name_cmd);
|
||||
/* deprecated 'mgw' commands ('mgw' prepended as first arg) */
|
||||
install_lib_element(node, &cfg_mgw_mgw_local_ip_cmd);
|
||||
install_lib_element(node, &cfg_mgw_mgw_local_port_cmd);
|
||||
install_lib_element(node, &cfg_mgw_mgw_remote_ip_cmd);
|
||||
install_lib_element(node, &cfg_mgw_mgw_remote_port_cmd);
|
||||
install_lib_element(node, &cfg_mgw_mgw_endpoint_range_cmd);
|
||||
install_lib_element(node, &cfg_mgw_mgw_endpoint_domain_name_cmd);
|
||||
install_lib_element(node, &cfg_mgw_mgw_reset_ep_name_cmd);
|
||||
install_lib_element(node, &cfg_mgw_mgw_no_reset_ep_name_cmd);
|
||||
|
||||
osmo_fsm_vty_add_cmds();
|
||||
}
|
||||
@@ -319,38 +371,62 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
|
||||
vty_init_common(talloc_ctx, node);
|
||||
}
|
||||
|
||||
static int config_write_pool(struct vty *vty)
|
||||
/* Mark whether user called mgcp_client_pool_config_write() and hence support new API */
|
||||
static bool mgcp_client_pool_config_write_called = false;
|
||||
|
||||
static int _mgcp_client_pool_config_write(struct vty *vty, const char *indent)
|
||||
{
|
||||
struct mgcp_client_pool *pool = global_mgcp_client_pool;
|
||||
struct mgcp_client_pool_member *pool_member;
|
||||
unsigned int indent_buf_len = strlen(pool->vty_indent) + 1 + 1;
|
||||
char *indent = talloc_zero_size(vty, indent_buf_len);
|
||||
unsigned int subindent_buf_len;
|
||||
char *subindent;
|
||||
|
||||
snprintf(indent, indent_buf_len, "%s ", pool->vty_indent);
|
||||
if (!indent)
|
||||
indent = pool->vty_indent ? : "";
|
||||
subindent_buf_len = strlen(indent) + 1 + 1;
|
||||
subindent = talloc_zero_size(vty, subindent_buf_len);
|
||||
|
||||
llist_for_each_entry(pool_member, &pool->pool, list) {
|
||||
vty_out(vty, "%smgw %u%s", pool->vty_indent, pool_member->nr, VTY_NEWLINE);
|
||||
config_write(vty, indent, &pool_member->conf);
|
||||
snprintf(subindent, subindent_buf_len, "%s ", indent);
|
||||
|
||||
llist_for_each_entry(pool_member, &pool->member_list, list) {
|
||||
vty_out(vty, "%smgw %u%s", indent, pool_member->nr, VTY_NEWLINE);
|
||||
config_write(vty, subindent, &pool_member->conf);
|
||||
}
|
||||
|
||||
talloc_free(indent);
|
||||
/* MGW pool API is supported by user (global_mgcp_client_pool is set
|
||||
* because mgcp_client_pool_vty_init was called). If single MGW was
|
||||
* configured through old VTY and no mgw in the new MGW pool VTY is
|
||||
* replacing it, then output the single MGW converted to the new MGW
|
||||
* pool VTY. */
|
||||
if (llist_empty(&pool->member_list) && pool->mgcp_client_single) {
|
||||
vty_out(vty, "%smgw 0%s", indent, VTY_NEWLINE);
|
||||
config_write(vty, subindent, global_mgcp_client_conf);
|
||||
}
|
||||
|
||||
talloc_free(subindent);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* Lookup the selected MGCP client config by its reference number */
|
||||
static struct mgcp_client_pool_member *pool_member_by_nr(unsigned int nr)
|
||||
/* Deprecated, used for backward compatibility with older users which didn't call
|
||||
* mgcp_client_pool_config_write(): */
|
||||
static int config_write_pool(struct vty *vty)
|
||||
{
|
||||
struct mgcp_client_pool_member *pool_member = NULL;
|
||||
struct mgcp_client_pool_member *pool_member_tmp;
|
||||
if (mgcp_client_pool_config_write_called)
|
||||
return CMD_SUCCESS;
|
||||
|
||||
llist_for_each_entry(pool_member_tmp, &global_mgcp_client_pool->pool, list) {
|
||||
if (pool_member_tmp->nr == nr) {
|
||||
pool_member = pool_member_tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return _mgcp_client_pool_config_write(vty, NULL);
|
||||
}
|
||||
|
||||
return pool_member;
|
||||
/*! Write out MGCP client config to VTY.
|
||||
* \param[in] vty VTY to which we should print.
|
||||
* \param[in] indent string used for indentation (e.g. " ").
|
||||
If NULL, indentation passed during mgcp_client_pool_vty_init() will be used.
|
||||
* \returns CMD_SUCCESS on success, CMD_WARNING on error */
|
||||
int mgcp_client_pool_config_write(struct vty *vty, const char *indent)
|
||||
{
|
||||
/* Tell internal node write function that the user supports calling proper API: */
|
||||
mgcp_client_pool_config_write_called = true;
|
||||
return _mgcp_client_pool_config_write(vty, indent);
|
||||
}
|
||||
|
||||
DEFUN_ATTR(cfg_mgw,
|
||||
@@ -359,13 +435,10 @@ DEFUN_ATTR(cfg_mgw,
|
||||
int nr = atoi(argv[0]);
|
||||
struct mgcp_client_pool_member *pool_member;
|
||||
|
||||
pool_member = pool_member_by_nr(nr);
|
||||
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
|
||||
if (!pool_member) {
|
||||
pool_member = talloc_zero(global_mgcp_client_pool, struct mgcp_client_pool_member);
|
||||
pool_member = mgcp_client_pool_member_alloc(global_mgcp_client_pool, nr);
|
||||
OSMO_ASSERT(pool_member);
|
||||
mgcp_client_conf_init(&pool_member->conf);
|
||||
pool_member->nr = nr;
|
||||
llist_add_tail(&pool_member->list, &global_mgcp_client_pool->pool);
|
||||
}
|
||||
|
||||
vty->index = &pool_member->conf;
|
||||
@@ -382,7 +455,7 @@ DEFUN_ATTR(cfg_no_mgw,
|
||||
int nr = atoi(argv[0]);
|
||||
struct mgcp_client_pool_member *pool_member;
|
||||
|
||||
pool_member = pool_member_by_nr(nr);
|
||||
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
|
||||
if (!pool_member) {
|
||||
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
@@ -395,12 +468,7 @@ DEFUN_ATTR(cfg_no_mgw,
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
llist_del(&pool_member->list);
|
||||
if (pool_member->client) {
|
||||
mgcp_client_disconnect(pool_member->client);
|
||||
talloc_free(pool_member->client);
|
||||
}
|
||||
talloc_free(pool_member);
|
||||
mgcp_client_pool_member_free(pool_member);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -412,7 +480,7 @@ DEFUN_ATTR(mgw_reconnect, mgw_reconnect_cmd,
|
||||
int nr = atoi(argv[0]);
|
||||
struct mgcp_client_pool_member *pool_member = NULL;
|
||||
|
||||
pool_member = pool_member_by_nr(nr);
|
||||
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
|
||||
if (!pool_member) {
|
||||
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
@@ -425,32 +493,10 @@ DEFUN_ATTR(mgw_reconnect, mgw_reconnect_cmd,
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
/* Get rid of a possibly existing old MGCP client instance first */
|
||||
if (pool_member->client) {
|
||||
mgcp_client_disconnect(pool_member->client);
|
||||
talloc_free(pool_member->client);
|
||||
}
|
||||
|
||||
/* Create a new MGCP client instance with the current config */
|
||||
pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
|
||||
if (!pool_member->client) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s initalization failed\n",
|
||||
mgcp_client_pool_member_name(pool_member));
|
||||
vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s",
|
||||
mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
/* Set backpointer so that we can detect later that this MGCP client is managed by this pool. */
|
||||
pool_member->client->pool = global_mgcp_client_pool;
|
||||
|
||||
/* Connect client */
|
||||
if (mgcp_client_connect(pool_member->client)) {
|
||||
if (mgcp_client_pool_member_reinit_client(pool_member) < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s connect failed at (%s:%u)\n",
|
||||
mgcp_client_pool_member_name(pool_member), pool_member->conf.remote_addr,
|
||||
pool_member->conf.remote_port);
|
||||
talloc_free(pool_member->client);
|
||||
pool_member->client = NULL;
|
||||
vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s",
|
||||
mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
@@ -466,7 +512,7 @@ DEFUN_ATTR(mgw_block, mgw_block_cmd,
|
||||
int nr = atoi(argv[0]);
|
||||
struct mgcp_client_pool_member *pool_member = NULL;
|
||||
|
||||
pool_member = pool_member_by_nr(nr);
|
||||
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
|
||||
if (!pool_member) {
|
||||
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
@@ -483,7 +529,7 @@ DEFUN_ATTR(mgw_unblock, mgw_unblock_cmd,
|
||||
int nr = atoi(argv[0]);
|
||||
struct mgcp_client_pool_member *pool_member = NULL;
|
||||
|
||||
pool_member = pool_member_by_nr(nr);
|
||||
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
|
||||
if (!pool_member) {
|
||||
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
@@ -493,20 +539,20 @@ DEFUN_ATTR(mgw_unblock, mgw_unblock_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(mgw_show, mgw_snow_cmd, "show mgw-pool", SHOW_STR "Display information about the MGW-Pool\n")
|
||||
DEFUN(mgw_show, mgw_show_cmd, "show mgw-pool", SHOW_STR "Display information about the MGW-Pool\n")
|
||||
{
|
||||
vty_out(vty, "%% MGW-Pool:%s", VTY_NEWLINE);
|
||||
struct mgcp_client_pool_member *pool_member;
|
||||
|
||||
if (llist_empty(&global_mgcp_client_pool->pool) && global_mgcp_client_pool->mgcp_client_single) {
|
||||
if (llist_empty(&global_mgcp_client_pool->member_list) && global_mgcp_client_pool->mgcp_client_single) {
|
||||
vty_out(vty, "%% (pool is empty, single MGCP client will be used)%s", VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
} else if (llist_empty(&global_mgcp_client_pool->pool)) {
|
||||
} else if (llist_empty(&global_mgcp_client_pool->member_list)) {
|
||||
vty_out(vty, "%% (pool is empty)%s", VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
llist_for_each_entry(pool_member, &global_mgcp_client_pool->pool, list) {
|
||||
llist_for_each_entry(pool_member, &global_mgcp_client_pool->member_list, list) {
|
||||
vty_out(vty, "%% MGW %s%s", mgcp_client_pool_member_name(pool_member), VTY_NEWLINE);
|
||||
vty_out(vty, "%% mgcp-client: %s%s", pool_member->client ? "connected" : "disconnected",
|
||||
VTY_NEWLINE);
|
||||
@@ -520,7 +566,8 @@ DEFUN(mgw_show, mgw_snow_cmd, "show mgw-pool", SHOW_STR "Display information abo
|
||||
* (called once at startup by the application process).
|
||||
* \param[in] parent_node identifier of the parent node on which the mgw node appears.
|
||||
* \param[in] mgw_node identifier that should be used with the newly installed MGW node.
|
||||
* \param[in] indent indentation string to match the indentation in the VTY config
|
||||
* \param[in] indent indentation string to match the indentation in the VTY config.
|
||||
If NULL, it must be passed explicitly each time mgcp_client_pool_config_write() is called.
|
||||
* \param[in] pool user provided memory to store the configured MGCP client (MGW) pool. */
|
||||
void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool)
|
||||
{
|
||||
@@ -528,11 +575,12 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent
|
||||
OSMO_ASSERT(pool);
|
||||
|
||||
/* Never allow this function to be called twice on the same pool */
|
||||
OSMO_ASSERT(!pool->vty_indent);
|
||||
OSMO_ASSERT(!pool->vty_node);
|
||||
|
||||
pool->vty_indent = talloc_strdup(pool, indent);
|
||||
OSMO_ASSERT(pool->vty_indent);
|
||||
if (indent) {
|
||||
pool->vty_indent = talloc_strdup(pool, indent);
|
||||
OSMO_ASSERT(pool->vty_indent);
|
||||
}
|
||||
pool->vty_node = talloc_zero(pool, struct cmd_node);
|
||||
OSMO_ASSERT(pool->vty_node);
|
||||
pool->vty_node->node = mgw_node;
|
||||
@@ -542,8 +590,19 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent
|
||||
install_lib_element(parent_node, &cfg_mgw_cmd);
|
||||
install_lib_element(parent_node, &cfg_no_mgw_cmd);
|
||||
|
||||
/* Note: config_write_pool is deprecated and user is expected to
|
||||
* manually call mgcp_client_pool_config_write() when printing the VTY
|
||||
* config */
|
||||
install_node(pool->vty_node, config_write_pool);
|
||||
vty_init_common(pool, mgw_node);
|
||||
install_lib_element(mgw_node, &cfg_mgw_local_ip_cmd);
|
||||
install_lib_element(mgw_node, &cfg_mgw_local_port_cmd);
|
||||
install_lib_element(mgw_node, &cfg_mgw_remote_ip_cmd);
|
||||
install_lib_element(mgw_node, &cfg_mgw_remote_port_cmd);
|
||||
install_lib_element(mgw_node, &cfg_mgw_rtp_bts_base_port_cmd);
|
||||
install_lib_element(mgw_node, &cfg_mgw_endpoint_domain_name_cmd);
|
||||
install_lib_element(mgw_node, &cfg_mgw_reset_ep_name_cmd);
|
||||
install_lib_element(mgw_node, &cfg_mgw_no_reset_ep_name_cmd);
|
||||
|
||||
install_element(mgw_node, &cfg_description_cmd);
|
||||
|
||||
@@ -551,7 +610,7 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent
|
||||
install_lib_element(ENABLE_NODE, &mgw_block_cmd);
|
||||
install_lib_element(ENABLE_NODE, &mgw_unblock_cmd);
|
||||
|
||||
install_lib_element_ve(&mgw_snow_cmd);
|
||||
install_lib_element_ve(&mgw_show_cmd);
|
||||
|
||||
global_mgcp_client_pool = pool;
|
||||
}
|
||||
|
@@ -45,7 +45,7 @@ libosmo_mgcp_a_SOURCES = \
|
||||
mgcp_stat.c \
|
||||
mgcp_endp.c \
|
||||
mgcp_trunk.c \
|
||||
mgcp_ctrl.c \
|
||||
mgcp_ratectr.c \
|
||||
mgcp_e1.c \
|
||||
mgcp_iuup.c \
|
||||
$(NULL)
|
||||
|
@@ -14,10 +14,6 @@
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
static inline int val_seg(int val)
|
||||
|
@@ -345,6 +345,18 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check if the codec has a specific AMR mode (octet-aligned or bandwith-efficient) set. */
|
||||
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
if (codec->param_present == false)
|
||||
return false;
|
||||
if (!codec->param.amr_octet_aligned_present)
|
||||
return false;
|
||||
if (strcmp(codec->subtype_name, "AMR") != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Return true if octet-aligned is set in the given codec. Default to octet-aligned=0, i.e. bandwidth-efficient mode.
|
||||
* See RFC4867 "RTP Payload Format for AMR and AMR-WB" sections "8.1. AMR Media Type Registration" and "8.2. AMR-WB
|
||||
* Media Type Registration":
|
||||
@@ -355,7 +367,7 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
|
||||
*
|
||||
* 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)
|
||||
return false;
|
||||
@@ -378,10 +390,10 @@ static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *c
|
||||
return false;
|
||||
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
|
||||
return false;
|
||||
if (!strcmp(codec_a->subtype_name, "AMR")) {
|
||||
if (amr_is_octet_aligned(codec_a) != amr_is_octet_aligned(codec_b))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Note: AMR allows to set the RTP payload format to octet-aligned or bandwith-efficient (octet-aligned=0)
|
||||
* 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;
|
||||
}
|
||||
@@ -417,7 +429,7 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp
|
||||
if (!codec_src)
|
||||
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 */
|
||||
codecs_assigned = rtp_dst->codecs_assigned;
|
||||
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
|
||||
|
@@ -30,12 +30,14 @@
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp/mgcp_iuup.h>
|
||||
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <ctype.h>
|
||||
|
||||
const static struct rate_ctr_group_desc rate_ctr_group_desc = {
|
||||
static const struct rate_ctr_group_desc rate_ctr_group_desc = {
|
||||
.group_name_prefix = "conn_rtp",
|
||||
.group_description = "rtp connection statistics",
|
||||
.class_id = 1,
|
||||
@@ -93,30 +95,36 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
|
||||
static atomic_uint rate_ctr_index = 0;
|
||||
|
||||
conn_rtp->type = MGCP_RTP_DEFAULT;
|
||||
conn_rtp->osmux.cid_allocated = false;
|
||||
conn_rtp->osmux.cid = 0;
|
||||
|
||||
/* Osmux specific defaults, only used if conn is later on Osmux-enabled: */
|
||||
conn_rtp->osmux.state = OSMUX_STATE_DISABLED;
|
||||
conn_rtp->osmux.local_cid_allocated = false;
|
||||
conn_rtp->osmux.local_cid = 0;
|
||||
conn_rtp->osmux.remote_cid_present = false;
|
||||
conn_rtp->osmux.remote_cid = 0;
|
||||
|
||||
/* backpointer to the generic part of the connection */
|
||||
conn->u.rtp.conn = conn;
|
||||
|
||||
end->rtp.fd = -1;
|
||||
end->rtcp.fd = -1;
|
||||
end->rtp_port = end->rtcp_port = 0;
|
||||
memset(&end->addr, 0, sizeof(end->addr));
|
||||
end->rtcp_port = 0;
|
||||
talloc_free(end->fmtp_extra);
|
||||
end->fmtp_extra = NULL;
|
||||
|
||||
/* Set default values */
|
||||
end->frames_per_packet = 0; /* unknown */
|
||||
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
|
||||
end->output_enabled = 0;
|
||||
end->output_enabled = false;
|
||||
end->maximum_packet_time = -1;
|
||||
|
||||
conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++);
|
||||
if (!conn_rtp->rate_ctr_group)
|
||||
conn_rtp->ctrg = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++);
|
||||
if (!conn_rtp->ctrg)
|
||||
return -1;
|
||||
|
||||
conn_rtp->state.in_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->rate_ctr_group, IN_STREAM_ERR_TSTMP_CTR);
|
||||
conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->rate_ctr_group, OUT_STREAM_ERR_TSTMP_CTR);
|
||||
conn_rtp->state.in_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, IN_STREAM_ERR_TSTMP_CTR);
|
||||
conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, OUT_STREAM_ERR_TSTMP_CTR);
|
||||
|
||||
/* Make sure codec table is reset */
|
||||
mgcp_codec_reset_all(conn_rtp);
|
||||
@@ -129,8 +137,10 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
if (mgcp_conn_rtp_is_osmux(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);
|
||||
rate_ctr_group_free(conn_rtp->rate_ctr_group);
|
||||
rate_ctr_group_free(conn_rtp->ctrg);
|
||||
mgcp_codec_reset_all(conn_rtp);
|
||||
}
|
||||
|
||||
@@ -259,7 +269,7 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
|
||||
static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats;
|
||||
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
|
||||
struct rate_ctr_group *conn_stats = conn_rtp->ctrg;
|
||||
|
||||
if (all_stats == NULL || conn_stats == NULL)
|
||||
return;
|
||||
@@ -325,6 +335,11 @@ void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
|
||||
|
||||
/*! free all connections at once.
|
||||
* \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)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
@@ -351,14 +366,38 @@ char *mgcp_conn_dump(struct mgcp_conn *conn)
|
||||
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
/* Dump RTP connection */
|
||||
snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, "
|
||||
"rtp:%u rtcp:%u)",
|
||||
conn->name,
|
||||
conn->id,
|
||||
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
|
||||
ntohs(conn->u.rtp.end.rtp_port),
|
||||
ntohs(conn->u.rtp.end.rtcp_port));
|
||||
switch (conn->u.rtp.type) {
|
||||
case MGCP_RTP_DEFAULT:
|
||||
/* Dump RTP connection */
|
||||
snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, "
|
||||
"rtp:%u rtcp:%u)",
|
||||
conn->name, conn->id,
|
||||
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa),
|
||||
ntohs(conn->u.rtp.end.rtcp_port));
|
||||
break;
|
||||
case MGCP_RTP_OSMUX:
|
||||
snprintf(str, sizeof(str), "(%s/osmux, id:0x%s, ip:%s, "
|
||||
"port:%u CID:%u)",
|
||||
conn->name, conn->id,
|
||||
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa),
|
||||
conn->u.rtp.osmux.local_cid);
|
||||
break;
|
||||
case MGCP_RTP_IUUP:
|
||||
snprintf(str, sizeof(str), "(%s/iuup, id:0x%s, ip:%s, "
|
||||
"port:%u)",
|
||||
conn->name, conn->id,
|
||||
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa));
|
||||
break;
|
||||
default:
|
||||
/* Should not happen, we should be able to dump
|
||||
* every possible connection type. */
|
||||
snprintf(str, sizeof(str), "(unknown conn_rtp connection type %u)",
|
||||
conn->u.rtp.type);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
|
||||
|
||||
static int mgw_ctrl_node_lookup(void *data, vector vline, int *node_type,
|
||||
void **node_data, int *i)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg,
|
||||
const char *bind_addr, uint16_t port)
|
||||
{
|
||||
return ctrl_interface_setup_dynip2(cfg, bind_addr, port, mgw_ctrl_node_lookup,
|
||||
_LAST_CTRL_NODE);
|
||||
}
|
@@ -496,7 +496,7 @@ static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
|
||||
}
|
||||
}
|
||||
|
||||
/* !Equip E1 endpoint with I.460 mux resources.
|
||||
/*! Equip E1 endpoint with I.460 mux resources.
|
||||
* \param[in] endp endpoint to equip
|
||||
* \param[in] ts E1 timeslot number.
|
||||
* \param[in] ss E1 subslot number.
|
||||
|
753
src/libosmo-mgcp/mgcp_iuup.c
Normal file
753
src/libosmo-mgcp/mgcp_iuup.c
Normal file
@@ -0,0 +1,753 @@
|
||||
/*
|
||||
* (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;
|
||||
if (!irp) {
|
||||
/* No IuUP Initialization has occured on the IuUP side yet. Return error and drop the RTP data, until
|
||||
* the IuUP Initialization has configured the link. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
if (mgcp_codec_amr_is_octet_aligned(conn_rtp_dst->end.codec)) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR OA: ft %d, len %d\n", ft, msgb_length(msg));
|
||||
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);
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR BE: ft %d, len %zd\n", ft, 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_RTP_OSMUX:
|
||||
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),
|
||||
osmo_sockaddr_port(&rtp_end->addr.u.sa), 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),
|
||||
osmo_sockaddr_port(&rtp_end->addr.u.sa), 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, (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 = osmo_sockaddr_port(&conn_rtp_src->end.addr.u.sa);
|
||||
conn_rtp_src->end.addr = *mc->from_addr;
|
||||
}
|
||||
|
||||
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;
|
||||
osmo_sockaddr_set_port(&conn_rtp_src->end.addr.u.sa, 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 (!osmo_amr_ft_valid(amr_hdr->ft)) {
|
||||
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 ? IUUP_FQC_FRAME_GOOD : IUUP_FQC_FRAME_BAD;
|
||||
irp->u.data.rfci = rfci;
|
||||
msgb_pull(msg, 2);
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Convert AMR OA -> IuUP: ft %d -> rfci %d len %d\n",
|
||||
amr_hdr->ft, rfci, msgb_length(msg));
|
||||
} else {
|
||||
uint8_t *amr_bwe_hdr = (uint8_t *) msgb_data(msg);
|
||||
int8_t ft;
|
||||
uint8_t q;
|
||||
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 (!osmo_amr_ft_valid(ft)) {
|
||||
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;
|
||||
}
|
||||
q = amr_bwe_hdr[1] & 0x40;
|
||||
irp->u.data.fqc = q ? IUUP_FQC_FRAME_GOOD : IUUP_FQC_FRAME_BAD;
|
||||
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);
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Convert AMR BE -> IuUP: ft %d -> rfci %d len %d\n",
|
||||
ft, rfci, msgb_length(msg));
|
||||
}
|
||||
|
||||
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 */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
conn->u.rtp.end.output_enabled =
|
||||
conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
|
||||
conn->u.rtp.end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY);
|
||||
}
|
||||
|
||||
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 */
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -203,7 +202,7 @@ int mgcp_parse_osmux_cid(const char *line)
|
||||
osmux_cid, OSMUX_CID_MAX);
|
||||
return -2;
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "MGCP client offered Osmux CID %u\n", osmux_cid);
|
||||
|
||||
return osmux_cid;
|
||||
}
|
||||
|
@@ -48,21 +48,16 @@
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
#include <osmocom/codec/codec.h>
|
||||
#include <osmocom/mgcp/mgcp_e1.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp_iuup.h>
|
||||
|
||||
#define RTP_SEQ_MOD (1 << 16)
|
||||
#define RTP_MAX_DROPOUT 3000
|
||||
#define RTP_MAX_MISORDER 100
|
||||
|
||||
enum rtp_proto {
|
||||
MGCP_PROTO_RTP,
|
||||
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)
|
||||
{
|
||||
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
|
||||
struct rate_ctr_group *conn_stats = conn_rtp->ctrg;
|
||||
struct rate_ctr_group *mgw_stats = endp->trunk->ratectr.all_rtp_conn_stats;
|
||||
|
||||
/* add to both the per-connection and the global stats */
|
||||
@@ -70,49 +65,68 @@ static void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_end
|
||||
rate_ctr_add(rate_ctr_group_get_ctr(mgw_stats, id), inc);
|
||||
}
|
||||
|
||||
static void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, int id)
|
||||
void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, int id)
|
||||
{
|
||||
rtpconn_rate_ctr_add(conn_rtp, endp, id, 1);
|
||||
}
|
||||
|
||||
static int rx_rtp(struct msgb *msg);
|
||||
|
||||
static bool addr_is_any(const struct osmo_sockaddr *osa)
|
||||
{
|
||||
if (osa->u.sa.sa_family == AF_INET6) {
|
||||
struct in6_addr ip6_any = IN6ADDR_ANY_INIT;
|
||||
return memcmp(&osa->u.sin6.sin6_addr,
|
||||
&ip6_any, sizeof(ip6_any)) == 0;
|
||||
} else {
|
||||
return osa->u.sin.sin_addr.s_addr == 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end)
|
||||
{
|
||||
return rtp_end->rtp_port && !addr_is_any(&rtp_end->addr);
|
||||
return (osmo_sockaddr_port(&rtp_end->addr.u.sa) != 0) &&
|
||||
(osmo_sockaddr_is_any(&rtp_end->addr) == 0);
|
||||
}
|
||||
|
||||
/*! Determine the local rtp bind IP-address.
|
||||
* \param[out] addr caller provided memory to store the resulting IP-Address.
|
||||
* \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters.
|
||||
* \ returns 0 on success, -1 if no local address could be provided.
|
||||
*
|
||||
* The local bind IP-address is automatically selected by probing the
|
||||
* IP-Address of the interface that is pointing towards the remote IP-Address,
|
||||
* if no remote IP-Address is known yet, the statically configured
|
||||
* IP-Addresses are used as fallback. */
|
||||
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
|
||||
int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
|
||||
struct mgcp_endpoint *endp;
|
||||
const struct mgcp_endpoint *endp = conn->conn->endp;
|
||||
const struct mgcp_config *cfg = endp->trunk->cfg;
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
int rc;
|
||||
endp = conn->conn->endp;
|
||||
bool rem_addr_set = !addr_is_any(&conn->end.addr);
|
||||
char *bind_addr;
|
||||
bool rem_addr_set = osmo_sockaddr_is_any(&conn->end.addr) == 0;
|
||||
const char *bind_addr;
|
||||
|
||||
/* Osmux: No smart IP addresses allocation is supported yet. Simply
|
||||
* return the one set in VTY config: */
|
||||
if (mgcp_conn_rtp_is_osmux(conn)) {
|
||||
if (rem_addr_set) {
|
||||
/* Match IP version with what was requested from remote: */
|
||||
bind_addr = conn->end.addr.u.sa.sa_family == AF_INET6 ?
|
||||
cfg->osmux.local_addr_v6 :
|
||||
cfg->osmux.local_addr_v4;
|
||||
} else {
|
||||
/* Choose any of the bind addresses, preferring v6 over v4 if available: */
|
||||
bind_addr = cfg->osmux.local_addr_v6;
|
||||
if (!bind_addr)
|
||||
bind_addr = cfg->osmux.local_addr_v4;
|
||||
}
|
||||
if (!bind_addr) {
|
||||
LOGPCONN(conn->conn, DOSMUX, LOGL_ERROR,
|
||||
"Unable to locate local Osmux address, check your configuration! v4=%u v6=%u remote_known=%s\n",
|
||||
!!cfg->osmux.local_addr_v4,
|
||||
!!cfg->osmux.local_addr_v6,
|
||||
rem_addr_set ? osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf) : "no");
|
||||
return -1;
|
||||
}
|
||||
LOGPCONN(conn->conn, DOSMUX, LOGL_DEBUG,
|
||||
"Using configured osmux bind ip as local bind ip %s\n",
|
||||
bind_addr);
|
||||
osmo_strlcpy(addr, bind_addr, INET6_ADDRSTRLEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try probing the local IP-Address */
|
||||
if (endp->trunk->cfg->net_ports.bind_addr_probe && rem_addr_set) {
|
||||
if (cfg->net_ports.bind_addr_probe && rem_addr_set) {
|
||||
rc = osmo_sock_local_ip(addr, osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf));
|
||||
if (rc < 0)
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
@@ -121,22 +135,22 @@ void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
|
||||
"selected local rtp bind ip %s by probing using remote ip %s\n",
|
||||
addr, osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf));
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Select from preconfigured IP-Addresses. We don't have bind_addr for Osmux (yet?). */
|
||||
/* Select from preconfigured IP-Addresses. */
|
||||
if (rem_addr_set) {
|
||||
/* Check there is a bind IP for the RTP traffic configured,
|
||||
* if so, use that IP-Address */
|
||||
bind_addr = conn->end.addr.u.sa.sa_family == AF_INET6 ?
|
||||
endp->trunk->cfg->net_ports.bind_addr_v6 :
|
||||
endp->trunk->cfg->net_ports.bind_addr_v4;
|
||||
cfg->net_ports.bind_addr_v6 :
|
||||
cfg->net_ports.bind_addr_v4;
|
||||
} else {
|
||||
/* Choose any of the bind addresses, preferring v6 over v4 */
|
||||
bind_addr = endp->trunk->cfg->net_ports.bind_addr_v6;
|
||||
bind_addr = cfg->net_ports.bind_addr_v6;
|
||||
if (!strlen(bind_addr))
|
||||
bind_addr = endp->trunk->cfg->net_ports.bind_addr_v4;
|
||||
bind_addr = cfg->net_ports.bind_addr_v4;
|
||||
}
|
||||
if (strlen(bind_addr)) {
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
|
||||
@@ -146,17 +160,18 @@ void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
|
||||
/* No specific bind IP is configured for the RTP traffic, so
|
||||
* assume the IP where we listen for incoming MGCP messages
|
||||
* as bind IP */
|
||||
bind_addr = endp->trunk->cfg->source_addr;
|
||||
bind_addr = cfg->source_addr;
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
|
||||
"using mgcp bind ip as local rtp bind ip: %s\n", bind_addr);
|
||||
}
|
||||
osmo_strlcpy(addr, bind_addr, INET6_ADDRSTRLEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This does not need to be a precision timestamp and
|
||||
* is allowed to wrap quite fast. The returned value is
|
||||
* 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;
|
||||
uint64_t ret;
|
||||
@@ -529,7 +544,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
|
||||
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
|
||||
seq = ntohs(rtp_hdr->sequence);
|
||||
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);
|
||||
marker_bit = !!rtp_hdr->marker;
|
||||
transit = arrival_time - timestamp;
|
||||
@@ -676,7 +691,7 @@ static int rfc5993_hr_convert(struct mgcp_endpoint *endp, struct msgb *msg)
|
||||
{
|
||||
struct rtp_hdr *rtp_hdr;
|
||||
if (msgb_length(msg) < sizeof(struct rtp_hdr)) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n",
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "RTP packet too short (%d < %zu)\n",
|
||||
msgb_length(msg), sizeof(struct rtp_hdr));
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -702,11 +717,18 @@ static int rfc5993_hr_convert(struct mgcp_endpoint *endp, struct msgb *msg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For AMR RTP two framing modes are defined RFC3267. There is a bandwith
|
||||
/*! Convert msg to AMR RTP framing mode specified by target_is_oa.
|
||||
* \param[in] endp MGCP Endpoint where this message belongs to (used for logging purposes)
|
||||
* \param[in] msg Message containing an AMR RTP payload (in octet-aligned or bandwidth-efficient format).
|
||||
* \param[in] target_is_oa the target framing mode that msg will contain after this function succeeds.
|
||||
* \returns The size of the new RTP AMR content on success, negative on error.
|
||||
*
|
||||
* For AMR RTP two framing modes are defined RFC3267. There is a bandwidth
|
||||
* efficient encoding scheme where all fields are packed together one after
|
||||
* another and an octet aligned mode where all fields are aligned to octet
|
||||
* boundaries. This function is used to convert between the two modes */
|
||||
static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
|
||||
* boundaries. This function is used to convert between the two modes.
|
||||
*/
|
||||
int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
|
||||
bool target_is_oa)
|
||||
{
|
||||
/* NOTE: the msgb has an allocated length of RTP_BUF_SIZE, so there is
|
||||
@@ -715,6 +737,7 @@ static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
|
||||
struct rtp_hdr *rtp_hdr;
|
||||
unsigned int payload_len;
|
||||
int rc;
|
||||
bool orig_is_oa;
|
||||
|
||||
if (msgb_length(msg) < sizeof(struct rtp_hdr)) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n", msgb_length(msg), sizeof(struct rtp_hdr));
|
||||
@@ -722,10 +745,10 @@ static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
|
||||
}
|
||||
|
||||
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
|
||||
|
||||
payload_len = msgb_length(msg) - sizeof(struct rtp_hdr);
|
||||
orig_is_oa = osmo_amr_is_oa(rtp_hdr->data, payload_len);
|
||||
|
||||
if (osmo_amr_is_oa(rtp_hdr->data, payload_len)) {
|
||||
if (orig_is_oa) {
|
||||
if (!target_is_oa)
|
||||
/* Input data is oa an target format is bwe
|
||||
* ==> convert */
|
||||
@@ -747,27 +770,16 @@ static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
|
||||
}
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"AMR RTP packet conversion failed\n");
|
||||
"RTP AMR packet conversion %s->%s failed: %s\n",
|
||||
orig_is_oa ? "OA" : "BWE",
|
||||
target_is_oa ? "OA" : "BWE",
|
||||
osmo_hexdump(rtp_hdr->data, payload_len));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return msgb_trim(msg, rc + sizeof(struct rtp_hdr));
|
||||
}
|
||||
|
||||
/* Check if a conversion between octet-aligned and bandwith-efficient mode is
|
||||
* indicated. */
|
||||
static bool amr_oa_bwe_convert_indicated(struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
if (codec->param_present == false)
|
||||
return false;
|
||||
if (!codec->param.amr_octet_aligned_present)
|
||||
return false;
|
||||
if (strcmp(codec->subtype_name, "AMR") != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Return whether an RTP packet with AMR payload is in octet-aligned mode.
|
||||
* Return 0 if in bandwidth-efficient mode, 1 for octet-aligned mode, and negative if the RTP data is invalid. */
|
||||
static int amr_oa_check(char *data, int len)
|
||||
@@ -789,7 +801,7 @@ static int amr_oa_check(char *data, int len)
|
||||
|
||||
/* Forward data to a debug tap. This is debug function that is intended for
|
||||
* 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;
|
||||
|
||||
@@ -815,7 +827,7 @@ static void gen_rtp_header(struct msgb *msg, struct mgcp_rtp_end *rtp_end,
|
||||
|
||||
hdr->version = 2;
|
||||
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->ssrc = state->alt_rtp_tx_ssrc;
|
||||
}
|
||||
@@ -826,7 +838,7 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad
|
||||
{
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
|
||||
if (addr_is_any(&conn->end.addr)) {
|
||||
if (osmo_sockaddr_is_any(&conn->end.addr) != 0) {
|
||||
switch (conn->conn->mode) {
|
||||
case MGCP_CONN_LOOPBACK:
|
||||
/* HACK: for IuUP, we want to reply with an IuUP Initialization ACK upon the first RTP
|
||||
@@ -875,14 +887,14 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad
|
||||
* the same as the remote port where we transmit outgoing RTP traffic
|
||||
* to (set by MDCX). We use this to check the origin of the data for
|
||||
* plausibility. */
|
||||
if (ntohs(conn->end.rtp_port) != osmo_sockaddr_port(&addr->u.sa) &&
|
||||
if (osmo_sockaddr_port(&conn->end.addr.u.sa) != osmo_sockaddr_port(&addr->u.sa) &&
|
||||
ntohs(conn->end.rtcp_port) != osmo_sockaddr_port(&addr->u.sa)) {
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"data from wrong source port: %d, ",
|
||||
osmo_sockaddr_port(&addr->u.sa));
|
||||
LOGPC(DRTP, LOGL_ERROR,
|
||||
"expected: %d for RTP or %d for RTCP\n",
|
||||
ntohs(conn->end.rtp_port), ntohs(conn->end.rtcp_port));
|
||||
osmo_sockaddr_port(&conn->end.addr.u.sa), ntohs(conn->end.rtcp_port));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR, "packet tossed\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -895,28 +907,29 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad
|
||||
static int check_rtp_destin(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
bool ip_is_any = addr_is_any(&conn->end.addr);
|
||||
bool ip_is_any = osmo_sockaddr_is_any(&conn->end.addr) != 0;
|
||||
uint16_t port = osmo_sockaddr_port(&conn->end.addr.u.sa);
|
||||
|
||||
/* Note: it is legal to create a connection but never setting a port
|
||||
* and IP-address for outgoing data. */
|
||||
if (ip_is_any && conn->end.rtp_port == 0) {
|
||||
if (ip_is_any && port == 0) {
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
|
||||
"destination IP-address and rtp port is not (yet) known (%s:%u)\n",
|
||||
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), conn->end.rtp_port);
|
||||
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ip_is_any) {
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"destination IP-address is invalid (%s:%u)\n",
|
||||
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), conn->end.rtp_port);
|
||||
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (conn->end.rtp_port == 0) {
|
||||
if (port == 0) {
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"destination rtp port is invalid (%s:%u)\n",
|
||||
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), conn->end.rtp_port);
|
||||
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -975,14 +988,21 @@ static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
|
||||
* the length is because we currently handle IUUP packets as RTP
|
||||
* packets, so they must pass this check, if we weould be more
|
||||
* strict here, we would possibly break 3G. (see also FIXME note
|
||||
* below */
|
||||
* below.*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Send RTP data. Possible options are standard RTP packet
|
||||
* transmission or trsmission via an osmux connection */
|
||||
static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
|
||||
/*! Dispatch msg bridged from the sister conn in the endpoint.
|
||||
* \param[in] conn_dst The destination conn that should handle and transmit the content to
|
||||
* its peer outside MGW.
|
||||
* \param[in] msg msgb containing an RTP pkt received by the sister conn in the endpoint,
|
||||
* \returns bytes sent, -1 on error.
|
||||
*
|
||||
* Possible options are standard RTP packet transmission, transmission
|
||||
* via IuUP or transmission via an osmux connection.
|
||||
*/
|
||||
static int mgcp_conn_rtp_dispatch_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
|
||||
{
|
||||
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
|
||||
enum rtp_proto proto = mc->proto;
|
||||
@@ -1007,12 +1027,24 @@ static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
|
||||
"using mgcp_send() to forward data directly\n");
|
||||
return mgcp_send(endp, proto == MGCP_PROTO_RTP,
|
||||
mc->from_addr, msg, conn_src, conn_dst);
|
||||
case MGCP_OSMUX_BSC_NAT:
|
||||
case MGCP_OSMUX_BSC:
|
||||
case MGCP_RTP_OSMUX:
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"endpoint type is MGCP_OSMUX_BSC_NAT, "
|
||||
"endpoint type is MGCP_RTP_OSMUX, "
|
||||
"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 conn_osmux_send_rtp(conn_dst, msg);
|
||||
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
|
||||
@@ -1026,26 +1058,22 @@ static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
|
||||
/*! send udp packet.
|
||||
* \param[in] fd associated file descriptor.
|
||||
* \param[in] addr destination ip-address.
|
||||
* \param[in] port destination UDP port (network byte order).
|
||||
* \param[in] buf buffer that holds the data to be send.
|
||||
* \param[in] len length of the data to be sent.
|
||||
* \returns bytes sent, -1 on error. */
|
||||
int mgcp_udp_send(int fd, struct osmo_sockaddr *addr, int port, const char *buf, int len)
|
||||
int mgcp_udp_send(int fd, const struct osmo_sockaddr *addr, const char *buf, int len)
|
||||
{
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
size_t addr_len;
|
||||
bool is_ipv6 = addr->u.sa.sa_family == AF_INET6;
|
||||
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"sending %i bytes length packet to %s:%u ...\n", len,
|
||||
osmo_sockaddr_ntop(&addr->u.sa, ipbuf),
|
||||
ntohs(port));
|
||||
osmo_sockaddr_port(&addr->u.sa));
|
||||
|
||||
if (is_ipv6) {
|
||||
addr->u.sin6.sin6_port = port;
|
||||
if (addr->u.sa.sa_family == AF_INET6) {
|
||||
addr_len = sizeof(addr->u.sin6);
|
||||
} else {
|
||||
addr->u.sin.sin_port = port;
|
||||
addr_len = sizeof(addr->u.sin);
|
||||
}
|
||||
|
||||
@@ -1060,6 +1088,7 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
int rc;
|
||||
int was_rtcp = 0;
|
||||
struct osmo_sockaddr rtcp_addr;
|
||||
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(conn);
|
||||
@@ -1073,8 +1102,11 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
if (check_rtp_destin(conn) != 0)
|
||||
goto failed;
|
||||
|
||||
rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
|
||||
conn->end.rtp_port, rtp_dummy_payload, sizeof(rtp_dummy_payload));
|
||||
if (mgcp_conn_rtp_is_iuup(conn))
|
||||
rc = mgcp_conn_iuup_send_dummy(conn);
|
||||
else
|
||||
rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
|
||||
rtp_dummy_payload, sizeof(rtp_dummy_payload));
|
||||
|
||||
if (rc == -1)
|
||||
goto failed;
|
||||
@@ -1083,8 +1115,10 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
return rc;
|
||||
|
||||
was_rtcp = 1;
|
||||
rc = mgcp_udp_send(conn->end.rtcp.fd, &conn->end.addr,
|
||||
conn->end.rtcp_port, rtp_dummy_payload, sizeof(rtp_dummy_payload));
|
||||
rtcp_addr = conn->end.addr;
|
||||
osmo_sockaddr_set_port(&rtcp_addr.u.sa, ntohs(conn->end.rtcp_port));
|
||||
rc = mgcp_udp_send(conn->end.rtcp.fd, &rtcp_addr,
|
||||
rtp_dummy_payload, sizeof(rtp_dummy_payload));
|
||||
|
||||
if (rc >= 0)
|
||||
return rc;
|
||||
@@ -1130,17 +1164,21 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
|
||||
else
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering RTCP packet...\n");
|
||||
|
||||
/* FIXME: It is legal that the payload type on the egress connection is
|
||||
* different from the payload type that has been negotiated on the
|
||||
* ingress connection. Essentially the codecs are the same so we can
|
||||
* match them and patch the payload type. However, if we can not find
|
||||
* the codec pendant (everything ist equal except the PT), we are of
|
||||
* course unable to patch the payload type. A situation like this
|
||||
* should not occur if transcoding is consequently avoided. Until
|
||||
* we have transcoding support in osmo-mgw we can not resolve this. */
|
||||
if (is_rtp) {
|
||||
/* Patch the payload type number: translate from conn_src to conn_dst.
|
||||
* Do not patch for IuUP, where the correct payload type number is already set in bridge_iuup_to_rtp_peer():
|
||||
* IuUP -> AMR: calls this function, skip patching if conn_src is IuUP.
|
||||
* {AMR or IuUP} -> IuUP: calls mgcp_udp_send() directly, skipping this function: No need to examine dst. */
|
||||
if (is_rtp && !mgcp_conn_rtp_is_iuup(conn_src)) {
|
||||
rc = mgcp_patch_pt(conn_src, conn_dst, msg);
|
||||
if (rc < 0) {
|
||||
/* FIXME: It is legal that the payload type on the egress connection is
|
||||
* different from the payload type that has been negotiated on the
|
||||
* ingress connection. Essentially the codecs are the same so we can
|
||||
* match them and patch the payload type. However, if we can not find
|
||||
* the codec pendant (everything ist equal except the PT), we are of
|
||||
* course unable to patch the payload type. A situation like this
|
||||
* should not occur if transcoding is consequently avoided. Until
|
||||
* we have transcoding support in osmo-mgw we can not resolve this. */
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"can not patch PT because no suitable egress codec was found.\n");
|
||||
}
|
||||
@@ -1164,7 +1202,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
|
||||
"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)
|
||||
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
|
||||
);
|
||||
} else if (is_rtp) {
|
||||
int cont;
|
||||
@@ -1185,12 +1223,15 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
|
||||
mgcp_patch_and_count(endp, rtp_state, rtp_end,
|
||||
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 (mgcp_codec_amr_align_mode_is_indicated(conn_dst->end.codec)) {
|
||||
rc = amr_oa_bwe_convert(endp, msg,
|
||||
conn_dst->end.codec->param.amr_octet_aligned);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"Error in AMR octet-aligned <-> bandwidth-efficient mode conversion\n");
|
||||
"Error in AMR octet-aligned <-> bandwidth-efficient mode conversion (target=%s)\n",
|
||||
conn_dst->end.codec->param.amr_octet_aligned ? "octet-aligned" : "bandwidth-efficient");
|
||||
break;
|
||||
}
|
||||
} else if (rtp_end->rfc5993_hr_convert &&
|
||||
@@ -1207,32 +1248,14 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
|
||||
"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)
|
||||
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_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);
|
||||
|
||||
/* 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,
|
||||
(char *)msgb_data(msg), msgb_length(msg));
|
||||
|
||||
if (len <= 0)
|
||||
@@ -1247,15 +1270,17 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
|
||||
} while (buflen > 0);
|
||||
return nbytes;
|
||||
} else if (!trunk->omit_rtcp) {
|
||||
struct osmo_sockaddr rtcp_addr = rtp_end->addr;
|
||||
osmo_sockaddr_set_port(&rtcp_addr.u.sa, rtp_end->rtcp_port);
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"send 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)
|
||||
dest_name, osmo_sockaddr_ntop(&rtcp_addr.u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&rtp_end->addr.u.sa),
|
||||
osmo_sockaddr_port(&rtcp_addr.u.sa)
|
||||
);
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtcp.fd,
|
||||
&rtp_end->addr,
|
||||
rtp_end->rtcp_port, (char *)msgb_data(msg), msgb_length(msg));
|
||||
len = mgcp_udp_send(rtp_end->rtcp.fd, &rtcp_addr,
|
||||
(char *)msgb_data(msg), msgb_length(msg));
|
||||
|
||||
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
|
||||
@@ -1267,13 +1292,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! dispatch incoming RTP packet to opposite RTP connection.
|
||||
* \param[in] proto protocol (MGCP_CONN_TYPE_RTP or MGCP_CONN_TYPE_RTCP).
|
||||
* \param[in] addr socket address where the RTP packet has been received from.
|
||||
* \param[in] buf buffer that hold the RTP payload.
|
||||
* \param[in] buf_size size data length of buf.
|
||||
* \param[in] conn originating connection.
|
||||
* \returns 0 on success, -1 on ERROR. */
|
||||
/*! Dispatch incoming RTP packet to opposite RTP connection.
|
||||
* \param[in] msg Message buffer to bridge, coming from source connection.
|
||||
* msg shall contain "struct osmo_rtp_msg_ctx *" attached in
|
||||
* "OSMO_RTP_MSG_CTX(msg)".
|
||||
* \returns 0 on success, -1 on ERROR.
|
||||
*/
|
||||
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
|
||||
{
|
||||
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
|
||||
@@ -1291,6 +1315,9 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
|
||||
* destination connection is known the RTP packet is sent via
|
||||
* 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
|
||||
* incoming data back to the origin */
|
||||
@@ -1299,25 +1326,15 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
|
||||
* packets back to their origin. We will use the originating
|
||||
* address data from the UDP packet header to patch the
|
||||
* outgoing address in connection on the fly */
|
||||
if (conn->u.rtp.end.rtp_port == 0) {
|
||||
if (osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa) == 0) {
|
||||
memcpy(&conn->u.rtp.end.addr, from_addr,
|
||||
sizeof(conn->u.rtp.end.addr));
|
||||
switch (from_addr->u.sa.sa_family) {
|
||||
case AF_INET:
|
||||
conn->u.rtp.end.rtp_port = from_addr->u.sin.sin_port;
|
||||
break;
|
||||
case AF_INET6:
|
||||
conn->u.rtp.end.rtp_port = from_addr->u.sin6.sin6_port;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
|
||||
"loopback mode: implicitly using source address (%s:%u) as destination address\n",
|
||||
osmo_sockaddr_ntop(&from_addr->u.sa, ipbuf),
|
||||
conn->u.rtp.end.rtp_port);
|
||||
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa));
|
||||
}
|
||||
return mgcp_send_rtp(conn_src, msg);
|
||||
return mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
|
||||
}
|
||||
|
||||
/* Find a destination connection. */
|
||||
@@ -1349,7 +1366,7 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
|
||||
}
|
||||
|
||||
/* Dispatch RTP packet to destination RTP connection */
|
||||
return mgcp_send_rtp(&conn_dst->u.rtp, msg);
|
||||
return mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg);
|
||||
}
|
||||
|
||||
/*! dispatch incoming RTP packet to E1 subslot, handle RTCP packets locally.
|
||||
@@ -1374,25 +1391,15 @@ int mgcp_dispatch_e1_bridge_cb(struct msgb *msg)
|
||||
* packets back to their origin. We will use the originating
|
||||
* address data from the UDP packet header to patch the
|
||||
* outgoing address in connection on the fly */
|
||||
if (conn->u.rtp.end.rtp_port == 0) {
|
||||
if (osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa) == 0) {
|
||||
memcpy(&conn->u.rtp.end.addr, from_addr,
|
||||
sizeof(conn->u.rtp.end.addr));
|
||||
switch (from_addr->u.sa.sa_family) {
|
||||
case AF_INET:
|
||||
conn->u.rtp.end.rtp_port = from_addr->u.sin.sin_port;
|
||||
break;
|
||||
case AF_INET6:
|
||||
conn->u.rtp.end.rtp_port = from_addr->u.sin6.sin6_port;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
|
||||
"loopback mode: implicitly using source address (%s:%u) as destination address\n",
|
||||
osmo_sockaddr_ntop(&from_addr->u.sa, ipbuf),
|
||||
conn->u.rtp.end.rtp_port);
|
||||
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa));
|
||||
}
|
||||
return mgcp_send_rtp(conn_src, msg);
|
||||
return mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
|
||||
}
|
||||
|
||||
/* Forward to E1 */
|
||||
@@ -1468,7 +1475,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
msgb_put(msg, ret);
|
||||
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n",
|
||||
proto == MGCP_PROTO_RTP ? "RTP" : "RTPC",
|
||||
proto == MGCP_PROTO_RTP ? "RTP" : "RTCP",
|
||||
msgb_length(msg), osmo_sockaddr_ntop(&addr.u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&addr.u.sa));
|
||||
|
||||
@@ -1501,12 +1508,12 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
sizeof(struct sockaddr_in)));
|
||||
|
||||
/* Increment RX statistics */
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(conn_src->rate_ctr_group, RTP_PACKETS_RX_CTR));
|
||||
rate_ctr_add(rate_ctr_group_get_ctr(conn_src->rate_ctr_group, RTP_OCTETS_RX_CTR), msgb_length(msg));
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(conn_src->ctrg, RTP_PACKETS_RX_CTR));
|
||||
rate_ctr_add(rate_ctr_group_get_ctr(conn_src->ctrg, RTP_OCTETS_RX_CTR), msgb_length(msg));
|
||||
/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */
|
||||
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
forward_data(fd->fd, &conn_src->tap_in, msg);
|
||||
forward_data_tap(fd->fd, &conn_src->tap_in, msg);
|
||||
|
||||
rc = rx_rtp(msg);
|
||||
|
||||
@@ -1515,6 +1522,7 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Note: This function is able to handle RTP and RTCP */
|
||||
static int rx_rtp(struct msgb *msg)
|
||||
{
|
||||
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
|
||||
@@ -1525,24 +1533,30 @@ static int rx_rtp(struct msgb *msg)
|
||||
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx_rtp(%u bytes)\n", msgb_length(msg));
|
||||
|
||||
mgcp_conn_watchdog_kick(conn_src->conn);
|
||||
|
||||
/* If AMR is configured for the ingress connection a conversion of the
|
||||
* framing mode (octet-aligned vs. bandwith-efficient is explicitly
|
||||
* define, then we check if the incoming payload matches that
|
||||
* expectation. */
|
||||
if (amr_oa_bwe_convert_indicated(conn_src->end.codec)) {
|
||||
int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg));
|
||||
if (oa < 0)
|
||||
return -1;
|
||||
if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check if the origin of the RTP packet seems plausible */
|
||||
if (!trunk->rtp_accept_all && check_rtp_origin(conn_src, from_addr))
|
||||
return -1;
|
||||
|
||||
/* If AMR is configured for the ingress connection and conversion of the
|
||||
* framing mode (octet-aligned vs. bandwith-efficient) is explicitly
|
||||
* defined, then we check if the incoming payload matches that
|
||||
* expectation. */
|
||||
if (mc->proto == MGCP_PROTO_RTP &&
|
||||
mgcp_codec_amr_align_mode_is_indicated(conn_src->end.codec)) {
|
||||
int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg));
|
||||
if (oa < 0)
|
||||
return -1;
|
||||
if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
|
||||
"rx_rtp(%u bytes): Expected RTP AMR octet-aligned=%u but got octet-aligned=%u."
|
||||
" check the config of your call-agent!\n",
|
||||
msgb_length(msg), conn_src->end.codec->param.amr_octet_aligned, oa);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
mgcp_conn_watchdog_kick(conn_src->conn);
|
||||
|
||||
/* Execute endpoint specific implementation that handles the
|
||||
* dispatching of the RTP data */
|
||||
return conn->endp->type->dispatch_rtp_cb(msg);
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -46,6 +46,8 @@
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_iuup.h>
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
|
||||
/* Contains the last successfully resolved endpoint name. This variable is used
|
||||
* for the unit-tests to verify that the endpoint was correctly resolved. */
|
||||
@@ -97,10 +99,6 @@ struct mgcp_request {
|
||||
/* function pointer to the request handler */
|
||||
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 */
|
||||
char *debug_name;
|
||||
};
|
||||
@@ -112,32 +110,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_noti_req(struct mgcp_request_data *data);
|
||||
static const struct mgcp_request mgcp_requests[] = {
|
||||
{ .name = "AUEP",
|
||||
.handle_request = handle_audit_endpoint,
|
||||
.debug_name = "AuditEndpoint",
|
||||
.require_endp = true },
|
||||
{ .name = "CRCX",
|
||||
.handle_request = handle_create_con,
|
||||
.debug_name = "CreateConnection",
|
||||
.require_endp = true },
|
||||
{ .name = "DLCX",
|
||||
.handle_request = handle_delete_con,
|
||||
.debug_name = "DeleteConnection",
|
||||
.require_endp = false },
|
||||
{ .name = "MDCX",
|
||||
.handle_request = handle_modify_con,
|
||||
.debug_name = "ModifiyConnection",
|
||||
.require_endp = true },
|
||||
{ .name = "RQNT",
|
||||
.handle_request = handle_noti_req,
|
||||
.debug_name = "NotificationRequest",
|
||||
.require_endp = true },
|
||||
{ .name = "AUEP", .handle_request = handle_audit_endpoint, .debug_name = "AuditEndpoint" },
|
||||
{
|
||||
.name = "CRCX",
|
||||
.handle_request = handle_create_con,
|
||||
.debug_name = "CreateConnection",
|
||||
},
|
||||
{
|
||||
.name = "DLCX",
|
||||
.handle_request = handle_delete_con,
|
||||
.debug_name = "DeleteConnection",
|
||||
},
|
||||
{
|
||||
.name = "MDCX",
|
||||
.handle_request = handle_modify_con,
|
||||
.debug_name = "ModifiyConnection",
|
||||
},
|
||||
{
|
||||
.name = "RQNT",
|
||||
.handle_request = handle_noti_req,
|
||||
.debug_name = "NotificationRequest",
|
||||
},
|
||||
|
||||
/* SPEC extension */
|
||||
{ .name = "RSIP",
|
||||
.handle_request = handle_rsip,
|
||||
.debug_name = "ReSetInProgress",
|
||||
.require_endp = true },
|
||||
{
|
||||
.name = "RSIP",
|
||||
.handle_request = handle_rsip,
|
||||
.debug_name = "ReSetInProgress",
|
||||
},
|
||||
};
|
||||
|
||||
/* Initalize transcoder */
|
||||
@@ -149,7 +149,12 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn_dst = 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_RTP_OSMUX:
|
||||
case MGCP_RTP_IUUP:
|
||||
break;
|
||||
default:
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
|
||||
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 */
|
||||
static struct msgb *mgcp_msgb_alloc(void)
|
||||
static struct msgb *mgcp_msgb_alloc(void *ctx)
|
||||
{
|
||||
struct msgb *msg;
|
||||
msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
|
||||
if (!msg)
|
||||
msg = msgb_alloc_headroom_c(ctx, 4096, 128, "MGCP msg");
|
||||
|
||||
if (!msg) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
@@ -186,7 +194,7 @@ static struct msgb *mgcp_msgb_alloc(void)
|
||||
/* Helper function for do_retransmission() and create_resp() */
|
||||
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)
|
||||
return NULL;
|
||||
|
||||
@@ -196,15 +204,14 @@ static struct msgb *create_retransmission_response(const struct mgcp_endpoint *e
|
||||
return msg;
|
||||
}
|
||||
|
||||
static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
|
||||
const char *txt, const char *msg,
|
||||
const char *trans, const char *param,
|
||||
const char *sdp)
|
||||
static struct msgb *create_resp(void *msgctx, struct mgcp_endpoint *endp, int code, const char *txt, const char *msg,
|
||||
const char *trans, const char *param, const char *sdp)
|
||||
{
|
||||
int len;
|
||||
struct msgb *res;
|
||||
|
||||
res = mgcp_msgb_alloc();
|
||||
OSMO_ASSERT(msgctx != 0);
|
||||
res = mgcp_msgb_alloc(msgctx);
|
||||
if (!res)
|
||||
return NULL;
|
||||
|
||||
@@ -236,26 +243,22 @@ static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp,
|
||||
int code, const char *msg,
|
||||
const char *trans,
|
||||
const char *param)
|
||||
static struct msgb *create_ok_resp_with_param(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
|
||||
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,
|
||||
int code, const char *msg,
|
||||
static struct msgb *create_ok_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
|
||||
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,
|
||||
int code, const char *msg,
|
||||
static struct msgb *create_err_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
|
||||
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) */
|
||||
@@ -278,7 +281,7 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
|
||||
int rc;
|
||||
struct msgb *result;
|
||||
|
||||
sdp = msgb_alloc_headroom(4096, 128, "sdp record");
|
||||
sdp = msgb_alloc_headroom_c(endp->trunk, 4096, 128, "sdp record");
|
||||
if (!sdp)
|
||||
return NULL;
|
||||
|
||||
@@ -298,7 +301,7 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
|
||||
|
||||
/* Attach optional OSMUX parameters */
|
||||
if (mgcp_conn_rtp_is_osmux(conn)) {
|
||||
rc = msgb_printf(sdp, "X-Osmux: %u\r\n", conn->osmux.cid);
|
||||
rc = msgb_printf(sdp, MGCP_X_OSMO_OSMUX_HEADER " %u\r\n", conn->osmux.local_cid);
|
||||
if (rc < 0)
|
||||
goto error;
|
||||
}
|
||||
@@ -309,7 +312,7 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
|
||||
rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
|
||||
if (rc < 0)
|
||||
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);
|
||||
return result;
|
||||
error:
|
||||
@@ -321,8 +324,13 @@ error:
|
||||
* osmux connection, send the dummy packet via OSMUX */
|
||||
static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
if (conn->osmux.state != OSMUX_STATE_DISABLED)
|
||||
osmux_send_dummy(endp, conn);
|
||||
/* Avoid sending dummy packet if the remote address was not yet
|
||||
* configured through CRCX/MDCX: */
|
||||
if (!mgcp_rtp_end_remote_addr_available(&conn->end))
|
||||
return;
|
||||
|
||||
if (mgcp_conn_rtp_is_osmux(conn))
|
||||
osmux_send_dummy(conn);
|
||||
else
|
||||
mgcp_send_dummy(endp, conn);
|
||||
}
|
||||
@@ -376,7 +384,7 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
if (rc < 0) {
|
||||
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));
|
||||
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. */
|
||||
@@ -397,7 +405,7 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
if (!rq.trunk) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n",
|
||||
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 {
|
||||
/* If the endpoint name suggests that the request refers to a specific endpoint, then the
|
||||
@@ -405,7 +413,7 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
|
||||
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 {
|
||||
osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name));
|
||||
@@ -422,15 +430,6 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
/* Find an appropriate handler for the current request and execute it */
|
||||
for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) {
|
||||
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 */
|
||||
if (rq.endp)
|
||||
LOGP(DLMGCP, LOGL_INFO,
|
||||
@@ -461,7 +460,12 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq)
|
||||
{
|
||||
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
|
||||
@@ -710,7 +714,7 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
|
||||
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
|
||||
"Configuring RTP endpoint: local port %d%s%s\n",
|
||||
ntohs(rtp->rtp_port),
|
||||
osmo_sockaddr_port(&rtp->addr.u.sa),
|
||||
rtp->force_aligned_timing ? ", force constant timing" : "",
|
||||
rtp->force_constant_ssrc ? ", force constant ssrc" : "");
|
||||
}
|
||||
@@ -741,12 +745,12 @@ uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
|
||||
*/
|
||||
static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
|
||||
{
|
||||
if (!endp->trunk->cfg->osmux_init) {
|
||||
if (osmux_init(OSMUX_ROLE_BSC, endp->trunk->cfg) < 0) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
|
||||
if (!endp->trunk->cfg->osmux.initialized) {
|
||||
if (osmux_init(endp->trunk) < 0) {
|
||||
LOGPENDP(endp, DOSMUX, LOGL_ERROR, "Cannot init OSMUX\n");
|
||||
return -3;
|
||||
}
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
|
||||
LOGPENDP(endp, DOSMUX, LOGL_NOTICE, "OSMUX socket has been set up\n");
|
||||
}
|
||||
|
||||
return mgcp_parse_osmux_cid(line);
|
||||
@@ -847,7 +851,7 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
|
||||
const char *callid = NULL;
|
||||
const char *mode = NULL;
|
||||
char *line;
|
||||
int have_sdp = 0, osmux_cid = -2;
|
||||
int have_sdp = 0, remote_osmux_cid = -2;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
struct mgcp_conn *_conn = NULL;
|
||||
char conn_name[512];
|
||||
@@ -855,11 +859,18 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
|
||||
|
||||
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)) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"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: */
|
||||
@@ -879,7 +890,7 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
|
||||
* together with a CRCX, the MGW will assign the
|
||||
* connection identifier by itself on CRCX */
|
||||
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;
|
||||
case 'M':
|
||||
mode = (const char *)line + 3;
|
||||
@@ -887,9 +898,9 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
|
||||
case 'X':
|
||||
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
|
||||
/* If osmux is disabled, just skip setting it up */
|
||||
if (!rq->endp->trunk->cfg->osmux)
|
||||
if (rq->endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF)
|
||||
break;
|
||||
osmux_cid = mgcp_osmux_setup(endp, line);
|
||||
remote_osmux_cid = mgcp_osmux_setup(endp, line);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -905,7 +916,7 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: unhandled option: '%c'/%d\n", *line, *line);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -916,14 +927,14 @@ mgcp_header_done:
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: insufficient parameters, missing callid\n");
|
||||
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) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: insufficient parameters, missing mode\n");
|
||||
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 */
|
||||
@@ -940,7 +951,7 @@ mgcp_header_done:
|
||||
/* There is no more room for a connection, leave
|
||||
* everything as it is and return with an error */
|
||||
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 +969,7 @@ mgcp_header_done:
|
||||
/* This is not our call, leave everything as it is and
|
||||
* return with an error. */
|
||||
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 +980,7 @@ mgcp_header_done:
|
||||
rc = mgcp_endp_claim(endp, callid);
|
||||
if (rc != 0) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -992,16 +1003,17 @@ mgcp_header_done:
|
||||
goto error2;
|
||||
}
|
||||
|
||||
/* Annotate Osmux circuit ID and set it to negotiating state until this
|
||||
* is fully set up from the dummy load. */
|
||||
conn->osmux.state = OSMUX_STATE_DISABLED;
|
||||
if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */
|
||||
conn->osmux.state = OSMUX_STATE_ACTIVATING;
|
||||
if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) {
|
||||
/* If X-Osmux (remote CID) was received (-1 is wilcard), alloc next avail CID as local CID */
|
||||
if (remote_osmux_cid >= -1) {
|
||||
if (osmux_init_conn(conn) < 0) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
|
||||
goto error2;
|
||||
}
|
||||
} else if (endp->trunk->cfg->osmux == OSMUX_USAGE_ONLY) {
|
||||
if (remote_osmux_cid >= 0) {
|
||||
conn->osmux.remote_cid_present = true;
|
||||
conn->osmux.remote_cid = remote_osmux_cid;
|
||||
}
|
||||
} else if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_ONLY) {
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: osmux only and no osmux offered\n");
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
|
||||
@@ -1029,6 +1041,11 @@ mgcp_header_done:
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
|
||||
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,
|
||||
trunk->audio_fmtp_extra);
|
||||
@@ -1043,7 +1060,7 @@ mgcp_header_done:
|
||||
/* check connection mode setting */
|
||||
if (conn->conn->mode != MGCP_CONN_LOOPBACK
|
||||
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
|
||||
&& conn->end.rtp_port == 0) {
|
||||
&& osmo_sockaddr_port(&conn->end.addr.u.sa) == 0) {
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: selected connection mode type requires an opposite end!\n");
|
||||
error_code = 527;
|
||||
@@ -1053,7 +1070,10 @@ mgcp_header_done:
|
||||
|
||||
/* Find a local address for conn based on policy and initial SDP remote
|
||||
information, then find a free port for it */
|
||||
mgcp_get_local_addr(conn->end.local_addr, conn);
|
||||
if (mgcp_get_local_addr(conn->end.local_addr, conn) < 0) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
|
||||
goto error2;
|
||||
}
|
||||
if (allocate_port(endp, conn) != 0) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
|
||||
goto error2;
|
||||
@@ -1066,13 +1086,20 @@ mgcp_header_done:
|
||||
goto error2;
|
||||
}
|
||||
|
||||
/* Notify Osmux conn that CRCX was received */
|
||||
if (mgcp_conn_rtp_is_osmux(conn)) {
|
||||
if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "CRCX: Osmux handling failed!\n");
|
||||
goto error2;
|
||||
}
|
||||
}
|
||||
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
|
||||
"CRCX: Creating connection: port: %u\n", conn->end.local_port);
|
||||
|
||||
/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
|
||||
OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
|
||||
if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
|
||||
mgcp_rtp_end_remote_addr_available(&conn->end) &&
|
||||
trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
|
||||
send_dummy(endp, conn);
|
||||
|
||||
@@ -1088,7 +1115,7 @@ error2:
|
||||
mgcp_endp_release(endp);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"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 */
|
||||
@@ -1107,31 +1134,29 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
|
||||
const char *mode = NULL;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
const char *conn_id = NULL;
|
||||
int osmux_cid = -2;
|
||||
int remote_osmux_cid = -2;
|
||||
int rc;
|
||||
|
||||
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 */
|
||||
if (rq->wildcarded) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: wildcarded endpoint names not supported.\n");
|
||||
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) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: endpoint is not holding a connection.\n");
|
||||
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) {
|
||||
@@ -1165,9 +1190,9 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
|
||||
case 'X':
|
||||
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
|
||||
/* If osmux is disabled, just skip setting it up */
|
||||
if (!endp->trunk->cfg->osmux)
|
||||
if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF)
|
||||
break;
|
||||
osmux_cid = mgcp_osmux_setup(endp, line);
|
||||
remote_osmux_cid = mgcp_osmux_setup(endp, line);
|
||||
break;
|
||||
}
|
||||
/* Ignore unknown X-headers */
|
||||
@@ -1181,7 +1206,7 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
|
||||
"MDCX: Unhandled MGCP option: '%c'/%d\n",
|
||||
line[0], line[0]);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1191,13 +1216,13 @@ mgcp_header_done:
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
|
||||
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);
|
||||
if (!conn) {
|
||||
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);
|
||||
@@ -1244,30 +1269,39 @@ mgcp_header_done:
|
||||
}
|
||||
|
||||
if (mgcp_conn_rtp_is_osmux(conn)) {
|
||||
OSMO_ASSERT(conn->osmux.cid_allocated);
|
||||
if (osmux_cid < -1) {
|
||||
OSMO_ASSERT(conn->osmux.local_cid_allocated);
|
||||
if (remote_osmux_cid < -1) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: Failed to parse Osmux CID!\n");
|
||||
goto error3;
|
||||
} else if (osmux_cid == -1) {
|
||||
} else if (remote_osmux_cid == -1) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: wilcard in MDCX is not supported!\n");
|
||||
goto error3;
|
||||
} else if (osmux_cid != (int) conn->osmux.cid) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: changing already allocated CID is not supported!\n");
|
||||
goto error3;
|
||||
} else if (conn->osmux.remote_cid_present &&
|
||||
remote_osmux_cid != conn->osmux.remote_cid) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: changing already allocated CID is not supported!\n");
|
||||
goto error3;
|
||||
} else {
|
||||
conn->osmux.remote_cid_present = true;
|
||||
conn->osmux.remote_cid = remote_osmux_cid;
|
||||
if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: Osmux handling failed!\n");
|
||||
goto error3;
|
||||
}
|
||||
}
|
||||
/* TODO: In the future (when we have recvCID!=sendCID), we need to
|
||||
tell Osmux code that osmux_cid is to be used as sendCID for
|
||||
that conn. */
|
||||
}
|
||||
|
||||
/* MDCX may have provided a new remote address, which means we may need
|
||||
to update our announced IP addr and re-bind our local end. This can
|
||||
happen for instance if MGW initially provided an IPv4 during CRCX
|
||||
ACK, and now MDCX tells us the remote has an IPv6 address. */
|
||||
mgcp_get_local_addr(new_local_addr, conn);
|
||||
if (mgcp_get_local_addr(new_local_addr, conn) < 0) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
|
||||
goto error3;
|
||||
}
|
||||
if (strcmp(new_local_addr, conn->end.local_addr)) {
|
||||
osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
|
||||
mgcp_free_rtp_port(&conn->end);
|
||||
@@ -1291,7 +1325,6 @@ mgcp_header_done:
|
||||
/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
|
||||
OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
|
||||
if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
|
||||
mgcp_rtp_end_remote_addr_available(&conn->end) &&
|
||||
trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
|
||||
send_dummy(endp, conn);
|
||||
|
||||
@@ -1304,7 +1337,7 @@ mgcp_header_done:
|
||||
mgcp_endp_update(endp);
|
||||
return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
|
||||
error3:
|
||||
return create_err_response(endp, error_code, "MDCX", pdata->trans);
|
||||
return create_err_response(endp, endp, error_code, "MDCX", pdata->trans);
|
||||
|
||||
out_silent:
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
|
||||
@@ -1335,14 +1368,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));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"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)) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"DLCX: endpoint is not holding a connection.\n");
|
||||
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) {
|
||||
@@ -1357,7 +1403,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
|
||||
LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
|
||||
"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));
|
||||
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) {
|
||||
@@ -1373,7 +1419,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
|
||||
LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
|
||||
"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));
|
||||
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;
|
||||
@@ -1389,24 +1435,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",
|
||||
line[0], line[0]);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 endp pointer populated. */
|
||||
OSMO_ASSERT(endp);
|
||||
@@ -1429,7 +1462,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
|
||||
/* Note: In this case we do not return any statistics,
|
||||
* as we assume that the client is not interested in
|
||||
* this case. */
|
||||
return create_ok_response(endp, 200, "DLCX", pdata->trans);
|
||||
return create_ok_response(endp, endp, 200, "DLCX", pdata->trans);
|
||||
}
|
||||
|
||||
/* Find the connection */
|
||||
@@ -1458,10 +1491,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));
|
||||
if (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:
|
||||
return create_err_response(endp, error_code, "DLCX", pdata->trans);
|
||||
return create_err_response(endp, endp, error_code, "DLCX", pdata->trans);
|
||||
|
||||
out_silent:
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
|
||||
@@ -1516,14 +1549,13 @@ static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
|
||||
|
||||
/* we didn't see a signal request with a tone */
|
||||
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)
|
||||
res = rq->pdata->cfg->rqnt_cb(rq->endp, tone);
|
||||
|
||||
return res == 0 ?
|
||||
create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans) :
|
||||
create_err_response(rq->endp, res, "RQNT", rq->pdata->trans);
|
||||
return res == 0 ? create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans) :
|
||||
create_err_response(rq->endp, rq->endp, res, "RQNT", rq->pdata->trans);
|
||||
}
|
||||
|
||||
/* Connection keepalive timer, will take care that dummy packets are send
|
||||
@@ -1561,8 +1593,7 @@ static void mgcp_keepalive_timer_cb(void *_trunk)
|
||||
struct mgcp_endpoint *endp = trunk->endpoints[i];
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP &&
|
||||
conn->mode == MGCP_CONN_RECV_ONLY &&
|
||||
mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end))
|
||||
conn->mode == MGCP_CONN_RECV_ONLY)
|
||||
send_dummy(endp, &conn->u.rtp);
|
||||
}
|
||||
}
|
||||
@@ -1616,7 +1647,6 @@ struct mgcp_config *mgcp_config_alloc(void)
|
||||
|
||||
cfg->source_port = 2427;
|
||||
osmo_strlcpy(cfg->source_addr, "0.0.0.0", sizeof(cfg->source_addr));
|
||||
osmo_strlcpy(cfg->osmux_addr, "0.0.0.0", sizeof(cfg->osmux_addr));
|
||||
|
||||
cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
|
||||
cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
|
||||
|
@@ -43,7 +43,7 @@ static const struct rate_ctr_desc mgcp_general_ctr_desc[] = {
|
||||
{ "mgcp:err_rx_no_endpoint", "can't find MGCP endpoint, probably we've used all allocated endpoints." },
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc mgcp_general_ctr_group_desc = {
|
||||
static const struct rate_ctr_group_desc mgcp_general_ctr_group_desc = {
|
||||
.group_name_prefix = "mgcp",
|
||||
.group_description = "mgcp general statistics",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
@@ -71,7 +71,7 @@ static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
|
||||
[MGCP_CRCX_FAIL_CLAIM] = { "crcx:claim", "endpoint can not be claimed." },
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
|
||||
static const struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
|
||||
.group_name_prefix = "crcx",
|
||||
.group_description = "crxc statistics",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
@@ -97,7 +97,7 @@ static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
|
||||
[MGCP_MDCX_FAIL_AVAIL] = { "mdcx:unavailable", "endpoint unavailable." },
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
|
||||
static const struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
|
||||
.group_name_prefix = "mdcx",
|
||||
.group_description = "mdcx statistics",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
@@ -116,7 +116,7 @@ static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = {
|
||||
[MGCP_DLCX_FAIL_AVAIL] = { "dlcx:unavailable", "endpoint unavailable." },
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
|
||||
static const struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
|
||||
.group_name_prefix = "dlcx",
|
||||
.group_description = "dlcx statistics",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
@@ -130,7 +130,7 @@ static const struct rate_ctr_desc e1_rate_ctr_desc[] = {
|
||||
[E1_I460_TRAU_MUX_EMPTY_CTR] = { "e1:i460", "Outbound I.460 MUX queue empty." }
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
|
||||
static const struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
|
||||
.group_name_prefix = "e1",
|
||||
.group_description = "e1 statistics",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
@@ -138,7 +138,7 @@ const static struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
|
||||
.ctr_desc = e1_rate_ctr_desc
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
|
||||
static const struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
|
||||
.group_name_prefix = "all_rtp_conn",
|
||||
.group_description = "aggregated statistics for all rtp connections",
|
||||
.class_id = 1,
|
||||
@@ -146,6 +146,14 @@ const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
|
||||
.ctr_desc = all_rtp_conn_rate_ctr_desc
|
||||
};
|
||||
|
||||
static const struct rate_ctr_group_desc all_osmux_conn_rate_ctr_group_desc = {
|
||||
.group_name_prefix = "all_osmux_conn",
|
||||
.group_description = "aggregated statistics for all osmux connections",
|
||||
.class_id = 1,
|
||||
.num_ctr = ARRAY_SIZE(all_osmux_conn_rate_ctr_desc),
|
||||
.ctr_desc = all_osmux_conn_rate_ctr_desc
|
||||
};
|
||||
|
||||
/*! allocate global rate counters
|
||||
* (called once at startup).
|
||||
* \param[in] cfg mgw configuration for which the rate counters are allocated.
|
||||
@@ -191,6 +199,7 @@ int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk)
|
||||
static atomic_uint mdcx_rate_ctr_index = 0;
|
||||
static atomic_uint dlcx_rate_ctr_index = 0;
|
||||
static atomic_uint all_rtp_conn_rate_ctr_index = 0;
|
||||
static atomic_uint all_osmux_conn_rate_ctr_index = 0;
|
||||
char ctr_name[256];
|
||||
|
||||
if (ratectr->mgcp_crcx_ctr_group == NULL) {
|
||||
@@ -229,6 +238,15 @@ int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk)
|
||||
trunk->trunk_nr);
|
||||
rate_ctr_group_set_name(ratectr->all_rtp_conn_stats, ctr_name);
|
||||
}
|
||||
if (ratectr->all_osmux_conn_stats == NULL) {
|
||||
ratectr->all_osmux_conn_stats = rate_ctr_group_alloc(trunk, &all_osmux_conn_rate_ctr_group_desc,
|
||||
all_osmux_conn_rate_ctr_index++);
|
||||
if (!ratectr->all_osmux_conn_stats)
|
||||
return -EINVAL;
|
||||
snprintf(ctr_name, sizeof(ctr_name), "%s-%u:osmux_conn", mgcp_trunk_type_strs_str(trunk->trunk_type),
|
||||
trunk->trunk_nr);
|
||||
rate_ctr_group_set_name(ratectr->all_osmux_conn_stats, ctr_name);
|
||||
}
|
||||
|
||||
/* E1 specific */
|
||||
if (trunk->trunk_type == MGCP_TRUNK_E1 && ratectr->e1_stats == NULL) {
|
||||
@@ -265,6 +283,10 @@ void mgcp_ratectr_trunk_free(struct mgcp_trunk *trunk)
|
||||
rate_ctr_group_free(ratectr->all_rtp_conn_stats);
|
||||
ratectr->all_rtp_conn_stats = NULL;
|
||||
}
|
||||
if (ratectr->all_osmux_conn_stats) {
|
||||
rate_ctr_group_free(ratectr->all_osmux_conn_stats);
|
||||
ratectr->all_osmux_conn_stats = NULL;
|
||||
}
|
||||
|
||||
/* E1 specific */
|
||||
if (ratectr->e1_stats) {
|
||||
|
@@ -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
|
||||
* SDP, called from: mgcp_parse_sdp_data() */
|
||||
static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
||||
int payload, const char *audio_name)
|
||||
int payload_type, const char *audio_name)
|
||||
{
|
||||
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
|
||||
* in our codec list. If we get an unexpected payload type,
|
||||
* we just drop it */
|
||||
if (codecs[i].payload_type != payload)
|
||||
if (codecs[i].payload_type != payload_type)
|
||||
continue;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
void *tmp_ctx = talloc_new(NULL);
|
||||
struct mgcp_rtp_end *rtp;
|
||||
|
||||
int payload;
|
||||
int payload_type;
|
||||
int ptime, ptime2 = 0;
|
||||
char audio_name[64];
|
||||
int port, rc;
|
||||
@@ -355,8 +355,8 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
/* skip these SDP attributes */
|
||||
break;
|
||||
case 'a':
|
||||
if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) {
|
||||
codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
|
||||
if (sscanf(line, "a=rtpmap:%d %63s", &payload_type, audio_name) == 2) {
|
||||
codecs_update(tmp_ctx, codecs, codecs_used, payload_type, audio_name);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -384,7 +384,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
case 'm':
|
||||
rc = sscanf(line, "m=audio %d RTP/AVP", &port);
|
||||
if (rc == 1) {
|
||||
rtp->rtp_port = htons(port);
|
||||
osmo_sockaddr_set_port(&rtp->addr.u.sa, port);
|
||||
rtp->rtcp_port = htons(port + 1);
|
||||
}
|
||||
|
||||
@@ -432,7 +432,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
|
||||
"Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
|
||||
ntohs(rtp->rtp_port), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&rtp->addr.u.sa), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf),
|
||||
rtp->packet_duration_ms);
|
||||
if (codecs_used == 0)
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, "none");
|
||||
@@ -601,7 +601,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
|
||||
payload_types[0] = payload_type;
|
||||
if (mgcp_conn_rtp_is_osmux(conn))
|
||||
local_port = endp->trunk->cfg->osmux_port;
|
||||
local_port = endp->trunk->cfg->osmux.local_port;
|
||||
else
|
||||
local_port = conn->end.local_port;
|
||||
rc = add_audio(sdp, payload_types, 1, local_port);
|
||||
|
@@ -39,7 +39,7 @@ __attribute__((no_sanitize("undefined")))
|
||||
void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss)
|
||||
{
|
||||
struct mgcp_rtp_state *state = &conn->state;
|
||||
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
|
||||
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
|
||||
|
||||
*expected = state->stats.cycles + state->stats.max_seq;
|
||||
*expected = *expected - state->stats.base_seq + 1;
|
||||
@@ -80,10 +80,10 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
|
||||
int ploss;
|
||||
int nchars;
|
||||
|
||||
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
|
||||
struct rate_ctr *octets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_RX_CTR);
|
||||
struct rate_ctr *packets_tx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_TX_CTR);
|
||||
struct rate_ctr *octets_tx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_TX_CTR);
|
||||
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
|
||||
struct rate_ctr *octets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_RX_CTR);
|
||||
struct rate_ctr *packets_tx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_TX_CTR);
|
||||
struct rate_ctr *octets_tx = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_TX_CTR);
|
||||
|
||||
calc_loss(conn, &expected, &ploss);
|
||||
jitter = calc_jitter(&conn->state);
|
||||
@@ -99,7 +99,7 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
|
||||
str += nchars;
|
||||
str_len -= nchars;
|
||||
|
||||
if (conn->conn->endp->trunk->cfg->osmux != OSMUX_USAGE_OFF) {
|
||||
if (conn->conn->endp->trunk->cfg->osmux.usage != OSMUX_USAGE_OFF) {
|
||||
/* Error Counter */
|
||||
nchars = snprintf(str, str_len,
|
||||
"\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64,
|
||||
@@ -112,9 +112,12 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
|
||||
str_len -= nchars;
|
||||
|
||||
if (conn->osmux.state == OSMUX_STATE_ENABLED) {
|
||||
struct rate_ctr *osmux_chunks_rx, *osmux_octets_rx;
|
||||
osmux_chunks_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_CHUNKS_RX_CTR);
|
||||
osmux_octets_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_OCTETS_RX_CTR);
|
||||
snprintf(str, str_len,
|
||||
"\r\nX-Osmux-ST: CR=%u, BR=%u",
|
||||
conn->osmux.stats.chunks, conn->osmux.stats.octets);
|
||||
"\r\nX-Osmux-ST: CR=%" PRIu64 ", BR=%" PRIu64,
|
||||
osmux_chunks_rx->current, osmux_octets_rx->current);
|
||||
}
|
||||
}
|
||||
|
||||
|
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;
|
||||
}
|
@@ -129,7 +129,7 @@ static int config_write_mgcp(struct vty *vty)
|
||||
vty_out(vty, " rtp force-ptime %d%s", g_cfg->force_ptime,
|
||||
VTY_NEWLINE);
|
||||
|
||||
switch (g_cfg->osmux) {
|
||||
switch (g_cfg->osmux.usage) {
|
||||
case OSMUX_USAGE_ON:
|
||||
vty_out(vty, " osmux on%s", VTY_NEWLINE);
|
||||
break;
|
||||
@@ -141,17 +141,23 @@ static int config_write_mgcp(struct vty *vty)
|
||||
vty_out(vty, " osmux off%s", VTY_NEWLINE);
|
||||
break;
|
||||
}
|
||||
if (g_cfg->osmux) {
|
||||
vty_out(vty, " osmux bind-ip %s%s",
|
||||
g_cfg->osmux_addr, VTY_NEWLINE);
|
||||
if (g_cfg->osmux.usage != OSMUX_USAGE_OFF) {
|
||||
if (g_cfg->osmux.local_addr_v4)
|
||||
vty_out(vty, " osmux bind-ip %s%s",
|
||||
g_cfg->osmux.local_addr_v4, VTY_NEWLINE);
|
||||
if (g_cfg->osmux.local_addr_v6)
|
||||
vty_out(vty, " osmux bind-ip-v6 %s%s",
|
||||
g_cfg->osmux.local_addr_v6, VTY_NEWLINE);
|
||||
vty_out(vty, " osmux batch-factor %d%s",
|
||||
g_cfg->osmux_batch, VTY_NEWLINE);
|
||||
g_cfg->osmux.batch_factor, VTY_NEWLINE);
|
||||
vty_out(vty, " osmux batch-size %u%s",
|
||||
g_cfg->osmux_batch_size, VTY_NEWLINE);
|
||||
g_cfg->osmux.batch_size, VTY_NEWLINE);
|
||||
vty_out(vty, " osmux port %u%s",
|
||||
g_cfg->osmux_port, VTY_NEWLINE);
|
||||
g_cfg->osmux.local_port, VTY_NEWLINE);
|
||||
vty_out(vty, " osmux dummy %s%s",
|
||||
g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);
|
||||
g_cfg->osmux.dummy_padding ? "on" : "off", VTY_NEWLINE);
|
||||
vty_out(vty, " osmux peer-behind-nat %s%s",
|
||||
g_cfg->osmux.peer_behind_nat ? "on" : "off", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
if (g_cfg->conn_timeout)
|
||||
@@ -169,11 +175,11 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
|
||||
struct rate_ctr *rx_packets, *rx_bytes;
|
||||
struct rate_ctr *dropped_packets;
|
||||
|
||||
tx_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_TX_CTR);
|
||||
tx_bytes = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_TX_CTR);
|
||||
rx_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
|
||||
rx_bytes = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_RX_CTR);
|
||||
dropped_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_DROPPED_PACKETS_CTR);
|
||||
tx_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_TX_CTR);
|
||||
tx_bytes = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_TX_CTR);
|
||||
rx_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
|
||||
rx_bytes = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_RX_CTR);
|
||||
dropped_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_DROPPED_PACKETS_CTR);
|
||||
|
||||
vty_out(vty,
|
||||
" Packets Sent: %" PRIu64 " (%" PRIu64 " bytes total)%s"
|
||||
@@ -189,7 +195,7 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
|
||||
rx_packets->current, rx_bytes->current, VTY_NEWLINE,
|
||||
state->in_stream.err_ts_ctr->current,
|
||||
state->out_stream.err_ts_ctr->current,
|
||||
VTY_NEWLINE,
|
||||
VTY_NEWLINE,
|
||||
dropped_packets->current, VTY_NEWLINE,
|
||||
codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
|
||||
codec->frame_duration_num, codec->frame_duration_den,
|
||||
@@ -197,6 +203,30 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
|
||||
VTY_NEWLINE, end->fmtp_extra, codec->audio_name,
|
||||
codec->subtype_name, VTY_NEWLINE, end->output_enabled,
|
||||
end->force_output_ptime, VTY_NEWLINE);
|
||||
if (mgcp_conn_rtp_is_osmux(conn)) {
|
||||
struct rate_ctr *rx_chunks, *rx_octets, *rtp_tx, *rtp_tx_dropped, *octets_tx;
|
||||
rx_chunks = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_CHUNKS_RX_CTR);
|
||||
rx_octets = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_OCTETS_RX_CTR);
|
||||
rtp_tx = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_RTP_PACKETS_TX_CTR);
|
||||
rtp_tx_dropped = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
|
||||
octets_tx = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_AMR_OCTETS_TX_CTR);
|
||||
vty_out(vty,
|
||||
" Osmux:%s"
|
||||
" State: %s%s"
|
||||
" Local CID: %d%s"
|
||||
" Remote CID: %d%s"
|
||||
" Chunks received: %" PRIu64 " (%" PRIu64 " bytes total)%s"
|
||||
" RTP Packets encoded (Tx): %" PRIu64 " (%" PRIu64 " AMR octets total)%s"
|
||||
" AMR payloads Dropped (Tx): %" PRIu64 "%s",
|
||||
VTY_NEWLINE, osmux_state_str(conn->osmux.state), VTY_NEWLINE,
|
||||
conn->osmux.local_cid_allocated ? conn->osmux.local_cid : -1, VTY_NEWLINE,
|
||||
conn->osmux.remote_cid_present ? conn->osmux.remote_cid : -1, VTY_NEWLINE,
|
||||
rx_chunks->current, rx_octets->current, VTY_NEWLINE,
|
||||
rtp_tx->current, octets_tx->current, VTY_NEWLINE,
|
||||
rtp_tx_dropped->current, VTY_NEWLINE
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp,
|
||||
@@ -289,6 +319,14 @@ static void dump_ratectr_trunk(struct vty *vty, struct mgcp_trunk *trunk)
|
||||
" %25n: %10c (%S/s %M/m %H/h %D/d) %d",
|
||||
ratectr->all_rtp_conn_stats);
|
||||
}
|
||||
if (ratectr->all_osmux_conn_stats) {
|
||||
vty_out(vty, " %s:%s",
|
||||
ratectr->all_osmux_conn_stats->desc->group_description,
|
||||
VTY_NEWLINE);
|
||||
vty_out_rate_ctr_group_fmt(vty,
|
||||
" %25n: %10c (%S/s %M/m %H/h %D/d) %d",
|
||||
ratectr->all_osmux_conn_stats);
|
||||
}
|
||||
|
||||
if (ratectr->e1_stats && trunk->trunk_type == MGCP_TRUNK_E1) {
|
||||
vty_out(vty, " %s:%s",
|
||||
@@ -342,7 +380,7 @@ static int mgcp_show(struct vty *vty, int argc, const char **argv,
|
||||
llist_for_each_entry(trunk, &g_cfg->trunks, entry)
|
||||
dump_trunk(vty, trunk, show_stats, active_only);
|
||||
|
||||
if (g_cfg->osmux)
|
||||
if (g_cfg->osmux.usage != OSMUX_USAGE_OFF)
|
||||
vty_out(vty, "Osmux used CID: %d%s", osmux_cid_pool_count_used(),
|
||||
VTY_NEWLINE);
|
||||
|
||||
@@ -1539,12 +1577,12 @@ DEFUN(cfg_mgcp_osmux,
|
||||
OSMO_ASSERT(trunk);
|
||||
|
||||
if (strcmp(argv[0], "off") == 0) {
|
||||
g_cfg->osmux = OSMUX_USAGE_OFF;
|
||||
g_cfg->osmux.usage = OSMUX_USAGE_OFF;
|
||||
return CMD_SUCCESS;
|
||||
} else if (strcmp(argv[0], "on") == 0)
|
||||
g_cfg->osmux = OSMUX_USAGE_ON;
|
||||
g_cfg->osmux.usage = OSMUX_USAGE_ON;
|
||||
else if (strcmp(argv[0], "only") == 0)
|
||||
g_cfg->osmux = OSMUX_USAGE_ONLY;
|
||||
g_cfg->osmux.usage = OSMUX_USAGE_ONLY;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
|
||||
@@ -1552,12 +1590,29 @@ DEFUN(cfg_mgcp_osmux,
|
||||
|
||||
DEFUN(cfg_mgcp_osmux_ip,
|
||||
cfg_mgcp_osmux_ip_cmd,
|
||||
"osmux bind-ip " VTY_IPV46_CMD,
|
||||
"osmux bind-ip " VTY_IPV4_CMD,
|
||||
OSMUX_STR IP_STR
|
||||
"IPv4 Address to bind to\n")
|
||||
{
|
||||
osmo_talloc_replace_string(g_cfg, &g_cfg->osmux.local_addr_v4, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_osmux_ip_v6,
|
||||
cfg_mgcp_osmux_ip_v6_cmd,
|
||||
"osmux bind-ip-v6 " VTY_IPV6_CMD,
|
||||
OSMUX_STR IP_STR
|
||||
"IPv4 Address to bind to\n"
|
||||
"IPv6 Address to bind to\n")
|
||||
{
|
||||
osmo_strlcpy(g_cfg->osmux_addr, argv[0], sizeof(g_cfg->osmux_addr));
|
||||
osmo_talloc_replace_string(g_cfg, &g_cfg->osmux.local_addr_v6, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_osmux_port,
|
||||
cfg_mgcp_osmux_port_cmd,
|
||||
"osmux port <1-65535>", OSMUX_STR "port\n" "UDP port\n")
|
||||
{
|
||||
g_cfg->osmux.local_port = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1566,7 +1621,7 @@ DEFUN(cfg_mgcp_osmux_batch_factor,
|
||||
"osmux batch-factor <1-8>",
|
||||
OSMUX_STR "Batching factor\n" "Number of messages in the batch\n")
|
||||
{
|
||||
g_cfg->osmux_batch = atoi(argv[0]);
|
||||
g_cfg->osmux.batch_factor = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1575,15 +1630,7 @@ DEFUN(cfg_mgcp_osmux_batch_size,
|
||||
"osmux batch-size <1-65535>",
|
||||
OSMUX_STR "batch size\n" "Batch size in bytes\n")
|
||||
{
|
||||
g_cfg->osmux_batch_size = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_osmux_port,
|
||||
cfg_mgcp_osmux_port_cmd,
|
||||
"osmux port <1-65535>", OSMUX_STR "port\n" "UDP port\n")
|
||||
{
|
||||
g_cfg->osmux_port = atoi(argv[0]);
|
||||
g_cfg->osmux.batch_size = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1594,9 +1641,24 @@ DEFUN(cfg_mgcp_osmux_dummy,
|
||||
"Disable dummy padding\n")
|
||||
{
|
||||
if (strcmp(argv[0], "on") == 0)
|
||||
g_cfg->osmux_dummy = 1;
|
||||
g_cfg->osmux.dummy_padding = true;
|
||||
else if (strcmp(argv[0], "off") == 0)
|
||||
g_cfg->osmux_dummy = 0;
|
||||
g_cfg->osmux.dummy_padding = false;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_osmux_peer_behind_nat,
|
||||
cfg_mgcp_osmux_peer_behind_nat_cmd,
|
||||
"osmux peer-behind-nat (on|off)",
|
||||
OSMUX_STR "Define whether peer is behind NAT\n"
|
||||
"Peer is behind NAT\n"
|
||||
"Peer is NOT behind NAT\n")
|
||||
{
|
||||
if (strcmp(argv[0], "on") == 0)
|
||||
g_cfg->osmux.peer_behind_nat = true;
|
||||
else if (strcmp(argv[0], "off") == 0)
|
||||
g_cfg->osmux.peer_behind_nat = false;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -1686,10 +1748,12 @@ int mgcp_vty_init(void)
|
||||
install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_name_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_osmux_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_v6_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_factor_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_size_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_osmux_peer_behind_nat_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_domain_cmd);
|
||||
@@ -1734,9 +1798,9 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
|
||||
int rc;
|
||||
struct mgcp_trunk *trunk;
|
||||
|
||||
cfg->osmux_port = OSMUX_PORT;
|
||||
cfg->osmux_batch = 4;
|
||||
cfg->osmux_batch_size = OSMUX_BATCH_DEFAULT_MAX;
|
||||
cfg->osmux.local_port = OSMUX_DEFAULT_PORT;
|
||||
cfg->osmux.batch_factor = 4;
|
||||
cfg->osmux.batch_size = OSMUX_BATCH_DEFAULT_MAX;
|
||||
|
||||
g_cfg = cfg;
|
||||
rc = vty_read_config_file(config_file, NULL);
|
||||
|
@@ -41,7 +41,6 @@
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_ctrl.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
@@ -51,7 +50,7 @@
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ctrl/control_vty.h>
|
||||
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
@@ -95,7 +94,7 @@ static char *config_file = "osmo-mgw.cfg";
|
||||
/* used by msgb and mgcp */
|
||||
void *tall_mgw_ctx = NULL;
|
||||
|
||||
static void print_help()
|
||||
static void print_help(void)
|
||||
{
|
||||
printf("Some useful options:\n");
|
||||
printf(" -h --help is printing this text.\n");
|
||||
@@ -295,13 +294,22 @@ static const struct log_info_cat log_categories[] = {
|
||||
.name = "DRTP",
|
||||
.description = "RTP stream handling",
|
||||
.color = "\033[1;30m",
|
||||
.enabled = 1,.loglevel = LOGL_NOTICE,
|
||||
.enabled = 1,
|
||||
.loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DE1] = {
|
||||
.name = "DE1",
|
||||
.description = "E1 line handling",
|
||||
.color = "\033[1;31m",
|
||||
.enabled = 1,.loglevel = LOGL_NOTICE,
|
||||
.enabled = 1,
|
||||
.loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DOSMUX] = {
|
||||
.name = "DOSMUX",
|
||||
.description = "Osmux (Osmocom RTP multiplexing)",
|
||||
.color = "\033[1;32m",
|
||||
.enabled = 1,
|
||||
.loglevel = LOGL_NOTICE,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -357,7 +365,7 @@ int main(int argc, char **argv)
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
cfg->ctrl = mgw_ctrl_interface_setup(cfg, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);
|
||||
cfg->ctrl = ctrl_interface_setup(cfg, OSMO_CTRL_PORT_MGW, NULL);
|
||||
if (!cfg->ctrl) {
|
||||
fprintf(stderr, "Failed to init the control interface on %s:%u. Exiting\n",
|
||||
ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);
|
||||
|
@@ -24,7 +24,7 @@ EXTRA_DIST = \
|
||||
mgcp_test.ok \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
check_PROGRAMS = \
|
||||
mgcp_test \
|
||||
$(NULL)
|
||||
|
||||
|
@@ -868,7 +868,7 @@ static void test_messages(void)
|
||||
printf("Connection mode not set\n");
|
||||
|
||||
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;
|
||||
|
||||
@@ -1071,7 +1071,7 @@ static void test_packet_loss_calc(void)
|
||||
"test-connection");
|
||||
conn = mgcp_conn_get_rtp(&endp, _conn->id);
|
||||
state = &conn->state;
|
||||
packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
|
||||
packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
|
||||
|
||||
state->stats.initialized = 1;
|
||||
state->stats.base_seq = pl_test_dat[i].base_seq;
|
||||
@@ -1492,7 +1492,7 @@ static void test_multilple_codec(void)
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 3);
|
||||
OSMO_ASSERT(conn->end.rtp_port == htons(16434));
|
||||
OSMO_ASSERT(osmo_sockaddr_port(&conn->end.addr.u.sa) == 16434);
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
inet_aton("8.8.8.8", &addr);
|
||||
OSMO_ASSERT(conn->end.addr.u.sa.sa_family == AF_INET);
|
||||
@@ -1632,7 +1632,8 @@ static void test_osmux_cid(void)
|
||||
|
||||
for (i = 0; i < 256; ++i) {
|
||||
id = osmux_cid_pool_get_next();
|
||||
OSMO_ASSERT(id == i);
|
||||
/* We called osmux_cid_pool_get_next() above, so next CID is i+1. */
|
||||
OSMO_ASSERT(id == ((i + 1) & 0xff));
|
||||
OSMO_ASSERT(osmux_cid_pool_count_used() == i + 1);
|
||||
}
|
||||
|
||||
@@ -1896,16 +1897,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
|
||||
.codecs = {
|
||||
{
|
||||
{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
|
||||
{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
|
||||
},
|
||||
{
|
||||
{ 122, "AMR/8000", &amr_param_octet_aligned_false, },
|
||||
{ 121, "AMR/8000", &amr_param_octet_aligned_true, },
|
||||
},
|
||||
},
|
||||
.expect = {
|
||||
{ .payload_type_map = {111, 121}, },
|
||||
{ .payload_type_map = {112, 122} },
|
||||
{ .payload_type_map = {111, 122}, },
|
||||
{ .end = true },
|
||||
},
|
||||
},
|
||||
@@ -1914,15 +1912,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
|
||||
.codecs = {
|
||||
{
|
||||
{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
|
||||
{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
|
||||
},
|
||||
{
|
||||
{ 122, "AMR/8000", &amr_param_octet_aligned_unset, },
|
||||
},
|
||||
},
|
||||
.expect = {
|
||||
{ .payload_type_map = {111, -EINVAL}, },
|
||||
{ .payload_type_map = {112, 122} },
|
||||
{ .payload_type_map = {111, 122}, },
|
||||
{ .end = true },
|
||||
},
|
||||
},
|
||||
@@ -1931,15 +1927,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
|
||||
.codecs = {
|
||||
{
|
||||
{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
|
||||
{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
|
||||
},
|
||||
{
|
||||
{ 122, "AMR/8000", NULL, },
|
||||
},
|
||||
},
|
||||
.expect = {
|
||||
{ .payload_type_map = {111, -EINVAL}, },
|
||||
{ .payload_type_map = {112, 122} },
|
||||
{ .payload_type_map = {111, 122}, },
|
||||
{ .end = true },
|
||||
},
|
||||
},
|
||||
@@ -2085,7 +2079,7 @@ static void test_mgcp_codec_pt_translate(void)
|
||||
OSMO_ASSERT(ok);
|
||||
}
|
||||
|
||||
void test_conn_id_matching()
|
||||
void test_conn_id_matching(void)
|
||||
{
|
||||
struct mgcp_endpoint endp = {};
|
||||
struct mgcp_conn *conn;
|
||||
@@ -2125,7 +2119,7 @@ void test_conn_id_matching()
|
||||
talloc_free(conn);
|
||||
}
|
||||
|
||||
void test_e1_trunk_nr_from_epname()
|
||||
void test_e1_trunk_nr_from_epname(void)
|
||||
{
|
||||
unsigned int trunk_nr;
|
||||
int rc;
|
||||
@@ -2175,7 +2169,7 @@ void test_e1_trunk_nr_from_epname()
|
||||
return;
|
||||
}
|
||||
|
||||
void test_mgcp_is_rtp_dummy_payload()
|
||||
void test_mgcp_is_rtp_dummy_payload(void)
|
||||
{
|
||||
/* realistic rtp packet */
|
||||
static const char rtp_payload[] =
|
||||
|
@@ -1347,32 +1347,24 @@ Testing mgcp_codec_pt_translate()
|
||||
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
|
||||
- mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
|
||||
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
|
||||
#4: conn1 has no codecs
|
||||
#4: conn1 has no codecs
|
||||
- add codecs on conn0:
|
||||
1: 0 PCMU/8000/1 -> rc=0
|
||||
2: 111 GSM-HR-08/8000/1 -> rc=0
|
||||
- add codecs on conn1:
|
||||
(none)
|
||||
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
|
||||
0: 112 AMR/8000/1 octet-aligned=1 -> rc=0
|
||||
1: 0 PCMU/8000/1 -> rc=0
|
||||
2: 111 GSM-HR-08/8000/1 -> rc=0
|
||||
- add codecs on conn1:
|
||||
(none)
|
||||
- add codecs on conn0:
|
||||
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
|
||||
- mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
|
||||
- add codecs on conn1:
|
||||
0: 122 AMR/8000 octet-aligned=0 -> rc=0
|
||||
1: 121 AMR/8000 octet-aligned=1 -> rc=0
|
||||
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
|
||||
#5: test AMR with differing octet-aligned settings
|
||||
- add codecs on conn0:
|
||||
0: 111 AMR/8000 octet-aligned=1 -> rc=0
|
||||
- add codecs on conn1:
|
||||
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
|
||||
0: 122 AMR/8000 octet-aligned=0 -> rc=0
|
||||
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 122
|
||||
0: 111 AMR/8000 octet-aligned=1 -> rc=0
|
||||
1: 112 AMR/8000 octet-aligned=0 -> rc=0
|
||||
- add codecs on conn1:
|
||||
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 111
|
||||
#6: test AMR with missing octet-aligned settings (defaults to 0)
|
||||
- add codecs on conn0:
|
||||
0: 111 AMR/8000 octet-aligned=1 -> rc=0
|
||||
- add codecs on conn1:
|
||||
|
@@ -22,7 +22,7 @@ EXTRA_DIST = \
|
||||
mgcp_client_test.err \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
check_PROGRAMS = \
|
||||
mgcp_client_test \
|
||||
$(NULL)
|
||||
|
||||
|
@@ -315,7 +315,7 @@ void test_mgcp_msg(void)
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
void test_mgcp_client_cancel()
|
||||
void test_mgcp_client_cancel(void)
|
||||
{
|
||||
mgcp_trans_id_t trans_id;
|
||||
struct msgb *msg;
|
||||
@@ -487,7 +487,7 @@ static struct sdp_section_start_test sdp_section_start_tests[] = {
|
||||
},
|
||||
};
|
||||
|
||||
void test_sdp_section_start()
|
||||
void test_sdp_section_start(void)
|
||||
{
|
||||
int i;
|
||||
int failures = 0;
|
||||
|
Reference in New Issue
Block a user