mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 08:12:01 +00:00
Compare commits
441 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 | ||
|
4f6a7ad5f8 | ||
|
55a9229922 | ||
|
7802ccc8d6 | ||
|
3ff5151661 | ||
|
5936a9c23e | ||
|
9ffaba7c1b | ||
|
535ede2598 | ||
|
6ccdaa243f | ||
|
ace0b946ff | ||
|
a34036c296 | ||
|
9fec391d6e | ||
|
a835fa85d7 | ||
|
2c66c15227 | ||
|
92a73cd637 | ||
|
6e2795bfbf | ||
|
276b595361 | ||
|
3c293b418b | ||
|
41425e9b85 | ||
|
31682a3274 | ||
|
870f94b493 | ||
|
242e9ada91 | ||
|
9b444fd417 | ||
|
e9473f610e | ||
|
53002b3a00 | ||
|
a098b65252 | ||
|
78b0055e5e | ||
|
648bec3982 | ||
|
c2a8f59560 | ||
|
38404969df | ||
|
19c430feba | ||
|
3ba409558e | ||
|
60be627557 | ||
|
8cfe7df3f1 | ||
|
ba94b6d7be | ||
|
30e01355f6 | ||
|
06624e13d4 | ||
|
19539866e5 | ||
|
1dc2dcebbf | ||
|
6049a63d1d | ||
|
3ef8e76534 | ||
|
fbf07f3f69 | ||
|
de133113a0 | ||
|
6d0a59a1bf | ||
|
2491401932 | ||
|
fbbe8f2f98 | ||
|
71d42e778a | ||
|
8a2a1b22fe | ||
|
a790f0c082 | ||
|
0ab152b2c6 | ||
|
2741d6bb0e | ||
|
74d0e5c318 | ||
|
c4ef4a21c6 | ||
|
ee2f33bf9a | ||
|
531470a0ca | ||
|
9dc73593a3 | ||
|
ae6858bddf | ||
|
99d4d368e8 | ||
|
8667d5169d | ||
|
1add5a53cb | ||
|
729bf3e45a | ||
|
2f34b53b5b | ||
|
ad79f9eb99 | ||
|
9e494e67c7 | ||
|
5322473d04 | ||
|
c57ad7ff9a | ||
|
c59b5c533d | ||
|
0653cc8a7c | ||
|
246233d0d4 | ||
|
a910a81b7c | ||
|
c8acee2234 | ||
|
55863e42c1 | ||
|
6177cae2a2 | ||
|
6af3ccf65d | ||
|
03cb5f3397 | ||
|
113141d4f9 | ||
|
993ea6be7a | ||
|
889fe7f203 | ||
|
efd09d0b77 | ||
|
ffbc618a13 | ||
|
563ffc51b5 | ||
|
51b42ff2e1 | ||
|
7a755be4c2 | ||
|
06ea49159e | ||
|
869b21c869 | ||
|
37a808c5a5 | ||
|
24b8c0ce92 | ||
|
fe67e094ad | ||
|
fbcf39976e | ||
|
4863591c23 | ||
|
827da2c867 | ||
|
efe1571b7b | ||
|
6521fc0695 | ||
|
f5531845bc | ||
|
13fae78b32 | ||
|
b7d395d83a | ||
|
6fbbeec064 | ||
|
0ffa3bdc45 | ||
|
ca8639dc05 | ||
|
8d6a193c1a | ||
|
58a1ba85c7 | ||
|
b0c05aa3a9 | ||
|
0996a1e4ae | ||
|
7e9ddc9904 | ||
|
04bbb9de3e | ||
|
bea56788cb | ||
|
48bcc2ee12 | ||
|
080935a8c7 | ||
|
a466f57007 | ||
|
41f77d8018 | ||
|
e674345e29 | ||
|
3f8139c55f | ||
|
98c09b3f30 | ||
|
7462b95829 | ||
|
7a64182f9a | ||
|
d19de2ee80 | ||
|
08eb9352ab | ||
|
0b79d21c7f | ||
|
bce5f29265 | ||
|
3d5a2dd19f | ||
|
7f90ddb519 | ||
|
c66ab2c4c3 | ||
|
f53796c1fe | ||
|
14b27a8893 | ||
|
21be42abed | ||
|
2d681fd84c | ||
|
c39b1bffec | ||
|
62612e8575 | ||
|
af932ce3bc | ||
|
265b0a8045 | ||
|
74390c521e | ||
|
abfb858f0c | ||
|
6500d72aaf | ||
|
d6877eb8f9 | ||
|
ebb9bf3f12 | ||
|
61cf9bb5f1 | ||
|
a7152e055a | ||
|
63866009e2 | ||
|
dab89af070 | ||
|
eebbf2b1fc | ||
|
e885bc5c24 | ||
|
173dc129fc | ||
|
3abced8d64 | ||
|
a48ff4a738 | ||
|
6c92f9d83e | ||
|
9852e22101 | ||
|
b141cccbfb | ||
|
ec45068972 | ||
|
832bcdf631 | ||
|
73f9c02f49 | ||
|
6a25a61142 | ||
|
1dbbed169a | ||
|
a8f27abe12 | ||
|
ca2aec0235 | ||
|
e827831514 | ||
|
923d60bb12 | ||
|
f2bf8dc8c8 | ||
|
055ded74de | ||
|
3ff71284fa | ||
|
cc0b97e197 | ||
|
8c69e29820 | ||
|
843d9038ce | ||
|
d071a30238 | ||
|
3ab8ca4d84 | ||
|
23f4048b57 | ||
|
401b740ccd | ||
|
a468b0f57f | ||
|
b0cfa7272e | ||
|
683e05f60b | ||
|
16b637bf1b | ||
|
2698540c1e | ||
|
d2f5e69d3e | ||
|
ce64f18587 | ||
|
667fa59b0c | ||
|
5a6220f43b | ||
|
7c6dd3c2c3 | ||
|
740af6ed44 | ||
|
782d607962 | ||
|
50e52e45f9 | ||
|
a2d10216ea | ||
|
f54eb96338 | ||
|
a2b1c5e6f6 | ||
|
c5c1430a1c | ||
|
f7de9aea7b | ||
|
199c5e965e | ||
|
06a49fc04b | ||
|
6349bd422a | ||
|
796a4a1325 | ||
|
2cf6652ba2 | ||
|
9b508f6b53 | ||
|
6e26c70f46 | ||
|
9a34592c09 | ||
|
7eb6f2cb56 | ||
|
83fd8a5692 | ||
|
17058484d1 | ||
|
166077ea48 | ||
|
0c6c3c1da6 | ||
|
fe9a1fe03b | ||
|
1e4a34d45e | ||
|
189f29e939 | ||
|
d2ce444008 | ||
|
ef9420adf5 | ||
|
cb96e05a91 | ||
|
f6387dc766 | ||
|
1b1d7ed98f | ||
|
df7d97e4b4 | ||
|
85978dadab | ||
|
9aaaab6b3b | ||
|
c1ad553d86 | ||
|
c1bf4694e7 | ||
|
1442c5a99e | ||
|
dac2ca7e21 | ||
|
375252279c | ||
|
295570c631 | ||
|
a93c6e9263 | ||
|
30907dc9d8 | ||
|
14f8a08f44 | ||
|
c63f15a9a7 | ||
|
ca538fc5eb | ||
|
6be2c49538 | ||
|
91088c305f | ||
|
900cd6518a | ||
|
fa810e8ccd | ||
|
497611ae84 | ||
|
2b89617aad | ||
|
b542b0457b | ||
|
120568651a | ||
|
f1d301a9a6 | ||
|
c9a6280c94 | ||
|
b5583cde41 | ||
|
6c1cd63a57 | ||
|
0d7ba56f5c | ||
|
3ac604e3ad | ||
|
9fb8ddf00e | ||
|
182ca3bad4 | ||
|
f027f17dcb | ||
|
ac772d8b0c | ||
|
9d9f44ac71 | ||
|
84274e4e9b | ||
|
c5479fe086 | ||
|
538d2c53d9 | ||
|
8de58e79b8 | ||
|
5e8d7995d1 | ||
|
3239f6212e | ||
|
209eb9f103 | ||
|
ef6304e4a1 | ||
|
21cdbfc196 | ||
|
fc8067348b | ||
|
9f11dc5616 | ||
|
ca0818c760 | ||
|
c12bfb7ffc | ||
|
228e591589 | ||
|
58128258b0 | ||
|
e7ae69a7be | ||
|
7e37ce6008 | ||
|
dbe09dd515 | ||
|
217d31d62f | ||
|
47642f245d | ||
|
cb760bdebe | ||
|
e3f8bca424 | ||
|
9fc8a02196 | ||
|
e36b775eae | ||
|
622dd61764 | ||
|
e81c1176aa | ||
|
ed0c1aa9d5 | ||
|
ac69ea9cdf | ||
|
0a40379214 | ||
|
72bc8da537 | ||
|
c3132fd508 | ||
|
96c3107daf | ||
|
ad21a0e166 | ||
|
32d15cc8ba | ||
|
1fd50e5306 | ||
|
6802374574 | ||
|
82c1d1eb20 | ||
|
5a5169e15c | ||
|
7c0b70a94e | ||
|
ca6a8495e1 | ||
|
c0a2196f91 | ||
|
da026abc27 | ||
|
27780313ac | ||
|
e5ed946bdb | ||
|
c319b79baa | ||
|
2818db8b33 | ||
|
0415de74b8 | ||
|
4075b8805d | ||
|
8ab3fbbaf2 | ||
|
cc39218dce | ||
|
8dbc9ed408 | ||
|
ba25eab0fa | ||
|
aa823bf24b | ||
|
a714abfc91 | ||
|
9270e91c09 | ||
|
b7974e2fa4 | ||
|
1e174875bf | ||
|
c26b665c0c | ||
|
0479b2a15f | ||
|
ff6606cacb | ||
|
407b1f186d | ||
|
de2a4d7c22 | ||
|
48aff62341 | ||
|
852ba86949 | ||
|
662fa421c6 | ||
|
d14163e74f | ||
|
11b4810142 | ||
|
9ecceb651b | ||
|
426a9d9103 | ||
|
17bf603222 | ||
|
bcd52e5724 | ||
|
956242dcec | ||
|
e686675f7d | ||
|
4ca0b0d1fa | ||
|
efd645e5a8 | ||
|
f0504e86b3 | ||
|
e28b673fba | ||
|
a77eade744 | ||
|
6531726a02 | ||
|
31b4729f27 | ||
|
a729db62a9 | ||
|
5336f57f05 | ||
|
eb72ff058f | ||
|
8a91d2c04e | ||
|
55e0dcf254 | ||
|
23e7bf1c00 | ||
|
10d487e13f | ||
|
40f5033cf9 | ||
|
b1bb1fa187 | ||
|
1d121483f6 | ||
|
124441af1f | ||
|
b861db97ea | ||
|
08e07046f9 | ||
|
a5e0cf09a6 | ||
|
f2388eab71 | ||
|
120865833c | ||
|
e6d8e91b3a | ||
|
475f868b7f | ||
|
0063ca2fb0 | ||
|
610fda6108 | ||
|
352eed09ae | ||
|
0a89e92a5e | ||
|
dde80f3f61 | ||
|
9a7ccc3746 | ||
|
910189d0a1 | ||
|
8482e8374c | ||
|
fee4fa9492 | ||
|
4dba769577 | ||
|
da895b1151 | ||
|
a74c0ea2db | ||
|
6a26c16eb0 | ||
|
af8e00ffe8 | ||
|
6931f9a7a4 | ||
|
544448abea | ||
|
e547bdd1ce | ||
|
d1562a7766 |
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
|
||||
...
|
57
.gitignore
vendored
57
.gitignore
vendored
@@ -2,26 +2,21 @@ debian/*.log
|
||||
*.o
|
||||
*.lo
|
||||
*.a
|
||||
*.la
|
||||
.deps
|
||||
Makefile
|
||||
Makefile.in
|
||||
bscconfig.h
|
||||
bscconfig.h.in
|
||||
openbsc.pc
|
||||
src/osmo-nitb/osmo-nitb
|
||||
src/osmo-bsc_mgcp/osmo-bsc_mgcp
|
||||
src/osmo-bsc/osmo-bsc
|
||||
src/utils/meas_vis
|
||||
src/utils/meas_json
|
||||
src/utils/osmo-meas-pcap2db
|
||||
src/utils/osmo-meas-udp2db
|
||||
src/utils/smpp_mirror
|
||||
src/osmo-mgw/osmo-mgw
|
||||
*.*~
|
||||
*.sw?
|
||||
.libs
|
||||
*.pyc
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.pc
|
||||
*~
|
||||
|
||||
#configure
|
||||
aclocal.m4
|
||||
@@ -45,32 +40,9 @@ m4/*.m4
|
||||
.version
|
||||
|
||||
|
||||
# apps and app data
|
||||
hlr.sqlite3
|
||||
src/utils/bs11_config
|
||||
src/ipaccess/ipaccess-config
|
||||
src/ipaccess/abisip-find
|
||||
src/ipaccess/ipaccess-firmware
|
||||
src/ipaccess/ipaccess-proxy
|
||||
src/utils/isdnsync
|
||||
src/nat/bsc_nat
|
||||
src/gprs/osmo-sgsn
|
||||
src/gprs/osmo-gbproxy
|
||||
src/gprs/osmo-gtphub
|
||||
src/osmo-bsc_nat/osmo-bsc_nat
|
||||
src/libcommon/gsup_test_client
|
||||
src/osmo-msc/osmo-msc
|
||||
|
||||
#tests
|
||||
tests/testsuite.dir
|
||||
tests/*/*_test
|
||||
# ignore compiled binaries like msc_vlr_test_foo; do not ignore
|
||||
# msc_vlr_test_foo.{c,ok,err}, but do still ignore the corresponding .o object
|
||||
# files:
|
||||
tests/msc_vlr/msc_vlr_test_*
|
||||
!tests/msc_vlr/msc_vlr_test_*.*
|
||||
tests/msc_vlr/msc_vlr_test_*.o
|
||||
|
||||
|
||||
tests/atconfig
|
||||
tests/atlocal
|
||||
@@ -78,7 +50,22 @@ tests/package.m4
|
||||
tests/testsuite
|
||||
tests/testsuite.log
|
||||
|
||||
gsn_restart
|
||||
src/openbsc.cfg*
|
||||
writtenconfig/
|
||||
gtphub_restart_count
|
||||
|
||||
# manuals
|
||||
doc/manuals/*.html
|
||||
doc/manuals/*.svg
|
||||
doc/manuals/*.pdf
|
||||
doc/manuals/*__*.png
|
||||
doc/manuals/*.check
|
||||
doc/manuals/generated/
|
||||
doc/manuals/osmomsc-usermanual.xml
|
||||
doc/manuals/common
|
||||
doc/manuals/build
|
||||
|
||||
contrib/osmo-mgw.spec
|
||||
|
||||
#vs code
|
||||
.cache
|
||||
.vscode
|
||||
|
||||
|
14
Makefile.am
14
Makefile.am
@@ -9,21 +9,29 @@ AM_CPPFLAGS = \
|
||||
$(NULL)
|
||||
|
||||
SUBDIRS = \
|
||||
doc \
|
||||
include \
|
||||
src \
|
||||
tests \
|
||||
doc \
|
||||
contrib \
|
||||
$(NULL)
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = \
|
||||
libosmo-legacy-mgcp.pc \
|
||||
libosmo-mgcp-client.pc \
|
||||
$(NULL)
|
||||
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
EXTRA_DIST = git-version-gen osmoappdesc.py .version
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
contrib/osmo-mgw.spec.in \
|
||||
debian \
|
||||
git-version-gen \
|
||||
osmoappdesc.py \
|
||||
$(NULL)
|
||||
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||
|
||||
@RELMAKE@
|
||||
|
||||
|
4
README
4
README
@@ -19,10 +19,6 @@ OsmoBSC, and receives and sends RTP streams as configured via MGCP.
|
||||
The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC (found
|
||||
in osmo-msc.git) to instruct OsmoMGW via its MGCP service.
|
||||
|
||||
The libosmo-mgcp library exposes MGCP server utilities used by e.g. OsmoBSC-NAT
|
||||
(found in osmo-bsc.git) to navigate RTP streams through a NAT.
|
||||
(At time of writing, this is still called libosmo-legacy-mgcp.)
|
||||
|
||||
Find OsmoMGW issue tracker and wiki online at
|
||||
https://osmocom.org/projects/osmo-mgw
|
||||
https://osmocom.org/projects/osmo-mgw/wiki
|
||||
|
131
configure.ac
131
configure.ac
@@ -9,6 +9,8 @@ AC_CONFIG_AUX_DIR([.])
|
||||
AM_INIT_AUTOMAKE([dist-bzip2])
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
|
||||
CFLAGS="$CFLAGS -std=gnu11"
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
@@ -22,6 +24,11 @@ AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
LT_INIT
|
||||
|
||||
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
|
||||
AS_CASE(["$LD"],[*clang*],
|
||||
[AS_CASE(["${host_os}"],
|
||||
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
|
||||
|
||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
|
||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
|
||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
|
||||
@@ -29,20 +36,24 @@ 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)
|
||||
|
||||
AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
|
||||
AC_SUBST(LIBRARY_DLSYM)
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.12.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.12.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.12.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.3.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.2.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.2.0)
|
||||
|
||||
CFLAGS="$CFLAGS -pthread"
|
||||
LDFLAGS="$LDFLAGS -pthread"
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
@@ -74,42 +85,6 @@ then
|
||||
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
|
||||
fi
|
||||
|
||||
# Enable/disable transcoding within osmo-bsc_mgcp?
|
||||
AC_ARG_ENABLE([mgcp-transcoding], [AS_HELP_STRING([--enable-mgcp-transcoding], [Build the MGCP gateway with internal transcoding enabled.])],
|
||||
[osmo_ac_mgcp_transcoding="$enableval"],[osmo_ac_mgcp_transcoding="no"])
|
||||
AC_ARG_WITH([g729], [AS_HELP_STRING([--with-g729], [Enable G.729 encoding/decoding.])], [osmo_ac_with_g729="$withval"],[osmo_ac_with_g729="no"])
|
||||
|
||||
if test "$osmo_ac_mgcp_transcoding" = "yes" ; then
|
||||
AC_SEARCH_LIBS([gsm_create], [gsm], [LIBRARY_GSM="$LIBS";LIBS=""], [AC_MSG_ERROR([--enable-mgcp-transcoding: cannot find usable libgsm])])
|
||||
AC_SUBST(LIBRARY_GSM)
|
||||
if test "$osmo_ac_with_g729" = "yes" ; then
|
||||
PKG_CHECK_MODULES(LIBBCG729, libbcg729 >= 0.1, [AC_DEFINE([HAVE_BCG729], [1], [Use bgc729 decoder/encoder])])
|
||||
fi
|
||||
AC_DEFINE(BUILD_MGCP_TRANSCODING, 1, [Define if we want to build the MGCP gateway with transcoding support])
|
||||
fi
|
||||
AM_CONDITIONAL(BUILD_MGCP_TRANSCODING, test "x$osmo_ac_mgcp_transcoding" = "xyes")
|
||||
AC_SUBST(osmo_ac_mgcp_transcoding)
|
||||
|
||||
dnl Checks for typedefs, structures and compiler characteristics
|
||||
|
||||
# The following test is taken from WebKit's webkit.m4
|
||||
saved_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS -fvisibility=hidden "
|
||||
AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])],
|
||||
[ AC_MSG_RESULT([yes])
|
||||
SYMBOL_VISIBILITY="-fvisibility=hidden"],
|
||||
AC_MSG_RESULT([no]))
|
||||
CFLAGS="$saved_CFLAGS"
|
||||
AC_SUBST(SYMBOL_VISIBILITY)
|
||||
|
||||
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([-Werror=null-dereference], [CFLAGS="$CFLAGS -Werror=null-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,
|
||||
@@ -144,6 +119,64 @@ AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
|
||||
AC_MSG_RESULT([$enable_ext_tests])
|
||||
AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes")
|
||||
|
||||
# Generate manuals
|
||||
AC_ARG_ENABLE(manuals,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-manuals],
|
||||
[Generate manual PDFs [default=no]],
|
||||
)],
|
||||
[osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"])
|
||||
AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"])
|
||||
AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals"
|
||||
fallback])
|
||||
if test x"$osmo_ac_build_manuals" = x"yes"
|
||||
then
|
||||
# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback)
|
||||
if test -n "$OSMO_GSM_MANUALS_DIR"; then
|
||||
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)"
|
||||
else
|
||||
OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)"
|
||||
if test -n "$OSMO_GSM_MANUALS_DIR"; then
|
||||
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)"
|
||||
else
|
||||
OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals"
|
||||
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)"
|
||||
fi
|
||||
fi
|
||||
if ! test -d "$OSMO_GSM_MANUALS_DIR"; then
|
||||
AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.")
|
||||
fi
|
||||
|
||||
# Find and run check-depends
|
||||
CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh"
|
||||
if ! test -x "$CHECK_DEPENDS"; then
|
||||
CHECK_DEPENDS="osmo-gsm-manuals-check-depends"
|
||||
fi
|
||||
if ! $CHECK_DEPENDS; then
|
||||
AC_MSG_ERROR("missing dependencies for --enable-manuals")
|
||||
fi
|
||||
|
||||
# Put in Makefile with absolute path
|
||||
OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")"
|
||||
AC_SUBST([OSMO_GSM_MANUALS_DIR])
|
||||
fi
|
||||
|
||||
# https://www.freedesktop.org/software/systemd/man/daemon.html
|
||||
AC_ARG_WITH([systemdsystemunitdir],
|
||||
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
|
||||
[with_systemdsystemunitdir=auto])
|
||||
AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
|
||||
def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
|
||||
|
||||
AS_IF([test "x$def_systemdsystemunitdir" = "x"],
|
||||
[AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
|
||||
[AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
|
||||
with_systemdsystemunitdir=no],
|
||||
[with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
|
||||
AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
|
||||
[AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
|
||||
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])
|
||||
|
||||
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
|
||||
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
|
||||
|
||||
@@ -151,25 +184,23 @@ dnl Generate the output
|
||||
AM_CONFIG_HEADER(bscconfig.h)
|
||||
|
||||
AC_OUTPUT(
|
||||
libosmo-legacy-mgcp.pc
|
||||
libosmo-mgcp-client.pc
|
||||
include/Makefile
|
||||
include/osmocom/Makefile
|
||||
include/osmocom/legacy_mgcp/Makefile
|
||||
include/osmocom/mgcp_client/Makefile
|
||||
include/osmocom/mgcp/Makefile
|
||||
src/Makefile
|
||||
src/libosmo-legacy-mgcp/Makefile
|
||||
src/libosmo-mgcp-client/Makefile
|
||||
src/libosmo-mgcp/Makefile
|
||||
src/osmo-bsc_mgcp/Makefile
|
||||
src/osmo-mgw/Makefile
|
||||
tests/Makefile
|
||||
tests/atlocal
|
||||
tests/legacy_mgcp/Makefile
|
||||
tests/mgcp_client/Makefile
|
||||
tests/mgcp/Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
doc/manuals/Makefile
|
||||
contrib/Makefile
|
||||
contrib/systemd/Makefile
|
||||
contrib/osmo-mgw.spec
|
||||
Makefile)
|
||||
|
@@ -1 +1,3 @@
|
||||
SUBDIRS = systemd
|
||||
|
||||
EXTRA_DIST = ipa.py
|
||||
|
@@ -1,5 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
# jenkins build helper script for openbsc. This is how we build on jenkins.osmocom.org
|
||||
#
|
||||
# environment variables:
|
||||
# * WITH_MANUALS: build manual PDFs if set to "1"
|
||||
# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
|
||||
#
|
||||
|
||||
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
|
||||
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
|
||||
@@ -23,10 +28,17 @@ verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
|
||||
|
||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
export LD_LIBRARY_PATH="$inst/lib"
|
||||
export PATH="$inst/bin:$PATH"
|
||||
|
||||
osmo-build-dep.sh libosmo-abis
|
||||
osmo-build-dep.sh libosmo-netif
|
||||
|
||||
# Additional configure options and depends
|
||||
CONFIG=""
|
||||
if [ "$WITH_MANUALS" = "1" ]; then
|
||||
CONFIG="--enable-manuals"
|
||||
fi
|
||||
|
||||
set +x
|
||||
echo
|
||||
echo
|
||||
@@ -37,13 +49,18 @@ set -x
|
||||
|
||||
cd "$base"
|
||||
autoreconf --install --force
|
||||
./configure $MGCP --enable-vty-tests --enable-external-tests --enable-werror
|
||||
./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
|
||||
LD_LIBRARY_PATH="$inst/lib" \
|
||||
DISTCHECK_CONFIGURE_FLAGS="$MGCP --enable-vty-tests --enable-external-tests" \
|
||||
$MAKE distcheck \
|
||||
DISTCHECK_CONFIGURE_FLAGS="--enable-vty-tests --enable-external-tests $CONFIG" \
|
||||
$MAKE $PARALLEL_MAKE distcheck \
|
||||
|| cat-testlogs.sh
|
||||
|
||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
|
||||
make -C "$base/doc/manuals" publish
|
||||
fi
|
||||
|
||||
$MAKE $PARALLEL_MAKE maintainer-clean
|
||||
osmo-clean-workspace.sh
|
||||
|
137
contrib/osmo-mgw.spec.in
Normal file
137
contrib/osmo-mgw.spec.in
Normal file
@@ -0,0 +1,137 @@
|
||||
#
|
||||
# spec file for package osmo-mgw
|
||||
#
|
||||
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
|
||||
#
|
||||
# All modifications and additions to the file contributed by third parties
|
||||
# remain the property of their copyright owners, unless otherwise agreed
|
||||
# upon. The license for this file, and modifications and additions to the
|
||||
# file, is the same license as for the pristine package itself (unless the
|
||||
# license for the pristine package is not an Open Source License, in which
|
||||
# case the license is the MIT License). An "Open Source License" is a
|
||||
# license that conforms to the Open Source Definition (Version 1.9)
|
||||
# published by the Open Source Initiative.
|
||||
|
||||
# Please submit bugfixes or comments via http://bugs.opensuse.org/
|
||||
#
|
||||
|
||||
Name: osmo-mgw
|
||||
Version: @VERSION@
|
||||
Release: 0
|
||||
Summary: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
|
||||
License: AGPL-3.0-or-later AND GPL-2.0-or-later
|
||||
Group: Hardware/Mobile
|
||||
URL: https://osmocom.org/projects/osmo-mgw
|
||||
Source: %{name}-%{version}.tar.xz
|
||||
BuildRequires: automake >= 1.9
|
||||
BuildRequires: libtool >= 2
|
||||
BuildRequires: pkgconfig >= 0.20
|
||||
%if 0%{?suse_version}
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%endif
|
||||
BuildRequires: pkgconfig(libosmo-netif) >= 1.1.0
|
||||
BuildRequires: pkgconfig(libosmocore) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmogsm) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmocoding) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmoabis) >= 1.2.0
|
||||
BuildRequires: pkgconfig(libosmotrau) >= 1.2.0
|
||||
%{?systemd_requires}
|
||||
|
||||
%description
|
||||
OsmoMGW is Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks.
|
||||
|
||||
%package -n libosmo-mgcp-client9
|
||||
Summary: Osmocom's Media Gateway Control Protocol client library
|
||||
Group: System/Libraries
|
||||
|
||||
%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-client9 = %{version}
|
||||
|
||||
%description -n libosmo-mgcp-client-devel
|
||||
Osmocom's Media Gateway Control Protocol client librarary.
|
||||
|
||||
This subpackage contains libraries and header files for developing
|
||||
applications that want to make use of libosmo-mgcp-client.
|
||||
|
||||
%package -n libosmo-mgcp-devel
|
||||
Summary: Development files for Osmocom's Media Gateway server library
|
||||
Group: Development/Libraries/C and C++
|
||||
|
||||
%description -n libosmo-mgcp-devel
|
||||
Osmocom's Media Gateway Control Protocol server library.
|
||||
|
||||
This subpackage contains libraries and header files for developing
|
||||
applications that want to make use of libosmo-mgcp.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
echo "%{version}" >.tarball-version
|
||||
autoreconf -fi
|
||||
%configure \
|
||||
--disable-static \
|
||||
--docdir=%{_docdir}/%{name} \
|
||||
--with-systemdsystemunitdir=%{_unitdir}
|
||||
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
%make_install
|
||||
find %{buildroot} -type f -name "*.la" -delete -print
|
||||
|
||||
%check
|
||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
|
||||
|
||||
%post -n libosmo-mgcp-client9 -p /sbin/ldconfig
|
||||
%postun -n libosmo-mgcp-client9 -p /sbin/ldconfig
|
||||
|
||||
%if 0%{?suse_version}
|
||||
%preun
|
||||
%service_del_preun osmo-mgw.service
|
||||
|
||||
%postun
|
||||
%service_del_postun osmo-mgw.service
|
||||
|
||||
%pre
|
||||
%service_add_pre osmo-mgw.service
|
||||
|
||||
%post
|
||||
%service_add_post osmo-mgw.service
|
||||
%endif
|
||||
|
||||
%files
|
||||
%license COPYING
|
||||
%doc AUTHORS README
|
||||
%dir %{_docdir}/%{name}/examples
|
||||
%dir %{_docdir}/%{name}/examples/osmo-mgw
|
||||
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw.cfg
|
||||
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw-abis_e1.cfg
|
||||
%{_bindir}/osmo-mgw
|
||||
%{_unitdir}/osmo-mgw.service
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-mgw.cfg
|
||||
|
||||
%files -n libosmo-mgcp-client9
|
||||
%{_libdir}/libosmo-mgcp-client.so.9*
|
||||
|
||||
%files -n libosmo-mgcp-client-devel
|
||||
%{_libdir}/libosmo-mgcp-client.so
|
||||
%{_libdir}/pkgconfig/libosmo-mgcp-client.pc
|
||||
%dir %{_includedir}/osmocom
|
||||
%dir %{_includedir}/osmocom/mgcp_client
|
||||
%{_includedir}/osmocom/mgcp_client/*.h
|
||||
|
||||
%files -n libosmo-mgcp-devel
|
||||
%dir %{_includedir}/osmocom
|
||||
%dir %{_includedir}/osmocom/mgcp
|
||||
%{_includedir}/osmocom/mgcp/*.h
|
||||
|
||||
%changelog
|
6
contrib/systemd/Makefile.am
Normal file
6
contrib/systemd/Makefile.am
Normal file
@@ -0,0 +1,6 @@
|
||||
EXTRA_DIST = osmo-mgw.service
|
||||
|
||||
if HAVE_SYSTEMD
|
||||
systemdsystemunit_DATA = \
|
||||
osmo-mgw.service
|
||||
endif
|
@@ -1,11 +0,0 @@
|
||||
[Unit]
|
||||
Description=OpenBSC MGCP
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/osmo-bsc_mgcp -s -c /etc/osmocom/osmo-bsc-mgcp.cfg
|
||||
RestartSec=2
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
542
debian/changelog
vendored
542
debian/changelog
vendored
@@ -1,3 +1,545 @@
|
||||
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
|
||||
|
||||
-- Harald Welte <laforge@osmocom.org> Wed, 24 Feb 2021 10:56:17 +0100
|
||||
|
||||
osmo-mgw (1.8.0) unstable; urgency=medium
|
||||
|
||||
[ Harald Welte ]
|
||||
* Fix number of endpoints of default trunk
|
||||
* Add CTRL interface to osmo-mgw
|
||||
* Update per-trunk global packet/byte counters in real-time
|
||||
* remove accidential TODO-RELEASE entry
|
||||
* mgcp_protocol: Avoid code duplication between virtual + other trunks
|
||||
* osmo-mgw.spec.in: Use %config(noreplace) to retain current config file
|
||||
* mgcp_client_init(): consider "talloc returns NULL" case
|
||||
* mgcp_client_pending_add(): Consider "talloc returns NULL" case
|
||||
* libosmo-mgcp-client: fix memleak in case if no response is received
|
||||
* debian/control + SPEC: Add missing build dependency to libosmo-abis
|
||||
* osmo-mgw.spec.in: Fix dependency to libosmoabis
|
||||
* osmo-mgw.spec.in: Add missing dependency to libosmotrau
|
||||
* Add example osmo-mgw configuration file for Abis/E1
|
||||
* use osmo_fd_setup() whenever applicable
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* fix vty dump_trunk: start from zero, do not omit first CONN
|
||||
* allow larger MGCP client wqueue: 10 -> 1024
|
||||
* add osmo-mgw --vty-ref-xml: dump VTY ref XML to stdout
|
||||
* manuals: generate vty reference xml at build time
|
||||
* refactor: use msgb to receive, pass and send RTP packets
|
||||
* change timer T2427001 to X2427
|
||||
|
||||
[ Philipp Maier ]
|
||||
* doc: do not bind osmo-mgw to random ip-address
|
||||
* cosmetic: remove excess newlines
|
||||
* mgcp: remove unused callback pointer
|
||||
* mgcp: find better locations for LOGPCONN and LOGPENDP
|
||||
* vty: fix unreachable code (error msg on trunk alloc fail)
|
||||
* mgcp_vty: fix indentation
|
||||
* osmo-mgw: rename struct mgcp_trunk_config and symbol tcfg
|
||||
* mgcp_vty: fix indentation in VTY config write
|
||||
* osmo-mgw: refactor endpoint and trunk handling
|
||||
* mgcp_trunk: remove audio_name and audio_payload
|
||||
* ratectr: move rate counter definitions into mgcp_ratectr.h
|
||||
* cosmetic: fix doxygen
|
||||
* cosmetic: fix doxygen
|
||||
* cosmetic: fix doxygen for mgcp_cleanup_rtp_bridge_cb()
|
||||
* trunk: get rid of virt_trunk pointer
|
||||
* cosmetic: remove excess space
|
||||
* endp: move endpoint name generation into mgcp_endp.c
|
||||
* endp: add name generator function for E1 endpoints
|
||||
* mgcp_client: add docstring for mgcp_client_rtpbridge_wildcard()
|
||||
* mgcp_trunk: fix docstring for mgcp_trunk_alloc()
|
||||
* mgcp_osmux: remove unused define constants
|
||||
* mgcp_trunk: move enum mgcp_trunk_type to mgcp_trunk.h
|
||||
* mgcp_trunk: use enum type for trunk type variable
|
||||
* trunk: parse E1 trunk number
|
||||
* endp: add typeset for e1-endpoints
|
||||
* mgcp_conn: move struct mgcp_conn mgcp_conn.h
|
||||
* mgcp_internal: remove forward declaration struct mgcp_endpoint_type
|
||||
* endp: add E1 endpoint interlocking
|
||||
* endp: require domain name also for E1 endpoints
|
||||
* mgcp_trunk: pick trunk by number and type
|
||||
* mgcp_client: add function to generate e1-endpoint names
|
||||
* mgcp_endp: use define constant to define max number of E1 subslots
|
||||
* mgcp_endp.c: cosmetic: fix sourcecode formatting
|
||||
* mgcp_trunk: remove double check
|
||||
* mgcp_test: do not access endpoint array elements directly
|
||||
* mgcp_vty: fix endpoint number configuration
|
||||
* mgcp_test: remove trunk2 from unit-test
|
||||
* mgcp_trunk: use talloc_zero_array instead of _talloc_zero_array
|
||||
* mgcp_protocol: remove unused variable
|
||||
* mgcp_e1: finish E1 support, add E1 support from libosmoabis
|
||||
* get rid of mgcp_internal.h
|
||||
* mgcp_ratectr: fix comments in header file
|
||||
* mgcp_e1: use return value of e1inp_line_update()
|
||||
* mgcp_protocol: log when endpoint is unavailable
|
||||
* cosmetic: add missing new-line
|
||||
* mgcp_trunk: drop "trunk 0" limitation
|
||||
* mgcp_e1: make E1 ts initalization more debugable
|
||||
* mgcp_e1: remove unused struct member trunk->e1.line
|
||||
* mgcp_endp: use NUM_E1_TS from e1_input.h
|
||||
* mgcp_trunk: increase default number of virtual endpoints
|
||||
* cosmetic: mgcp_client_fsm: change error message.
|
||||
* mgcp_vty: remove remains of loopback functionality
|
||||
* mgcp_vty: deprecate bind early command
|
||||
* mgcp_e1: do not expose function mgcp_e1_init()
|
||||
* mgcp_vty: add user attributes to configuration commands
|
||||
* overview: update section limitations.
|
||||
* overview: fix graph "OsmoMGW used with OsmoBSC"
|
||||
* overview: fix graph "OsmoMGW used with OsmoMSC"
|
||||
* overview: add graph to show E1 integration
|
||||
* configuration: remove hint towards trunk 0 limit
|
||||
* configuration: drop note about lackin E1 support
|
||||
* configuration: add note that changes to trunks need a restart
|
||||
* mgcp_vty: fix config write for trunk 0
|
||||
* mgcp_vty: add missing VTY commands for E1 trunks
|
||||
* mgcp_trunk: get rid of magic numbers for E1 slots
|
||||
* configuration: add section about E1 trunks
|
||||
* usermanual: add chapter about mgcp endpoints
|
||||
* mgcp_client: get rid of magic numbers for E1 slots
|
||||
|
||||
[ Eric ]
|
||||
* tests: dlopen does not imply availability of dlsym..
|
||||
* configure.ac: fix libtool issue with clang and sanitizer
|
||||
|
||||
[ Alexander Chemeris ]
|
||||
* vty: Prepend VTY output of counters for better visual separation.
|
||||
* counters: Implement more useful counters.
|
||||
* mgcp_network: Fix a typo in the comment bahviour -> behaviour
|
||||
* rtp_bridge: Demote a chatty ERROR log message to DEBUG level.
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* Use OSMO_FD_* instead of deprecated BSC_FD_*
|
||||
* Support setting rt-prio and cpu-affinity mask through VTY
|
||||
* cosmetic: Rename main talloc ctx
|
||||
* mgcp-client: Support IPv6 in osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr() implementation
|
||||
* mgcp-client: Fix trailing whitespace in mgcp_client_fsm.h
|
||||
* mgcp_client: Use INET6_ADDRSTRLEN to store addresses in str format
|
||||
* mgcp_client: Allow submitting and parsing IPv6 addr in SDP
|
||||
* mgcp_client: Allow setting IPv6 addresses
|
||||
* mgcp_client: Make MGCP_CLIENT_LOCAL_ADDR_DEFAULT IPv6 compatible
|
||||
* mgcp_client: Support validating IPv6 addresses in CRCX and MDCX commands
|
||||
* mgcp_client: Deprecate unused IPv4-only API
|
||||
* mgcp_client: copy back Connection Information from MDCX ACK
|
||||
* mgw: Fix mgcp_rtp_end field description comment
|
||||
* mgw: Initial IPv6 support
|
||||
* mgw: Introduce VTY cmd 'rtp bind-ip-v6' command
|
||||
* mgw: Find and store RTP conn local_addr once during CRCX handling
|
||||
* mgw: Announce and rebind new local address if change required during MDCX
|
||||
* mgw: osmux: Avoid sending packets on recvonly connection
|
||||
* mgw: Release endpoint after last conn times out
|
||||
* mgw: osmux: Fix conn watchdog timeout not updated
|
||||
* mgw: Don't be case-sensitive when parsing X-Osmo-IGN param
|
||||
* cosmetic: Fix typo in comment
|
||||
* mgw: Avoid logging notice message each time we receive nt param in LCO
|
||||
* mgw: Fix return value documentation for API mgcp_verify_call_id
|
||||
* cosmetic: Fix typo in comment
|
||||
* contrib/jenkins: Enable parallel make in make distcheck
|
||||
* .gitignore: Ignore new autofoo tmp files
|
||||
* tests: Replace deprecated API log_set_print_filename
|
||||
|
||||
[ Oliver Smith ]
|
||||
* contrib: import RPM spec
|
||||
* contrib: integrate RPM spec
|
||||
* Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
|
||||
* contrib/jenkins: don't build osmo-gsm-manuals
|
||||
* configure.ac: set -std=gnu11
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* libosmo-mgcp-client: fix use-after-free in mgcp_client_tx()
|
||||
* libosmo-mgcp-client: fix use-after-free in mgcp_msg_gen()
|
||||
* libosmo-mgcp: fix unsigned compared against 0 in mgcp_trunk_by_name()
|
||||
* libosmo-mgcp: fix unused extra argument to printf() in add_fmtp()
|
||||
* libosmo-mgcp: always check result of msgb_printf() in add_fmtp()
|
||||
* libosmo-mgcp-client: mgcp_client_tx(): return rc on error
|
||||
* debian/control: change maintainer to the Osmocom team / mailing list
|
||||
* vty: use install_lib_element() and install_lib_element_ve()
|
||||
* main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
|
||||
* fixup mgcp_trunk: increase default number of virtual endpoints
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* configure.ac: require libosmoabis + libosmotrau >= 1.0.0
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 23 Feb 2021 18:28:45 +0100
|
||||
|
||||
osmo-mgw (1.7.0) unstable; urgency=medium
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* rename codecs_cmp() to codecs_same()
|
||||
* mgcp_codec: constify 'param' arg
|
||||
* fix crashes: don't assert on incoming RTP packet size
|
||||
* mgcp_send(): stop looping on conversion error
|
||||
* mgcp_codec: split codec_free() off of codec_init()
|
||||
* fix memleak: actually free strings in mgcp_codec_reset_all()
|
||||
* mgcp_test: extend / rewrite test_mgcp_codec_pt_translate()
|
||||
* test_mgcp_codec_pt_translate(): more tests
|
||||
* differentiate AMR octet-aligned=0 vs =1
|
||||
* ptmap: implicitly match '/8000' and '/8000/1'
|
||||
* mgcp_codec: codec_set(): log about all possible errors
|
||||
* mgcp_codec_add: fix audio_name size check
|
||||
* explicitly free codecs in mgcp_rtp_conn_cleanup()
|
||||
* tweak mgcp_parse_audio_ptime_rtpmap()
|
||||
* SDP: store all ptmap entries
|
||||
* mgcp_client_fsm cleanup: Do not assert on DLCX failure
|
||||
* clear pending requests on MGCP failure
|
||||
* client: endp fsm: add notify struct, prep for cancel-notify
|
||||
* client: endp fsm: clear ci[] before dispatching DLCX success
|
||||
* client: endp fsm: allow cancelling a notify event
|
||||
* client: endp fsm: add osmo_mgcpc_ep_ci_ep()
|
||||
* accept MGCP without SDP
|
||||
* fix use-after-free: require new fsm deferred dealloc, check for term
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* mgcp_test: Correctly release all endpoints allocated
|
||||
* mgw: Allocate mgcp_conn instance under tcfg->endpoints
|
||||
|
||||
[ Harald Welte ]
|
||||
* manual: Fix copy+paste error
|
||||
* mgcp_client: Check for osmo_fsm_register() error return value
|
||||
* Move fsm_mgcp_client regstration to __attribute__((contructor))
|
||||
* exit(2) on unsupported positional arguments on command line
|
||||
|
||||
[ Oliver Smith ]
|
||||
* osmoappdesc.py: switch to python 3
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 03 Jan 2020 13:35:09 +0100
|
||||
|
||||
osmo-mgw (1.6.0) unstable; urgency=medium
|
||||
|
||||
[ Oliver Smith ]
|
||||
* Cosmetic: fix spaces/tabs in mgcp_requests[]
|
||||
* Inactive connection cleanup (disabled by default)
|
||||
* debian: create -doc subpackage with pdf manuals
|
||||
* vty: allow 0 as conn-timeout to disable it
|
||||
* vty: update desc of conn-timeout
|
||||
* manuals: update VTY documentation
|
||||
* "make dist" fix for: no rule to make mgcp_common.h
|
||||
* contrib/jenkins.sh: run "make maintainer-clean"
|
||||
|
||||
[ Philipp Maier ]
|
||||
* Add option to GSM HR frames to RFC5593 representation
|
||||
* mgcp_sdp: cosmetic: correct typo in comment
|
||||
* mgco_client: cosmetic: remove excess whitespace
|
||||
* mgcp_sdp: mgcp_sdp.c does not include own header file
|
||||
* mgcp_sdp: untangle parsing of a= parameters
|
||||
* mgcp_network: use mgcp_rtp_codec in downlink format callback
|
||||
* AMR: Add function to convert between bw-effient and octet aligned mode
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* log: don't spam with "can not patch PT" messages
|
||||
* mgcp client: allow setting conn mode for CRCX and MDCX
|
||||
* make codec_table public as osmo_mgcpc_codec_names
|
||||
* move MGW endpoint FSM from osmo-bsc to here
|
||||
* fix: multiple initial CRCX
|
||||
* constify map_codec_to_pt() ptmap arg
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* mgcp-client: whitespace cleanup in mgcp_client.c
|
||||
* mgcp-client: Sanitize implementation of mgcp_client_rtpbridge_wildcard
|
||||
* libosmo-mgcp: Use trunk type during endpoint allocation
|
||||
* cosmetic: Fix typos in comment
|
||||
* cosmetic: tests: mgcp_client_test: clean trailing whitespace
|
||||
* mgcp_msg: Log faulty line on Osmux parsing error
|
||||
* cosmetic: handle_modify_con: Fix indentation level
|
||||
* Introduce log fmt helpers LOGPENDP and LOGPCONN
|
||||
* create_response_with_sdp: Fix inclusion of X-Osmux
|
||||
* osmux: Cleanup of CID alloc pool APIs
|
||||
* mgcp_osmux.c: osmux_enable_endpoint: Fix incorrect return check
|
||||
* osmux: Log osmux socket during osmux_init
|
||||
* mgcp-cli: Change osmo_mgcpc_ep_fsm name to avoid collision with old osmo-bsc
|
||||
* osmux: Document func and return different rc upon osmux init failure
|
||||
* osmux: Fix reception of legacy dummy payloads
|
||||
* osmux: Use LOGPCONN in several log calls
|
||||
* cosmetic: mgcp_udp_send: Document port param is in network byte order
|
||||
* cosmetic: osmux: Document network byte order in port variable
|
||||
* vty: Allow enabling Osmux
|
||||
* osmux: Allocate CID during CRCX
|
||||
* osmux: Introduce mgcp_conn_rtp_is_osmux() helper
|
||||
* osmux: Mark conn_rtp->type as osmux during CRCX
|
||||
* mgcp-cli: Allow submitting X-Osmux on CRCX request
|
||||
* mgcp-cli: Parse X-Osmux on CRCX response
|
||||
* osmux: Handle Osmux MGCP extension in MDCX messages
|
||||
* mgcp-cli: Allow submitting X-Osmux on MDCX request
|
||||
* mgcp-cli: Parse X-Osmux on MDCX response
|
||||
* osmux: Drop unneeded OSMUX_STATE_NEGOTIATING
|
||||
* mgcp-cli: endpoint_fsm: Add API to retrieve Osmux CID from MGW
|
||||
* osmux: Provide correct local port during mgcp resp
|
||||
* osmux: Use remote port to send osmux frames
|
||||
* osmux: Improve logging around osmux enabling events
|
||||
* osmux: Delay osmux enable of conn until remote addr is configured by MDCX
|
||||
* osmux: Redo read/write osmux glue code to have data routed correctly
|
||||
* mgw, mgcp-li: Handle X-Osmux param name as case insensitive
|
||||
* osmux: Use DUMMY ft msg as per Osmux spec
|
||||
* osmux: Fix loopback for Osmux connections
|
||||
* osmux: Fix CID release for non-enabled connections
|
||||
* osmux: Fix hardcoded rtp payload_type 98 in osmux conn
|
||||
* mgcp-cli: Validate osmux cid value during mgcp_msg_gen
|
||||
* mgw: Support uppercase LCO options
|
||||
* mgw: Support lowercase header parameters
|
||||
* mgcp-cli: Support lowercase header parameters
|
||||
* mgw: Support receiving uppercase connection mode
|
||||
* mgw: Support receiving lowercase LCO codec
|
||||
* mgw: Make check of duplicated LCO fields case insensitive
|
||||
* mgw: Allow receiving lowercase MGCP header keyword
|
||||
* mgw: Allow receiving lowercase X-Osmo-Ign Callid field
|
||||
* mgw: Allow receiving uppercase noanswer keyword
|
||||
* doc: X-Osmo-IGN: small formatting and typo fixes
|
||||
* doc: Add Osmux documentation to OsmoMGW User Manual
|
||||
* Catch unsigned integer MGCP parsing errors with strtoul
|
||||
* Fix return variable of strtoul()
|
||||
* Remove undefined param passed to {logging,osmo_stats}_vty_add_cmds
|
||||
* configure.ac: Require libosmo-netif 0.6.0
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* mgcp_internal: LOGPENDP: ensure *endp is not NULL
|
||||
|
||||
[ Harald Welte ]
|
||||
* handle NULL return of rate_ctr_group_alloc()
|
||||
* update .gitignore
|
||||
* mgcp_sdp: Don't check if an unsigned int is below 0
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* configure.ac: drop useless check for -fvisibility=hidden
|
||||
|
||||
[ Daniel Willmann ]
|
||||
* manuals: Add script to regenerate vty/counter documentation
|
||||
* manuals: Update vty/counter documentation
|
||||
|
||||
[ Hoernchen ]
|
||||
* turn -Werror=null-dereference into a warning
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Wed, 07 Aug 2019 16:52:58 +0200
|
||||
|
||||
osmo-mgw (1.5.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* gitignore: Filter *.pc
|
||||
* configure: Find correct libgsm's gsm.h header
|
||||
* vty: Fix typo writing bts-jitter-buffer-delay-{min,max}
|
||||
* Remove libosmo-legacy-mgcp and osmo-bsc-mgcp
|
||||
* debian: Remove dangling symlink to osmo-bsc-mgcp.service
|
||||
* Install systemd services with autotools
|
||||
* Install sample cfg file to /etc/osmocom
|
||||
* mgcp: Fix osmux_cid_bitmap static array size calculation
|
||||
* mgcp_osmux: Use define to calculate rtp_ssrc_winlen
|
||||
* osmux: Avoid initing output without enabling osmux
|
||||
* mgcp: Log endpoint nr consistently as hex
|
||||
* osmux_send_dummy: Avoid logging incorrectly and sending if osmux not enabled
|
||||
* osmux: Don't process regular osmux frames if disabled by cfg
|
||||
* osmux: Move parse_cid of legacy dummy frames to own function
|
||||
* osmux: Make func handling dummy frames independent of endp type
|
||||
* osmux: allow enabling osmux only on correct activating state
|
||||
* osmux: Improve checks around activating and using enabled osmux
|
||||
* osmux.h: Document enum osmux_state
|
||||
* osmux: Avoid processing further frames if conn not found
|
||||
|
||||
[ Philipp Maier ]
|
||||
* mgcp_client_fsm: allow ptmap in mgcp_client_fsm as well
|
||||
* mgcp_network: translate payload type numbers in RTP packets
|
||||
* mgcp_client: use IETF source port as for MGCP
|
||||
* mgcp_client: increment local port number when port is in use
|
||||
* mgcp_test: release endpoints after use
|
||||
* network: do not patch PT of RTCP packets
|
||||
* network: check packets before further processing
|
||||
* Cosmetic: remove misplaced line break
|
||||
* mgcp_sdp: restructure mgcp_write_response_sdp() (rtpmap)
|
||||
* mgcp_sdp: restructure mgcp_write_response_sdp() (audio)
|
||||
* mgcp_client: check local port only once
|
||||
* mgcp_client_fsm: switch to MGCP_CONN_RECV_SEND in add_audio()
|
||||
* mgcp_protocol: increase buffer space for codec name in LCO
|
||||
* osmo-mgw: Add vty reference manual
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* fix handling of "Wrong domain name" error
|
||||
* interpret domain '*' as 'allow any domain'
|
||||
* cosmetic: log: fix "CallIDs does not match"
|
||||
* fix 3G hack: allow any IP for loopback and 0.0.0.0
|
||||
* cosmetic: drop code dup in mgcp_client_fsm.c CRCX
|
||||
* add X-Osmo-IGN MGCP header to ignore CallID
|
||||
* X-Osmo-IGN: rather parse items by token, not char
|
||||
* mgcp_test: fix log of conn_id presence
|
||||
* mgcp_test: fix get_conn_id_from_response() CI length
|
||||
* mgcp_client_test: cosmetically re-arrange reply_to() args
|
||||
* mgcp_client_test: use "\r\n\r\n" instead of "\n\n"
|
||||
* mgcp_client_test: also verify received conn_id
|
||||
* mgcp_client_test: test long conn_id
|
||||
* mgcp_client: error on too long conn id
|
||||
* mgcp_common: rename to MGCP_CONN_ID_MAXLEN
|
||||
* doc: fix mgcp_verify_ci() return val doc
|
||||
* mgcp_verify_ci(): return meaningful error codes
|
||||
* fix mgcp_verify_ci(): off-by-one in max len check
|
||||
* generate shorter 'I:' conn IDs
|
||||
* mgcp_conn_get(): compare conn Id ('I:') case insensitively
|
||||
* mgcp_conn_get(): match conn Id ('I:') despite leading zeros
|
||||
* cosmetic: mgcp_test: fix get_conn_id_from_response()
|
||||
* comment: indicate struct type for mgcp_endpoint.conns
|
||||
* log: avoid logging early media as error
|
||||
* fix osmo-mgw -s; fixes osmo-mgw.service using -s
|
||||
* Importing history from osmo-gsm-manuals.git
|
||||
* OsmoMGW: update VTY reference
|
||||
* OsmoMGW: document the 'X-Osmo-IGN' MGCP extension
|
||||
* mgw: update vty reference
|
||||
* drop/replace very weird logging in mgcp_client.c
|
||||
* check_rtp: on IP:port errors, log the IP and port
|
||||
* osmo-mgw: err-log: include expected domain name
|
||||
* mgcp_client_vty: fix missing talloc_free
|
||||
* mgcp_client: drop a bunch of dead code
|
||||
* mgcp_client: logging tweaks
|
||||
* mgcp_client: make domain part of endpoint configurable
|
||||
* mgcp_client: tweak some log levels INFO -> {DEBUG,ERROR}
|
||||
|
||||
[ Stefan Sperling ]
|
||||
* add VTY commands which show specific mgcp endpoints
|
||||
* add MGCP CRCX command statistics to osmo-mgw
|
||||
* show RTP TX/RX stats in 'mgcp show stats' output
|
||||
* use local variable for rate counters in handle_create_con()
|
||||
* add more mgcp crxc error counters
|
||||
* add MDCX command statistics to osmo-mgw
|
||||
* add aggregated rtp connection stats to osmo-mgw
|
||||
* add DLCX command statistics to osmo-mgw
|
||||
|
||||
[ Harald Welte ]
|
||||
* debian/rules: Don't overwrite .tarball-version
|
||||
* check_rtp_origin(): Don't memcmp sockadd_in and in_addr
|
||||
* check_rtp_origin(): Avoid using memcmp for comparing integer types
|
||||
* vty-ref: Update URI of docbook 5.0 schema
|
||||
|
||||
[ Daniel Willmann ]
|
||||
* mgw: Add new VTY reference
|
||||
* Add initial OsmoMGW manual
|
||||
|
||||
[ Oliver Smith ]
|
||||
* build manuals moved here from osmo-gsm-manuals.git
|
||||
* jenkins.sh: remove leftover MGCP env variable
|
||||
* Fix DISTCHECK_CONFIGURE_FLAGS override
|
||||
* contrib/jenkins.sh: build and publish manuals
|
||||
* contrib: fix makedistcheck with disabled systemd
|
||||
|
||||
-- Harald Welte <laforge@gnumonks.org> Sun, 20 Jan 2019 15:02:18 +0100
|
||||
|
||||
osmo-mgw (1.4.0) unstable; urgency=medium
|
||||
|
||||
[ Philipp Maier ]
|
||||
|
40
debian/control
vendored
40
debian/control
vendored
@@ -1,13 +1,15 @@
|
||||
Source: osmo-mgw
|
||||
Section: net
|
||||
Priority: extra
|
||||
Maintainer: Alexander Couzens <lynxis@fe80.eu>
|
||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
|
||||
Build-Depends: debhelper (>=9),
|
||||
dh-autoreconf,
|
||||
pkg-config,
|
||||
autotools-dev,
|
||||
libosmocore-dev,
|
||||
libosmo-netif-dev
|
||||
libosmocore-dev (>= 1.6.0),
|
||||
libosmo-netif-dev (>= 1.1.0),
|
||||
libosmo-abis-dev (>= 1.2.0),
|
||||
osmo-gsm-manuals-dev (>= 1.2.0)
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Git: git://git.osmocom.org/osmo-mgw.git
|
||||
Vcs-Browser: https://git.osmocom.org/osmo-mgw/
|
||||
@@ -19,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-client3
|
||||
Package: libosmo-mgcp-client9
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
@@ -31,26 +33,14 @@ Package: libosmo-mgcp-client-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-mgcp-client3 (= ${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-bsc-mgcp
|
||||
Architecture: any
|
||||
Multi-Arch: foreign
|
||||
Depends: libosmo-legacy-mgcp0, ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: OsmoBSC-MGCP: Osmocom's Legacy Media Gateway; use osmo-mgw instead.
|
||||
|
||||
Package: libosmo-legacy-mgcp0
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Pre-Depends: ${misc:Pre-Depends}
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: libosmo-legacy-mgcp: Osmocom's Legacy Media Gateway server library; use libosmo-mgcp instead.
|
||||
|
||||
Package: libosmo-legacy-mgcp-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-legacy-mgcp0 (= ${binary:Version}), ${misc:Depends}
|
||||
Description: libosmo-legacy-mgcp: Osmocom's Legacy Media Gateway server library; use libosmo-mgcp instead.
|
||||
Package: osmo-mgw-doc
|
||||
Architecture: all
|
||||
Section: doc
|
||||
Priority: optional
|
||||
Depends: ${misc:Depends}
|
||||
Description: ${misc:Package} PDF documentation
|
||||
Various manuals: user manual, VTY reference manual and/or
|
||||
protocol/interface manuals.
|
||||
|
36
debian/copyright
vendored
36
debian/copyright
vendored
@@ -40,30 +40,6 @@ License: GPL-2.0+
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Files: src/libosmo-legacy-mgcp/g711common.h
|
||||
Copyright: 2000 Abramo Bagnara <abramo@alsa-project.org>
|
||||
License: GPL-2.0+
|
||||
Wrapper for linphone Codec class by Simon Morlat <simon.morlat@linphone.org>
|
||||
.
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
.
|
||||
The FSF address in the above text is the old one.
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General Public License
|
||||
Version 2 can be found in `/usr/share/common-licenses/GPL-2'.
|
||||
|
||||
Files: tests/vty_test_runner.py
|
||||
Copyright: 2013 Holger Hans Peter Freyther
|
||||
2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
|
||||
@@ -87,15 +63,3 @@ License: GPL-3.0+
|
||||
Files: osmoappdesc.py
|
||||
Copyright: 2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
|
||||
License: GPL-3.0+
|
||||
|
||||
Files: src/libosmo-legacy-mgcp/mgcp_osmux.c
|
||||
Copyright: 2012-2013 On Waves ehf <http://www.on-waves.com>
|
||||
2012-2013 Pablo Neira Ayuso <pablo@gnumonks.org>
|
||||
License: AGPL-3.0+
|
||||
All rights not specifically granted under this license are reserved.
|
||||
.
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Affero General Public License as published by the
|
||||
Free Software Foundation; either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
|
4
debian/libosmo-legacy-mgcp-dev.install
vendored
4
debian/libosmo-legacy-mgcp-dev.install
vendored
@@ -1,4 +0,0 @@
|
||||
usr/include/osmocom/legacy_mgcp
|
||||
usr/lib/*/libosmo-legacy-mgcp.so
|
||||
usr/lib/*/libosmo-legacy-mgcp.a
|
||||
usr/lib/*/pkgconfig/libosmo-legacy-mgcp.pc
|
1
debian/libosmo-legacy-mgcp0.install
vendored
1
debian/libosmo-legacy-mgcp0.install
vendored
@@ -1 +0,0 @@
|
||||
usr/lib/*/libosmo-legacy-mgcp.so.*
|
2
debian/osmo-bsc-mgcp.install
vendored
2
debian/osmo-bsc-mgcp.install
vendored
@@ -1,2 +0,0 @@
|
||||
usr/bin/osmo-bsc_mgcp
|
||||
usr/share/doc/osmo-mgw/examples/osmo-bsc_mgcp/mgcp.cfg
|
1
debian/osmo-bsc-mgcp.service
vendored
1
debian/osmo-bsc-mgcp.service
vendored
@@ -1 +0,0 @@
|
||||
../contrib/systemd/osmo-bsc-mgcp.service
|
1
debian/osmo-mgw-doc.install
vendored
Normal file
1
debian/osmo-mgw-doc.install
vendored
Normal file
@@ -0,0 +1 @@
|
||||
usr/share/doc/osmo-mgw-doc/*.pdf
|
3
debian/osmo-mgw.install
vendored
3
debian/osmo-mgw.install
vendored
@@ -1,2 +1,5 @@
|
||||
etc/osmocom/osmo-mgw.cfg
|
||||
lib/systemd/system/osmo-mgw.service
|
||||
usr/bin/osmo-mgw
|
||||
usr/share/doc/osmo-mgw/examples/osmo-mgw/osmo-mgw.cfg
|
||||
usr/share/doc/osmo-mgw/examples/osmo-mgw/osmo-mgw-abis_e1.cfg
|
||||
|
1
debian/osmo-mgw.service
vendored
1
debian/osmo-mgw.service
vendored
@@ -1 +0,0 @@
|
||||
../contrib/systemd/osmo-mgw.service
|
11
debian/rules
vendored
11
debian/rules
vendored
@@ -14,7 +14,7 @@ CFLAGS += -g
|
||||
|
||||
# main packaging script based on dh7 syntax
|
||||
%:
|
||||
dh $@ --with autoreconf
|
||||
dh $@ --with autoreconf
|
||||
|
||||
# debmake generated override targets
|
||||
# Set options for ./configure
|
||||
@@ -29,8 +29,11 @@ CFLAGS += -g
|
||||
override_dh_auto_test:
|
||||
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
|
||||
|
||||
override_dh_autoreconf:
|
||||
echo $(VERSION) > .tarball-version
|
||||
dh_autoreconf
|
||||
override_dh_auto_configure:
|
||||
dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
|
||||
|
||||
# Don't create .pdf.gz files (barely saves space and they can't be opened directly by most pdf readers)
|
||||
override_dh_compress:
|
||||
dh_compress -X.pdf
|
||||
|
||||
# See https://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.html#bpp-dbg
|
||||
|
@@ -1,3 +1,4 @@
|
||||
SUBDIRS = \
|
||||
examples \
|
||||
manuals \
|
||||
$(NULL)
|
||||
|
@@ -1,3 +1,11 @@
|
||||
OSMOCONF_FILES = \
|
||||
osmo-mgw/osmo-mgw.cfg
|
||||
|
||||
osmoconfdir = $(sysconfdir)/osmocom
|
||||
osmoconf_DATA = $(OSMOCONF_FILES)
|
||||
|
||||
EXTRA_DIST = $(OSMOCONF_FILES)
|
||||
|
||||
CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,'
|
||||
|
||||
dist-hook:
|
||||
|
@@ -1,14 +0,0 @@
|
||||
!
|
||||
! MGCP configuration example
|
||||
!
|
||||
mgcp
|
||||
!local ip 10.23.24.2
|
||||
!bts ip 10.24.24.1
|
||||
!bind ip 10.23.24.1
|
||||
bind port 2427
|
||||
rtp base 4000
|
||||
rtp force-ptime 20
|
||||
sdp audio payload number 98
|
||||
sdp audio payload name AMR/8000
|
||||
number endpoints 31
|
||||
no rtcp-omit
|
25
doc/examples/osmo-mgw/osmo-mgw-abis_e1.cfg
Normal file
25
doc/examples/osmo-mgw/osmo-mgw-abis_e1.cfg
Normal file
@@ -0,0 +1,25 @@
|
||||
!
|
||||
! MGCP configuration example
|
||||
!
|
||||
e1_input
|
||||
e1_line 0 driver dahdi
|
||||
e1_line 0 port 0
|
||||
mgcp
|
||||
bind ip 127.0.0.1
|
||||
rtp port-range 4002 16000
|
||||
rtp bind-ip 127.0.0.1
|
||||
rtp ip-probing
|
||||
rtp ip-dscp 46
|
||||
bind port 2427
|
||||
sdp audio payload number 98
|
||||
sdp audio payload name GSM
|
||||
number endpoints 512
|
||||
loop 0
|
||||
force-realloc 1
|
||||
rtcp-omit
|
||||
rtp-patch ssrc
|
||||
rtp-patch timestamp
|
||||
trunk 1
|
||||
rtp keep-alive once
|
||||
no rtp keep-alive
|
||||
line 0
|
@@ -4,13 +4,13 @@
|
||||
mgcp
|
||||
bind ip 127.0.0.1
|
||||
rtp port-range 4002 16000
|
||||
rtp bind-ip 10.9.1.122
|
||||
rtp bind-ip 127.0.0.1
|
||||
rtp ip-probing
|
||||
rtp ip-tos 184
|
||||
rtp ip-dscp 46
|
||||
bind port 2427
|
||||
sdp audio payload number 98
|
||||
sdp audio payload name GSM
|
||||
number endpoints 31
|
||||
number endpoints 512
|
||||
loop 0
|
||||
force-realloc 1
|
||||
rtcp-omit
|
||||
|
23
doc/manuals/Makefile.am
Normal file
23
doc/manuals/Makefile.am
Normal file
@@ -0,0 +1,23 @@
|
||||
EXTRA_DIST = osmomgw-usermanual.adoc \
|
||||
osmomgw-usermanual-docinfo.xml \
|
||||
osmomgw-vty-reference.xml \
|
||||
regen_doc.sh \
|
||||
chapters \
|
||||
vty
|
||||
|
||||
if BUILD_MANUALS
|
||||
ASCIIDOC = osmomgw-usermanual.adoc
|
||||
ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
|
||||
|
||||
VTY_REFERENCE = osmomgw-vty-reference.xml
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
|
||||
|
||||
BUILT_REFERENCE_XML = $(builddir)/vty/mgw_vty_reference.xml
|
||||
$(builddir)/vty/mgw_vty_reference.xml: $(top_builddir)/src/osmo-mgw/osmo-mgw
|
||||
mkdir -p $(builddir)/vty
|
||||
$(top_builddir)/src/osmo-mgw/osmo-mgw --vty-ref-xml > $@
|
||||
|
||||
OSMO_REPOSITORY = osmo-mgw
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
|
||||
endif
|
124
doc/manuals/chapters/configuration.adoc
Normal file
124
doc/manuals/chapters/configuration.adoc
Normal file
@@ -0,0 +1,124 @@
|
||||
== Configuring OsmoMGW
|
||||
|
||||
A basic configation of OsmoMGW mainly consists of specifying the IP address
|
||||
and port on which to listen to MGCP commands, but changing the port range at
|
||||
which the RTP streams terminate as well as limiting operation to a single call
|
||||
agent can be done as well as changing the number of endpoints.
|
||||
|
||||
=== Configuring MGCP
|
||||
|
||||
By default OsmoMGW listens for MGCP on port 2427 on any IP address. If a call
|
||||
agent address is configured then OsmoMGW will only answer MGCP commands from
|
||||
that IP port 2727, otherwise all sources are handled. A domain can be
|
||||
specified
|
||||
|
||||
.Example: MGCP configuration
|
||||
----
|
||||
OsmoMGW(config-mgcp)# bind ip 127.0.0.1
|
||||
OsmoMGW(config-mgcp)# bind port 2427
|
||||
OsmoMGW(config-mgcp)# call-agent ip 127.0.0.1
|
||||
OsmoMGW(config-mgcp)# domain mgw-bsc
|
||||
OsmoMGW(config-mgcp)# local ip 127.0.0.1
|
||||
----
|
||||
|
||||
=== Configuring the trunk
|
||||
|
||||
The first trunk is considered a virtual trunk in OsmoMGW. All
|
||||
endpoints of type "rtpbridge" are routed here. The virtual trunk is configured
|
||||
in the config-mgcp context.
|
||||
|
||||
All other trunks are configured in the config-mgcp-trunk context, but the
|
||||
commands used are identical. Right now trunks are considered only for "ds/e1"
|
||||
type endpoints.
|
||||
|
||||
.Example: MGCP trunk configuration
|
||||
----
|
||||
OsmoMGW(config-mgcp)# number endpoints 63 <1>
|
||||
OsmoMGW(config-mgcp)# rtp bind-ip 10.0.0.1 <2>
|
||||
OsmoMGW(config-mgcp)# rtp port-range 12000-14000 <3>
|
||||
----
|
||||
<1> Maximum number of endpoints that can be allocated at once
|
||||
<2> Use this IP when binding RTP endpoints
|
||||
<3> Use ports in this range when binding RTP endpoints
|
||||
|
||||
There are some options to tweak how RTP forwarding behaves in OsmoMGW:
|
||||
|
||||
.Example: MGCP trunk rtp options
|
||||
----
|
||||
OsmoMGW(config-mgcp)# rtp keep-alive 30 <1>
|
||||
OsmoMGW(config-mgcp)# rtp-patch ssrc <2>
|
||||
OsmoMGW(config-mgcp)# rtp-patch timestamp <3>
|
||||
----
|
||||
<1> Send dummy UDP packets periodically to RTP destination
|
||||
<2> Hide SSRC changes
|
||||
<3> Ensure RTP timestamp is aligned with frame duration
|
||||
|
||||
NOTE: Changes to trunks that affect resource allocation, such as newly created
|
||||
trunks or a change of the number of available endpoints, require a full restart
|
||||
of osmo-mgw!
|
||||
|
||||
=== E1 trunk considerations
|
||||
|
||||
While the RTP bridge trunks are natively based on IP no special considerations
|
||||
are required during setup. E1 trunks are mapped on a physical E1 line, which has
|
||||
to be configured as shown below.
|
||||
|
||||
.Example: E1 line setup
|
||||
----
|
||||
OsmoMGW(config-e1_input)# e1_line 0 driver dahdi <1>
|
||||
OsmoMGW(config-e1_input)# e1_line 0 port 2 <2>
|
||||
----
|
||||
<1> Name of the libosmo-abis driver implementation ("dahdi")
|
||||
<2> Port number of the physical E1 port to use (2)
|
||||
|
||||
In osmo-mgw the e1_input node is used to configure the physical E1 line. The
|
||||
line number will be used internally to identify the configured E1 line. The
|
||||
port number is the physical E1 connector (sometimes called 'span') at the E1
|
||||
hardware. Per trunk an individual E1 line will be needed. Beware that the E1
|
||||
driver may also need configuration settings that are not discussed here.
|
||||
|
||||
.Example: E1 trunk setup
|
||||
----
|
||||
OsmoMGW(config-mgcp)# trunk 0 <1>
|
||||
OsmoMGW(config-mgcp-trunk)# line 0 <2>
|
||||
----
|
||||
<1> Creation of a trunk (0)
|
||||
<2> Reference to the E1 line to use (0)
|
||||
|
||||
The E1 trunk is created along with a number, typically starting at 0, but if
|
||||
required any number from 0-64 is allowed. The E1 trunk configuration concerning
|
||||
the IP related aspects is nearly identical to the configuration of the virtual
|
||||
trunk. However, it is important that the user assigns one of the E1 line numbers
|
||||
that were configured under the e1_input node.
|
||||
|
||||
.Example: A typical configuration with one E1 trunk
|
||||
----
|
||||
e1_input
|
||||
e1_line 0 driver dahdi
|
||||
e1_line 0 port 2
|
||||
mgcp
|
||||
bind ip 127.0.0.1
|
||||
rtp net-range 6000 6011
|
||||
rtp net-bind-ip 192.168.100.130
|
||||
rtp ip-probing
|
||||
rtp ip-dscp 46
|
||||
no rtp keep-alive
|
||||
bind port 2428
|
||||
number endpoints 30
|
||||
loop 0
|
||||
force-realloc 1
|
||||
osmux off
|
||||
rtp-patch rfc5993hr
|
||||
trunk 0
|
||||
rtp keep-alive once
|
||||
no rtp keep-alive
|
||||
line 0
|
||||
----
|
||||
|
||||
NOTE: One E1 trunk always covers a whole E1 line. All subslots (I.640) will be mapped
|
||||
to individual MGCP endpoints. As long as the endpoints remain unused the
|
||||
underlying E1 timeslot is not used.
|
||||
|
||||
NOTE: The E1 trunk implementation also works with T1 lines, however since T1 has
|
||||
24 instead of 31 usable timeslots only the endpoints that fall into that 1-24 timeslot
|
||||
range will be useable.
|
4
doc/manuals/chapters/counters.adoc
Normal file
4
doc/manuals/chapters/counters.adoc
Normal file
@@ -0,0 +1,4 @@
|
||||
[[counters]]
|
||||
== Counters
|
||||
|
||||
include::./counters_generated.adoc[]
|
83
doc/manuals/chapters/counters_generated.adoc
Normal file
83
doc/manuals/chapters/counters_generated.adoc
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
// autogenerated by show asciidoc counters
|
||||
These counters and their description based on OsmoMGW 1.5.0.64-189f (OsmoMGW).
|
||||
|
||||
=== Rate Counters
|
||||
|
||||
// generating tables for rate_ctr_group
|
||||
// rate_ctr_group table aggregated statistics for all rtp connections
|
||||
.all_rtp_conn - aggregated statistics for all rtp connections
|
||||
[options="header"]
|
||||
|===
|
||||
| Name | Reference | Description
|
||||
| all_rtp:err_tstmp_in | <<all_rtp_conn_all_rtp:err_tstmp_in>> | Total inbound rtp-stream timestamp errors.
|
||||
| all_rtp:err_tstmp_out | <<all_rtp_conn_all_rtp:err_tstmp_out>> | Total outbound rtp-stream timestamp errors.
|
||||
| all_rtp:packets_rx | <<all_rtp_conn_all_rtp:packets_rx>> | Total inbound rtp packets.
|
||||
| all_rtp:octets_rx | <<all_rtp_conn_all_rtp:octets_rx>> | Total inbound rtp octets.
|
||||
| all_rtp:packets_tx | <<all_rtp_conn_all_rtp:packets_tx>> | Total outbound rtp packets.
|
||||
| all_rtp:octets_tx | <<all_rtp_conn_all_rtp:octets_tx>> | Total outbound rtp octets.
|
||||
| all_rtp:dropped | <<all_rtp_conn_all_rtp:dropped>> | Total dropped rtp packets.
|
||||
| all_rtp:num_closed_conns | <<all_rtp_conn_all_rtp:num_closed_conns>> | Total number of rtp connections closed.
|
||||
|===
|
||||
// rate_ctr_group table dlcx statistics
|
||||
.dlcx - dlcx statistics
|
||||
[options="header"]
|
||||
|===
|
||||
| Name | Reference | Description
|
||||
| dlcx:success | <<dlcx_dlcx:success>> | DLCX command processed successfully.
|
||||
| dlcx:wildcard | <<dlcx_dlcx:wildcard>> | wildcard names in DLCX commands are unsupported.
|
||||
| dlcx:no_conn | <<dlcx_dlcx:no_conn>> | endpoint specified in DLCX command has no active connections.
|
||||
| dlcx:callid | <<dlcx_dlcx:callid>> | CallId specified in DLCX command mismatches endpoint's CallId .
|
||||
| dlcx:connid | <<dlcx_dlcx:connid>> | connection ID specified in DLCX command does not exist on endpoint.
|
||||
| dlcx:unhandled_param | <<dlcx_dlcx:unhandled_param>> | unhandled parameter in DLCX command.
|
||||
| dlcx:rejected | <<dlcx_dlcx:rejected>> | connection deletion rejected by policy.
|
||||
| dlcx:deferred | <<dlcx_dlcx:deferred>> | connection deletion deferred by policy.
|
||||
|===
|
||||
// rate_ctr_group table mdcx statistics
|
||||
.mdcx - mdcx statistics
|
||||
[options="header"]
|
||||
|===
|
||||
| Name | Reference | Description
|
||||
| mdcx:success | <<mdcx_mdcx:success>> | MDCX command processed successfully.
|
||||
| mdcx:wildcard | <<mdcx_mdcx:wildcard>> | wildcard endpoint names in MDCX commands are unsupported.
|
||||
| mdcx:no_conn | <<mdcx_mdcx:no_conn>> | endpoint specified in MDCX command has no active connections.
|
||||
| mdcx:callid | <<mdcx_mdcx:callid>> | invalid CallId specified in MDCX command.
|
||||
| mdcx:connid | <<mdcx_mdcx:connid>> | invalid connection ID specified in MDCX command.
|
||||
| crcx:unhandled_param | <<mdcx_crcx:unhandled_param>> | unhandled parameter in MDCX command.
|
||||
| mdcx:no_connid | <<mdcx_mdcx:no_connid>> | no connection ID specified in MDCX command.
|
||||
| mdcx:conn_not_found | <<mdcx_mdcx:conn_not_found>> | connection specified in MDCX command does not exist.
|
||||
| mdcx:invalid_mode | <<mdcx_mdcx:invalid_mode>> | invalid connection mode in MDCX command.
|
||||
| mdcx:conn_opt | <<mdcx_mdcx:conn_opt>> | connection options invalid.
|
||||
| mdcx:no_remote_conn_desc | <<mdcx_mdcx:no_remote_conn_desc>> | no opposite end specified for connection.
|
||||
| mdcx:start_rtp_failure | <<mdcx_mdcx:start_rtp_failure>> | failure to start RTP processing.
|
||||
| mdcx:conn_rejected | <<mdcx_mdcx:conn_rejected>> | connection rejected by policy.
|
||||
| mdcx:conn_deferred | <<mdcx_mdcx:conn_deferred>> | connection deferred by policy.
|
||||
|===
|
||||
// rate_ctr_group table crxc statistics
|
||||
.crcx - crxc statistics
|
||||
[options="header"]
|
||||
|===
|
||||
| Name | Reference | Description
|
||||
| crcx:success | <<crcx_crcx:success>> | CRCX command processed successfully.
|
||||
| crcx:bad_action | <<crcx_crcx:bad_action>> | bad action in CRCX command.
|
||||
| crcx:unhandled_param | <<crcx_crcx:unhandled_param>> | unhandled parameter in CRCX command.
|
||||
| crcx:missing_callid | <<crcx_crcx:missing_callid>> | missing CallId in CRCX command.
|
||||
| crcx:invalid_mode | <<crcx_crcx:invalid_mode>> | invalid connection mode in CRCX command.
|
||||
| crcx:limit_exceeded | <<crcx_crcx:limit_exceeded>> | limit of concurrent connections was reached.
|
||||
| crcx:unkown_callid | <<crcx_crcx:unkown_callid>> | unknown CallId in CRCX command.
|
||||
| crcx:alloc_conn_fail | <<crcx_crcx:alloc_conn_fail>> | connection allocation failure.
|
||||
| crcx:no_remote_conn_desc | <<crcx_crcx:no_remote_conn_desc>> | no opposite end specified for connection.
|
||||
| crcx:start_rtp_failure | <<crcx_crcx:start_rtp_failure>> | failure to start RTP processing.
|
||||
| crcx:conn_rejected | <<crcx_crcx:conn_rejected>> | connection rejected by policy.
|
||||
| crcx:no_osmux | <<crcx_crcx:no_osmux>> | no osmux offered by peer.
|
||||
| crcx:conn_opt | <<crcx_crcx:conn_opt>> | connection options invalid.
|
||||
| crcx:codec_nego | <<crcx_crcx:codec_nego>> | codec negotiation failure.
|
||||
| crcx:bind_port | <<crcx_crcx:bind_port>> | port bind failure.
|
||||
|===
|
||||
== Osmo Stat Items
|
||||
|
||||
// generating tables for osmo_stat_items
|
||||
== Osmo Counters
|
||||
|
||||
// generating tables for osmo_counters
|
||||
// there are no ungrouped osmo_counters
|
94
doc/manuals/chapters/mgcp_endpoints.adoc
Normal file
94
doc/manuals/chapters/mgcp_endpoints.adoc
Normal file
@@ -0,0 +1,94 @@
|
||||
== MGCP Endpoints
|
||||
|
||||
MGCP organizes the switching resources in so called endpoints. Each endpoint is
|
||||
referenced by its unique identifier. While RFC 3435 specifies a naming scheme, the
|
||||
actual identifier naming is subject to the implementation and configuration.
|
||||
|
||||
=== RTP proxy / RTP bridge endpoints
|
||||
|
||||
OsmoMGW implements a freely configurable number of `rtpbridge` endpoints. Those
|
||||
endpoints are able to host two connections at a time to model the functionality
|
||||
of a tandem switch.
|
||||
|
||||
RTP bridge endpoint identifiers are referenced by the string `rtpbridge/`, a
|
||||
hexadecimal number without leading zeros and a domain name (configurable).
|
||||
|
||||
----
|
||||
rtpbridge/<number>@<domain>
|
||||
----
|
||||
|
||||
.Example: List of virtual endpoints
|
||||
----
|
||||
rtpbridge/1@mgw
|
||||
rtpbridge/2@mgw
|
||||
rtpbridge/3@mgw
|
||||
rtpbridge/4@mgw
|
||||
rtpbridge/5@mgw
|
||||
rtpbridge/6@mgw
|
||||
rtpbridge/7@mgw
|
||||
rtpbridge/8@mgw
|
||||
rtpbridge/9@mgw
|
||||
rtpbridge/a@mgw
|
||||
rtpbridge/b@mgw
|
||||
rtpbridge/c@mgw
|
||||
rtpbridge/d@mgw
|
||||
rtpbridge/e@mgw
|
||||
rtpbridge/f@mgw
|
||||
rtpbridge/10@mgw
|
||||
----
|
||||
|
||||
=== E1/T1 endpoints
|
||||
|
||||
OsmoMGW supports E1 subslot multiplexing as specified by I.460. All possible
|
||||
subslot combinations are mapped on individual endpoints. The endpoint names
|
||||
are prefixed with `ds/e1-` followed by the trunk number and the E1 timeslot.
|
||||
The subslot is defined by a bit rate and a bit offset.
|
||||
|
||||
----
|
||||
ds/e1-<trunk>/s-<timeslot>/su<bitrate>-<bitoffset>@<domain>
|
||||
----
|
||||
|
||||
.Example: List of endpoints on E1 trunk 0 at E1 timeslot 2
|
||||
----
|
||||
ds/e1-0/s-2/su64-0@mgw
|
||||
ds/e1-0/s-2/su32-0@mgw
|
||||
ds/e1-0/s-2/su32-4@mgw
|
||||
ds/e1-0/s-2/su16-0@mgw
|
||||
ds/e1-0/s-2/su16-2@mgw
|
||||
ds/e1-0/s-2/su16-4@mgw
|
||||
ds/e1-0/s-2/su16-6@mgw
|
||||
ds/e1-0/s-2/su8-0@mgw
|
||||
ds/e1-0/s-2/su8-1@mgw
|
||||
ds/e1-0/s-2/su8-2@mgw
|
||||
ds/e1-0/s-2/su8-3@mgw
|
||||
ds/e1-0/s-2/su8-4@mgw
|
||||
ds/e1-0/s-2/su8-5@mgw
|
||||
ds/e1-0/s-2/su8-6@mgw
|
||||
ds/e1-0/s-2/su8-7@mgw
|
||||
----
|
||||
|
||||
When creating connections on endpoints that reside in one E1 timeslot the call
|
||||
agent must make sure that no overlapping endpoints are used. It is for example
|
||||
not possible to use `ds/e1-0/s-2/su16-2@mgw` and `ds/e1-0/s-2/su8-3@mgw` at the
|
||||
same time because they overlap.
|
||||
|
||||
.Subslot overlapping
|
||||
[options="header"]
|
||||
|===
|
||||
| Bit offset 4+| Subslots
|
||||
| 0 | 8k .2+| 16k .4+| 32k .8+| 64k
|
||||
| 1 | 8k
|
||||
| 2 | 8k .2+| 16k
|
||||
| 3 | 8k
|
||||
| 4 | 8k .2+| 16k .4+| 32k
|
||||
| 5 | 8k
|
||||
| 6 | 8k .2+| 16k
|
||||
| 7 | 8k
|
||||
|===
|
||||
|
||||
NOTE: The current implementation (December 2020) only implements TRAU frame
|
||||
encoding/decoding for 16K and 8K subslots. Endpoints with other bitrates are
|
||||
not yet useable.
|
||||
|
||||
NOTE: the VTY command "show mgcp" can be used to get a list of all available
|
||||
endpoints (including identifiers)
|
72
doc/manuals/chapters/mgcp_extensions.adoc
Normal file
72
doc/manuals/chapters/mgcp_extensions.adoc
Normal file
@@ -0,0 +1,72 @@
|
||||
== MGCP Extensions
|
||||
|
||||
The MGCP protocol is extendable. The following non-standard extensions are
|
||||
understood by OsmoMGW.
|
||||
|
||||
=== `X-Osmo-IGN`
|
||||
|
||||
`X-Osmo-IGN` indicates to OsmoMGW that specific items of an endpoint should be
|
||||
ignored, so that it is lenient on mismatching values that would normally
|
||||
indicate collisions or configuration errors.
|
||||
|
||||
==== `X-Osmo-IGN` Format
|
||||
|
||||
The value part of `X-Osmo-IGN` must be one or more items separated by one or more
|
||||
spaces. Each item consists of one or more non-whitespace characters.
|
||||
|
||||
.Example: `X-Osmo-IGN` format with three ficticious items "X", "abc" and "123".
|
||||
----
|
||||
X-Osmo-IGN: X abc 123
|
||||
----
|
||||
|
||||
`X-Osmo-IGN` must be issued in the MGCP header section (typically as its last item),
|
||||
before the SDP section starts.
|
||||
|
||||
==== Supported `X-Osmo-IGN` Items
|
||||
|
||||
Currently, the following `X-Osmo-IGN` items are supported:
|
||||
|
||||
* `C`: ignore CallID mismatches, i.e. differing "C" values between connections
|
||||
on the same endpoint.
|
||||
|
||||
.Note:
|
||||
`X-Osmo-IGN` does not support ignoring mismatches on the domain part of
|
||||
an endpoint name, e.g. ignoring a mismatch on "example.com" in
|
||||
`rtpbridge/123abc@example.com`. Instead, you may globally configure OsmoMGW
|
||||
with `mgcp` / `domain *` to permit all domain parts.
|
||||
|
||||
===== `X-Osmo-IGN: C`
|
||||
|
||||
By default, OsmoMGW verifies that all CallIDs ("C" values) match for all
|
||||
connections on any one given endpoint. To ignore CallID mismatches, pass a `C`
|
||||
in the `X-Osmo-IGN` header, for the first or the second `CRCX` on an endpoint.
|
||||
When the `X-Osmo-IGN: C` is sent for any one `CRCX` on an endpoint, CallID
|
||||
mismatches will be ignored for that and all subsequent messages for that
|
||||
endpoint. Hence this header only needs to be included once per endpoint, in any
|
||||
`CRCX` message that precedes or coincides with a CallID mismatch.
|
||||
|
||||
This is particularly useful for a BSC that is connected to an A/SCCPlite MSC,
|
||||
where the BSC and MSC each are expected to configure their respective own
|
||||
connection on a shared endpoint. For A/SCCPlite, it is impossible for the BSC
|
||||
to know the CallID that the MSC will use, so CallID mismatches are inevitable.
|
||||
See also OsmoBSC, which will by default pass the `X-Osmo-IGN: C` header for
|
||||
endpoints associated with an A/SCCPlite MSC.
|
||||
|
||||
.Example: `CRCX` message that instructs OsmoMGW to ignore CallID mismatches
|
||||
----
|
||||
CRCX 2 rtpbridge/123abc@mgw MGCP 1.0
|
||||
M: recvonly
|
||||
C: 2
|
||||
L: p:20
|
||||
X-Osmo-IGN: C
|
||||
|
||||
v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 5904 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
a=ptime:40
|
||||
----
|
||||
|
||||
=== `X-Osmux`
|
||||
|
||||
See <<mgcp-extension-osmux>>
|
115
doc/manuals/chapters/overview.adoc
Normal file
115
doc/manuals/chapters/overview.adoc
Normal file
@@ -0,0 +1,115 @@
|
||||
[[overview]]
|
||||
== Overview
|
||||
|
||||
This manual should help you getting started with OsmoMGW. It will cover
|
||||
aspects of configuring and running the media gateway.
|
||||
|
||||
[[intro_overview]]
|
||||
=== About OsmoMGW
|
||||
|
||||
OsmoMGW is the Osmocom implementation of a media gateway to handle user
|
||||
plane (voice) traffic in cellular networks. It can connect and optionally
|
||||
transcode RTP voice streams between different network elements such as BTSs
|
||||
and external entities like SIP. It is typically co-located with both OsmoBSC
|
||||
and OsmoMSC and controlled by them via MGCP, the Media Gateway Control
|
||||
Protocol.
|
||||
|
||||
[[fig-bsc]]
|
||||
.OsmoMGW used with OsmoBSC
|
||||
[graphviz]
|
||||
----
|
||||
digraph G {
|
||||
rankdir = LR;
|
||||
OsmoBTS -> OsmoBSC [label="Abis/IP"];
|
||||
OsmoBSC -> "core-network" [label="3GPP AoIP"];
|
||||
OsmoBSC -> OsmoMGW [label="MGCP"];
|
||||
OsmoBTS -> OsmoMGW [label="RTP",dir=both];
|
||||
OsmoMGW -> "core-network" [label="RTP",dir=both];
|
||||
{rank=same OsmoBSC OsmoMGW}
|
||||
OsmoMGW [color=red];
|
||||
}
|
||||
----
|
||||
|
||||
[[fig-msc]]
|
||||
.OsmoMGW used with OsmoMSC
|
||||
[graphviz]
|
||||
----
|
||||
digraph G {
|
||||
rankdir = LR;
|
||||
"2G BSS" -> OsmoMSC [label="3GPP AoIP"];
|
||||
OsmoMSC -> OsmoMGW [label="MGCP"];
|
||||
"2G BSS" -> OsmoMGW [label="RTP",dir=both];
|
||||
OsmoMSC -> OsmoSIP [label="MNCC"];
|
||||
OsmoSIP -> PBX [label="SIP Trunk"];
|
||||
OsmoMGW -> PBX [label="RTP",dir=both];
|
||||
{rank=same OsmoMSC OsmoMGW}
|
||||
OsmoSIP [label="osmo-sip-connector"];
|
||||
OsmoMGW [color=red];
|
||||
|
||||
hNodeB -> OsmoHNBGW [label="Iuh"];
|
||||
OsmoHNBGW -> OsmoMSC [label="IuCS"];
|
||||
hNodeB -> OsmoMGW [label="RTP",dir=both];
|
||||
}
|
||||
----
|
||||
|
||||
[[fig-bsc-e1]]
|
||||
.Integration of legacy E1 BTS in AoIP network
|
||||
[graphviz]
|
||||
----
|
||||
digraph G {
|
||||
rankdir = LR;
|
||||
BTS -> OsmoBSC [label="Abis/E1"];
|
||||
OsmoBSC -> "core-network" [label="3GPP AoIP"];
|
||||
OsmoBSC -> OsmoMGW [label="MGCP"];
|
||||
BTS -> OsmoMGW [label="TRAU/E1",dir=both];
|
||||
OsmoMGW -> "core-network" [label="RTP",dir=both];
|
||||
{rank=same OsmoBSC OsmoMGW}
|
||||
OsmoMGW [color=red];
|
||||
}
|
||||
----
|
||||
|
||||
=== Software Components
|
||||
|
||||
OsmoMGW contains a variety of different software components, which we’ll
|
||||
quickly describe in this section.
|
||||
|
||||
==== MGCP Implementation
|
||||
|
||||
OsmoMGW brings its own MGCP implementation through which OsmoMGW is
|
||||
controlled.
|
||||
|
||||
The commands implemented are CRCX, MDCX, DLCX and RSIP. The command AUEP is
|
||||
implemented as a stub and will simply respond with OK.
|
||||
|
||||
==== RTP implementation
|
||||
|
||||
Support for IuUP which is used in 3G cells is quite lacking at the moment.
|
||||
3G<->3G and 2G<->2G calls should work, but 3G<->2G does not.
|
||||
|
||||
==== Audio transcoder
|
||||
|
||||
Transcoding is currently not supported in OsmoMGW.
|
||||
|
||||
=== Limitations
|
||||
|
||||
At the moment (November 2020), OsmoMGW implements RTP proxy / RTP bridge
|
||||
type endpoints and E1/T1 16k/8k sub-slots with TRAU frames for classic BTS
|
||||
support. To the RTP proxy / RTP bridge endpoints two RTP connections can
|
||||
be established, which then work as a tandem. E1/T1 endpoints support one
|
||||
RTP connection at a time that is associated with a sub-slot on an E1 line.
|
||||
We are planning to add further endpoint types for:
|
||||
|
||||
- classic E1/T1 timeslots (64kBps alaw/ulaw)
|
||||
- announcement/playout end-points
|
||||
- conference endpoints
|
||||
|
||||
=== Additional resources
|
||||
|
||||
You can find the OsmoMGW issue tracker and wiki online at
|
||||
|
||||
- https://osmocom.org/projects/osmomgw
|
||||
- https://osmocom.org/projects/osmomgw/wiki
|
||||
|
||||
RFC 3435 for MGCP is located at
|
||||
|
||||
- https://tools.ietf.org/html/rfc3435
|
42
doc/manuals/chapters/qos-example.adoc
Normal file
42
doc/manuals/chapters/qos-example.adoc
Normal file
@@ -0,0 +1,42 @@
|
||||
==== Full example of QoS for osmo-mgw
|
||||
|
||||
In the below example we will show the full set of configuration required
|
||||
for both DSCP and PCP differentiation of RTP traffic by osmo-mgw.
|
||||
|
||||
What we want to achieve in this example is the following configuration:
|
||||
|
||||
.DSCP and PCP assignments for osmo-mgw Abis downlink traffic in this example
|
||||
[options="header",width="30%",cols="2,1,1"]
|
||||
|===
|
||||
|Traffic |DSCP|PCP
|
||||
|RTP | 46| 6
|
||||
|===
|
||||
|
||||
. configure the osmo-mgw program to set the DSCP value
|
||||
. configure an egrees QoS map to map from priority to PCP
|
||||
|
||||
.Example Step 1: add related VTY configuration to `osmo-mgw.cfg`
|
||||
----
|
||||
...
|
||||
mgcp
|
||||
rtp ip-dscp 46
|
||||
rtp socket-priority 6
|
||||
...
|
||||
----
|
||||
|
||||
.Example Step 2: egress QoS map to map from socket priority to PCP values
|
||||
----
|
||||
$ sudo ip link set dev eth0.9<1> type vlan egress-qos-map 0:0 5:5 6:6 7:7 <2>
|
||||
----
|
||||
<1> make sure to specify your specific VLAN interface name here instead of `eth0.9`.
|
||||
<2> create a egress QoS map that maps the priority value 1:1 to the PCP. We also include the
|
||||
mapping 5:5 and 7:7 from the osmo-bsc example (see <<userman-osmobsc>>) here.
|
||||
|
||||
NOTE:: The settings of the `ip` command are volatile and only active until
|
||||
the next reboot (or the network device or VLAN is removed). Please refer to
|
||||
the documentation of your specific Linux distribution in order to find out how
|
||||
to make such settings persistent by means of an `ifup` hook whenever the interface
|
||||
comes up. For CentOS/RHEL 8 this can e.g. be achieved by means of an `/sbin/ifup-local
|
||||
script` (when using `network-scripts` and not NetworkManager). For Debian or Ubuntu,
|
||||
this typically involves adding `up` lines to `/etc/network/interfaces` or a `/etc/network/if-up.d`
|
||||
script.
|
25
doc/manuals/chapters/running.adoc
Normal file
25
doc/manuals/chapters/running.adoc
Normal file
@@ -0,0 +1,25 @@
|
||||
== Running OsmoMGW
|
||||
|
||||
The OsmoMGW executable (`osmo-mgw`) offers the following command-line
|
||||
arguments:
|
||||
|
||||
=== SYNOPSIS
|
||||
|
||||
*osmo-mgw* [-h|-V] [-D] [-c 'CONFIGFILE'] [-s]
|
||||
|
||||
=== OPTIONS
|
||||
|
||||
*-h, --help*::
|
||||
Print a short help message about the supported options
|
||||
*-V, --version*::
|
||||
Print the compile-time version number of the program
|
||||
*-D, --daemonize*::
|
||||
Fork the process as a daemon into background.
|
||||
*-c, --config-file 'CONFIGFILE'*::
|
||||
Specify the file and path name of the configuration file to be
|
||||
used. If none is specified, use `osmo-mgw.cfg` in the current
|
||||
working directory.
|
||||
*-s, --disable-color*::
|
||||
Disable colors for logging to stderr. This has mostly been
|
||||
deprecated by VTY based logging configuration, see <<logging>>
|
||||
for more information.
|
47
doc/manuals/osmomgw-usermanual-docinfo.xml
Normal file
47
doc/manuals/osmomgw-usermanual-docinfo.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<revhistory>
|
||||
<revision>
|
||||
<revnumber>1</revnumber>
|
||||
<date>July 24th, 2018</date>
|
||||
<authorinitials>DW</authorinitials>
|
||||
<revremark>
|
||||
Initial version
|
||||
</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Daniel</firstname>
|
||||
<surname>Willmann</surname>
|
||||
<email>dwillmann@sysmocom.de</email>
|
||||
<authorinitials>DW</authorinitials>
|
||||
<affiliation>
|
||||
<shortaffil>sysmocom</shortaffil>
|
||||
<orgname>sysmocom - s.f.m.c. GmbH</orgname>
|
||||
</affiliation>
|
||||
</author>
|
||||
</authorgroup>
|
||||
|
||||
<copyright>
|
||||
<year>2018</year>
|
||||
<holder>sysmocom - s.f.m.c. GmbH</holder>
|
||||
</copyright>
|
||||
|
||||
<legalnotice>
|
||||
<para>
|
||||
Permission is granted to copy, distribute and/or modify this
|
||||
document under the terms of the GNU Free Documentation License,
|
||||
Version 1.3 or any later version published by the Free Software
|
||||
Foundation; with the Invariant Sections being just 'Foreword',
|
||||
'Acknowledgements' and 'Preface', with no Front-Cover Texts,
|
||||
and no Back-Cover Texts. A copy of the license is included in
|
||||
the section entitled "GNU Free Documentation License".
|
||||
</para>
|
||||
<para>
|
||||
The Asciidoc source code of this manual can be found at
|
||||
<ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
|
||||
http://git.osmocom.org/osmo-gsm-manuals/
|
||||
</ulink>
|
||||
</para>
|
||||
</legalnotice>
|
||||
|
39
doc/manuals/osmomgw-usermanual.adoc
Normal file
39
doc/manuals/osmomgw-usermanual.adoc
Normal file
@@ -0,0 +1,39 @@
|
||||
:gfdl-enabled:
|
||||
:program-name: OsmoMGW
|
||||
|
||||
OsmoMGW User Manual
|
||||
====================
|
||||
Daniel Willmann <dwillmann@sysmocom.de>
|
||||
|
||||
|
||||
include::./common/chapters/preface.adoc[]
|
||||
|
||||
include::{srcdir}/chapters/overview.adoc[]
|
||||
|
||||
include::{srcdir}/chapters/running.adoc[]
|
||||
|
||||
include::./common/chapters/vty.adoc[]
|
||||
|
||||
include::./common/chapters/logging.adoc[]
|
||||
|
||||
include::{srcdir}/chapters/configuration.adoc[]
|
||||
|
||||
include::{srcdir}/chapters/mgcp_endpoints.adoc[]
|
||||
|
||||
include::{srcdir}/chapters/mgcp_extensions.adoc[]
|
||||
|
||||
include::./common/chapters/osmux/osmux.adoc[]
|
||||
|
||||
include::./common/chapters/qos-dscp-pcp.adoc[]
|
||||
|
||||
//include::{srcdir}/chapters/counters.adoc[]
|
||||
|
||||
include::./common/chapters/vty_cpu_sched.adoc[]
|
||||
|
||||
include::./common/chapters/port_numbers.adoc[]
|
||||
|
||||
include::./common/chapters/bibliography.adoc[]
|
||||
|
||||
include::./common/chapters/glossary.adoc[]
|
||||
|
||||
include::./common/chapters/gfdl.adoc[]
|
38
doc/manuals/osmomgw-vty-reference.xml
Normal file
38
doc/manuals/osmomgw-vty-reference.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
ex:ts=2:sw=42sts=2:et
|
||||
-*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
-->
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN"
|
||||
"http://docbook.org/xml/5.0/dtd/docbook.dtd" [
|
||||
<!ENTITY chapter-vty SYSTEM "./common/chapters/vty.xml" >
|
||||
<!ENTITY sections-vty SYSTEM "generated/docbook_vty.xml" >
|
||||
]>
|
||||
|
||||
<book>
|
||||
<info>
|
||||
<revhistory>
|
||||
<revision>
|
||||
<revnumber>v1</revnumber>
|
||||
<date>12th December 2017</date>
|
||||
<authorinitials>pm</authorinitials>
|
||||
<revremark>Initial</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
|
||||
<title>OsmoMGW VTY Reference</title>
|
||||
|
||||
<copyright>
|
||||
<year>2017</year>
|
||||
</copyright>
|
||||
|
||||
<legalnotice>
|
||||
<para>This work is copyright by <orgname>sysmocom - s.f.m.c. GmbH</orgname>. All rights reserved.
|
||||
</para>
|
||||
</legalnotice>
|
||||
</info>
|
||||
|
||||
<!-- Main chapters-->
|
||||
&chapter-vty;
|
||||
</book>
|
||||
|
17
doc/manuals/regen_doc.sh
Executable file
17
doc/manuals/regen_doc.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh -x
|
||||
|
||||
if [ -z "$DOCKER_PLAYGROUND" ]; then
|
||||
echo "You need to set DOCKER_PLAYGROUND"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SCRIPT=$(realpath "$0")
|
||||
MANUAL_DIR=$(dirname "$SCRIPT")
|
||||
|
||||
COMMIT=${COMMIT:-$(git log -1 --format=format:%H)}
|
||||
|
||||
cd "$DOCKER_PLAYGROUND/scripts" || exit 1
|
||||
|
||||
OSMO_MGW_BRANCH=$COMMIT ./regen_doc.sh osmo-mgw 4243 \
|
||||
"$MANUAL_DIR/chapters/counters_generated.adoc" \
|
||||
"$MANUAL_DIR/vty/mgw_vty_reference.xml"
|
2
doc/manuals/vty/mgw_vty_additions.xml
Normal file
2
doc/manuals/vty/mgw_vty_additions.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
|
||||
</vtydoc>
|
@@ -3,14 +3,17 @@ SUBDIRS = \
|
||||
$(NULL)
|
||||
|
||||
nobase_include_HEADERS = \
|
||||
osmocom/legacy_mgcp/mgcp.h \
|
||||
osmocom/legacy_mgcp/mgcp_internal.h \
|
||||
osmocom/legacy_mgcp/osmux.h \
|
||||
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_common.h \
|
||||
osmocom/mgcp_client/mgcp_client_pool.h \
|
||||
osmocom/mgcp/mgcp.h \
|
||||
osmocom/mgcp/mgcp_common.h \
|
||||
osmocom/mgcp/mgcp_internal.h \
|
||||
osmocom/mgcp/osmux.h \
|
||||
$(NULL)
|
||||
|
||||
# This gets copied during make from osmocom/mgcp/mgcp_common.h. Therefore it is not included in the source tree and we
|
||||
# don't need to distribute it (OS#4084).
|
||||
nobase_nodist_include_HEADERS = \
|
||||
osmocom/mgcp_client/mgcp_common.h \
|
||||
$(NULL)
|
||||
|
@@ -1,5 +1,4 @@
|
||||
SUBDIRS = \
|
||||
legacy_mgcp \
|
||||
mgcp_client \
|
||||
mgcp \
|
||||
$(NULL)
|
||||
|
@@ -1,4 +0,0 @@
|
||||
noinst_HEADERS = \
|
||||
mgcp_transcode.h \
|
||||
vty.h \
|
||||
$(NULL)
|
@@ -1,298 +0,0 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef OPENBSC_MGCP_H
|
||||
#define OPENBSC_MGCP_H
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#define RTP_PORT_DEFAULT 4000
|
||||
#define RTP_PORT_NET_DEFAULT 16000
|
||||
|
||||
/**
|
||||
* Calculate the RTP audio port for the given multiplex
|
||||
* and the direction. This allows a semi static endpoint
|
||||
* to port calculation removing the need for the BSC
|
||||
* and the MediaGateway to communicate.
|
||||
*
|
||||
* Port usage explained:
|
||||
* base + (multiplex * 2) + 0 == local port to wait for network packets
|
||||
* base + (multiplex * 2) + 1 == local port for rtcp
|
||||
*
|
||||
* The above port will receive packets from the BTS that need
|
||||
* to be patched and forwarded to the network.
|
||||
* The above port will receive packets from the network that
|
||||
* need to be patched and forwarded to the BTS.
|
||||
*
|
||||
* We assume to have a static BTS IP address so we can differentiate
|
||||
* network and BTS.
|
||||
*
|
||||
*/
|
||||
static inline int rtp_calculate_port(int multiplex, int base)
|
||||
{
|
||||
return base + (multiplex * 2);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handling of MGCP Endpoints and the MGCP Config
|
||||
*/
|
||||
struct mgcp_endpoint;
|
||||
struct mgcp_config;
|
||||
struct mgcp_trunk_config;
|
||||
struct mgcp_rtp_end;
|
||||
|
||||
#define MGCP_ENDP_CRCX 1
|
||||
#define MGCP_ENDP_DLCX 2
|
||||
#define MGCP_ENDP_MDCX 3
|
||||
|
||||
/*
|
||||
* what to do with the msg?
|
||||
* - continue as usual?
|
||||
* - reject and send a failure code?
|
||||
* - defer? do not send anything
|
||||
*/
|
||||
#define MGCP_POLICY_CONT 4
|
||||
#define MGCP_POLICY_REJECT 5
|
||||
#define MGCP_POLICY_DEFER 6
|
||||
|
||||
typedef int (*mgcp_realloc)(struct mgcp_trunk_config *cfg, int endpoint);
|
||||
typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state);
|
||||
typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id);
|
||||
typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg);
|
||||
typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
|
||||
|
||||
/**
|
||||
* Return:
|
||||
* < 0 in case no audio was processed
|
||||
* >= 0 in case audio was processed. The remaining payload
|
||||
* length will be returned.
|
||||
*/
|
||||
typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end,
|
||||
char *data, int *len, int buf_size);
|
||||
typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end,
|
||||
struct mgcp_rtp_end *src_end);
|
||||
|
||||
typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
|
||||
int *payload_type,
|
||||
const char**subtype_name,
|
||||
const char**fmtp_extra);
|
||||
|
||||
#define PORT_ALLOC_STATIC 0
|
||||
#define PORT_ALLOC_DYNAMIC 1
|
||||
|
||||
/**
|
||||
* This holds information on how to allocate ports
|
||||
*/
|
||||
struct mgcp_port_range {
|
||||
int mode;
|
||||
|
||||
/* addr or NULL to fall-back to default */
|
||||
char *bind_addr;
|
||||
|
||||
/* pre-allocated from a base? */
|
||||
int base_port;
|
||||
|
||||
/* dynamically allocated */
|
||||
int range_start;
|
||||
int range_end;
|
||||
int last_port;
|
||||
};
|
||||
|
||||
#define MGCP_KEEPALIVE_ONCE (-1)
|
||||
|
||||
struct mgcp_trunk_config {
|
||||
struct llist_head entry;
|
||||
|
||||
struct mgcp_config *cfg;
|
||||
|
||||
int trunk_nr;
|
||||
int trunk_type;
|
||||
|
||||
char *audio_fmtp_extra;
|
||||
char *audio_name;
|
||||
int audio_payload;
|
||||
int audio_send_ptime;
|
||||
int audio_send_name;
|
||||
int audio_loop;
|
||||
|
||||
int no_audio_transcoding;
|
||||
|
||||
int omit_rtcp;
|
||||
int keepalive_interval;
|
||||
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* 0: don't, 1: once */
|
||||
int force_aligned_timing;
|
||||
|
||||
/* spec handling */
|
||||
int force_realloc;
|
||||
|
||||
/* timer */
|
||||
struct osmo_timer_list keepalive_timer;
|
||||
|
||||
unsigned int number_endpoints;
|
||||
struct mgcp_endpoint *endpoints;
|
||||
};
|
||||
|
||||
enum mgcp_role {
|
||||
MGCP_BSC = 0,
|
||||
MGCP_BSC_NAT,
|
||||
};
|
||||
|
||||
enum mgcp_connection_mode {
|
||||
MGCP_CONN_NONE = 0,
|
||||
MGCP_CONN_RECV_ONLY = 1,
|
||||
MGCP_CONN_SEND_ONLY = 2,
|
||||
MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY,
|
||||
MGCP_CONN_LOOPBACK = 4 | MGCP_CONN_RECV_SEND,
|
||||
};
|
||||
|
||||
struct mgcp_config {
|
||||
int source_port;
|
||||
char *local_ip;
|
||||
char *source_addr;
|
||||
char *bts_ip;
|
||||
char *call_agent_addr;
|
||||
|
||||
struct in_addr bts_in;
|
||||
|
||||
/* transcoder handling */
|
||||
char *transcoder_ip;
|
||||
struct in_addr transcoder_in;
|
||||
int transcoder_remote_base;
|
||||
|
||||
/* RTP processing */
|
||||
mgcp_processing rtp_processing_cb;
|
||||
mgcp_processing_setup setup_rtp_processing_cb;
|
||||
|
||||
mgcp_get_format get_net_downlink_format_cb;
|
||||
|
||||
struct osmo_wqueue gw_fd;
|
||||
|
||||
struct mgcp_port_range bts_ports;
|
||||
struct mgcp_port_range net_ports;
|
||||
struct mgcp_port_range transcoder_ports;
|
||||
int endp_dscp;
|
||||
|
||||
int bts_force_ptime;
|
||||
|
||||
mgcp_change change_cb;
|
||||
mgcp_policy policy_cb;
|
||||
mgcp_reset reset_cb;
|
||||
mgcp_realloc realloc_cb;
|
||||
mgcp_rqnt rqnt_cb;
|
||||
void *data;
|
||||
|
||||
uint32_t last_call_id;
|
||||
|
||||
/* trunk handling */
|
||||
struct mgcp_trunk_config trunk;
|
||||
struct llist_head trunks;
|
||||
|
||||
/* only used for start with a static configuration */
|
||||
int last_net_port;
|
||||
int last_bts_port;
|
||||
|
||||
enum mgcp_role role;
|
||||
|
||||
/* osmux translator: 0 means disabled, 1 means enabled */
|
||||
int osmux;
|
||||
/* addr to bind the server to */
|
||||
char *osmux_addr;
|
||||
/* The BSC-NAT may ask for enabling osmux on demand. This tells us if
|
||||
* the osmux socket is already initialized.
|
||||
*/
|
||||
int osmux_init;
|
||||
/* osmux batch factor: from 1 to 4 maximum */
|
||||
int osmux_batch;
|
||||
/* osmux batch size (in bytes) */
|
||||
int osmux_batch_size;
|
||||
/* osmux port */
|
||||
uint16_t osmux_port;
|
||||
/* Pad circuit with dummy messages until we see the first voice
|
||||
* message.
|
||||
*/
|
||||
uint16_t osmux_dummy;
|
||||
|
||||
/* Use a jitterbuffer on the bts-side receiver */
|
||||
bool bts_use_jibuf;
|
||||
/* Minimum and maximum buffer size for the jitter buffer, in ms */
|
||||
uint32_t bts_jitter_delay_min;
|
||||
uint32_t bts_jitter_delay_max;
|
||||
};
|
||||
|
||||
/* config management */
|
||||
struct mgcp_config *mgcp_config_alloc(void);
|
||||
int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
|
||||
enum mgcp_role role);
|
||||
int mgcp_vty_init(void);
|
||||
int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg);
|
||||
void mgcp_release_endp(struct mgcp_endpoint *endp);
|
||||
void mgcp_initialize_endp(struct mgcp_endpoint *endp);
|
||||
int mgcp_reset_transcoder(struct mgcp_config *cfg);
|
||||
void mgcp_format_stats(struct mgcp_endpoint *endp, char *stats, size_t size);
|
||||
int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os, uint32_t *pr, uint32_t *_or, int *loss, uint32_t *jitter);
|
||||
|
||||
void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
|
||||
|
||||
/*
|
||||
* format helper functions
|
||||
*/
|
||||
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);
|
||||
|
||||
/* adc helper */
|
||||
static inline int mgcp_timeslot_to_endpoint(int multiplex, int timeslot)
|
||||
{
|
||||
if (timeslot == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Timeslot should not be 0\n");
|
||||
timeslot = 255;
|
||||
}
|
||||
|
||||
return timeslot + (32 * multiplex);
|
||||
}
|
||||
|
||||
static inline void mgcp_endpoint_to_timeslot(int endpoint, int *multiplex, int *timeslot)
|
||||
{
|
||||
*multiplex = endpoint / 32;
|
||||
*timeslot = endpoint % 32;
|
||||
}
|
||||
|
||||
int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint);
|
||||
int mgcp_send_reset_all(struct mgcp_config *cfg);
|
||||
|
||||
|
||||
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port);
|
||||
int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, struct sockaddr_in *addr, char *buf, int rc);
|
||||
int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len);
|
||||
|
||||
#endif
|
@@ -1,351 +0,0 @@
|
||||
/* MGCP Private Data */
|
||||
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/netif/jibuf.h>
|
||||
|
||||
#define CI_UNUSED 0
|
||||
|
||||
enum mgcp_trunk_type {
|
||||
MGCP_TRUNK_VIRTUAL,
|
||||
MGCP_TRUNK_E1,
|
||||
};
|
||||
|
||||
struct mgcp_rtp_stream_state {
|
||||
uint32_t ssrc;
|
||||
uint16_t last_seq;
|
||||
uint32_t last_timestamp;
|
||||
uint32_t err_ts_counter;
|
||||
int32_t last_tsdelta;
|
||||
uint32_t last_arrival_time;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_state {
|
||||
int initialized;
|
||||
int patch_ssrc;
|
||||
|
||||
uint32_t orig_ssrc;
|
||||
|
||||
int seq_offset;
|
||||
|
||||
int32_t timestamp_offset;
|
||||
uint32_t packet_duration;
|
||||
|
||||
struct mgcp_rtp_stream_state in_stream;
|
||||
struct mgcp_rtp_stream_state out_stream;
|
||||
|
||||
/* jitter and packet loss calculation */
|
||||
int stats_initialized;
|
||||
uint16_t stats_base_seq;
|
||||
uint16_t stats_max_seq;
|
||||
uint32_t stats_ssrc;
|
||||
uint32_t stats_jitter;
|
||||
int32_t stats_transit;
|
||||
int stats_cycles;
|
||||
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
|
||||
};
|
||||
|
||||
struct mgcp_rtp_codec {
|
||||
uint32_t rate;
|
||||
int channels;
|
||||
uint32_t frame_duration_num;
|
||||
uint32_t frame_duration_den;
|
||||
|
||||
int payload_type;
|
||||
char *audio_name;
|
||||
char *subtype_name;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_end {
|
||||
/* statistics */
|
||||
unsigned int packets;
|
||||
unsigned int octets;
|
||||
unsigned int dropped_packets;
|
||||
struct in_addr addr;
|
||||
|
||||
/* in network byte order */
|
||||
int rtp_port, rtcp_port;
|
||||
|
||||
/* audio codec information */
|
||||
struct mgcp_rtp_codec codec;
|
||||
struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */
|
||||
|
||||
/* per endpoint data */
|
||||
int frames_per_packet;
|
||||
uint32_t packet_duration_ms;
|
||||
char *fmtp_extra;
|
||||
int output_enabled;
|
||||
int force_output_ptime;
|
||||
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
|
||||
int force_aligned_timing;
|
||||
void *rtp_process_data;
|
||||
|
||||
/*
|
||||
* Each end has a socket...
|
||||
*/
|
||||
struct osmo_fd rtp;
|
||||
struct osmo_fd rtcp;
|
||||
|
||||
int local_port;
|
||||
int local_alloc;
|
||||
};
|
||||
|
||||
enum {
|
||||
MGCP_TAP_BTS_IN,
|
||||
MGCP_TAP_BTS_OUT,
|
||||
MGCP_TAP_NET_IN,
|
||||
MGCP_TAP_NET_OUT,
|
||||
|
||||
/* last element */
|
||||
MGCP_TAP_COUNT
|
||||
};
|
||||
|
||||
struct mgcp_rtp_tap {
|
||||
int enabled;
|
||||
struct sockaddr_in forward;
|
||||
};
|
||||
|
||||
struct mgcp_lco {
|
||||
char *string;
|
||||
char *codec;
|
||||
int pkt_period_min; /* time in ms */
|
||||
int pkt_period_max; /* time in ms */
|
||||
};
|
||||
|
||||
enum mgcp_type {
|
||||
MGCP_RTP_DEFAULT = 0,
|
||||
MGCP_RTP_TRANSCODED,
|
||||
MGCP_OSMUX_BSC,
|
||||
MGCP_OSMUX_BSC_NAT,
|
||||
};
|
||||
|
||||
#include <osmocom/legacy_mgcp/osmux.h>
|
||||
|
||||
struct mgcp_endpoint {
|
||||
int allocated;
|
||||
uint32_t ci;
|
||||
char *callid;
|
||||
struct mgcp_lco local_options;
|
||||
int conn_mode;
|
||||
int orig_mode;
|
||||
|
||||
/* backpointer */
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_trunk_config *tcfg;
|
||||
|
||||
/* port status for bts/net */
|
||||
struct mgcp_rtp_end bts_end;
|
||||
struct mgcp_rtp_end net_end;
|
||||
|
||||
/*
|
||||
* For transcoding we will send from the local_port
|
||||
* of trans_bts and it will arrive at trans_net from
|
||||
* where we will forward it to the network.
|
||||
*/
|
||||
struct mgcp_rtp_end trans_bts;
|
||||
struct mgcp_rtp_end trans_net;
|
||||
enum mgcp_type type;
|
||||
|
||||
/* sequence bits */
|
||||
struct mgcp_rtp_state net_state;
|
||||
struct mgcp_rtp_state bts_state;
|
||||
|
||||
/* fields for re-transmission */
|
||||
char *last_trans;
|
||||
char *last_response;
|
||||
|
||||
/* tap for the endpoint */
|
||||
struct mgcp_rtp_tap taps[MGCP_TAP_COUNT];
|
||||
|
||||
struct {
|
||||
/* Osmux state: disabled, activating, active */
|
||||
enum osmux_state state;
|
||||
/* Allocated Osmux circuit ID for this endpoint */
|
||||
int allocated_cid;
|
||||
/* Used Osmux circuit ID for this endpoint */
|
||||
uint8_t cid;
|
||||
/* handle to batch messages */
|
||||
struct osmux_in_handle *in;
|
||||
/* handle to unbatch messages */
|
||||
struct osmux_out_handle out;
|
||||
/* statistics */
|
||||
struct {
|
||||
uint32_t chunks;
|
||||
uint32_t octets;
|
||||
} stats;
|
||||
} osmux;
|
||||
|
||||
/* Jitter buffer */
|
||||
struct osmo_jibuf* bts_jb;
|
||||
/* Use a jitterbuffer on the bts-side receiver */
|
||||
bool bts_use_jibuf;
|
||||
/* Minimum and maximum buffer size for the jitter buffer, in ms */
|
||||
uint32_t bts_jitter_delay_min;
|
||||
uint32_t bts_jitter_delay_max;
|
||||
};
|
||||
|
||||
#define for_each_line(line, save) \
|
||||
for (line = strline_r(NULL, &save); line;\
|
||||
line = strline_r(NULL, &save))
|
||||
|
||||
static inline char *strline_r(char *str, char **saveptr)
|
||||
{
|
||||
char *result;
|
||||
|
||||
if (str)
|
||||
*saveptr = str;
|
||||
|
||||
result = *saveptr;
|
||||
|
||||
if (*saveptr != NULL) {
|
||||
*saveptr = strpbrk(*saveptr, "\r\n");
|
||||
|
||||
if (*saveptr != NULL) {
|
||||
char *eos = *saveptr;
|
||||
|
||||
if ((*saveptr)[0] == '\r' && (*saveptr)[1] == '\n')
|
||||
(*saveptr)++;
|
||||
(*saveptr)++;
|
||||
if ((*saveptr)[0] == '\0')
|
||||
*saveptr = NULL;
|
||||
|
||||
*eos = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
|
||||
|
||||
/**
|
||||
* Internal structure while parsing a request
|
||||
*/
|
||||
struct mgcp_parse_data {
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_endpoint *endp;
|
||||
char *trans;
|
||||
char *save;
|
||||
int found;
|
||||
};
|
||||
|
||||
int mgcp_send_dummy(struct mgcp_endpoint *endp);
|
||||
int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port);
|
||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port);
|
||||
int mgcp_bind_trans_bts_rtp_port(struct mgcp_endpoint *enp, int rtp_port);
|
||||
int mgcp_bind_trans_net_rtp_port(struct mgcp_endpoint *enp, int rtp_port);
|
||||
int mgcp_free_rtp_port(struct mgcp_rtp_end *end);
|
||||
|
||||
/* For transcoding we need to manage an in and an output that are connected */
|
||||
static inline int endp_back_channel(int endpoint)
|
||||
{
|
||||
return endpoint + 60;
|
||||
}
|
||||
|
||||
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
|
||||
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
|
||||
|
||||
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);
|
||||
|
||||
void mgcp_state_calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *,
|
||||
uint32_t *expected, int *loss);
|
||||
uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *);
|
||||
|
||||
/* payload processing default functions */
|
||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
|
||||
char *data, int *len, int buf_size);
|
||||
|
||||
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end,
|
||||
struct mgcp_rtp_end *src_end);
|
||||
|
||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
int *payload_type,
|
||||
const char**subtype_name,
|
||||
const char**fmtp_extra);
|
||||
|
||||
/* internal RTP Annex A counting */
|
||||
void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
|
||||
const uint16_t seq, const int32_t transit,
|
||||
const uint32_t ssrc);
|
||||
|
||||
int mgcp_set_ip_tos(int fd, int tos);
|
||||
|
||||
enum {
|
||||
MGCP_DEST_NET = 0,
|
||||
MGCP_DEST_BTS,
|
||||
};
|
||||
|
||||
|
||||
#define MGCP_DUMMY_LOAD 0x23
|
||||
|
||||
|
||||
/**
|
||||
* SDP related information
|
||||
*/
|
||||
/* Assume audio frame length of 20ms */
|
||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
|
||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
|
||||
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
|
||||
#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
|
||||
#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
|
||||
|
||||
#define PTYPE_UNDEFINED (-1)
|
||||
int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p);
|
||||
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
|
||||
int payload_type, const char *audio_name);
|
||||
|
||||
|
||||
/**
|
||||
* Internal network related
|
||||
*/
|
||||
static inline const char *mgcp_net_src_addr(struct mgcp_endpoint *endp)
|
||||
{
|
||||
if (endp->cfg->net_ports.bind_addr)
|
||||
return endp->cfg->net_ports.bind_addr;
|
||||
return endp->cfg->source_addr;
|
||||
}
|
||||
|
||||
static inline const char *mgcp_bts_src_addr(struct mgcp_endpoint *endp)
|
||||
{
|
||||
if (endp->cfg->bts_ports.bind_addr)
|
||||
return endp->cfg->bts_ports.bind_addr;
|
||||
return endp->cfg->source_addr;
|
||||
}
|
||||
|
||||
int mgcp_msg_terminate_nul(struct msgb *msg);
|
||||
|
||||
/**
|
||||
* Internal jitter buffer related
|
||||
*/
|
||||
void mgcp_dejitter_udp_send(struct msgb *msg, void *data);
|
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* (C) 2014 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENBSC_MGCP_TRANSCODE_H
|
||||
#define OPENBSC_MGCP_TRANSCODE_H
|
||||
|
||||
#include "bscconfig.h"
|
||||
|
||||
#include <gsm.h>
|
||||
#ifdef HAVE_BCG729
|
||||
#include <bcg729/decoder.h>
|
||||
#include <bcg729/encoder.h>
|
||||
#endif
|
||||
|
||||
enum audio_format {
|
||||
AF_INVALID,
|
||||
AF_S16,
|
||||
AF_L16,
|
||||
AF_GSM,
|
||||
AF_G729,
|
||||
AF_PCMA,
|
||||
AF_PCMU
|
||||
};
|
||||
|
||||
|
||||
struct mgcp_process_rtp_state {
|
||||
/* decoding */
|
||||
enum audio_format src_fmt;
|
||||
union {
|
||||
gsm gsm_handle;
|
||||
#ifdef HAVE_BCG729
|
||||
bcg729DecoderChannelContextStruct *g729_dec;
|
||||
#endif
|
||||
} src;
|
||||
size_t src_frame_size;
|
||||
size_t src_samples_per_frame;
|
||||
|
||||
/* processing */
|
||||
|
||||
/* encoding */
|
||||
enum audio_format dst_fmt;
|
||||
union {
|
||||
gsm gsm_handle;
|
||||
#ifdef HAVE_BCG729
|
||||
bcg729EncoderChannelContextStruct *g729_enc;
|
||||
#endif
|
||||
} dst;
|
||||
size_t dst_frame_size;
|
||||
size_t dst_samples_per_frame;
|
||||
int dst_packet_duration;
|
||||
|
||||
int is_running;
|
||||
uint16_t next_seq;
|
||||
uint32_t next_time;
|
||||
int16_t samples[10*160];
|
||||
size_t sample_cnt;
|
||||
size_t sample_offs;
|
||||
};
|
||||
|
||||
|
||||
int mgcp_transcoding_setup(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end,
|
||||
struct mgcp_rtp_end *src_end);
|
||||
|
||||
void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp,
|
||||
int *payload_type,
|
||||
const char**audio_name,
|
||||
const char**fmtp_extra);
|
||||
|
||||
int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end,
|
||||
char *data, int *len, int buf_size);
|
||||
|
||||
int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst);
|
||||
#endif /* OPENBSC_MGCP_TRANSCODE_H */
|
@@ -1,41 +0,0 @@
|
||||
#ifndef _OPENBSC_OSMUX_H_
|
||||
#define _OPENBSC_OSMUX_H_
|
||||
|
||||
#include <osmocom/netif/osmux.h>
|
||||
|
||||
#define OSMUX_PORT 1984
|
||||
|
||||
enum {
|
||||
OSMUX_ROLE_BSC = 0,
|
||||
OSMUX_ROLE_BSC_NAT,
|
||||
};
|
||||
|
||||
int osmux_init(int role, struct mgcp_config *cfg);
|
||||
int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint16_t port);
|
||||
void osmux_disable_endpoint(struct mgcp_endpoint *endp);
|
||||
void osmux_allocate_cid(struct mgcp_endpoint *endp);
|
||||
void osmux_release_cid(struct mgcp_endpoint *endp);
|
||||
|
||||
int osmux_xfrm_to_rtp(struct mgcp_endpoint *endp, int type, char *buf, int rc);
|
||||
int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp);
|
||||
|
||||
int osmux_send_dummy(struct mgcp_endpoint *endp);
|
||||
|
||||
int osmux_get_cid(void);
|
||||
void osmux_put_cid(uint8_t osmux_cid);
|
||||
int osmux_used_cid(void);
|
||||
|
||||
enum osmux_state {
|
||||
OSMUX_STATE_DISABLED = 0,
|
||||
OSMUX_STATE_NEGOTIATING,
|
||||
OSMUX_STATE_ACTIVATING,
|
||||
OSMUX_STATE_ENABLED,
|
||||
};
|
||||
|
||||
enum osmux_usage {
|
||||
OSMUX_USAGE_OFF = 0,
|
||||
OSMUX_USAGE_ON = 1,
|
||||
OSMUX_USAGE_ONLY = 2,
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
enum mgcp_vty_node {
|
||||
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
TRUNK_NODE,
|
||||
};
|
@@ -6,5 +6,11 @@ noinst_HEADERS = \
|
||||
mgcp_endp.h \
|
||||
mgcp_sdp.h \
|
||||
mgcp_codec.h \
|
||||
mgcp_ctrl.h \
|
||||
mgcp_trunk.h \
|
||||
debug.h \
|
||||
mgcp_ratectr.h \
|
||||
mgcp_e1.h \
|
||||
mgcp_network.h \
|
||||
mgcp_protocol.h \
|
||||
$(NULL)
|
||||
|
@@ -29,6 +29,7 @@
|
||||
/* Debug Areas of the code */
|
||||
enum {
|
||||
DRTP,
|
||||
DE1,
|
||||
Debug_LastEntry,
|
||||
};
|
||||
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
@@ -33,6 +34,9 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "mgcp_ratectr.h"
|
||||
|
||||
#define RTP_PORT_DEFAULT_RANGE_START 16002
|
||||
#define RTP_PORT_DEFAULT_RANGE_END RTP_PORT_DEFAULT_RANGE_START + 64
|
||||
@@ -42,7 +46,7 @@
|
||||
*/
|
||||
struct mgcp_endpoint;
|
||||
struct mgcp_config;
|
||||
struct mgcp_trunk_config;
|
||||
struct mgcp_trunk;
|
||||
struct mgcp_rtp_end;
|
||||
|
||||
#define MGCP_ENDP_CRCX 1
|
||||
@@ -59,10 +63,7 @@ struct mgcp_rtp_end;
|
||||
#define MGCP_POLICY_REJECT 5
|
||||
#define MGCP_POLICY_DEFER 6
|
||||
|
||||
typedef int (*mgcp_realloc)(struct mgcp_trunk_config *cfg, int endpoint);
|
||||
typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state);
|
||||
typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id);
|
||||
typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg);
|
||||
typedef int (*mgcp_reset)(struct mgcp_trunk *cfg);
|
||||
typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
|
||||
|
||||
/**
|
||||
@@ -81,18 +82,21 @@ typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn_dst,
|
||||
struct mgcp_conn_rtp *conn_src);
|
||||
|
||||
struct mgcp_rtp_codec;
|
||||
|
||||
typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
|
||||
int *payload_type,
|
||||
const char**subtype_name,
|
||||
const char**fmtp_extra,
|
||||
const struct mgcp_rtp_codec **codec,
|
||||
const char **fmtp_extra,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
char bind_addr_v4[INET6_ADDRSTRLEN];
|
||||
char bind_addr_v6[INET6_ADDRSTRLEN];
|
||||
|
||||
/* dynamically allocated */
|
||||
int range_start;
|
||||
@@ -118,45 +122,6 @@ struct mgcp_port_range {
|
||||
#define MGCP_KEEPALIVE_ONCE (-1)
|
||||
#define MGCP_KEEPALIVE_NEVER 0
|
||||
|
||||
struct mgcp_trunk_config {
|
||||
struct llist_head entry;
|
||||
|
||||
struct mgcp_config *cfg;
|
||||
|
||||
int trunk_nr;
|
||||
int trunk_type;
|
||||
|
||||
char *audio_fmtp_extra;
|
||||
char *audio_name;
|
||||
int audio_payload;
|
||||
int audio_send_ptime;
|
||||
int audio_send_name;
|
||||
int audio_loop;
|
||||
|
||||
int no_audio_transcoding;
|
||||
|
||||
int omit_rtcp;
|
||||
int keepalive_interval;
|
||||
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* 0: don't, 1: once */
|
||||
int force_aligned_timing;
|
||||
|
||||
/* spec handling */
|
||||
int force_realloc;
|
||||
|
||||
/* timer */
|
||||
struct osmo_timer_list keepalive_timer;
|
||||
|
||||
/* When set, incoming RTP packets are not filtered
|
||||
* when ports and ip-address do not match (debug) */
|
||||
int rtp_accept_all;
|
||||
|
||||
unsigned int number_endpoints;
|
||||
int vty_number_endpoints;
|
||||
struct mgcp_endpoint *endpoints;
|
||||
};
|
||||
|
||||
enum mgcp_role {
|
||||
MGCP_BSC = 0,
|
||||
MGCP_BSC_NAT,
|
||||
@@ -164,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;
|
||||
@@ -178,20 +143,15 @@ struct mgcp_config {
|
||||
|
||||
struct mgcp_port_range net_ports;
|
||||
int endp_dscp;
|
||||
int endp_priority;
|
||||
|
||||
int force_ptime;
|
||||
|
||||
mgcp_change change_cb;
|
||||
mgcp_policy policy_cb;
|
||||
mgcp_reset reset_cb;
|
||||
mgcp_realloc realloc_cb;
|
||||
mgcp_rqnt rqnt_cb;
|
||||
void *data;
|
||||
|
||||
uint32_t last_call_id;
|
||||
|
||||
/* trunk handling */
|
||||
struct mgcp_trunk_config trunk;
|
||||
/* list holding the trunks */
|
||||
struct llist_head trunks;
|
||||
|
||||
enum mgcp_role role;
|
||||
@@ -199,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.
|
||||
*/
|
||||
@@ -216,6 +176,16 @@ struct mgcp_config {
|
||||
uint16_t osmux_dummy;
|
||||
/* domain name of the media gateway */
|
||||
char domain[255+1];
|
||||
|
||||
/* time after which inactive connections (CIs) get closed */
|
||||
int conn_timeout;
|
||||
|
||||
/* osmocom CTRL interface */
|
||||
struct ctrl_handle *ctrl;
|
||||
|
||||
/* global rate counters to measure the MGWs overall performance and
|
||||
* health */
|
||||
struct mgcp_ratectr_global ratectr;
|
||||
};
|
||||
|
||||
/* config management */
|
||||
@@ -223,8 +193,7 @@ struct mgcp_config *mgcp_config_alloc(void);
|
||||
int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
|
||||
enum mgcp_role role);
|
||||
int mgcp_vty_init(void);
|
||||
int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg);
|
||||
void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
|
||||
void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval);
|
||||
|
||||
/*
|
||||
* format helper functions
|
||||
@@ -232,9 +201,10 @@ void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
|
||||
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);
|
||||
|
||||
|
||||
int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint);
|
||||
int mgcp_send_reset_ep(struct mgcp_endpoint *endp);
|
||||
int mgcp_send_reset_all(struct mgcp_config *cfg);
|
||||
|
||||
|
||||
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port);
|
||||
int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len);
|
||||
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint8_t dscp,
|
||||
uint8_t prio);
|
||||
int mgcp_udp_send(int fd, struct osmo_sockaddr *addr, int port, const char *buf, int len);
|
||||
|
@@ -1,6 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
|
||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
|
||||
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
|
||||
#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
|
||||
#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
|
||||
|
||||
#define PTYPE_UNDEFINED (-1)
|
||||
|
||||
struct mgcp_conn_rtp;
|
||||
|
||||
void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
|
||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name);
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param);
|
||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
|
||||
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
|
||||
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
|
||||
const char *subtype_name, unsigned int match_nr);
|
||||
|
@@ -49,6 +49,21 @@ enum mgcp_connection_mode {
|
||||
MGCP_CONN_LOOPBACK = 4 | MGCP_CONN_RECV_SEND,
|
||||
};
|
||||
|
||||
#define MGCP_X_OSMO_IGN_HEADER "X-Osmo-IGN:"
|
||||
#define MGCP_X_OSMO_OSMUX_HEADER "X-Osmux:"
|
||||
|
||||
/* Values should be bitwise-OR-able */
|
||||
enum mgcp_x_osmo_ign {
|
||||
MGCP_X_OSMO_IGN_NONE = 0,
|
||||
MGCP_X_OSMO_IGN_CALLID = 1,
|
||||
};
|
||||
|
||||
/* Codec parameters (communicated via SDP/fmtp) */
|
||||
struct mgcp_codec_param {
|
||||
bool amr_octet_aligned_present;
|
||||
bool amr_octet_aligned;
|
||||
};
|
||||
|
||||
/* Ensure that the msg->l2h is NUL terminated. */
|
||||
static inline int mgcp_msg_terminate_nul(struct msgb *msg)
|
||||
{
|
||||
@@ -71,9 +86,12 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg)
|
||||
/* Maximum length of the comment field */
|
||||
#define MGCP_COMMENT_MAXLEN 256
|
||||
|
||||
/* String length of Connection Identifiers
|
||||
* (see also RFC3435 2.1.3.2 Names of Connections) */
|
||||
#define MGCP_CONN_ID_LENGTH 32+1
|
||||
/* Maximum allowed String length of Connection Identifiers as per spec
|
||||
* (see also RFC3435 2.1.3.2 Names of Connections), plus one for '\0'. */
|
||||
#define MGCP_CONN_ID_MAXLEN 32+1
|
||||
|
||||
/* Deprecated: old name of MGCP_CONN_ID_MAXLEN. */
|
||||
#define MGCP_CONN_ID_LENGTH MGCP_CONN_ID_MAXLEN
|
||||
|
||||
/* String length of Endpoint Identifiers.
|
||||
/ (see also RFC3435 section 3.2.1.3) */
|
||||
@@ -82,6 +100,10 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg)
|
||||
/* A prefix to denote the virtual trunk (RTP on both ends) */
|
||||
#define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/"
|
||||
|
||||
/* A prefix to denote the e1 trunk
|
||||
* (see also RFC3435 section E.2) */
|
||||
#define MGCP_ENDPOINT_PREFIX_E1_TRUNK "ds/e1-"
|
||||
|
||||
/* Maximal number of payload types / codecs that can be negotiated via SDP at
|
||||
* at once. */
|
||||
#define MGCP_MAX_CODECS 10
|
||||
|
@@ -23,10 +23,114 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_network.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#define LOGPCONN(conn, cat, level, fmt, args...) \
|
||||
LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
|
||||
(conn)->id, \
|
||||
## args)
|
||||
|
||||
#define LOG_CONN(conn, level, fmt, args...) \
|
||||
LOGP(DRTP, level, "(%s I:%s) " fmt, \
|
||||
(conn)->endp ? (conn)->endp->name : "none", (conn)->id, ## args)
|
||||
|
||||
#define LOG_CONN_RTP(conn_rtp, level, fmt, args...) \
|
||||
LOG_CONN((conn_rtp)->conn, level, fmt, ## args)
|
||||
|
||||
/* Specific rtp connection type (see struct mgcp_conn_rtp) */
|
||||
enum mgcp_conn_rtp_type {
|
||||
MGCP_RTP_DEFAULT = 0,
|
||||
MGCP_OSMUX_BSC,
|
||||
MGCP_OSMUX_BSC_NAT,
|
||||
};
|
||||
|
||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
|
||||
* contains a useful connection description (currently only RTP) */
|
||||
enum mgcp_conn_type {
|
||||
MGCP_CONN_TYPE_RTP,
|
||||
};
|
||||
|
||||
/* MGCP connection (RTP) */
|
||||
struct mgcp_conn_rtp {
|
||||
|
||||
/* Backpointer to conn struct */
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
/* Specific connection type */
|
||||
enum mgcp_conn_rtp_type type;
|
||||
|
||||
/* Port status */
|
||||
struct mgcp_rtp_end end;
|
||||
|
||||
/* Sequence bits */
|
||||
struct mgcp_rtp_state state;
|
||||
|
||||
/* taps for the rtp connection; one per direction */
|
||||
struct mgcp_rtp_tap tap_in;
|
||||
struct mgcp_rtp_tap tap_out;
|
||||
|
||||
/* Osmux states (optional) */
|
||||
struct {
|
||||
/* Osmux state: disabled, activating, active */
|
||||
enum osmux_state state;
|
||||
/* Is cid holding valid data? is it allocated from pool? */
|
||||
bool cid_allocated;
|
||||
/* Allocated Osmux circuit ID for this conn */
|
||||
uint8_t cid;
|
||||
/* handle to batch messages */
|
||||
struct osmux_in_handle *in;
|
||||
/* handle to unbatch messages */
|
||||
struct osmux_out_handle out;
|
||||
/* statistics */
|
||||
struct {
|
||||
uint32_t chunks;
|
||||
uint32_t octets;
|
||||
} stats;
|
||||
} osmux;
|
||||
|
||||
struct rate_ctr_group *rate_ctr_group;
|
||||
};
|
||||
|
||||
/*! MGCP connection (untyped) */
|
||||
struct mgcp_conn {
|
||||
/*! list head */
|
||||
struct llist_head entry;
|
||||
|
||||
/*! Backpointer to the endpoint where the conn belongs to */
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
/*! type of the connection (union) */
|
||||
enum mgcp_conn_type type;
|
||||
|
||||
/*! mode of the connection */
|
||||
enum mgcp_connection_mode mode;
|
||||
|
||||
/*! copy of the mode to restore the original setting (VTY) */
|
||||
enum mgcp_connection_mode mode_orig;
|
||||
|
||||
/*! connection id to identify the connection */
|
||||
char id[MGCP_CONN_ID_MAXLEN];
|
||||
|
||||
/*! human readable name (vty, logging) */
|
||||
char name[256];
|
||||
|
||||
/*! activity tracker (for cleaning up inactive connections) */
|
||||
struct osmo_timer_list watchdog;
|
||||
|
||||
/*! union with connection description */
|
||||
union {
|
||||
struct mgcp_conn_rtp rtp;
|
||||
} u;
|
||||
|
||||
/*! pointer to optional private data */
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/* RTP connection related counters */
|
||||
enum {
|
||||
IN_STREAM_ERR_TSTMP_CTR,
|
||||
@@ -35,9 +139,43 @@ enum {
|
||||
RTP_OCTETS_RX_CTR,
|
||||
RTP_PACKETS_TX_CTR,
|
||||
RTP_OCTETS_TX_CTR,
|
||||
RTP_DROPPED_PACKETS_CTR
|
||||
RTP_DROPPED_PACKETS_CTR,
|
||||
RTP_NUM_CONNECTIONS,
|
||||
};
|
||||
|
||||
/* RTP per-connection statistics. Instances of the corresponding rate counter group
|
||||
* exist for the lifetime of an RTP connection.
|
||||
* Must be kept in sync with all_rtp_conn_rate_ctr_desc below */
|
||||
static const struct rate_ctr_desc mgcp_conn_rate_ctr_desc[] = {
|
||||
[IN_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:in", "Inbound rtp-stream timestamp errors."},
|
||||
[OUT_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:out", "Outbound rtp-stream timestamp errors."},
|
||||
[RTP_PACKETS_RX_CTR] = {"rtp:packets_rx", "Inbound rtp packets."},
|
||||
[RTP_OCTETS_RX_CTR] = {"rtp:octets_rx", "Inbound rtp octets."},
|
||||
[RTP_PACKETS_TX_CTR] = {"rtp:packets_tx", "Outbound rtp packets."},
|
||||
[RTP_OCTETS_TX_CTR] = {"rtp:octets_tx", "Outbound rtp octets."},
|
||||
[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "dropped rtp packets."}
|
||||
};
|
||||
|
||||
/* Aggregated RTP connection stats. These are updated when an RTP connection is freed.
|
||||
* Must be kept in sync with mgcp_conn_rate_ctr_desc above */
|
||||
static const struct rate_ctr_desc all_rtp_conn_rate_ctr_desc[] = {
|
||||
[IN_STREAM_ERR_TSTMP_CTR] = {"all_rtp:err_tstmp_in", "Total inbound rtp-stream timestamp errors."},
|
||||
[OUT_STREAM_ERR_TSTMP_CTR] = {"all_rtp:err_tstmp_out", "Total outbound rtp-stream timestamp errors."},
|
||||
[RTP_PACKETS_RX_CTR] = {"all_rtp:packets_rx", "Total inbound rtp packets."},
|
||||
[RTP_OCTETS_RX_CTR] = {"all_rtp:octets_rx", "Total inbound rtp octets."},
|
||||
[RTP_PACKETS_TX_CTR] = {"all_rtp:packets_tx", "Total outbound rtp packets."},
|
||||
[RTP_OCTETS_TX_CTR] = {"all_rtp:octets_tx", "Total outbound rtp octets."},
|
||||
[RTP_DROPPED_PACKETS_CTR] = {"all_rtp:dropped", "Total dropped rtp packets."},
|
||||
|
||||
/* This last counter does not exist in per-connection stats, only here. */
|
||||
[RTP_NUM_CONNECTIONS] = {"all_rtp:num_closed_conns", "Total number of rtp connections closed."}
|
||||
};
|
||||
|
||||
/* Was conn configured to handle Osmux? */
|
||||
static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) {
|
||||
return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT;
|
||||
}
|
||||
|
||||
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
enum mgcp_conn_type type, char *name);
|
||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
|
||||
@@ -48,3 +186,5 @@ void mgcp_conn_free_oldest(struct mgcp_endpoint *endp);
|
||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp);
|
||||
char *mgcp_conn_dump(struct mgcp_conn *conn);
|
||||
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn);
|
||||
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp);
|
||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);
|
||||
|
24
include/osmocom/mgcp/mgcp_ctrl.h
Normal file
24
include/osmocom/mgcp/mgcp_ctrl.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg,
|
||||
const char *bind_addr, uint16_t port);
|
||||
|
27
include/osmocom/mgcp/mgcp_e1.h
Normal file
27
include/osmocom/mgcp/mgcp_e1.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
/* A 64k timeslot on an E1 line can be subdevied into the following
|
||||
* subslot combinations:
|
||||
*
|
||||
* subslot: offset:
|
||||
* [ ][ ][ 16k ][8k_subslot] 0
|
||||
* [ ][ 32k ][_subslot__][8k_subslot] 1
|
||||
* [ ][ subslot ][ 16k ][8k_subslot] 2
|
||||
* [ 64k ][__________][_subslot__][8k_subslot] 3
|
||||
* [ timeslot ][ ][ 16k ][8k_subslot] 4
|
||||
* [ ][ 32K ][_subslot__][8k_subslot] 5
|
||||
* [ ][ subslot ][ 16k ][8k_subslot] 6
|
||||
* [ ][ ][ subslot ][8k_subslot] 7
|
||||
*
|
||||
* Since overlapping assignment of subslots is not possible there is a limited
|
||||
* set of subslot assignments possible. The e1_rates array lists the possible
|
||||
* assignments as depicted above. Also each subslot assignment comes along with
|
||||
* a bit offset in the E1 bitstream. The e1_offsets arrays lists the bit
|
||||
* offsets. */
|
||||
static const uint8_t e1_rates[] = { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8 };
|
||||
static const uint8_t e1_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
|
||||
int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs);
|
||||
void mgcp_e1_endp_update(struct mgcp_endpoint *endp);
|
||||
void mgcp_e1_endp_release(struct mgcp_endpoint *endp);
|
||||
int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg);
|
@@ -23,15 +23,36 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
struct sockaddr_in;
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/i460_mux.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
|
||||
struct sockaddr;
|
||||
struct mgcp_conn;
|
||||
struct mgcp_conn_rtp;
|
||||
struct mgcp_endpoint;
|
||||
|
||||
/* Callback type for RTP dispatcher functions
|
||||
(e.g mgcp_dispatch_rtp_bridge_cb, see below) */
|
||||
typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in *addr,
|
||||
char *buf, unsigned int buf_size,
|
||||
struct mgcp_conn *conn);
|
||||
/* Number of E1 subslots (different variants, not all useable at the same time) */
|
||||
#define MGCP_ENDP_E1_SUBSLOTS 15
|
||||
|
||||
#define LOGPENDP(endp, cat, level, fmt, args...) \
|
||||
LOGP(cat, level, "endpoint:%s " fmt, \
|
||||
endp ? endp->name : "none", \
|
||||
## args)
|
||||
|
||||
struct osmo_rtp_msg_ctx {
|
||||
int proto;
|
||||
struct mgcp_conn_rtp *conn_src;
|
||||
struct osmo_sockaddr *from_addr;
|
||||
};
|
||||
|
||||
#define OSMO_RTP_MSG_CTX(MSGB) ((struct osmo_rtp_msg_ctx*)(MSGB)->cb)
|
||||
|
||||
osmo_static_assert(sizeof(((struct msgb*)0)->cb) >= sizeof(struct osmo_rtp_msg_ctx), osmo_rtp_msg_ctx_fits_in_msgb_cb);
|
||||
|
||||
/* Callback type for RTP dispatcher functions (e.g mgcp_dispatch_rtp_bridge_cb, see below).
|
||||
* The OSMO_RTP_MSG_CTX() should be set appropriately on the msg. */
|
||||
typedef int (*mgcp_dispatch_rtp_cb) (struct msgb *msg);
|
||||
|
||||
/* Callback type for endpoint specific cleanup actions. This function
|
||||
* is automatically executed when a connection is freed (see mgcp_conn_free()
|
||||
@@ -55,6 +76,7 @@ struct mgcp_endpoint_type {
|
||||
/*! MGCP endpoint typeset */
|
||||
struct mgcp_endpoint_typeset {
|
||||
struct mgcp_endpoint_type rtp;
|
||||
struct mgcp_endpoint_type e1;
|
||||
};
|
||||
|
||||
/*! static MGCP endpoint typeset (pre-initalized, read-only) */
|
||||
@@ -63,20 +85,20 @@ extern const struct mgcp_endpoint_typeset ep_typeset;
|
||||
/*! MGCP endpoint model */
|
||||
struct mgcp_endpoint {
|
||||
|
||||
/*! Unique endpoint name, used for addressing via MGCP */
|
||||
char *name;
|
||||
|
||||
/*! Call identifier string (as supplied by the call agant) */
|
||||
char *callid;
|
||||
|
||||
/*! Local connection options (see mgcp_internal.h) */
|
||||
struct mgcp_lco local_options;
|
||||
|
||||
/*! List with connections active on this 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 specific configuration */
|
||||
struct mgcp_trunk_config *tcfg;
|
||||
/*! Backpointer to the trunk this endpoint belongs to */
|
||||
struct mgcp_trunk *trunk;
|
||||
|
||||
/*! Endpoint properties (see above) */
|
||||
const struct mgcp_endpoint_type *type;
|
||||
@@ -87,13 +109,34 @@ 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;
|
||||
|
||||
/* E1 specific */
|
||||
struct {
|
||||
struct osmo_i460_schan_desc scd;
|
||||
struct osmo_i460_subchan *schan;
|
||||
struct osmo_fsm_inst *trau_sync_fi;
|
||||
struct osmo_trau2rtp_state *trau_rtp_st;
|
||||
uint8_t last_amr_ft;
|
||||
struct mgcp_rtp_codec *last_codec;
|
||||
} e1;
|
||||
|
||||
};
|
||||
|
||||
/*! Extract endpoint number for a given endpoint */
|
||||
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
|
||||
|
||||
struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, unsigned int index);
|
||||
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,
|
||||
struct mgcp_config *cfg);
|
||||
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);
|
||||
|
@@ -1,330 +0,0 @@
|
||||
/* MGCP Private Data */
|
||||
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/counter.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
|
||||
#define CI_UNUSED 0
|
||||
|
||||
/* FIXME: This this is only needed to compile the currently
|
||||
* broken OSMUX support. Remove when fixed */
|
||||
#define CONN_ID_BTS "0"
|
||||
#define CONN_ID_NET "1"
|
||||
|
||||
enum mgcp_trunk_type {
|
||||
MGCP_TRUNK_VIRTUAL,
|
||||
MGCP_TRUNK_E1,
|
||||
};
|
||||
|
||||
struct mgcp_rtp_stream_state {
|
||||
uint32_t ssrc;
|
||||
uint16_t last_seq;
|
||||
uint32_t last_timestamp;
|
||||
struct rate_ctr *err_ts_ctr;
|
||||
int32_t last_tsdelta;
|
||||
uint32_t last_arrival_time;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_state {
|
||||
/* has this state structure been initialized? */
|
||||
int initialized;
|
||||
|
||||
struct {
|
||||
/* are we patching the SSRC value? */
|
||||
int patch_ssrc;
|
||||
/* original SSRC (to which we shall patch any different SSRC) */
|
||||
uint32_t orig_ssrc;
|
||||
/* offset to apply on the sequence number */
|
||||
int seq_offset;
|
||||
/* offset to apply on the timestamp number */
|
||||
int32_t timestamp_offset;
|
||||
} patch;
|
||||
|
||||
/* duration of a packet (FIXME: in which unit?) */
|
||||
uint32_t packet_duration;
|
||||
|
||||
struct mgcp_rtp_stream_state in_stream;
|
||||
struct mgcp_rtp_stream_state out_stream;
|
||||
|
||||
/* jitter and packet loss calculation */
|
||||
struct {
|
||||
int initialized;
|
||||
uint16_t base_seq;
|
||||
uint16_t max_seq;
|
||||
uint32_t ssrc;
|
||||
uint32_t jitter;
|
||||
int32_t transit;
|
||||
int cycles;
|
||||
} stats;
|
||||
|
||||
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
|
||||
};
|
||||
|
||||
struct mgcp_rtp_codec {
|
||||
uint32_t rate;
|
||||
int channels;
|
||||
uint32_t frame_duration_num;
|
||||
uint32_t frame_duration_den;
|
||||
|
||||
int payload_type;
|
||||
char *audio_name;
|
||||
char *subtype_name;
|
||||
};
|
||||
|
||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
|
||||
struct mgcp_rtp_end {
|
||||
/* local IP address of the RTP socket */
|
||||
struct in_addr addr;
|
||||
|
||||
/* in network byte order */
|
||||
int rtp_port, rtcp_port;
|
||||
|
||||
/* currently selected audio codec */
|
||||
struct mgcp_rtp_codec *codec;
|
||||
|
||||
/* array with assigned audio codecs to choose from (SDP) */
|
||||
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
|
||||
|
||||
/* number of assigned audio codecs (SDP) */
|
||||
unsigned int codecs_assigned;
|
||||
|
||||
/* per endpoint data */
|
||||
int frames_per_packet;
|
||||
uint32_t packet_duration_ms;
|
||||
int maximum_packet_time; /* -1: not set */
|
||||
char *fmtp_extra;
|
||||
/* are we transmitting packets (1) or dropping (0) outbound packets */
|
||||
int output_enabled;
|
||||
/* FIXME: This parameter can be set + printed, but is nowhere used! */
|
||||
int force_output_ptime;
|
||||
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
|
||||
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
|
||||
int force_aligned_timing;
|
||||
|
||||
/* Each end has a separate socket for RTP and RTCP */
|
||||
struct osmo_fd rtp;
|
||||
struct osmo_fd rtcp;
|
||||
|
||||
/* local UDP port number of the RTP socket; RTCP is +1 */
|
||||
int local_port;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_tap {
|
||||
/* is this tap active (1) or not (0) */
|
||||
int enabled;
|
||||
/* IP/port to which we're forwarding the tapped data */
|
||||
struct sockaddr_in forward;
|
||||
};
|
||||
|
||||
struct mgcp_lco {
|
||||
char *string;
|
||||
char *codec;
|
||||
int pkt_period_min; /* time in ms */
|
||||
int pkt_period_max; /* time in ms */
|
||||
};
|
||||
|
||||
/* Specific rtp connection type (see struct mgcp_conn_rtp) */
|
||||
enum mgcp_conn_rtp_type {
|
||||
MGCP_RTP_DEFAULT = 0,
|
||||
MGCP_OSMUX_BSC,
|
||||
MGCP_OSMUX_BSC_NAT,
|
||||
};
|
||||
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
struct mgcp_conn;
|
||||
|
||||
/* MGCP connection (RTP) */
|
||||
struct mgcp_conn_rtp {
|
||||
|
||||
/* Backpointer to conn struct */
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
/* Specific connection type */
|
||||
enum mgcp_conn_rtp_type type;
|
||||
|
||||
/* Port status */
|
||||
struct mgcp_rtp_end end;
|
||||
|
||||
/* Sequence bits */
|
||||
struct mgcp_rtp_state state;
|
||||
|
||||
/* taps for the rtp connection; one per direction */
|
||||
struct mgcp_rtp_tap tap_in;
|
||||
struct mgcp_rtp_tap tap_out;
|
||||
|
||||
/* Osmux states (optional) */
|
||||
struct {
|
||||
/* Osmux state: disabled, activating, active */
|
||||
enum osmux_state state;
|
||||
/* Allocated Osmux circuit ID for this endpoint */
|
||||
int allocated_cid;
|
||||
/* Used Osmux circuit ID for this endpoint */
|
||||
uint8_t cid;
|
||||
/* handle to batch messages */
|
||||
struct osmux_in_handle *in;
|
||||
/* handle to unbatch messages */
|
||||
struct osmux_out_handle out;
|
||||
/* statistics */
|
||||
struct {
|
||||
uint32_t chunks;
|
||||
uint32_t octets;
|
||||
} stats;
|
||||
} osmux;
|
||||
|
||||
struct rate_ctr_group *rate_ctr_group;
|
||||
};
|
||||
|
||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
|
||||
* contains a useful connection description (currently only RTP) */
|
||||
enum mgcp_conn_type {
|
||||
MGCP_CONN_TYPE_RTP,
|
||||
};
|
||||
|
||||
/*! MGCP connection (untyped) */
|
||||
struct mgcp_conn {
|
||||
/*! list head */
|
||||
struct llist_head entry;
|
||||
|
||||
/*! Backpointer to the endpoint where the conn belongs to */
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
/*! type of the connection (union) */
|
||||
enum mgcp_conn_type type;
|
||||
|
||||
/*! mode of the connection */
|
||||
enum mgcp_connection_mode mode;
|
||||
|
||||
/*! copy of the mode to restore the original setting (VTY) */
|
||||
enum mgcp_connection_mode mode_orig;
|
||||
|
||||
/*! connection id to identify the connection */
|
||||
char id[MGCP_CONN_ID_LENGTH];
|
||||
|
||||
/*! human readable name (vty, logging) */
|
||||
char name[256];
|
||||
|
||||
/*! union with connection description */
|
||||
union {
|
||||
struct mgcp_conn_rtp rtp;
|
||||
} u;
|
||||
|
||||
/*! pointer to optional private data */
|
||||
void *priv;
|
||||
};
|
||||
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
|
||||
struct mgcp_endpoint_type;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Internal structure while parsing a request
|
||||
*/
|
||||
struct mgcp_parse_data {
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_endpoint *endp;
|
||||
char *trans;
|
||||
char *save;
|
||||
};
|
||||
|
||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
char *buf, int rc, struct mgcp_conn_rtp *conn_src,
|
||||
struct mgcp_conn_rtp *conn_dst);
|
||||
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
|
||||
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
||||
unsigned int buf_size, struct mgcp_conn *conn);
|
||||
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
|
||||
|
||||
/* For transcoding we need to manage an in and an output that are connected */
|
||||
static inline int endp_back_channel(int endpoint)
|
||||
{
|
||||
return endpoint + 60;
|
||||
}
|
||||
|
||||
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
|
||||
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
|
||||
|
||||
char *get_lco_identifier(const char *options);
|
||||
int check_local_cx_options(void *ctx, const char *options);
|
||||
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);
|
||||
|
||||
/* payload processing default functions */
|
||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
|
||||
char *data, int *len, int buf_size);
|
||||
|
||||
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn_dst,
|
||||
struct mgcp_conn_rtp *conn_src);
|
||||
|
||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
int *payload_type,
|
||||
const char**audio_name,
|
||||
const char**fmtp_extra,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
|
||||
/* internal RTP Annex A counting */
|
||||
void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
|
||||
const uint16_t seq, const int32_t transit,
|
||||
const uint32_t ssrc);
|
||||
|
||||
int mgcp_set_ip_tos(int fd, int tos);
|
||||
|
||||
enum {
|
||||
MGCP_DEST_NET = 0,
|
||||
MGCP_DEST_BTS,
|
||||
};
|
||||
|
||||
|
||||
#define MGCP_DUMMY_LOAD 0x23
|
||||
|
||||
|
||||
/**
|
||||
* SDP related information
|
||||
*/
|
||||
/* Assume audio frame length of 20ms */
|
||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
|
||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
|
||||
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
|
||||
#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
|
||||
#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
|
||||
|
||||
#define PTYPE_UNDEFINED (-1)
|
||||
|
||||
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
|
@@ -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);
|
||||
|
||||
|
179
include/osmocom/mgcp/mgcp_network.h
Normal file
179
include/osmocom/mgcp/mgcp_network.h
Normal file
@@ -0,0 +1,179 @@
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
|
||||
/* 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 {
|
||||
uint32_t ssrc;
|
||||
uint16_t last_seq;
|
||||
uint32_t last_timestamp;
|
||||
struct rate_ctr *err_ts_ctr;
|
||||
int32_t last_tsdelta;
|
||||
uint32_t last_arrival_time;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_state {
|
||||
/* has this state structure been initialized? */
|
||||
int initialized;
|
||||
|
||||
struct {
|
||||
/* are we patching the SSRC value? */
|
||||
bool patch_ssrc;
|
||||
/* original SSRC (to which we shall patch any different SSRC) */
|
||||
uint32_t orig_ssrc;
|
||||
/* offset to apply on the sequence number */
|
||||
int seq_offset;
|
||||
/* offset to apply on the timestamp number */
|
||||
int32_t timestamp_offset;
|
||||
} patch;
|
||||
|
||||
/* duration of a packet (FIXME: in which unit?) */
|
||||
uint32_t packet_duration;
|
||||
|
||||
/* Note: These states are not continuously updated, they serve as an
|
||||
* information source to patch certain values in the RTP header. Do
|
||||
* not use this state if constantly updated data about the RTP stream
|
||||
* is needed. (see also mgcp_patch_and_count() */
|
||||
struct mgcp_rtp_stream_state in_stream;
|
||||
struct mgcp_rtp_stream_state out_stream;
|
||||
|
||||
/* jitter and packet loss calculation */
|
||||
struct {
|
||||
int initialized;
|
||||
uint16_t base_seq;
|
||||
uint16_t max_seq;
|
||||
uint32_t ssrc;
|
||||
uint32_t jitter;
|
||||
int32_t transit;
|
||||
int cycles;
|
||||
} stats;
|
||||
|
||||
/* Alternative values for RTP tx, in case no sufficient header
|
||||
* information is available so the header needs to be generated
|
||||
* locally (when just forwarding packets, the header of incoming
|
||||
* data is just re-used) */
|
||||
uint16_t alt_rtp_tx_sequence;
|
||||
uint32_t alt_rtp_tx_ssrc;
|
||||
|
||||
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
|
||||
};
|
||||
|
||||
struct mgcp_rtp_codec {
|
||||
uint32_t rate;
|
||||
int channels;
|
||||
uint32_t frame_duration_num;
|
||||
uint32_t frame_duration_den;
|
||||
|
||||
int payload_type;
|
||||
char audio_name[64];
|
||||
char subtype_name[64];
|
||||
|
||||
bool param_present;
|
||||
struct mgcp_codec_param param;
|
||||
};
|
||||
|
||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
|
||||
struct mgcp_rtp_end {
|
||||
/* remote IP address of the RTP socket */
|
||||
struct osmo_sockaddr addr;
|
||||
|
||||
/* in network byte order */
|
||||
int rtp_port, rtcp_port;
|
||||
|
||||
/* currently selected audio codec */
|
||||
struct mgcp_rtp_codec *codec;
|
||||
|
||||
/* array with assigned audio codecs to choose from (SDP) */
|
||||
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
|
||||
|
||||
/* number of assigned audio codecs (SDP) */
|
||||
unsigned int codecs_assigned;
|
||||
|
||||
/* per endpoint data */
|
||||
int frames_per_packet;
|
||||
uint32_t packet_duration_ms;
|
||||
int maximum_packet_time; /* -1: not set */
|
||||
char *fmtp_extra;
|
||||
/* are we transmitting packets (1) or dropping (0) outbound packets */
|
||||
int output_enabled;
|
||||
/* FIXME: This parameter can be set + printed, but is nowhere used! */
|
||||
int force_output_ptime;
|
||||
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
|
||||
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
|
||||
int force_aligned_timing;
|
||||
bool rfc5993_hr_convert;
|
||||
|
||||
/* Each end has a separate socket for RTP and RTCP */
|
||||
struct osmo_fd rtp;
|
||||
struct osmo_fd rtcp;
|
||||
|
||||
/* local UDP port number of the RTP socket; RTCP is +1 */
|
||||
int local_port;
|
||||
/* where the endpoint RTP connection binds to, set during CRCX and
|
||||
* possibly updated during MDCX */
|
||||
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;
|
||||
/* IP/port to which we're forwarding the tapped data */
|
||||
struct osmo_sockaddr forward;
|
||||
};
|
||||
|
||||
struct mgcp_conn;
|
||||
|
||||
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);
|
||||
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
|
||||
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg);
|
||||
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
int mgcp_dispatch_e1_bridge_cb(struct msgb *msg);
|
||||
void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
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(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *rtp_end,
|
||||
struct osmo_sockaddr *addr, struct msgb *msg);
|
||||
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
|
||||
|
||||
/* payload processing default functions */
|
||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
|
||||
char *data, int *len, int buf_size);
|
||||
|
||||
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn_dst,
|
||||
struct mgcp_conn_rtp *conn_src);
|
||||
|
||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
const struct mgcp_rtp_codec **codec,
|
||||
const char **fmtp_extra,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
|
||||
/* internal RTP Annex A counting */
|
||||
void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
|
||||
const uint16_t seq, const int32_t transit,
|
||||
const uint32_t ssrc, const bool marker_bit);
|
30
include/osmocom/mgcp/mgcp_protocol.h
Normal file
30
include/osmocom/mgcp/mgcp_protocol.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
/* Internal structure while parsing a request */
|
||||
struct mgcp_parse_data {
|
||||
struct mgcp_config *cfg;
|
||||
char *epname;
|
||||
char *trans;
|
||||
char *save;
|
||||
};
|
||||
|
||||
/* Local connection options */
|
||||
struct mgcp_lco {
|
||||
char *string;
|
||||
char *codec;
|
||||
int pkt_period_min; /* time in ms */
|
||||
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(const struct mgcp_endpoint *endp,
|
||||
const struct mgcp_rtp_end *rtp);
|
107
include/osmocom/mgcp/mgcp_ratectr.h
Normal file
107
include/osmocom/mgcp/mgcp_ratectr.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
/* Global MCGP general rate counters */
|
||||
enum {
|
||||
MGCP_GENERAL_RX_MSGS_TOTAL,
|
||||
MGCP_GENERAL_RX_MSGS_RETRANSMITTED,
|
||||
MGCP_GENERAL_RX_MSGS_HANDLED,
|
||||
MGCP_GENERAL_RX_MSGS_UNHANDLED,
|
||||
MGCP_GENERAL_RX_FAIL_MSG_PARSE,
|
||||
MGCP_GENERAL_RX_FAIL_NO_ENDPOINT,
|
||||
};
|
||||
|
||||
/* Trunk-global MCGP CRCX related rate counters */
|
||||
enum {
|
||||
MGCP_CRCX_SUCCESS,
|
||||
MGCP_CRCX_FAIL_BAD_ACTION,
|
||||
MGCP_CRCX_FAIL_UNHANDLED_PARAM,
|
||||
MGCP_CRCX_FAIL_MISSING_CALLID,
|
||||
MGCP_CRCX_FAIL_INVALID_MODE,
|
||||
MGCP_CRCX_FAIL_LIMIT_EXCEEDED,
|
||||
MGCP_CRCX_FAIL_UNKNOWN_CALLID,
|
||||
MGCP_CRCX_FAIL_ALLOC_CONN,
|
||||
MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC,
|
||||
MGCP_CRCX_FAIL_START_RTP,
|
||||
MGCP_CRCX_FAIL_NO_OSMUX,
|
||||
MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS,
|
||||
MGCP_CRCX_FAIL_CODEC_NEGOTIATION,
|
||||
MGCP_CRCX_FAIL_BIND_PORT,
|
||||
MGCP_CRCX_FAIL_AVAIL,
|
||||
MGCP_CRCX_FAIL_CLAIM,
|
||||
};
|
||||
|
||||
/* Trunk-global MCGP MDCX related rate counters */
|
||||
enum {
|
||||
MGCP_MDCX_SUCCESS,
|
||||
MGCP_MDCX_FAIL_WILDCARD,
|
||||
MGCP_MDCX_FAIL_NO_CONN,
|
||||
MGCP_MDCX_FAIL_INVALID_CALLID,
|
||||
MGCP_MDCX_FAIL_INVALID_CONNID,
|
||||
MGCP_MDCX_FAIL_UNHANDLED_PARAM,
|
||||
MGCP_MDCX_FAIL_NO_CONNID,
|
||||
MGCP_MDCX_FAIL_CONN_NOT_FOUND,
|
||||
MGCP_MDCX_FAIL_INVALID_MODE,
|
||||
MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS,
|
||||
MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC,
|
||||
MGCP_MDCX_FAIL_START_RTP,
|
||||
MGCP_MDCX_FAIL_AVAIL,
|
||||
};
|
||||
|
||||
/* Trunk-global MCGP DLCX related rate counters */
|
||||
enum {
|
||||
MGCP_DLCX_SUCCESS,
|
||||
MGCP_DLCX_FAIL_NO_CONN,
|
||||
MGCP_DLCX_FAIL_INVALID_CALLID,
|
||||
MGCP_DLCX_FAIL_INVALID_CONNID,
|
||||
MGCP_DLCX_FAIL_UNHANDLED_PARAM,
|
||||
MGCP_DLCX_FAIL_AVAIL,
|
||||
};
|
||||
|
||||
/* Trunk-global E1 related counters */
|
||||
enum {
|
||||
E1_I460_TRAU_RX_FAIL_CTR,
|
||||
E1_I460_TRAU_TX_FAIL_CTR,
|
||||
E1_I460_TRAU_MUX_EMPTY_CTR,
|
||||
};
|
||||
|
||||
/* NOTE: When adding counters, also the dump_ratectr_* routines in vty.c must be updated. */
|
||||
|
||||
struct mgcp_ratectr_global {
|
||||
/* Rate counter group which contains stats for generic MGCP events. */
|
||||
struct rate_ctr_group *mgcp_general_ctr_group;
|
||||
};
|
||||
|
||||
struct mgcp_ratectr_trunk {
|
||||
/* Rate counter group which contains stats for processed CRCX commands. */
|
||||
struct rate_ctr_group *mgcp_crcx_ctr_group;
|
||||
/* Rate counter group which contains stats for processed MDCX commands. */
|
||||
struct rate_ctr_group *mgcp_mdcx_ctr_group;
|
||||
/* Rate counter group which contains stats for processed DLCX commands. */
|
||||
struct rate_ctr_group *mgcp_dlcx_ctr_group;
|
||||
/* Rate counter group which aggregates stats of individual RTP connections. */
|
||||
struct rate_ctr_group *all_rtp_conn_stats;
|
||||
/* Rate counter group which contains stats for E1 events (only valid for E1 trunks) */
|
||||
struct rate_ctr_group *e1_stats;
|
||||
};
|
||||
|
||||
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);
|
@@ -24,7 +24,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn);
|
||||
|
88
include/osmocom/mgcp/mgcp_trunk.h
Normal file
88
include/osmocom/mgcp/mgcp_trunk.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#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;
|
||||
|
||||
unsigned int trunk_nr;
|
||||
enum mgcp_trunk_type trunk_type;
|
||||
|
||||
char *audio_fmtp_extra;
|
||||
int audio_send_ptime;
|
||||
int audio_send_name;
|
||||
|
||||
int no_audio_transcoding;
|
||||
|
||||
int omit_rtcp;
|
||||
int keepalive_interval;
|
||||
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* 0: don't, 1: once */
|
||||
int force_aligned_timing;
|
||||
bool rfc5993_hr_convert;
|
||||
|
||||
/* spec handling */
|
||||
int force_realloc;
|
||||
|
||||
/* timer */
|
||||
struct osmo_timer_list keepalive_timer;
|
||||
|
||||
/* When set, incoming RTP packets are not filtered
|
||||
* when ports and ip-address do not match (debug) */
|
||||
int rtp_accept_all;
|
||||
|
||||
unsigned int number_endpoints;
|
||||
struct mgcp_endpoint **endpoints;
|
||||
|
||||
/* 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 */
|
||||
struct {
|
||||
unsigned int vty_number_endpoints;
|
||||
} v;
|
||||
/* E1 specific */
|
||||
struct {
|
||||
unsigned int vty_line_nr;
|
||||
bool ts_in_use[NUM_E1_TS-1];
|
||||
struct osmo_i460_timeslot i460_ts[NUM_E1_TS-1];
|
||||
/* Note: on an E1 line TS 0 is devoted to framing and
|
||||
* alignment and therefore only NUM_E1_TS-1 timeslots
|
||||
* are available for traffic. */
|
||||
} e1;
|
||||
};
|
||||
};
|
||||
|
||||
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, unsigned int nr);
|
||||
struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, 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,
|
||||
* use this define constant as ID when allocating a virtual trunk. Other
|
||||
* trunks may be assigned with arbritrary id numbers */
|
||||
#define MGCP_VIRT_TRUNK_ID 0
|
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/netif/osmux.h>
|
||||
struct mgcp_conn_rtp;
|
||||
@@ -13,21 +14,23 @@ enum {
|
||||
|
||||
int osmux_init(int role, struct mgcp_config *cfg);
|
||||
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
||||
struct in_addr *addr, uint16_t port);
|
||||
void osmux_disable_conn(struct mgcp_conn_rtp *conn);
|
||||
void osmux_allocate_cid(struct mgcp_conn_rtp *conn);
|
||||
void osmux_release_cid(struct mgcp_conn_rtp *conn);
|
||||
struct osmo_sockaddr *addr, uint16_t port);
|
||||
void conn_osmux_disable(struct mgcp_conn_rtp *conn);
|
||||
int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid);
|
||||
void conn_osmux_release_cid(struct mgcp_conn_rtp *conn);
|
||||
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn);
|
||||
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
|
||||
int osmux_get_cid(void);
|
||||
void osmux_put_cid(uint8_t osmux_cid);
|
||||
int osmux_used_cid(void);
|
||||
|
||||
void osmux_cid_pool_get(uint8_t osmux_cid);
|
||||
int osmux_cid_pool_get_next(void);
|
||||
void osmux_cid_pool_put(uint8_t osmux_cid);
|
||||
bool osmux_cid_pool_allocated(uint8_t osmux_cid);
|
||||
int osmux_cid_pool_count_used(void);
|
||||
|
||||
enum osmux_state {
|
||||
OSMUX_STATE_DISABLED = 0,
|
||||
OSMUX_STATE_NEGOTIATING,
|
||||
OSMUX_STATE_ACTIVATING,
|
||||
OSMUX_STATE_ENABLED,
|
||||
OSMUX_STATE_DISABLED = 0, /* Osmux not being currently used by endp */
|
||||
OSMUX_STATE_ACTIVATING, /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref osmux_enable_endpoint. */
|
||||
OSMUX_STATE_ENABLED, /* Osmux was initialized by \ref osmux_enable_endpoint and can process frames */
|
||||
};
|
||||
|
||||
enum osmux_usage {
|
||||
@@ -35,4 +38,3 @@ enum osmux_usage {
|
||||
OSMUX_USAGE_ON = 1,
|
||||
OSMUX_USAGE_ONLY = 2,
|
||||
};
|
||||
|
||||
|
@@ -6,3 +6,7 @@ enum mgcp_vty_node {
|
||||
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
TRUNK_NODE,
|
||||
};
|
||||
|
||||
enum mgw_vty_cmd_attr {
|
||||
MGW_CMD_ATTR_NEWCONN = 0,
|
||||
};
|
||||
|
@@ -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
|
||||
|
@@ -5,11 +5,14 @@
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_common.h>
|
||||
|
||||
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0"
|
||||
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 0
|
||||
/* See also: RFC 3435, chapter 3.5 Transmission over UDP */
|
||||
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT NULL /* INADDR(6)_ANY */
|
||||
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 2727
|
||||
#define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
|
||||
#define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427
|
||||
|
||||
#define MGCP_CLIENT_MGW_STR "Configure MGCP connection to Media Gateway\n"
|
||||
|
||||
struct msgb;
|
||||
struct vty;
|
||||
struct mgcp_client;
|
||||
@@ -19,9 +22,19 @@ struct mgcp_client_conf {
|
||||
int local_port;
|
||||
const char *remote_addr;
|
||||
int remote_port;
|
||||
uint16_t first_endpoint;
|
||||
uint16_t last_endpoint;
|
||||
uint16_t bts_base;
|
||||
|
||||
/* 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;
|
||||
@@ -33,7 +46,7 @@ enum mgcp_codecs {
|
||||
CODEC_PCMA_8000_1 = 8,
|
||||
CODEC_G729_8000_1 = 18,
|
||||
CODEC_GSMEFR_8000_1 = 110,
|
||||
CODEC_GSMHR_8000_1 = 111,
|
||||
CODEC_GSMHR_8000_1 = 111,
|
||||
CODEC_AMR_8000_1 = 112,
|
||||
CODEC_AMRWB_16000_1 = 113,
|
||||
};
|
||||
@@ -43,6 +56,10 @@ enum mgcp_codecs {
|
||||
* this is an internal assumption that is made to avoid lookup tables.
|
||||
* The API-User should not rely on this coincidence! */
|
||||
|
||||
extern const struct value_string osmo_mgcpc_codec_names[];
|
||||
static inline const char *osmo_mgcpc_codec_name(enum mgcp_codecs val)
|
||||
{ return get_value_string(osmo_mgcpc_codec_names, val); }
|
||||
|
||||
/*! Structure to build a payload type map to allow the defiition custom payload
|
||||
* types. */
|
||||
struct ptmap {
|
||||
@@ -57,20 +74,22 @@ struct mgcp_response_head {
|
||||
int response_code;
|
||||
mgcp_trans_id_t trans_id;
|
||||
char comment[MGCP_COMMENT_MAXLEN];
|
||||
char conn_id[MGCP_CONN_ID_LENGTH];
|
||||
char conn_id[MGCP_CONN_ID_MAXLEN];
|
||||
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
bool x_osmo_osmux_use;
|
||||
uint8_t x_osmo_osmux_cid;
|
||||
};
|
||||
|
||||
struct mgcp_response {
|
||||
char *body;
|
||||
struct mgcp_response_head head;
|
||||
uint16_t audio_port;
|
||||
char audio_ip[INET_ADDRSTRLEN];
|
||||
char audio_ip[INET6_ADDRSTRLEN];
|
||||
unsigned int ptime;
|
||||
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
|
||||
unsigned int codecs_len;
|
||||
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||
unsigned int ptmap_len;
|
||||
unsigned int ptmap_len;
|
||||
};
|
||||
|
||||
enum mgcp_verb {
|
||||
@@ -87,6 +106,8 @@ enum mgcp_verb {
|
||||
#define MGCP_MSG_PRESENCE_AUDIO_IP 0x0008
|
||||
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
|
||||
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
|
||||
#define MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID 0x4000
|
||||
#define MGCP_MSG_PRESENCE_X_OSMO_IGN 0x8000
|
||||
|
||||
struct mgcp_msg {
|
||||
enum mgcp_verb verb;
|
||||
@@ -103,6 +124,11 @@ struct mgcp_msg {
|
||||
unsigned int codecs_len;
|
||||
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||
unsigned int ptmap_len;
|
||||
uint32_t x_osmo_ign;
|
||||
bool x_osmo_osmux_use;
|
||||
int x_osmo_osmux_cid; /* -1 is wildcard */
|
||||
bool param_present;
|
||||
struct mgcp_codec_param param;
|
||||
};
|
||||
|
||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
|
||||
@@ -113,13 +139,17 @@ 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);
|
||||
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp);
|
||||
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp) OSMO_DEPRECATED("deprecated, returns 0");
|
||||
|
||||
int mgcp_client_next_endpoint(struct mgcp_client *client);
|
||||
void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client);
|
||||
const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp);
|
||||
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp);
|
||||
const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uint8_t trunk_id, uint8_t ts,
|
||||
uint8_t rate, uint8_t offset);
|
||||
|
||||
/* Invoked when an MGCP response is received or sending failed. When the
|
||||
* response is passed as NULL, this indicates failure during transmission. */
|
||||
@@ -132,20 +162,6 @@ int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
|
||||
|
||||
enum mgcp_connection_mode;
|
||||
|
||||
struct msgb *mgcp_msg_crcx(struct mgcp_client *mgcp,
|
||||
uint16_t rtp_endpoint, unsigned int call_id,
|
||||
enum mgcp_connection_mode mode)
|
||||
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
|
||||
|
||||
struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp,
|
||||
uint16_t rtp_endpoint, const char *rtp_conn_addr,
|
||||
uint16_t rtp_port, enum mgcp_connection_mode mode)
|
||||
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
|
||||
|
||||
struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
|
||||
unsigned int call_id)
|
||||
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
|
||||
|
||||
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
|
||||
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);
|
||||
|
||||
@@ -156,7 +172,9 @@ static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
|
||||
}
|
||||
|
||||
enum mgcp_codecs map_str_to_codec(const char *str);
|
||||
unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
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);
|
||||
|
54
include/osmocom/mgcp_client/mgcp_client_endpoint_fsm.h
Normal file
54
include/osmocom/mgcp_client/mgcp_client_endpoint_fsm.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* FSM to manage multiple connections of an MGW endpoint */
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client_fsm.h>
|
||||
|
||||
#define LOG_MGCPC_EP(ep, level, fmt, args...) do { \
|
||||
LOGPFSML(ep->fi, level, "%s " fmt, \
|
||||
osmo_mgcpc_ep_name(ep), ## args); \
|
||||
} while(0)
|
||||
|
||||
struct osmo_mgcpc_ep;
|
||||
struct osmo_mgcpc_ep_ci;
|
||||
struct osmo_tdef;
|
||||
|
||||
struct osmo_mgcpc_ep *osmo_mgcpc_ep_alloc(struct osmo_fsm_inst *parent, uint32_t parent_term_event,
|
||||
struct mgcp_client *mgcp_client,
|
||||
const struct osmo_tdef *T_defs,
|
||||
const char *fsm_id,
|
||||
const char *endpoint_str_fmt, ...);
|
||||
|
||||
struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_ci_add(struct osmo_mgcpc_ep *ep, const char *label_fmt, ...);
|
||||
const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgcpc_ep_ci *ci);
|
||||
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,
|
||||
uint32_t event_success, uint32_t event_failure,
|
||||
void *notify_data);
|
||||
|
||||
void osmo_mgcpc_ep_cancel_notify(struct osmo_mgcpc_ep *ep, struct osmo_fsm_inst *notify);
|
||||
struct osmo_mgcpc_ep *osmo_mgcpc_ep_ci_ep(struct osmo_mgcpc_ep_ci *ci);
|
||||
|
||||
/*! Dispatch a DLCX for the given connection.
|
||||
* \param ci Connection identifier as obtained from osmo_mgcpc_ep_ci_add().
|
||||
*/
|
||||
static inline void osmo_mgcpc_ep_ci_dlcx(struct osmo_mgcpc_ep_ci *ci)
|
||||
{
|
||||
osmo_mgcpc_ep_ci_request(ci, MGCP_VERB_DLCX, NULL, NULL, 0, 0, NULL);
|
||||
}
|
||||
|
||||
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)
|
||||
{ return get_value_string(osmo_mgcp_verb_names, val); }
|
@@ -8,14 +8,14 @@
|
||||
* (either remote or local). It is used to pass parameters (local) to the FSM
|
||||
* and get responses (remote) from the FSM as pointer attached to the FSM
|
||||
* event.
|
||||
*
|
||||
*
|
||||
* When modifiying a connection, the endpoint and call_id members may be left
|
||||
* unpopulated. The call_id field is ignored in this case. If an endpoint
|
||||
* identifier is supplied it is checked against the internal state to make
|
||||
* sure it is correct. */
|
||||
struct mgcp_conn_peer {
|
||||
/*! RTP connection IP-Address (optional, string e.g. "127.0.0.1") */
|
||||
char addr[INET_ADDRSTRLEN];
|
||||
char addr[INET6_ADDRSTRLEN];
|
||||
|
||||
/*! RTP connection IP-Port (optional) */
|
||||
uint16_t port;
|
||||
@@ -34,6 +34,33 @@ struct mgcp_conn_peer {
|
||||
|
||||
/*! Number of codecs in RTP codec list (optional) */
|
||||
unsigned int codecs_len;
|
||||
|
||||
/*! RTP payload type map (optional, only needed when payload types are
|
||||
* used that differ from what IANA/3GPP defines) */
|
||||
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||
|
||||
/*! RTP payload type map length (optional, only needed when payload
|
||||
* types are used that differ from what IANA/3GPP defines) */
|
||||
unsigned int ptmap_len;
|
||||
|
||||
/*! If nonzero, send 'X-Osmo-IGN:' header. This is useful e.g. for SCCPlite MSCs where the MSC is
|
||||
* known to issue incoherent or unknown CallIDs / to issue CRCX commands with a different domain
|
||||
* name than the BSC. An OsmoMGW will then ignore these and not fail on mismatches. */
|
||||
uint32_t x_osmo_ign;
|
||||
|
||||
/*! send 'X-Osmux: %d' header (or "*" as wildcard). */
|
||||
bool x_osmo_osmux_use;
|
||||
/*! -1 means send wildcard. */
|
||||
int x_osmo_osmux_cid;
|
||||
|
||||
/*! If left MGCP_CONN_NONE, use MGCP_CONN_RECV_ONLY or MGCP_CONN_RECV_SEND, depending on whether an audio RTP
|
||||
* address is set. If != MGCP_CONN_NONE, force this conn mode. */
|
||||
enum mgcp_connection_mode conn_mode;
|
||||
|
||||
/*! If the codec requires additional format parameters (fmtp), those cann be set here, see also
|
||||
* mgcp_common.h */
|
||||
bool param_present;
|
||||
struct mgcp_codec_param param;
|
||||
};
|
||||
|
||||
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
|
||||
@@ -42,3 +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,13 +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;
|
||||
uint32_t remote_addr;
|
||||
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);
|
@@ -1,10 +0,0 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: Osmocom legacy Media Gateway Control Protocol library
|
||||
Description: C Utility Library
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -losmo-legacy-mgcp
|
||||
Cflags: -I${includedir}/
|
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -17,11 +17,9 @@
|
||||
|
||||
app_configs = {
|
||||
"osmo-mgw": ["doc/examples/osmo-mgw/osmo-mgw.cfg"],
|
||||
"osmo-bsc_mgcp": ["doc/examples/osmo-bsc_mgcp/mgcp.cfg"],
|
||||
}
|
||||
|
||||
apps = [(4243, "src/osmo-mgw/osmo-mgw", "OsmoMGW", "osmo-mgw"),
|
||||
(4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "osmo-bsc_mgcp"),
|
||||
]
|
||||
|
||||
vty_command = ["./src/osmo-mgw/osmo-mgw", "-c",
|
||||
|
@@ -21,13 +21,11 @@ AM_LDFLAGS = \
|
||||
|
||||
# Libraries
|
||||
SUBDIRS = \
|
||||
libosmo-legacy-mgcp \
|
||||
libosmo-mgcp-client \
|
||||
libosmo-mgcp \
|
||||
$(NULL)
|
||||
|
||||
# Programs
|
||||
SUBDIRS += \
|
||||
osmo-bsc_mgcp \
|
||||
osmo-mgw \
|
||||
$(NULL)
|
||||
|
@@ -1,51 +0,0 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(LIBBCG729_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(LIBBCG729_LIBS) \
|
||||
$(LIBRARY_GSM) \
|
||||
$(NULL)
|
||||
|
||||
# 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!
|
||||
LEGACY_MGCP_LIBVERSION=1:0:1
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-legacy-mgcp.la \
|
||||
$(NULL)
|
||||
|
||||
noinst_HEADERS = \
|
||||
g711common.h \
|
||||
$(NULL)
|
||||
|
||||
libosmo_legacy_mgcp_la_SOURCES = \
|
||||
mgcp_common.c \
|
||||
mgcp_protocol.c \
|
||||
mgcp_network.c \
|
||||
mgcp_vty.c \
|
||||
mgcp_osmux.c \
|
||||
mgcp_sdp.c \
|
||||
$(NULL)
|
||||
if BUILD_MGCP_TRANSCODING
|
||||
libosmo_legacy_mgcp_la_SOURCES += \
|
||||
mgcp_transcode.c \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
libosmo_legacy_mgcp_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LEGACY_MGCP_LIBVERSION)
|
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
* PCM - A-Law conversion
|
||||
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
|
||||
*
|
||||
* Wrapper for linphone Codec class by Simon Morlat <simon.morlat@linphone.org>
|
||||
*
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
static inline int val_seg(int val)
|
||||
{
|
||||
int r = 0;
|
||||
val >>= 7; /*7 = 4 + 3*/
|
||||
if (val & 0xf0) {
|
||||
val >>= 4;
|
||||
r += 4;
|
||||
}
|
||||
if (val & 0x0c) {
|
||||
val >>= 2;
|
||||
r += 2;
|
||||
}
|
||||
if (val & 0x02)
|
||||
r += 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
|
||||
*
|
||||
* s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data.
|
||||
*
|
||||
* Linear Input Code Compressed Code
|
||||
* ------------------------ ---------------
|
||||
* 0000000wxyza 000wxyz
|
||||
* 0000001wxyza 001wxyz
|
||||
* 000001wxyzab 010wxyz
|
||||
* 00001wxyzabc 011wxyz
|
||||
* 0001wxyzabcd 100wxyz
|
||||
* 001wxyzabcde 101wxyz
|
||||
* 01wxyzabcdef 110wxyz
|
||||
* 1wxyzabcdefg 111wxyz
|
||||
*
|
||||
* For further information see John C. Bellamy's Digital Telephony, 1982,
|
||||
* John Wiley & Sons, pps 98-111 and 472-476.
|
||||
* G711 is designed for 13 bits input signal, this function add extra shifting to take this into account.
|
||||
*/
|
||||
|
||||
static inline unsigned char s16_to_alaw(int pcm_val)
|
||||
{
|
||||
int mask;
|
||||
int seg;
|
||||
unsigned char aval;
|
||||
|
||||
if (pcm_val >= 0) {
|
||||
mask = 0xD5;
|
||||
} else {
|
||||
mask = 0x55;
|
||||
pcm_val = -pcm_val;
|
||||
if (pcm_val > 0x7fff)
|
||||
pcm_val = 0x7fff;
|
||||
}
|
||||
|
||||
if (pcm_val < 256) /*256 = 32 << 3*/
|
||||
aval = pcm_val >> 4; /*4 = 1 + 3*/
|
||||
else {
|
||||
/* Convert the scaled magnitude to segment number. */
|
||||
seg = val_seg(pcm_val);
|
||||
aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
|
||||
}
|
||||
return aval ^ mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* alaw_to_s16() - Convert an A-law value to 16-bit linear PCM
|
||||
*
|
||||
*/
|
||||
static inline int alaw_to_s16(unsigned char a_val)
|
||||
{
|
||||
int t;
|
||||
int seg;
|
||||
|
||||
a_val ^= 0x55;
|
||||
t = a_val & 0x7f;
|
||||
if (t < 16)
|
||||
t = (t << 4) + 8;
|
||||
else {
|
||||
seg = (t >> 4) & 0x07;
|
||||
t = ((t & 0x0f) << 4) + 0x108;
|
||||
t <<= seg -1;
|
||||
}
|
||||
return ((a_val & 0x80) ? t : -t);
|
||||
}
|
||||
/*
|
||||
* s16_to_ulaw() - Convert a linear PCM value to u-law
|
||||
*
|
||||
* In order to simplify the encoding process, the original linear magnitude
|
||||
* is biased by adding 33 which shifts the encoding range from (0 - 8158) to
|
||||
* (33 - 8191). The result can be seen in the following encoding table:
|
||||
*
|
||||
* Biased Linear Input Code Compressed Code
|
||||
* ------------------------ ---------------
|
||||
* 00000001wxyza 000wxyz
|
||||
* 0000001wxyzab 001wxyz
|
||||
* 000001wxyzabc 010wxyz
|
||||
* 00001wxyzabcd 011wxyz
|
||||
* 0001wxyzabcde 100wxyz
|
||||
* 001wxyzabcdef 101wxyz
|
||||
* 01wxyzabcdefg 110wxyz
|
||||
* 1wxyzabcdefgh 111wxyz
|
||||
*
|
||||
* Each biased linear code has a leading 1 which identifies the segment
|
||||
* number. The value of the segment number is equal to 7 minus the number
|
||||
* of leading 0's. The quantization interval is directly available as the
|
||||
* four bits wxyz. * The trailing bits (a - h) are ignored.
|
||||
*
|
||||
* Ordinarily the complement of the resulting code word is used for
|
||||
* transmission, and so the code word is complemented before it is returned.
|
||||
*
|
||||
* For further information see John C. Bellamy's Digital Telephony, 1982,
|
||||
* John Wiley & Sons, pps 98-111 and 472-476.
|
||||
*/
|
||||
|
||||
static inline unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */
|
||||
{
|
||||
int mask;
|
||||
int seg;
|
||||
unsigned char uval;
|
||||
|
||||
if (pcm_val < 0) {
|
||||
pcm_val = 0x84 - pcm_val;
|
||||
mask = 0x7f;
|
||||
} else {
|
||||
pcm_val += 0x84;
|
||||
mask = 0xff;
|
||||
}
|
||||
if (pcm_val > 0x7fff)
|
||||
pcm_val = 0x7fff;
|
||||
|
||||
/* Convert the scaled magnitude to segment number. */
|
||||
seg = val_seg(pcm_val);
|
||||
|
||||
/*
|
||||
* Combine the sign, segment, quantization bits;
|
||||
* and complement the code word.
|
||||
*/
|
||||
uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
|
||||
return uval ^ mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM
|
||||
*
|
||||
* First, a biased linear code is derived from the code word. An unbiased
|
||||
* output can then be obtained by subtracting 33 from the biased code.
|
||||
*
|
||||
* Note that this function expects to be passed the complement of the
|
||||
* original code word. This is in keeping with ISDN conventions.
|
||||
*/
|
||||
static inline int ulaw_to_s16(unsigned char u_val)
|
||||
{
|
||||
int t;
|
||||
|
||||
/* Complement to obtain normal u-law value. */
|
||||
u_val = ~u_val;
|
||||
|
||||
/*
|
||||
* Extract and bias the quantization bits. Then
|
||||
* shift up by the segment number and subtract out the bias.
|
||||
*/
|
||||
t = ((u_val & 0x0f) << 3) + 0x84;
|
||||
t <<= (u_val & 0x70) >> 4;
|
||||
|
||||
return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84));
|
||||
}
|
@@ -1,54 +0,0 @@
|
||||
/* Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* Implementations useful both for the MGCP GW as well as MGCP GW clients */
|
||||
|
||||
/*
|
||||
* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/legacy_mgcp/mgcp.h>
|
||||
|
||||
const struct value_string mgcp_connection_mode_strs[] = {
|
||||
{ MGCP_CONN_NONE, "none" },
|
||||
{ MGCP_CONN_RECV_SEND, "sendrecv" },
|
||||
{ MGCP_CONN_SEND_ONLY, "sendonly" },
|
||||
{ MGCP_CONN_RECV_ONLY, "recvonly" },
|
||||
{ MGCP_CONN_LOOPBACK, "loopback" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/* Ensure that the msg->l2h is NUL terminated. */
|
||||
int mgcp_msg_terminate_nul(struct msgb *msg)
|
||||
{
|
||||
unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */
|
||||
if (tail[-1] == '\0')
|
||||
/* nothing to do */;
|
||||
else if (msgb_tailroom(msg) > 0)
|
||||
tail[0] = '\0';
|
||||
else if (tail[-1] == '\r' || tail[-1] == '\n')
|
||||
tail[-1] = '\0';
|
||||
else {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: "
|
||||
"Length: %d, Buffer size: %d\n",
|
||||
msgb_l2len(msg), msg->data_len);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,588 +0,0 @@
|
||||
/*
|
||||
* (C) 2012-2013 by Pablo Neira Ayuso <pablo@gnumonks.org>
|
||||
* (C) 2012-2013 by On Waves ehf <http://www.on-waves.com>
|
||||
* All rights not specifically granted under this license are reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* for printf */
|
||||
#include <string.h> /* for memcpy */
|
||||
#include <stdlib.h> /* for abs */
|
||||
#include <inttypes.h> /* for PRIu64 */
|
||||
#include <netinet/in.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
||||
#include <osmocom/netif/osmux.h>
|
||||
#include <osmocom/netif/rtp.h>
|
||||
|
||||
#include <osmocom/legacy_mgcp/mgcp.h>
|
||||
#include <osmocom/legacy_mgcp/mgcp_internal.h>
|
||||
#include <osmocom/legacy_mgcp/osmux.h>
|
||||
|
||||
static struct osmo_fd osmux_fd;
|
||||
|
||||
static LLIST_HEAD(osmux_handle_list);
|
||||
|
||||
struct osmux_handle {
|
||||
struct llist_head head;
|
||||
struct osmux_in_handle *in;
|
||||
struct in_addr rem_addr;
|
||||
int rem_port;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
static void *osmux;
|
||||
|
||||
static void osmux_deliver(struct msgb *batch_msg, void *data)
|
||||
{
|
||||
struct osmux_handle *handle = data;
|
||||
struct sockaddr_in out = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_port = handle->rem_port,
|
||||
};
|
||||
|
||||
memcpy(&out.sin_addr, &handle->rem_addr, sizeof(handle->rem_addr));
|
||||
sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0,
|
||||
(struct sockaddr *)&out, sizeof(out));
|
||||
msgb_free(batch_msg);
|
||||
}
|
||||
|
||||
static struct osmux_handle *
|
||||
osmux_handle_find_get(struct in_addr *addr, int rem_port)
|
||||
{
|
||||
struct osmux_handle *h;
|
||||
|
||||
/* Lookup for existing OSMUX handle for this destination address. */
|
||||
llist_for_each_entry(h, &osmux_handle_list, head) {
|
||||
if (memcmp(&h->rem_addr, addr, sizeof(struct in_addr)) == 0 &&
|
||||
h->rem_port == rem_port) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "using existing OSMUX handle "
|
||||
"for addr=%s:%d\n",
|
||||
inet_ntoa(*addr), ntohs(rem_port));
|
||||
h->refcnt++;
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void osmux_handle_put(struct osmux_in_handle *in)
|
||||
{
|
||||
struct osmux_handle *h;
|
||||
|
||||
/* Lookup for existing OSMUX handle for this destination address. */
|
||||
llist_for_each_entry(h, &osmux_handle_list, head) {
|
||||
if (h->in == in) {
|
||||
if (--h->refcnt == 0) {
|
||||
LOGP(DLMGCP, LOGL_INFO,
|
||||
"Releasing unused osmux handle for %s:%d\n",
|
||||
inet_ntoa(h->rem_addr),
|
||||
ntohs(h->rem_port));
|
||||
LOGP(DLMGCP, LOGL_INFO, "Stats: "
|
||||
"input RTP msgs: %u bytes: %"PRIu64" "
|
||||
"output osmux msgs: %u bytes: %"PRIu64"\n",
|
||||
in->stats.input_rtp_msgs,
|
||||
in->stats.input_rtp_bytes,
|
||||
in->stats.output_osmux_msgs,
|
||||
in->stats.output_osmux_bytes);
|
||||
llist_del(&h->head);
|
||||
osmux_xfrm_input_fini(h->in);
|
||||
talloc_free(h);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_ERROR, "cannot find Osmux input handle %p\n", in);
|
||||
}
|
||||
|
||||
static struct osmux_handle *
|
||||
osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
|
||||
{
|
||||
struct osmux_handle *h;
|
||||
|
||||
h = talloc_zero(osmux, struct osmux_handle);
|
||||
if (!h)
|
||||
return NULL;
|
||||
h->rem_addr = *addr;
|
||||
h->rem_port = rem_port;
|
||||
h->refcnt++;
|
||||
|
||||
h->in = talloc_zero(h, struct osmux_in_handle);
|
||||
if (!h->in) {
|
||||
talloc_free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->in->osmux_seq = 0; /* sequence number to start OSmux message from */
|
||||
h->in->batch_factor = cfg->osmux_batch;
|
||||
/* If batch size is zero, the library defaults to 1470 bytes. */
|
||||
h->in->batch_size = cfg->osmux_batch_size;
|
||||
h->in->deliver = osmux_deliver;
|
||||
osmux_xfrm_input_init(h->in);
|
||||
h->in->data = h;
|
||||
|
||||
llist_add(&h->head, &osmux_handle_list);
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "created new OSMUX handle for addr=%s:%d\n",
|
||||
inet_ntoa(*addr), ntohs(rem_port));
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static struct osmux_in_handle *
|
||||
osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
|
||||
{
|
||||
struct osmux_handle *h;
|
||||
|
||||
h = osmux_handle_find_get(addr, rem_port);
|
||||
if (h != NULL)
|
||||
return h->in;
|
||||
|
||||
h = osmux_handle_alloc(cfg, addr, rem_port);
|
||||
if (h == NULL)
|
||||
return NULL;
|
||||
|
||||
return h->in;
|
||||
}
|
||||
|
||||
int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp)
|
||||
{
|
||||
int ret;
|
||||
struct msgb *msg;
|
||||
|
||||
msg = msgb_alloc(4096, "RTP");
|
||||
if (!msg)
|
||||
return 0;
|
||||
|
||||
memcpy(msg->data, buf, rc);
|
||||
msgb_put(msg, rc);
|
||||
|
||||
while ((ret = osmux_xfrm_input(endp->osmux.in, msg, endp->osmux.cid)) > 0) {
|
||||
/* batch full, build and deliver it */
|
||||
osmux_xfrm_input_deliver(endp->osmux.in);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mgcp_endpoint *
|
||||
endpoint_lookup(struct mgcp_config *cfg, int cid,
|
||||
struct in_addr *from_addr, int type)
|
||||
{
|
||||
struct mgcp_endpoint *tmp = NULL;
|
||||
int i;
|
||||
|
||||
/* Lookup for the endpoint that corresponds to this port */
|
||||
for (i=0; i<cfg->trunk.number_endpoints; i++) {
|
||||
struct in_addr *this;
|
||||
|
||||
tmp = &cfg->trunk.endpoints[i];
|
||||
|
||||
if (!tmp->allocated)
|
||||
continue;
|
||||
|
||||
switch(type) {
|
||||
case MGCP_DEST_NET:
|
||||
this = &tmp->net_end.addr;
|
||||
break;
|
||||
case MGCP_DEST_BTS:
|
||||
this = &tmp->bts_end.addr;
|
||||
break;
|
||||
default:
|
||||
/* Should not ever happen */
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (tmp->osmux.cid == cid && this->s_addr == from_addr->s_addr)
|
||||
return tmp;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n", cid);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void scheduled_tx_net_cb(struct msgb *msg, void *data)
|
||||
{
|
||||
struct mgcp_endpoint *endp = data;
|
||||
struct sockaddr_in addr = {
|
||||
.sin_addr = endp->net_end.addr,
|
||||
.sin_port = endp->net_end.rtp_port,
|
||||
};
|
||||
|
||||
endp->bts_end.octets += msg->len;
|
||||
endp->bts_end.packets++;
|
||||
|
||||
mgcp_send(endp, MGCP_DEST_NET, 1, &addr, (char *)msg->data, msg->len);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
|
||||
{
|
||||
struct mgcp_endpoint *endp = data;
|
||||
struct sockaddr_in addr = {
|
||||
.sin_addr = endp->bts_end.addr,
|
||||
.sin_port = endp->bts_end.rtp_port,
|
||||
};
|
||||
|
||||
endp->net_end.octets += msg->len;
|
||||
endp->net_end.packets++;
|
||||
|
||||
mgcp_send(endp, MGCP_DEST_BTS, 1, &addr, (char *)msg->data, msg->len);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr)
|
||||
{
|
||||
struct msgb *msg;
|
||||
socklen_t slen = sizeof(*addr);
|
||||
int ret;
|
||||
|
||||
msg = msgb_alloc(4096, "OSMUX");
|
||||
if (!msg) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "cannot allocate message\n");
|
||||
return NULL;
|
||||
}
|
||||
ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0,
|
||||
(struct sockaddr *)addr, &slen);
|
||||
if (ret <= 0) {
|
||||
msgb_free(msg);
|
||||
LOGP(DLMGCP, LOGL_ERROR, "cannot receive message\n");
|
||||
return NULL;
|
||||
}
|
||||
msgb_put(msg, ret);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
#define osmux_chunk_length(msg, rem) (rem - msg->len);
|
||||
|
||||
int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct osmux_hdr *osmuxh;
|
||||
struct sockaddr_in addr;
|
||||
struct mgcp_config *cfg = ofd->data;
|
||||
uint32_t rem;
|
||||
|
||||
msg = osmux_recv(ofd, &addr);
|
||||
if (!msg)
|
||||
return -1;
|
||||
|
||||
/* not any further processing dummy messages */
|
||||
if (msg->data[0] == MGCP_DUMMY_LOAD)
|
||||
goto out;
|
||||
|
||||
rem = msg->len;
|
||||
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
/* Yes, we use MGCP_DEST_NET to locate the origin */
|
||||
endp = endpoint_lookup(cfg, osmuxh->circuit_id,
|
||||
&addr.sin_addr, MGCP_DEST_NET);
|
||||
if (!endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot find an endpoint for circuit_id=%d\n",
|
||||
osmuxh->circuit_id);
|
||||
goto out;
|
||||
}
|
||||
endp->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
||||
endp->osmux.stats.chunks++;
|
||||
rem = msg->len;
|
||||
|
||||
osmux_xfrm_output_sched(&endp->osmux.out, osmuxh);
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is called from the bsc-nat */
|
||||
static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
|
||||
struct msgb *msg)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
uint8_t osmux_cid;
|
||||
|
||||
if (msg->len < 1 + sizeof(osmux_cid)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Discarding truncated Osmux dummy load\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Received Osmux dummy load from %s\n",
|
||||
inet_ntoa(addr->sin_addr));
|
||||
|
||||
if (!cfg->osmux) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"bsc wants to use Osmux but bsc-nat did not request it\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* extract the osmux CID from the dummy message */
|
||||
memcpy(&osmux_cid, &msg->data[1], sizeof(osmux_cid));
|
||||
|
||||
endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, MGCP_DEST_BTS);
|
||||
if (!endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot find endpoint for Osmux CID %d\n", osmux_cid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (endp->osmux.state == OSMUX_STATE_ENABLED)
|
||||
goto out;
|
||||
|
||||
if (osmux_enable_endpoint(endp, &addr->sin_addr, addr->sin_port) < 0 ) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Could not enable osmux in endpoint %d\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
goto out;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_INFO, "Enabling osmux in endpoint %d for %s:%u\n",
|
||||
ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr),
|
||||
ntohs(addr->sin_port));
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct osmux_hdr *osmuxh;
|
||||
struct sockaddr_in addr;
|
||||
struct mgcp_config *cfg = ofd->data;
|
||||
uint32_t rem;
|
||||
|
||||
msg = osmux_recv(ofd, &addr);
|
||||
if (!msg)
|
||||
return -1;
|
||||
|
||||
/* not any further processing dummy messages */
|
||||
if (msg->data[0] == MGCP_DUMMY_LOAD)
|
||||
return osmux_handle_dummy(cfg, &addr, msg);
|
||||
|
||||
rem = msg->len;
|
||||
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
/* Yes, we use MGCP_DEST_BTS to locate the origin */
|
||||
endp = endpoint_lookup(cfg, osmuxh->circuit_id,
|
||||
&addr.sin_addr, MGCP_DEST_BTS);
|
||||
if (!endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot find an endpoint for circuit_id=%d\n",
|
||||
osmuxh->circuit_id);
|
||||
goto out;
|
||||
}
|
||||
endp->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
||||
endp->osmux.stats.chunks++;
|
||||
rem = msg->len;
|
||||
|
||||
osmux_xfrm_output_sched(&endp->osmux.out, osmuxh);
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmux_init(int role, struct mgcp_config *cfg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch(role) {
|
||||
case OSMUX_ROLE_BSC:
|
||||
osmux_fd.cb = osmux_read_from_bsc_nat_cb;
|
||||
break;
|
||||
case OSMUX_ROLE_BSC_NAT:
|
||||
osmux_fd.cb = osmux_read_from_bsc_cb;
|
||||
break;
|
||||
default:
|
||||
LOGP(DLMGCP, LOGL_ERROR, "wrong role for OSMUX\n");
|
||||
return -1;
|
||||
}
|
||||
osmux_fd.data = cfg;
|
||||
|
||||
ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port);
|
||||
if (ret < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "cannot bind OSMUX socket\n");
|
||||
return ret;
|
||||
}
|
||||
mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp);
|
||||
osmux_fd.when |= BSC_FD_READ;
|
||||
|
||||
ret = osmo_fd_register(&osmux_fd);
|
||||
if (ret < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "cannot register OSMUX socket\n");
|
||||
return ret;
|
||||
}
|
||||
cfg->osmux_init = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint16_t port)
|
||||
{
|
||||
/* If osmux is enabled, initialize the output handler. This handler is
|
||||
* used to reconstruct the RTP flow from osmux. The RTP SSRC is
|
||||
* allocated based on the circuit ID (endp->osmux.cid), which is unique
|
||||
* in the local scope to the BSC/BSC-NAT. We use it to divide the RTP
|
||||
* SSRC space (2^32) by the 256 possible circuit IDs, then randomly
|
||||
* select one value from that window. Thus, we have no chance to have
|
||||
* overlapping RTP SSRC traveling to the BTSes behind the BSC,
|
||||
* similarly, for flows traveling to the MSC.
|
||||
*/
|
||||
static const uint32_t rtp_ssrc_winlen = UINT32_MAX / 256;
|
||||
|
||||
if (endp->osmux.state == OSMUX_STATE_DISABLED) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Endpoint %u didn't request Osmux\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
osmux_xfrm_output_init(&endp->osmux.out,
|
||||
(endp->osmux.cid * rtp_ssrc_winlen) +
|
||||
(random() % rtp_ssrc_winlen));
|
||||
|
||||
endp->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
|
||||
if (!endp->osmux.in) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle\n");
|
||||
return -1;
|
||||
}
|
||||
if (!osmux_xfrm_input_open_circuit(endp->osmux.in, endp->osmux.cid,
|
||||
endp->cfg->osmux_dummy)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot open osmux circuit %u\n",
|
||||
endp->osmux.cid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (endp->cfg->role) {
|
||||
case MGCP_BSC_NAT:
|
||||
endp->type = MGCP_OSMUX_BSC_NAT;
|
||||
osmux_xfrm_output_set_tx_cb(&endp->osmux.out,
|
||||
scheduled_tx_net_cb, endp);
|
||||
break;
|
||||
case MGCP_BSC:
|
||||
endp->type = MGCP_OSMUX_BSC;
|
||||
osmux_xfrm_output_set_tx_cb(&endp->osmux.out,
|
||||
scheduled_tx_bts_cb, endp);
|
||||
break;
|
||||
}
|
||||
endp->osmux.state = OSMUX_STATE_ENABLED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void osmux_disable_endpoint(struct mgcp_endpoint *endp)
|
||||
{
|
||||
LOGP(DLMGCP, LOGL_INFO, "Releasing endpoint %u using Osmux CID %u\n",
|
||||
ENDPOINT_NUMBER(endp), endp->osmux.cid);
|
||||
|
||||
/* We are closing, we don't need pending RTP packets to be transmitted */
|
||||
osmux_xfrm_output_set_tx_cb(&endp->osmux.out, NULL, NULL);
|
||||
osmux_xfrm_output_flush(&endp->osmux.out);
|
||||
|
||||
osmux_xfrm_input_close_circuit(endp->osmux.in, endp->osmux.cid);
|
||||
endp->osmux.state = OSMUX_STATE_DISABLED;
|
||||
endp->osmux.cid = -1;
|
||||
osmux_handle_put(endp->osmux.in);
|
||||
}
|
||||
|
||||
void osmux_release_cid(struct mgcp_endpoint *endp)
|
||||
{
|
||||
if (endp->osmux.allocated_cid >= 0)
|
||||
osmux_put_cid(endp->osmux.allocated_cid);
|
||||
endp->osmux.allocated_cid = -1;
|
||||
}
|
||||
|
||||
void osmux_allocate_cid(struct mgcp_endpoint *endp)
|
||||
{
|
||||
osmux_release_cid(endp);
|
||||
endp->osmux.allocated_cid = osmux_get_cid();
|
||||
}
|
||||
|
||||
/* We don't need to send the dummy load for osmux so often as another endpoint
|
||||
* may have already punched the hole in the firewall. This approach is simple
|
||||
* though.
|
||||
*/
|
||||
int osmux_send_dummy(struct mgcp_endpoint *endp)
|
||||
{
|
||||
char buf[1 + sizeof(uint8_t)];
|
||||
struct in_addr addr_unset = {};
|
||||
|
||||
buf[0] = MGCP_DUMMY_LOAD;
|
||||
memcpy(&buf[1], &endp->osmux.cid, sizeof(endp->osmux.cid));
|
||||
|
||||
/* Wait until we have the connection information from MDCX */
|
||||
if (memcmp(&endp->net_end.addr, &addr_unset, sizeof(addr_unset)) == 0)
|
||||
return 0;
|
||||
|
||||
if (endp->osmux.state == OSMUX_STATE_ACTIVATING) {
|
||||
if (osmux_enable_endpoint(endp, &endp->net_end.addr,
|
||||
htons(endp->cfg->osmux_port)) < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Could not activate osmux in endpoint %d\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Osmux CID %u for %s:%u is now enabled\n",
|
||||
endp->osmux.cid, inet_ntoa(endp->net_end.addr),
|
||||
endp->cfg->osmux_port);
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"sending OSMUX dummy load to %s CID %u\n",
|
||||
inet_ntoa(endp->net_end.addr), endp->osmux.cid);
|
||||
|
||||
return mgcp_udp_send(osmux_fd.fd, &endp->net_end.addr,
|
||||
htons(endp->cfg->osmux_port), buf, sizeof(buf));
|
||||
}
|
||||
|
||||
/* bsc-nat allocates/releases the Osmux circuit ID */
|
||||
static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1) / 8];
|
||||
|
||||
int osmux_used_cid(void)
|
||||
{
|
||||
int i, j, used = 0;
|
||||
|
||||
for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
|
||||
for (j = 0; j < 8; j++) {
|
||||
if (osmux_cid_bitmap[i] & (1 << j))
|
||||
used += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
int osmux_get_cid(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
|
||||
for (j = 0; j < 8; j++) {
|
||||
if (osmux_cid_bitmap[i] & (1 << j))
|
||||
continue;
|
||||
|
||||
osmux_cid_bitmap[i] |= (1 << j);
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"Allocating Osmux CID %u from pool\n", (i * 8) + j);
|
||||
return (i * 8) + j;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "All Osmux circuits are in use!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void osmux_put_cid(uint8_t osmux_cid)
|
||||
{
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid);
|
||||
osmux_cid_bitmap[osmux_cid / 8] &= ~(1 << (osmux_cid % 8));
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,305 +0,0 @@
|
||||
/*
|
||||
* Some SDP file parsing...
|
||||
*
|
||||
* (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2014 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/legacy_mgcp/mgcp.h>
|
||||
#include <osmocom/legacy_mgcp/mgcp_internal.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
struct sdp_rtp_map {
|
||||
/* the type */
|
||||
int payload_type;
|
||||
/* null, static or later dynamic codec name */
|
||||
char *codec_name;
|
||||
/* A pointer to the original line for later parsing */
|
||||
char *map_line;
|
||||
|
||||
int rate;
|
||||
int channels;
|
||||
};
|
||||
|
||||
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
|
||||
int payload_type, const char *audio_name)
|
||||
{
|
||||
int rate = codec->rate;
|
||||
int channels = codec->channels;
|
||||
char audio_codec[64];
|
||||
|
||||
talloc_free(codec->subtype_name);
|
||||
codec->subtype_name = NULL;
|
||||
talloc_free(codec->audio_name);
|
||||
codec->audio_name = NULL;
|
||||
|
||||
if (payload_type != PTYPE_UNDEFINED)
|
||||
codec->payload_type = payload_type;
|
||||
|
||||
if (!audio_name) {
|
||||
switch (payload_type) {
|
||||
case 0: audio_name = "PCMU/8000/1"; break;
|
||||
case 3: audio_name = "GSM/8000/1"; break;
|
||||
case 8: audio_name = "PCMA/8000/1"; break;
|
||||
case 18: audio_name = "G729/8000/1"; break;
|
||||
default:
|
||||
/* Payload type is unknown, don't change rate and
|
||||
* channels. */
|
||||
/* TODO: return value? */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
||||
audio_codec, &rate, &channels) < 1)
|
||||
return -EINVAL;
|
||||
|
||||
codec->rate = rate;
|
||||
codec->channels = channels;
|
||||
codec->subtype_name = talloc_strdup(ctx, audio_codec);
|
||||
codec->audio_name = talloc_strdup(ctx, audio_name);
|
||||
|
||||
if (!strcmp(audio_codec, "G729")) {
|
||||
codec->frame_duration_num = 10;
|
||||
codec->frame_duration_den = 1000;
|
||||
} else {
|
||||
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
||||
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
||||
}
|
||||
|
||||
if (payload_type < 0) {
|
||||
payload_type = 96;
|
||||
if (rate == 8000 && channels == 1) {
|
||||
if (!strcmp(audio_codec, "GSM"))
|
||||
payload_type = 3;
|
||||
else if (!strcmp(audio_codec, "PCMA"))
|
||||
payload_type = 8;
|
||||
else if (!strcmp(audio_codec, "PCMU"))
|
||||
payload_type = 0;
|
||||
else if (!strcmp(audio_codec, "G729"))
|
||||
payload_type = 18;
|
||||
}
|
||||
|
||||
codec->payload_type = payload_type;
|
||||
}
|
||||
|
||||
if (channels != 1)
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Channels != 1 in SDP: '%s'\n", audio_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < used; ++i) {
|
||||
switch (codecs[i].payload_type) {
|
||||
case 0:
|
||||
codecs[i].codec_name = "PCMU";
|
||||
codecs[i].rate = 8000;
|
||||
codecs[i].channels = 1;
|
||||
break;
|
||||
case 3:
|
||||
codecs[i].codec_name = "GSM";
|
||||
codecs[i].rate = 8000;
|
||||
codecs[i].channels = 1;
|
||||
break;
|
||||
case 8:
|
||||
codecs[i].codec_name = "PCMA";
|
||||
codecs[i].rate = 8000;
|
||||
codecs[i].channels = 1;
|
||||
break;
|
||||
case 18:
|
||||
codecs[i].codec_name = "G729";
|
||||
codecs[i].rate = 8000;
|
||||
codecs[i].channels = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, int payload, char *audio_name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < used; ++i) {
|
||||
char audio_codec[64];
|
||||
int rate = -1;
|
||||
int channels = -1;
|
||||
if (codecs[i].payload_type != payload)
|
||||
continue;
|
||||
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
||||
audio_codec, &rate, &channels) < 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n", audio_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
codecs[i].map_line = talloc_strdup(ctx, audio_name);
|
||||
codecs[i].codec_name = talloc_strdup(ctx, audio_codec);
|
||||
codecs[i].rate = rate;
|
||||
codecs[i].channels = channels;
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload, audio_name);
|
||||
}
|
||||
|
||||
int is_codec_compatible(struct mgcp_endpoint *endp, struct sdp_rtp_map *codec)
|
||||
{
|
||||
char *bts_codec;
|
||||
char audio_codec[64];
|
||||
|
||||
if (!codec->codec_name)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* GSM, GSM/8000 and GSM/8000/1 should all be compatible.. let's go
|
||||
* by name first.
|
||||
*/
|
||||
bts_codec = endp->tcfg->audio_name;
|
||||
if (sscanf(bts_codec, "%63[^/]/%*d/%*d", audio_codec) < 1)
|
||||
return 0;
|
||||
|
||||
return strcasecmp(audio_codec, codec->codec_name) == 0;
|
||||
}
|
||||
|
||||
int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
|
||||
{
|
||||
struct sdp_rtp_map codecs[10];
|
||||
int codecs_used = 0;
|
||||
char *line;
|
||||
int maxptime = -1;
|
||||
int i;
|
||||
int codecs_assigned = 0;
|
||||
void *tmp_ctx = talloc_new(NULL);
|
||||
|
||||
memset(&codecs, 0, sizeof(codecs));
|
||||
|
||||
for_each_line(line, p->save) {
|
||||
switch (line[0]) {
|
||||
case 'o':
|
||||
case 's':
|
||||
case 't':
|
||||
case 'v':
|
||||
/* skip these SDP attributes */
|
||||
break;
|
||||
case 'a': {
|
||||
int payload;
|
||||
int ptime, ptime2 = 0;
|
||||
char audio_name[64];
|
||||
|
||||
|
||||
if (sscanf(line, "a=rtpmap:%d %63s",
|
||||
&payload, audio_name) == 2) {
|
||||
codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
|
||||
} else if (sscanf(line, "a=ptime:%d-%d",
|
||||
&ptime, &ptime2) >= 1) {
|
||||
if (ptime2 > 0 && ptime2 != ptime)
|
||||
rtp->packet_duration_ms = 0;
|
||||
else
|
||||
rtp->packet_duration_ms = ptime;
|
||||
} else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
|
||||
maxptime = ptime2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'm': {
|
||||
int port, rc;
|
||||
|
||||
rc = sscanf(line, "m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d",
|
||||
&port,
|
||||
&codecs[0].payload_type,
|
||||
&codecs[1].payload_type,
|
||||
&codecs[2].payload_type,
|
||||
&codecs[3].payload_type,
|
||||
&codecs[4].payload_type,
|
||||
&codecs[5].payload_type,
|
||||
&codecs[6].payload_type,
|
||||
&codecs[7].payload_type,
|
||||
&codecs[8].payload_type,
|
||||
&codecs[9].payload_type);
|
||||
if (rc >= 2) {
|
||||
rtp->rtp_port = htons(port);
|
||||
rtp->rtcp_port = htons(port + 1);
|
||||
codecs_used = rc - 1;
|
||||
codecs_initialize(tmp_ctx, codecs, codecs_used);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
char ipv4[16];
|
||||
|
||||
if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
|
||||
inet_aton(ipv4, &rtp->addr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (p->endp)
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Unhandled SDP option: '%c'/%d on 0x%x\n",
|
||||
line[0], line[0], ENDPOINT_NUMBER(p->endp));
|
||||
else
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Unhandled SDP option: '%c'/%d\n",
|
||||
line[0], line[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now select the primary and alt_codec */
|
||||
for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) {
|
||||
struct mgcp_rtp_codec *codec = codecs_assigned == 0 ?
|
||||
&rtp->codec : &rtp->alt_codec;
|
||||
|
||||
if (endp->tcfg->no_audio_transcoding &&
|
||||
!is_codec_compatible(endp, &codecs[i])) {
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "Skipping codec %s\n",
|
||||
codecs[i].codec_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
mgcp_set_audio_info(p->cfg, codec,
|
||||
codecs[i].payload_type,
|
||||
codecs[i].map_line);
|
||||
codecs_assigned += 1;
|
||||
}
|
||||
|
||||
if (codecs_assigned > 0) {
|
||||
/* TODO/XXX: Store this per codec and derive it on use */
|
||||
if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den >
|
||||
rtp->codec.frame_duration_num * 1500) {
|
||||
/* more than 1 frame */
|
||||
rtp->packet_duration_ms = 0;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Got media info via SDP: port %d, payload %d (%s), "
|
||||
"duration %d, addr %s\n",
|
||||
ntohs(rtp->rtp_port), rtp->codec.payload_type,
|
||||
rtp->codec.subtype_name ? rtp->codec.subtype_name : "unknown",
|
||||
rtp->packet_duration_ms, inet_ntoa(rtp->addr));
|
||||
}
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
return codecs_assigned > 0;
|
||||
}
|
||||
|
@@ -1,611 +0,0 @@
|
||||
/*
|
||||
* (C) 2014 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
#include "g711common.h"
|
||||
|
||||
#include <osmocom/legacy_mgcp/mgcp.h>
|
||||
#include <osmocom/legacy_mgcp/mgcp_internal.h>
|
||||
#include <osmocom/legacy_mgcp/mgcp_transcode.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/netif/rtp.h>
|
||||
|
||||
int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst)
|
||||
{
|
||||
struct mgcp_process_rtp_state *state = state_;
|
||||
if (dst)
|
||||
return (nsamples >= 0 ?
|
||||
nsamples / state->dst_samples_per_frame :
|
||||
1) * state->dst_frame_size;
|
||||
else
|
||||
return (nsamples >= 0 ?
|
||||
nsamples / state->src_samples_per_frame :
|
||||
1) * state->src_frame_size;
|
||||
}
|
||||
|
||||
static enum audio_format get_audio_format(const struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
if (codec->subtype_name) {
|
||||
if (!strcasecmp("GSM", codec->subtype_name))
|
||||
return AF_GSM;
|
||||
if (!strcasecmp("PCMA", codec->subtype_name))
|
||||
return AF_PCMA;
|
||||
if (!strcasecmp("PCMU", codec->subtype_name))
|
||||
return AF_PCMU;
|
||||
#ifdef HAVE_BCG729
|
||||
if (!strcasecmp("G729", codec->subtype_name))
|
||||
return AF_G729;
|
||||
#endif
|
||||
if (!strcasecmp("L16", codec->subtype_name))
|
||||
return AF_L16;
|
||||
}
|
||||
|
||||
switch (codec->payload_type) {
|
||||
case 0 /* PCMU */:
|
||||
return AF_PCMU;
|
||||
case 3 /* GSM */:
|
||||
return AF_GSM;
|
||||
case 8 /* PCMA */:
|
||||
return AF_PCMA;
|
||||
#ifdef HAVE_BCG729
|
||||
case 18 /* G.729 */:
|
||||
return AF_G729;
|
||||
#endif
|
||||
case 11 /* L16 */:
|
||||
return AF_L16;
|
||||
default:
|
||||
return AF_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
static void l16_encode(short *sample, unsigned char *buf, size_t n)
|
||||
{
|
||||
for (; n > 0; --n, ++sample, buf += 2) {
|
||||
buf[0] = sample[0] >> 8;
|
||||
buf[1] = sample[0] & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
static void l16_decode(unsigned char *buf, short *sample, size_t n)
|
||||
{
|
||||
for (; n > 0; --n, ++sample, buf += 2)
|
||||
sample[0] = ((short)buf[0] << 8) | buf[1];
|
||||
}
|
||||
|
||||
static void alaw_encode(short *sample, unsigned char *buf, size_t n)
|
||||
{
|
||||
for (; n > 0; --n)
|
||||
*(buf++) = s16_to_alaw(*(sample++));
|
||||
}
|
||||
|
||||
static void alaw_decode(unsigned char *buf, short *sample, size_t n)
|
||||
{
|
||||
for (; n > 0; --n)
|
||||
*(sample++) = alaw_to_s16(*(buf++));
|
||||
}
|
||||
|
||||
static void ulaw_encode(short *sample, unsigned char *buf, size_t n)
|
||||
{
|
||||
for (; n > 0; --n)
|
||||
*(buf++) = s16_to_ulaw(*(sample++));
|
||||
}
|
||||
|
||||
static void ulaw_decode(unsigned char *buf, short *sample, size_t n)
|
||||
{
|
||||
for (; n > 0; --n)
|
||||
*(sample++) = ulaw_to_s16(*(buf++));
|
||||
}
|
||||
|
||||
static int processing_state_destructor(struct mgcp_process_rtp_state *state)
|
||||
{
|
||||
switch (state->src_fmt) {
|
||||
case AF_GSM:
|
||||
if (state->src.gsm_handle)
|
||||
gsm_destroy(state->src.gsm_handle);
|
||||
break;
|
||||
#ifdef HAVE_BCG729
|
||||
case AF_G729:
|
||||
if (state->src.g729_dec)
|
||||
closeBcg729DecoderChannel(state->src.g729_dec);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (state->dst_fmt) {
|
||||
case AF_GSM:
|
||||
if (state->dst.gsm_handle)
|
||||
gsm_destroy(state->dst.gsm_handle);
|
||||
break;
|
||||
#ifdef HAVE_BCG729
|
||||
case AF_G729:
|
||||
if (state->dst.g729_enc)
|
||||
closeBcg729EncoderChannel(state->dst.g729_enc);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mgcp_transcoding_setup(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end,
|
||||
struct mgcp_rtp_end *src_end)
|
||||
{
|
||||
struct mgcp_process_rtp_state *state;
|
||||
enum audio_format src_fmt, dst_fmt;
|
||||
const struct mgcp_rtp_codec *dst_codec = &dst_end->codec;
|
||||
|
||||
/* cleanup first */
|
||||
if (dst_end->rtp_process_data) {
|
||||
talloc_free(dst_end->rtp_process_data);
|
||||
dst_end->rtp_process_data = NULL;
|
||||
}
|
||||
|
||||
if (!src_end)
|
||||
return 0;
|
||||
|
||||
const struct mgcp_rtp_codec *src_codec = &src_end->codec;
|
||||
|
||||
if (endp->tcfg->no_audio_transcoding) {
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Transcoding disabled on endpoint 0x%x\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return 0;
|
||||
}
|
||||
|
||||
src_fmt = get_audio_format(src_codec);
|
||||
dst_fmt = get_audio_format(dst_codec);
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Checking transcoding: %s (%d) -> %s (%d)\n",
|
||||
src_codec->subtype_name, src_codec->payload_type,
|
||||
dst_codec->subtype_name, dst_codec->payload_type);
|
||||
|
||||
if (src_fmt == AF_INVALID || dst_fmt == AF_INVALID) {
|
||||
if (!src_codec->subtype_name || !dst_codec->subtype_name)
|
||||
/* Not enough info, do nothing */
|
||||
return 0;
|
||||
|
||||
if (strcasecmp(src_codec->subtype_name, dst_codec->subtype_name) == 0)
|
||||
/* Nothing to do */
|
||||
return 0;
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot transcode: %s codec not supported (%s -> %s).\n",
|
||||
src_fmt != AF_INVALID ? "destination" : "source",
|
||||
src_codec->audio_name, dst_codec->audio_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (src_codec->rate && dst_codec->rate && src_codec->rate != dst_codec->rate) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot transcode: rate conversion (%d -> %d) not supported.\n",
|
||||
src_codec->rate, dst_codec->rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
state = talloc_zero(endp->tcfg->cfg, struct mgcp_process_rtp_state);
|
||||
talloc_set_destructor(state, processing_state_destructor);
|
||||
dst_end->rtp_process_data = state;
|
||||
|
||||
state->src_fmt = src_fmt;
|
||||
|
||||
switch (state->src_fmt) {
|
||||
case AF_L16:
|
||||
case AF_S16:
|
||||
state->src_frame_size = 80 * sizeof(short);
|
||||
state->src_samples_per_frame = 80;
|
||||
break;
|
||||
case AF_GSM:
|
||||
state->src_frame_size = sizeof(gsm_frame);
|
||||
state->src_samples_per_frame = 160;
|
||||
state->src.gsm_handle = gsm_create();
|
||||
if (!state->src.gsm_handle) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to initialize GSM decoder.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
#ifdef HAVE_BCG729
|
||||
case AF_G729:
|
||||
state->src_frame_size = 10;
|
||||
state->src_samples_per_frame = 80;
|
||||
state->src.g729_dec = initBcg729DecoderChannel();
|
||||
if (!state->src.g729_dec) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to initialize G.729 decoder.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case AF_PCMU:
|
||||
case AF_PCMA:
|
||||
state->src_frame_size = 80;
|
||||
state->src_samples_per_frame = 80;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
state->dst_fmt = dst_fmt;
|
||||
|
||||
switch (state->dst_fmt) {
|
||||
case AF_L16:
|
||||
case AF_S16:
|
||||
state->dst_frame_size = 80*sizeof(short);
|
||||
state->dst_samples_per_frame = 80;
|
||||
break;
|
||||
case AF_GSM:
|
||||
state->dst_frame_size = sizeof(gsm_frame);
|
||||
state->dst_samples_per_frame = 160;
|
||||
state->dst.gsm_handle = gsm_create();
|
||||
if (!state->dst.gsm_handle) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to initialize GSM encoder.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
#ifdef HAVE_BCG729
|
||||
case AF_G729:
|
||||
state->dst_frame_size = 10;
|
||||
state->dst_samples_per_frame = 80;
|
||||
state->dst.g729_enc = initBcg729EncoderChannel();
|
||||
if (!state->dst.g729_enc) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to initialize G.729 decoder.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case AF_PCMU:
|
||||
case AF_PCMA:
|
||||
state->dst_frame_size = 80;
|
||||
state->dst_samples_per_frame = 80;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (dst_end->force_output_ptime)
|
||||
state->dst_packet_duration = mgcp_rtp_packet_duration(endp, dst_end);
|
||||
|
||||
LOGP(DLMGCP, LOGL_INFO,
|
||||
"Initialized RTP processing on: 0x%x "
|
||||
"conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n",
|
||||
ENDPOINT_NUMBER(endp),
|
||||
src_fmt, src_codec->payload_type, src_codec->rate, src_end->fmtp_extra,
|
||||
dst_fmt, dst_codec->payload_type, dst_codec->rate, dst_end->fmtp_extra);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp,
|
||||
int *payload_type,
|
||||
const char**audio_name,
|
||||
const char**fmtp_extra)
|
||||
{
|
||||
struct mgcp_process_rtp_state *state = endp->net_end.rtp_process_data;
|
||||
struct mgcp_rtp_codec *net_codec = &endp->net_end.codec;
|
||||
struct mgcp_rtp_codec *bts_codec = &endp->bts_end.codec;
|
||||
|
||||
if (!state || net_codec->payload_type < 0) {
|
||||
*payload_type = bts_codec->payload_type;
|
||||
*audio_name = bts_codec->audio_name;
|
||||
*fmtp_extra = endp->bts_end.fmtp_extra;
|
||||
return;
|
||||
}
|
||||
|
||||
*payload_type = net_codec->payload_type;
|
||||
*audio_name = net_codec->audio_name;
|
||||
*fmtp_extra = endp->net_end.fmtp_extra;
|
||||
}
|
||||
|
||||
static int decode_audio(struct mgcp_process_rtp_state *state,
|
||||
uint8_t **src, size_t *nbytes)
|
||||
{
|
||||
while (*nbytes >= state->src_frame_size) {
|
||||
if (state->sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(state->samples)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Sample buffer too small: %zu > %zu.\n",
|
||||
state->sample_cnt + state->src_samples_per_frame,
|
||||
ARRAY_SIZE(state->samples));
|
||||
return -ENOSPC;
|
||||
}
|
||||
switch (state->src_fmt) {
|
||||
case AF_GSM:
|
||||
if (gsm_decode(state->src.gsm_handle,
|
||||
(gsm_byte *)*src, state->samples + state->sample_cnt) < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to decode GSM.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
#ifdef HAVE_BCG729
|
||||
case AF_G729:
|
||||
bcg729Decoder(state->src.g729_dec, *src, 0, state->samples + state->sample_cnt);
|
||||
break;
|
||||
#endif
|
||||
case AF_PCMU:
|
||||
ulaw_decode(*src, state->samples + state->sample_cnt,
|
||||
state->src_samples_per_frame);
|
||||
break;
|
||||
case AF_PCMA:
|
||||
alaw_decode(*src, state->samples + state->sample_cnt,
|
||||
state->src_samples_per_frame);
|
||||
break;
|
||||
case AF_S16:
|
||||
memmove(state->samples + state->sample_cnt, *src,
|
||||
state->src_frame_size);
|
||||
break;
|
||||
case AF_L16:
|
||||
l16_decode(*src, state->samples + state->sample_cnt,
|
||||
state->src_samples_per_frame);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
*src += state->src_frame_size;
|
||||
*nbytes -= state->src_frame_size;
|
||||
state->sample_cnt += state->src_samples_per_frame;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int encode_audio(struct mgcp_process_rtp_state *state,
|
||||
uint8_t *dst, size_t buf_size, size_t max_samples)
|
||||
{
|
||||
int nbytes = 0;
|
||||
size_t nsamples = 0;
|
||||
/* Encode samples into dst */
|
||||
while (nsamples + state->dst_samples_per_frame <= max_samples) {
|
||||
if (nbytes + state->dst_frame_size > buf_size) {
|
||||
if (nbytes > 0)
|
||||
break;
|
||||
|
||||
/* Not even one frame fits into the buffer */
|
||||
LOGP(DLMGCP, LOGL_INFO,
|
||||
"Encoding (RTP) buffer too small: %zu > %zu.\n",
|
||||
nbytes + state->dst_frame_size, buf_size);
|
||||
return -ENOSPC;
|
||||
}
|
||||
switch (state->dst_fmt) {
|
||||
case AF_GSM:
|
||||
gsm_encode(state->dst.gsm_handle,
|
||||
state->samples + state->sample_offs, dst);
|
||||
break;
|
||||
#ifdef HAVE_BCG729
|
||||
case AF_G729:
|
||||
bcg729Encoder(state->dst.g729_enc,
|
||||
state->samples + state->sample_offs, dst);
|
||||
break;
|
||||
#endif
|
||||
case AF_PCMU:
|
||||
ulaw_encode(state->samples + state->sample_offs, dst,
|
||||
state->src_samples_per_frame);
|
||||
break;
|
||||
case AF_PCMA:
|
||||
alaw_encode(state->samples + state->sample_offs, dst,
|
||||
state->src_samples_per_frame);
|
||||
break;
|
||||
case AF_S16:
|
||||
memmove(dst, state->samples + state->sample_offs,
|
||||
state->dst_frame_size);
|
||||
break;
|
||||
case AF_L16:
|
||||
l16_encode(state->samples + state->sample_offs, dst,
|
||||
state->src_samples_per_frame);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dst += state->dst_frame_size;
|
||||
nbytes += state->dst_frame_size;
|
||||
state->sample_offs += state->dst_samples_per_frame;
|
||||
nsamples += state->dst_samples_per_frame;
|
||||
}
|
||||
state->sample_cnt -= nsamples;
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
static struct mgcp_rtp_end *source_for_dest(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end)
|
||||
{
|
||||
if (&endp->bts_end == dst_end)
|
||||
return &endp->net_end;
|
||||
else if (&endp->net_end == dst_end)
|
||||
return &endp->bts_end;
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* With some modems we get offered multiple codecs
|
||||
* and we have selected one of them. It might not
|
||||
* be the right one and we need to detect this with
|
||||
* the first audio packets. One difficulty is that
|
||||
* we patch the rtp payload type in place, so we
|
||||
* need to discuss this.
|
||||
*/
|
||||
struct mgcp_process_rtp_state *check_transcode_state(
|
||||
struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end,
|
||||
struct rtp_hdr *rtp_hdr)
|
||||
{
|
||||
struct mgcp_rtp_end *src_end;
|
||||
|
||||
/* Only deal with messages from net to bts */
|
||||
if (&endp->bts_end != dst_end)
|
||||
goto done;
|
||||
|
||||
src_end = source_for_dest(endp, dst_end);
|
||||
|
||||
/* Already patched */
|
||||
if (rtp_hdr->payload_type == dst_end->codec.payload_type)
|
||||
goto done;
|
||||
/* The payload we expect */
|
||||
if (rtp_hdr->payload_type == src_end->codec.payload_type)
|
||||
goto done;
|
||||
/* The matching alternate payload type? Then switch */
|
||||
if (rtp_hdr->payload_type == src_end->alt_codec.payload_type) {
|
||||
struct mgcp_config *cfg = endp->cfg;
|
||||
struct mgcp_rtp_codec tmp_codec = src_end->alt_codec;
|
||||
src_end->alt_codec = src_end->codec;
|
||||
src_end->codec = tmp_codec;
|
||||
cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end);
|
||||
cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end);
|
||||
}
|
||||
|
||||
done:
|
||||
return dst_end->rtp_process_data;
|
||||
}
|
||||
|
||||
int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end,
|
||||
char *data, int *len, int buf_size)
|
||||
{
|
||||
struct mgcp_process_rtp_state *state;
|
||||
const size_t rtp_hdr_size = sizeof(struct rtp_hdr);
|
||||
struct rtp_hdr *rtp_hdr = (struct rtp_hdr *) data;
|
||||
char *payload_data = (char *) &rtp_hdr->data[0];
|
||||
int payload_len = *len - rtp_hdr_size;
|
||||
uint8_t *src = (uint8_t *)payload_data;
|
||||
uint8_t *dst = (uint8_t *)payload_data;
|
||||
size_t nbytes = payload_len;
|
||||
size_t nsamples;
|
||||
size_t max_samples;
|
||||
uint32_t ts_no;
|
||||
int rc;
|
||||
|
||||
state = check_transcode_state(endp, dst_end, rtp_hdr);
|
||||
if (!state)
|
||||
return 0;
|
||||
|
||||
if (state->src_fmt == state->dst_fmt) {
|
||||
if (!state->dst_packet_duration)
|
||||
return 0;
|
||||
|
||||
/* TODO: repackage without transcoding */
|
||||
}
|
||||
|
||||
/* If the remaining samples do not fit into a fixed ptime,
|
||||
* a) discard them, if the next packet is much later
|
||||
* b) add silence and * send it, if the current packet is not
|
||||
* yet too late
|
||||
* c) append the sample data, if the timestamp matches exactly
|
||||
*/
|
||||
|
||||
/* TODO: check payload type (-> G.711 comfort noise) */
|
||||
|
||||
if (payload_len > 0) {
|
||||
ts_no = ntohl(rtp_hdr->timestamp);
|
||||
if (!state->is_running) {
|
||||
state->next_seq = ntohs(rtp_hdr->sequence);
|
||||
state->next_time = ts_no;
|
||||
state->is_running = 1;
|
||||
}
|
||||
|
||||
|
||||
if (state->sample_cnt > 0) {
|
||||
int32_t delta = ts_no - state->next_time;
|
||||
/* TODO: check sequence? reordering? packet loss? */
|
||||
|
||||
if (delta > state->sample_cnt) {
|
||||
/* There is a time gap between the last packet
|
||||
* and the current one. Just discard the
|
||||
* partial data that is left in the buffer.
|
||||
* TODO: This can be improved by adding silence
|
||||
* instead if the delta is small enough.
|
||||
*/
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"0x%x dropping sample buffer due delta=%d sample_cnt=%zu\n",
|
||||
ENDPOINT_NUMBER(endp), delta, state->sample_cnt);
|
||||
state->sample_cnt = 0;
|
||||
state->next_time = ts_no;
|
||||
} else if (delta < 0) {
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"RTP time jumps backwards, delta = %d, "
|
||||
"discarding buffered samples\n",
|
||||
delta);
|
||||
state->sample_cnt = 0;
|
||||
state->sample_offs = 0;
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* Make sure the samples start without offset */
|
||||
if (state->sample_offs && state->sample_cnt)
|
||||
memmove(&state->samples[0],
|
||||
&state->samples[state->sample_offs],
|
||||
state->sample_cnt *
|
||||
sizeof(state->samples[0]));
|
||||
}
|
||||
|
||||
state->sample_offs = 0;
|
||||
|
||||
/* Append decoded audio to samples */
|
||||
decode_audio(state, &src, &nbytes);
|
||||
|
||||
if (nbytes > 0)
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Skipped audio frame in RTP packet: %zu octets\n",
|
||||
nbytes);
|
||||
} else
|
||||
ts_no = state->next_time;
|
||||
|
||||
if (state->sample_cnt < state->dst_packet_duration)
|
||||
return -EAGAIN;
|
||||
|
||||
max_samples =
|
||||
state->dst_packet_duration ?
|
||||
state->dst_packet_duration : state->sample_cnt;
|
||||
|
||||
nsamples = state->sample_cnt;
|
||||
|
||||
rc = encode_audio(state, dst, buf_size, max_samples);
|
||||
/*
|
||||
* There were no samples to encode?
|
||||
* TODO: how does this work for comfort noise?
|
||||
*/
|
||||
if (rc == 0)
|
||||
return -ENOMSG;
|
||||
/* Any other error during the encoding */
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
nsamples -= state->sample_cnt;
|
||||
|
||||
*len = rtp_hdr_size + rc;
|
||||
rtp_hdr->sequence = htons(state->next_seq);
|
||||
rtp_hdr->timestamp = htonl(ts_no);
|
||||
|
||||
state->next_seq += 1;
|
||||
state->next_time = ts_no + nsamples;
|
||||
|
||||
/*
|
||||
* XXX: At this point we should always have consumed
|
||||
* samples. So doing OSMO_ASSERT(nsamples > 0) and returning
|
||||
* rtp_hdr_size should be fine.
|
||||
*/
|
||||
return nsamples ? rtp_hdr_size : 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@ AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMOABIS_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
@@ -20,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=4:0:1
|
||||
MGCP_CLIENT_LIBVERSION=9:0:0
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-mgcp-client.la \
|
||||
@@ -30,6 +31,8 @@ libosmo_mgcp_client_la_SOURCES = \
|
||||
mgcp_client.c \
|
||||
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)
|
||||
|
File diff suppressed because it is too large
Load Diff
1036
src/libosmo-mgcp-client/mgcp_client_endpoint_fsm.c
Normal file
1036
src/libosmo-mgcp-client/mgcp_client_endpoint_fsm.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,7 @@
|
||||
#include <osmocom/core/byteswap.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
|
||||
/* Context information, this is attached to the priv pointer of the FSM and
|
||||
* is also handed back when dispatcheing events to the parent FSM. This is
|
||||
@@ -41,7 +42,7 @@ struct mgcp_ctx {
|
||||
bool mgw_trans_pending;
|
||||
|
||||
/* Connection ID which has been assigned by he MGW */
|
||||
char conn_id[MGCP_CONN_ID_LENGTH];
|
||||
char conn_id[MGCP_CONN_ID_MAXLEN];
|
||||
|
||||
/* Local RTP connection info, the MGW will send outgoing traffic to the
|
||||
* ip/port specified here. The Address does not have to be choosen right
|
||||
@@ -104,44 +105,48 @@ static const struct value_string fsm_mgcp_client_evt_names[] = {
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static struct msgb *make_crcx_msg_bind(struct mgcp_ctx *mgcp_ctx)
|
||||
static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
|
||||
{
|
||||
struct mgcp_msg mgcp_msg;
|
||||
|
||||
mgcp_msg = (struct mgcp_msg) {
|
||||
*mgcp_msg = (struct mgcp_msg) {
|
||||
.verb = MGCP_VERB_CRCX,
|
||||
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
|
||||
.call_id = mgcp_ctx->conn_peer_local.call_id,
|
||||
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID
|
||||
| MGCP_MSG_PRESENCE_CONN_MODE),
|
||||
.call_id = info->call_id,
|
||||
.conn_mode = MGCP_CONN_RECV_ONLY,
|
||||
.ptime = mgcp_ctx->conn_peer_local.ptime,
|
||||
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len
|
||||
.ptime = info->ptime,
|
||||
.codecs_len = info->codecs_len,
|
||||
.ptmap_len = info->ptmap_len,
|
||||
.param_present = info->param_present
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
|
||||
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
|
||||
osmo_strlcpy(mgcp_msg->endpoint, info->endpoint, MGCP_ENDPOINT_MAXLEN);
|
||||
memcpy(mgcp_msg->codecs, info->codecs, sizeof(mgcp_msg->codecs));
|
||||
memcpy(mgcp_msg->ptmap, info->ptmap, sizeof(mgcp_msg->ptmap));
|
||||
memcpy(&mgcp_msg->param, &info->param, sizeof(mgcp_msg->param));
|
||||
|
||||
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
if (info->x_osmo_ign) {
|
||||
mgcp_msg->x_osmo_ign = info->x_osmo_ign;
|
||||
mgcp_msg->presence |= MGCP_MSG_PRESENCE_X_OSMO_IGN;
|
||||
}
|
||||
|
||||
if (info->x_osmo_osmux_use) {
|
||||
mgcp_msg->x_osmo_osmux_cid = info->x_osmo_osmux_cid;
|
||||
mgcp_msg->presence |= MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID;
|
||||
}
|
||||
}
|
||||
|
||||
static struct msgb *make_crcx_msg_bind_connect(struct mgcp_ctx *mgcp_ctx)
|
||||
static void add_audio(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
|
||||
{
|
||||
struct mgcp_msg mgcp_msg;
|
||||
mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT;
|
||||
mgcp_msg->audio_ip = info->addr;
|
||||
mgcp_msg->audio_port = info->port;
|
||||
mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND;
|
||||
}
|
||||
|
||||
mgcp_msg = (struct mgcp_msg) {
|
||||
.verb = MGCP_VERB_CRCX,
|
||||
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
|
||||
MGCP_MSG_PRESENCE_AUDIO_PORT),
|
||||
.call_id = mgcp_ctx->conn_peer_local.call_id,
|
||||
.conn_mode = MGCP_CONN_RECV_SEND,
|
||||
.audio_ip = mgcp_ctx->conn_peer_local.addr,
|
||||
.audio_port = mgcp_ctx->conn_peer_local.port,
|
||||
.ptime = mgcp_ctx->conn_peer_local.ptime,
|
||||
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
|
||||
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
|
||||
|
||||
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
static void set_conn_mode(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *peer)
|
||||
{
|
||||
enum mgcp_connection_mode conn_mode = peer->conn_mode;
|
||||
if (conn_mode != MGCP_CONN_NONE)
|
||||
mgcp_msg->conn_mode = conn_mode;
|
||||
}
|
||||
|
||||
static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||
@@ -158,10 +163,21 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||
.audio_ip = mgcp_ctx->conn_peer_local.addr,
|
||||
.audio_port = mgcp_ctx->conn_peer_local.port,
|
||||
.ptime = mgcp_ctx->conn_peer_local.ptime,
|
||||
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len
|
||||
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len,
|
||||
.ptmap_len = mgcp_ctx->conn_peer_local.ptmap_len,
|
||||
.param_present = mgcp_ctx->conn_peer_local.param_present
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
|
||||
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
|
||||
memcpy(mgcp_msg.ptmap, mgcp_ctx->conn_peer_local.ptmap, sizeof(mgcp_msg.ptmap));
|
||||
memcpy(&mgcp_msg.param, &mgcp_ctx->conn_peer_local.param, sizeof(mgcp_ctx->conn_peer_local.param));
|
||||
|
||||
set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||
|
||||
if (mgcp_ctx->conn_peer_local.x_osmo_osmux_use) {
|
||||
mgcp_msg.x_osmo_osmux_cid = mgcp_ctx->conn_peer_local.x_osmo_osmux_cid;
|
||||
mgcp_msg.presence |= MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID;
|
||||
}
|
||||
|
||||
/* Note: We take the endpoint and the call_id from the remote
|
||||
* connection info, because we can be confident that the
|
||||
@@ -191,6 +207,7 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = data;
|
||||
struct mgcp_client *mgcp;
|
||||
struct mgcp_msg mgcp_msg;
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
@@ -203,10 +220,12 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: creating connection on MGW endpoint:%s...\n",
|
||||
mgcp_ctx->conn_peer_local.endpoint);
|
||||
|
||||
make_crcx_msg(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||
if (mgcp_ctx->conn_peer_local.port)
|
||||
msg = make_crcx_msg_bind_connect(mgcp_ctx);
|
||||
else
|
||||
msg = make_crcx_msg_bind(mgcp_ctx);
|
||||
add_audio(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||
set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||
|
||||
msg = mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
OSMO_ASSERT(msg);
|
||||
|
||||
mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
|
||||
@@ -233,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;
|
||||
@@ -262,6 +293,11 @@ static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv)
|
||||
return;
|
||||
}
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
|
||||
if (r->head.x_osmo_osmux_use) {
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded using Osmux %u\n", r->head.x_osmo_osmux_cid);
|
||||
mgcp_ctx->conn_peer_remote.x_osmo_osmux_use = true;
|
||||
mgcp_ctx->conn_peer_remote.x_osmo_osmux_cid = r->head.x_osmo_osmux_cid;
|
||||
}
|
||||
|
||||
osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr));
|
||||
mgcp_ctx->conn_peer_remote.port = r->audio_port;
|
||||
@@ -381,6 +417,12 @@ static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv)
|
||||
}
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/MDCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
|
||||
|
||||
if (r->head.x_osmo_osmux_use) {
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded using Osmux %u\n", r->head.x_osmo_osmux_cid);
|
||||
mgcp_ctx->conn_peer_remote.x_osmo_osmux_use = true;
|
||||
mgcp_ctx->conn_peer_remote.x_osmo_osmux_cid = r->head.x_osmo_osmux_cid;
|
||||
}
|
||||
|
||||
osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr));
|
||||
mgcp_ctx->conn_peer_remote.port = r->audio_port;
|
||||
|
||||
@@ -491,13 +533,17 @@ 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_ERROR,
|
||||
"MGW/DLCX: abrupt 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);
|
||||
OSMO_ASSERT(msg);
|
||||
mgcp_client_tx(mgcp, msg, NULL, NULL);
|
||||
if (!msg)
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW/DLCX: Error composing DLCX message\n");
|
||||
else
|
||||
mgcp_client_tx(mgcp, msg, NULL, NULL);
|
||||
}
|
||||
|
||||
talloc_free(mgcp_ctx);
|
||||
@@ -562,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.
|
||||
@@ -575,24 +622,19 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm
|
||||
uint32_t parent_term_evt, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx;
|
||||
static bool fsm_registered = false;
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct in_addr ip_test;
|
||||
struct in6_addr ip_test;
|
||||
|
||||
|
||||
OSMO_ASSERT(parent_fi);
|
||||
OSMO_ASSERT(mgcp);
|
||||
OSMO_ASSERT(conn_peer);
|
||||
|
||||
/* Check if IP/Port information in conn info makes sense */
|
||||
if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0)
|
||||
if (conn_peer->port && inet_pton(osmo_ip_str_type(conn_peer->addr),
|
||||
conn_peer->addr, &ip_test) != 1)
|
||||
return NULL;
|
||||
|
||||
/* Register the fsm description (if not already done) */
|
||||
if (fsm_registered == false) {
|
||||
osmo_fsm_register(&fsm_mgcp_client);
|
||||
fsm_registered = true;
|
||||
}
|
||||
|
||||
/* Allocate and configure a new fsm instance */
|
||||
fi = osmo_fsm_inst_alloc_child(&fsm_mgcp_client, parent_fi, parent_term_evt);
|
||||
OSMO_ASSERT(fi);
|
||||
@@ -620,7 +662,7 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
|
||||
{
|
||||
OSMO_ASSERT(fi);
|
||||
struct mgcp_ctx *mgcp_ctx = fi->priv;
|
||||
struct in_addr ip_test;
|
||||
struct in6_addr ip_test;
|
||||
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
OSMO_ASSERT(conn_peer);
|
||||
@@ -644,8 +686,8 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
|
||||
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, port == 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (inet_aton(conn_peer->addr, &ip_test) == 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address == 0.0.0.0\n");
|
||||
if (inet_pton(osmo_ip_str_type(conn_peer->addr), conn_peer->addr, &ip_test) != 1) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address %s\n", conn_peer->addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -677,8 +719,11 @@ void mgcp_conn_delete(struct osmo_fsm_inst *fi)
|
||||
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
|
||||
/* Unlink FSM from parent */
|
||||
osmo_fsm_inst_unlink_parent(fi, NULL);
|
||||
if (fi->proc.terminating)
|
||||
return;
|
||||
|
||||
/* 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
|
||||
@@ -692,3 +737,30 @@ void mgcp_conn_delete(struct osmo_fsm_inst *fi)
|
||||
}
|
||||
osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx);
|
||||
}
|
||||
|
||||
const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info)
|
||||
{
|
||||
/* I'd be fine with a smaller buffer and accept truncation, but gcc possibly refuses to build if
|
||||
* this buffer is too small. */
|
||||
static char buf[1024];
|
||||
|
||||
if (!info)
|
||||
return "NULL";
|
||||
|
||||
if (info->endpoint[0]
|
||||
&& info->addr[0])
|
||||
snprintf(buf, sizeof(buf), "%s:%s:%u",
|
||||
info->endpoint, info->addr, info->port);
|
||||
else if (info->endpoint[0])
|
||||
snprintf(buf, sizeof(buf), "%s", info->endpoint);
|
||||
else if (info->addr[0])
|
||||
snprintf(buf, sizeof(buf), "%s:%u", info->addr, info->port);
|
||||
else
|
||||
return "empty";
|
||||
return buf;
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init()
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&fsm_mgcp_client) == 0);
|
||||
}
|
||||
|
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,27 +24,48 @@
|
||||
#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 "Configure MGCP connection to Media Gateway\n"
|
||||
#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 A.B.C.D",
|
||||
"mgw local-ip " VTY_IPV46_CMD,
|
||||
MGW_STR "local bind to connect to MGW from\n"
|
||||
"local bind IP address\n")
|
||||
"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);
|
||||
global_mgcp_client_conf->local_addr =
|
||||
talloc_strdup(global_mgcp_client_ctx, argv[0]);
|
||||
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
|
||||
|
||||
osmo_talloc_replace_string(global_mgcp_client_ctx,
|
||||
(char **)&conf->local_addr,
|
||||
argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd,
|
||||
@@ -57,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,
|
||||
@@ -68,15 +89,15 @@ ALIAS_DEPRECATED(cfg_mgw_local_port, cfg_mgcpgw_local_port_cmd,
|
||||
"local bind port\n")
|
||||
|
||||
DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
|
||||
"mgw remote-ip A.B.C.D",
|
||||
"mgw remote-ip " VTY_IPV46_CMD,
|
||||
MGW_STR "remote IP address to reach the MGW at\n"
|
||||
"remote IP address\n")
|
||||
"remote IPv4 address\n"
|
||||
"remote IPv6 address\n")
|
||||
{
|
||||
if (!global_mgcp_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
OSMO_ASSERT(global_mgcp_client_ctx);
|
||||
global_mgcp_client_conf->remote_addr =
|
||||
talloc_strdup(global_mgcp_client_ctx, argv[0]);
|
||||
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
|
||||
|
||||
osmo_talloc_replace_string(global_mgcp_client_ctx,
|
||||
(char **)&conf->remote_addr, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
|
||||
@@ -89,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,
|
||||
@@ -99,23 +120,14 @@ ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
|
||||
MGW_STR "remote bind to connect to MGCP gateway with\n"
|
||||
"remote bind port\n")
|
||||
|
||||
DEFUN(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
|
||||
DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
|
||||
"mgw endpoint-range <1-65534> <1-65534>",
|
||||
MGW_STR "usable range of endpoint identifiers\n"
|
||||
"set first usable endpoint identifier\n"
|
||||
"set last usable endpoint identifier\n")
|
||||
MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n"
|
||||
"-\n" "-\n")
|
||||
{
|
||||
uint16_t first_endpoint = atoi(argv[0]);
|
||||
uint16_t last_endpoint = atoi(argv[1]);
|
||||
|
||||
if (last_endpoint < first_endpoint) {
|
||||
vty_out(vty, "last endpoint must be greater than first endpoint!%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
global_mgcp_client_conf->first_endpoint = first_endpoint;
|
||||
global_mgcp_client_conf->last_endpoint = last_endpoint;
|
||||
vty_out(vty, "Please do not use legacy config 'mgw endpoint-range'"
|
||||
" (the range can no longer be defined by the MGCP client)%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
|
||||
@@ -126,14 +138,15 @@ ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
|
||||
|
||||
#define BTS_START_STR "First UDP port allocated for the BTS side\n"
|
||||
#define UDP_PORT_STR "UDP Port number\n"
|
||||
DEFUN(cfg_mgw_rtp_bts_base_port,
|
||||
DEFUN_DEPRECATED(cfg_mgw_rtp_bts_base_port,
|
||||
cfg_mgw_rtp_bts_base_port_cmd,
|
||||
"mgw bts-base <0-65534>",
|
||||
MGW_STR
|
||||
BTS_START_STR
|
||||
UDP_PORT_STR)
|
||||
"DEPRECATED: there is no explicit BTS side in current osmo-mgw\n" "-\n")
|
||||
{
|
||||
global_mgcp_client_conf->bts_base = atoi(argv[0]);
|
||||
vty_out(vty, "Please do not use legacy config 'mgw bts-base'"
|
||||
" (there is no explicit BTS side in an MGW anymore)%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
|
||||
@@ -143,67 +156,402 @@ ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
|
||||
BTS_START_STR
|
||||
UDP_PORT_STR)
|
||||
|
||||
int mgcp_client_config_write(struct vty *vty, const char *indent)
|
||||
DEFUN(cfg_mgw_endpoint_domain_name,
|
||||
cfg_mgw_endpoint_domain_name_cmd,
|
||||
"mgw endpoint-domain NAME",
|
||||
MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
|
||||
"Domain name, should be alphanumeric.\n")
|
||||
{
|
||||
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(conf->endpoint_domain_name) - 1, argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
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")
|
||||
{
|
||||
int rc;
|
||||
struct reset_ep *reset_ep;
|
||||
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
uint16_t first_endpoint;
|
||||
uint16_t last_endpoint;
|
||||
uint16_t bts_base;
|
||||
struct reset_ep *reset_ep;
|
||||
|
||||
addr = global_mgcp_client_conf->local_addr;
|
||||
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 = global_mgcp_client_conf->local_port;
|
||||
port = conf->local_port;
|
||||
if (port >= 0)
|
||||
vty_out(vty, "%smgw local-port %u%s", indent,
|
||||
(uint16_t)port, VTY_NEWLINE);
|
||||
|
||||
addr = global_mgcp_client_conf->remote_addr;
|
||||
addr = conf->remote_addr;
|
||||
if (addr)
|
||||
vty_out(vty, "%smgw remote-ip %s%s", indent, addr,
|
||||
VTY_NEWLINE);
|
||||
port = global_mgcp_client_conf->remote_port;
|
||||
port = conf->remote_port;
|
||||
if (port >= 0)
|
||||
vty_out(vty, "%smgw remote-port %u%s", indent,
|
||||
(uint16_t)port, VTY_NEWLINE);
|
||||
|
||||
first_endpoint = global_mgcp_client_conf->first_endpoint;
|
||||
last_endpoint = global_mgcp_client_conf->last_endpoint;
|
||||
if (last_endpoint != 0) {
|
||||
vty_out(vty, "%smgw endpoint-range %u %u%s", indent,
|
||||
first_endpoint, last_endpoint, 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;
|
||||
|
||||
install_lib_element(node, &cfg_mgw_local_ip_cmd);
|
||||
install_lib_element(node, &cfg_mgw_local_port_cmd);
|
||||
install_lib_element(node, &cfg_mgw_remote_ip_cmd);
|
||||
install_lib_element(node, &cfg_mgw_remote_port_cmd);
|
||||
install_lib_element(node, &cfg_mgw_endpoint_range_cmd);
|
||||
install_lib_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
|
||||
install_lib_element(node, &cfg_mgw_endpoint_domain_name_cmd);
|
||||
install_lib_element(node, &cfg_mgw_reset_ep_name_cmd);
|
||||
install_lib_element(node, &cfg_mgw_no_reset_ep_name_cmd);
|
||||
|
||||
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);
|
||||
install_lib_element(node, &cfg_mgcpgw_local_port_cmd);
|
||||
install_lib_element(node, &cfg_mgcpgw_remote_ip_cmd);
|
||||
install_lib_element(node, &cfg_mgcpgw_remote_port_cmd);
|
||||
install_lib_element(node, &cfg_mgcpgw_endpoint_range_cmd);
|
||||
install_lib_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bts_base = global_mgcp_client_conf->bts_base;
|
||||
if (bts_base) {
|
||||
vty_out(vty, "%smgw bts-base %u%s", indent,
|
||||
bts_base, VTY_NEWLINE);
|
||||
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;
|
||||
}
|
||||
|
||||
void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *conf)
|
||||
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)
|
||||
{
|
||||
global_mgcp_client_ctx = talloc_ctx;
|
||||
global_mgcp_client_conf = conf;
|
||||
int nr = atoi(argv[0]);
|
||||
struct mgcp_client_pool_member *pool_member = NULL;
|
||||
|
||||
install_element(node, &cfg_mgw_local_ip_cmd);
|
||||
install_element(node, &cfg_mgw_local_port_cmd);
|
||||
install_element(node, &cfg_mgw_remote_ip_cmd);
|
||||
install_element(node, &cfg_mgw_remote_port_cmd);
|
||||
install_element(node, &cfg_mgw_endpoint_range_cmd);
|
||||
install_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
|
||||
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;
|
||||
}
|
||||
|
||||
/* deprecated 'mgcpgw' commands */
|
||||
install_element(node, &cfg_mgcpgw_local_ip_cmd);
|
||||
install_element(node, &cfg_mgcpgw_local_port_cmd);
|
||||
install_element(node, &cfg_mgcpgw_remote_ip_cmd);
|
||||
install_element(node, &cfg_mgcpgw_remote_port_cmd);
|
||||
install_element(node, &cfg_mgcpgw_endpoint_range_cmd);
|
||||
install_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd);
|
||||
|
||||
osmo_fsm_vty_add_cmds();
|
||||
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;
|
||||
}
|
||||
|
@@ -10,6 +10,8 @@ AM_CFLAGS = \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(LIBOSMOABIS_CFLAGS) \
|
||||
$(LIBOSMOTRAU_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
@@ -18,6 +20,8 @@ AM_LDFLAGS = \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
$(LIBOSMOTRAU_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
@@ -40,4 +44,8 @@ libosmo_mgcp_a_SOURCES = \
|
||||
mgcp_conn.c \
|
||||
mgcp_stat.c \
|
||||
mgcp_endp.c \
|
||||
mgcp_trunk.c \
|
||||
mgcp_ctrl.c \
|
||||
mgcp_ratectr.c \
|
||||
mgcp_e1.c \
|
||||
$(NULL)
|
||||
|
@@ -17,8 +17,13 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* Helper function to dump codec information of a specified codec to a printable
|
||||
@@ -56,8 +61,8 @@ void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
|
||||
endp = conn->conn->endp;
|
||||
|
||||
if (rtp->codecs_assigned == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x conn:%s no codecs available\n", ENDPOINT_NUMBER(endp),
|
||||
mgcp_conn_dump(conn->conn));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "conn:%s no codecs available\n",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -65,8 +70,8 @@ void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
|
||||
for (i = 0; i < rtp->codecs_assigned; i++) {
|
||||
codec = &rtp->codecs[i];
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x conn:%s codecs[%u]:%s", ENDPOINT_NUMBER(endp),
|
||||
mgcp_conn_dump(conn->conn), i, dump_codec(codec));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s codecs[%u]:%s",
|
||||
mgcp_conn_dump(conn->conn), i, dump_codec(codec));
|
||||
|
||||
if (codec == rtp->codec)
|
||||
LOGPC(DLMGCP, LOGL_DEBUG, " [selected]");
|
||||
@@ -76,36 +81,57 @@ void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
|
||||
}
|
||||
|
||||
/* Initalize or reset codec information with default data. */
|
||||
void codec_init(struct mgcp_rtp_codec *codec)
|
||||
static void codec_init(struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
if (codec->subtype_name)
|
||||
talloc_free(codec->subtype_name);
|
||||
if (codec->audio_name)
|
||||
talloc_free(codec->audio_name);
|
||||
memset(codec, 0, sizeof(*codec));
|
||||
codec->payload_type = -1;
|
||||
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
||||
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
||||
codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
|
||||
codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
|
||||
*codec = (struct mgcp_rtp_codec){
|
||||
.payload_type = -1,
|
||||
.frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM,
|
||||
.frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN,
|
||||
.rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE,
|
||||
.channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS,
|
||||
.subtype_name = "",
|
||||
.audio_name = "",
|
||||
};
|
||||
}
|
||||
|
||||
static void codec_free(struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
*codec = (struct mgcp_rtp_codec){};
|
||||
}
|
||||
|
||||
/*! Initalize or reset codec information with default data.
|
||||
* \param[out] conn related rtp-connection. */
|
||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
memset(conn->end.codecs, 0, sizeof(conn->end.codecs));
|
||||
int i;
|
||||
for (i = 0; i < conn->end.codecs_assigned; i++)
|
||||
codec_free(&conn->end.codecs[i]);
|
||||
conn->end.codecs_assigned = 0;
|
||||
conn->end.codec = NULL;
|
||||
}
|
||||
|
||||
/* Set members of struct mgcp_rtp_codec, extrapolate in missing information */
|
||||
static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
|
||||
int payload_type, const char *audio_name, unsigned int pt_offset)
|
||||
/*! Add codec configuration depending on payload type and/or codec name. This
|
||||
* function uses the input parameters to extrapolate the full codec information.
|
||||
* \param[out] codec configuration (caller provided memory).
|
||||
* \param[out] conn related rtp-connection.
|
||||
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
|
||||
* \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
|
||||
* \param[in] param optional codec parameters (set to NULL when unused).
|
||||
* \returns 0 on success, -EINVAL on failure. */
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
|
||||
{
|
||||
int rate;
|
||||
int channels;
|
||||
char audio_codec[64];
|
||||
struct mgcp_rtp_codec *codec;
|
||||
unsigned int pt_offset = conn->end.codecs_assigned;
|
||||
|
||||
/* The amount of codecs we can store is limited, make sure we do not
|
||||
* overrun this limit. */
|
||||
if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
|
||||
return -EINVAL;
|
||||
|
||||
/* First unused entry */
|
||||
codec = &conn->end.codecs[conn->end.codecs_assigned];
|
||||
|
||||
/* Initalize the codec struct with some default data to begin with */
|
||||
codec_init(codec);
|
||||
@@ -113,12 +139,13 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
|
||||
if (payload_type != PTYPE_UNDEFINED) {
|
||||
/* Make sure we do not get any reserved or undefined type numbers */
|
||||
/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
|
||||
if (payload_type == 1 || payload_type == 2 || payload_type == 19)
|
||||
goto error;
|
||||
if (payload_type >= 72 && payload_type <= 76)
|
||||
goto error;
|
||||
if (payload_type >= 127)
|
||||
if ((payload_type == 1 || payload_type == 2 || payload_type == 19)
|
||||
|| (payload_type >= 72 && payload_type <= 76)
|
||||
|| (payload_type >= 127)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot add codec, payload type number %d is reserved\n",
|
||||
payload_type);
|
||||
goto error;
|
||||
}
|
||||
|
||||
codec->payload_type = payload_type;
|
||||
}
|
||||
@@ -129,46 +156,55 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
|
||||
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
|
||||
* it is a dynamic payload type for which we do not
|
||||
* know the audio name. We must give up here */
|
||||
LOGP(DLMGCP, LOGL_ERROR, "No audio codec name given, and payload type %d unknown\n",
|
||||
payload_type);
|
||||
goto error;
|
||||
}
|
||||
} 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))
|
||||
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)
|
||||
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)
|
||||
if (channels != 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot handle audio codec with more than one channel: %s\n",
|
||||
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 {
|
||||
@@ -178,6 +214,7 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
|
||||
|
||||
/* Derive the payload type if it is unknown */
|
||||
if (codec->payload_type == PTYPE_UNDEFINED) {
|
||||
/* TODO: This is semi dead code, see OS#4150 */
|
||||
|
||||
/* For the known codecs from the static range we restore
|
||||
* the IANA or 3GPP assigned payload type number */
|
||||
@@ -213,80 +250,46 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
|
||||
* 110 onwards 3gpp defines prefered codec types, which are
|
||||
* also fixed, see above) */
|
||||
if (codec->payload_type < 0) {
|
||||
/* FIXME: pt_offset is completely unrelated and useless here, any of those numbers may already
|
||||
* have been added to the codecs. Instead, there should be an iterator checking for an actually
|
||||
* unused dynamic payload type number. */
|
||||
codec->payload_type = 96 + pt_offset;
|
||||
if (codec->payload_type > 109)
|
||||
if (codec->payload_type > 109) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Ran out of payload type numbers to assign dynamically\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy over optional codec parameters */
|
||||
if (param) {
|
||||
codec->param = *param;
|
||||
codec->param_present = true;
|
||||
} else
|
||||
codec->param_present = false;
|
||||
|
||||
conn->end.codecs_assigned++;
|
||||
return 0;
|
||||
error:
|
||||
/* Make sure we leave a clean codec entry on error. */
|
||||
codec_init(codec);
|
||||
memset(codec, 0, sizeof(*codec));
|
||||
codec_free(codec);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! Add codec configuration depending on payload type and/or codec name. This
|
||||
* function uses the input parameters to extrapolate the full codec information.
|
||||
* \param[out] codec configuration (caller provided memory).
|
||||
* \param[out] conn related rtp-connection.
|
||||
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
|
||||
* \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
|
||||
* \returns 0 on success, -EINVAL on failure. */
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* The amount of codecs we can store is limited, make sure we do not
|
||||
* overrun this limit. */
|
||||
if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
|
||||
return -EINVAL;
|
||||
|
||||
rc = codec_set(conn->conn, &conn->end.codecs[conn->end.codecs_assigned], payload_type, audio_name,
|
||||
conn->end.codecs_assigned);
|
||||
if (rc != 0)
|
||||
return -EINVAL;
|
||||
|
||||
conn->end.codecs_assigned++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if the given codec is applicable on the specified endpoint
|
||||
* Helper function for mgcp_codec_decide() */
|
||||
static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
char codec_name[64];
|
||||
|
||||
/* 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;
|
||||
|
||||
/* We now extract the codec_name (letters before the /, e.g. "GSM"
|
||||
* from the audio name that is stored in the trunk configuration.
|
||||
* We do not compare to the full audio_name because we expect that
|
||||
* "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the
|
||||
* audio name of the codec is set to "GSM" */
|
||||
if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1)
|
||||
return false;
|
||||
/* FIXME: implement meaningful checks to make sure that the given codec
|
||||
* is compatible with the given endpoint */
|
||||
|
||||
/* Finally we check if the subtype_name we have generated from the
|
||||
* audio_name in the trunc struct patches the codec_name of the
|
||||
* given codec */
|
||||
if (strcasecmp(codec_name, codec->subtype_name) == 0)
|
||||
return true;
|
||||
|
||||
/* FIXME: It is questinable that the method to pick a compatible
|
||||
* codec can work properly. Since this useses tcfg->audio_name, as
|
||||
* a reference, which is set to "AMR/8000" permanently.
|
||||
* tcfg->audio_name must be updated by the first connection that
|
||||
* has been made on an endpoint, so that the second connection
|
||||
* can make a meaningful decision here */
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*! Decide for one suitable codec
|
||||
@@ -313,7 +316,7 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
|
||||
for (i = 0; i < rtp->codecs_assigned; i++) {
|
||||
/* When no transcoding is available, avoid codecs that would
|
||||
* require transcoding. */
|
||||
if (endp->tcfg->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
|
||||
if (endp->trunk->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n",
|
||||
rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name);
|
||||
continue;
|
||||
@@ -341,3 +344,116 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Return true if octet-aligned is set in the given codec. Default to octet-aligned=0, i.e. bandwidth-efficient mode.
|
||||
* See RFC4867 "RTP Payload Format for AMR and AMR-WB" sections "8.1. AMR Media Type Registration" and "8.2. AMR-WB
|
||||
* Media Type Registration":
|
||||
*
|
||||
* octet-align: Permissible values are 0 and 1. If 1, octet-aligned
|
||||
* operation SHALL be used. If 0 or if not present,
|
||||
* bandwidth-efficient operation is employed.
|
||||
*
|
||||
* https://tools.ietf.org/html/rfc4867
|
||||
*/
|
||||
static bool amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
if (!codec->param_present)
|
||||
return false;
|
||||
if (!codec->param.amr_octet_aligned_present)
|
||||
return false;
|
||||
return codec->param.amr_octet_aligned;
|
||||
}
|
||||
|
||||
/* Compare two codecs, all parameters must match up, except for the payload type
|
||||
* number. */
|
||||
static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
|
||||
{
|
||||
if (codec_a->rate != codec_b->rate)
|
||||
return false;
|
||||
if (codec_a->channels != codec_b->channels)
|
||||
return false;
|
||||
if (codec_a->frame_duration_num != codec_b->frame_duration_num)
|
||||
return false;
|
||||
if (codec_a->frame_duration_den != codec_b->frame_duration_den)
|
||||
return false;
|
||||
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
|
||||
return false;
|
||||
if (!strcmp(codec_a->subtype_name, "AMR")) {
|
||||
if (amr_is_octet_aligned(codec_a) != amr_is_octet_aligned(codec_b))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*! Translate a given payload type number that belongs to the packet of a
|
||||
* source connection to the equivalent payload type number that matches the
|
||||
* configuration of a destination connection.
|
||||
* \param[in] conn_src related source rtp-connection.
|
||||
* \param[in] conn_dst related destination rtp-connection.
|
||||
* \param[in] payload_type number from the source packet or source connection.
|
||||
* \returns translated payload type number on success, -EINVAL on failure. */
|
||||
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type)
|
||||
{
|
||||
struct mgcp_rtp_end *rtp_src;
|
||||
struct mgcp_rtp_end *rtp_dst;
|
||||
struct mgcp_rtp_codec *codec_src = NULL;
|
||||
struct mgcp_rtp_codec *codec_dst = NULL;
|
||||
unsigned int i;
|
||||
unsigned int codecs_assigned;
|
||||
|
||||
rtp_src = &conn_src->end;
|
||||
rtp_dst = &conn_dst->end;
|
||||
|
||||
/* Find the codec information that is used on the source side */
|
||||
codecs_assigned = rtp_src->codecs_assigned;
|
||||
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
|
||||
for (i = 0; i < codecs_assigned; i++) {
|
||||
if (payload_type == rtp_src->codecs[i].payload_type) {
|
||||
codec_src = &rtp_src->codecs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!codec_src)
|
||||
return -EINVAL;
|
||||
|
||||
/* Use the codec infrmation from the source and try to find the
|
||||
* equivalent of it on the destination side */
|
||||
codecs_assigned = rtp_dst->codecs_assigned;
|
||||
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
|
||||
for (i = 0; i < codecs_assigned; i++) {
|
||||
if (codecs_same(codec_src, &rtp_dst->codecs[i])) {
|
||||
codec_dst = &rtp_dst->codecs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!codec_dst)
|
||||
return -EINVAL;
|
||||
|
||||
return codec_dst->payload_type;
|
||||
}
|
||||
|
||||
/* Find the payload type number configured for a specific codec by SDP.
|
||||
* For example, IuUP gets assigned a payload type number, and the endpoint needs to translate that to the number
|
||||
* assigned to "AMR" on the other conn (by a=rtpmap:N).
|
||||
* \param conn The side of an endpoint to get the payload type number for (to translate the payload type number to).
|
||||
* \param subtype_name SDP codec name without parameters (e.g. "AMR").
|
||||
* \param match_nr Index for the match found, first being match_nr == 0. Iterate all matches by calling multiple times
|
||||
* with incrementing match_nr.
|
||||
* \return codec definition for that conn matching the subtype_name, or NULL if no such match_nr is found.
|
||||
*/
|
||||
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
|
||||
const char *subtype_name, unsigned int match_nr)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < conn->end.codecs_assigned; i++) {
|
||||
if (!strcmp(conn->end.codecs[i].subtype_name, subtype_name)) {
|
||||
if (match_nr) {
|
||||
match_nr--;
|
||||
continue;
|
||||
}
|
||||
return &conn->end.codecs[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@@ -21,32 +21,26 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_network.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <ctype.h>
|
||||
|
||||
static const struct rate_ctr_desc rate_ctr_desc[] = {
|
||||
[IN_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:in", "Inbound rtp-stream timestamp errors."},
|
||||
[OUT_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:out", "Outbound rtp-stream timestamp errors."},
|
||||
[RTP_PACKETS_RX_CTR] = {"rtp:packets_rx", "Inbound rtp packets."},
|
||||
[RTP_OCTETS_RX_CTR] = {"rtp:octets_rx", "Inbound rtp octets."},
|
||||
[RTP_PACKETS_TX_CTR] = {"rtp:packets_tx", "Outbound rtp packets."},
|
||||
[RTP_OCTETS_TX_CTR] = {"rtp:octets_rx", "Outbound rtp octets."},
|
||||
[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "dropped rtp packets."}
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc rate_ctr_group_desc = {
|
||||
.group_name_prefix = "conn_rtp",
|
||||
.group_description = "rtp connection statistics",
|
||||
.class_id = 1,
|
||||
.num_ctr = ARRAY_SIZE(rate_ctr_desc),
|
||||
.ctr_desc = rate_ctr_desc
|
||||
.num_ctr = ARRAY_SIZE(mgcp_conn_rate_ctr_desc),
|
||||
.ctr_desc = mgcp_conn_rate_ctr_desc
|
||||
};
|
||||
|
||||
|
||||
@@ -55,10 +49,11 @@ const static struct rate_ctr_group_desc rate_ctr_group_desc = {
|
||||
* memory for id) */
|
||||
static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
|
||||
{
|
||||
#define MGCP_CONN_ID_GEN_LEN 8
|
||||
int i;
|
||||
int k;
|
||||
int rc;
|
||||
uint8_t id_bin[16];
|
||||
uint8_t id_bin[MGCP_CONN_ID_GEN_LEN / 2];
|
||||
char *id_hex;
|
||||
|
||||
/* Generate a connection id that is unique for the current endpoint.
|
||||
@@ -78,28 +73,28 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
|
||||
/* ensure that the generated conn_id is unique
|
||||
* for this endpoint */
|
||||
if (!mgcp_conn_get_rtp(endp, id_hex)) {
|
||||
osmo_strlcpy(id, id_hex, MGCP_CONN_ID_LENGTH);
|
||||
osmo_strlcpy(id, id_hex, MGCP_CONN_ID_MAXLEN);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x, unable to generate a unique connectionIdentifier\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "unable to generate a unique connectionIdentifier\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialize rtp connection struct with default values */
|
||||
static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
|
||||
static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
|
||||
{
|
||||
struct mgcp_rtp_end *end = &conn_rtp->end;
|
||||
/* 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.allocated_cid = -1;
|
||||
conn_rtp->osmux.cid_allocated = false;
|
||||
conn_rtp->osmux.cid = 0;
|
||||
|
||||
/* backpointer to the generic part of the connection */
|
||||
conn->u.rtp.conn = conn;
|
||||
@@ -116,22 +111,44 @@ static void 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->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->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 = 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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Cleanup rtp connection struct */
|
||||
static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
osmux_disable_conn(conn_rtp);
|
||||
osmux_release_cid(conn_rtp);
|
||||
if (mgcp_conn_rtp_is_osmux(conn_rtp))
|
||||
conn_osmux_disable(conn_rtp);
|
||||
mgcp_free_rtp_port(&conn_rtp->end);
|
||||
rate_ctr_group_free(conn_rtp->rate_ctr_group);
|
||||
mgcp_codec_reset_all(conn_rtp);
|
||||
}
|
||||
|
||||
void mgcp_conn_watchdog_cb(void *data)
|
||||
{
|
||||
struct mgcp_conn *conn = data;
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR, "connection timed out!\n");
|
||||
mgcp_conn_free(conn->endp, conn->id);
|
||||
}
|
||||
|
||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn)
|
||||
{
|
||||
int timeout = conn->endp->trunk->cfg->conn_timeout;
|
||||
if (!timeout)
|
||||
return;
|
||||
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "watchdog kicked\n");
|
||||
osmo_timer_schedule(&conn->watchdog, timeout, 0);
|
||||
}
|
||||
|
||||
/*! allocate a new connection list entry.
|
||||
@@ -167,7 +184,10 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
|
||||
switch (type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
mgcp_rtp_conn_init(&conn->u.rtp, conn);
|
||||
if (mgcp_rtp_conn_init(&conn->u.rtp, conn) < 0) {
|
||||
talloc_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* NOTE: This should never be called with an
|
||||
@@ -176,7 +196,10 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
llist_add(&conn->entry, &endp->conns);
|
||||
/* Initialize watchdog */
|
||||
osmo_timer_setup(&conn->watchdog, mgcp_conn_watchdog_cb, conn);
|
||||
mgcp_conn_watchdog_kick(conn);
|
||||
mgcp_endp_add_conn(endp, conn);
|
||||
|
||||
return conn;
|
||||
}
|
||||
@@ -188,9 +211,26 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
const char *id_upper;
|
||||
const char *conn_id;
|
||||
|
||||
if (!id || !*id)
|
||||
return NULL;
|
||||
|
||||
/* Ignore leading zeros in needle */
|
||||
while (*id == '0')
|
||||
id++;
|
||||
|
||||
/* Use uppercase to compare identifiers, to avoid mismatches: RFC3435 2.1.3.2 "Names of
|
||||
* Connections" defines the id as a hex string, so clients may return lower case hex even though
|
||||
* we sent upper case hex in the CRCX response. */
|
||||
id_upper = osmo_str_toupper(id);
|
||||
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
if (strncmp(conn->id, id, sizeof(conn->id)) == 0)
|
||||
/* Ignore leading zeros in haystack */
|
||||
for (conn_id=conn->id; *conn_id == '0'; conn_id++);
|
||||
|
||||
if (strcmp(conn_id, id_upper) == 0)
|
||||
return conn;
|
||||
}
|
||||
|
||||
@@ -216,6 +256,28 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats;
|
||||
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
|
||||
|
||||
if (all_stats == NULL || conn_stats == NULL)
|
||||
return;
|
||||
|
||||
/* Compared to per-connection RTP statistics, aggregated RTP statistics
|
||||
* contain one additional rate couter item (RTP_NUM_CONNECTIONS).
|
||||
* All other counters in both counter groups correspond to each other. */
|
||||
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(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(rate_ctr_group_get_ctr(all_stats, RTP_NUM_CONNECTIONS));
|
||||
}
|
||||
|
||||
/*! free a connection by its ID.
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection */
|
||||
@@ -227,14 +289,9 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
/* Run endpoint cleanup action. By this we inform the endpoint about
|
||||
* the removal of the connection and allow it to clean up its inner
|
||||
* state accordingly */
|
||||
if (endp->type->cleanup_cb)
|
||||
endp->type->cleanup_cb(endp, conn);
|
||||
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
aggregate_rtp_conn_stats(endp, &conn->u.rtp);
|
||||
mgcp_rtp_conn_cleanup(&conn->u.rtp);
|
||||
break;
|
||||
default:
|
||||
@@ -244,7 +301,9 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
llist_del(&conn->entry);
|
||||
osmo_timer_del(&conn->watchdog);
|
||||
mgcp_endp_remove_conn(endp, conn);
|
||||
/* WARN: endp may have be freed after call to mgcp_endp_remove_conn */
|
||||
talloc_free(conn);
|
||||
}
|
||||
|
||||
@@ -269,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;
|
||||
}
|
||||
@@ -285,6 +342,7 @@ void mgcp_conn_free_all(struct mgcp_endpoint *endp)
|
||||
char *mgcp_conn_dump(struct mgcp_conn *conn)
|
||||
{
|
||||
static char str[sizeof(conn->name)+sizeof(conn->id)+256];
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
|
||||
if (!conn) {
|
||||
snprintf(str, sizeof(str), "(null connection)");
|
||||
@@ -298,7 +356,7 @@ char *mgcp_conn_dump(struct mgcp_conn *conn)
|
||||
"rtp:%u rtcp:%u)",
|
||||
conn->name,
|
||||
conn->id,
|
||||
inet_ntoa(conn->u.rtp.end.addr),
|
||||
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
|
||||
ntohs(conn->u.rtp.end.rtp_port),
|
||||
ntohs(conn->u.rtp.end.rtcp_port));
|
||||
break;
|
||||
@@ -334,3 +392,13 @@ struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! get oldest connection in the list.
|
||||
* \param[in] endp associated endpoint */
|
||||
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp)
|
||||
{
|
||||
if (llist_empty(&endp->conns))
|
||||
return NULL;
|
||||
|
||||
return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
|
||||
}
|
||||
|
36
src/libosmo-mgcp/mgcp_ctrl.c
Normal file
36
src/libosmo-mgcp/mgcp_ctrl.c
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
|
||||
|
||||
static int mgw_ctrl_node_lookup(void *data, vector vline, int *node_type,
|
||||
void **node_data, int *i)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg,
|
||||
const char *bind_addr, uint16_t port)
|
||||
{
|
||||
return ctrl_interface_setup_dynip2(cfg, bind_addr, port, mgw_ctrl_node_lookup,
|
||||
_LAST_CTRL_NODE);
|
||||
}
|
689
src/libosmo-mgcp/mgcp_e1.c
Normal file
689
src/libosmo-mgcp/mgcp_e1.c
Normal file
@@ -0,0 +1,689 @@
|
||||
/* E1 traffic handling */
|
||||
|
||||
/*
|
||||
* (C) 2020 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 Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/abis/abis.h>
|
||||
|
||||
#include <osmocom/trau/trau_sync.h>
|
||||
#include <osmocom/trau/trau_frame.h>
|
||||
#include <osmocom/trau/trau_rtp.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/netif/rtp.h>
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
#include <osmocom/mgcp/mgcp_e1.h>
|
||||
#include <osmocom/codec/codec.h>
|
||||
|
||||
#define DEBUG_BITS_MAX 80
|
||||
#define DEBUG_BYTES_MAX 40
|
||||
#define DEBUG_E1_TS 0
|
||||
#define E1_TS_BYTES 160
|
||||
#define E1_TRAU_BITS 320
|
||||
#define E1_TRAU_BITS_MSGB 2048
|
||||
|
||||
static struct mgcp_config *cfg;
|
||||
|
||||
static const struct e1inp_line_ops dummy_e1_line_ops = {
|
||||
.sign_link_up = NULL,
|
||||
.sign_link_down = NULL,
|
||||
.sign_link = NULL,
|
||||
};
|
||||
|
||||
/* EFR idle frame */
|
||||
static const ubit_t idle_tf_efr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
};
|
||||
|
||||
/* FR idle frame */
|
||||
static const ubit_t idle_tf_fr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
};
|
||||
|
||||
/* Idle speech frame, see also GSM 08.60, chapter 3.4 */
|
||||
static const ubit_t idle_tf_spch[] = { 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
};
|
||||
|
||||
/* If the RTP transmission has dropouts for some reason the I.460 TX-Queue may
|
||||
* run empty. In order to make sure that the TRAU frame transmission continues
|
||||
* we generate idle TRAU frames here. */
|
||||
static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_data)
|
||||
{
|
||||
struct mgcp_endpoint *endp = user_data;
|
||||
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
|
||||
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_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;
|
||||
switch (ft) {
|
||||
case OSMO_TRAU16_FT_FR:
|
||||
ptr_ft = idle_tf_fr;
|
||||
break;
|
||||
case OSMO_TRAU16_FT_EFR:
|
||||
ptr_ft = idle_tf_efr;
|
||||
break;
|
||||
default:
|
||||
/* FIXME: What about 8k subslots and AMR frames? */
|
||||
ptr_ft = idle_tf_spch;
|
||||
}
|
||||
|
||||
/* Put the replacement into a message buffer and enqueue it into the
|
||||
* I.460 multiplexer */
|
||||
ptr = msgb_put(msg, E1_TRAU_BITS);
|
||||
memcpy(ptr, ptr_ft, E1_TRAU_BITS);
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-IDLE-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg),
|
||||
osmo_ubit_dump(msgb_data(msg), msgb_length(msg) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg)));
|
||||
osmo_i460_mux_enqueue(endp->e1.schan, msg);
|
||||
}
|
||||
|
||||
/* called by I.460 de-multeiplexer; feed output of I.460 demux into TRAU frame sync */
|
||||
static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data, const ubit_t *bits,
|
||||
unsigned int num_bits)
|
||||
{
|
||||
struct mgcp_endpoint *endp = user_data;
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u bits from subslot: %s...\n", num_bits,
|
||||
osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
|
||||
|
||||
OSMO_ASSERT(endp->e1.trau_sync_fi);
|
||||
osmo_trau_sync_rx_ubits(endp->e1.trau_sync_fi, bits, num_bits);
|
||||
}
|
||||
|
||||
/* called for each synchronized TRAU frame received; decode frame + convert to RTP
|
||||
* (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)
|
||||
{
|
||||
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;
|
||||
int rc;
|
||||
|
||||
if (!bits || num_bits == 0)
|
||||
goto skip;
|
||||
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u TRAU frame bits from E1 subslot: %s...\n",
|
||||
num_bits, osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
|
||||
|
||||
/* Decode TRAU frame */
|
||||
switch (endp->e1.scd.rate) {
|
||||
case OSMO_I460_RATE_8k:
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 8k trau frame...\n");
|
||||
rc = osmo_trau_frame_decode_8k(&fr, bits, OSMO_TRAU_DIR_UL);
|
||||
break;
|
||||
case OSMO_I460_RATE_16k:
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 16k trau frame...\n");
|
||||
rc = osmo_trau_frame_decode_16k(&fr, bits, OSMO_TRAU_DIR_UL);
|
||||
break;
|
||||
default:
|
||||
/* TRAU frames only exist in 8K or 16K subslots. */
|
||||
OSMO_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
if (rc != 0) {
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to decode trau frame\n");
|
||||
goto skip;
|
||||
}
|
||||
|
||||
/* Check if the payload type is supported and what the expected lenth
|
||||
* of the RTP payload will be. */
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoded trau frame type: %s\n",
|
||||
osmo_trau_frame_type_name(fr.type));
|
||||
|
||||
/* Convert decoded trau frame to RTP frame */
|
||||
struct osmo_trau2rtp_state t2rs = {
|
||||
.type = fr.type,
|
||||
};
|
||||
rc = osmo_trau2rtp(msgb_data(msg) + rtp_hdr_len, msg->data_len - rtp_hdr_len, &fr, &t2rs);
|
||||
if (rc <= 0) {
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to convert trau frame to RTP audio\n");
|
||||
goto skip;
|
||||
}
|
||||
msgb_put(msg, rtp_hdr_len + rc);
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: encoded %u bytes of RTP audio: %s\n", rc,
|
||||
osmo_hexdump(msgb_data(msg) + rtp_hdr_len, msgb_length(msg) - rtp_hdr_len));
|
||||
|
||||
/* Forward RTP data to IP */
|
||||
conn_dst = llist_first_entry(&endp->conns, struct mgcp_conn, entry);
|
||||
if (!conn_dst) {
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG,
|
||||
"E1-I.460-RX: unable to forward RTP audio data from E1: no connection to forward an incoming RTP packet to\n");
|
||||
goto skip;
|
||||
}
|
||||
OSMO_ASSERT(conn_dst->type == MGCP_CONN_TYPE_RTP);
|
||||
|
||||
mgcp_send(endp, 1, NULL, msg, &conn_dst->u.rtp, &conn_dst->u.rtp);
|
||||
|
||||
msgb_free(msg);
|
||||
return;
|
||||
skip:
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_RX_FAIL_CTR));
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Function to handle outgoing E1 traffic */
|
||||
static void e1_send(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc_c(trunk, E1_TS_BYTES, "E1-TX-timeslot-bytes");
|
||||
uint8_t *ptr;
|
||||
|
||||
/* Get E1 frame from I.460 multiplexer */
|
||||
ptr = msgb_put(msg, E1_TS_BYTES);
|
||||
osmo_i460_mux_out(&trunk->e1.i460_ts[ts->num - 1], ptr, E1_TS_BYTES);
|
||||
|
||||
#if DEBUG_E1_TS == 1
|
||||
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-TX: (ts:%u) sending %u bytes: %s...\n", ts->num, msgb_length(msg),
|
||||
osmo_hexdump_nospc(msgb_data(msg),
|
||||
msgb_length(msg) > DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
|
||||
#endif
|
||||
/* Hand data over to the E1 stack */
|
||||
msgb_enqueue(&ts->raw.tx_queue, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Callback function to handle incoming E1 traffic */
|
||||
static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
|
||||
{
|
||||
struct mgcp_trunk *trunk;
|
||||
|
||||
/* Find associated trunk */
|
||||
trunk = mgcp_trunk_by_line_num(cfg, ts->line->num);
|
||||
if (!trunk) {
|
||||
LOGP(DE1, LOGL_ERROR, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num);
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if the incoming data looks sane */
|
||||
if (msgb_length(msg) != E1_TS_BYTES) {
|
||||
LOGPTRUNK(trunk, DE1, LOGL_NOTICE,
|
||||
"E1-RX: (ts:%u) expected length is %u, actual length is %u!\n", ts->num, E1_TS_BYTES,
|
||||
msgb_length(msg));
|
||||
}
|
||||
#if DEBUG_E1_TS == 1
|
||||
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-RX: (ts:%u) receiving %u bytes: %s...\n", ts->num,
|
||||
msgb_length(msg), osmo_hexdump_nospc(msgb_data(msg),
|
||||
msgb_length(msg) >
|
||||
DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
|
||||
#endif
|
||||
|
||||
/* Hand data over to the I.460 demultiplexer. */
|
||||
osmo_i460_demux_in(&trunk->e1.i460_ts[ts->num - 1], msgb_data(msg), msgb_length(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)
|
||||
{
|
||||
/*! Each timeslot needs only to be configured once. The Timeslot then
|
||||
* stays open and permanently receives data. It is then up to the
|
||||
* I.460 demultiplexer to add/remove subchannels as needed. It is
|
||||
* allowed to call this function multiple times since we check if the
|
||||
* timeslot is already configured. */
|
||||
|
||||
struct e1inp_line *e1_line;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
|
||||
cfg = trunk->cfg;
|
||||
|
||||
if (trunk->e1.ts_in_use[ts_nr - 1]) {
|
||||
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_ERROR, "no such E1 line %u - check VTY config!\n",
|
||||
trunk->e1.vty_line_nr);
|
||||
return -EINVAL;
|
||||
}
|
||||
e1inp_line_bind_ops(e1_line, &dummy_e1_line_ops);
|
||||
|
||||
/* Configure E1 timeslot */
|
||||
rc = e1inp_ts_config_raw(&e1_line->ts[ts_nr - 1], e1_line, e1_recv_cb);
|
||||
if (rc < 0) {
|
||||
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to put E1 timeslot %u in raw mode.\n", ts_nr);
|
||||
return -EINVAL;
|
||||
}
|
||||
rc = e1inp_line_update(e1_line);
|
||||
if (rc < 0) {
|
||||
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 timeslot %u.\n", ts_nr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* Determine a suitable TRAU frame type for a given codec */
|
||||
static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
|
||||
uint8_t amr_ft, struct mgcp_endpoint *endp)
|
||||
{
|
||||
if (strcmp(sdp_subtype_name, "GSM") == 0)
|
||||
return OSMO_TRAU16_FT_FR;
|
||||
else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
|
||||
return OSMO_TRAU16_FT_EFR;
|
||||
else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
|
||||
return OSMO_TRAU16_FT_HR;
|
||||
else if (strcmp(sdp_subtype_name, "AMR") == 0) {
|
||||
if (i460_rate == OSMO_I460_RATE_8k) {
|
||||
switch (amr_ft) {
|
||||
case AMR_4_75:
|
||||
case AMR_5_15:
|
||||
case AMR_5_90:
|
||||
return OSMO_TRAU8_AMR_LOW;
|
||||
case AMR_6_70:
|
||||
return OSMO_TRAU8_AMR_6k7;
|
||||
case AMR_7_40:
|
||||
return OSMO_TRAU8_AMR_7k4;
|
||||
default:
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR,
|
||||
"E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
|
||||
return OSMO_TRAU_FT_NONE;
|
||||
}
|
||||
}
|
||||
return OSMO_TRAU16_FT_AMR;
|
||||
} else {
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
|
||||
sdp_subtype_name);
|
||||
return OSMO_TRAU_FT_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine a suitable TRAU frame type for a given codec */
|
||||
static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
|
||||
uint8_t amr_ft, struct mgcp_endpoint *endp)
|
||||
{
|
||||
if (strcmp(sdp_subtype_name, "GSM") == 0)
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
|
||||
return OSMO_TRAU_SYNCP_8_HR;
|
||||
else if (strcmp(sdp_subtype_name, "AMR") == 0) {
|
||||
if (i460_rate == OSMO_I460_RATE_8k) {
|
||||
switch (amr_ft) {
|
||||
case AMR_4_75:
|
||||
case AMR_5_15:
|
||||
case AMR_5_90:
|
||||
return OSMO_TRAU_SYNCP_8_AMR_LOW;
|
||||
case AMR_6_70:
|
||||
return OSMO_TRAU_SYNCP_8_AMR_6K7;
|
||||
case AMR_7_40:
|
||||
return OSMO_TRAU_SYNCP_8_AMR_7K4;
|
||||
default:
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR,
|
||||
"E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
}
|
||||
}
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
} else {
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
|
||||
sdp_subtype_name);
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find out if a given TRAU frame type is AMR */
|
||||
static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
|
||||
{
|
||||
|
||||
switch (ft) {
|
||||
case OSMO_TRAU16_FT_AMR:
|
||||
case OSMO_TRAU8_AMR_LOW:
|
||||
case OSMO_TRAU8_AMR_6k7:
|
||||
case OSMO_TRAU8_AMR_7k4:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* !Equip E1 endpoint with I.460 mux resources.
|
||||
* \param[in] endp endpoint to equip
|
||||
* \param[in] ts E1 timeslot number.
|
||||
* \param[in] ss E1 subslot number.
|
||||
* \param[in] offset E1 bit offset.
|
||||
* \returns 0 on success, -EINVAL on error. */
|
||||
int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs)
|
||||
{
|
||||
int rc;
|
||||
enum osmo_tray_sync_pat_id sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
|
||||
OSMO_ASSERT(ts != 0);
|
||||
OSMO_ASSERT(ts != 0xFF);
|
||||
OSMO_ASSERT(ss != 0xFF);
|
||||
OSMO_ASSERT(offs != 0xFF);
|
||||
|
||||
memset(&endp->e1, 0, sizeof(endp->e1));
|
||||
|
||||
endp->e1.last_amr_ft = AMR_4_75;
|
||||
|
||||
/* Set up E1 line / timeslot */
|
||||
rc = e1_init(endp->trunk, ts);
|
||||
if (rc != 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Set up I.460 mux */
|
||||
switch (e1_rates[ss]) {
|
||||
case 64:
|
||||
endp->e1.scd.rate = OSMO_I460_RATE_64k;
|
||||
endp->e1.scd.demux.num_bits = 160 * 8;
|
||||
break;
|
||||
case 32:
|
||||
endp->e1.scd.rate = OSMO_I460_RATE_32k;
|
||||
endp->e1.scd.demux.num_bits = 80 * 8;
|
||||
break;
|
||||
case 16:
|
||||
endp->e1.scd.rate = OSMO_I460_RATE_16k;
|
||||
endp->e1.scd.demux.num_bits = 40 * 8;
|
||||
sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
break;
|
||||
case 8:
|
||||
endp->e1.scd.rate = OSMO_I460_RATE_8k;
|
||||
endp->e1.scd.demux.num_bits = 20 * 8;
|
||||
sync_pat_id = OSMO_TRAU_SYNCP_8_HR;
|
||||
break;
|
||||
}
|
||||
endp->e1.scd.bit_offset = offs;
|
||||
endp->e1.scd.demux.out_cb_bits = e1_i460_demux_bits_cb;
|
||||
endp->e1.scd.demux.out_cb_bytes = NULL;
|
||||
endp->e1.scd.demux.user_data = endp;
|
||||
endp->e1.scd.mux.in_cb_queue_empty = e1_i460_mux_empty_cb;
|
||||
endp->e1.scd.mux.user_data = endp;
|
||||
|
||||
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) {
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 subchannel: failed!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (endp->e1.scd.rate == OSMO_I460_RATE_16k || endp->e1.scd.rate == OSMO_I460_RATE_8k) {
|
||||
/* TRAU frames are only specified for 16k and 8k subslots. For all other subslot
|
||||
* types the concept of TRAU frames does not apply. However, at the moment this
|
||||
* is the only format we currently support in osmo-mgw */
|
||||
endp->e1.trau_sync_fi = osmo_trau_sync_alloc(endp, "trau-sync", sync_frame_out_cb, sync_pat_id, endp);
|
||||
if (!endp->e1.trau_sync_fi) {
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 TRAU frame sync: failed!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
endp->e1.trau_rtp_st = talloc_zero(endp->e1.trau_sync_fi, struct osmo_trau2rtp_state);
|
||||
endp->e1.trau_rtp_st->type = OSMO_TRAU_FT_NONE;
|
||||
} else {
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR,
|
||||
"osmo-mgw currently only supports 16K and 8K subslots (TRAU frames)!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Update E1 related parameters (codec and sync pattern).
|
||||
* \param[in] endp endpoint to update. */
|
||||
void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
struct mgcp_rtp_codec *codec;
|
||||
enum osmo_tray_sync_pat_id sync_pat_id;
|
||||
|
||||
/* In order to determine the codec, find the oldest connection on
|
||||
* the endpoint and use its codec information. Normally on an E1
|
||||
* endpoint no more than one connection should exist. */
|
||||
conn = mgcp_conn_get_oldest(endp);
|
||||
OSMO_ASSERT(conn);
|
||||
codec = conn->u.rtp.end.codec;
|
||||
OSMO_ASSERT(codec);
|
||||
|
||||
/* Update codec information */
|
||||
endp->e1.trau_rtp_st->type =
|
||||
determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
|
||||
endp->e1.last_codec = codec;
|
||||
|
||||
/* Update sync pattern */
|
||||
sync_pat_id = determine_trau_sync_pat(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
|
||||
osmo_trau_sync_set_pat(endp->e1.trau_sync_fi, sync_pat_id);
|
||||
}
|
||||
|
||||
/*! Remove E1 resources from endpoint
|
||||
* \param[in] endp endpoint to release. */
|
||||
void mgcp_e1_endp_release(struct mgcp_endpoint *endp)
|
||||
{
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n");
|
||||
|
||||
if (endp->e1.schan)
|
||||
osmo_i460_subchan_del(endp->e1.schan);
|
||||
if (endp->e1.trau_rtp_st)
|
||||
talloc_free(endp->e1.trau_rtp_st);
|
||||
if (endp->e1.trau_sync_fi)
|
||||
osmo_fsm_inst_term(endp->e1.trau_sync_fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
|
||||
memset(&endp->e1, 0, sizeof(endp->e1));
|
||||
}
|
||||
|
||||
/*! Accept RTP message buffer with RTP data and enqueue voice data for E1 transmit.
|
||||
* \param[in] endp related endpoint (does not take ownership).
|
||||
* \param[in] codec configuration.
|
||||
* \param[in] msg RTP message buffer (including RTP header).
|
||||
* \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_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;
|
||||
uint8_t amr_ft;
|
||||
int rc;
|
||||
|
||||
/* Extract AMR frame type from AMR head (if AMR is used) */
|
||||
if (tf_type_is_amr(endp->e1.trau_rtp_st->type))
|
||||
amr_ft = (msgb_data(msg)[rtp_hdr_len + 1] >> 3) & 0xf;
|
||||
else
|
||||
amr_ft = 0xff;
|
||||
|
||||
/* Adapt TRAU frame type on codec changes */
|
||||
OSMO_ASSERT(endp->e1.last_codec);
|
||||
if (codec != endp->e1.last_codec || (amr_ft != 0xff && amr_ft != endp->e1.last_amr_ft)) {
|
||||
endp->e1.trau_rtp_st->type =
|
||||
determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, amr_ft, endp);
|
||||
endp->e1.last_codec = codec;
|
||||
endp->e1.last_amr_ft = amr_ft;
|
||||
}
|
||||
if (endp->e1.trau_rtp_st->type == OSMO_TRAU_FT_NONE)
|
||||
goto skip;
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: using trau frame type for encoding: %s\n",
|
||||
osmo_trau_frame_type_name(endp->e1.trau_rtp_st->type));
|
||||
|
||||
/* Convert from RTP to TRAU format */
|
||||
msg->l2h = msgb_data(msg) + rtp_hdr_len;
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: decoding %u bytes of RTP audio to TRAU format: %s\n",
|
||||
msgb_length(msg), osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
|
||||
memset(&tf, 0, sizeof(tf));
|
||||
tf.dir = OSMO_TRAU_DIR_DL;
|
||||
rc = osmo_rtp2trau(&tf, msgb_l2(msg), msgb_l2len(msg), endp->e1.trau_rtp_st);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG,
|
||||
"E1-I.460-TX: failed to decode from RTP payload format to TRAU format\n");
|
||||
goto skip;
|
||||
}
|
||||
rc = osmo_trau_frame_encode(msgb_data(msg_tf), msg_tf->data_len, &tf);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: failed to encode TRAU frame\n");
|
||||
goto skip;
|
||||
}
|
||||
msgb_put(msg_tf, rc);
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg_tf),
|
||||
osmo_ubit_dump(msgb_data(msg_tf),
|
||||
msgb_length(msg_tf) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg_tf)));
|
||||
|
||||
/* Enqueue data to I.460 multiplexer */
|
||||
OSMO_ASSERT(endp->e1.schan);
|
||||
OSMO_ASSERT(endp->e1.trau_sync_fi);
|
||||
|
||||
osmo_i460_mux_enqueue(endp->e1.schan, msg_tf);
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: %u bits of audio enqued for E1 tx\n", msgb_length(msg_tf));
|
||||
|
||||
return 0;
|
||||
skip:
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_TX_FAIL_CTR));
|
||||
msgb_free(msg_tf);
|
||||
return -1;
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
/* Endpoint types */
|
||||
|
||||
/*
|
||||
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
@@ -21,23 +21,100 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
|
||||
#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
|
||||
|
||||
/* Endpoint typeset definition */
|
||||
const struct mgcp_endpoint_typeset ep_typeset = {
|
||||
/* Specify endpoint properties for RTP endpoint */
|
||||
.rtp.max_conns = 2,
|
||||
.rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
|
||||
.rtp.cleanup_cb = mgcp_cleanup_rtp_bridge_cb
|
||||
.rtp = {
|
||||
.max_conns = 2,
|
||||
.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
|
||||
.cleanup_cb = mgcp_cleanup_rtp_bridge_cb,
|
||||
},
|
||||
/* Specify endpoint properties for E1 endpoint */
|
||||
.e1 = {
|
||||
.max_conns = 1,
|
||||
.dispatch_rtp_cb = mgcp_dispatch_e1_bridge_cb,
|
||||
.cleanup_cb = mgcp_cleanup_e1_bridge_cb,
|
||||
},
|
||||
};
|
||||
|
||||
/* Generate virtual endpoint name from given parameters */
|
||||
static char *gen_virtual_epname(void *ctx, const char *domain,
|
||||
unsigned int index)
|
||||
{
|
||||
return talloc_asprintf(ctx, "%s%x@%s",
|
||||
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, index, domain);
|
||||
}
|
||||
|
||||
/* Generate E1 endpoint name from given numeric parameters */
|
||||
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;
|
||||
unsigned int offset;
|
||||
|
||||
OSMO_ASSERT(ss_nr < sizeof(e1_rates));
|
||||
|
||||
rate = e1_rates[ss_nr];
|
||||
offset = e1_offsets[ss_nr];
|
||||
|
||||
return talloc_asprintf(ctx, "%s%u/s-%u/su%u-%u@%s",
|
||||
MGCP_ENDPOINT_PREFIX_E1_TRUNK, trunk_nr, ts_nr,
|
||||
rate, offset, domain);
|
||||
}
|
||||
|
||||
/*! allocate an endpoint and set default values.
|
||||
* \param[in] trunk configuration.
|
||||
* \param[in] name endpoint index.
|
||||
* \returns endpoint on success, NULL on failure. */
|
||||
struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk,
|
||||
unsigned int index)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
endp = talloc_zero(trunk->endpoints, struct mgcp_endpoint);
|
||||
if (!endp)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&endp->conns);
|
||||
endp->trunk = trunk;
|
||||
|
||||
switch (trunk->trunk_type) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
endp->type = &ep_typeset.rtp;
|
||||
endp->name = gen_virtual_epname(endp, trunk->cfg->domain, index);
|
||||
break;
|
||||
case MGCP_TRUNK_E1:
|
||||
endp->type = &ep_typeset.e1;
|
||||
endp->name = gen_e1_epname(endp, trunk->cfg->domain,
|
||||
trunk->trunk_nr,
|
||||
index / MGCP_ENDP_E1_SUBSLOTS, index % MGCP_ENDP_E1_SUBSLOTS);
|
||||
break;
|
||||
default:
|
||||
osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n",
|
||||
trunk->trunk_type, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
return endp;
|
||||
}
|
||||
|
||||
/*! release endpoint, all open connections are closed.
|
||||
* \param[in] endp endpoint to release */
|
||||
void mgcp_endp_release(struct mgcp_endpoint *endp)
|
||||
{
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint:0x%x\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
|
||||
|
||||
/* Normally this function should only be called when
|
||||
* all connections have been removed already. In case
|
||||
@@ -45,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;
|
||||
@@ -52,5 +135,543 @@ 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);
|
||||
}
|
||||
|
||||
/* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or
|
||||
* "ds/e1-") and write the epname without the prefix back to the memory
|
||||
* pointed at by epname. (per trunk the prefix is the same for all endpoints,
|
||||
* so no ambiguity is introduced) */
|
||||
static void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk)
|
||||
{
|
||||
size_t prefix_len;
|
||||
switch (trunk->trunk_type) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
|
||||
if (strncmp
|
||||
(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
|
||||
prefix_len) == 0)
|
||||
memmove(epname, epname + prefix_len,
|
||||
strlen(epname) - prefix_len + 1);
|
||||
return;
|
||||
case MGCP_TRUNK_E1:
|
||||
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
|
||||
if (strncmp
|
||||
(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
|
||||
prefix_len) == 0)
|
||||
memmove(epname, epname + prefix_len,
|
||||
strlen(epname) - prefix_len + 1);
|
||||
return;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the endpoint name contains a suffix (e.g. "@mgw") and truncate
|
||||
* epname by writing a '\0' char where the suffix starts. */
|
||||
static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
|
||||
{
|
||||
char *suffix_begin;
|
||||
|
||||
/* Endpoints on the virtual trunk may have a domain name that is
|
||||
* followed after an @ character, this can be chopped off. All
|
||||
* other supported trunk types do not have any suffixes that may
|
||||
* be chopped off */
|
||||
if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
|
||||
suffix_begin = strchr(epname, '@');
|
||||
if (!suffix_begin)
|
||||
return;
|
||||
*suffix_begin = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! 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)
|
||||
* \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);
|
||||
chop_epname_prefix(epname_stripped, trunk);
|
||||
chop_epname_suffix(epname_stripped, trunk);
|
||||
}
|
||||
|
||||
/* Go through the trunk and find a random free (no active calls) endpoint,
|
||||
* this function is called when a wildcarded request is carried out, which
|
||||
* means that it is up to the MGW to choose a random free endpoint. */
|
||||
static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < trunk->number_endpoints; i++) {
|
||||
endp = trunk->endpoints[i];
|
||||
/* A free endpoint must not serve a call already and it must
|
||||
* be available. */
|
||||
if (endp->callid == NULL && mgcp_endp_avail(endp))
|
||||
return endp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! 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];
|
||||
char epname_stripped_endp[MGCP_ENDPOINT_MAXLEN];
|
||||
struct mgcp_endpoint *endp;
|
||||
unsigned int i;
|
||||
|
||||
/* Strip irrelevant information from the endpoint name */
|
||||
mgcp_endp_strip_name(epname_stripped, epname, trunk);
|
||||
|
||||
for (i = 0; i < trunk->number_endpoints; i++) {
|
||||
endp = trunk->endpoints[i];
|
||||
mgcp_endp_strip_name(epname_stripped_endp, endp->name, trunk);
|
||||
if (strcmp(epname_stripped_endp, epname_stripped) == 0)
|
||||
return endp;
|
||||
}
|
||||
|
||||
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.
|
||||
* \param[in] trunk where the endpoint is located.
|
||||
* \returns endpoint or NULL if endpoint was not found. */
|
||||
struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
|
||||
const struct mgcp_trunk *trunk)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
if (cause)
|
||||
*cause = 0;
|
||||
|
||||
/* At the moment we only support a primitive ('*'-only) method of
|
||||
* wildcarded endpoint searches that picks the next free endpoint on
|
||||
* a trunk. */
|
||||
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);
|
||||
return endp;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"(trunk:%d) Not able to find a free endpoint\n",
|
||||
trunk->trunk_nr);
|
||||
if (cause)
|
||||
*cause = -403;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find an endpoint by its name (if wildcarded request is not
|
||||
* applicable) */
|
||||
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);
|
||||
return endp;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"(trunk:%d) Not able to find specified endpoint: %s\n",
|
||||
trunk->trunk_nr, epname);
|
||||
if (cause)
|
||||
*cause = -500;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! 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.
|
||||
* \param[in] cfg, mgcp configuration (trunks).
|
||||
* \returns endpoint or NULL if endpoint was not found. */
|
||||
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
|
||||
struct mgcp_config *cfg)
|
||||
{
|
||||
struct mgcp_trunk *trunk;
|
||||
struct mgcp_endpoint *endp;
|
||||
char epname_lc[MGCP_ENDPOINT_MAXLEN];
|
||||
|
||||
osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
|
||||
epname = epname_lc;
|
||||
|
||||
if (cause)
|
||||
*cause = -500;
|
||||
|
||||
/* Identify the trunk where the endpoint is located */
|
||||
trunk = mgcp_trunk_by_name(cfg, epname);
|
||||
if (!trunk)
|
||||
return NULL;
|
||||
|
||||
/* Identify the endpoint on the trunk */
|
||||
endp = mgcp_endp_by_name_trunk(cause, epname, trunk);
|
||||
if (!endp) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cause)
|
||||
*cause = 0;
|
||||
return endp;
|
||||
}
|
||||
|
||||
/* Get the E1 timeslot number from a given E1 endpoint name
|
||||
* (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
|
||||
static uint8_t e1_ts_nr_from_epname(const char *epname)
|
||||
{
|
||||
char buf[MGCP_ENDPOINT_MAXLEN + 1];
|
||||
char *save_ptr = NULL;
|
||||
char *buf_ptr = buf;
|
||||
char *token;
|
||||
unsigned long int res = 0;
|
||||
|
||||
strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
|
||||
|
||||
while (1) {
|
||||
token = strtok_r(buf_ptr, "/", &save_ptr);
|
||||
buf_ptr = NULL;
|
||||
if (!token)
|
||||
break;
|
||||
if (strncmp(token, "s-", 2) == 0) {
|
||||
errno = 0;
|
||||
res = strtoul(token + 2, NULL, 10);
|
||||
if (errno == ERANGE || res > NUM_E1_TS)
|
||||
return 0xff;
|
||||
return (uint8_t) res;
|
||||
}
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
/* Get the E1 timeslot number from a given E1 endpoint name
|
||||
* (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
|
||||
static uint8_t e1_rate_from_epname(const char *epname)
|
||||
{
|
||||
char buf[MGCP_ENDPOINT_MAXLEN + 1];
|
||||
char *save_ptr = NULL;
|
||||
char *buf_ptr = buf;
|
||||
char *token;
|
||||
unsigned long int res = 0;
|
||||
unsigned int i;
|
||||
|
||||
strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
|
||||
|
||||
while (1) {
|
||||
token = strtok_r(buf_ptr, "/", &save_ptr);
|
||||
buf_ptr = NULL;
|
||||
if (!token)
|
||||
break;
|
||||
if (strncmp(token, "su", 2) == 0) {
|
||||
errno = 0;
|
||||
res = strtoul(token + 2, NULL, 10);
|
||||
if (errno == ERANGE || res > E1_RATE_MAX)
|
||||
return 0xff;
|
||||
/* Make sure the rate is a valid rate */
|
||||
for (i = 0; i < sizeof(e1_rates); i++) {
|
||||
if (res == e1_rates[i])
|
||||
return (uint8_t) res;
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
/* Get the E1 bitstream offset from a given E1 endpoint name
|
||||
* (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
|
||||
static uint8_t e1_offs_from_epname(const char *epname)
|
||||
{
|
||||
char buf[MGCP_ENDPOINT_MAXLEN + 1];
|
||||
char *save_ptr = NULL;
|
||||
char *buf_ptr = buf;
|
||||
char *token;
|
||||
unsigned long int res = 0;
|
||||
|
||||
strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
|
||||
|
||||
while (1) {
|
||||
token = strtok_r(buf_ptr, "/", &save_ptr);
|
||||
buf_ptr = NULL;
|
||||
if (!token)
|
||||
break;
|
||||
if (strncmp(token, "su", 2) == 0) {
|
||||
token = strstr(token, "-");
|
||||
if (!token)
|
||||
return 0xff;
|
||||
token += 1;
|
||||
errno = 0;
|
||||
res = strtoul(token, NULL, 10);
|
||||
if (errno == ERANGE || res > E1_OFFS_MAX)
|
||||
return 0xff;
|
||||
return (uint8_t) res;
|
||||
}
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
/* Get the E1 subslot number (internal) from a given E1 endpoint name
|
||||
* (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
|
||||
static uint8_t e1_ss_nr_from_epname(const char *epname)
|
||||
{
|
||||
uint8_t rate;
|
||||
uint8_t offs;
|
||||
unsigned int i;
|
||||
|
||||
rate = e1_rate_from_epname(epname);
|
||||
offs = e1_offs_from_epname(epname);
|
||||
|
||||
osmo_static_assert(sizeof(e1_rates) == sizeof(e1_offsets), e1_rates_e1_offsets_size);
|
||||
|
||||
for (i = 0; i < sizeof(e1_rates); i++) {
|
||||
if ((e1_rates[i] == rate) && (e1_offsets[i] == offs))
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
/* Check if the selected E1 endpoint is avalable, which means that none of
|
||||
* the overlapping endpoints are currently serving a call. (if the system
|
||||
* is properly configured such a situation should never ocurr!) */
|
||||
static bool endp_avail_e1(struct mgcp_endpoint *endp)
|
||||
{
|
||||
/* The following map shows the overlapping of the subslots and their
|
||||
* respective rates. The numbers on the right running from top to bottom
|
||||
* are the bit offsets in the whole 64k timeslot. The numbers inside the
|
||||
* boxes symbolize the internal subslot number (array index) and the
|
||||
* rate in the form: i:r where i is the subslot number and r the
|
||||
* respective rate.
|
||||
*
|
||||
* +--------+--------+--------+--------+ 0
|
||||
* | | | | 7:8k |
|
||||
* | | + 3:16k +--------+ 1
|
||||
* | | | | 8:8k |
|
||||
* | | 1:32k +--------+--------+ 2
|
||||
* | | | | 9:8k |
|
||||
* | | + 4:16k +--------+ 3
|
||||
* | | | | 10:8k |
|
||||
* | 0:64k +--------+--------+--------+ 4
|
||||
* | | | | 11:8k |
|
||||
* | | + 5:16k +--------+ 5
|
||||
* | | | | 12:8k |
|
||||
* | | 2:32k +--------+--------+ 6
|
||||
* | | | | 13:8k |
|
||||
* | | + 6:16k +--------+ 7
|
||||
* | | | | 14:8k |
|
||||
* +--------+--------+--------+--------+ 8
|
||||
*
|
||||
* The following array contains tables with the subslot numbers that must be
|
||||
* unused for each subslot. During this test we do not have to check the
|
||||
* endpoint we need to verify, only the overlaps need to be checked. This is
|
||||
* also the reason why the related subslot number is missing from each each
|
||||
* line. */
|
||||
const int8_t interlock_tab[MGCP_ENDP_E1_SUBSLOTS][15] = {
|
||||
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1 },
|
||||
{ 0, 3, 4, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 5, 6, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 1, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 1, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 2, 11, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 2, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } };
|
||||
|
||||
const int8_t *interlock;
|
||||
unsigned int i;
|
||||
uint8_t ts_nr = 0;
|
||||
uint8_t ss_nr = 0;
|
||||
char *epname_check;
|
||||
struct mgcp_endpoint *endp_check;
|
||||
bool available = true;
|
||||
|
||||
/* This function must only be used with E1 type endpoints! */
|
||||
OSMO_ASSERT(endp->trunk->trunk_type == MGCP_TRUNK_E1);
|
||||
|
||||
ts_nr = e1_ts_nr_from_epname(endp->name);
|
||||
ss_nr = e1_ss_nr_from_epname(endp->name);
|
||||
if (ts_nr == 0xff || ss_nr == 0xff) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"cannot check endpoint availability, endpoint name not parseable!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
interlock = interlock_tab[ss_nr];
|
||||
|
||||
for (i = 0; i < sizeof(interlock_tab[0]); i++) {
|
||||
/* Detect row end */
|
||||
if (interlock[i] == -1)
|
||||
break;
|
||||
|
||||
/* Pick overlapping endpoint to check */
|
||||
epname_check = gen_e1_epname(endp, endp->trunk->cfg->domain,
|
||||
endp->trunk->trunk_nr, ts_nr,
|
||||
interlock[i]);
|
||||
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",
|
||||
epname_check);
|
||||
talloc_free(epname_check);
|
||||
continue;
|
||||
}
|
||||
talloc_free(epname_check);
|
||||
|
||||
/* Check if overlapping endpoint currently serves another call
|
||||
* (This is an exceptional situation, that should not occur
|
||||
* in a properly configured environment!) */
|
||||
if (endp_check->callid) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"endpoint unavailable - overlapping endpoint:%s already serves a call!\n",
|
||||
endp_check->name);
|
||||
available = false;
|
||||
}
|
||||
}
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
/*! check if an endpoint is available for any kind of operation.
|
||||
* \param[in] endp endpoint to check.
|
||||
* \returns true if endpoint is avalable, false it is blocked for any reason. */
|
||||
bool mgcp_endp_avail(struct mgcp_endpoint *endp)
|
||||
{
|
||||
switch (endp->trunk->trunk_type) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
/* There are no obstacles that may render a virtual trunk
|
||||
* endpoint unusable, so virtual trunk endpoints are always
|
||||
* available */
|
||||
return true;
|
||||
case MGCP_TRUNK_E1:
|
||||
return endp_avail_e1(endp);
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*! claim endpoint, sets callid and activates endpoint, should be called at the
|
||||
* beginning of the CRCX procedure when it is clear that a new call should be
|
||||
* created.
|
||||
* \param[in] endp endpoint to claim.
|
||||
* \param[in] callid that is assingned to this endpoint. */
|
||||
int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid)
|
||||
{
|
||||
int rc = 0;
|
||||
uint8_t ts;
|
||||
uint8_t ss;
|
||||
uint8_t offs;
|
||||
|
||||
/* TODO: Make this function more intelligent, it should run the
|
||||
* call id checks we currently have in protocol.c directly here. */
|
||||
|
||||
/* Set the callid, creation of another connection will only be possible
|
||||
* when the callid matches up. (Connections are distinguished by their
|
||||
* 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) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
/* No additional initaliziation required here, virtual
|
||||
* endpoints will open/close network sockets themselves
|
||||
* on demand. */
|
||||
break;
|
||||
case MGCP_TRUNK_E1:
|
||||
ts = e1_ts_nr_from_epname(endp->name);
|
||||
ss = e1_ss_nr_from_epname(endp->name);
|
||||
offs = e1_offs_from_epname(endp->name);
|
||||
OSMO_ASSERT(ts != 0xFF);
|
||||
OSMO_ASSERT(ts != 0);
|
||||
OSMO_ASSERT(ss != 0xFF);
|
||||
OSMO_ASSERT(offs != 0xFF);
|
||||
rc = mgcp_e1_endp_equip(endp, ts, ss, offs);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
/* Make sure the endpoint is released when claiming the endpoint fails. */
|
||||
if (rc < 0)
|
||||
mgcp_endp_release(endp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! update endpoint, updates internal endpoint specific data, should be
|
||||
* after when MDCX or CRCX has been executed successuflly.
|
||||
* \param[in] endp endpoint to update. */
|
||||
void mgcp_endp_update(struct mgcp_endpoint *endp)
|
||||
{
|
||||
/* Allocate resources */
|
||||
switch (endp->trunk->trunk_type) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
/* No updating initaliziation required for virtual endpoints. */
|
||||
break;
|
||||
case MGCP_TRUNK_E1:
|
||||
mgcp_e1_endp_update(endp);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
|
||||
{
|
||||
llist_add(&conn->entry, &endp->conns);
|
||||
}
|
||||
|
||||
void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
|
||||
{
|
||||
/* Run endpoint cleanup action. By this we inform the endpoint about
|
||||
* the removal of the connection and allow it to clean up its inner
|
||||
* state accordingly */
|
||||
if (endp->type->cleanup_cb)
|
||||
endp->type->cleanup_cb(endp, conn);
|
||||
llist_del(&conn->entry);
|
||||
if (llist_empty(&endp->conns))
|
||||
mgcp_endp_release(endp);
|
||||
}
|
||||
|
@@ -24,11 +24,14 @@
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#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
|
||||
@@ -82,9 +85,8 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
|
||||
int ret = 0;
|
||||
|
||||
if (!mode) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x missing connection mode\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"missing connection mode\n");
|
||||
return -1;
|
||||
}
|
||||
if (!conn)
|
||||
@@ -92,39 +94,35 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
|
||||
if (!endp)
|
||||
return -1;
|
||||
|
||||
if (strcmp(mode, "recvonly") == 0)
|
||||
if (strcasecmp(mode, "recvonly") == 0)
|
||||
conn->mode = MGCP_CONN_RECV_ONLY;
|
||||
else if (strcmp(mode, "sendrecv") == 0)
|
||||
else if (strcasecmp(mode, "sendrecv") == 0)
|
||||
conn->mode = MGCP_CONN_RECV_SEND;
|
||||
else if (strcmp(mode, "sendonly") == 0)
|
||||
else if (strcasecmp(mode, "sendonly") == 0)
|
||||
conn->mode = MGCP_CONN_SEND_ONLY;
|
||||
else if (strcmp(mode, "loopback") == 0)
|
||||
else if (strcasecmp(mode, "loopback") == 0)
|
||||
conn->mode = MGCP_CONN_LOOPBACK;
|
||||
else {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x unknown connection mode: '%s'\n",
|
||||
ENDPOINT_NUMBER(endp), mode);
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"unknown connection mode: '%s'\n", mode);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
/* Special handling für RTP connections */
|
||||
/* Special handling for RTP connections */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
conn->u.rtp.end.output_enabled =
|
||||
conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint:0x%x conn:%s\n",
|
||||
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint:0x%x connection mode '%s' %d\n",
|
||||
ENDPOINT_NUMBER(endp), mode, conn->mode);
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
|
||||
mode, conn->mode);
|
||||
|
||||
/* Special handling für RTP connections */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x output_enabled %d\n",
|
||||
ENDPOINT_NUMBER(endp), conn->u.rtp.end.output_enabled);
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %d\n",
|
||||
conn->u.rtp.end.output_enabled);
|
||||
}
|
||||
|
||||
/* The VTY might change the connection mode at any time, so we have
|
||||
@@ -134,180 +132,20 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We have a null terminated string with the endpoint name here. We only
|
||||
* support two kinds. Simple ones as seen on the BSC level and the ones
|
||||
* seen on the trunk side. (helper function for find_endpoint()) */
|
||||
static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
|
||||
const char *mgcp)
|
||||
{
|
||||
char *rest = NULL;
|
||||
struct mgcp_trunk_config *tcfg;
|
||||
int trunk, endp;
|
||||
struct mgcp_endpoint *endp_ptr;
|
||||
|
||||
trunk = strtoul(mgcp + 6, &rest, 10);
|
||||
if (rest == NULL || rest[0] != '/' || trunk < 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
endp = strtoul(rest + 1, &rest, 10);
|
||||
if (rest == NULL || rest[0] != '@') {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* signalling is on timeslot 1 */
|
||||
if (endp == 1)
|
||||
return NULL;
|
||||
|
||||
tcfg = mgcp_trunk_num(cfg, trunk);
|
||||
if (!tcfg) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "The trunk %d is not declared.\n",
|
||||
trunk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!tcfg->endpoints) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Endpoints of trunk %d not allocated.\n", trunk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (endp < 1 || endp >= tcfg->number_endpoints) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n",
|
||||
mgcp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
endp_ptr = &tcfg->endpoints[endp];
|
||||
endp_ptr->wildcarded_req = false;
|
||||
return endp_ptr;
|
||||
}
|
||||
|
||||
/* Find an endpoint that is not in use. Do this by going through the endpoint
|
||||
* array, check the callid. A callid nullpointer indicates that the endpoint
|
||||
* is free */
|
||||
static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints,
|
||||
unsigned int number_endpoints)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < number_endpoints; i++) {
|
||||
if (endpoints[i].callid == NULL) {
|
||||
endp = &endpoints[i];
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint:0x%x found free endpoint\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
endp->wildcarded_req = true;
|
||||
return endp;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if the domain name, which is supplied with the endpoint name
|
||||
* matches the configuration. */
|
||||
static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
|
||||
{
|
||||
char *domain_to_check;
|
||||
|
||||
domain_to_check = strstr(mgcp, "@");
|
||||
if (!domain_to_check)
|
||||
return -EINVAL;
|
||||
|
||||
if (strcmp(domain_to_check+1, cfg->domain) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Search the endpoint pool for the endpoint that had been selected via the
|
||||
* MGCP message (helper function for mgcp_analyze_header()) */
|
||||
static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
|
||||
const char *mgcp,
|
||||
int *cause)
|
||||
{
|
||||
char *endptr = NULL;
|
||||
unsigned int gw = INT_MAX;
|
||||
const char *endpoint_number_str;
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
*cause = 0;
|
||||
|
||||
/* Check if the domainname in the request is correct */
|
||||
if (check_domain_name(cfg, mgcp)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s'\n", mgcp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if the E1 trunk is requested */
|
||||
if (strncmp(mgcp, "ds/e1", 5) == 0) {
|
||||
endp = find_e1_endpoint(cfg, mgcp);
|
||||
if (!endp)
|
||||
*cause = -500;
|
||||
return endp;
|
||||
}
|
||||
|
||||
/* Check if the virtual trunk is addressed (new, correct way with prefix) */
|
||||
if (strncmp
|
||||
(mgcp, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
|
||||
strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK)) == 0) {
|
||||
endpoint_number_str =
|
||||
mgcp + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
|
||||
if (endpoint_number_str[0] == '*') {
|
||||
endp = find_free_endpoint(cfg->trunk.endpoints,
|
||||
cfg->trunk.number_endpoints);
|
||||
if (!endp)
|
||||
*cause = -403;
|
||||
return endp;
|
||||
}
|
||||
gw = strtoul(endpoint_number_str, &endptr, 16);
|
||||
if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
|
||||
endp = &cfg->trunk.endpoints[gw];
|
||||
endp->wildcarded_req = false;
|
||||
return endp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Deprecated method without prefix */
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Addressing virtual trunk without prefix (deprecated), please use %s: '%s'\n",
|
||||
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, mgcp);
|
||||
gw = strtoul(mgcp, &endptr, 16);
|
||||
if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
|
||||
endp = &cfg->trunk.endpoints[gw];
|
||||
endp->wildcarded_req = false;
|
||||
return endp;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
|
||||
*cause = -500;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! 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)) {
|
||||
@@ -316,26 +154,18 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
pdata->trans = elem;
|
||||
break;
|
||||
case 1:
|
||||
pdata->endp = find_endpoint(pdata->cfg, elem, &cause);
|
||||
if (!pdata->endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Unable to find Endpoint `%s'\n", elem);
|
||||
return cause;
|
||||
}
|
||||
pdata->epname = elem;
|
||||
break;
|
||||
case 2:
|
||||
if (strcmp("MGCP", elem)) {
|
||||
if (strcasecmp("MGCP", elem)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MGCP header parsing error\n");
|
||||
return -510;
|
||||
}
|
||||
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++;
|
||||
@@ -343,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;
|
||||
}
|
||||
|
||||
@@ -353,18 +181,27 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
|
||||
/*! Extract OSMUX CID from an MGCP parameter line (string).
|
||||
* \param[in] line single parameter line from the MGCP message
|
||||
* \returns OSMUX CID, -1 on error */
|
||||
* \returns OSMUX CID, -1 wildcard, -2 on error */
|
||||
int mgcp_parse_osmux_cid(const char *line)
|
||||
{
|
||||
int osmux_cid;
|
||||
|
||||
if (sscanf(line + 2, "Osmux: %u", &osmux_cid) != 1)
|
||||
|
||||
if (strcasecmp(line + 2, "Osmux: *") == 0) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Parsed wilcard Osmux CID\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sscanf(line + 2 + 7, "%u", &osmux_cid) != 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed parsing Osmux in MGCP msg line: %s\n",
|
||||
line);
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (osmux_cid > OSMUX_CID_MAX) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
|
||||
osmux_cid, OSMUX_CID_MAX);
|
||||
return -1;
|
||||
return -2;
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
|
||||
|
||||
@@ -372,28 +209,30 @@ 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 0x%x\n",
|
||||
line, ENDPOINT_NUMBER(endp));
|
||||
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.
|
||||
* \param[in] endp pointer to endpoint
|
||||
* \param{in] callid to verify
|
||||
* \returns 1 when callid seems plausible, 0 on error */
|
||||
* \returns 0 when callid seems plausible, -1 on error */
|
||||
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
|
||||
{
|
||||
/*! This function compares the supplied callid with the called that is
|
||||
@@ -401,15 +240,20 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
|
||||
|
||||
if (!endp)
|
||||
return -1;
|
||||
|
||||
/* Accept any CallID for "X-Osmo-IGN: C" */
|
||||
if (endp->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID)
|
||||
return 0;
|
||||
|
||||
if (!callid)
|
||||
return -1;
|
||||
if (!endp->callid)
|
||||
return -1;
|
||||
|
||||
if (strcmp(endp->callid, callid) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x CallIDs does not match '%s' != '%s'\n",
|
||||
ENDPOINT_NUMBER(endp), endp->callid, callid);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CallIDs mismatch: '%s' != '%s'\n",
|
||||
endp->callid, callid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -419,42 +263,45 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
|
||||
/*! Check if the specified connection id seems plausible.
|
||||
* \param[in] endp pointer to endpoint
|
||||
* \param{in] connection id to verify
|
||||
* \returns 1 when connection id seems plausible, 0 on error */
|
||||
* \returns 0 when connection id is valid and exists, an RFC3435 error code on error.
|
||||
*/
|
||||
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
|
||||
{
|
||||
/* For invalid conn_ids, return 510 "The transaction could not be executed, because some
|
||||
* unspecified protocol error was detected." */
|
||||
|
||||
/* Check for null identifiers */
|
||||
if (!conn_id) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x invalid ConnectionIdentifier (missing)\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return -1;
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"invalid ConnectionIdentifier (missing)\n");
|
||||
return 510;
|
||||
}
|
||||
|
||||
/* Check for empty connection identifiers */
|
||||
if (strlen(conn_id) == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x invalid ConnectionIdentifier (empty)\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return -1;
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"invalid ConnectionIdentifier (empty)\n");
|
||||
return 510;
|
||||
}
|
||||
|
||||
/* Check for over long connection identifiers */
|
||||
if (strlen(conn_id) > MGCP_CONN_ID_LENGTH) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x invalid ConnectionIdentifier (too long) 0x%s\n",
|
||||
ENDPOINT_NUMBER(endp), conn_id);
|
||||
return -1;
|
||||
if (strlen(conn_id) > (MGCP_CONN_ID_MAXLEN-1)) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"invalid ConnectionIdentifier (too long: %zu > %d) 0x%s\n",
|
||||
strlen(conn_id), MGCP_CONN_ID_MAXLEN-1, conn_id);
|
||||
return 510;
|
||||
}
|
||||
|
||||
/* Check if connection exists */
|
||||
if (mgcp_conn_get(endp, conn_id))
|
||||
return 0;
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x no connection found under ConnectionIdentifier 0x%s\n",
|
||||
ENDPOINT_NUMBER(endp), conn_id);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"no connection found under ConnectionIdentifier 0x%s\n", conn_id);
|
||||
|
||||
return -1;
|
||||
/* When the conn_id was not found, return error code 515 "The transaction refers to an incorrect
|
||||
* connection-id (may have been already deleted)." */
|
||||
return 515;
|
||||
}
|
||||
|
||||
/*! Extract individual lines from MCGP message.
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user