mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 08:12:01 +00:00
Compare commits
88 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
23c623b487 | ||
|
1a099fd34c | ||
|
58dc88bc31 | ||
|
aac84edc9d | ||
|
172f5acfce | ||
|
fdbefde869 | ||
|
25ecc91c3b | ||
|
995529548b | ||
|
0c04500ac5 | ||
|
a0b69f1896 | ||
|
fbf78d13f1 | ||
|
df9192efee | ||
|
70a658a2ba | ||
|
2764bdb1aa | ||
|
55fdfc223e | ||
|
8f33303660 | ||
|
a94c56e4c6 | ||
|
2ebcf5c34a | ||
|
f936e10f07 | ||
|
e303fa9ff3 | ||
|
958f5e74cc | ||
|
1e8d5fa44b | ||
|
374ad788d8 | ||
|
da07b90f0f | ||
|
fa495d669f | ||
|
439c86fc58 | ||
|
74e83d3d82 | ||
|
d55be05af1 | ||
|
8f91a7f450 | ||
|
da3a5759e1 | ||
|
29e671f257 | ||
|
d6a7e17911 | ||
|
3f4a4cb49c | ||
|
3d2b76fd95 | ||
|
276a414aa3 | ||
|
c534ad17dc | ||
|
3f2c15f275 | ||
|
38533ba9b3 | ||
|
39889e4389 | ||
|
c824fe4eb0 | ||
|
124a3e0b34 | ||
|
96c6e06681 | ||
|
ce18705875 | ||
|
41ab87f67c | ||
|
a8739fc7be | ||
|
21dfeff8aa | ||
|
f486e741a4 | ||
|
268568593e | ||
|
41d59205c0 | ||
|
036612b035 | ||
|
6bad138c96 | ||
|
8dc3597085 | ||
|
d70eef6421 | ||
|
33d97f721d | ||
|
d57310731f | ||
|
a8c684b51f | ||
|
03fcc91aad | ||
|
9de30e7cc4 | ||
|
d64c041cdb | ||
|
d02716d6c2 | ||
|
a065e632c0 | ||
|
bd060c3c99 | ||
|
97cae477fb | ||
|
02c880a7dd | ||
|
ba09687768 | ||
|
a4b677c45a | ||
|
6b7afe8f57 | ||
|
4131a65c4d | ||
|
6a5e5ac2d4 | ||
|
b066bd0c86 | ||
|
d6769ea207 | ||
|
ca280a1a84 | ||
|
a24dcc61d7 | ||
|
62d9df684f | ||
|
8358c4ba07 | ||
|
4c77e9b748 | ||
|
33347a4f5d | ||
|
7d86d4c523 | ||
|
eb984bd630 | ||
|
b3d14eb552 | ||
|
907744e2fc | ||
|
776846a0b9 | ||
|
59e7cf4437 | ||
|
b2064423fe | ||
|
97a9312be8 | ||
|
427cede4ba | ||
|
7371663aeb | ||
|
fe53edd776 |
563
.clang-format
Normal file
563
.clang-format
Normal file
@@ -0,0 +1,563 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# clang-format configuration file. Intended for clang-format >= 4.
|
||||
#
|
||||
# For more information, see:
|
||||
#
|
||||
# Documentation/process/clang-format.rst
|
||||
# https://clang.llvm.org/docs/ClangFormat.html
|
||||
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
#
|
||||
---
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
#AlignEscapedNewlines: Left # Unknown to clang-format-4.0
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
#AfterExternBlock: false # Unknown to clang-format-5.0
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
#SplitEmptyFunction: true # Unknown to clang-format-4.0
|
||||
#SplitEmptyRecord: true # Unknown to clang-format-4.0
|
||||
#SplitEmptyNamespace: true # Unknown to clang-format-4.0
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Custom
|
||||
#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: 120
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
#CompactNamespaces: false # Unknown to clang-format-4.0
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 8
|
||||
ContinuationIndentWidth: 8
|
||||
Cpp11BracedListStyle: false
|
||||
DerivePointerAlignment: false
|
||||
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: '.*'
|
||||
Priority: 1
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IndentCaseLabels: false
|
||||
#IndentPPDirectives: None # Unknown to clang-format-5.0
|
||||
IndentWidth: 8
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
|
||||
ObjCBlockIndentWidth: 8
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
|
||||
# Taken from git's rules
|
||||
#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0
|
||||
PenaltyBreakBeforeFirstCallParameter: 30
|
||||
PenaltyBreakComment: 10
|
||||
PenaltyBreakFirstLessLess: 0
|
||||
PenaltyBreakString: 10
|
||||
PenaltyExcessCharacter: 100
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
|
||||
PointerAlignment: Right
|
||||
ReflowComments: false
|
||||
SortIncludes: false
|
||||
#SortUsingDeclarations: false # Unknown to clang-format-4.0
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0
|
||||
#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0
|
||||
SpaceBeforeParens: ControlStatements
|
||||
#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Cpp03
|
||||
TabWidth: 8
|
||||
UseTab: Always
|
||||
...
|
5
.gitignore
vendored
5
.gitignore
vendored
@@ -64,3 +64,8 @@ doc/manuals/common
|
||||
doc/manuals/build
|
||||
|
||||
contrib/osmo-mgw.spec
|
||||
|
||||
#vs code
|
||||
.cache
|
||||
.vscode
|
||||
|
||||
|
@@ -24,4 +24,3 @@
|
||||
# If any interfaces have been removed or changed since the last public release, a=0.
|
||||
#
|
||||
#library what description / commit summary line
|
||||
update dependency to libosmocore > 1.5.1 for our use of osmo_sock_set_dscp()
|
||||
|
29
configure.ac
29
configure.ac
@@ -36,11 +36,6 @@ if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
|
||||
fi
|
||||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
dnl check for AX_CHECK_COMPILE_FLAG
|
||||
m4_ifdef([AX_CHECK_COMPILE_FLAG], [], [
|
||||
AC_MSG_ERROR([Please install autoconf-archive; re-run 'autoreconf -fi' for it to take effect.])
|
||||
])
|
||||
|
||||
dnl checks for libraries
|
||||
AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
|
||||
AC_SUBST(LIBRARY_DL)
|
||||
@@ -49,13 +44,16 @@ AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
|
||||
AC_SUBST(LIBRARY_DLSYM)
|
||||
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.5.0)
|
||||
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.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.2.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.2.0)
|
||||
|
||||
CFLAGS="$CFLAGS -pthread"
|
||||
LDFLAGS="$LDFLAGS -pthread"
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
@@ -87,15 +85,6 @@ then
|
||||
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
|
||||
fi
|
||||
|
||||
dnl Checks for typedefs, structures and compiler characteristics
|
||||
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=implicit], [CFLAGS="$CFLAGS -Werror=implicit"])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=maybe-uninitialized], [CFLAGS="$CFLAGS -Werror=maybe-uninitialized"])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=memset-transposed-args], [CFLAGS="$CFLAGS -Werror=memset-transposed-args"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wnull-dereference], [CFLAGS="$CFLAGS -Wnull-dereference"])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=sizeof-array-argument], [CFLAGS="$CFLAGS -Werror=sizeof-array-argument"])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=sizeof-pointer-memaccess], [CFLAGS="$CFLAGS -Werror=sizeof-pointer-memaccess"])
|
||||
|
||||
# Coverage build taken from WebKit's configure.in
|
||||
AC_MSG_CHECKING([whether to enable code coverage support])
|
||||
AC_ARG_ENABLE(coverage,
|
||||
|
@@ -49,7 +49,7 @@ set -x
|
||||
|
||||
cd "$base"
|
||||
autoreconf --install --force
|
||||
./configure --enable-vty-tests --enable-external-tests --enable-werror $CONFIG
|
||||
./configure --enable-sanitize --enable-vty-tests --enable-external-tests --enable-werror $CONFIG
|
||||
$MAKE $PARALLEL_MAKE
|
||||
LD_LIBRARY_PATH="$inst/lib" $MAKE check \
|
||||
|| cat-testlogs.sh
|
||||
|
@@ -30,29 +30,29 @@ BuildRequires: pkgconfig >= 0.20
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%endif
|
||||
BuildRequires: pkgconfig(libosmo-netif) >= 1.1.0
|
||||
BuildRequires: pkgconfig(libosmocore) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmogsm) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmocoding) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmoabis) >= 1.1.0
|
||||
BuildRequires: pkgconfig(libosmotrau) >= 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
|
||||
%{?systemd_requires}
|
||||
|
||||
%description
|
||||
OsmoMGW is Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks.
|
||||
|
||||
%package -n libosmo-mgcp-client8
|
||||
%package -n libosmo-mgcp-client9
|
||||
Summary: Osmocom's Media Gateway Control Protocol client library
|
||||
Group: System/Libraries
|
||||
|
||||
%description -n libosmo-mgcp-client8
|
||||
%description -n libosmo-mgcp-client9
|
||||
Osmocom's Media Gateway Control Protocol client library.
|
||||
|
||||
%package -n libosmo-mgcp-client-devel
|
||||
Summary: Development files for Osmocom's Media Gateway Control Protocol client library
|
||||
Group: Development/Libraries/C and C++
|
||||
Requires: libosmo-mgcp-client8 = %{version}
|
||||
Requires: libosmo-mgcp-client9 = %{version}
|
||||
|
||||
%description -n libosmo-mgcp-client-devel
|
||||
Osmocom's Media Gateway Control Protocol client librarary.
|
||||
@@ -90,8 +90,8 @@ find %{buildroot} -type f -name "*.la" -delete -print
|
||||
%check
|
||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
|
||||
|
||||
%post -n libosmo-mgcp-client8 -p /sbin/ldconfig
|
||||
%postun -n libosmo-mgcp-client8 -p /sbin/ldconfig
|
||||
%post -n libosmo-mgcp-client9 -p /sbin/ldconfig
|
||||
%postun -n libosmo-mgcp-client9 -p /sbin/ldconfig
|
||||
|
||||
%if 0%{?suse_version}
|
||||
%preun
|
||||
@@ -119,8 +119,8 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-mgw.cfg
|
||||
|
||||
%files -n libosmo-mgcp-client8
|
||||
%{_libdir}/libosmo-mgcp-client.so.8*
|
||||
%files -n libosmo-mgcp-client9
|
||||
%{_libdir}/libosmo-mgcp-client.so.9*
|
||||
|
||||
%files -n libosmo-mgcp-client-devel
|
||||
%{_libdir}/libosmo-mgcp-client.so
|
||||
|
121
debian/changelog
vendored
121
debian/changelog
vendored
@@ -1,3 +1,124 @@
|
||||
osmo-mgw (1.9.1) unstable; urgency=medium
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* libosmo-mgcp: e1: fix memleaks in e1_recv_cb()
|
||||
|
||||
-- Harald Welte <laforge@osmocom.org> Mon, 18 Apr 2022 10:58:50 +0200
|
||||
|
||||
osmo-mgw (1.9.0) unstable; urgency=medium
|
||||
|
||||
[ Harald Welte ]
|
||||
* TOS bits != DSCP
|
||||
* manual: don't define fig-bsc twice
|
||||
* switch from osmo_sock_set_dscp() to OSMO_SOCK_F_DSCP()
|
||||
* mgw: Add support for setting socket priority from VTY
|
||||
* manual: Include QoS chapter and add osmo-mgw specific example
|
||||
|
||||
[ Keith ]
|
||||
* Add vty command 'show mgcp active'
|
||||
* Log some useful messages at ERROR/INFO instead of DEBUG
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* send DLCX only once
|
||||
* tweak termination DLCX log msg
|
||||
* add osmo_mgcpc_ep_ci_get_remote_rtp_info()
|
||||
* check_rtp_destin(): clarify log msg
|
||||
* mgcp_client_fsm delete: set mgcp_client as ctx, not NULL
|
||||
* mgcp_client_endpoint_fsm: on term, still let conns wait for DLCX OK
|
||||
* mgcp_client_fsm: add missing log_subsys
|
||||
* mgcp_client: add logging on received MGCP messages
|
||||
|
||||
[ Philipp Maier ]
|
||||
* mgcp_network: fix implicit address loopback
|
||||
* mgcp_common, mgcp_udp_send: make parameter buf const
|
||||
* mgcp_network: refactor MGCP_DUMMY_LOAD
|
||||
* mgcp_client: drop nunnecessary else statement
|
||||
* mgcp_client: fix error handling in mgcp message generation
|
||||
* mgcp_protocol: fix loop that sends dummy RTP packets
|
||||
* mgw_main: fix loop that resets all endpoints
|
||||
* mgcp_trunk: drop ws line
|
||||
* mgcp_ratectr: drop ws line
|
||||
* mgcp_ratectr: fix sourcecode formatting
|
||||
* mgcp_trunk: add value string for trunk type.
|
||||
* mgcp_ratectr: refactor rate counter and set group name
|
||||
* mgcp_protocol: forward declare mgcp_endpoint
|
||||
* mgcp_endp: make wildcarded detection separate
|
||||
* mgcp_protocol: refactor request handler array
|
||||
* mgcp_trunk: use unsigned int instead of int as trunk_nr
|
||||
* mgcp_protocol: refactor MGCP request handling
|
||||
* mgcp_ratectr: add stats items to monitor trunk usage
|
||||
* mgcp_msg: add trunk parameter to mgcp_check_param for logging
|
||||
* mgcp_protocol: refactor function create_response_with_sdp
|
||||
* mgcp_protocol: add support for wildcarded DLCX
|
||||
* remove struct member wildcarded_req from struct mgcp_endpoint
|
||||
* mgcp_lient: remove unsubstantial FIXME note
|
||||
* mgcp_protocol: assert endp when it becomes mandatory
|
||||
* mgcp_trunk: check MGW domain name earlier
|
||||
* mgcp_ratectr: add stats items to monitor trunk usage
|
||||
* mgcp_client: fix typo Initalize -> Initialize
|
||||
* mgcp_protocol: get rid of policy_cb and change_cb
|
||||
* mgcp_ratectr: do not set talloc destructor on library allocated item
|
||||
* mgcp_client: allow to reset endpoints on startup
|
||||
* mgcp_client_vty: remove unnecessary checks
|
||||
* mgcp_client: do not print (null) when address is ANY
|
||||
* mgcp_client: refactor function init_socket
|
||||
* libosmo-mgcp-client: extend the mgcp_client for MGW pooling
|
||||
* mgcp_client_vty: add missing docstrings
|
||||
* mgcp_client_vty: add OSMO_ASSERT on pool parameter
|
||||
* mgcp_client_vty: fix docstrings for mgw-pool
|
||||
* mgcp_client_vty: cosmetic: doc string should terminated with \n
|
||||
* mgcp_client_vty: fixing docstring
|
||||
* mgcp_ratectr: remove unusued rate counters
|
||||
* mgcp_client: fix typo in doxygen comment
|
||||
* mgcp_client: add MGW name as logging context
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* Use new stat item/ctr getter APIs
|
||||
* constify arg in addr_is_any()
|
||||
* Define patch_ssrc as bool type
|
||||
* constify some function arg pointers
|
||||
* mgcp_network.c: Reorder some functions in file
|
||||
* mgcp_send_dummy: Check RTP destination is available before attempt tx
|
||||
* mgw: rx CRCX: Avoid sending dummy rtp if remote address not provided
|
||||
* Fail rx MDCX sendrecv with invalid remote addr
|
||||
* Take into account Marker bit when patching RTP stream
|
||||
* Use DLMGCP instead of DLGLOBAL in log lines
|
||||
* Fix attribute parsing on gcc 11.1.0
|
||||
|
||||
[ neels ]
|
||||
* Revert "mgcp_ratectr: add stats items to monitor trunk usage"
|
||||
|
||||
[ Daniel Willmann ]
|
||||
* contrib/jenkins: Use ASAN for osmo-mgw
|
||||
|
||||
[ Oliver Smith ]
|
||||
* mgcp_client_vty: add missing NO_STR
|
||||
* Revert "Turn some compiler warnings into errors"
|
||||
|
||||
[ Eric ]
|
||||
* add vscode stuff to gitignore
|
||||
* configure.ac: fix maybe-uninitialized for clang
|
||||
* fix missing includes and forward declarations
|
||||
* rename do_retransmission
|
||||
* mgcp_sdp: fix potential leak
|
||||
* stats: make sanitizers happy
|
||||
* libosmo-mgcp: cleanup audio codex alloc
|
||||
* libosmo-mgcp: atomic rate counter group indexes
|
||||
* libosmo-mgcp: do not use the default msgb talloc context
|
||||
* globally lock the portrange when trying to grab a port to prep for multithreading
|
||||
* embed strings into structs
|
||||
* adjust talloc context
|
||||
* endp: do not cache cfg pointer
|
||||
* add modified .clang-format
|
||||
* rename strip_epname and find_specific_endpoint and make them available
|
||||
* fix mgcp_conn_free_all
|
||||
* fix up some docstrings that lost their dot
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* libosmo-mgcp: use OSMO_STRLCPY_ARRAY in mgcp_codec_add()
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 16:59:21 +0100
|
||||
|
||||
osmo-mgw (1.8.1) unstable; urgency=medium
|
||||
|
||||
* attempt to fix RPM spec file after recent soversion bump
|
||||
|
10
debian/control
vendored
10
debian/control
vendored
@@ -6,10 +6,10 @@ Build-Depends: debhelper (>=9),
|
||||
dh-autoreconf,
|
||||
pkg-config,
|
||||
autotools-dev,
|
||||
libosmocore-dev (>= 1.5.0),
|
||||
libosmocore-dev (>= 1.6.0),
|
||||
libosmo-netif-dev (>= 1.1.0),
|
||||
libosmo-abis-dev (>= 1.1.0),
|
||||
osmo-gsm-manuals-dev (>= 1.1.0)
|
||||
libosmo-abis-dev (>= 1.2.0),
|
||||
osmo-gsm-manuals-dev (>= 1.2.0)
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Git: git://git.osmocom.org/osmo-mgw.git
|
||||
Vcs-Browser: https://git.osmocom.org/osmo-mgw/
|
||||
@@ -21,7 +21,7 @@ Multi-Arch: foreign
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
|
||||
|
||||
Package: libosmo-mgcp-client8
|
||||
Package: libosmo-mgcp-client9
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
@@ -33,7 +33,7 @@ Package: libosmo-mgcp-client-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-mgcp-client8 (= ${binary:Version}), ${misc:Depends}
|
||||
Depends: libosmo-mgcp-client9 (= ${binary:Version}), ${misc:Depends}
|
||||
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
|
||||
|
||||
Package: osmo-mgw-doc
|
||||
|
@@ -6,6 +6,7 @@ nobase_include_HEADERS = \
|
||||
osmocom/mgcp_client/mgcp_client.h \
|
||||
osmocom/mgcp_client/mgcp_client_endpoint_fsm.h \
|
||||
osmocom/mgcp_client/mgcp_client_fsm.h \
|
||||
osmocom/mgcp_client/mgcp_client_pool.h \
|
||||
osmocom/mgcp/mgcp.h \
|
||||
osmocom/mgcp/mgcp_common.h \
|
||||
osmocom/mgcp/osmux.h \
|
||||
|
@@ -34,6 +34,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "mgcp_ratectr.h"
|
||||
|
||||
@@ -62,8 +63,6 @@ struct mgcp_rtp_end;
|
||||
#define MGCP_POLICY_REJECT 5
|
||||
#define MGCP_POLICY_DEFER 6
|
||||
|
||||
typedef int (*mgcp_change)(struct mgcp_endpoint *endp, int state);
|
||||
typedef int (*mgcp_policy)(struct mgcp_endpoint *endp, int state, const char *transaction_id);
|
||||
typedef int (*mgcp_reset)(struct mgcp_trunk *cfg);
|
||||
typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
|
||||
|
||||
@@ -94,9 +93,10 @@ typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
|
||||
* This holds information on how to allocate ports
|
||||
*/
|
||||
struct mgcp_port_range {
|
||||
pthread_mutex_t lock;
|
||||
/* addr or NULL to fall-back to default */
|
||||
char *bind_addr_v4;
|
||||
char *bind_addr_v6;
|
||||
char bind_addr_v4[INET6_ADDRSTRLEN];
|
||||
char bind_addr_v6[INET6_ADDRSTRLEN];
|
||||
|
||||
/* dynamically allocated */
|
||||
int range_start;
|
||||
@@ -129,9 +129,9 @@ enum mgcp_role {
|
||||
|
||||
struct mgcp_config {
|
||||
int source_port;
|
||||
char *local_ip;
|
||||
char *source_addr;
|
||||
char *call_agent_addr;
|
||||
char local_ip[INET6_ADDRSTRLEN];
|
||||
char source_addr[INET6_ADDRSTRLEN];
|
||||
char call_agent_addr[INET6_ADDRSTRLEN];
|
||||
|
||||
/* RTP processing */
|
||||
mgcp_processing rtp_processing_cb;
|
||||
@@ -147,14 +147,10 @@ struct mgcp_config {
|
||||
|
||||
int force_ptime;
|
||||
|
||||
mgcp_change change_cb;
|
||||
mgcp_policy policy_cb;
|
||||
mgcp_reset reset_cb;
|
||||
mgcp_rqnt rqnt_cb;
|
||||
void *data;
|
||||
|
||||
uint32_t last_call_id;
|
||||
|
||||
/* list holding the trunks */
|
||||
struct llist_head trunks;
|
||||
|
||||
@@ -163,7 +159,7 @@ struct mgcp_config {
|
||||
/* osmux translator: 0 means disabled, 1 means enabled */
|
||||
int osmux;
|
||||
/* addr to bind the server to */
|
||||
char *osmux_addr;
|
||||
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.
|
||||
*/
|
||||
@@ -211,4 +207,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, char *buf, int len);
|
||||
int mgcp_udp_send(int fd, struct osmo_sockaddr *addr, int port, const char *buf, int len);
|
||||
|
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/i460_mux.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
|
||||
struct sockaddr;
|
||||
struct mgcp_conn;
|
||||
@@ -96,9 +97,6 @@ struct mgcp_endpoint {
|
||||
/*! List of struct mgcp_conn, of the connections active on this endpoint */
|
||||
struct llist_head conns;
|
||||
|
||||
/*! Backpointer to the MGW configuration */
|
||||
struct mgcp_config *cfg;
|
||||
|
||||
/*! Backpointer to the trunk this endpoint belongs to */
|
||||
struct mgcp_trunk *trunk;
|
||||
|
||||
@@ -111,10 +109,6 @@ struct mgcp_endpoint {
|
||||
/*! Last MGCP response (in case re-transmission is required) */
|
||||
char *last_response;
|
||||
|
||||
/*! Memorize if this endpoint was choosen by the MGW (wildcarded, true)
|
||||
* or if the user has choosen the particular endpoint explicitly. */
|
||||
bool wildcarded_req;
|
||||
|
||||
/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */
|
||||
uint32_t x_osmo_ign;
|
||||
|
||||
@@ -134,6 +128,7 @@ struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, unsigned int ind
|
||||
void mgcp_endp_release(struct mgcp_endpoint *endp);
|
||||
int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid);
|
||||
void mgcp_endp_update(struct mgcp_endpoint *endp);
|
||||
bool mgcp_endp_is_wildcarded(const char *epname);
|
||||
struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
|
||||
const struct mgcp_trunk *trunk);
|
||||
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
|
||||
@@ -141,3 +136,7 @@ struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
|
||||
bool mgcp_endp_avail(struct mgcp_endpoint *endp);
|
||||
void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
void mgcp_endp_strip_name(char *epname_stripped, const char *epname,
|
||||
const struct mgcp_trunk *trunk);
|
||||
struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname,
|
||||
const struct mgcp_trunk *trunk);
|
||||
|
@@ -25,10 +25,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct mgcp_conn;
|
||||
struct mgcp_parse_data;
|
||||
struct mgcp_endpoint;
|
||||
struct mgcp_trunk;
|
||||
|
||||
void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble);
|
||||
|
||||
@@ -39,7 +41,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data);
|
||||
|
||||
int mgcp_parse_osmux_cid(const char *line);
|
||||
|
||||
int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line);
|
||||
bool mgcp_check_param(const struct mgcp_endpoint *endp, struct mgcp_trunk *trunk, const char *line);
|
||||
|
||||
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid);
|
||||
|
||||
|
@@ -7,7 +7,16 @@
|
||||
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
|
||||
#define MGCP_DUMMY_LOAD 0x23
|
||||
/* The following constant defines an RTP dummy payload that is used for
|
||||
* "UDP Hole Punching" (NAT) */
|
||||
static const char rtp_dummy_payload[] = { 0x23 };
|
||||
|
||||
/* Check if the data in a given message buffer matches the rtp dummy payload
|
||||
* defined above */
|
||||
#define mgcp_is_rtp_dummy_payload(msg) \
|
||||
(msgb_length(msg) == sizeof(rtp_dummy_payload) && \
|
||||
memcmp(msgb_data(msg), rtp_dummy_payload, sizeof(rtp_dummy_payload)) == 0)
|
||||
|
||||
#define RTP_BUF_SIZE 4096
|
||||
|
||||
struct mgcp_rtp_stream_state {
|
||||
@@ -25,7 +34,7 @@ struct mgcp_rtp_state {
|
||||
|
||||
struct {
|
||||
/* are we patching the SSRC value? */
|
||||
int patch_ssrc;
|
||||
bool patch_ssrc;
|
||||
/* original SSRC (to which we shall patch any different SSRC) */
|
||||
uint32_t orig_ssrc;
|
||||
/* offset to apply on the sequence number */
|
||||
@@ -72,8 +81,8 @@ struct mgcp_rtp_codec {
|
||||
uint32_t frame_duration_den;
|
||||
|
||||
int payload_type;
|
||||
char *audio_name;
|
||||
char *subtype_name;
|
||||
char audio_name[64];
|
||||
char subtype_name[64];
|
||||
|
||||
bool param_present;
|
||||
struct mgcp_codec_param param;
|
||||
@@ -123,6 +132,8 @@ struct mgcp_rtp_end {
|
||||
char local_addr[INET6_ADDRSTRLEN];
|
||||
};
|
||||
|
||||
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end);
|
||||
|
||||
struct mgcp_rtp_tap {
|
||||
/* is this tap active (1) or not (0) */
|
||||
int enabled;
|
||||
@@ -143,7 +154,7 @@ void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *con
|
||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
|
||||
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
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);
|
||||
@@ -163,6 +174,6 @@ void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
|
||||
/* internal RTP Annex A counting */
|
||||
void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
|
||||
void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
|
||||
const uint16_t seq, const int32_t transit,
|
||||
const uint32_t ssrc);
|
||||
const uint32_t ssrc, const bool marker_bit);
|
||||
|
@@ -3,7 +3,7 @@
|
||||
/* Internal structure while parsing a request */
|
||||
struct mgcp_parse_data {
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_endpoint *endp;
|
||||
char *epname;
|
||||
char *trans;
|
||||
char *save;
|
||||
};
|
||||
@@ -16,12 +16,15 @@ struct mgcp_lco {
|
||||
int pkt_period_max; /* time in ms */
|
||||
};
|
||||
|
||||
char *mgcp_debug_get_last_endpoint_name(void);
|
||||
|
||||
char *get_lco_identifier(const char *options);
|
||||
int check_local_cx_options(void *ctx, const char *options);
|
||||
|
||||
struct mgcp_rtp_end;
|
||||
struct mgcp_endpoint;
|
||||
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
|
||||
struct mgcp_rtp_end *rtp);
|
||||
|
||||
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *rtp);
|
||||
uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
|
||||
const struct mgcp_rtp_end *rtp);
|
||||
|
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
/* Global MCGP general rate counters */
|
||||
enum {
|
||||
MGCP_GENERAL_RX_MSGS_TOTAL,
|
||||
@@ -23,7 +22,6 @@ enum {
|
||||
MGCP_CRCX_FAIL_ALLOC_CONN,
|
||||
MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC,
|
||||
MGCP_CRCX_FAIL_START_RTP,
|
||||
MGCP_CRCX_FAIL_REJECTED_BY_POLICY,
|
||||
MGCP_CRCX_FAIL_NO_OSMUX,
|
||||
MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS,
|
||||
MGCP_CRCX_FAIL_CODEC_NEGOTIATION,
|
||||
@@ -46,21 +44,16 @@ enum {
|
||||
MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS,
|
||||
MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC,
|
||||
MGCP_MDCX_FAIL_START_RTP,
|
||||
MGCP_MDCX_FAIL_REJECTED_BY_POLICY,
|
||||
MGCP_MDCX_DEFERRED_BY_POLICY,
|
||||
MGCP_MDCX_FAIL_AVAIL,
|
||||
};
|
||||
|
||||
/* Trunk-global MCGP DLCX related rate counters */
|
||||
enum {
|
||||
MGCP_DLCX_SUCCESS,
|
||||
MGCP_DLCX_FAIL_WILDCARD,
|
||||
MGCP_DLCX_FAIL_NO_CONN,
|
||||
MGCP_DLCX_FAIL_INVALID_CALLID,
|
||||
MGCP_DLCX_FAIL_INVALID_CONNID,
|
||||
MGCP_DLCX_FAIL_UNHANDLED_PARAM,
|
||||
MGCP_DLCX_FAIL_REJECTED_BY_POLICY,
|
||||
MGCP_DLCX_DEFERRED_BY_POLICY,
|
||||
MGCP_DLCX_FAIL_AVAIL,
|
||||
};
|
||||
|
||||
@@ -91,5 +84,24 @@ struct mgcp_ratectr_trunk {
|
||||
struct rate_ctr_group *e1_stats;
|
||||
};
|
||||
|
||||
int mgcp_ratectr_global_alloc(void *ctx, struct mgcp_ratectr_global *ratectr);
|
||||
int mgcp_ratectr_trunk_alloc(void *ctx, struct mgcp_ratectr_trunk *ratectr);
|
||||
struct mgcp_config;
|
||||
struct mgcp_trunk;
|
||||
|
||||
int mgcp_ratectr_global_alloc(struct mgcp_config *cfg);
|
||||
void mgcp_ratectr_global_free(struct mgcp_config *cfg);
|
||||
int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk);
|
||||
void mgcp_ratectr_trunk_free(struct mgcp_trunk *trunk);
|
||||
|
||||
/* Trunk-global common stat items */
|
||||
enum {
|
||||
TRUNK_STAT_ENDPOINTS_TOTAL,
|
||||
TRUNK_STAT_ENDPOINTS_USED,
|
||||
};
|
||||
|
||||
struct mgcp_stat_trunk {
|
||||
/* Stat item group which contains general status values of the trunk. */
|
||||
struct osmo_stat_item_group *common;
|
||||
};
|
||||
|
||||
int mgcp_stat_trunk_alloc(struct mgcp_trunk *trunk);
|
||||
void mgcp_stat_trunk_free(struct mgcp_trunk *trunk);
|
||||
|
@@ -2,24 +2,29 @@
|
||||
|
||||
#include <osmocom/gsm/i460_mux.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/mgcp/mgcp_ratectr.h>
|
||||
|
||||
|
||||
#define LOGPTRUNK(trunk, cat, level, fmt, args...) \
|
||||
LOGP(cat, level, "trunk:%u " fmt, \
|
||||
trunk ? trunk->trunk_nr : 0, \
|
||||
## args)
|
||||
|
||||
|
||||
enum mgcp_trunk_type {
|
||||
MGCP_TRUNK_VIRTUAL,
|
||||
MGCP_TRUNK_E1,
|
||||
};
|
||||
|
||||
extern const struct value_string mgcp_trunk_type_strs[];
|
||||
static inline const char *mgcp_trunk_type_strs_str(enum mgcp_trunk_type val)
|
||||
{ return get_value_string(mgcp_trunk_type_strs, val); }
|
||||
|
||||
struct mgcp_trunk {
|
||||
struct llist_head entry;
|
||||
|
||||
struct mgcp_config *cfg;
|
||||
|
||||
int trunk_nr;
|
||||
unsigned int trunk_nr;
|
||||
enum mgcp_trunk_type trunk_type;
|
||||
|
||||
char *audio_fmtp_extra;
|
||||
@@ -49,8 +54,9 @@ struct mgcp_trunk {
|
||||
unsigned int number_endpoints;
|
||||
struct mgcp_endpoint **endpoints;
|
||||
|
||||
/* global rate counters to measure the trunks overall performance and health */
|
||||
/* rate counters and stat items to measure the trunks overall performance and health */
|
||||
struct mgcp_ratectr_trunk ratectr;
|
||||
struct mgcp_stat_trunk stats;
|
||||
|
||||
union {
|
||||
/* Virtual trunk specific */
|
||||
@@ -69,11 +75,11 @@ struct mgcp_trunk {
|
||||
};
|
||||
};
|
||||
|
||||
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr);
|
||||
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, unsigned int nr);
|
||||
int mgcp_trunk_equip(struct mgcp_trunk *trunk);
|
||||
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr);
|
||||
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, unsigned int nr);
|
||||
struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname);
|
||||
int e1_trunk_nr_from_epname(const char *epname);
|
||||
int e1_trunk_nr_from_epname(unsigned int *trunk_nr, const char *epname);
|
||||
struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num);
|
||||
|
||||
/* The virtual trunk is always created on trunk id 0 for historical reasons,
|
||||
|
@@ -4,6 +4,7 @@ BUILT_SOURCES = \
|
||||
|
||||
noinst_HEADERS = \
|
||||
mgcp_client_internal.h \
|
||||
mgcp_client_pool_internal.h \
|
||||
$(NULL)
|
||||
|
||||
mgcp_common.h: $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h
|
||||
|
@@ -26,6 +26,15 @@ struct mgcp_client_conf {
|
||||
/* By default, we are always addressing the MGW with e.g. 'rtpbridge/123@mgw'.
|
||||
* If this is nonempty, the contained name will be used instead of 'mgw'. */
|
||||
char endpoint_domain_name[MGCP_ENDPOINT_MAXLEN];
|
||||
|
||||
/* The user may configure certain endpoint names that are reset via DLCX
|
||||
* on startup. Usually this will be one wildcarded endpoint e.g.
|
||||
* 'rtpbridge/(wildcard)' or a number of specific E1 like e.g.
|
||||
* 'ds/e1-0/s-3/su16-4' */
|
||||
struct llist_head reset_epnames;
|
||||
|
||||
/* human readable name / description */
|
||||
char *description;
|
||||
};
|
||||
|
||||
typedef unsigned int mgcp_trans_id_t;
|
||||
@@ -130,6 +139,8 @@ 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);
|
||||
void mgcp_client_disconnect(struct mgcp_client *mgcp);
|
||||
|
||||
const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp);
|
||||
uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp);
|
||||
@@ -165,3 +176,5 @@ unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
enum mgcp_codecs codec);
|
||||
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
unsigned int pt);
|
||||
|
||||
const char *mgcp_client_name(const struct mgcp_client *mgcp);
|
||||
|
@@ -23,6 +23,8 @@ const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgc
|
||||
bool osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(const struct osmo_mgcpc_ep_ci *ci, struct sockaddr_storage *dest);
|
||||
bool osmo_mgcpc_ep_ci_get_crcx_info_to_osmux_cid(const struct osmo_mgcpc_ep_ci *ci, uint8_t* cid);
|
||||
|
||||
const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_remote_rtp_info(const struct osmo_mgcpc_ep_ci *ci);
|
||||
|
||||
void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
|
||||
enum mgcp_verb verb, const struct mgcp_conn_peer *verb_info,
|
||||
struct osmo_fsm_inst *notify,
|
||||
@@ -45,6 +47,7 @@ void osmo_mgcpc_ep_clear(struct osmo_mgcpc_ep *ep);
|
||||
const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep);
|
||||
const char *osmo_mgcpc_ep_ci_name(const struct osmo_mgcpc_ep_ci *ci);
|
||||
const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci);
|
||||
struct mgcp_client *osmo_mgcpc_ep_client(const struct osmo_mgcpc_ep *ep);
|
||||
|
||||
extern const struct value_string osmo_mgcp_verb_names[];
|
||||
static inline const char *osmo_mgcp_verb_name(enum mgcp_verb val)
|
||||
|
@@ -69,5 +69,6 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
|
||||
void mgcp_conn_delete(struct osmo_fsm_inst *fi);
|
||||
|
||||
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi);
|
||||
struct mgcp_client *mgcp_conn_get_client(struct osmo_fsm_inst *fi);
|
||||
|
||||
const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info);
|
||||
|
@@ -4,12 +4,19 @@
|
||||
|
||||
#define MSGB_CB_MGCP_TRANS_ID 0
|
||||
|
||||
/* Struct that holds one endpoint name */
|
||||
struct reset_ep {
|
||||
struct llist_head list;
|
||||
char name[MGCP_ENDPOINT_MAXLEN];
|
||||
};
|
||||
|
||||
struct mgcp_client {
|
||||
struct mgcp_client_conf actual;
|
||||
struct osmo_wqueue wq;
|
||||
mgcp_trans_id_t next_trans_id;
|
||||
struct llist_head responses_pending;
|
||||
struct llist_head inuse_endpoints;
|
||||
struct mgcp_client_pool *pool;
|
||||
};
|
||||
|
||||
struct mgcp_inuse_endpoint {
|
||||
|
11
include/osmocom/mgcp_client/mgcp_client_pool.h
Normal file
11
include/osmocom/mgcp_client/mgcp_client_pool.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
struct mgcp_client;
|
||||
struct mgcp_client_pool;
|
||||
|
||||
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);
|
||||
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);
|
45
include/osmocom/mgcp_client/mgcp_client_pool_internal.h
Normal file
45
include/osmocom/mgcp_client/mgcp_client_pool_internal.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
/* Struct to handle a member of a pool of MGWs. */
|
||||
struct mgcp_client_pool_member {
|
||||
struct llist_head list;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* MGCP client configuration, this is not the running configuration, when mgcp_client_init() is executed, a
|
||||
* copy of this config is created. */
|
||||
struct mgcp_client_conf conf;
|
||||
|
||||
/* MGCP client descriptor, will be automatically allocated when mgcp_client_pool_connect() is called. (the MGCP
|
||||
* client is connected when this pointer is populated) */
|
||||
struct mgcp_client *client;
|
||||
|
||||
/* A pool member may be set as 'blocked' from the VTY, this means that the pool member may still work and serve
|
||||
* ongoing calls, but it won't be picked from the pool anymore. */
|
||||
bool blocked;
|
||||
|
||||
/* Reference counter to count how often this pool member is currently picked. */
|
||||
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;
|
||||
};
|
||||
|
||||
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member);
|
@@ -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=8:0:0
|
||||
MGCP_CLIENT_LIBVERSION=9:0:0
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-mgcp-client.la \
|
||||
@@ -32,6 +32,7 @@ libosmo_mgcp_client_la_SOURCES = \
|
||||
mgcp_client_vty.c \
|
||||
mgcp_client_fsm.c \
|
||||
mgcp_client_endpoint_fsm.c \
|
||||
mgcp_client_pool.c \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION)
|
||||
|
@@ -46,6 +46,9 @@
|
||||
#define OSMUX_CID_MAX 255 /* FIXME: use OSMUX_CID_MAX from libosmo-netif? */
|
||||
#endif
|
||||
|
||||
#define LOGPMGW(mgcp, level, fmt, args...) \
|
||||
LOGP(DLMGCP, level, "MGW(%s) " fmt, mgcp_client_name(mgcp), ## args)
|
||||
|
||||
/* Codec descripton for dynamic payload types (SDP) */
|
||||
const struct value_string osmo_mgcpc_codec_names[] = {
|
||||
{ CODEC_PCMU_8000_1, "PCMU/8000/1" },
|
||||
@@ -69,7 +72,6 @@ static char *extract_codec_name(const char *str)
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
||||
/* FIXME osmo_strlcpy */
|
||||
osmo_strlcpy(buf, str, sizeof(buf));
|
||||
|
||||
for (i = 0; i < strlen(buf); i++) {
|
||||
@@ -190,7 +192,7 @@ enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
return pt;
|
||||
}
|
||||
|
||||
/*! Initalize MGCP client configuration struct with default values.
|
||||
/*! Initialize MGCP client configuration struct with default values.
|
||||
* \param[out] conf Client configuration.*/
|
||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf)
|
||||
{
|
||||
@@ -201,6 +203,8 @@ void mgcp_client_conf_init(struct mgcp_client_conf *conf)
|
||||
.remote_addr = NULL,
|
||||
.remote_port = -1,
|
||||
};
|
||||
|
||||
INIT_LLIST_HEAD(&conf->reset_epnames);
|
||||
}
|
||||
|
||||
static void mgcp_client_handle_response(struct mgcp_client *mgcp,
|
||||
@@ -208,14 +212,13 @@ static void mgcp_client_handle_response(struct mgcp_client *mgcp,
|
||||
struct mgcp_response *response)
|
||||
{
|
||||
if (!pending) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot handle NULL response\n");
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Cannot handle NULL response\n");
|
||||
return;
|
||||
}
|
||||
if (pending->response_cb)
|
||||
pending->response_cb(response, pending->priv);
|
||||
else
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "MGCP response ignored (NULL cb)\n");
|
||||
LOGPMGW(mgcp, LOGL_DEBUG, "MGCP response ignored (NULL cb)\n");
|
||||
talloc_free(pending);
|
||||
}
|
||||
|
||||
@@ -672,23 +675,25 @@ int mgcp_client_rx(struct mgcp_client *mgcp, struct msgb *msg)
|
||||
|
||||
rc = mgcp_response_parse_head(r, msg);
|
||||
if (rc) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head)\n");
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Cannot parse MGCP response (head)\n");
|
||||
rc = 1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
LOGPMGW(mgcp, LOGL_DEBUG, "MGCP client: Rx %d %u %s\n",
|
||||
r->head.response_code, r->head.trans_id, r->head.comment);
|
||||
|
||||
rc = parse_head_params(r);
|
||||
if (rc) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head parameters)\n");
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Cannot parse MGCP response (head parameters)\n");
|
||||
rc = 1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
pending = mgcp_client_response_pending_get(mgcp, r->head.trans_id);
|
||||
if (!pending) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot find matching MGCP transaction for trans_id %d\n",
|
||||
r->head.trans_id);
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Cannot find matching MGCP transaction for trans_id %d\n",
|
||||
r->head.trans_id);
|
||||
rc = -ENOENT;
|
||||
goto error;
|
||||
}
|
||||
@@ -709,19 +714,19 @@ static int mgcp_do_read(struct osmo_fd *fd)
|
||||
|
||||
msg = msgb_alloc_headroom(4096, 128, "mgcp_from_gw");
|
||||
if (!msg) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate MGCP message.\n");
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Failed to allocate MGCP message.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = read(fd->fd, msg->data, 4096 - 128);
|
||||
if (ret <= 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to read: %s: %d='%s'\n", osmo_sock_get_name2(fd->fd),
|
||||
errno, strerror(errno));
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Failed to read: %s: %d='%s'\n",
|
||||
osmo_sock_get_name2(fd->fd), errno, strerror(errno));
|
||||
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
} else if (ret > 4096 - 128) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Too much data: %s: %d\n", osmo_sock_get_name2(fd->fd), ret);
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Too much data: %s: %d\n", osmo_sock_get_name2(fd->fd), ret);
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
@@ -735,15 +740,17 @@ static int mgcp_do_read(struct osmo_fd *fd)
|
||||
static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
|
||||
{
|
||||
int ret;
|
||||
struct mgcp_client *mgcp = fd->data;
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Tx MGCP: %s: len=%u '%s'...\n",
|
||||
osmo_sock_get_name2(fd->fd), msg->len, osmo_escape_str((const char*)msg->data, OSMO_MIN(42, msg->len)));
|
||||
LOGPMGW(mgcp, LOGL_DEBUG, "Tx MGCP: %s: len=%u '%s'...\n",
|
||||
osmo_sock_get_name2(fd->fd), msg->len,
|
||||
osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len)));
|
||||
|
||||
ret = write(fd->fd, msg->data, msg->len);
|
||||
if (ret != msg->len)
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n",
|
||||
osmo_sock_get_name2(fd->fd), errno, strerror(errno),
|
||||
msg->len, osmo_escape_str((const char*)msg->data, OSMO_MIN(42, msg->len)));
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n",
|
||||
osmo_sock_get_name2(fd->fd), errno, strerror(errno),
|
||||
msg->len, osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -751,6 +758,8 @@ struct mgcp_client *mgcp_client_init(void *ctx,
|
||||
struct mgcp_client_conf *conf)
|
||||
{
|
||||
struct mgcp_client *mgcp;
|
||||
struct reset_ep *reset_ep;
|
||||
struct reset_ep *actual_reset_ep;
|
||||
|
||||
mgcp = talloc_zero(ctx, struct mgcp_client);
|
||||
if (!mgcp)
|
||||
@@ -774,62 +783,115 @@ struct mgcp_client *mgcp_client_init(void *ctx,
|
||||
if (osmo_strlcpy(mgcp->actual.endpoint_domain_name, conf->endpoint_domain_name,
|
||||
sizeof(mgcp->actual.endpoint_domain_name))
|
||||
>= sizeof(mgcp->actual.endpoint_domain_name)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "MGCP client: endpoint domain name is too long, max length is %zu: '%s'\n",
|
||||
sizeof(mgcp->actual.endpoint_domain_name) - 1, conf->endpoint_domain_name);
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "MGCP client: endpoint domain name is too long, max length is %zu: '%s'\n",
|
||||
sizeof(mgcp->actual.endpoint_domain_name) - 1, conf->endpoint_domain_name);
|
||||
talloc_free(mgcp);
|
||||
return NULL;
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "MGCP client: using endpoint domain '@%s'\n", mgcp_client_endpoint_domain(mgcp));
|
||||
LOGPMGW(mgcp, LOGL_NOTICE, "MGCP client: using endpoint domain '@%s'\n", mgcp_client_endpoint_domain(mgcp));
|
||||
|
||||
INIT_LLIST_HEAD(&mgcp->actual.reset_epnames);
|
||||
llist_for_each_entry(reset_ep, &conf->reset_epnames, list) {
|
||||
actual_reset_ep = talloc_memdup(mgcp, reset_ep, sizeof(*reset_ep));
|
||||
llist_add_tail(&actual_reset_ep->list, &mgcp->actual.reset_epnames);
|
||||
}
|
||||
|
||||
if (conf->description)
|
||||
mgcp->actual.description = talloc_strdup(mgcp, conf->description);
|
||||
|
||||
return mgcp;
|
||||
}
|
||||
|
||||
static int init_socket(struct mgcp_client *mgcp)
|
||||
static int init_socket(struct mgcp_client *mgcp, unsigned int retry_n_ports)
|
||||
{
|
||||
int rc;
|
||||
struct osmo_wqueue *wq;
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
wq = &mgcp->wq;
|
||||
|
||||
for (i = 0; i < 100; i++) {
|
||||
for (i = 0; i < retry_n_ports + 1; i++) {
|
||||
|
||||
/* Initalize socket with the currently configured port
|
||||
* number */
|
||||
/* 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 choosen
|
||||
* that port conciously and we will not try to resolve
|
||||
* this by silently choosing a different port. */
|
||||
/* 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;
|
||||
|
||||
/* Choose a new port number to try next */
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"MGCPGW failed to bind to %s:%u, retrying with port %u\n",
|
||||
mgcp->actual.local_addr, mgcp->actual.local_port, mgcp->actual.local_port + 1);
|
||||
mgcp->actual.local_port++;
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_FATAL, "MGCPGW failed to find a port to bind on %i times.\n", i);
|
||||
LOGPMGW(mgcp, LOGL_FATAL, "Failed to find a port to bind on %u times -- check configuration!\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! Initalize client connection (opens socket only, no request is sent yet)
|
||||
/* 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) { }
|
||||
|
||||
/* Format DLCX message (fire and forget) and send it off to the MGW */
|
||||
static void _mgcp_client_send_dlcx(struct mgcp_client *mgcp, const char *epname)
|
||||
{
|
||||
struct msgb *msgb_dlcx;
|
||||
struct mgcp_msg mgcp_msg_dlcx = {
|
||||
.verb = MGCP_VERB_DLCX,
|
||||
.presence = MGCP_MSG_PRESENCE_ENDPOINT,
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg_dlcx.endpoint, epname, sizeof(mgcp_msg_dlcx.endpoint));
|
||||
msgb_dlcx = mgcp_msg_gen(mgcp, &mgcp_msg_dlcx);
|
||||
mgcp_client_tx(mgcp, msgb_dlcx, &_ignore_mgcp_response, NULL);
|
||||
}
|
||||
|
||||
static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgcp, const char *name)
|
||||
{
|
||||
static char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
int rc;
|
||||
|
||||
rc = snprintf(endpoint, sizeof(endpoint), "%s@%s", name, mgcp_client_endpoint_domain(mgcp));
|
||||
if (rc > sizeof(endpoint) - 1) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '%s@%s'\n",
|
||||
sizeof(endpoint) - 1, name, mgcp_client_endpoint_domain(mgcp));
|
||||
return NULL;
|
||||
}
|
||||
if (rc < 1) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Cannot compose MGCP endpoint name\n");
|
||||
return NULL;
|
||||
}
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
/*! 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_connect(struct mgcp_client *mgcp)
|
||||
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports)
|
||||
{
|
||||
struct osmo_wqueue *wq;
|
||||
int rc;
|
||||
struct reset_ep *reset_ep;
|
||||
const char *epname;
|
||||
|
||||
if (!mgcp) {
|
||||
LOGP(DLMGCP, LOGL_FATAL, "MGCPGW client not initialized properly\n");
|
||||
LOGPMGW(mgcp, LOGL_FATAL, "Client not initialized properly\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -840,18 +902,25 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
|
||||
|
||||
osmo_fd_setup(&wq->bfd, -1, OSMO_FD_READ, osmo_wqueue_bfd_cb, mgcp, 0);
|
||||
|
||||
rc = init_socket(mgcp);
|
||||
rc = init_socket(mgcp, retry_n_ports);
|
||||
if (rc < 0) {
|
||||
LOGP(DLMGCP, LOGL_FATAL,
|
||||
"Failed to initialize socket %s:%u -> %s:%u for MGCP GW: %s\n",
|
||||
mgcp->actual.local_addr, mgcp->actual.local_port,
|
||||
mgcp->actual.remote_addr, mgcp->actual.remote_port, strerror(errno));
|
||||
LOGPMGW(mgcp, LOGL_FATAL,
|
||||
"Failed to initialize socket %s:%u -> %s:%u for MGW: %s\n",
|
||||
mgcp->actual.local_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.local_port,
|
||||
mgcp->actual.remote_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.remote_port,
|
||||
strerror(errno));
|
||||
goto error_close_fd;
|
||||
}
|
||||
|
||||
LOGPMGW(mgcp, LOGL_INFO, "MGW connection: %s\n", osmo_sock_get_name2(wq->bfd.fd));
|
||||
|
||||
LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s\n", osmo_sock_get_name2(wq->bfd.fd));
|
||||
|
||||
/* If configured, send a DLCX message to the endpoints that are configured to
|
||||
* be reset on startup. Usually this is a wildcarded endpoint. */
|
||||
llist_for_each_entry(reset_ep, &mgcp->actual.reset_epnames, list) {
|
||||
epname = _mgcp_client_name_append_domain(mgcp, reset_ep->name);
|
||||
LOGPMGW(mgcp, LOGL_INFO, "Sending DLCX to: %s\n", epname);
|
||||
_mgcp_client_send_dlcx(mgcp, epname);
|
||||
}
|
||||
return 0;
|
||||
error_close_fd:
|
||||
close(wq->bfd.fd);
|
||||
@@ -859,6 +928,35 @@ error_close_fd:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! 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)
|
||||
{
|
||||
return mgcp_client_connect2(mgcp, 99);
|
||||
}
|
||||
|
||||
/*! Terminate client connection
|
||||
* \param[in,out] mgcp MGCP client descriptor.
|
||||
* \returns 0 on success, -EINVAL on error. */
|
||||
void mgcp_client_disconnect(struct mgcp_client *mgcp)
|
||||
{
|
||||
struct osmo_wqueue *wq;
|
||||
|
||||
if (!mgcp) {
|
||||
LOGP(DLMGCP, LOGL_FATAL, "MGCP client not initialized properly\n");
|
||||
return;
|
||||
}
|
||||
|
||||
wq = &mgcp->wq;
|
||||
osmo_wqueue_clear(wq);
|
||||
LOGPMGW(mgcp, LOGL_INFO, "MGCP association: %s -- closed!\n", osmo_sock_get_name2(wq->bfd.fd));
|
||||
close(wq->bfd.fd);
|
||||
wq->bfd.fd = -1;
|
||||
if (osmo_fd_is_registered(&wq->bfd))
|
||||
osmo_fd_unregister(&wq->bfd);
|
||||
}
|
||||
|
||||
/*! Get the IP-Aaddress of the associated MGW as string.
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \returns a pointer to the address string. */
|
||||
@@ -891,24 +989,6 @@ const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp)
|
||||
return mgcp->actual.endpoint_domain_name[0] ? mgcp->actual.endpoint_domain_name : "mgw";
|
||||
}
|
||||
|
||||
static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgcp, char *name)
|
||||
{
|
||||
static char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
int rc;
|
||||
|
||||
rc = snprintf(endpoint, sizeof(endpoint), "%s@%s", name, mgcp_client_endpoint_domain(mgcp));
|
||||
if (rc > sizeof(endpoint) - 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '%s@%s'\n",
|
||||
sizeof(endpoint) - 1, name, mgcp_client_endpoint_domain(mgcp));
|
||||
return NULL;
|
||||
}
|
||||
if (rc < 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot compose MGCP endpoint name\n");
|
||||
return NULL;
|
||||
}
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
/*! Compose endpoint name for a wildcarded request to the virtual trunk
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \returns string containing the endpoint name (e.g. rtpbridge\*@mgw) */
|
||||
@@ -940,7 +1020,7 @@ const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uin
|
||||
talloc_asprintf(ctx, "ds/e1-%u/s-%u/su%u-%u@%s", trunk_id, ts, rate, offset,
|
||||
mgcp_client_endpoint_domain(mgcp));
|
||||
if (!epname) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot compose MGCP e1-endpoint name!\n");
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Cannot compose MGCP e1-endpoint name!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -950,9 +1030,9 @@ const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uin
|
||||
rate_offs_valid = true;
|
||||
}
|
||||
if (!rate_offs_valid) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot compose MGCP e1-endpoint name (%s), rate(%u)/offset(%u) combination is invalid!\n", epname,
|
||||
rate, offset);
|
||||
LOGPMGW(mgcp, LOGL_ERROR,
|
||||
"Cannot compose MGCP e1-endpoint name (%s), rate(%u)/offset(%u) combination is invalid!\n",
|
||||
epname, rate, offset);
|
||||
talloc_free(epname);
|
||||
return NULL;
|
||||
}
|
||||
@@ -960,8 +1040,9 @@ const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uin
|
||||
/* An E1 line has a maximum of 32 timeslots, while the first (ts=0) is
|
||||
* reserverd for framing and alignment, so we can not use it here. */
|
||||
if (ts == 0 || ts > NUM_E1_TS-1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot compose MGCP e1-endpoint name (%s), E1-timeslot number (%u) is invalid!\n", epname, ts);
|
||||
LOGPMGW(mgcp, LOGL_ERROR,
|
||||
"Cannot compose MGCP e1-endpoint name (%s), E1-timeslot number (%u) is invalid!\n",
|
||||
epname, ts);
|
||||
talloc_free(epname);
|
||||
return NULL;
|
||||
}
|
||||
@@ -989,7 +1070,7 @@ struct mgcp_response_pending * mgcp_client_pending_add(
|
||||
return pending;
|
||||
}
|
||||
|
||||
/* Send the MGCP message in msg to the MGCP GW and handle a response with
|
||||
/* Send the MGCP message in msg to the MGW and handle a response with
|
||||
* response_cb. NOTE: the response_cb still needs to call
|
||||
* mgcp_response_parse_params(response) to get the parsed parameters -- to
|
||||
* potentially save some CPU cycles, only the head line has been parsed when
|
||||
@@ -1006,8 +1087,8 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
||||
|
||||
trans_id = msg->cb[MSGB_CB_MGCP_TRANS_ID];
|
||||
if (!trans_id) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Unset transaction id in mgcp send request\n");
|
||||
LOGPMGW(mgcp, LOGL_ERROR,
|
||||
"Unset transaction id in mgcp send request\n");
|
||||
talloc_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -1022,9 +1103,9 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
||||
}
|
||||
|
||||
if (msgb_l2len(msg) > 4096) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot send, MGCP message too large: %u\n",
|
||||
msgb_l2len(msg));
|
||||
LOGPMGW(mgcp, LOGL_ERROR,
|
||||
"Cannot send, MGCP message too large: %u\n",
|
||||
msgb_l2len(msg));
|
||||
msgb_free(msg);
|
||||
rc = -EINVAL;
|
||||
goto mgcp_tx_error;
|
||||
@@ -1032,12 +1113,12 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
||||
|
||||
rc = osmo_wqueue_enqueue(&mgcp->wq, msg);
|
||||
if (rc) {
|
||||
LOGP(DLMGCP, LOGL_FATAL, "Could not queue message to MGCP GW\n");
|
||||
LOGPMGW(mgcp, LOGL_FATAL, "Could not queue message to MGW\n");
|
||||
msgb_free(msg);
|
||||
goto mgcp_tx_error;
|
||||
} else
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Queued %u bytes for MGCP GW\n",
|
||||
msgb_l2len(msg));
|
||||
LOGPMGW(mgcp, LOGL_DEBUG, "Queued %u bytes for MGW\n",
|
||||
msgb_l2len(msg));
|
||||
return 0;
|
||||
|
||||
mgcp_tx_error:
|
||||
@@ -1064,10 +1145,10 @@ int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id)
|
||||
struct mgcp_response_pending *pending = mgcp_client_response_pending_get(mgcp, trans_id);
|
||||
if (!pending) {
|
||||
/*! Note: it is not harmful to cancel a transaction twice. */
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot cancel, no such transaction: %u\n", trans_id);
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Cannot cancel, no such transaction: %u\n", trans_id);
|
||||
return -ENOENT;
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Canceled transaction %u\n", trans_id);
|
||||
LOGPMGW(mgcp, LOGL_DEBUG, "Canceled transaction %u\n", trans_id);
|
||||
talloc_free(pending);
|
||||
return 0;
|
||||
/*! We don't really need to clean up the wqueue: In all sane cases, the msgb has already been sent
|
||||
@@ -1105,30 +1186,40 @@ static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
|
||||
const char *codec;
|
||||
unsigned int pt;
|
||||
|
||||
rc += msgb_printf(msg, "L:");
|
||||
rc |= msgb_printf(msg, "L:");
|
||||
|
||||
if (mgcp_msg->ptime)
|
||||
rc += msgb_printf(msg, " p:%u,", mgcp_msg->ptime);
|
||||
rc |= msgb_printf(msg, " p:%u,", mgcp_msg->ptime);
|
||||
|
||||
if (mgcp_msg->codecs_len) {
|
||||
rc += msgb_printf(msg, " a:");
|
||||
rc |= msgb_printf(msg, " a:");
|
||||
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||
pt = mgcp_msg->codecs[i];
|
||||
codec = get_value_string_or_null(osmo_mgcpc_codec_names, pt);
|
||||
|
||||
/* Note: Use codec descriptors from enum mgcp_codecs
|
||||
* in mgcp_client only! */
|
||||
OSMO_ASSERT(codec);
|
||||
rc += msgb_printf(msg, "%s", extract_codec_name(codec));
|
||||
if (!codec) {
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc |= msgb_printf(msg, "%s", extract_codec_name(codec));
|
||||
if (i < mgcp_msg->codecs_len - 1)
|
||||
rc += msgb_printf(msg, ";");
|
||||
rc |= msgb_printf(msg, ";");
|
||||
}
|
||||
rc += msgb_printf(msg, ",");
|
||||
rc |= msgb_printf(msg, ",");
|
||||
}
|
||||
|
||||
rc += msgb_printf(msg, " nt:IN\r\n");
|
||||
rc |= msgb_printf(msg, " nt:IN\r\n");
|
||||
|
||||
return rc;
|
||||
if (rc != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"message buffer to small, can not generate MGCP message (LCO)\n");
|
||||
msgb_free(msg);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper function for mgcp_msg_gen(): Add SDP information to MGCP message */
|
||||
@@ -1142,64 +1233,64 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
|
||||
unsigned int pt;
|
||||
|
||||
/* Add separator to mark the beginning of the SDP block */
|
||||
rc += msgb_printf(msg, "\r\n");
|
||||
rc |= msgb_printf(msg, "\r\n");
|
||||
|
||||
/* Add SDP protocol version */
|
||||
rc += msgb_printf(msg, "v=0\r\n");
|
||||
rc |= msgb_printf(msg, "v=0\r\n");
|
||||
|
||||
/* Determine local IP-Address */
|
||||
if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Could not determine local IP-Address!\n");
|
||||
LOGPMGW(mgcp, LOGL_ERROR,
|
||||
"Could not determine local IP-Address!\n");
|
||||
msgb_free(msg);
|
||||
return -2;
|
||||
return -EINVAL;
|
||||
}
|
||||
local_ip_family = osmo_ip_str_type(local_ip);
|
||||
if (local_ip_family == AF_UNSPEC) {
|
||||
msgb_free(msg);
|
||||
return -2;
|
||||
return -EINVAL;
|
||||
}
|
||||
audio_ip_family = osmo_ip_str_type(mgcp_msg->audio_ip);
|
||||
if (audio_ip_family == AF_UNSPEC) {
|
||||
msgb_free(msg);
|
||||
return -2;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Add owner/creator (SDP) */
|
||||
rc += msgb_printf(msg, "o=- %x 23 IN IP%c %s\r\n", mgcp_msg->call_id,
|
||||
rc |= msgb_printf(msg, "o=- %x 23 IN IP%c %s\r\n", mgcp_msg->call_id,
|
||||
local_ip_family == AF_INET6 ? '6' : '4',
|
||||
local_ip);
|
||||
|
||||
/* Add session name (none) */
|
||||
rc += msgb_printf(msg, "s=-\r\n");
|
||||
rc |= msgb_printf(msg, "s=-\r\n");
|
||||
|
||||
/* Add RTP address and port */
|
||||
if (mgcp_msg->audio_port == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Invalid port number, can not generate MGCP message\n");
|
||||
LOGPMGW(mgcp, LOGL_ERROR,
|
||||
"Invalid port number, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return -2;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (strlen(mgcp_msg->audio_ip) <= 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Empty ip address, can not generate MGCP message\n");
|
||||
LOGPMGW(mgcp, LOGL_ERROR,
|
||||
"Empty ip address, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return -2;
|
||||
return -EINVAL;
|
||||
}
|
||||
rc += msgb_printf(msg, "c=IN IP%c %s\r\n",
|
||||
rc |= msgb_printf(msg, "c=IN IP%c %s\r\n",
|
||||
audio_ip_family == AF_INET6 ? '6' : '4',
|
||||
mgcp_msg->audio_ip);
|
||||
|
||||
/* Add time description, active time (SDP) */
|
||||
rc += msgb_printf(msg, "t=0 0\r\n");
|
||||
rc |= msgb_printf(msg, "t=0 0\r\n");
|
||||
|
||||
rc += msgb_printf(msg, "m=audio %u RTP/AVP", mgcp_msg->audio_port);
|
||||
rc |= msgb_printf(msg, "m=audio %u RTP/AVP", mgcp_msg->audio_port);
|
||||
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
|
||||
rc += msgb_printf(msg, " %u", pt);
|
||||
rc |= msgb_printf(msg, " %u", pt);
|
||||
|
||||
}
|
||||
rc += msgb_printf(msg, "\r\n");
|
||||
rc |= msgb_printf(msg, "\r\n");
|
||||
|
||||
/* Add optional codec parameters (fmtp) */
|
||||
if (mgcp_msg->param_present) {
|
||||
@@ -1209,9 +1300,9 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
|
||||
continue;
|
||||
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
|
||||
if (mgcp_msg->param.amr_octet_aligned_present && mgcp_msg->param.amr_octet_aligned)
|
||||
rc += msgb_printf(msg, "a=fmtp:%u octet-align=1\r\n", pt);
|
||||
rc |= msgb_printf(msg, "a=fmtp:%u octet-align=1\r\n", pt);
|
||||
else if (mgcp_msg->param.amr_octet_aligned_present && !mgcp_msg->param.amr_octet_aligned)
|
||||
rc += msgb_printf(msg, "a=fmtp:%u octet-align=0\r\n", pt);
|
||||
rc |= msgb_printf(msg, "a=fmtp:%u octet-align=0\r\n", pt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1226,16 +1317,25 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
|
||||
|
||||
/* Note: Use codec descriptors from enum mgcp_codecs
|
||||
* in mgcp_client only! */
|
||||
OSMO_ASSERT(codec);
|
||||
if (!codec) {
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc += msgb_printf(msg, "a=rtpmap:%u %s\r\n", pt, codec);
|
||||
rc |= msgb_printf(msg, "a=rtpmap:%u %s\r\n", pt, codec);
|
||||
}
|
||||
}
|
||||
|
||||
if (mgcp_msg->ptime)
|
||||
rc += msgb_printf(msg, "a=ptime:%u\r\n", mgcp_msg->ptime);
|
||||
rc |= msgb_printf(msg, "a=ptime:%u\r\n", mgcp_msg->ptime);
|
||||
|
||||
return rc;
|
||||
if (rc != 0) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Message buffer to small, can not generate MGCP message (SDP)\n");
|
||||
msgb_free(msg);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Generate an MGCP message
|
||||
@@ -1248,7 +1348,6 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
uint32_t mandatory_mask;
|
||||
struct msgb *msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
|
||||
int rc = 0;
|
||||
int rc_sdp;
|
||||
bool use_sdp = false;
|
||||
char buf[32];
|
||||
|
||||
@@ -1259,35 +1358,34 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
switch (mgcp_msg->verb) {
|
||||
case MGCP_VERB_CRCX:
|
||||
mandatory_mask = MGCP_CRCX_MANDATORY;
|
||||
rc += msgb_printf(msg, "CRCX %u", trans_id);
|
||||
rc |= msgb_printf(msg, "CRCX %u", trans_id);
|
||||
break;
|
||||
case MGCP_VERB_MDCX:
|
||||
mandatory_mask = MGCP_MDCX_MANDATORY;
|
||||
rc += msgb_printf(msg, "MDCX %u", trans_id);
|
||||
rc |= msgb_printf(msg, "MDCX %u", trans_id);
|
||||
break;
|
||||
case MGCP_VERB_DLCX:
|
||||
mandatory_mask = MGCP_DLCX_MANDATORY;
|
||||
rc += msgb_printf(msg, "DLCX %u", trans_id);
|
||||
rc |= msgb_printf(msg, "DLCX %u", trans_id);
|
||||
break;
|
||||
case MGCP_VERB_AUEP:
|
||||
mandatory_mask = MGCP_AUEP_MANDATORY;
|
||||
rc += msgb_printf(msg, "AUEP %u", trans_id);
|
||||
rc |= msgb_printf(msg, "AUEP %u", trans_id);
|
||||
break;
|
||||
case MGCP_VERB_RSIP:
|
||||
mandatory_mask = MGCP_RSIP_MANDATORY;
|
||||
rc += msgb_printf(msg, "RSIP %u", trans_id);
|
||||
rc |= msgb_printf(msg, "RSIP %u", trans_id);
|
||||
break;
|
||||
default:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Invalid command verb, can not generate MGCP message\n");
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Invalid command verb, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if mandatory fields are missing */
|
||||
if (!((mgcp_msg->presence & mandatory_mask) == mandatory_mask)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"One or more missing mandatory fields, can not generate MGCP message\n");
|
||||
LOGPMGW(mgcp, LOGL_ERROR,
|
||||
"One or more missing mandatory fields, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
@@ -1295,39 +1393,37 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
/* Add endpoint name */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_ENDPOINT) {
|
||||
if (strlen(mgcp_msg->endpoint) <= 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Empty endpoint name, can not generate MGCP message\n");
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Empty endpoint name, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strstr(mgcp_msg->endpoint, "@") == NULL) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Endpoint name (%s) lacks separator (@), can not generate MGCP message\n",
|
||||
mgcp_msg->endpoint);
|
||||
LOGPMGW(mgcp, LOGL_ERROR,
|
||||
"Endpoint name (%s) lacks separator (@), can not generate MGCP message\n",
|
||||
mgcp_msg->endpoint);
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc += msgb_printf(msg, " %s", mgcp_msg->endpoint);
|
||||
rc |= msgb_printf(msg, " %s", mgcp_msg->endpoint);
|
||||
}
|
||||
|
||||
/* Add protocol version */
|
||||
rc += msgb_printf(msg, " MGCP 1.0\r\n");
|
||||
rc |= msgb_printf(msg, " MGCP 1.0\r\n");
|
||||
|
||||
/* Add call id */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CALL_ID)
|
||||
rc += msgb_printf(msg, "C: %x\r\n", mgcp_msg->call_id);
|
||||
rc |= msgb_printf(msg, "C: %x\r\n", mgcp_msg->call_id);
|
||||
|
||||
/* Add connection id */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_ID) {
|
||||
if (strlen(mgcp_msg->conn_id) <= 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Empty connection id, can not generate MGCP message\n");
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Empty connection id, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
rc += msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id);
|
||||
rc |= msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id);
|
||||
}
|
||||
|
||||
/* Using SDP makes sense when a valid IP/Port combination is specifiec,
|
||||
@@ -1339,33 +1435,34 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
/* Add local connection options (LCO) */
|
||||
if (!use_sdp
|
||||
&& (mgcp_msg->verb == MGCP_VERB_CRCX
|
||||
|| mgcp_msg->verb == MGCP_VERB_MDCX))
|
||||
rc += add_lco(msg, mgcp_msg);
|
||||
|| mgcp_msg->verb == MGCP_VERB_MDCX)) {
|
||||
if (add_lco(msg, mgcp_msg) < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Add mode */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_MODE)
|
||||
rc +=
|
||||
rc |=
|
||||
msgb_printf(msg, "M: %s\r\n",
|
||||
mgcp_client_cmode_name(mgcp_msg->conn_mode));
|
||||
|
||||
/* Add X-Osmo-IGN */
|
||||
if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_X_OSMO_IGN)
|
||||
&& (mgcp_msg->x_osmo_ign != 0))
|
||||
rc +=
|
||||
rc |=
|
||||
msgb_printf(msg, MGCP_X_OSMO_IGN_HEADER "%s\r\n",
|
||||
mgcp_msg->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID ? " C": "");
|
||||
|
||||
/* Add X-Osmo-Osmux */
|
||||
if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID)) {
|
||||
if (mgcp_msg->x_osmo_osmux_cid < -1 || mgcp_msg->x_osmo_osmux_cid > OSMUX_CID_MAX) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Wrong Osmux CID %d, can not generate MGCP message\n",
|
||||
mgcp_msg->x_osmo_osmux_cid);
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Wrong Osmux CID %d, can not generate MGCP message\n",
|
||||
mgcp_msg->x_osmo_osmux_cid);
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), " %d", mgcp_msg->x_osmo_osmux_cid);
|
||||
rc +=
|
||||
rc |=
|
||||
msgb_printf(msg, MGCP_X_OSMO_OSMUX_HEADER "%s\r\n",
|
||||
mgcp_msg->x_osmo_osmux_cid == -1 ? " *": buf);
|
||||
}
|
||||
@@ -1375,16 +1472,12 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
if (use_sdp
|
||||
&& (mgcp_msg->verb == MGCP_VERB_CRCX
|
||||
|| mgcp_msg->verb == MGCP_VERB_MDCX)) {
|
||||
rc_sdp = add_sdp(msg, mgcp_msg, mgcp);
|
||||
if (rc_sdp == -2)
|
||||
if (add_sdp(msg, mgcp_msg, mgcp) < 0)
|
||||
return NULL;
|
||||
else
|
||||
rc += rc_sdp;
|
||||
}
|
||||
|
||||
if (rc != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"message buffer to small, can not generate MGCP message\n");
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Message buffer to small, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
msg = NULL;
|
||||
}
|
||||
@@ -1400,7 +1493,7 @@ mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg)
|
||||
return (mgcp_trans_id_t)msg->cb[MSGB_CB_MGCP_TRANS_ID];
|
||||
}
|
||||
|
||||
/*! Get the configuration parameters a given MGCP client instance
|
||||
/*! Get the configuration parameters for a given MGCP client instance
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \returns configuration */
|
||||
struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp)
|
||||
@@ -1416,3 +1509,22 @@ const struct value_string mgcp_client_connection_mode_strs[] = {
|
||||
{ MGCP_CONN_LOOPBACK, "loopback" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/*! Get MGCP client instance name (VTY).
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \returns MGCP client name.
|
||||
*
|
||||
* The user can only modify the name of an MGCP client instance when it is
|
||||
* part of a pool. For single MGCP client instances and MGCP client instance
|
||||
* where no description is set via the VTY, the MGW domain name will be used
|
||||
* as name. */
|
||||
const char *mgcp_client_name(const struct mgcp_client *mgcp)
|
||||
{
|
||||
if (!mgcp)
|
||||
return "(null)";
|
||||
|
||||
if (mgcp->actual.description)
|
||||
return mgcp->actual.description;
|
||||
else
|
||||
return mgcp_client_endpoint_domain(mgcp);
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@
|
||||
|
||||
#define LOG_CI(ci, level, fmt, args...) do { \
|
||||
if (!ci || !ci->ep) \
|
||||
LOGP(DLGLOBAL, level, "(unknown MGW endpoint) " fmt, ## args); \
|
||||
LOGP(DLMGCP, level, "(unknown MGW endpoint) " fmt, ## args); \
|
||||
else \
|
||||
LOG_MGCPC_EP(ci->ep, level, "CI[%d] %s%s%s: " fmt, \
|
||||
(int)(ci - ci->ep->ci), \
|
||||
@@ -216,6 +216,13 @@ const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci)
|
||||
return ci->mgcp_ci_str;
|
||||
}
|
||||
|
||||
struct mgcp_client *osmo_mgcpc_ep_client(const struct osmo_mgcpc_ep *ep)
|
||||
{
|
||||
if (!ep)
|
||||
return NULL;
|
||||
return ep->mgcp_client;
|
||||
}
|
||||
|
||||
static struct value_string osmo_mgcpc_ep_fsm_event_names[33] = {};
|
||||
|
||||
static char osmo_mgcpc_ep_fsm_event_name_bufs[32][32] = {};
|
||||
@@ -508,7 +515,8 @@ static void on_success(struct osmo_mgcpc_ep_ci *ci, void *data)
|
||||
osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi);
|
||||
}
|
||||
|
||||
/*! Return the MGW's RTP port information for this connection, as returned by the last CRCX/MDCX OK message. */
|
||||
/*! Return the MGW's local RTP port information for this connection, i.e. the local port that MGW is receiving on, as
|
||||
* returned by the last CRCX-OK / MDCX-OK message. */
|
||||
const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgcpc_ep_ci *ci)
|
||||
{
|
||||
ci = osmo_mgcpc_ep_check_ci((struct osmo_mgcpc_ep_ci*)ci);
|
||||
@@ -519,6 +527,16 @@ const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgc
|
||||
return &ci->rtp_info;
|
||||
}
|
||||
|
||||
/*! Return the MGW's remote RTP port information for this connection, i.e. the remote RTP port that the MGW is sending
|
||||
* to, as sent to the MGW by the last CRCX / MDCX message. */
|
||||
const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_remote_rtp_info(const struct osmo_mgcpc_ep_ci *ci)
|
||||
{
|
||||
ci = osmo_mgcpc_ep_check_ci((struct osmo_mgcpc_ep_ci*)ci);
|
||||
if (!ci)
|
||||
return NULL;
|
||||
return &ci->verb_info;
|
||||
}
|
||||
|
||||
/*! Return the MGW's RTP port information for this connection, as returned by the last CRCX/MDCX OK message. */
|
||||
bool osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(const struct osmo_mgcpc_ep_ci *ci, struct sockaddr_storage *dest)
|
||||
{
|
||||
@@ -605,7 +623,7 @@ void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
|
||||
ci = osmo_mgcpc_ep_check_ci(ci);
|
||||
|
||||
if (!ci) {
|
||||
LOGP(DLGLOBAL, LOGL_ERROR, "Invalid MGW endpoint request: no ci\n");
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Invalid MGW endpoint request: no ci\n");
|
||||
goto dispatch_error;
|
||||
}
|
||||
if (!verb_info && verb != MGCP_VERB_DLCX) {
|
||||
@@ -979,6 +997,34 @@ 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)
|
||||
{
|
||||
int i;
|
||||
struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
|
||||
|
||||
/* We want the mgcp_client_fsm to still stick around until it received the DLCX "OK" responses from the MGW. So
|
||||
* it should not dealloc along with this ep_fsm instance. Instead, signal DLCX for each conn on the endpoint,
|
||||
* and detach the mgcp_client_fsm from being a child-fsm.
|
||||
*
|
||||
* After mgcp_conn_delete(), an mgcp_client_fsm instance goes into ST_DLCX_RESP, which waits up to 4 seconds for
|
||||
* a DLCX OK. If none is received in that time, the instance terminates. So cleanup of the instance is
|
||||
* guaranteed. */
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
|
||||
struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
|
||||
|
||||
if (!ci->occupied || !ci->mgcp_client_fi)
|
||||
continue;
|
||||
|
||||
/* mgcp_conn_delete() unlinks itself from this parent FSM implicitly and waits for the DLCX OK. */
|
||||
mgcp_conn_delete(ci->mgcp_client_fi);
|
||||
/* Forget all about this ci */
|
||||
*ci = (struct osmo_mgcpc_ep_ci){
|
||||
.ep = ep,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static struct osmo_fsm osmo_mgcpc_ep_fsm = {
|
||||
.name = "mgw-endp",
|
||||
.states = osmo_mgcpc_ep_fsm_states,
|
||||
@@ -986,5 +1032,5 @@ static struct osmo_fsm osmo_mgcpc_ep_fsm = {
|
||||
.log_subsys = DLMGCP,
|
||||
.event_names = osmo_mgcpc_ep_fsm_event_names,
|
||||
.timer_cb = osmo_mgcpc_ep_fsm_timer_cb,
|
||||
/* The FSM termination will automatically trigger any mgcp_client_fsm instances to DLCX. */
|
||||
.pre_term = osmo_mgcpc_ep_fsm_pre_term,
|
||||
};
|
||||
|
@@ -252,6 +252,18 @@ const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi)
|
||||
return mgcp_ctx->conn_id;
|
||||
}
|
||||
|
||||
/* Get the mgcp_client that is used with this mgcp_client_fsm instance */
|
||||
struct mgcp_client *mgcp_conn_get_client(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx;
|
||||
|
||||
if (!fi)
|
||||
return NULL;
|
||||
|
||||
mgcp_ctx = fi->priv;
|
||||
return mgcp_ctx->mgcp;
|
||||
}
|
||||
|
||||
static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = priv;
|
||||
@@ -521,10 +533,12 @@ static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca
|
||||
/* Should the FSM be terminated while there are still open connections
|
||||
* on the MGW, we send an unconditional DLCX to terminate the
|
||||
* connection. This is not the normal case. The user should always use
|
||||
* mgcp_conn_delete() to instruct the FSM to perform a graceful exit */
|
||||
if (strlen(mgcp_ctx->conn_id)) {
|
||||
LOGPFSML(fi, LOGL_NOTICE,
|
||||
"MGW/DLCX: FSM termination with connections still present, sending unconditional DLCX...\n");
|
||||
* mgcp_conn_delete() to instruct the FSM to perform a graceful exit.
|
||||
* If in ST_DLCX_RESP, a DLCX was already sent and we did not get a
|
||||
* response. No point in sending another one. */
|
||||
if (fi->state != ST_DLCX_RESP && strlen(mgcp_ctx->conn_id)) {
|
||||
LOGPFSML(fi, LOGL_INFO, "Conn cleanup, sending DLCX for %s %s\n", mgcp_ctx->conn_peer_remote.endpoint,
|
||||
mgcp_ctx->conn_id);
|
||||
msg = make_dlcx_msg(mgcp_ctx);
|
||||
if (!msg)
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW/DLCX: Error composing DLCX message\n");
|
||||
@@ -594,6 +608,7 @@ static struct osmo_fsm fsm_mgcp_client = {
|
||||
.timer_cb = fsm_timeout_cb,
|
||||
.cleanup = fsm_cleanup_cb,
|
||||
.event_names = fsm_mgcp_client_evt_names,
|
||||
.log_subsys = DLMGCP,
|
||||
};
|
||||
|
||||
/*! allocate FSM, and create a new connection on the MGW.
|
||||
@@ -707,8 +722,8 @@ void mgcp_conn_delete(struct osmo_fsm_inst *fi)
|
||||
if (fi->proc.terminating)
|
||||
return;
|
||||
|
||||
/* Unlink FSM from parent */
|
||||
osmo_fsm_inst_unlink_parent(fi, NULL);
|
||||
/* Unlink FSM from parent, set the struct mgcp_client as new talloc ctx. */
|
||||
osmo_fsm_inst_unlink_parent(fi, mgcp_ctx->mgcp);
|
||||
|
||||
/* An error situation where the parent FSM must be killed immediately
|
||||
* may lead into a situation where the DLCX can not be executed right
|
||||
|
216
src/libosmo-mgcp-client/mgcp_client_pool.c
Normal file
216
src/libosmo-mgcp-client/mgcp_client_pool.c
Normal file
@@ -0,0 +1,216 @@
|
||||
/* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* 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 2 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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>
|
||||
#include <stddef.h>
|
||||
|
||||
#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. */
|
||||
struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx)
|
||||
{
|
||||
struct mgcp_client_pool *pool;
|
||||
|
||||
pool = talloc_zero(talloc_ctx, struct mgcp_client_pool);
|
||||
if (!pool)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&pool->pool);
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
/*! Initialize and connect an mcgp client pool.
|
||||
* \param[in,out] mgcp MGCP client pool descriptor.
|
||||
* \returns number of successfully initialized pool members. */
|
||||
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) {
|
||||
|
||||
/* 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++;
|
||||
}
|
||||
|
||||
return pool_members_initialized;
|
||||
}
|
||||
|
||||
/*! register a single mgcp_client instance to the pool.
|
||||
* \param[out] pool MGCP client pool descriptor.
|
||||
* \param[in] mgcp MGCP client descriptor. */
|
||||
void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp_client *mgcp_client)
|
||||
{
|
||||
/*! Some applications still support the non-pooled MGW VTY configuration variant provided by
|
||||
* mgcp_client_vty_init(). If this is the case the mgcp_client instance created by mgcp_client_init()
|
||||
* can be registered here so that it will appear as if it were part of the pool. When the user actively
|
||||
* configures MGW pool members, the MGCP client registered here will be ignored. (The registration of
|
||||
* multiple singe mgcp_client instances is not possible.) */
|
||||
pool->mgcp_client_single = mgcp_client;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
llist_for_each_entry(pool_member, &pool->pool, list) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (pool_member_picked) {
|
||||
LOGPPMGW(pool_member_picked, LOGL_DEBUG, "MGW pool has %u members -- using MGW %u (active calls: %u)\n",
|
||||
n_pool_members, pool_member_picked->nr, pool_member_picked->refcount);
|
||||
return pool_member_picked;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MGW pool has %u members, but no functional MGW pool member found -- check configuration!\n",
|
||||
n_pool_members);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! get an MGCP client from the pool (increment reference counter).
|
||||
* \param[in,out] pool MGCP client pool descriptor.
|
||||
* \returns MGCP client descriptor, NULL if no member was found (empty pool). */
|
||||
struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool)
|
||||
{
|
||||
struct mgcp_client_pool_member *pool_member;
|
||||
|
||||
/*! When an MGCP client is taken from the pool it is still available for other calls. In fact only a reference
|
||||
* counter is incremented to keep track on how many references to a specific MGCP client are currently used
|
||||
* 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) {
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! put an MGCP client back into the pool (decrement reference counter).
|
||||
* \param[in,out] pool MGCP client pool descriptor.
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
*
|
||||
* This function is able to detect automatically to which pool the mgcp_client belongs. If the mgcp_client does
|
||||
* not belong to a pool at all, the function call will have no effect. */
|
||||
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
|
||||
return;
|
||||
|
||||
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--;
|
||||
}
|
||||
}
|
||||
}
|
@@ -24,16 +24,36 @@
|
||||
#include <stdlib.h>
|
||||
#include <talloc.h>
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/core/utils.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>
|
||||
|
||||
#define MGW_STR MGCP_CLIENT_MGW_STR
|
||||
|
||||
void *global_mgcp_client_ctx = NULL;
|
||||
struct mgcp_client_conf *global_mgcp_client_conf = NULL;
|
||||
/* Only common (non-pooled) VTY connands 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;
|
||||
|
||||
/* MGCP Client configuration used with mgcp_client_vty_init(). (This pointer
|
||||
* points to user provided memory, so it cannot be used as talloc context.) */
|
||||
static struct mgcp_client_conf *global_mgcp_client_conf = NULL;
|
||||
|
||||
/* Pointer to the MGCP pool that is managed by mgcp_client_pool_vty_init() */
|
||||
static struct mgcp_client_pool *global_mgcp_client_pool = NULL;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
|
||||
"mgw local-ip " VTY_IPV46_CMD,
|
||||
@@ -41,11 +61,10 @@ DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
|
||||
"local bind IPv4 address\n"
|
||||
"local bind IPv6 address\n")
|
||||
{
|
||||
if (!global_mgcp_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
OSMO_ASSERT(global_mgcp_client_ctx);
|
||||
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
|
||||
|
||||
osmo_talloc_replace_string(global_mgcp_client_ctx,
|
||||
(char**)&global_mgcp_client_conf->local_addr,
|
||||
(char **)&conf->local_addr,
|
||||
argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -59,9 +78,9 @@ DEFUN(cfg_mgw_local_port, cfg_mgw_local_port_cmd,
|
||||
MGW_STR "local port to connect to MGW from\n"
|
||||
"local bind port\n")
|
||||
{
|
||||
if (!global_mgcp_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
global_mgcp_client_conf->local_port = atoi(argv[0]);
|
||||
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
|
||||
|
||||
conf->local_port = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_local_port, cfg_mgcpgw_local_port_cmd,
|
||||
@@ -75,12 +94,10 @@ DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
|
||||
"remote IPv4 address\n"
|
||||
"remote IPv6 address\n")
|
||||
{
|
||||
if (!global_mgcp_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
OSMO_ASSERT(global_mgcp_client_ctx);
|
||||
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
|
||||
|
||||
osmo_talloc_replace_string(global_mgcp_client_ctx,
|
||||
(char**)&global_mgcp_client_conf->remote_addr,
|
||||
argv[0]);
|
||||
(char **)&conf->remote_addr, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
|
||||
@@ -93,9 +110,9 @@ DEFUN(cfg_mgw_remote_port, cfg_mgw_remote_port_cmd,
|
||||
MGW_STR "remote port to reach the MGW at\n"
|
||||
"remote port\n")
|
||||
{
|
||||
if (!global_mgcp_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
global_mgcp_client_conf->remote_port = atoi(argv[0]);
|
||||
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
|
||||
|
||||
conf->remote_port = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
|
||||
@@ -145,50 +162,129 @@ DEFUN(cfg_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")
|
||||
{
|
||||
if (osmo_strlcpy(global_mgcp_client_conf->endpoint_domain_name, argv[0],
|
||||
sizeof(global_mgcp_client_conf->endpoint_domain_name))
|
||||
>= sizeof(global_mgcp_client_conf->endpoint_domain_name)) {
|
||||
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
|
||||
|
||||
if (osmo_strlcpy(conf->endpoint_domain_name, argv[0], sizeof(conf->endpoint_domain_name))
|
||||
>= sizeof(conf->endpoint_domain_name)) {
|
||||
vty_out(vty, "%% Error: 'mgw endpoint-domain' name too long, max length is %zu: '%s'%s",
|
||||
sizeof(global_mgcp_client_conf->endpoint_domain_name) - 1, argv[0], VTY_NEWLINE);
|
||||
sizeof(conf->endpoint_domain_name) - 1, argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int mgcp_client_config_write(struct vty *vty, const char *indent)
|
||||
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,"
|
||||
"e.g. 'rtpbridge/*'\n"
|
||||
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
|
||||
{
|
||||
const char *addr;
|
||||
int port;
|
||||
int rc;
|
||||
struct reset_ep *reset_ep;
|
||||
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
|
||||
|
||||
addr = global_mgcp_client_conf->local_addr;
|
||||
if (addr)
|
||||
vty_out(vty, "%smgw local-ip %s%s", indent, addr,
|
||||
VTY_NEWLINE);
|
||||
port = global_mgcp_client_conf->local_port;
|
||||
if (port >= 0)
|
||||
vty_out(vty, "%smgw local-port %u%s", indent,
|
||||
(uint16_t)port, VTY_NEWLINE);
|
||||
/* stop when the address is already in the list */
|
||||
llist_for_each_entry(reset_ep, &conf->reset_epnames, list) {
|
||||
if (strcmp(argv[0], reset_ep->name) == 0) {
|
||||
vty_out(vty, "%% duplicate endpoint name configured ('%s')%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
}
|
||||
|
||||
addr = global_mgcp_client_conf->remote_addr;
|
||||
if (addr)
|
||||
vty_out(vty, "%smgw remote-ip %s%s", indent, addr,
|
||||
VTY_NEWLINE);
|
||||
port = global_mgcp_client_conf->remote_port;
|
||||
if (port >= 0)
|
||||
vty_out(vty, "%smgw remote-port %u%s", indent,
|
||||
(uint16_t)port, VTY_NEWLINE);
|
||||
/* the domain name is not part of the actual endpoint name */
|
||||
if (strchr(argv[0], '@')) {
|
||||
vty_out(vty, "%% the endpoint name must be given without domain name ('%s')%s",
|
||||
argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (global_mgcp_client_conf->endpoint_domain_name[0])
|
||||
vty_out(vty, "%smgw endpoint-domain %s%s", indent,
|
||||
global_mgcp_client_conf->endpoint_domain_name, VTY_NEWLINE);
|
||||
reset_ep = talloc_zero(global_mgcp_client_ctx, struct reset_ep);
|
||||
OSMO_ASSERT(reset_ep);
|
||||
|
||||
rc = osmo_strlcpy(reset_ep->name, argv[0], sizeof(reset_ep->name));
|
||||
if (rc >= sizeof(reset_ep->name)) {
|
||||
vty_out(vty, "%% Error: 'mgw reset-endpoint' name too long, max length is %zu: '%s'%s",
|
||||
sizeof(reset_ep->name) - 1, argv[0], VTY_NEWLINE);
|
||||
talloc_free(reset_ep);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
llist_add_tail(&reset_ep->list, &conf->reset_epnames);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *conf)
|
||||
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"
|
||||
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
|
||||
{
|
||||
struct reset_ep *reset_ep;
|
||||
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
|
||||
|
||||
llist_for_each_entry(reset_ep, &conf->reset_epnames, list) {
|
||||
if (strcmp(argv[0], reset_ep->name) == 0) {
|
||||
llist_del(&reset_ep->list);
|
||||
talloc_free(reset_ep);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
vty_out(vty, "%% no such endpoint name configured ('%s')%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
static int config_write(struct vty *vty, const char *indent, struct mgcp_client_conf *conf)
|
||||
{
|
||||
const char *addr;
|
||||
int port;
|
||||
struct reset_ep *reset_ep;
|
||||
|
||||
if (conf->description)
|
||||
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);
|
||||
port = conf->local_port;
|
||||
if (port >= 0)
|
||||
vty_out(vty, "%smgw local-port %u%s", indent,
|
||||
(uint16_t)port, VTY_NEWLINE);
|
||||
|
||||
addr = conf->remote_addr;
|
||||
if (addr)
|
||||
vty_out(vty, "%smgw remote-ip %s%s", indent, addr,
|
||||
VTY_NEWLINE);
|
||||
port = conf->remote_port;
|
||||
if (port >= 0)
|
||||
vty_out(vty, "%smgw remote-port %u%s", indent,
|
||||
(uint16_t)port, VTY_NEWLINE);
|
||||
|
||||
if (conf->endpoint_domain_name[0])
|
||||
vty_out(vty, "%smgw endpoint-domain %s%s", indent,
|
||||
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);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/*! Write out MGCP client config to VTY.
|
||||
* \param[in] vty VTY to which we should print.
|
||||
* \param[in] string used for indentation (e.g. " ").
|
||||
* \returns CMD_SUCCESS on success, CMD_WARNING on error */
|
||||
int mgcp_client_config_write(struct vty *vty, const char *indent)
|
||||
{
|
||||
return config_write(vty, indent, global_mgcp_client_conf);
|
||||
}
|
||||
|
||||
static void vty_init_common(void *talloc_ctx, int node)
|
||||
{
|
||||
global_mgcp_client_ctx = talloc_ctx;
|
||||
global_mgcp_client_conf = conf;
|
||||
|
||||
install_lib_element(node, &cfg_mgw_local_ip_cmd);
|
||||
install_lib_element(node, &cfg_mgw_local_port_cmd);
|
||||
@@ -197,6 +293,20 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
|
||||
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);
|
||||
|
||||
osmo_fsm_vty_add_cmds();
|
||||
}
|
||||
|
||||
/*! Set up MGCP client VTY
|
||||
* (called once at startup by the application process).
|
||||
* \param[in] talloc_ctx talloc context to be used by the VTY for allocating memory.
|
||||
* \param[in] node identifier of the node on which the VTY commands should be installed.
|
||||
* \param[in] conf user provided memory to to store the MGCP client configuration data. */
|
||||
void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *conf)
|
||||
{
|
||||
global_mgcp_client_conf = conf;
|
||||
|
||||
/* deprecated 'mgcpgw' commands */
|
||||
install_lib_element(node, &cfg_mgcpgw_local_ip_cmd);
|
||||
@@ -206,5 +316,242 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
|
||||
install_lib_element(node, &cfg_mgcpgw_endpoint_range_cmd);
|
||||
install_lib_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd);
|
||||
|
||||
osmo_fsm_vty_add_cmds();
|
||||
vty_init_common(talloc_ctx, node);
|
||||
}
|
||||
|
||||
static int config_write_pool(struct vty *vty)
|
||||
{
|
||||
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);
|
||||
|
||||
snprintf(indent, indent_buf_len, "%s ", pool->vty_indent);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
talloc_free(indent);
|
||||
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)
|
||||
{
|
||||
struct mgcp_client_pool_member *pool_member = NULL;
|
||||
struct mgcp_client_pool_member *pool_member_tmp;
|
||||
|
||||
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 pool_member;
|
||||
}
|
||||
|
||||
DEFUN_ATTR(cfg_mgw,
|
||||
cfg_mgw_cmd, "mgw <0-255>", "Select a MGCP client config to setup\n" "reference number\n", CMD_ATTR_IMMEDIATE)
|
||||
{
|
||||
int nr = atoi(argv[0]);
|
||||
struct mgcp_client_pool_member *pool_member;
|
||||
|
||||
pool_member = pool_member_by_nr(nr);
|
||||
if (!pool_member) {
|
||||
pool_member = talloc_zero(global_mgcp_client_pool, struct mgcp_client_pool_member);
|
||||
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;
|
||||
vty->index_sub = &pool_member->conf.description;
|
||||
vty->node = global_mgcp_client_pool->vty_node->node;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_ATTR(cfg_no_mgw,
|
||||
cfg_no_mgw_cmd,
|
||||
"no mgw <0-255>", NO_STR "Select a MGCP client config to remove\n" "reference number\n", CMD_ATTR_IMMEDIATE)
|
||||
{
|
||||
int nr = atoi(argv[0]);
|
||||
struct mgcp_client_pool_member *pool_member;
|
||||
|
||||
pool_member = pool_member_by_nr(nr);
|
||||
if (!pool_member) {
|
||||
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
/* Make sure that there are no ongoing calls */
|
||||
if (pool_member->refcount > 0) {
|
||||
vty_out(vty, "%% MGCP client (MGW %s) is still serving ongoing calls -- can't remove it now!%s",
|
||||
mgcp_client_pool_member_name(pool_member), VTY_NEWLINE);
|
||||
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);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_ATTR(mgw_reconnect, mgw_reconnect_cmd,
|
||||
"mgw <0-255> reconnect",
|
||||
MGW_STR "reference number\n" "reconfigure and reconnect MGCP client\n", CMD_ATTR_IMMEDIATE)
|
||||
{
|
||||
int nr = atoi(argv[0]);
|
||||
struct mgcp_client_pool_member *pool_member = NULL;
|
||||
|
||||
pool_member = pool_member_by_nr(nr);
|
||||
if (!pool_member) {
|
||||
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
/* Make sure that there are no ongoing calls */
|
||||
if (pool_member->refcount > 0) {
|
||||
vty_out(vty, "%% MGCP client (MGW %s) is still serving ongoing calls -- can't reconnect it now!%s",
|
||||
mgcp_client_pool_member_name(pool_member), VTY_NEWLINE);
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_ATTR(mgw_block, mgw_block_cmd,
|
||||
"mgw <0-255> block",
|
||||
MGW_STR "reference number\n" "block MGCP client so that it won't be used for new calls\n", CMD_ATTR_IMMEDIATE)
|
||||
{
|
||||
int nr = atoi(argv[0]);
|
||||
struct mgcp_client_pool_member *pool_member = NULL;
|
||||
|
||||
pool_member = pool_member_by_nr(nr);
|
||||
if (!pool_member) {
|
||||
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
pool_member->blocked = true;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_ATTR(mgw_unblock, mgw_unblock_cmd,
|
||||
"mgw <0-255> unblock",
|
||||
MGW_STR "reference number\n" "unblock MGCP client so that it will be available for new calls\n", CMD_ATTR_IMMEDIATE)
|
||||
{
|
||||
int nr = atoi(argv[0]);
|
||||
struct mgcp_client_pool_member *pool_member = NULL;
|
||||
|
||||
pool_member = pool_member_by_nr(nr);
|
||||
if (!pool_member) {
|
||||
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
pool_member->blocked = false;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(mgw_show, mgw_snow_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) {
|
||||
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)) {
|
||||
vty_out(vty, "%% (pool is empty)%s", VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
llist_for_each_entry(pool_member, &global_mgcp_client_pool->pool, 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);
|
||||
vty_out(vty, "%% service: %s%s", pool_member->blocked ? "blocked" : "unblocked", VTY_NEWLINE);
|
||||
vty_out(vty, "%% ongoing calls: %u%s", pool_member->refcount, VTY_NEWLINE);
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/*! Set up MGCP client VTY (pooled)
|
||||
* (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] 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)
|
||||
{
|
||||
/* A pool must be allocated before this function can be called */
|
||||
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);
|
||||
pool->vty_node = talloc_zero(pool, struct cmd_node);
|
||||
OSMO_ASSERT(pool->vty_node);
|
||||
pool->vty_node->node = mgw_node;
|
||||
pool->vty_node->vtysh = 1;
|
||||
pool->vty_node->prompt = talloc_strdup(pool->vty_node, "%s(config-mgw)# ");
|
||||
|
||||
install_lib_element(parent_node, &cfg_mgw_cmd);
|
||||
install_lib_element(parent_node, &cfg_no_mgw_cmd);
|
||||
|
||||
install_node(pool->vty_node, config_write_pool);
|
||||
vty_init_common(pool, mgw_node);
|
||||
|
||||
install_element(mgw_node, &cfg_description_cmd);
|
||||
|
||||
install_lib_element(ENABLE_NODE, &mgw_reconnect_cmd);
|
||||
install_lib_element(ENABLE_NODE, &mgw_block_cmd);
|
||||
install_lib_element(ENABLE_NODE, &mgw_unblock_cmd);
|
||||
|
||||
install_lib_element_ve(&mgw_snow_cmd);
|
||||
|
||||
global_mgcp_client_pool = pool;
|
||||
}
|
||||
|
@@ -89,15 +89,13 @@ static void codec_init(struct mgcp_rtp_codec *codec)
|
||||
.frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN,
|
||||
.rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE,
|
||||
.channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS,
|
||||
.subtype_name = "",
|
||||
.audio_name = "",
|
||||
};
|
||||
}
|
||||
|
||||
static void codec_free(struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
if (codec->subtype_name)
|
||||
talloc_free(codec->subtype_name);
|
||||
if (codec->audio_name)
|
||||
talloc_free(codec->audio_name);
|
||||
*codec = (struct mgcp_rtp_codec){};
|
||||
}
|
||||
|
||||
@@ -124,10 +122,8 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud
|
||||
{
|
||||
int rate;
|
||||
int channels;
|
||||
char audio_codec[64];
|
||||
struct mgcp_rtp_codec *codec;
|
||||
unsigned int pt_offset = conn->end.codecs_assigned;
|
||||
void *ctx = conn->conn;
|
||||
|
||||
/* The amount of codecs we can store is limited, make sure we do not
|
||||
* overrun this limit. */
|
||||
@@ -160,16 +156,16 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud
|
||||
if (!audio_name) {
|
||||
switch (payload_type) {
|
||||
case 0:
|
||||
audio_name = talloc_strdup(ctx, "PCMU/8000/1");
|
||||
strcpy(codec->audio_name, "PCMU/8000/1");
|
||||
break;
|
||||
case 3:
|
||||
audio_name = talloc_strdup(ctx, "GSM/8000/1");
|
||||
strcpy(codec->audio_name, "GSM/8000/1");
|
||||
break;
|
||||
case 8:
|
||||
audio_name = talloc_strdup(ctx, "PCMA/8000/1");
|
||||
strcpy(codec->audio_name, "PCMA/8000/1");
|
||||
break;
|
||||
case 18:
|
||||
audio_name = talloc_strdup(ctx, "G729/8000/1");
|
||||
strcpy(codec->audio_name, "G729/8000/1");
|
||||
break;
|
||||
default:
|
||||
/* The given payload type is not known to us, or it
|
||||
@@ -179,36 +175,36 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud
|
||||
payload_type);
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
OSMO_STRLCPY_ARRAY(codec->audio_name, audio_name);
|
||||
}
|
||||
|
||||
/* Now we extract the codec subtype name, rate and channels. The latter
|
||||
* two are optional. If they are not present we use the safe defaults
|
||||
* above. */
|
||||
if (strlen(audio_name) >= sizeof(audio_codec)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Audio codec too long: %s\n", osmo_quote_str(audio_name, -1));
|
||||
if (strlen(codec->audio_name) >= sizeof(codec->subtype_name)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Audio codec too long: %s\n", osmo_quote_str(codec->audio_name, -1));
|
||||
goto error;
|
||||
}
|
||||
channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
|
||||
rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
|
||||
if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Invalid audio codec: %s\n", osmo_quote_str(audio_name, -1));
|
||||
if (sscanf(codec->audio_name, "%63[^/]/%d/%d", codec->subtype_name, &rate, &channels) < 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Invalid audio codec: %s\n", osmo_quote_str(codec->audio_name, -1));
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Note: We only accept configurations with one audio channel! */
|
||||
if (channels != 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot handle audio codec with more than one channel: %s\n",
|
||||
osmo_quote_str(audio_name, -1));
|
||||
osmo_quote_str(codec->audio_name, -1));
|
||||
goto error;
|
||||
}
|
||||
|
||||
codec->rate = rate;
|
||||
codec->channels = channels;
|
||||
codec->subtype_name = talloc_strdup(ctx, audio_codec);
|
||||
codec->audio_name = talloc_strdup(ctx, audio_name);
|
||||
codec->payload_type = payload_type;
|
||||
|
||||
if (!strcmp(audio_codec, "G729")) {
|
||||
if (!strcmp(codec->subtype_name, "G729")) {
|
||||
codec->frame_duration_num = 10;
|
||||
codec->frame_duration_den = 1000;
|
||||
} else {
|
||||
@@ -287,7 +283,7 @@ static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct m
|
||||
/* A codec name must be set, if not, this might mean that the codec
|
||||
* (payload type) that was assigned is unknown to us so we must stop
|
||||
* here. */
|
||||
if (!codec->subtype_name)
|
||||
if (!strlen(codec->subtype_name))
|
||||
return false;
|
||||
|
||||
/* FIXME: implement meaningful checks to make sure that the given codec
|
||||
|
@@ -21,6 +21,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_network.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
@@ -89,7 +90,7 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
|
||||
/* FIXME: Each new rate counter group requires an unique index. At the
|
||||
* moment we generate this index using this counter, but perhaps there
|
||||
* is a more concious way to assign the indexes. */
|
||||
static unsigned int rate_ctr_index = 0;
|
||||
static atomic_uint rate_ctr_index = 0;
|
||||
|
||||
conn_rtp->type = MGCP_RTP_DEFAULT;
|
||||
conn_rtp->osmux.cid_allocated = false;
|
||||
@@ -110,13 +111,12 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
|
||||
end->output_enabled = 0;
|
||||
end->maximum_packet_time = -1;
|
||||
|
||||
conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index);
|
||||
conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++);
|
||||
if (!conn_rtp->rate_ctr_group)
|
||||
return -1;
|
||||
|
||||
conn_rtp->state.in_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[IN_STREAM_ERR_TSTMP_CTR];
|
||||
conn_rtp->state.out_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[OUT_STREAM_ERR_TSTMP_CTR];
|
||||
rate_ctr_index++;
|
||||
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);
|
||||
|
||||
/* Make sure codec table is reset */
|
||||
mgcp_codec_reset_all(conn_rtp);
|
||||
@@ -143,7 +143,7 @@ void mgcp_conn_watchdog_cb(void *data)
|
||||
|
||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn)
|
||||
{
|
||||
int timeout = conn->endp->cfg->conn_timeout;
|
||||
int timeout = conn->endp->trunk->cfg->conn_timeout;
|
||||
if (!timeout)
|
||||
return;
|
||||
|
||||
@@ -270,12 +270,12 @@ static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_con
|
||||
OSMO_ASSERT(conn_stats->desc->num_ctr + 1 == all_stats->desc->num_ctr);
|
||||
|
||||
/* all other counters are [now] updated in real-time */
|
||||
rate_ctr_add(&all_stats->ctr[IN_STREAM_ERR_TSTMP_CTR],
|
||||
conn_stats->ctr[IN_STREAM_ERR_TSTMP_CTR].current);
|
||||
rate_ctr_add(&all_stats->ctr[OUT_STREAM_ERR_TSTMP_CTR],
|
||||
conn_stats->ctr[OUT_STREAM_ERR_TSTMP_CTR].current);
|
||||
rate_ctr_add(rate_ctr_group_get_ctr(all_stats, IN_STREAM_ERR_TSTMP_CTR),
|
||||
rate_ctr_group_get_ctr(conn_stats, IN_STREAM_ERR_TSTMP_CTR)->current);
|
||||
rate_ctr_add(rate_ctr_group_get_ctr(all_stats, OUT_STREAM_ERR_TSTMP_CTR),
|
||||
rate_ctr_group_get_ctr(conn_stats, OUT_STREAM_ERR_TSTMP_CTR)->current);
|
||||
|
||||
rate_ctr_inc(&all_stats->ctr[RTP_NUM_CONNECTIONS]);
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(all_stats, RTP_NUM_CONNECTIONS));
|
||||
}
|
||||
|
||||
/*! free a connection by its ID.
|
||||
@@ -328,12 +328,10 @@ void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
|
||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
struct mgcp_conn *conn_tmp;
|
||||
|
||||
/* Drop all items in the list */
|
||||
llist_for_each_entry_safe(conn, conn_tmp, &endp->conns, entry) {
|
||||
/* Drop all items in the list, might be consecutive! */
|
||||
while ((conn = llist_first_entry_or_null(&endp->conns, struct mgcp_conn, entry)))
|
||||
mgcp_conn_free(endp, conn->id);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
@@ -192,12 +192,12 @@ static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_dat
|
||||
{
|
||||
struct mgcp_endpoint *endp = user_data;
|
||||
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
|
||||
struct msgb *msg = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-IDLE-TX-TRAU-frame");
|
||||
struct msgb *msg = msgb_alloc_c(endp->trunk, E1_TRAU_BITS_MSGB, "E1-I.460-IDLE-TX-TRAU-frame");
|
||||
uint8_t *ptr;
|
||||
const uint8_t *ptr_ft;
|
||||
enum osmo_trau_frame_type ft;
|
||||
|
||||
rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_MUX_EMPTY_CTR]);
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_MUX_EMPTY_CTR));
|
||||
|
||||
/* Choose an appropiate idle frame type */
|
||||
ft = endp->e1.trau_rtp_st->type;
|
||||
@@ -238,9 +238,9 @@ static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_da
|
||||
* (the resulting frame will be prepended with an all-zero (12-byte) rtp header) */
|
||||
static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int num_bits)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc(RTP_BUF_SIZE, "RTP-rx-from-E1");
|
||||
unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
|
||||
struct mgcp_endpoint *endp = user_data;
|
||||
struct msgb *msg = msgb_alloc_c(endp->trunk, RTP_BUF_SIZE, "RTP-rx-from-E1");
|
||||
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
|
||||
struct mgcp_conn *conn_dst;
|
||||
struct osmo_trau_frame fr;
|
||||
@@ -304,7 +304,7 @@ static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int
|
||||
msgb_free(msg);
|
||||
return;
|
||||
skip:
|
||||
rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_RX_FAIL_CTR]);
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_RX_FAIL_CTR));
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
@@ -312,7 +312,7 @@ skip:
|
||||
/* Function to handle outgoing E1 traffic */
|
||||
static void e1_send(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc(E1_TS_BYTES, "E1-TX-timeslot-bytes");
|
||||
struct msgb *msg = msgb_alloc_c(trunk, E1_TS_BYTES, "E1-TX-timeslot-bytes");
|
||||
uint8_t *ptr;
|
||||
|
||||
/* Get E1 frame from I.460 multiplexer */
|
||||
@@ -337,7 +337,8 @@ static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
|
||||
/* Find associated trunk */
|
||||
trunk = mgcp_trunk_by_line_num(cfg, ts->line->num);
|
||||
if (!trunk) {
|
||||
LOGP(DE1, LOGL_DEBUG, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num);
|
||||
LOGP(DE1, LOGL_ERROR, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num);
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -359,6 +360,9 @@ static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
|
||||
|
||||
/* Trigger sending of pending E1 traffic */
|
||||
e1_send(ts, trunk);
|
||||
|
||||
/* e1inp_rx_ts() does not free() msgb */
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
|
||||
@@ -376,14 +380,14 @@ static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
|
||||
cfg = trunk->cfg;
|
||||
|
||||
if (trunk->e1.ts_in_use[ts_nr - 1]) {
|
||||
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u already set up, skipping...\n", ts_nr);
|
||||
LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u already set up, skipping...\n", ts_nr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get E1 line */
|
||||
e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
|
||||
if (!e1_line) {
|
||||
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "no such E1 line %u - check VTY config!\n",
|
||||
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "no such E1 line %u - check VTY config!\n",
|
||||
trunk->e1.vty_line_nr);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -401,7 +405,7 @@ static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u set up successfully.\n", ts_nr);
|
||||
LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u set up successfully.\n", ts_nr);
|
||||
trunk->e1.ts_in_use[ts_nr - 1] = true;
|
||||
|
||||
return 0;
|
||||
@@ -545,7 +549,7 @@ int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8
|
||||
endp->e1.scd.mux.in_cb_queue_empty = e1_i460_mux_empty_cb;
|
||||
endp->e1.scd.mux.user_data = endp;
|
||||
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "adding I.460 subchannel: ts=%u, bit_offset=%u, rate=%uk, num_bits=%lu\n", ts,
|
||||
LOGPENDP(endp, DE1, LOGL_INFO, "adding I.460 subchannel: ts=%u, bit_offset=%u, rate=%uk, num_bits=%lu\n", ts,
|
||||
offs, e1_rates[ss], endp->e1.scd.demux.num_bits);
|
||||
endp->e1.schan = osmo_i460_subchan_add(endp, &endp->trunk->e1.i460_ts[ts - 1], &endp->e1.scd);
|
||||
if (!endp->e1.schan) {
|
||||
@@ -622,7 +626,7 @@ void mgcp_e1_endp_release(struct mgcp_endpoint *endp)
|
||||
* \returns 0 on success, -1 on ERROR. */
|
||||
int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg)
|
||||
{
|
||||
struct msgb *msg_tf = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-TX-TRAU-frame");
|
||||
struct msgb *msg_tf = msgb_alloc_c(endp->trunk, E1_TRAU_BITS_MSGB, "E1-I.460-TX-TRAU-frame");
|
||||
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
|
||||
unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
|
||||
struct osmo_trau_frame tf;
|
||||
@@ -679,7 +683,7 @@ int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, s
|
||||
|
||||
return 0;
|
||||
skip:
|
||||
rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_TX_FAIL_CTR]);
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_TX_FAIL_CTR));
|
||||
msgb_free(msg_tf);
|
||||
return -1;
|
||||
}
|
||||
|
@@ -29,6 +29,7 @@
|
||||
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/mgcp/mgcp_e1.h>
|
||||
#include <osmocom/core/stat_item.h>
|
||||
|
||||
#define E1_RATE_MAX 64
|
||||
#define E1_OFFS_MAX 8
|
||||
@@ -58,7 +59,7 @@ static char *gen_virtual_epname(void *ctx, const char *domain,
|
||||
}
|
||||
|
||||
/* Generate E1 endpoint name from given numeric parameters */
|
||||
static char *gen_e1_epname(void *ctx, const char *domain, uint8_t trunk_nr,
|
||||
static char *gen_e1_epname(void *ctx, const char *domain, unsigned int trunk_nr,
|
||||
uint8_t ts_nr, uint8_t ss_nr)
|
||||
{
|
||||
unsigned int rate;
|
||||
@@ -88,7 +89,6 @@ struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk,
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&endp->conns);
|
||||
endp->cfg = trunk->cfg;
|
||||
endp->trunk = trunk;
|
||||
|
||||
switch (trunk->trunk_type) {
|
||||
@@ -122,6 +122,12 @@ void mgcp_endp_release(struct mgcp_endpoint *endp)
|
||||
* RSIP is executed), free them all at once. */
|
||||
mgcp_conn_free_all(endp);
|
||||
|
||||
/* We must only decrement the stat item when the endpoint as actually
|
||||
* claimed. An endpoint is claimed when a call-id is set */
|
||||
if (endp->callid)
|
||||
osmo_stat_item_dec(osmo_stat_item_group_get_item(endp->trunk->stats.common,
|
||||
TRUNK_STAT_ENDPOINTS_USED), 1);
|
||||
|
||||
/* Reset endpoint parameters and states */
|
||||
talloc_free(endp->callid);
|
||||
endp->callid = NULL;
|
||||
@@ -129,7 +135,6 @@ void mgcp_endp_release(struct mgcp_endpoint *endp)
|
||||
endp->local_options.string = NULL;
|
||||
talloc_free(endp->local_options.codec);
|
||||
endp->local_options.codec = NULL;
|
||||
endp->wildcarded_req = false;
|
||||
|
||||
if (endp->trunk->trunk_type == MGCP_TRUNK_E1)
|
||||
mgcp_e1_endp_release(endp);
|
||||
@@ -182,12 +187,16 @@ static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert all characters in epname to lowercase and strip trunk prefix and
|
||||
|
||||
/*! Convert all characters in epname to lowercase and strip trunk prefix and
|
||||
* endpoint name suffix (domain name) from epname. The result is written to
|
||||
* to the memory pointed at by epname_stripped. The expected size of the
|
||||
* result is either equal or lower then the length of the input string
|
||||
* (epname) */
|
||||
static void strip_epname(char *epname_stripped, const char *epname,
|
||||
* (epname)
|
||||
* \param[out] epname_stripped pointer to store the stripped ep name.
|
||||
* \param[in] epname endpoint name to lookup.
|
||||
* \param[in] trunk where the endpoint is located. */
|
||||
void mgcp_endp_strip_name(char *epname_stripped, const char *epname,
|
||||
const struct mgcp_trunk *trunk)
|
||||
{
|
||||
osmo_str_tolower_buf(epname_stripped, MGCP_ENDPOINT_MAXLEN, epname);
|
||||
@@ -214,9 +223,11 @@ static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find an endpoint specified by its name. If the endpoint can not be found,
|
||||
* return NULL */
|
||||
static struct mgcp_endpoint *find_specific_endpoint(const char *epname,
|
||||
/*! Find an endpoint of a trunk specified by its name.
|
||||
* \param[in] epname endpoint name to check.
|
||||
* \param[in] trunk mgcp_trunk that might have this endpoint.
|
||||
* \returns NULL if no ep found, else endpoint. */
|
||||
struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname,
|
||||
const struct mgcp_trunk *trunk)
|
||||
{
|
||||
char epname_stripped[MGCP_ENDPOINT_MAXLEN];
|
||||
@@ -225,11 +236,11 @@ static struct mgcp_endpoint *find_specific_endpoint(const char *epname,
|
||||
unsigned int i;
|
||||
|
||||
/* Strip irrelevant information from the endpoint name */
|
||||
strip_epname(epname_stripped, epname, trunk);
|
||||
mgcp_endp_strip_name(epname_stripped, epname, trunk);
|
||||
|
||||
for (i = 0; i < trunk->number_endpoints; i++) {
|
||||
endp = trunk->endpoints[i];
|
||||
strip_epname(epname_stripped_endp, endp->name, trunk);
|
||||
mgcp_endp_strip_name(epname_stripped_endp, endp->name, trunk);
|
||||
if (strcmp(epname_stripped_endp, epname_stripped) == 0)
|
||||
return endp;
|
||||
}
|
||||
@@ -237,6 +248,18 @@ static struct mgcp_endpoint *find_specific_endpoint(const char *epname,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Check if the given epname refers to a wildcarded request or to a specific
|
||||
* endpoint.
|
||||
* \param[in] epname endpoint name to check
|
||||
* \returns true if epname refers to wildcarded request, else false. */
|
||||
bool mgcp_endp_is_wildcarded(const char *epname)
|
||||
{
|
||||
if (strstr(epname, "*"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*! Find an endpoint by its name on a specified trunk.
|
||||
* \param[out] cause pointer to store cause code, can be NULL.
|
||||
* \param[in] epname endpoint name to lookup.
|
||||
@@ -253,13 +276,12 @@ struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
|
||||
/* At the moment we only support a primitive ('*'-only) method of
|
||||
* wildcarded endpoint searches that picks the next free endpoint on
|
||||
* a trunk. */
|
||||
if (strstr(epname, "*")) {
|
||||
if (mgcp_endp_is_wildcarded(epname)) {
|
||||
endp = find_free_endpoint(trunk);
|
||||
if (endp) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
|
||||
"(trunk:%d) found free endpoint: %s\n",
|
||||
trunk->trunk_nr, endp->name);
|
||||
endp->wildcarded_req = true;
|
||||
return endp;
|
||||
}
|
||||
|
||||
@@ -273,12 +295,11 @@ struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
|
||||
|
||||
/* Find an endpoint by its name (if wildcarded request is not
|
||||
* applicable) */
|
||||
endp = find_specific_endpoint(epname, trunk);
|
||||
endp = mgcp_endp_find_specific(epname, trunk);
|
||||
if (endp) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
|
||||
"(trunk:%d) found endpoint: %s\n",
|
||||
trunk->trunk_nr, endp->name);
|
||||
endp->wildcarded_req = false;
|
||||
return endp;
|
||||
}
|
||||
|
||||
@@ -291,32 +312,6 @@ struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if the domain name, which is supplied with the endpoint name
|
||||
* matches the configuration. */
|
||||
static int check_domain_name(const char *epname, struct mgcp_config *cfg)
|
||||
{
|
||||
char *domain_to_check;
|
||||
|
||||
domain_to_check = strstr(epname, "@");
|
||||
if (!domain_to_check) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "missing domain name in endpoint name \"%s\", expecting \"%s\"\n",
|
||||
epname, cfg->domain);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Accept any domain if configured as "*" */
|
||||
if (!strcmp(cfg->domain, "*"))
|
||||
return 0;
|
||||
|
||||
if (strcmp(domain_to_check+1, cfg->domain) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "wrong domain name in endpoint name \"%s\", expecting \"%s\"\n",
|
||||
epname, cfg->domain);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Find an endpoint by its name, search at all trunks.
|
||||
* \param[out] cause, pointer to store cause code, can be NULL.
|
||||
* \param[in] epname, must contain trunk prefix.
|
||||
@@ -340,10 +335,6 @@ struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
|
||||
if (!trunk)
|
||||
return NULL;
|
||||
|
||||
/* All endpoint names require a domain as suffix */
|
||||
if (check_domain_name(epname, cfg))
|
||||
return NULL;
|
||||
|
||||
/* Identify the endpoint on the trunk */
|
||||
endp = mgcp_endp_by_name_trunk(cause, epname, trunk);
|
||||
if (!endp) {
|
||||
@@ -555,7 +546,7 @@ static bool endp_avail_e1(struct mgcp_endpoint *endp)
|
||||
epname_check = gen_e1_epname(endp, endp->trunk->cfg->domain,
|
||||
endp->trunk->trunk_nr, ts_nr,
|
||||
interlock[i]);
|
||||
endp_check = find_specific_endpoint(epname_check, endp->trunk);
|
||||
endp_check = mgcp_endp_find_specific(epname_check, endp->trunk);
|
||||
if (!endp_check) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"cannot check endpoint availability, overlapping endpoint:%s not found!\n",
|
||||
@@ -619,6 +610,8 @@ int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid)
|
||||
* connection ids) */
|
||||
endp->callid = talloc_strdup(endp, callid);
|
||||
OSMO_ASSERT(endp->callid);
|
||||
osmo_stat_item_inc(osmo_stat_item_group_get_item(endp->trunk->stats.common,
|
||||
TRUNK_STAT_ENDPOINTS_USED), 1);
|
||||
|
||||
/* Allocate resources */
|
||||
switch (endp->trunk->trunk_type) {
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#include <osmocom/mgcp/mgcp_msg.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
|
||||
/*! Display an mgcp message on the log output.
|
||||
* \param[in] message mgcp message string
|
||||
@@ -132,24 +133,19 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
|
||||
}
|
||||
|
||||
/*! Analyze and parse the the hader of an MGCP messeage string.
|
||||
* \param[out] pdata caller provided memory to store the parsing results
|
||||
* \param[in] data mgcp message string
|
||||
* \returns when the status line was complete and transaction_id and
|
||||
* endp out parameters are set, -1 on error */
|
||||
* \param[out] pdata caller provided memory to store the parsing results.
|
||||
* \param[in] data mgcp message string.
|
||||
* \returns 0 when the status line was complete and parseable, negative (MGCP
|
||||
* cause code) on error. */
|
||||
int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
{
|
||||
int i = 0;
|
||||
char *elem, *save = NULL;
|
||||
int cause;
|
||||
|
||||
/*! This function will parse the header part of the received
|
||||
* MGCP message. The parsing results are stored in pdata.
|
||||
* The function will also automatically search the pool with
|
||||
* available endpoints in order to find an endpoint that matches
|
||||
* the endpoint string in in the header */
|
||||
* MGCP message. The parsing results are stored in pdata. */
|
||||
|
||||
OSMO_ASSERT(data);
|
||||
pdata->trans = "000000";
|
||||
|
||||
for (elem = strtok_r(data, " ", &save); elem;
|
||||
elem = strtok_r(NULL, " ", &save)) {
|
||||
@@ -158,13 +154,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
pdata->trans = elem;
|
||||
break;
|
||||
case 1:
|
||||
pdata->endp = mgcp_endp_by_name(&cause, elem, pdata->cfg);
|
||||
if (!pdata->endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Unable to find Endpoint `%s'\n", elem);
|
||||
OSMO_ASSERT(cause < 0);
|
||||
return cause;
|
||||
}
|
||||
pdata->epname = elem;
|
||||
break;
|
||||
case 2:
|
||||
if (strcasecmp("MGCP", elem)) {
|
||||
@@ -174,11 +164,8 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (strcmp("1.0", elem)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' "
|
||||
"not supported\n", elem);
|
||||
if (strcmp("1.0", elem))
|
||||
return -528;
|
||||
}
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
@@ -186,8 +173,6 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
|
||||
if (i != 4) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n");
|
||||
pdata->trans = "000000";
|
||||
pdata->endp = NULL;
|
||||
return -510;
|
||||
}
|
||||
|
||||
@@ -224,22 +209,24 @@ int mgcp_parse_osmux_cid(const char *line)
|
||||
}
|
||||
|
||||
/*! Check MGCP parameter line (string) for plausibility.
|
||||
* \param[in] endp pointer to endpoint (only used for log output)
|
||||
* \param[in] endp pointer to endpoint (only used for log output, may be NULL)
|
||||
* \param[in] trunk pointer to trunk (only used for log output, may be NULL if endp is not NULL)
|
||||
* \param[in] line single parameter line from the MGCP message
|
||||
* \returns 1 when line seems plausible, 0 on error */
|
||||
int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
|
||||
* \returns true when line seems plausible, false on error */
|
||||
bool mgcp_check_param(const struct mgcp_endpoint *endp, struct mgcp_trunk *trunk, const char *line)
|
||||
{
|
||||
const size_t line_len = strlen(line);
|
||||
if (line[0] != '\0' && line_len < 2) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Wrong MGCP option format: '%s' on %s\n",
|
||||
line, endp->name);
|
||||
return 0;
|
||||
if (endp)
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "wrong MGCP option format: '%s'\n", line);
|
||||
else
|
||||
LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE, "wrong MGCP option format: '%s'\n", line);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* FIXME: A couple more checks wouldn't hurt... */
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*! Check if the specified callid seems plausible.
|
||||
|
@@ -66,8 +66,8 @@ static void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_end
|
||||
struct rate_ctr_group *mgw_stats = endp->trunk->ratectr.all_rtp_conn_stats;
|
||||
|
||||
/* add to both the per-connection and the global stats */
|
||||
rate_ctr_add(&conn_stats->ctr[id], inc);
|
||||
rate_ctr_add(&mgw_stats->ctr[id], inc);
|
||||
rate_ctr_add(rate_ctr_group_get_ctr(conn_stats, id), inc);
|
||||
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)
|
||||
@@ -77,7 +77,8 @@ static void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_end
|
||||
|
||||
static int rx_rtp(struct msgb *msg);
|
||||
|
||||
static bool addr_is_any(struct osmo_sockaddr *osa) {
|
||||
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,
|
||||
@@ -87,6 +88,11 @@ static bool addr_is_any(struct osmo_sockaddr *osa) {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*! 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.
|
||||
@@ -106,7 +112,7 @@ void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
|
||||
char *bind_addr;
|
||||
|
||||
/* Try probing the local IP-Address */
|
||||
if (endp->cfg->net_ports.bind_addr_probe && rem_addr_set) {
|
||||
if (endp->trunk->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,
|
||||
@@ -124,15 +130,15 @@ void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
|
||||
/* 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->cfg->net_ports.bind_addr_v6 :
|
||||
endp->cfg->net_ports.bind_addr_v4;
|
||||
endp->trunk->cfg->net_ports.bind_addr_v6 :
|
||||
endp->trunk->cfg->net_ports.bind_addr_v4;
|
||||
} else {
|
||||
/* Choose any of the bind addresses, preferring v6 over v4 */
|
||||
bind_addr = endp->cfg->net_ports.bind_addr_v6;
|
||||
if (!bind_addr)
|
||||
bind_addr = endp->cfg->net_ports.bind_addr_v4;
|
||||
bind_addr = endp->trunk->cfg->net_ports.bind_addr_v6;
|
||||
if (!strlen(bind_addr))
|
||||
bind_addr = endp->trunk->cfg->net_ports.bind_addr_v4;
|
||||
}
|
||||
if (bind_addr) {
|
||||
if (strlen(bind_addr)) {
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
|
||||
"using configured rtp bind ip as local bind ip %s\n",
|
||||
bind_addr);
|
||||
@@ -140,7 +146,7 @@ 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->cfg->source_addr;
|
||||
bind_addr = endp->trunk->cfg->source_addr;
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
|
||||
"using mgcp bind ip as local rtp bind ip: %s\n", bind_addr);
|
||||
}
|
||||
@@ -170,77 +176,8 @@ static uint32_t get_current_ts(unsigned codec_rate)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*! 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, 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));
|
||||
|
||||
if (is_ipv6) {
|
||||
addr->u.sin6.sin6_port = port;
|
||||
addr_len = sizeof(addr->u.sin6);
|
||||
} else {
|
||||
addr->u.sin.sin_port = port;
|
||||
addr_len = sizeof(addr->u.sin);
|
||||
}
|
||||
|
||||
return sendto(fd, buf, len, 0, &addr->u.sa, addr_len);
|
||||
}
|
||||
|
||||
/*! send RTP dummy packet (to keep NAT connection open).
|
||||
* \param[in] endp mcgp endpoint that holds the RTP connection.
|
||||
* \param[in] conn associated RTP connection.
|
||||
* \returns bytes sent, -1 on error. */
|
||||
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
static char buf[] = { MGCP_DUMMY_LOAD };
|
||||
int rc;
|
||||
int was_rtcp = 0;
|
||||
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(conn);
|
||||
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,"sending dummy packet... %s\n",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
|
||||
rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
|
||||
conn->end.rtp_port, buf, 1);
|
||||
|
||||
if (rc == -1)
|
||||
goto failed;
|
||||
|
||||
if (endp->trunk->omit_rtcp)
|
||||
return rc;
|
||||
|
||||
was_rtcp = 1;
|
||||
rc = mgcp_udp_send(conn->end.rtcp.fd, &conn->end.addr,
|
||||
conn->end.rtcp_port, buf, 1);
|
||||
|
||||
if (rc >= 0)
|
||||
return rc;
|
||||
|
||||
failed:
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"Failed to send dummy %s packet.\n",
|
||||
was_rtcp ? "RTCP" : "RTP");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Compute timestamp alignment error */
|
||||
static int32_t ts_alignment_error(struct mgcp_rtp_stream_state *sstate,
|
||||
static int32_t ts_alignment_error(const struct mgcp_rtp_stream_state *sstate,
|
||||
int ptime, uint32_t timestamp)
|
||||
{
|
||||
int32_t timestamp_delta;
|
||||
@@ -255,11 +192,11 @@ static int32_t ts_alignment_error(struct mgcp_rtp_stream_state *sstate,
|
||||
}
|
||||
|
||||
/* Check timestamp and sequence number for plausibility */
|
||||
static int check_rtp_timestamp(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_stream_state *sstate,
|
||||
struct mgcp_rtp_end *rtp_end,
|
||||
struct osmo_sockaddr *addr,
|
||||
static int check_rtp_timestamp(const struct mgcp_endpoint *endp,
|
||||
const struct mgcp_rtp_state *state,
|
||||
const struct mgcp_rtp_stream_state *sstate,
|
||||
const struct mgcp_rtp_end *rtp_end,
|
||||
const struct osmo_sockaddr *addr,
|
||||
uint16_t seq, uint32_t timestamp,
|
||||
const char *text, int32_t * tsdelta_out)
|
||||
{
|
||||
@@ -344,17 +281,25 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
|
||||
}
|
||||
|
||||
/* Set the timestamp offset according to the packet duration. */
|
||||
static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp,
|
||||
static int adjust_rtp_timestamp_offset(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *rtp_end,
|
||||
struct osmo_sockaddr *addr,
|
||||
int16_t delta_seq, uint32_t in_timestamp)
|
||||
const struct mgcp_rtp_end *rtp_end,
|
||||
const struct osmo_sockaddr *addr,
|
||||
int16_t delta_seq, uint32_t in_timestamp,
|
||||
bool marker_bit)
|
||||
{
|
||||
int32_t tsdelta = state->packet_duration;
|
||||
int timestamp_offset;
|
||||
uint32_t out_timestamp;
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
|
||||
if (marker_bit) {
|
||||
/* If RTP pkt contains marker bit, the timestamps are not longer
|
||||
* in sync, so we can erase timestamp offset patching. */
|
||||
state->patch.timestamp_offset = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tsdelta == 0) {
|
||||
tsdelta = state->out_stream.last_tsdelta;
|
||||
if (tsdelta != 0) {
|
||||
@@ -394,17 +339,23 @@ static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp,
|
||||
}
|
||||
|
||||
/* Set the timestamp offset according to the packet duration. */
|
||||
static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp,
|
||||
static int align_rtp_timestamp_offset(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *rtp_end,
|
||||
struct osmo_sockaddr *addr,
|
||||
uint32_t timestamp)
|
||||
const struct mgcp_rtp_end *rtp_end,
|
||||
const struct osmo_sockaddr *addr,
|
||||
uint32_t timestamp, bool marker_bit)
|
||||
{
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
int ts_error = 0;
|
||||
int ts_check = 0;
|
||||
int ptime = state->packet_duration;
|
||||
|
||||
if (marker_bit) {
|
||||
/* If RTP pkt contains marker bit, the timestamps are not longer
|
||||
* in sync, so no alignment is needed. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Align according to: T + Toffs - Tlast = k * Tptime */
|
||||
|
||||
ts_error = ts_alignment_error(&state->out_stream, ptime,
|
||||
@@ -475,14 +426,15 @@ void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
*fmtp_extra = conn->end.fmtp_extra;
|
||||
}
|
||||
|
||||
void mgcp_rtp_annex_count(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 int32_t transit, const uint32_t ssrc,
|
||||
const bool marker_bit)
|
||||
{
|
||||
int32_t d;
|
||||
|
||||
/* initialize or re-initialize */
|
||||
if (!state->stats.initialized || state->stats.ssrc != ssrc) {
|
||||
if (!state->stats.initialized || state->stats.ssrc != ssrc || marker_bit) {
|
||||
state->stats.initialized = 1;
|
||||
state->stats.base_seq = seq;
|
||||
state->stats.max_seq = seq - 1;
|
||||
@@ -556,7 +508,7 @@ static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
|
||||
* is only one source.
|
||||
* There is also no probation period for new sources. Every RTP header
|
||||
* we receive will be seen as a switch in streams. */
|
||||
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
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)
|
||||
@@ -566,6 +518,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
int32_t transit;
|
||||
uint16_t seq;
|
||||
uint32_t timestamp, ssrc;
|
||||
bool marker_bit;
|
||||
struct rtp_hdr *rtp_hdr;
|
||||
int payload = rtp_end->codec->payload_type;
|
||||
unsigned int len = msgb_length(msg);
|
||||
@@ -578,9 +531,10 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
timestamp = ntohl(rtp_hdr->timestamp);
|
||||
arrival_time = get_current_ts(rtp_end->codec->rate);
|
||||
ssrc = ntohl(rtp_hdr->ssrc);
|
||||
marker_bit = !!rtp_hdr->marker;
|
||||
transit = arrival_time - timestamp;
|
||||
|
||||
mgcp_rtp_annex_count(endp, state, seq, transit, ssrc);
|
||||
mgcp_rtp_annex_count(endp, state, seq, transit, ssrc, marker_bit);
|
||||
|
||||
if (!state->initialized) {
|
||||
state->initialized = 1;
|
||||
@@ -634,9 +588,9 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
state->packet_duration;
|
||||
|
||||
adjust_rtp_timestamp_offset(endp, state, rtp_end, addr,
|
||||
delta_seq, timestamp);
|
||||
delta_seq, timestamp, marker_bit);
|
||||
|
||||
state->patch.patch_ssrc = 1;
|
||||
state->patch.patch_ssrc = true;
|
||||
ssrc = state->patch.orig_ssrc;
|
||||
if (rtp_end->force_constant_ssrc != -1)
|
||||
rtp_end->force_constant_ssrc -= 1;
|
||||
@@ -652,10 +606,14 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
|
||||
state->in_stream.last_tsdelta = 0;
|
||||
} else {
|
||||
/* Compute current per-packet timestamp delta */
|
||||
check_rtp_timestamp(endp, state, &state->in_stream, rtp_end,
|
||||
addr, seq, timestamp, "input",
|
||||
&state->in_stream.last_tsdelta);
|
||||
if (!marker_bit) {
|
||||
/* Compute current per-packet timestamp delta */
|
||||
check_rtp_timestamp(endp, state, &state->in_stream, rtp_end,
|
||||
addr, seq, timestamp, "input",
|
||||
&state->in_stream.last_tsdelta);
|
||||
} else {
|
||||
state->in_stream.last_tsdelta = 0;
|
||||
}
|
||||
|
||||
if (state->patch.patch_ssrc)
|
||||
ssrc = state->patch.orig_ssrc;
|
||||
@@ -670,7 +628,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
state->out_stream.ssrc == ssrc && state->packet_duration)
|
||||
/* Align the timestamp offset */
|
||||
align_rtp_timestamp_offset(endp, state, rtp_end, addr,
|
||||
timestamp);
|
||||
timestamp, marker_bit);
|
||||
|
||||
/* Store the updated SSRC back to the packet */
|
||||
if (state->patch.patch_ssrc)
|
||||
@@ -685,10 +643,14 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
rtp_hdr->timestamp = htonl(timestamp);
|
||||
|
||||
/* Check again, whether the timestamps are still valid */
|
||||
if (state->out_stream.ssrc == ssrc)
|
||||
check_rtp_timestamp(endp, state, &state->out_stream, rtp_end,
|
||||
addr, seq, timestamp, "output",
|
||||
&state->out_stream.last_tsdelta);
|
||||
if (!marker_bit) {
|
||||
if (state->out_stream.ssrc == ssrc)
|
||||
check_rtp_timestamp(endp, state, &state->out_stream, rtp_end,
|
||||
addr, seq, timestamp, "output",
|
||||
&state->out_stream.last_tsdelta);
|
||||
} else {
|
||||
state->out_stream.last_tsdelta = 0;
|
||||
}
|
||||
|
||||
/* Save output values */
|
||||
state->out_stream.last_seq = seq;
|
||||
@@ -858,182 +820,6 @@ static void gen_rtp_header(struct msgb *msg, struct mgcp_rtp_end *rtp_end,
|
||||
hdr->ssrc = state->alt_rtp_tx_ssrc;
|
||||
}
|
||||
|
||||
|
||||
/*! Send RTP/RTCP data to a specified destination connection.
|
||||
* \param[in] endp associated endpoint (for configuration, logging).
|
||||
* \param[in] is_rtp flag to specify if the packet is of type RTP or RTCP.
|
||||
* \param[in] spoofed source address (set to NULL to disable).
|
||||
* \param[in] buf buffer that contains the RTP/RTCP data.
|
||||
* \param[in] len length of the buffer that contains the RTP/RTCP data.
|
||||
* \param[in] conn_src associated source connection.
|
||||
* \param[in] conn_dst associated destination connection.
|
||||
* \returns 0 on success, -1 on ERROR. */
|
||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr,
|
||||
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_trunk *trunk = endp->trunk;
|
||||
struct mgcp_rtp_end *rtp_end;
|
||||
struct mgcp_rtp_state *rtp_state;
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
char *dest_name;
|
||||
int rc;
|
||||
int len;
|
||||
|
||||
OSMO_ASSERT(conn_src);
|
||||
OSMO_ASSERT(conn_dst);
|
||||
|
||||
if (is_rtp) {
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering RTP packet...\n");
|
||||
} 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) {
|
||||
rc = mgcp_patch_pt(conn_src, conn_dst, msg);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"can not patch PT because no suitable egress codec was found.\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_inc(conn_dst, endp, RTP_DROPPED_PACKETS_CTR);
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"output disabled, drop to %s %s "
|
||||
"rtp_port:%u rtcp_port:%u\n",
|
||||
dest_name,
|
||||
osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
|
||||
ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
|
||||
);
|
||||
} else if (is_rtp) {
|
||||
int cont;
|
||||
int nbytes = 0;
|
||||
int buflen = msgb_length(msg);
|
||||
|
||||
/* Make sure we have a valid RTP header, in cases where no RTP
|
||||
* header is present, we will generate one. */
|
||||
gen_rtp_header(msg, rtp_end, rtp_state);
|
||||
|
||||
do {
|
||||
/* Run transcoder */
|
||||
cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
|
||||
(char*)msgb_data(msg), &buflen,
|
||||
RTP_BUF_SIZE);
|
||||
if (cont < 0)
|
||||
break;
|
||||
|
||||
if (addr)
|
||||
mgcp_patch_and_count(endp, rtp_state, rtp_end,
|
||||
addr, msg);
|
||||
|
||||
if (amr_oa_bwe_convert_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");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (rtp_end->rfc5993_hr_convert
|
||||
&& strcmp(conn_src->end.codec->subtype_name,
|
||||
"GSM-HR-08") == 0) {
|
||||
rc = rfc5993_hr_convert(endp, msg);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"process/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)
|
||||
);
|
||||
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
forward_data(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,
|
||||
(char*)msgb_data(msg), msgb_length(msg));
|
||||
|
||||
if (len <= 0)
|
||||
return len;
|
||||
|
||||
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
|
||||
rtp_state->alt_rtp_tx_sequence++;
|
||||
|
||||
nbytes += len;
|
||||
buflen = cont;
|
||||
} while (buflen > 0);
|
||||
return nbytes;
|
||||
} else if (!trunk->omit_rtcp) {
|
||||
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)
|
||||
);
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtcp.fd,
|
||||
&rtp_end->addr,
|
||||
rtp_end->rtcp_port, (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);
|
||||
rtp_state->alt_rtp_tx_sequence++;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if the origin (addr) matches the address/port data of the RTP
|
||||
* connections. */
|
||||
static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *addr)
|
||||
@@ -1115,7 +901,7 @@ static int check_rtp_destin(struct mgcp_conn_rtp *conn)
|
||||
* and IP-address for outgoing data. */
|
||||
if (ip_is_any && conn->end.rtp_port == 0) {
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
|
||||
"destination IP-address and rtp port is (not yet) known (%s:%u)\n",
|
||||
"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);
|
||||
return -1;
|
||||
}
|
||||
@@ -1237,6 +1023,250 @@ static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! 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)
|
||||
{
|
||||
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));
|
||||
|
||||
if (is_ipv6) {
|
||||
addr->u.sin6.sin6_port = port;
|
||||
addr_len = sizeof(addr->u.sin6);
|
||||
} else {
|
||||
addr->u.sin.sin_port = port;
|
||||
addr_len = sizeof(addr->u.sin);
|
||||
}
|
||||
|
||||
return sendto(fd, buf, len, 0, &addr->u.sa, addr_len);
|
||||
}
|
||||
|
||||
/*! send RTP dummy packet (to keep NAT connection open).
|
||||
* \param[in] endp mcgp endpoint that holds the RTP connection.
|
||||
* \param[in] conn associated RTP connection.
|
||||
* \returns bytes sent, -1 on error. */
|
||||
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
int rc;
|
||||
int was_rtcp = 0;
|
||||
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(conn);
|
||||
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "sending dummy packet... %s\n",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
|
||||
/* Before we try to deliver the packet, we check if the destination
|
||||
* port and IP-Address make sense at all. If not, we will be unable
|
||||
* to deliver the packet. */
|
||||
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 (rc == -1)
|
||||
goto failed;
|
||||
|
||||
if (endp->trunk->omit_rtcp)
|
||||
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));
|
||||
|
||||
if (rc >= 0)
|
||||
return rc;
|
||||
|
||||
failed:
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"Failed to send dummy %s packet.\n",
|
||||
was_rtcp ? "RTCP" : "RTP");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! Send RTP/RTCP data to a specified destination connection.
|
||||
* \param[in] endp associated endpoint (for configuration, logging).
|
||||
* \param[in] is_rtp flag to specify if the packet is of type RTP or RTCP.
|
||||
* \param[in] spoofed source address (set to NULL to disable).
|
||||
* \param[in] buf buffer that contains the RTP/RTCP data.
|
||||
* \param[in] len length of the buffer that contains the RTP/RTCP data.
|
||||
* \param[in] conn_src associated source connection.
|
||||
* \param[in] conn_dst associated destination connection.
|
||||
* \returns 0 on success, -1 on ERROR. */
|
||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr,
|
||||
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_trunk *trunk = endp->trunk;
|
||||
struct mgcp_rtp_end *rtp_end;
|
||||
struct mgcp_rtp_state *rtp_state;
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
char *dest_name;
|
||||
int rc;
|
||||
int len;
|
||||
|
||||
OSMO_ASSERT(conn_src);
|
||||
OSMO_ASSERT(conn_dst);
|
||||
|
||||
if (is_rtp)
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering RTP packet...\n");
|
||||
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) {
|
||||
rc = mgcp_patch_pt(conn_src, conn_dst, msg);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"can not patch PT because no suitable egress codec was found.\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_inc(conn_dst, endp, RTP_DROPPED_PACKETS_CTR);
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"output disabled, drop to %s %s "
|
||||
"rtp_port:%u rtcp_port:%u\n",
|
||||
dest_name,
|
||||
osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
|
||||
ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
|
||||
);
|
||||
} else if (is_rtp) {
|
||||
int cont;
|
||||
int nbytes = 0;
|
||||
int buflen = msgb_length(msg);
|
||||
|
||||
/* Make sure we have a valid RTP header, in cases where no RTP
|
||||
* header is present, we will generate one. */
|
||||
gen_rtp_header(msg, rtp_end, rtp_state);
|
||||
|
||||
do {
|
||||
/* Run transcoder */
|
||||
cont = endp->trunk->cfg->rtp_processing_cb(endp, rtp_end, (char *)msgb_data(msg), &buflen, RTP_BUF_SIZE);
|
||||
if (cont < 0)
|
||||
break;
|
||||
|
||||
if (addr)
|
||||
mgcp_patch_and_count(endp, rtp_state, rtp_end,
|
||||
addr, msg);
|
||||
|
||||
if (amr_oa_bwe_convert_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");
|
||||
break;
|
||||
}
|
||||
} else if (rtp_end->rfc5993_hr_convert &&
|
||||
strcmp(conn_src->end.codec->subtype_name, "GSM-HR-08") == 0) {
|
||||
rc = rfc5993_hr_convert(endp, msg);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"process/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)
|
||||
);
|
||||
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
forward_data(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,
|
||||
(char *)msgb_data(msg), msgb_length(msg));
|
||||
|
||||
if (len <= 0)
|
||||
return len;
|
||||
|
||||
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
|
||||
rtp_state->alt_rtp_tx_sequence++;
|
||||
|
||||
nbytes += len;
|
||||
buflen = cont;
|
||||
} while (buflen > 0);
|
||||
return nbytes;
|
||||
} else if (!trunk->omit_rtcp) {
|
||||
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)
|
||||
);
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtcp.fd,
|
||||
&rtp_end->addr,
|
||||
rtp_end->rtcp_port, (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);
|
||||
rtp_state->alt_rtp_tx_sequence++;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -1251,6 +1281,7 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
|
||||
struct mgcp_conn *conn = conn_src->conn;
|
||||
struct mgcp_conn *conn_dst;
|
||||
struct osmo_sockaddr *from_addr = mc->from_addr;
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
|
||||
/*! NOTE: This callback function implements the endpoint specific
|
||||
* dispatch behaviour of an rtp bridge/proxy endpoint. It is assumed
|
||||
@@ -1269,19 +1300,22 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
|
||||
* 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) {
|
||||
OSMO_ASSERT(conn->u.rtp.end.addr.u.sa.sa_family == from_addr->u.sa.sa_family);
|
||||
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.addr.u.sin.sin_addr = from_addr->u.sin.sin_addr;
|
||||
conn->u.rtp.end.rtp_port = from_addr->u.sin.sin_port;
|
||||
break;
|
||||
case AF_INET6:
|
||||
conn->u.rtp.end.addr.u.sin6.sin6_addr = from_addr->u.sin6.sin6_addr;
|
||||
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);
|
||||
}
|
||||
return mgcp_send_rtp(conn_src, msg);
|
||||
}
|
||||
@@ -1331,6 +1365,7 @@ int mgcp_dispatch_e1_bridge_cb(struct msgb *msg)
|
||||
struct mgcp_conn_rtp *conn_src = mc->conn_src;
|
||||
struct mgcp_conn *conn = conn_src->conn;
|
||||
struct osmo_sockaddr *from_addr = mc->from_addr;
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
|
||||
/* Check if the connection is in loopback mode, if yes, just send the
|
||||
* incoming data back to the origin */
|
||||
@@ -1340,19 +1375,22 @@ int mgcp_dispatch_e1_bridge_cb(struct msgb *msg)
|
||||
* 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) {
|
||||
OSMO_ASSERT(conn->u.rtp.end.addr.u.sa.sa_family == from_addr->u.sa.sa_family);
|
||||
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.addr.u.sin.sin_addr = from_addr->u.sin.sin_addr;
|
||||
conn->u.rtp.end.rtp_port = from_addr->u.sin.sin_port;
|
||||
break;
|
||||
case AF_INET6:
|
||||
conn->u.rtp.end.addr.u.sin6.sin6_addr = from_addr->u.sin6.sin6_addr;
|
||||
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);
|
||||
}
|
||||
return mgcp_send_rtp(conn_src, msg);
|
||||
}
|
||||
@@ -1390,11 +1428,6 @@ void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *con
|
||||
mgcp_cleanup_rtp_bridge_cb(endp, conn);
|
||||
}
|
||||
|
||||
static bool is_dummy_msg(enum rtp_proto proto, struct msgb *msg)
|
||||
{
|
||||
return msgb_length(msg) == 1 && msgb_data(msg)[0] == MGCP_DUMMY_LOAD;
|
||||
}
|
||||
|
||||
/* Handle incoming RTP data from NET */
|
||||
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
@@ -1413,13 +1446,14 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
int ret;
|
||||
enum rtp_proto proto;
|
||||
struct osmo_rtp_msg_ctx *mc;
|
||||
struct msgb *msg = msgb_alloc(RTP_BUF_SIZE, "RTP-rx");
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
conn_src = (struct mgcp_conn_rtp *)fd->data;
|
||||
OSMO_ASSERT(conn_src);
|
||||
endp = conn_src->conn->endp;
|
||||
OSMO_ASSERT(endp);
|
||||
msg = msgb_alloc_c(endp->trunk, RTP_BUF_SIZE, "RTP-rx");
|
||||
|
||||
proto = (fd == &conn_src->end.rtp)? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
|
||||
|
||||
@@ -1445,7 +1479,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (is_dummy_msg(proto, msg)) {
|
||||
if (mgcp_is_rtp_dummy_payload(msg)) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx dummy packet (dropped)\n");
|
||||
rc = 0;
|
||||
goto out;
|
||||
@@ -1467,8 +1501,8 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
sizeof(struct sockaddr_in)));
|
||||
|
||||
/* Increment RX statistics */
|
||||
rate_ctr_inc(&conn_src->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]);
|
||||
rate_ctr_add(&conn_src->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], msgb_length(msg));
|
||||
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));
|
||||
/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */
|
||||
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
@@ -1621,7 +1655,7 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
osmo_fd_setup(&end->rtp, -1, OSMO_FD_READ, rtp_data_net, conn, 0);
|
||||
osmo_fd_setup(&end->rtcp, -1, OSMO_FD_READ, rtp_data_net, conn, 0);
|
||||
|
||||
return bind_rtp(endp->cfg, conn->end.local_addr, end, endp);
|
||||
return bind_rtp(endp->trunk->cfg, conn->end.local_addr, end, endp);
|
||||
}
|
||||
|
||||
/*! free allocated RTP and RTCP ports.
|
||||
|
@@ -116,7 +116,7 @@ static struct osmux_handle *
|
||||
osmux_handle_alloc(struct mgcp_conn_rtp *conn, struct in_addr *addr, int rem_port)
|
||||
{
|
||||
struct osmux_handle *h;
|
||||
struct mgcp_config *cfg = conn->conn->endp->cfg;
|
||||
struct mgcp_config *cfg = conn->conn->endp->trunk->cfg;
|
||||
|
||||
h = talloc_zero(osmux, struct osmux_handle);
|
||||
if (!h)
|
||||
@@ -375,7 +375,7 @@ static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
}
|
||||
|
||||
/* not any further processing dummy messages */
|
||||
if (msg->data[0] == MGCP_DUMMY_LOAD)
|
||||
if (mgcp_is_rtp_dummy_payload(msg))
|
||||
return osmux_handle_dummy(cfg, &addr, msg);
|
||||
|
||||
rem = msg->len;
|
||||
@@ -460,7 +460,7 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
||||
*/
|
||||
struct in6_addr addr_unset = {};
|
||||
static const uint32_t rtp_ssrc_winlen = UINT32_MAX / (OSMUX_CID_MAX + 1);
|
||||
uint16_t osmux_dummy = endp->cfg->osmux_dummy;
|
||||
uint16_t osmux_dummy = endp->trunk->cfg->osmux_dummy;
|
||||
|
||||
/* Check if osmux is enabled for the specified connection */
|
||||
if (conn->osmux.state != OSMUX_STATE_ACTIVATING) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -22,9 +22,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <errno.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/stat_item.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_ratectr.h>
|
||||
|
||||
static const struct rate_ctr_desc mgcp_general_ctr_desc[] = {
|
||||
@@ -58,7 +63,6 @@ static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
|
||||
[MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC] =
|
||||
{ "crcx:no_remote_conn_desc", "no opposite end specified for connection." },
|
||||
[MGCP_CRCX_FAIL_START_RTP] = { "crcx:start_rtp_failure", "failure to start RTP processing." },
|
||||
[MGCP_CRCX_FAIL_REJECTED_BY_POLICY] = { "crcx:conn_rejected", "connection rejected by policy." },
|
||||
[MGCP_CRCX_FAIL_NO_OSMUX] = { "crcx:no_osmux", "no osmux offered by peer." },
|
||||
[MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS] = { "crcx:conn_opt", "connection options invalid." },
|
||||
[MGCP_CRCX_FAIL_CODEC_NEGOTIATION] = { "crcx:codec_nego", "codec negotiation failure." },
|
||||
@@ -90,8 +94,6 @@ static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
|
||||
[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC] =
|
||||
{ "mdcx:no_remote_conn_desc", "no opposite end specified for connection." },
|
||||
[MGCP_MDCX_FAIL_START_RTP] = { "mdcx:start_rtp_failure", "failure to start RTP processing." },
|
||||
[MGCP_MDCX_FAIL_REJECTED_BY_POLICY] = { "mdcx:conn_rejected", "connection rejected by policy." },
|
||||
[MGCP_MDCX_DEFERRED_BY_POLICY] = { "mdcx:conn_deferred", "connection deferred by policy." },
|
||||
[MGCP_MDCX_FAIL_AVAIL] = { "mdcx:unavailable", "endpoint unavailable." },
|
||||
};
|
||||
|
||||
@@ -105,15 +107,12 @@ const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
|
||||
|
||||
static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = {
|
||||
[MGCP_DLCX_SUCCESS] = { "dlcx:success", "DLCX command processed successfully." },
|
||||
[MGCP_DLCX_FAIL_WILDCARD] = { "dlcx:wildcard", "wildcard names in DLCX commands are unsupported." },
|
||||
[MGCP_DLCX_FAIL_NO_CONN] = { "dlcx:no_conn", "endpoint specified in DLCX command has no active connections." },
|
||||
[MGCP_DLCX_FAIL_INVALID_CALLID] =
|
||||
{ "dlcx:callid", "CallId specified in DLCX command mismatches endpoint's CallId ." },
|
||||
[MGCP_DLCX_FAIL_INVALID_CONNID] =
|
||||
{ "dlcx:connid", "connection ID specified in DLCX command does not exist on endpoint." },
|
||||
[MGCP_DLCX_FAIL_UNHANDLED_PARAM] = { "dlcx:unhandled_param", "unhandled parameter in DLCX command." },
|
||||
[MGCP_DLCX_FAIL_REJECTED_BY_POLICY] = { "dlcx:rejected", "connection deletion rejected by policy." },
|
||||
[MGCP_DLCX_DEFERRED_BY_POLICY] = { "dlcx:deferred", "connection deletion deferred by policy." },
|
||||
[MGCP_DLCX_FAIL_AVAIL] = { "dlcx:unavailable", "endpoint unavailable." },
|
||||
};
|
||||
|
||||
@@ -126,9 +125,9 @@ const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc e1_rate_ctr_desc[] = {
|
||||
[E1_I460_TRAU_RX_FAIL_CTR] = {"e1:rx_fail", "Inbound I.460 TRAU failures."},
|
||||
[E1_I460_TRAU_TX_FAIL_CTR] = {"e1:tx_fail", "Outbound I.460 TRAU failures."},
|
||||
[E1_I460_TRAU_MUX_EMPTY_CTR] = {"e1:i460", "Outbound I.460 MUX queue empty."}
|
||||
[E1_I460_TRAU_RX_FAIL_CTR] = { "e1:rx_fail", "Inbound I.460 TRAU failures." },
|
||||
[E1_I460_TRAU_TX_FAIL_CTR] = { "e1:tx_fail", "Outbound I.460 TRAU failures." },
|
||||
[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 = {
|
||||
@@ -147,83 +146,180 @@ const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
|
||||
.ctr_desc = all_rtp_conn_rate_ctr_desc
|
||||
};
|
||||
|
||||
static int free_rate_counter_group(struct rate_ctr_group *rate_ctr_group)
|
||||
/*! allocate global rate counters
|
||||
* (called once at startup).
|
||||
* \param[in] cfg mgw configuration for which the rate counters are allocated.
|
||||
* \returns 0 on success, -EINVAL on failure. */
|
||||
int mgcp_ratectr_global_alloc(struct mgcp_config *cfg)
|
||||
{
|
||||
rate_ctr_group_free(rate_ctr_group);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! allocate global rate counters into a given rate counter struct
|
||||
* (called once at startup)
|
||||
* \param[in] ctx talloc context.
|
||||
* \param[out] ratectr struct that holds the counters
|
||||
* \returns 0 on success, -EINVAL on failure */
|
||||
int mgcp_ratectr_global_alloc(void *ctx, struct mgcp_ratectr_global *ratectr)
|
||||
{
|
||||
/* FIXME: Each new rate counter group requires a unique index. At the
|
||||
* moment we generate an index using a counter, but perhaps there is
|
||||
* a better way of assigning indices? */
|
||||
static unsigned int general_rate_ctr_index = 0;
|
||||
struct mgcp_ratectr_global *ratectr = &cfg->ratectr;
|
||||
static atomic_uint general_rate_ctr_index = 0;
|
||||
char ctr_name[512];
|
||||
|
||||
if (ratectr->mgcp_general_ctr_group == NULL) {
|
||||
ratectr->mgcp_general_ctr_group =
|
||||
rate_ctr_group_alloc(ctx, &mgcp_general_ctr_group_desc, general_rate_ctr_index);
|
||||
rate_ctr_group_alloc(cfg, &mgcp_general_ctr_group_desc, general_rate_ctr_index++);
|
||||
if (!ratectr->mgcp_general_ctr_group)
|
||||
return -EINVAL;
|
||||
talloc_set_destructor(ratectr->mgcp_general_ctr_group, free_rate_counter_group);
|
||||
general_rate_ctr_index++;
|
||||
snprintf(ctr_name, sizeof(ctr_name), "%s:general", cfg->domain);
|
||||
rate_ctr_group_set_name(ratectr->mgcp_general_ctr_group, ctr_name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! allocate trunk specific rate counters into a given rate counter struct
|
||||
* (called once on trunk initialization)
|
||||
* \param[in] ctx talloc context.
|
||||
* \param[out] ratectr struct that holds the counters
|
||||
* \returns 0 on success, -EINVAL on failure */
|
||||
int mgcp_ratectr_trunk_alloc(void *ctx, struct mgcp_ratectr_trunk *ratectr)
|
||||
/*! free global rate counters
|
||||
* (called once at process shutdown).
|
||||
* \param[in] cfg mgw configuration for which the rate counters are allocated. */
|
||||
void mgcp_ratectr_global_free(struct mgcp_config *cfg)
|
||||
{
|
||||
/* FIXME: see comment in mgcp_ratectr_global_alloc() */
|
||||
static unsigned int crcx_rate_ctr_index = 0;
|
||||
static unsigned int mdcx_rate_ctr_index = 0;
|
||||
static unsigned int dlcx_rate_ctr_index = 0;
|
||||
static unsigned int all_rtp_conn_rate_ctr_index = 0;
|
||||
struct mgcp_ratectr_global *ratectr = &cfg->ratectr;
|
||||
|
||||
if (ratectr->mgcp_general_ctr_group) {
|
||||
rate_ctr_group_free(ratectr->mgcp_general_ctr_group);
|
||||
ratectr->mgcp_general_ctr_group = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*! allocate trunk specific rate counters
|
||||
* (called once on trunk initialization).
|
||||
* \param[in] trunk mgw trunk for which the rate counters are allocated.
|
||||
* \returns 0 on success, -EINVAL on failure */
|
||||
int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk)
|
||||
{
|
||||
struct mgcp_ratectr_trunk *ratectr = &trunk->ratectr;
|
||||
static atomic_uint crcx_rate_ctr_index = 0;
|
||||
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;
|
||||
char ctr_name[256];
|
||||
|
||||
if (ratectr->mgcp_crcx_ctr_group == NULL) {
|
||||
ratectr->mgcp_crcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index);
|
||||
ratectr->mgcp_crcx_ctr_group =
|
||||
rate_ctr_group_alloc(trunk, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index++);
|
||||
if (!ratectr->mgcp_crcx_ctr_group)
|
||||
return -EINVAL;
|
||||
talloc_set_destructor(ratectr->mgcp_crcx_ctr_group, free_rate_counter_group);
|
||||
crcx_rate_ctr_index++;
|
||||
snprintf(ctr_name, sizeof(ctr_name), "%s-%u:crcx", mgcp_trunk_type_strs_str(trunk->trunk_type),
|
||||
trunk->trunk_nr);
|
||||
rate_ctr_group_set_name(ratectr->mgcp_crcx_ctr_group, ctr_name);
|
||||
}
|
||||
if (ratectr->mgcp_mdcx_ctr_group == NULL) {
|
||||
ratectr->mgcp_mdcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index);
|
||||
ratectr->mgcp_mdcx_ctr_group =
|
||||
rate_ctr_group_alloc(trunk, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index++);
|
||||
if (!ratectr->mgcp_mdcx_ctr_group)
|
||||
return -EINVAL;
|
||||
talloc_set_destructor(ratectr->mgcp_mdcx_ctr_group, free_rate_counter_group);
|
||||
mdcx_rate_ctr_index++;
|
||||
snprintf(ctr_name, sizeof(ctr_name), "%s-%u:mdcx", mgcp_trunk_type_strs_str(trunk->trunk_type),
|
||||
trunk->trunk_nr);
|
||||
rate_ctr_group_set_name(ratectr->mgcp_mdcx_ctr_group, ctr_name);
|
||||
}
|
||||
if (ratectr->mgcp_dlcx_ctr_group == NULL) {
|
||||
ratectr->mgcp_dlcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index);
|
||||
ratectr->mgcp_dlcx_ctr_group =
|
||||
rate_ctr_group_alloc(trunk, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index++);
|
||||
if (!ratectr->mgcp_dlcx_ctr_group)
|
||||
return -EINVAL;
|
||||
talloc_set_destructor(ratectr->mgcp_dlcx_ctr_group, free_rate_counter_group);
|
||||
dlcx_rate_ctr_index++;
|
||||
snprintf(ctr_name, sizeof(ctr_name), "%s-%u:dlcx", mgcp_trunk_type_strs_str(trunk->trunk_type),
|
||||
trunk->trunk_nr);
|
||||
rate_ctr_group_set_name(ratectr->mgcp_dlcx_ctr_group, ctr_name);
|
||||
}
|
||||
if (ratectr->all_rtp_conn_stats == NULL) {
|
||||
ratectr->all_rtp_conn_stats = rate_ctr_group_alloc(ctx, &all_rtp_conn_rate_ctr_group_desc,
|
||||
all_rtp_conn_rate_ctr_index);
|
||||
ratectr->all_rtp_conn_stats = rate_ctr_group_alloc(trunk, &all_rtp_conn_rate_ctr_group_desc,
|
||||
all_rtp_conn_rate_ctr_index++);
|
||||
if (!ratectr->all_rtp_conn_stats)
|
||||
return -EINVAL;
|
||||
talloc_set_destructor(ratectr->all_rtp_conn_stats, free_rate_counter_group);
|
||||
all_rtp_conn_rate_ctr_index++;
|
||||
snprintf(ctr_name, sizeof(ctr_name), "%s-%u:rtp_conn", mgcp_trunk_type_strs_str(trunk->trunk_type),
|
||||
trunk->trunk_nr);
|
||||
rate_ctr_group_set_name(ratectr->all_rtp_conn_stats, ctr_name);
|
||||
}
|
||||
if (ratectr->e1_stats == NULL) {
|
||||
ratectr->e1_stats = rate_ctr_group_alloc(ctx, &e1_rate_ctr_group_desc, mdcx_rate_ctr_index);
|
||||
|
||||
/* E1 specific */
|
||||
if (trunk->trunk_type == MGCP_TRUNK_E1 && ratectr->e1_stats == NULL) {
|
||||
ratectr->e1_stats = rate_ctr_group_alloc(trunk, &e1_rate_ctr_group_desc, mdcx_rate_ctr_index++);
|
||||
if (!ratectr->e1_stats)
|
||||
return -EINVAL;
|
||||
talloc_set_destructor(ratectr->e1_stats, free_rate_counter_group);
|
||||
mdcx_rate_ctr_index++;
|
||||
snprintf(ctr_name, sizeof(ctr_name), "%s-%u:e1", mgcp_trunk_type_strs_str(trunk->trunk_type),
|
||||
trunk->trunk_nr);
|
||||
rate_ctr_group_set_name(ratectr->e1_stats, ctr_name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! free trunk specific rate counters
|
||||
* (called once when trunk is freed).
|
||||
* \param[in] trunk mgw trunk on which the rate counters are allocated. */
|
||||
void mgcp_ratectr_trunk_free(struct mgcp_trunk *trunk)
|
||||
{
|
||||
struct mgcp_ratectr_trunk *ratectr = &trunk->ratectr;
|
||||
|
||||
if (ratectr->mgcp_crcx_ctr_group) {
|
||||
rate_ctr_group_free(ratectr->mgcp_crcx_ctr_group);
|
||||
ratectr->mgcp_crcx_ctr_group = NULL;
|
||||
}
|
||||
if (ratectr->mgcp_mdcx_ctr_group) {
|
||||
rate_ctr_group_free(ratectr->mgcp_mdcx_ctr_group);
|
||||
ratectr->mgcp_mdcx_ctr_group = NULL;
|
||||
}
|
||||
if (ratectr->mgcp_dlcx_ctr_group) {
|
||||
rate_ctr_group_free(ratectr->mgcp_dlcx_ctr_group);
|
||||
ratectr->mgcp_dlcx_ctr_group = NULL;
|
||||
}
|
||||
if (ratectr->all_rtp_conn_stats) {
|
||||
rate_ctr_group_free(ratectr->all_rtp_conn_stats);
|
||||
ratectr->all_rtp_conn_stats = NULL;
|
||||
}
|
||||
|
||||
/* E1 specific */
|
||||
if (ratectr->e1_stats) {
|
||||
rate_ctr_group_free(ratectr->e1_stats);
|
||||
ratectr->e1_stats = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const struct osmo_stat_item_desc trunk_stat_desc[] = {
|
||||
[TRUNK_STAT_ENDPOINTS_TOTAL] = { "endpoints:total",
|
||||
"Number of endpoints that exist on the trunk",
|
||||
"", 60, 0 },
|
||||
[TRUNK_STAT_ENDPOINTS_USED] = { "endpoints:used",
|
||||
"Number of endpoints in use",
|
||||
"", 60, 0 },
|
||||
};
|
||||
|
||||
const struct osmo_stat_item_group_desc trunk_statg_desc = {
|
||||
.group_name_prefix = "trunk",
|
||||
.group_description = "mgw trunk",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_items = ARRAY_SIZE(trunk_stat_desc),
|
||||
.item_desc = trunk_stat_desc,
|
||||
};
|
||||
|
||||
/*! allocate trunk specific stat items
|
||||
* (called once on trunk initialization).
|
||||
* \param[in] trunk for which the stat items are allocated.
|
||||
* \returns 0 on success, -EINVAL on failure. */
|
||||
int mgcp_stat_trunk_alloc(struct mgcp_trunk *trunk)
|
||||
{
|
||||
struct mgcp_stat_trunk *stats = &trunk->stats;
|
||||
static unsigned int common_stat_index = 0;
|
||||
char stat_name[256];
|
||||
|
||||
stats->common = osmo_stat_item_group_alloc(trunk, &trunk_statg_desc, common_stat_index);
|
||||
if (!stats->common)
|
||||
return -EINVAL;
|
||||
snprintf(stat_name, sizeof(stat_name), "%s-%u:common", mgcp_trunk_type_strs_str(trunk->trunk_type),
|
||||
trunk->trunk_nr);
|
||||
osmo_stat_item_group_set_name(stats->common, stat_name);
|
||||
common_stat_index++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! free trunk specific stat items
|
||||
* (called once when trunk is freed).
|
||||
* \param[in] trunk on which the stat items are allocated. */
|
||||
void mgcp_stat_trunk_free(struct mgcp_trunk *trunk)
|
||||
{
|
||||
struct mgcp_stat_trunk *stats = &trunk->stats;
|
||||
|
||||
if (stats->common) {
|
||||
osmo_stat_item_group_free(stats->common);
|
||||
stats->common = NULL;
|
||||
}
|
||||
}
|
||||
|
@@ -394,11 +394,13 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
codecs_used = rc;
|
||||
break;
|
||||
case 'c':
|
||||
if (audio_ip_from_sdp(&rtp->addr, line) < 0)
|
||||
if (audio_ip_from_sdp(&rtp->addr, line) < 0) {
|
||||
talloc_free(tmp_ctx);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (p->endp)
|
||||
if (endp)
|
||||
/* TODO: Check spec: We used the bare endpoint number before,
|
||||
* now we use the endpoint name as a whole? Is this allowed? */
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
@@ -437,7 +439,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
for (i = 0; i < codecs_used; i++) {
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
|
||||
rtp->codecs[i].payload_type,
|
||||
rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown");
|
||||
strlen(rtp->codecs[i].subtype_name) ? rtp->codecs[i].subtype_name : "unknown");
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, " ");
|
||||
}
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, "\n");
|
||||
@@ -574,7 +576,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
OSMO_ASSERT(addr);
|
||||
|
||||
/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
|
||||
endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
|
||||
endp->trunk->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
|
||||
&codec, &fmtp_extra,
|
||||
(struct mgcp_conn_rtp *)conn);
|
||||
|
||||
@@ -599,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->cfg->osmux_port;
|
||||
local_port = endp->trunk->cfg->osmux_port;
|
||||
else
|
||||
local_port = conn->end.local_port;
|
||||
rc = add_audio(sdp, payload_types, 1, local_port);
|
||||
|
@@ -28,12 +28,18 @@
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_stat.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
|
||||
/* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
|
||||
#if defined(__has_attribute)
|
||||
#if __has_attribute(no_sanitize)
|
||||
__attribute__((no_sanitize("undefined")))
|
||||
#endif
|
||||
#endif
|
||||
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 = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
|
||||
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
|
||||
|
||||
*expected = state->stats.cycles + state->stats.max_seq;
|
||||
*expected = *expected - state->stats.base_seq + 1;
|
||||
@@ -74,10 +80,10 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
|
||||
int ploss;
|
||||
int nchars;
|
||||
|
||||
struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
|
||||
struct rate_ctr *octets_rx = &conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR];
|
||||
struct rate_ctr *packets_tx = &conn->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR];
|
||||
struct rate_ctr *octets_tx = &conn->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR];
|
||||
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);
|
||||
|
||||
calc_loss(conn, &expected, &ploss);
|
||||
jitter = calc_jitter(&conn->state);
|
||||
@@ -93,7 +99,7 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
|
||||
str += nchars;
|
||||
str_len -= nchars;
|
||||
|
||||
if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) {
|
||||
if (conn->conn->endp->trunk->cfg->osmux != OSMUX_USAGE_OFF) {
|
||||
/* Error Counter */
|
||||
nchars = snprintf(str, str_len,
|
||||
"\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64,
|
||||
|
@@ -27,14 +27,31 @@
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_e1.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/core/stat_item.h>
|
||||
|
||||
/*! allocate trunk and add it (if required) to the trunk list.
|
||||
const struct value_string mgcp_trunk_type_strs[] = {
|
||||
{ MGCP_TRUNK_VIRTUAL, "virtual" },
|
||||
{ MGCP_TRUNK_E1, "e1" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/* Free trunk, this function is automatically called by talloc_free when the trunk is freed. It does not free the
|
||||
* endpoints on the trunk, this must be done separately before freeing the trunk. */
|
||||
static int trunk_free_talloc_destructor(struct mgcp_trunk *trunk)
|
||||
{
|
||||
llist_del(&trunk->entry);
|
||||
mgcp_ratectr_trunk_free(trunk);
|
||||
mgcp_stat_trunk_free(trunk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! allocate trunk and add it to the trunk list.
|
||||
* (called once at startup by VTY).
|
||||
* \param[in] cfg mgcp configuration.
|
||||
* \param[in] ttype trunk type.
|
||||
* \param[in] nr trunk number.
|
||||
* \returns pointer to allocated trunk, NULL on failure. */
|
||||
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr)
|
||||
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, unsigned int nr)
|
||||
{
|
||||
struct mgcp_trunk *trunk;
|
||||
|
||||
@@ -57,7 +74,9 @@ struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_typ
|
||||
|
||||
llist_add_tail(&trunk->entry, &cfg->trunks);
|
||||
|
||||
mgcp_ratectr_trunk_alloc(cfg, &trunk->ratectr);
|
||||
mgcp_ratectr_trunk_alloc(trunk);
|
||||
mgcp_stat_trunk_alloc(trunk);
|
||||
talloc_set_destructor(trunk, trunk_free_talloc_destructor);
|
||||
|
||||
return trunk;
|
||||
}
|
||||
@@ -104,7 +123,7 @@ int mgcp_trunk_alloc_endpts(struct mgcp_trunk *trunk)
|
||||
OSMO_ASSERT(number_endpoints < 65534);
|
||||
|
||||
/* allocate pointer array for the endpoints */
|
||||
trunk->endpoints = talloc_zero_array(trunk->cfg, struct mgcp_endpoint*,
|
||||
trunk->endpoints = talloc_zero_array(trunk, struct mgcp_endpoint*,
|
||||
number_endpoints);
|
||||
if (!trunk->endpoints)
|
||||
return -1;
|
||||
@@ -121,7 +140,8 @@ int mgcp_trunk_alloc_endpts(struct mgcp_trunk *trunk)
|
||||
|
||||
/* make the endpoints we just created available to the MGW code */
|
||||
trunk->number_endpoints = number_endpoints;
|
||||
|
||||
osmo_stat_item_set(osmo_stat_item_group_get_item(trunk->stats.common, TRUNK_STAT_ENDPOINTS_TOTAL),
|
||||
trunk->number_endpoints);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -165,7 +185,7 @@ int mgcp_trunk_equip(struct mgcp_trunk *trunk)
|
||||
* \param[in] ttype trunk type.
|
||||
* \param[in] nr trunk number.
|
||||
* \returns pointer to trunk configuration, NULL on error. */
|
||||
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr)
|
||||
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, unsigned int nr)
|
||||
{
|
||||
struct mgcp_trunk *trunk;
|
||||
|
||||
@@ -178,9 +198,9 @@ struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_tr
|
||||
}
|
||||
|
||||
/* Made public for unit-testing, do not use from outside this file */
|
||||
int e1_trunk_nr_from_epname(const char *epname)
|
||||
int e1_trunk_nr_from_epname(unsigned int *trunk_nr, const char *epname)
|
||||
{
|
||||
unsigned long int trunk_nr;
|
||||
unsigned long trunk_nr_temp;
|
||||
size_t prefix_len;
|
||||
char *str_trunk_nr_end;
|
||||
|
||||
@@ -189,13 +209,41 @@ int e1_trunk_nr_from_epname(const char *epname)
|
||||
return -EINVAL;
|
||||
|
||||
errno = 0;
|
||||
trunk_nr = strtoul(epname + prefix_len, &str_trunk_nr_end, 10);
|
||||
if (errno == ERANGE || trunk_nr > 64
|
||||
trunk_nr_temp = strtoul(epname + prefix_len, &str_trunk_nr_end, 10);
|
||||
if (errno == ERANGE || trunk_nr_temp > 64
|
||||
|| epname + prefix_len == str_trunk_nr_end
|
||||
|| str_trunk_nr_end[0] != '/')
|
||||
return -EINVAL;
|
||||
else
|
||||
return trunk_nr;
|
||||
else {
|
||||
*trunk_nr = (unsigned int)trunk_nr_temp;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the domain name, which is supplied with the endpoint name
|
||||
* matches the configuration. */
|
||||
static int check_domain_name(const char *epname, const struct mgcp_config *cfg)
|
||||
{
|
||||
char *domain_to_check;
|
||||
|
||||
domain_to_check = strstr(epname, "@");
|
||||
if (!domain_to_check) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "missing domain name in endpoint name \"%s\", expecting \"%s\"\n",
|
||||
epname, cfg->domain);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Accept any domain if configured as "*" */
|
||||
if (!strcmp(cfg->domain, "*"))
|
||||
return 0;
|
||||
|
||||
if (strcmp(domain_to_check+1, cfg->domain) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "wrong domain name in endpoint name \"%s\", expecting \"%s\"\n",
|
||||
epname, cfg->domain);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Find a trunk by the trunk prefix in the endpoint name.
|
||||
@@ -206,18 +254,23 @@ struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char
|
||||
{
|
||||
size_t prefix_len;
|
||||
char epname_lc[MGCP_ENDPOINT_MAXLEN];
|
||||
int trunk_nr;
|
||||
unsigned int trunk_nr;
|
||||
int rc;
|
||||
|
||||
osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
|
||||
epname = epname_lc;
|
||||
|
||||
/* All endpoint names require a domain as suffix */
|
||||
if (check_domain_name(epname, cfg))
|
||||
return NULL;
|
||||
|
||||
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
|
||||
if (strncmp(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, prefix_len) == 0) {
|
||||
return mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
}
|
||||
|
||||
trunk_nr = e1_trunk_nr_from_epname(epname);
|
||||
if (trunk_nr >= 0)
|
||||
rc = e1_trunk_nr_from_epname(&trunk_nr, epname);
|
||||
if (rc == 0)
|
||||
return mgcp_trunk_by_num(cfg, MGCP_TRUNK_E1, trunk_nr);
|
||||
|
||||
/* Earlier versions of osmo-mgw were accepting endpoint names
|
||||
|
@@ -65,17 +65,17 @@ static int config_write_mgcp(struct vty *vty)
|
||||
|
||||
vty_out(vty, "mgcp%s", VTY_NEWLINE);
|
||||
vty_out(vty, " domain %s%s", g_cfg->domain, VTY_NEWLINE);
|
||||
if (g_cfg->local_ip)
|
||||
if (strlen(g_cfg->local_ip))
|
||||
vty_out(vty, " local ip %s%s", g_cfg->local_ip, VTY_NEWLINE);
|
||||
vty_out(vty, " bind ip %s%s", g_cfg->source_addr, VTY_NEWLINE);
|
||||
vty_out(vty, " bind port %u%s", g_cfg->source_port, VTY_NEWLINE);
|
||||
vty_out(vty, " rtp port-range %u %u%s",
|
||||
g_cfg->net_ports.range_start, g_cfg->net_ports.range_end,
|
||||
VTY_NEWLINE);
|
||||
if (g_cfg->net_ports.bind_addr_v4)
|
||||
if (strlen(g_cfg->net_ports.bind_addr_v4))
|
||||
vty_out(vty, " rtp bind-ip %s%s",
|
||||
g_cfg->net_ports.bind_addr_v4, VTY_NEWLINE);
|
||||
if (g_cfg->net_ports.bind_addr_v6)
|
||||
if (strlen(g_cfg->net_ports.bind_addr_v6))
|
||||
vty_out(vty, " rtp bind-ip-v6 %s%s",
|
||||
g_cfg->net_ports.bind_addr_v6, VTY_NEWLINE);
|
||||
if (g_cfg->net_ports.bind_addr_probe)
|
||||
@@ -122,7 +122,7 @@ static int config_write_mgcp(struct vty *vty)
|
||||
trunk->v.vty_number_endpoints, VTY_NEWLINE);
|
||||
vty_out(vty, " %sallow-transcoding%s",
|
||||
trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE);
|
||||
if (g_cfg->call_agent_addr)
|
||||
if (strlen(g_cfg->call_agent_addr))
|
||||
vty_out(vty, " call-agent ip %s%s", g_cfg->call_agent_addr,
|
||||
VTY_NEWLINE);
|
||||
if (g_cfg->force_ptime > 0)
|
||||
@@ -169,11 +169,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 = &conn->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR];
|
||||
tx_bytes = &conn->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR];
|
||||
rx_packets = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
|
||||
rx_bytes = &conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR];
|
||||
dropped_packets = &conn->rate_ctr_group->ctr[RTP_DROPPED_PACKETS_CTR];
|
||||
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);
|
||||
|
||||
vty_out(vty,
|
||||
" Packets Sent: %" PRIu64 " (%" PRIu64 " bytes total)%s"
|
||||
@@ -200,11 +200,11 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
|
||||
}
|
||||
|
||||
static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp,
|
||||
int trunk_nr, enum mgcp_trunk_type trunk_type, int show_stats)
|
||||
unsigned int trunk_nr, enum mgcp_trunk_type trunk_type, int show_stats)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
vty_out(vty, "%s trunk %d endpoint %s:%s",
|
||||
vty_out(vty, "%s trunk %u endpoint %s:%s",
|
||||
trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1", trunk_nr, endp->name, VTY_NEWLINE);
|
||||
vty_out(vty, " Availability: %s%s",
|
||||
mgcp_endp_avail(endp) ? "available" : "not in service", VTY_NEWLINE);
|
||||
@@ -218,7 +218,7 @@ static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp,
|
||||
vty_out(vty, " CONN: %s%s", mgcp_conn_dump(conn), VTY_NEWLINE);
|
||||
|
||||
if (show_stats) {
|
||||
if (endp->cfg->conn_timeout) {
|
||||
if (endp->trunk->cfg->conn_timeout) {
|
||||
struct timeval remaining;
|
||||
osmo_timer_remaining(&conn->watchdog, NULL, &remaining);
|
||||
vty_out(vty, " Currently remaining timeout (seconds): %d.%06d%s",
|
||||
@@ -301,9 +301,10 @@ static void dump_ratectr_trunk(struct vty *vty, struct mgcp_trunk *trunk)
|
||||
}
|
||||
|
||||
|
||||
static void dump_trunk(struct vty *vty, struct mgcp_trunk *trunk, int show_stats)
|
||||
static void dump_trunk(struct vty *vty, struct mgcp_trunk *trunk, int show_stats, int active_only)
|
||||
{
|
||||
int i;
|
||||
int active_count = 0;
|
||||
|
||||
vty_out(vty, "%s trunk %d with %d endpoints:%s",
|
||||
trunk->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1",
|
||||
@@ -316,29 +317,30 @@ static void dump_trunk(struct vty *vty, struct mgcp_trunk *trunk, int show_stats
|
||||
|
||||
for (i = 0; i < trunk->number_endpoints; ++i) {
|
||||
struct mgcp_endpoint *endp = trunk->endpoints[i];
|
||||
dump_endpoint(vty, endp, trunk->trunk_nr, trunk->trunk_type,
|
||||
show_stats);
|
||||
if (i < trunk->number_endpoints - 1)
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
if (!active_only || !llist_empty(&endp->conns)) {
|
||||
dump_endpoint(vty, endp, trunk->trunk_nr, trunk->trunk_type,
|
||||
show_stats);
|
||||
if (i < trunk->number_endpoints - 1)
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
}
|
||||
if (!llist_empty(&endp->conns))
|
||||
active_count++;
|
||||
}
|
||||
|
||||
if (active_count == 0)
|
||||
vty_out(vty, "No endpoints in use.%s", VTY_NEWLINE);
|
||||
|
||||
if (show_stats)
|
||||
dump_ratectr_trunk(vty, trunk);
|
||||
}
|
||||
|
||||
#define SHOW_MGCP_STR "Display information about the MGCP Media Gateway\n"
|
||||
|
||||
DEFUN(show_mcgp, show_mgcp_cmd,
|
||||
"show mgcp [stats]",
|
||||
SHOW_STR
|
||||
SHOW_MGCP_STR
|
||||
"Include Statistics\n")
|
||||
static int mgcp_show(struct vty *vty, int argc, const char **argv,
|
||||
int show_stats, int active_only)
|
||||
{
|
||||
struct mgcp_trunk *trunk;
|
||||
int show_stats = argc >= 1;
|
||||
|
||||
llist_for_each_entry(trunk, &g_cfg->trunks, entry)
|
||||
dump_trunk(vty, trunk, show_stats);
|
||||
dump_trunk(vty, trunk, show_stats, active_only);
|
||||
|
||||
if (g_cfg->osmux)
|
||||
vty_out(vty, "Osmux used CID: %d%s", osmux_cid_pool_count_used(),
|
||||
@@ -350,6 +352,27 @@ DEFUN(show_mcgp, show_mgcp_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define SHOW_MGCP_STR "Display information about the MGCP Media Gateway\n"
|
||||
|
||||
DEFUN(show_mgcp, show_mgcp_cmd,
|
||||
"show mgcp [stats]",
|
||||
SHOW_STR
|
||||
SHOW_MGCP_STR
|
||||
"Include statistics\n")
|
||||
{
|
||||
int show_stats = argc >= 1;
|
||||
return mgcp_show(vty, argc, argv, show_stats, 0);
|
||||
}
|
||||
|
||||
DEFUN(show_mgcp_active, show_mgcp_active_cmd,
|
||||
"show mgcp active",
|
||||
SHOW_STR
|
||||
SHOW_MGCP_STR
|
||||
"Show only endpoints with active connections\n")
|
||||
{
|
||||
return mgcp_show(vty, argc, argv, 0, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
dump_mgcp_endpoint(struct vty *vty, struct mgcp_trunk *trunk, const char *epname)
|
||||
{
|
||||
@@ -359,7 +382,7 @@ dump_mgcp_endpoint(struct vty *vty, struct mgcp_trunk *trunk, const char *epname
|
||||
/* If a trunk is given, search on that specific trunk only */
|
||||
endp = mgcp_endp_by_name_trunk(NULL, epname, trunk);
|
||||
if (!endp) {
|
||||
vty_out(vty, "endpoint %s not configured on trunk %d%s", epname, trunk->trunk_nr, VTY_NEWLINE);
|
||||
vty_out(vty, "endpoint %s not configured on trunk %u%s", epname, trunk->trunk_nr, VTY_NEWLINE);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@@ -420,7 +443,7 @@ DEFUN_USRATTR(cfg_mgcp_local_ip,
|
||||
"IPv4 Address to use in SDP record\n"
|
||||
"IPv6 Address to use in SDP record\n")
|
||||
{
|
||||
osmo_talloc_replace_string(g_cfg, &g_cfg->local_ip, argv[0]);
|
||||
osmo_strlcpy(g_cfg->local_ip, argv[0], sizeof(g_cfg->local_ip));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -432,7 +455,7 @@ DEFUN(cfg_mgcp_bind_ip,
|
||||
"IPv4 Address to bind to\n"
|
||||
"IPv6 Address to bind to\n")
|
||||
{
|
||||
osmo_talloc_replace_string(g_cfg, &g_cfg->source_addr, argv[0]);
|
||||
osmo_strlcpy(g_cfg->source_addr, argv[0], sizeof(g_cfg->source_addr));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -510,7 +533,7 @@ DEFUN_USRATTR(cfg_mgcp_rtp_bind_ip,
|
||||
RTP_STR "Bind endpoints facing the Network\n"
|
||||
"IPv4 Address to bind to\n")
|
||||
{
|
||||
osmo_talloc_replace_string(g_cfg, &g_cfg->net_ports.bind_addr_v4, argv[0]);
|
||||
osmo_strlcpy(g_cfg->net_ports.bind_addr_v4, argv[0], sizeof(g_cfg->net_ports.bind_addr_v4));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgcp_rtp_bind_ip,
|
||||
@@ -525,8 +548,7 @@ DEFUN_USRATTR(cfg_mgcp_rtp_no_bind_ip,
|
||||
NO_STR RTP_STR "Bind endpoints facing the Network\n"
|
||||
"Address to bind to\n")
|
||||
{
|
||||
talloc_free(g_cfg->net_ports.bind_addr_v4);
|
||||
g_cfg->net_ports.bind_addr_v4 = NULL;
|
||||
osmo_strlcpy(g_cfg->net_ports.bind_addr_v4, "", sizeof(g_cfg->net_ports.bind_addr_v4));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgcp_rtp_no_bind_ip,
|
||||
@@ -542,7 +564,7 @@ DEFUN_USRATTR(cfg_mgcp_rtp_bind_ip_v6,
|
||||
RTP_STR "Bind endpoints facing the Network\n"
|
||||
"IPv6 Address to bind to\n")
|
||||
{
|
||||
osmo_talloc_replace_string(g_cfg, &g_cfg->net_ports.bind_addr_v6, argv[0]);
|
||||
osmo_strlcpy(g_cfg->net_ports.bind_addr_v6, argv[0], sizeof(g_cfg->net_ports.bind_addr_v6));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -553,8 +575,7 @@ DEFUN_USRATTR(cfg_mgcp_rtp_no_bind_ip_v6,
|
||||
NO_STR RTP_STR "Bind endpoints facing the Network\n"
|
||||
"Address to bind to\n")
|
||||
{
|
||||
talloc_free(g_cfg->net_ports.bind_addr_v6);
|
||||
g_cfg->net_ports.bind_addr_v6 = NULL;
|
||||
osmo_strlcpy(g_cfg->net_ports.bind_addr_v6, "", sizeof(g_cfg->net_ports.bind_addr_v6));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -927,7 +948,7 @@ DEFUN(cfg_mgcp_agent_addr,
|
||||
"IPv4 Address of the call agent\n"
|
||||
"IPv6 Address of the call agent\n")
|
||||
{
|
||||
osmo_talloc_replace_string(g_cfg, &g_cfg->call_agent_addr, argv[0]);
|
||||
osmo_strlcpy(g_cfg->call_agent_addr, argv[0], sizeof(g_cfg->call_agent_addr));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -940,7 +961,7 @@ DEFUN(cfg_mgcp_trunk, cfg_mgcp_trunk_cmd,
|
||||
"trunk <0-64>", "Configure a SS7 trunk\n" "Trunk Nr\n")
|
||||
{
|
||||
struct mgcp_trunk *trunk;
|
||||
int index = atoi(argv[0]);
|
||||
unsigned int index = atoi(argv[0]);
|
||||
|
||||
trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_E1, index);
|
||||
if (!trunk) {
|
||||
@@ -972,7 +993,7 @@ static int config_write_trunk(struct vty *vty)
|
||||
&& trunk->trunk_nr == MGCP_VIRT_TRUNK_ID)
|
||||
continue;
|
||||
|
||||
vty_out(vty, " trunk %d%s", trunk->trunk_nr, VTY_NEWLINE);
|
||||
vty_out(vty, " trunk %u%s", trunk->trunk_nr, VTY_NEWLINE);
|
||||
vty_out(vty, " line %u%s", trunk->e1.vty_line_nr, VTY_NEWLINE);
|
||||
vty_out(vty, " %ssdp audio-payload send-ptime%s",
|
||||
trunk->audio_send_ptime ? "" : "no ", VTY_NEWLINE);
|
||||
@@ -1312,7 +1333,7 @@ DEFUN(loop_conn,
|
||||
}
|
||||
|
||||
if (!trunk->endpoints) {
|
||||
vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
|
||||
vty_out(vty, "%%Trunk %u has no endpoints allocated.%s",
|
||||
trunk->trunk_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
@@ -1373,7 +1394,7 @@ DEFUN(tap_rtp,
|
||||
}
|
||||
|
||||
if (!trunk->endpoints) {
|
||||
vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
|
||||
vty_out(vty, "%%Trunk %u has no endpoints allocated.%s",
|
||||
trunk->trunk_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
@@ -1440,7 +1461,7 @@ DEFUN(free_endp, free_endp_cmd,
|
||||
}
|
||||
|
||||
if (!trunk->endpoints) {
|
||||
vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
|
||||
vty_out(vty, "%%Trunk %u has no endpoints allocated.%s",
|
||||
trunk->trunk_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
@@ -1473,7 +1494,7 @@ DEFUN(reset_endp, reset_endp_cmd,
|
||||
}
|
||||
|
||||
if (!trunk->endpoints) {
|
||||
vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
|
||||
vty_out(vty, "%%Trunk %u has no endpoints allocated.%s",
|
||||
trunk->trunk_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
@@ -1536,7 +1557,7 @@ DEFUN(cfg_mgcp_osmux_ip,
|
||||
"IPv4 Address to bind to\n"
|
||||
"IPv6 Address to bind to\n")
|
||||
{
|
||||
osmo_talloc_replace_string(g_cfg, &g_cfg->osmux_addr, argv[0]);
|
||||
osmo_strlcpy(g_cfg->osmux_addr, argv[0], sizeof(g_cfg->osmux_addr));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1606,6 +1627,7 @@ DEFUN(cfg_mgcp_conn_timeout,
|
||||
int mgcp_vty_init(void)
|
||||
{
|
||||
install_element_ve(&show_mgcp_cmd);
|
||||
install_element_ve(&show_mgcp_active_cmd);
|
||||
install_element_ve(&show_mgcp_endpoint_cmd);
|
||||
install_element_ve(&show_mgcp_trunk_endpoint_cmd);
|
||||
install_element(ENABLE_NODE, &loop_conn_cmd);
|
||||
@@ -1724,7 +1746,7 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!g_cfg->source_addr) {
|
||||
if (!strlen(g_cfg->source_addr)) {
|
||||
fprintf(stderr, "You need to specify a bind address.\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -1732,7 +1754,7 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
|
||||
llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
|
||||
if (mgcp_trunk_equip(trunk) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to initialize trunk %d (%d endpoints)\n",
|
||||
"Failed to initialize trunk %u (%d endpoints)\n",
|
||||
trunk->trunk_nr, trunk->number_endpoints);
|
||||
return -1;
|
||||
}
|
||||
|
@@ -235,7 +235,7 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
|
||||
/* reset endpoints */
|
||||
if (reset_endpoints) {
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Asked to reset endpoints: %d/%d\n",
|
||||
"Asked to reset endpoints: %u/%d\n",
|
||||
reset_trunk->trunk_nr, reset_trunk->trunk_type);
|
||||
|
||||
/* reset flag */
|
||||
@@ -243,7 +243,7 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
|
||||
|
||||
/* Walk over all endpoints and trigger a release, this will release all
|
||||
* endpoints, possible open connections are forcefully dropped */
|
||||
for (i = 1; i < reset_trunk->number_endpoints; ++i)
|
||||
for (i = 0; i < reset_trunk->number_endpoints; ++i)
|
||||
mgcp_endp_release(reset_trunk->endpoints[i]);
|
||||
}
|
||||
|
||||
@@ -369,12 +369,12 @@ int main(int argc, char **argv)
|
||||
|
||||
/* we need to bind a socket */
|
||||
flags = OSMO_SOCK_F_BIND;
|
||||
if (cfg->call_agent_addr)
|
||||
if (strlen(cfg->call_agent_addr))
|
||||
flags |= OSMO_SOCK_F_CONNECT;
|
||||
|
||||
rc = osmo_sock_init2_ofd(&cfg->gw_fd.bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
|
||||
cfg->source_addr, cfg->source_port,
|
||||
cfg->call_agent_addr, cfg->call_agent_addr ? 2727 : 0, flags);
|
||||
cfg->call_agent_addr, strlen(cfg->call_agent_addr) ? 2727 : 0, flags);
|
||||
if (rc < 0) {
|
||||
perror("Gateway failed to bind");
|
||||
return -1;
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp/mgcp_network.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
@@ -127,7 +128,7 @@ static void test_strline(void)
|
||||
"a=fmtp:126 0/1/2\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
|
||||
#define MDCX4 \
|
||||
#define MDCX4_ADDR0000 \
|
||||
"MDCX 18983216 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendrecv\r" \
|
||||
"C: 2\r\n" \
|
||||
@@ -142,6 +143,24 @@ static void test_strline(void)
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
|
||||
#define MDCX4_ADDR0000_RET \
|
||||
"527 18983216 FAIL\r\n"
|
||||
|
||||
#define MDCX4 \
|
||||
"MDCX 18983217 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendrecv\r" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20, a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
|
||||
#define MDCX4_RET(Ident) \
|
||||
"200 " Ident " OK\r\n" \
|
||||
"\r\n" \
|
||||
@@ -167,45 +186,45 @@ static void test_strline(void)
|
||||
"a=ptime:40\r\n"
|
||||
|
||||
#define MDCX4_PT1 \
|
||||
"MDCX 18983217 1@mgw MGCP 1.0\r\n" \
|
||||
"MDCX 18983218 1@mgw MGCP 1.0\r\n" \
|
||||
"M: SENDRECV\r" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20-40, a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
|
||||
#define MDCX4_PT2 \
|
||||
"MDCX 18983218 1@mgw MGCP 1.0\r\n" \
|
||||
"MDCX 18983219 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendrecv\r" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20-20, a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
|
||||
#define MDCX4_PT3 \
|
||||
"MDCX 18983219 1@mgw MGCP 1.0\r\n" \
|
||||
"MDCX 18983220 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendrecv\r" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
@@ -213,47 +232,47 @@ static void test_strline(void)
|
||||
|
||||
/* Test different upper/lower case in options */
|
||||
#define MDCX4_PT4 \
|
||||
"MDCX 18983220 1@mgw MGCP 1.0\r\n" \
|
||||
"MDCX 18983221 1@mgw MGCP 1.0\r\n" \
|
||||
"m: sendrecv\r" \
|
||||
"c: 2\r\n" \
|
||||
"i: %s\r\n" \
|
||||
"l: A:amr, NT:IN\r\n" \
|
||||
"\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
|
||||
#define MDCX4_SO \
|
||||
"MDCX 18983221 1@mgw MGCP 1.0\r\n" \
|
||||
"MDCX 18983222 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendonly\r" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20, a:AMR, nt:IN\r\n" \
|
||||
"\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
|
||||
"c=IN IP4 5.6.7.8\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
|
||||
#define MDCX4_RO \
|
||||
"MDCX 18983222 1@mgw MGCP 1.0\r\n" \
|
||||
"MDCX 18983223 1@mgw MGCP 1.0\r\n" \
|
||||
"M: recvonly\r" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20, a:AMR, nt:IN\r\n"
|
||||
|
||||
#define MDCX_TOO_LONG_CI \
|
||||
"MDCX 18983223 1@mgw MGCP 1.0\r\n" \
|
||||
"MDCX 18983224 1@mgw MGCP 1.0\r\n" \
|
||||
"I: 123456789012345678901234567890123\n"
|
||||
|
||||
#define MDCX_TOO_LONG_CI_RET "510 18983223 FAIL\r\n"
|
||||
#define MDCX_TOO_LONG_CI_RET "510 18983224 FAIL\r\n"
|
||||
|
||||
#define SHORT2 "CRCX 1"
|
||||
#define SHORT2_RET "510 000000 FAIL\r\n"
|
||||
@@ -541,13 +560,14 @@ static const struct mgcp_test tests[] = {
|
||||
{"MDCX2", MDCX_UNALLOCATED, MDCX_RET},
|
||||
{"CRCX", CRCX, CRCX_RET, 97},
|
||||
{"MDCX3", MDCX3, MDCX3_RET, PTYPE_IGNORE},
|
||||
{"MDCX4", MDCX4, MDCX4_RET("18983216"), 99},
|
||||
{"MDCX4_PT1", MDCX4_PT1, MDCX4_RET("18983217"), 99},
|
||||
{"MDCX4_PT2", MDCX4_PT2, MDCX4_RET("18983218"), 99},
|
||||
{"MDCX4_PT3", MDCX4_PT3, MDCX4_RET("18983219"), 99},
|
||||
{"MDCX4_PT4", MDCX4_PT4, MDCX4_RET("18983220"), 99},
|
||||
{"MDCX4_SO", MDCX4_SO, MDCX4_RET("18983221"), 99},
|
||||
{"MDCX4_RO", MDCX4_RO, MDCX4_RO_RET("18983222"), PTYPE_IGNORE},
|
||||
{"MDCX4_ADDR000", MDCX4_ADDR0000, MDCX4_ADDR0000_RET},
|
||||
{"MDCX4", MDCX4, MDCX4_RET("18983217"), 99},
|
||||
{"MDCX4_PT1", MDCX4_PT1, MDCX4_RET("18983218"), 99},
|
||||
{"MDCX4_PT2", MDCX4_PT2, MDCX4_RET("18983219"), 99},
|
||||
{"MDCX4_PT3", MDCX4_PT3, MDCX4_RET("18983220"), 99},
|
||||
{"MDCX4_PT4", MDCX4_PT4, MDCX4_RET("18983221"), 99},
|
||||
{"MDCX4_SO", MDCX4_SO, MDCX4_RET("18983222"), 99},
|
||||
{"MDCX4_RO", MDCX4_RO, MDCX4_RO_RET("18983223"), PTYPE_IGNORE},
|
||||
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE},
|
||||
{"CRCX_ZYN", CRCX_ZYN, CRCX_ZYN_RET, 97},
|
||||
{"EMPTY", EMPTY, EMPTY_RET},
|
||||
@@ -594,29 +614,6 @@ static struct msgb *create_msg(const char *str, const char *conn_id)
|
||||
return msg;
|
||||
}
|
||||
|
||||
static char last_endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
|
||||
static int mgcp_test_policy_cb(struct mgcp_endpoint *endp,
|
||||
int state, const char *transaction_id)
|
||||
{
|
||||
unsigned int i;
|
||||
struct mgcp_trunk *trunk;
|
||||
|
||||
fprintf(stderr, "Policy CB got state %d on endpoint %s\n",
|
||||
state, endp->name);
|
||||
|
||||
trunk = endp->trunk;
|
||||
last_endpoint[0] = '\0';
|
||||
for (i = 0; i < trunk->number_endpoints; i++) {
|
||||
if (strcmp(endp->name, trunk->endpoints[i]->name) == 0)
|
||||
osmo_strlcpy(last_endpoint, trunk->endpoints[i]->name,
|
||||
sizeof(last_endpoint));
|
||||
}
|
||||
|
||||
return MGCP_POLICY_CONT;
|
||||
}
|
||||
|
||||
#define MGCP_DUMMY_LOAD 0x23
|
||||
static int dummy_packets = 0;
|
||||
/* override and forward */
|
||||
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
|
||||
@@ -626,13 +623,18 @@ ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
|
||||
htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr);
|
||||
int dest_port = htons(((struct sockaddr_in *)dest_addr)->sin_port);
|
||||
|
||||
if (len == 1 && ((const char *)buf)[0] == MGCP_DUMMY_LOAD) {
|
||||
if (len == sizeof(rtp_dummy_payload)
|
||||
&& memcmp(buf, rtp_dummy_payload, sizeof(rtp_dummy_payload)) == 0) {
|
||||
fprintf(stderr,
|
||||
"Dummy packet to 0x%08x:%d, msg length %zu\n%s\n\n",
|
||||
dest_host, dest_port, len, osmo_hexdump(buf, len));
|
||||
dummy_packets += 1;
|
||||
}
|
||||
|
||||
/* Make sure address+port are valid */
|
||||
OSMO_ASSERT(dest_host);
|
||||
OSMO_ASSERT(dest_port);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -768,13 +770,13 @@ static void test_messages(void)
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
char last_conn_id[256];
|
||||
int rc;
|
||||
char *last_endpoint = mgcp_debug_get_last_endpoint_name();
|
||||
|
||||
cfg = mgcp_config_alloc();
|
||||
trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
|
||||
trunk->v.vty_number_endpoints = 64;
|
||||
mgcp_trunk_equip(trunk);
|
||||
cfg->policy_cb = mgcp_test_policy_cb;
|
||||
|
||||
memset(last_conn_id, 0, sizeof(last_conn_id));
|
||||
|
||||
@@ -786,7 +788,6 @@ static void test_messages(void)
|
||||
printf("\n================================================\n");
|
||||
printf("Testing %s\n", t->name);
|
||||
|
||||
last_endpoint[0] = '\0';
|
||||
dummy_packets = 0;
|
||||
|
||||
osmo_talloc_replace_string(cfg, &trunk->audio_fmtp_extra,
|
||||
@@ -959,7 +960,7 @@ static void test_retransmission(void)
|
||||
static int rqnt_cb(struct mgcp_endpoint *endp, char _tone)
|
||||
{
|
||||
ptrdiff_t tone = _tone;
|
||||
endp->cfg->data = (void *)tone;
|
||||
endp->trunk->cfg->data = (void *)tone;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1041,20 +1042,19 @@ static void test_packet_loss_calc(void)
|
||||
int i;
|
||||
struct mgcp_endpoint endp;
|
||||
struct mgcp_endpoint *endpoints[1];
|
||||
struct mgcp_config cfg = {0};
|
||||
struct mgcp_trunk trunk;
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_trunk *trunk;
|
||||
|
||||
printf("Testing packet loss calculation.\n");
|
||||
|
||||
memset(&endp, 0, sizeof(endp));
|
||||
memset(&trunk, 0, sizeof(trunk));
|
||||
|
||||
endp.cfg = &cfg;
|
||||
cfg = mgcp_config_alloc();
|
||||
trunk = mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
endp.type = &ep_typeset.rtp;
|
||||
trunk.v.vty_number_endpoints = 1;
|
||||
trunk.endpoints = endpoints;
|
||||
trunk.endpoints[0] = &endp;
|
||||
endp.trunk = &trunk;
|
||||
trunk->v.vty_number_endpoints = 1;
|
||||
trunk->endpoints = endpoints;
|
||||
trunk->endpoints[0] = &endp;
|
||||
endp.trunk = trunk;
|
||||
INIT_LLIST_HEAD(&endp.conns);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pl_test_dat); ++i) {
|
||||
@@ -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 = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
|
||||
packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
|
||||
|
||||
state->stats.initialized = 1;
|
||||
state->stats.base_seq = pl_test_dat[i].base_seq;
|
||||
@@ -1092,6 +1092,8 @@ static void test_packet_loss_calc(void)
|
||||
mgcp_conn_free_all(&endp);
|
||||
}
|
||||
|
||||
talloc_free(trunk);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os,
|
||||
@@ -1258,21 +1260,25 @@ struct rtp_packet_info test_rtp_packets1[] = {
|
||||
/* RTP: SeqNo=1002, TS=160320 */
|
||||
{2.040000, 20, "\x80\x62\x03\xEA\x00\x02\x72\x40\x50\x60\x70\x80"
|
||||
"\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
|
||||
/* RTP: SeqNo=1003, TS=180320, Marker */
|
||||
{2.060000, 20, "\x80\xE2\x03\xEB\x00\x02\xC0\x60\x50\x60\x70\x80"
|
||||
"\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
|
||||
/* RTP: SeqNo=1004, TS=180480 */
|
||||
{2.080000, 20, "\x80\x62\x03\xEC\x00\x02\xC1\x00\x50\x60\x70\x80"
|
||||
"\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
|
||||
/* RTP: SeqNo=1005, TS=180480, 10ms too late */
|
||||
{2.110000, 20, "\x80\x62\x03\xED\x00\x02\xC1\xA0\x50\x60\x70\x80"
|
||||
"\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
|
||||
};
|
||||
|
||||
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *rtp_end,
|
||||
struct osmo_sockaddr *addr, struct msgb *msg);
|
||||
|
||||
static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
{
|
||||
int i;
|
||||
|
||||
struct mgcp_trunk trunk;
|
||||
struct mgcp_trunk *trunk;
|
||||
struct mgcp_endpoint endp;
|
||||
struct mgcp_endpoint *endpoints[1];
|
||||
struct mgcp_config cfg = {0};
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_rtp_state state;
|
||||
struct mgcp_rtp_end *rtp;
|
||||
struct osmo_sockaddr addr = { 0 };
|
||||
@@ -1290,7 +1296,8 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
patch_ssrc ? ", patch SSRC" : "",
|
||||
patch_ts ? ", patch timestamps" : "");
|
||||
|
||||
memset(&trunk, 0, sizeof(trunk));
|
||||
cfg = mgcp_config_alloc();
|
||||
trunk = mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
memset(&endp, 0, sizeof(endp));
|
||||
memset(&state, 0, sizeof(state));
|
||||
|
||||
@@ -1299,16 +1306,15 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
state.in_stream.err_ts_ctr = &test_ctr_in;
|
||||
state.out_stream.err_ts_ctr = &test_ctr_out;
|
||||
|
||||
endp.cfg = &cfg;
|
||||
endp.type = &ep_typeset.rtp;
|
||||
|
||||
trunk.v.vty_number_endpoints = 1;
|
||||
trunk.endpoints = endpoints;
|
||||
trunk.endpoints[0] = &endp;
|
||||
trunk.force_constant_ssrc = patch_ssrc;
|
||||
trunk.force_aligned_timing = patch_ts;
|
||||
trunk->v.vty_number_endpoints = 1;
|
||||
trunk->endpoints = endpoints;
|
||||
trunk->endpoints[0] = &endp;
|
||||
trunk->force_constant_ssrc = patch_ssrc;
|
||||
trunk->force_aligned_timing = patch_ts;
|
||||
|
||||
endp.trunk = &trunk;
|
||||
endp.trunk = trunk;
|
||||
|
||||
INIT_LLIST_HEAD(&endp.conns);
|
||||
_conn = mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
|
||||
@@ -1367,6 +1373,8 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
|
||||
force_monotonic_time_us = -1;
|
||||
mgcp_conn_free_all(&endp);
|
||||
talloc_free(trunk);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
static void test_multilple_codec(void)
|
||||
@@ -1378,6 +1386,7 @@ static void test_multilple_codec(void)
|
||||
struct in_addr addr;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
char conn_id[256];
|
||||
char *last_endpoint = mgcp_debug_get_last_endpoint_name();
|
||||
|
||||
printf("Testing multiple payload types\n");
|
||||
|
||||
@@ -1385,7 +1394,6 @@ static void test_multilple_codec(void)
|
||||
trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
trunk->v.vty_number_endpoints = 64;
|
||||
mgcp_trunk_equip(trunk);
|
||||
cfg->policy_cb = mgcp_test_policy_cb;
|
||||
|
||||
/* Allocate endpoint 1@mgw with two codecs */
|
||||
last_endpoint[0] = '\0';
|
||||
@@ -1547,24 +1555,24 @@ static void test_no_cycle(void)
|
||||
|
||||
OSMO_ASSERT(conn->state.stats.initialized == 0);
|
||||
|
||||
mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342);
|
||||
mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342, false);
|
||||
OSMO_ASSERT(conn->state.stats.initialized == 1);
|
||||
OSMO_ASSERT(conn->state.stats.cycles == 0);
|
||||
OSMO_ASSERT(conn->state.stats.max_seq == 0);
|
||||
|
||||
mgcp_rtp_annex_count(endp, &conn->state, 1, 0, 2342);
|
||||
mgcp_rtp_annex_count(endp, &conn->state, 1, 0, 2342, false);
|
||||
OSMO_ASSERT(conn->state.stats.initialized == 1);
|
||||
OSMO_ASSERT(conn->state.stats.cycles == 0);
|
||||
OSMO_ASSERT(conn->state.stats.max_seq == 1);
|
||||
|
||||
/* now jump.. */
|
||||
mgcp_rtp_annex_count(endp, &conn->state, UINT16_MAX, 0, 2342);
|
||||
mgcp_rtp_annex_count(endp, &conn->state, UINT16_MAX, 0, 2342, false);
|
||||
OSMO_ASSERT(conn->state.stats.initialized == 1);
|
||||
OSMO_ASSERT(conn->state.stats.cycles == 0);
|
||||
OSMO_ASSERT(conn->state.stats.max_seq == UINT16_MAX);
|
||||
|
||||
/* and wrap */
|
||||
mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342);
|
||||
mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342, false);
|
||||
OSMO_ASSERT(conn->state.stats.initialized == 1);
|
||||
OSMO_ASSERT(conn->state.stats.cycles == UINT16_MAX + 1);
|
||||
OSMO_ASSERT(conn->state.stats.max_seq == 0);
|
||||
@@ -1587,8 +1595,6 @@ static void test_no_name(void)
|
||||
trunk->audio_send_name = 0;
|
||||
mgcp_trunk_equip(trunk);
|
||||
|
||||
cfg->policy_cb = mgcp_test_policy_cb;
|
||||
|
||||
inp = create_msg(CRCX, NULL);
|
||||
msg = mgcp_handle_message(cfg, inp);
|
||||
|
||||
@@ -2121,46 +2127,95 @@ void test_conn_id_matching()
|
||||
|
||||
void test_e1_trunk_nr_from_epname()
|
||||
{
|
||||
int trunk_nr;
|
||||
unsigned int trunk_nr;
|
||||
int rc;
|
||||
|
||||
/* Note: e1_trunk_nr_from_epname does not check the text
|
||||
* after the E1 trunk number, after the delimiter
|
||||
* character "/" arbitrary text may follow. */
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-0/s-1/su16-0");
|
||||
rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-0/s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == 0);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-1/s-1/su16-0");
|
||||
OSMO_ASSERT(rc == 0);
|
||||
rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-1/s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == 1);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-2/s-2/su16-0");
|
||||
OSMO_ASSERT(rc == 0);
|
||||
rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-2/s-2/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == 2);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-3/s-23/su32-0");
|
||||
OSMO_ASSERT(rc == 0);
|
||||
rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-3/s-23/su32-0");
|
||||
OSMO_ASSERT(trunk_nr == 3);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-3/xxxxxxx");
|
||||
OSMO_ASSERT(rc == 0);
|
||||
rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-3/xxxxxxx");
|
||||
OSMO_ASSERT(trunk_nr == 3);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-24/s-1/su16-0");
|
||||
OSMO_ASSERT(rc == 0);
|
||||
rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-24/s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == 24);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-64/s-1/su16-0");
|
||||
OSMO_ASSERT(rc == 0);
|
||||
rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-64/s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == 64);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
/* The following endpoint strings should fail, either the
|
||||
* trunk number exceeds the valid range or the trunk prefix
|
||||
* is wrong. Also when the delimiter character "/" at the
|
||||
* end of the trunk is wrong the parsing should fail. */
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-65/s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == -EINVAL);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1--1/s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == -EINVAL);
|
||||
trunk_nr = e1_trunk_nr_from_epname("xxxxxx4zyz");
|
||||
OSMO_ASSERT(trunk_nr == -EINVAL);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1+2/s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == -EINVAL);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e2-24/s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == -EINVAL);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-24s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == -EINVAL);
|
||||
rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-65/s-1/su16-0");
|
||||
OSMO_ASSERT(rc == -EINVAL);
|
||||
rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1--1/s-1/su16-0");
|
||||
OSMO_ASSERT(rc == -EINVAL);
|
||||
rc = e1_trunk_nr_from_epname(&trunk_nr, "xxxxxx4zyz");
|
||||
OSMO_ASSERT(rc == -EINVAL);
|
||||
rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1+2/s-1/su16-0");
|
||||
OSMO_ASSERT(rc == -EINVAL);
|
||||
rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e2-24/s-1/su16-0");
|
||||
OSMO_ASSERT(rc == -EINVAL);
|
||||
rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-24s-1/su16-0");
|
||||
OSMO_ASSERT(rc == -EINVAL);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void test_mgcp_is_rtp_dummy_payload()
|
||||
{
|
||||
/* realistic rtp packet */
|
||||
static const char rtp_payload[] =
|
||||
{ 0x80, 0x03, 0xca, 0xd7, 0x62, 0x12, 0x75, 0xc4, 0x43, 0x4b, 0x3e,
|
||||
0x72, 0xd2, 0x57, 0x7a, 0x1c, 0xda, 0x50, 0x00, 0x49, 0x24, 0x92,
|
||||
0x49, 0x24, 0x50, 0x00, 0x49, 0x24, 0x92, 0x49, 0x24, 0x50, 0x00,
|
||||
0x49, 0x24, 0x92, 0x49, 0x24, 0x50, 0x00, 0x49, 0x23, 0x92, 0x49,
|
||||
0x24 };
|
||||
|
||||
struct msgb *msg_dummy = msgb_alloc(RTP_BUF_SIZE, "RTP-msg_dummy");
|
||||
struct msgb *msg_rtp = msgb_alloc(RTP_BUF_SIZE, "RTP-msg_rtp");
|
||||
struct msgb *msg_dummy_rtp =
|
||||
msgb_alloc(RTP_BUF_SIZE, "RTP-msg_dummy_rtp");
|
||||
|
||||
uint8_t *buf;
|
||||
|
||||
/* Dummy RTP packet */
|
||||
buf = msgb_put(msg_dummy, ARRAY_SIZE(rtp_dummy_payload));
|
||||
memcpy(buf, rtp_dummy_payload, ARRAY_SIZE(rtp_dummy_payload));
|
||||
|
||||
/* Normal RTP packet */
|
||||
buf = msgb_put(msg_rtp, ARRAY_SIZE(rtp_payload));
|
||||
memcpy(buf, rtp_payload, ARRAY_SIZE(rtp_payload));
|
||||
|
||||
/* Dummy RTP packet with normal RTP packet attached, this must not be
|
||||
* recognized as Dummy RTP packet */
|
||||
buf = msgb_put(msg_dummy_rtp, ARRAY_SIZE(rtp_dummy_payload));
|
||||
memcpy(buf, rtp_dummy_payload, ARRAY_SIZE(rtp_dummy_payload));
|
||||
buf = msgb_put(msg_dummy_rtp, ARRAY_SIZE(rtp_payload));
|
||||
memcpy(buf, rtp_payload, ARRAY_SIZE(rtp_payload));
|
||||
|
||||
OSMO_ASSERT(mgcp_is_rtp_dummy_payload(msg_dummy) == true);
|
||||
OSMO_ASSERT(mgcp_is_rtp_dummy_payload(msg_rtp) == false);
|
||||
OSMO_ASSERT(mgcp_is_rtp_dummy_payload(msg_dummy_rtp) == false);
|
||||
|
||||
msgb_free(msg_dummy);
|
||||
msgb_free(msg_rtp);
|
||||
msgb_free(msg_dummy_rtp);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
|
||||
@@ -2187,6 +2242,7 @@ int main(int argc, char **argv)
|
||||
test_mgcp_codec_pt_translate();
|
||||
test_conn_id_matching();
|
||||
test_e1_trunk_nr_from_epname();
|
||||
test_mgcp_is_rtp_dummy_payload();
|
||||
|
||||
OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
|
||||
OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
|
||||
|
@@ -97,7 +97,7 @@ Response matches our expectations.
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
Testing MDCX4
|
||||
Testing MDCX4_ADDR000
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983216 1@mgw MGCP 1.0
|
||||
@@ -113,6 +113,29 @@ m=audio 4441 RTP/AVP 99
|
||||
m=audio 4441 RTP/AVP 99
|
||||
a=rtpmap:99 AMR/8000
|
||||
a=ptime:40
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
using message as statically defined for comparison
|
||||
Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
Testing MDCX4
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983217 1@mgw MGCP 1.0
|
||||
M: sendrecv
|
||||
C: 2
|
||||
I: %s
|
||||
L: p:20, a:AMR, nt:IN
|
||||
|
||||
v=0
|
||||
o=- %s 23 IN IP4 5.6.7.8
|
||||
c=IN IP4 5.6.7.8
|
||||
t=0 0
|
||||
m=audio 4441 RTP/AVP 99
|
||||
a=rtpmap:99 AMR/8000
|
||||
a=ptime:40
|
||||
|
||||
---------8<---------
|
||||
@@ -124,14 +147,14 @@ Dummy packets: 2
|
||||
|
||||
================================================
|
||||
Testing MDCX4_PT1
|
||||
---------8<---------
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983218 1@mgw MGCP 1.0
|
||||
M: SENDRECV
|
||||
C: 2
|
||||
I: %s
|
||||
|
||||
v=0
|
||||
L: p:20-40, a:AMR, nt:IN
|
||||
|
||||
v=0
|
||||
o=- %s 23 IN IP4 5.6.7.8
|
||||
c=IN IP4 5.6.7.8
|
||||
@@ -148,14 +171,14 @@ Dummy packets: 2
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
creating message from statically defined input:
|
||||
Testing MDCX4_PT2
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983219 1@mgw MGCP 1.0
|
||||
M: sendrecv
|
||||
C: 2
|
||||
L: p:20-20, a:AMR, nt:IN
|
||||
|
||||
I: %s
|
||||
L: p:20-20, a:AMR, nt:IN
|
||||
|
||||
v=0
|
||||
o=- %s 23 IN IP4 5.6.7.8
|
||||
@@ -172,14 +195,14 @@ Dummy packets: 2
|
||||
(response contains a connection id)
|
||||
Dummy packets: 2
|
||||
|
||||
Testing MDCX4_PT3
|
||||
================================================
|
||||
Testing MDCX4_PT3
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983220 1@mgw MGCP 1.0
|
||||
M: sendrecv
|
||||
I: %s
|
||||
L: a:AMR, nt:IN
|
||||
C: 2
|
||||
I: %s
|
||||
L: a:AMR, nt:IN
|
||||
|
||||
v=0
|
||||
@@ -196,14 +219,14 @@ Dummy packets: 2
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
Dummy packets: 2
|
||||
================================================
|
||||
|
||||
================================================
|
||||
Testing MDCX4_PT4
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983221 1@mgw MGCP 1.0
|
||||
c: 2
|
||||
i: %s
|
||||
m: sendrecv
|
||||
c: 2
|
||||
i: %s
|
||||
l: A:amr, NT:IN
|
||||
|
||||
@@ -220,14 +243,14 @@ Dummy packets: 2
|
||||
using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
Testing MDCX4_SO
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
M: sendonly
|
||||
C: 2
|
||||
MDCX 18983222 1@mgw MGCP 1.0
|
||||
M: sendonly
|
||||
C: 2
|
||||
I: %s
|
||||
L: p:20, a:AMR, nt:IN
|
||||
@@ -243,7 +266,7 @@ Response matches our expectations.
|
||||
---------8<---------
|
||||
checking response:
|
||||
using message with patched conn_id for comparison
|
||||
(response contains a connection id)
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
|
||||
================================================
|
||||
@@ -441,7 +464,6 @@ checking response:
|
||||
Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
Testing CRCX
|
||||
================================================
|
||||
Testing CRCX
|
||||
creating message from statically defined input:
|
||||
@@ -470,7 +492,7 @@ Dummy packets: 2
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 5904 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
|
||||
a=ptime:40
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -770,6 +792,15 @@ Stats: Jitter = 0, Transit = -144000
|
||||
Stats: Jitter = 24, Transit = -32888
|
||||
In TS: 36728, dTS: 160, Seq: 24
|
||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
||||
Stats: Jitter = 22, Transit = -32888
|
||||
In TS: 36888, dTS: 160, Seq: 25
|
||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
||||
Stats: Jitter = 21, Transit = -32888
|
||||
In TS: 160000, dTS: 0, Seq: 1000
|
||||
Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0
|
||||
Stats: Jitter = 0, Transit = -144000
|
||||
In TS: 160160, dTS: 160, Seq: 1001
|
||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
||||
Stats: Jitter = 0, Transit = -144000
|
||||
In TS: 160320, dTS: 160, Seq: 1002
|
||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
||||
@@ -864,6 +895,15 @@ Stats: Jitter = 0, Transit = -144000
|
||||
In TS: 36728, dTS: 160, Seq: 24
|
||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
||||
Stats: Jitter = 22, Transit = -32888
|
||||
In TS: 36888, dTS: 160, Seq: 25
|
||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
||||
Stats: Jitter = 21, Transit = -32888
|
||||
Output SSRC changed to 50607080
|
||||
In TS: 160000, dTS: 0, Seq: 1000
|
||||
Out TS change: 123112, dTS: 160, Seq change: 975, TS Err change: in +0, out +0
|
||||
Stats: Jitter = 0, Transit = -144000
|
||||
In TS: 160160, dTS: 160, Seq: 1001
|
||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
||||
Stats: Jitter = 0, Transit = -144000
|
||||
In TS: 160320, dTS: 160, Seq: 1002
|
||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
||||
@@ -958,6 +998,15 @@ Stats: Jitter = 0, Transit = -144000
|
||||
In TS: 36728, dTS: 160, Seq: 24
|
||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
||||
Stats: Jitter = 22, Transit = -32888
|
||||
In TS: 36888, dTS: 160, Seq: 25
|
||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
||||
Stats: Jitter = 21, Transit = -32888
|
||||
Output SSRC changed to 50607080
|
||||
In TS: 160000, dTS: 0, Seq: 1000
|
||||
Out TS change: 123112, dTS: 160, Seq change: 975, TS Err change: in +0, out +0
|
||||
Stats: Jitter = 0, Transit = -144000
|
||||
In TS: 160160, dTS: 160, Seq: 1001
|
||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
||||
Stats: Jitter = 0, Transit = -144000
|
||||
In TS: 160320, dTS: 160, Seq: 1002
|
||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
||||
@@ -1050,6 +1099,15 @@ Stats: Jitter = 0, Transit = -144000
|
||||
Stats: Jitter = 24, Transit = -32888
|
||||
In TS: 36728, dTS: 160, Seq: 24
|
||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
||||
Stats: Jitter = 22, Transit = -32888
|
||||
In TS: 36888, dTS: 160, Seq: 25
|
||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
||||
Stats: Jitter = 21, Transit = -32888
|
||||
In TS: 160000, dTS: 0, Seq: 1000
|
||||
Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0
|
||||
Stats: Jitter = 0, Transit = -144000
|
||||
In TS: 160160, dTS: 160, Seq: 1001
|
||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
||||
Stats: Jitter = 0, Transit = -144000
|
||||
In TS: 160320, dTS: 160, Seq: 1002
|
||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
||||
|
@@ -1,18 +1,19 @@
|
||||
DLMGCP MGCP client: using endpoint domain '@mgw'
|
||||
DLMGCP message buffer to small, can not generate MGCP message
|
||||
DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw'
|
||||
DLMGCP MGW(mgw) Message buffer to small, can not generate MGCP message (SDP)
|
||||
|
||||
test_mgcp_client_cancel():
|
||||
DLMGCP MGCP client: using endpoint domain '@mgw'
|
||||
DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw'
|
||||
- composed msg with trans_id=1
|
||||
- not in queue yet, cannot cancel yet
|
||||
DLMGCP Cannot cancel, no such transaction: 1
|
||||
DLMGCP MGW(mgw) Cannot cancel, no such transaction: 1
|
||||
- enqueue
|
||||
- cancel succeeds
|
||||
DLMGCP Canceled transaction 1
|
||||
DLMGCP MGW(mgw) Canceled transaction 1
|
||||
- late response gets discarded
|
||||
DLMGCP Cannot find matching MGCP transaction for trans_id 1
|
||||
DLMGCP MGW(mgw) MGCP client: Rx 200 1 OK
|
||||
DLMGCP MGW(mgw) Cannot find matching MGCP transaction for trans_id 1
|
||||
- canceling again does nothing
|
||||
DLMGCP Cannot cancel, no such transaction: 1
|
||||
DLMGCP MGW(mgw) Cannot cancel, no such transaction: 1
|
||||
test_mgcp_client_cancel() done
|
||||
|
||||
test_sdp_section_start() test [0]:
|
||||
@@ -129,9 +130,9 @@ DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
|
||||
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
|
||||
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
|
||||
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
|
||||
DLMGCP MGCP client: using endpoint domain '@mgw'
|
||||
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su128-0@mgw), rate(128)/offset(0) combination is invalid!
|
||||
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su8-16@mgw), rate(8)/offset(16) combination is invalid!
|
||||
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-0/su8-2@mgw), E1-timeslot number (0) is invalid!
|
||||
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-64/su8-2@mgw), E1-timeslot number (64) is invalid!
|
||||
DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw'
|
||||
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su128-0@mgw), rate(128)/offset(0) combination is invalid!
|
||||
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su8-16@mgw), rate(8)/offset(16) combination is invalid!
|
||||
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-0/su8-2@mgw), E1-timeslot number (0) is invalid!
|
||||
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-64/su8-2@mgw), E1-timeslot number (64) is invalid!
|
||||
Done
|
||||
|
Reference in New Issue
Block a user