Compare commits

...

176 Commits
1.4.0 ... 1.6.0

Author SHA1 Message Date
Pau Espin Pedrol
50e52e45f9 Bump version: 1.5.0.84-a2d10-dirty → 1.6.0
Change-Id: I57277c34bbab1fc9ea2be6a754d5a79786ce627d
2019-08-07 16:52:59 +02:00
Pau Espin Pedrol
a2d10216ea configure.ac: Require libosmo-netif 0.6.0
Current code uses APIs like osmux_xfrm_output_init2() and osmo_amr_*(),
that are only available in libosmo-netif 0.6.0 onwards. Let's update
configure.ac accordingly.

Change-Id: I3bc0196bb6b5c5e5cf96935422fd56c4582ed7f5
2019-08-07 16:42:28 +02:00
Pau Espin Pedrol
f54eb96338 Remove undefined param passed to {logging,osmo_stats}_vty_add_cmds
Since March 15th 2017, libosmocore API logging_vty_add_cmds() had its
parameter removed (c65c5b4ea075ef6cef11fff9442ae0b15c1d6af7). However,
definition in C file doesn't contain "(void)", which means number of
parameters is undefined and thus compiler doesn't complain. Let's remove
parameters from all callers before enforcing "(void)" on it.
API osmo_stats_vty_add_cmds never had a param list but has seem problem
(no "void"), so some users decided to pass a parameter to it.

Change-Id: Icf4d18969488c9eacca7a597d4071828e649e772
Related: OS#4138
2019-08-05 16:05:14 +02:00
Pau Espin Pedrol
a2b1c5e6f6 Fix return variable of strtoul()
Return variable specified by strtoul() is "unsigned long int". If
"unsigned int" is used, according to Coverity the return value can never
be ULONG_MAX:

CID 202173:  Integer handling issues  (CONSTANT_EXPRESSION_RESULT)
"pt == 18446744073709551615UL /* 9223372036854775807L * 2UL + 1UL */" is always false regardless of the values of its operands. This occurs as the logical second operand of "&&".

Furthermore, PT is 7 bit in RTP header [1], so let's avoid accepting
incorrect values.

[1] https://tools.ietf.org/html/rfc3550#section-5

Fixes: c5c1430a1c ("Catch unsigned integer MGCP parsing errors with strtoul")
Fixes: Coverity CID#202172
FIxes: Coverity CID#202173
Change-Id: Ice9eee6a252fab73dbab5ebf3cfc83c1b354fd08
2019-07-29 18:33:38 +02:00
Pau Espin Pedrol
c5c1430a1c Catch unsigned integer MGCP parsing errors with strtoul
Checks to find if strotul failed are taken both from:
man strtoul
man strtol

Change-Id: Ifba1c1e3151d6f92f9da3d4ca2569a5908455ca8
2019-07-25 12:14:48 +00:00
Pau Espin Pedrol
f7de9aea7b doc: Add Osmux documentation to OsmoMGW User Manual
Depends: osmo-gsm-manuals.git I182d94c63f7d74ef882b77be59a95b1b7d8a4e5e
Change-Id: Ie53f98777070fc00ed085646f698d20f8cf49553
2019-07-24 19:32:56 +00:00
Hoernchen
199c5e965e turn -Werror=null-dereference into a warning
There is unfortunately no way to suppres this witha pragma,
and gcc 9 uncovers quite a few new instaces with enabled LTO that can't/won't be fixed

Related: OS#4123
Change-Id: Ib157bd2e110b271dbe5ac928c98251e016477f56
2019-07-22 19:56:59 +00:00
Harald Welte
06a49fc04b mgcp_sdp: Don't check if an unsigned int is below 0
Change-Id: I129f5c6175a8e961bc08b9768bdf22a2232e2fcb
Closes: CID#188849
2019-07-21 09:28:28 +02:00
Oliver Smith
6349bd422a contrib/jenkins.sh: run "make maintainer-clean"
Related: OS#3047
Change-Id: I60c713a64ef629f0cb88121632ea6adc017fd0ae
2019-07-10 13:29:36 +02:00
Pau Espin Pedrol
796a4a1325 doc: X-Osmo-IGN: small formatting and typo fixes
Change-Id: I658901a63504f3733793c34947d59b034daa93f6
2019-07-07 14:34:17 +00:00
Oliver Smith
2cf6652ba2 "make dist" fix for: no rule to make mgcp_common.h
Mark osmocom/mgcp_client/mgcp_common.h as nodist, so "make dist" will
not try to include it in the source tarball. This caused "make dist" to
fail in a clean osmo-mgw source tree with:
make[2]: *** No rule to make target 'osmocom/mgcp_client/mgcp_common.h', needed by 'distdir'.  Stop.

The file gets copied during make from osmocom/mgcp/mgcp_common.h (see
include/osmocom/mgcp_client/Makefile.am). Therefore it is not included
in the source tree and we don't need to distribute it.

Related: OS#4084
Change-Id: Ia1d7b051c0924a785b0f7ec0195192e3a852ed70
2019-07-07 08:15:28 +00:00
Pau Espin Pedrol
9b508f6b53 mgw: Allow receiving uppercase noanswer keyword
MGCP RFC3435 (https://tools.ietf.org/html/rfc3435) states almost all
text has to be handled in a case-insensitive way, except SDP parts.

Related: OS#4001
Change-Id: I637cb20f0af4de33ebf6589b1aff260d57d03e7b
2019-07-03 12:29:43 +02:00
Pau Espin Pedrol
6e26c70f46 mgw: Allow receiving lowercase X-Osmo-Ign Callid field
MGCP RFC3435 (https://tools.ietf.org/html/rfc3435) states almost all
text has to be handled in a case-insensitive way, except SDP parts.

Related: OS#4001

Change-Id: Ifc1b3bfe6ff6922df478cea89bbbb291b5fa5706
2019-07-03 12:29:43 +02:00
Pau Espin Pedrol
9a34592c09 mgw: Allow receiving lowercase MGCP header keyword
MGCP RFC3435 (https://tools.ietf.org/html/rfc3435) states almost all
text has to be handled in a case-insensitive way, except SDP parts.

Related: OS#4001
Change-Id: I7d1e55faddafa3c3093d38513d4a434ecf5ea5bd
2019-07-03 12:29:43 +02:00
Pau Espin Pedrol
7eb6f2cb56 mgw: Make check of duplicated LCO fields case insensitive
Otherwise it would not catch a duplicate if first the param is
introduced in upper case and later in lower case, or the other way
around.

MGCP RFC3435 (https://tools.ietf.org/html/rfc3435) states almost all
text has to be handled in a case-insensitive way, except SDP parts.

Related: OS#4001
Change-Id: I254bfa3a2d2562441ca3a576cc8e1e7967d9c495
2019-07-03 12:29:42 +02:00
Pau Espin Pedrol
83fd8a5692 mgw: Support receiving lowercase LCO codec
MGCP RFC3435 (https://tools.ietf.org/html/rfc3435) states almost all
text has to be handled in a case-insensitive way, except SDP parts.

Related: OS#4001
Change-Id: I51dc1cdcbe2a5587769335fbecb5039ef22cae5d
2019-07-03 12:29:42 +02:00
Pau Espin Pedrol
17058484d1 mgw: Support receiving uppercase connection mode
MGCP RFC3435 (https://tools.ietf.org/html/rfc3435) states almost all
text has to be handled in a case-insensitive way, except SDP parts.

Related: OS#4001
Change-Id: I4da93dfc69b5585a197a7e201a1afb72c2f97030
2019-07-03 12:29:42 +02:00
Pau Espin Pedrol
166077ea48 mgcp-cli: Support lowercase header parameters
MGCP RFC3435 (https://tools.ietf.org/html/rfc3435) states almost all
text has to be handled in a case-insensitive way, except SDP parts.

Related: OS#4001
Change-Id: I4f7b07b77c2946e9cd6f0eeca00011bd905126dd
2019-07-03 12:29:37 +02:00
Pau Espin Pedrol
0c6c3c1da6 mgw: Support lowercase header parameters
MGCP RFC3435 (https://tools.ietf.org/html/rfc3435) states almost all
text has to be handled in a case-insensitive way, except SDP parts.

Related: OS#4001
Change-Id: I48252415f9d0cd985ad097f334aa4c1665f52511
2019-07-03 12:28:45 +02:00
Pau Espin Pedrol
fe9a1fe03b mgw: Support uppercase LCO options
MGCP RFC3435 (https://tools.ietf.org/html/rfc3435) states almost all
text has to be handled in a case-insensitive way, except SDP parts.

Related: OS#4001
Change-Id: Ic28a5eacc4c441d68e8a20d2743956ab2e01125d
2019-07-03 12:27:53 +02:00
Oliver Smith
1e4a34d45e manuals: update VTY documentation
Change-Id: Id34c363080ced158d1f1eddd15b954e731797cf8
2019-06-27 09:41:35 +02:00
Oliver Smith
189f29e939 vty: update desc of conn-timeout
It can be used together with LCLS, just make sure to also enable
keep-alive packets.

In OS#3429 it was pointed out, that during LCLS the media path remains
active but is not used. Without any traffic flowing, this looks like a
timed out connection and so it will be killed if conn-timeout is set.

However, OsmoBSC and OsmoMSC have an option to enable RTP keep-alive
packets (through libosmo-mgcp, originally intended to keep connections
behind NAT open). If that option is enabled, the keep-alive packets
should also prevent the conn-timeout.

Related: OS#3783
Change-Id: Ib4d19104d558a26a444a80fb36f4b7b33bc5cc59
2019-06-26 16:28:14 +02:00
Oliver Smith
d2ce444008 vty: allow 0 as conn-timeout to disable it
VTY command to disable conn-timeout again, after it has been enabled.
conn-timeout was introduced in [1].

[1] Change-Id I18886052e090466f73829133c24f011806cc1fe0.
Change-Id: I7dee7dafaaf4bb93fd692ea06b52b9e012beac6d
2019-06-26 16:27:54 +02:00
Daniel Willmann
ef9420adf5 manuals: Update vty/counter documentation
Change-Id: Icc86ef7ddd8a30984f91b025157e11fc0df9631e
Depends: OS#1700
2019-06-19 14:08:22 +02:00
Daniel Willmann
cb96e05a91 manuals: Add script to regenerate vty/counter documentation
Change-Id: Ib5e0bd9ec430a6ef3dce6845d7def39720c54637
Depends: Ic5828957a29d4f317e1ebf4f03b5f5359f6250e8 (docker-playground.git)
Related: OS#1700
2019-06-19 14:08:21 +02:00
Oliver Smith
f6387dc766 debian: create -doc subpackage with pdf manuals
I have verified, that the resulting debian packages build in my own OBS
namespace (see the -doc packages):
https://download.opensuse.org/repositories/home:/osmith42/Debian_9.0/all/
https://build.opensuse.org/project/show/home:osmith42

Depends: Ib7251cca9116151e473798879375cd5eb48ff3ad (osmo-ci)
Related: OS#3899
Change-Id: I8466713a8d414ea56ca24f6f7119338ad2b98ce5
2019-05-31 14:25:27 +00:00
Pau Espin Pedrol
1b1d7ed98f mgcp-cli: Validate osmux cid value during mgcp_msg_gen
Change-Id: I5c4d39b346b94de933f86200902c6c0ea2e1d5df
2019-05-23 16:48:24 +02:00
Pau Espin Pedrol
df7d97e4b4 osmux: Fix hardcoded rtp payload_type 98 in osmux conn
Depends on: libosmo-netif.git I5cbeb494a8932953d9fd2dc24dacf8cd97fd84e4
Change-Id: I24698a9613bc0de9460c6ad2d1067c152ebcf0b2
2019-05-19 19:28:49 +02:00
Pau Espin Pedrol
85978dadab osmux: Fix CID release for non-enabled connections
Change-Id: If65c70b421476776e20233733722d72aa26d69a8
2019-05-19 07:31:51 +00:00
Pau Espin Pedrol
9aaaab6b3b osmux: Fix loopback for Osmux connections
Move code in RTP specific path to generic dispatch_rtp_cb. This way
loopback logic is applied both for Osmux and RTP connections.

Change-Id: Ia30f5a14f150e4d151eac4d1046ea834f1685a5f
2019-05-15 23:32:09 +02:00
Pau Espin Pedrol
c1ad553d86 osmux: Use DUMMY ft msg as per Osmux spec
That MGCP_DUMMY_LOAD is an old hack prior to Osmux spec update, and it's
not nice since it cannot be 100% distinguished from a usual AMR ft
frame.

Let's use the correct DUMMY ft type and build it according spec. Allow
handling differently the old format for a while until we are sure no old
implementations (like bsc-nat) exist sending that kind of message.

Change-Id: Ib17d20b87b28aade49ba60519b56a96e694819af
2019-05-15 23:00:55 +02:00
Pau Espin Pedrol
c1bf4694e7 mgw, mgcp-li: Handle X-Osmux param name as case insensitive
RFC3435 states most text (except SDP) must be handled as case
insensitive.

Related: OS#4001
Change-Id: Iac073f1db46569b46eddeaecc9934a2986bd50f1
2019-05-14 20:35:13 +02:00
Pau Espin Pedrol
1442c5a99e osmux: Redo read/write osmux glue code to have data routed correctly
Remove old BTS/NET no longer in use and meaningless. Use new osmo-mgw
APIs to inject payload RTP<->Osmux on the correct socket and conn.

Change-Id: I60b6ba3ffdc74efff945ba13a0b736798bdf5d8c
2019-05-14 11:36:33 +02:00
Pau Espin Pedrol
dac2ca7e21 osmux: Delay osmux enable of conn until remote addr is configured by MDCX
Change-Id: I243e53681ebeb3d9cd8ed38bb132172b41745795
2019-05-14 11:36:33 +02:00
Pau Espin Pedrol
375252279c osmux: Improve logging around osmux enabling events
Change-Id: Iab687b97010fd484cb353b240b120c9c382066fa
2019-05-14 11:36:33 +02:00
Pau Espin Pedrol
295570c631 osmux: Use remote port to send osmux frames
Previously the local one was used but nobody cared because probably
everybody was using default 1984 on different IP addresses.

Change-Id: I01e590465fa247185d74103578681e9041249099
2019-05-14 11:36:33 +02:00
Pau Espin Pedrol
a93c6e9263 osmux: Provide correct local port during mgcp resp
Also document some possible future improvements for local addr.

Change-Id: I12c8fcdc8b772b9f92a70774406d4662f44bd9a9
2019-05-14 11:36:33 +02:00
Pau Espin Pedrol
30907dc9d8 mgcp-cli: endpoint_fsm: Add API to retrieve Osmux CID from MGW
Change-Id: Ic80d47f8eedda1c6ac8c33f1cafeb55c65e74692
2019-05-14 11:36:33 +02:00
Pau Espin Pedrol
14f8a08f44 osmux: Drop unneeded OSMUX_STATE_NEGOTIATING
Change-Id: I94e7df3287d037975adc16c5ada05adf94269ead
2019-05-14 11:36:33 +02:00
Pau Espin Pedrol
c63f15a9a7 mgcp-cli: Parse X-Osmux on MDCX response
During MDCX state is already changed to ACTIVATING but we still want to
send the local CID back to announce that we still use same local CID.

Change-Id: If182a48743ebe03f97caf9034e49b9947014bdf9
2019-05-14 11:36:29 +02:00
Pau Espin Pedrol
ca538fc5eb mgcp-cli: Allow submitting X-Osmux on MDCX request
Change-Id: I41243f3ed212ace6087d5b0341e3a52f4069e37d
2019-05-14 11:32:33 +02:00
Pau Espin Pedrol
6be2c49538 osmux: Handle Osmux MGCP extension in MDCX messages
Change-Id: I65e53bd5dd08b58c253e03d2f358f3be523a2688
2019-05-14 11:32:33 +02:00
Pau Espin Pedrol
91088c305f mgcp-cli: Parse X-Osmux on CRCX response
Change-Id: I6174d092b7425b8d3d6d02a55bf294be3e710e6a
2019-05-14 11:32:29 +02:00
Pau Espin Pedrol
900cd6518a mgcp-cli: Allow submitting X-Osmux on CRCX request
Change-Id: I73b4c62baf39050da81d65553cbea07bc51163de
2019-05-14 11:29:44 +02:00
Pau Espin Pedrol
fa810e8ccd osmux: Mark conn_rtp->type as osmux during CRCX
We also update code to allow setting up RTP related fields to succeed
during CRCX. We also update code to allow setting up RTP related fields to
succeed during CRCX.

Change-Id: Ia6e723d9a28ba38fc3382a4fb35ea6e5bab30c09
2019-05-13 18:56:56 +02:00
Pau Espin Pedrol
497611ae84 osmux: Introduce mgcp_conn_rtp_is_osmux() helper
This is going to be useful to know if a conn is to use Osmux without
looking at implementation details. Currently we have some duplicated
information (type, osmux.state, etc.) which we may want to refactor
later. This will allow changing implementation details without caring
much about rest of code.

Change-Id: Ib5a239fdbc319bcb16317f5e959d9a724b7a444a
2019-05-13 18:56:56 +02:00
Pau Espin Pedrol
2b89617aad osmux: Allocate CID during CRCX
Change-Id: Ie0e1835ff7e99421de9a5741a5eb57a11c004f7e
2019-05-13 18:56:56 +02:00
Pau Espin Pedrol
b542b0457b vty: Allow enabling Osmux
Change-Id: Ica2f82473bf1934502444be2325ee2049d938781
2019-05-13 18:56:56 +02:00
Pau Espin Pedrol
120568651a cosmetic: osmux: Document network byte order in port variable
Change-Id: Ia367ef08625265bc9cbdfcc693720a9b88852f4a
2019-05-13 18:56:56 +02:00
Pau Espin Pedrol
f1d301a9a6 cosmetic: mgcp_udp_send: Document port param is in network byte order
Change-Id: I7c4a388eba850ac066e60db089d46da0247773ec
2019-05-13 18:56:56 +02:00
Pau Espin Pedrol
c9a6280c94 osmux: Use LOGPCONN in several log calls
Change-Id: Ieb2c4b53db2df44e0dfbedb7de76d8cf6c83da91
2019-05-13 13:20:13 +02:00
Pau Espin Pedrol
b5583cde41 osmux: Fix reception of legacy dummy payloads
Size check had a bug. Take the opportunity to print wrong frames on
error.

Change-Id: I9f0d4e28a2019c7ad94344f2c34d17c365bebea9
2019-05-13 12:59:51 +02:00
Vadim Yanitskiy
6c1cd63a57 configure.ac: drop useless check for -fvisibility=hidden
We don't use this attribute in OsmoMGW anyway.

Change-Id: I61d08adbaaf938310f9474a1ea449e016a611ca3
2019-05-10 23:34:21 +07:00
Harald Welte
0d7ba56f5c update .gitignore
* remove tons of old cruft from openbsc.git that doesn't exist here
* actually add the osmo-mgw binary and .la file that we're building

Change-Id: Ic0e27c05e3ab368c195f9f9961fa70feb077a5e0
2019-05-08 22:15:27 +00:00
Harald Welte
3ac604e3ad handle NULL return of rate_ctr_group_alloc()
Change-Id: Ieadded9c088ef8f86164400a60ce542e3c868e9d
Related: OS#3701
2019-05-08 22:15:27 +00:00
Pau Espin Pedrol
9fb8ddf00e osmux: Document func and return different rc upon osmux init failure
Change-Id: Id8593bc374b598e63a70c60ac256273b9d99ba6e
2019-05-08 16:39:13 +00:00
Pau Espin Pedrol
182ca3bad4 mgcp-cli: Change osmo_mgcpc_ep_fsm name to avoid collision with old osmo-bsc
Recent commit moved mgw_endpoint_fsm from osmo-bsc.git here as
osmo_mgcpc_ep_fsm. Some API name changes were applied to avoid
collisions, but FSM was kept and it is registered during startup with
__attribute__((constructor)). As a result, with old osmo-bsc (+tests)
try to allocate its copy of mgw_endpoint_fsm, it fails because that name
is already registered.

Fixes: 538d2c53d9
Change-Id: I694ce58baa43f536b7e594b003edc891f029aa4a
2019-05-08 14:03:13 +02:00
Pau Espin Pedrol
f027f17dcb osmux: Log osmux socket during osmux_init
Change-Id: I43a658b19765b1c3b3cc42f78602b793ee36c67d
2019-05-06 17:41:37 +00:00
Pau Espin Pedrol
ac772d8b0c mgcp_osmux.c: osmux_enable_endpoint: Fix incorrect return check
osmux_xfrm_input_open_circuit returns 0 on success and -1 on error.
Confusion comes from that function being implemented by calling
osmux_batch_add_circuit which returns NULL on error.

cherry-picked from: openbsc.git ac1b03c8e59408336d07527e2597171cb99ed654.

Change-Id: Iba018aa57901642ea4c486526a973fe6023e10cf
2019-05-06 18:02:02 +02:00
Alexander Couzens
9d9f44ac71 mgcp_internal: LOGPENDP: ensure *endp is not NULL
In certain cases the endp can be NULL. LOGPENDP will dereference the
pointer to retreive the endpoint number.

Fixes: 8a893442a1e9 ("mgcp_internal: LOGPENDP: ensure *endp is not NULL")
Change-Id: Ie9b5ecf08f69533ccb2fbd7fbbb529105e0c922f
2019-05-02 17:59:51 +02:00
Neels Hofmeyr
84274e4e9b constify map_codec_to_pt() ptmap arg
Change-Id: I030843d2d692b7a73cca8f427df070d2806ab695
2019-04-30 02:25:05 +02:00
Neels Hofmeyr
c5479fe086 fix: multiple initial CRCX
The first CRCX responds with the actual MGW endpoint name that is assigned (at
least for rtpbridge/*@mgw requests).

If multiple CRCX are scheduled at the same time on a fresh MGW endpoint, both
get fired with a '*' and each creates a separate MGW endpoint.

Make mgcp_client_endpoint_fsm avoid this, and schedule only one CRCX at first,
and the rest once the MGW endpoint name is fixated. It is thus possible to
safely issue two osmo_mgcpc_ep_ci_request() at the same time.

Change-Id: I92a9944acc96398acd6649f9c3c5badec5dd6dcc
2019-04-30 02:25:05 +02:00
Neels Hofmeyr
538d2c53d9 move MGW endpoint FSM from osmo-bsc to here
Move mgw_endpoint_fsm from osmo-bsc here as osmo_mgcpc_ep_fsm. Apply various
renames for consistency. Use osmo_tdef from libosmocore instead of osmo-bsc's
(so far) local T_defs API.

Change T23042 to T2427001, which is a slightly less arbitrary number and
slightly more extendable in the future (2427 corresponds to the default MGCP
port at osmo-mgw, 001 is the first MGCP timer and so far the only one).

Change-Id: I9a3effd38e72841529df6c135c077116981dea36
2019-04-30 02:25:05 +02:00
Pau Espin Pedrol
8de58e79b8 osmux: Cleanup of CID alloc pool APIs
* Cleanup naming to make it far more clear
* Drop 2 variables holding CID values (allocated_cid, cid), in favour of
1 value holdinf the value and one bool stating whether the value is
used.
* Change conn_osmux_allocate_cid to allow allocating either next
available CID or a specific one, in preparation for forthcoming patches.

This commit can be merged straight away because anyway osmux cannot be
enabled in current status (blocked by vty config) and
(conn_)osmux_allocate_cid was/is not called anywhere. However, it helps
improving code base for future re-introduction of Osmux as it is
envisioned.

Change-Id: I737a248ac6c74add8e917fe2e2f36779d0f1d685
2019-04-25 21:38:38 +00:00
Pau Espin Pedrol
5e8d7995d1 create_response_with_sdp: Fix inclusion of X-Osmux
In previous code, 2 blocks were handling osmux inclusion one after the
other under same osmux.state. However, first block changes osmux.state
so second block can never be true and X-Osmux is never added.

Change-Id: Iceee8b64978651f1fe6bb883923561b081f73d9b
2019-04-25 21:38:31 +00:00
Pau Espin Pedrol
3239f6212e Introduce log fmt helpers LOGPENDP and LOGPCONN
Let's define macro once and use it everywhere instead of passing endp
information in different ways everywhere. Furthermore, use conn whenever
appropiate to have more information.

Change-Id: I1c49b1eb16bc5f1010376da5cf407ca6e31d81f2
2019-04-24 18:57:57 +02:00
Pau Espin Pedrol
209eb9f103 cosmetic: handle_modify_con: Fix indentation level
Change-Id: Ieb1e07d667a9fc1ff1e2fd367cbdb3c0dbfd4607
2019-04-24 12:03:24 +02:00
Pau Espin Pedrol
ef6304e4a1 mgcp_msg: Log faulty line on Osmux parsing error
Change-Id: I436e53963f8e7d00f3111ff81f7b08475c4b8ae9
2019-04-23 13:24:58 +02:00
Pau Espin Pedrol
21cdbfc196 cosmetic: tests: mgcp_client_test: clean trailing whitespace
Change-Id: Ie27c0a9bf7a16983f31a07c314b0a602e9fb8999
2019-04-23 12:36:51 +02:00
Pau Espin Pedrol
fc8067348b cosmetic: Fix typos in comment
Change-Id: I3c638033f1008325d2d653f00717e8c4a1bf9789
2019-04-23 00:20:32 +02:00
Pau Espin Pedrol
9f11dc5616 libosmo-mgcp: Use trunk type during endpoint allocation
This way we prepare it to add more endpoint types in the future (osmux)
and also make it clear that E1 endpoint specifics allocation is still
missing.

Change-Id: I7633b5287a436c11f0bbbdbaef1cf59a051a2471
2019-04-22 20:50:03 +02:00
Pau Espin Pedrol
ca0818c760 mgcp-client: Sanitize implementation of mgcp_client_rtpbridge_wildcard
* Get rid of string define containing printf statements
* Split name from rest of checks to easily add new names later

Change-Id: I46e05a7a3432733976760bbf1c5deb4f7610db11
2019-04-16 19:45:29 +02:00
Pau Espin Pedrol
c12bfb7ffc mgcp-client: whitespace cleanup in mgcp_client.c
Change-Id: Ic3495d70cb9c4e12552c6d97481cc0cf04b79f94
2019-04-16 17:23:09 +02:00
Philipp Maier
228e591589 AMR: Add function to convert between bw-effient and octet aligned mode
RFC3267 specifies two framing modes for AMR packets. An octet aligned
mode is specified where all fields of the AMR packets are aligned to
octet boundaries. The second framing mode is the bandwith efficient mode
where the fields are directly packed one after another.

- add paring/generation functions for related SDP fmtp parameters
- add conversion function to convert AMR payload

Depends: libosmo-netif I5b5a0fa644d8dbb1f04f9d7e35312683c7b3d196
Change-Id: I622c01874b25f5049d4f59eb8157e0ea3cbe16ba
Related: OS#3807
2019-03-12 09:56:28 +01:00
Philipp Maier
58128258b0 mgcp_network: use mgcp_rtp_codec in downlink format callback
The callback function mgcp_get_format() is used to request the codec
parameters for a specific connection. This function returns the
parameters as idividual pointers. Since there is a struct that holds all
important codec information among the ones which are currently returned
by mgcp_get_format, lets just use this codec struct as single return
value.

Change-Id: I348f9141eb59ed1a986447b96ab4a24ddf326936
Related: OS#3807
2019-03-11 09:55:04 +01:00
Philipp Maier
e7ae69a7be mgcp_sdp: untangle parsing of a= parameters
The if construct that takes care for parsing the a= parameters is
unnecessary complex. Lets handle each of the possible parameters
seperately on the same level.

Change-Id: Ifc801a757e9beb6b3974863d5ee99fc7c194559e
Related: OS#3807
2019-03-11 09:24:52 +01:00
Philipp Maier
7e37ce6008 mgcp_sdp: mgcp_sdp.c does not include own header file
The header file mgcp_sdp.h is not included by mgcp_sdp.c. Lets include
it here as well in order to be complete.

Change-Id: I3d9f28d6e7ca027b1be25d775a6a75d0fc491a50
2019-03-07 13:50:32 +01:00
Philipp Maier
dbe09dd515 mgco_client: cosmetic: remove excess whitespace
Change-Id: I54d70d8dfd7b4e1d3db892ec553eb78c6792d520
2019-03-07 13:50:32 +01:00
Philipp Maier
217d31d62f mgcp_sdp: cosmetic: correct typo in comment
Change-Id: I42aa9b5ce36df347cfb72be97e0f7a1898e6e8d0
2019-03-06 09:54:29 +01:00
Neels Hofmeyr
47642f245d make codec_table public as osmo_mgcpc_codec_names
These value_string[]s are also useful for printing chosen codecs in osmo-msc.

Change-Id: Ida0e59f9a1f2dd18efea0a51680a67b69f141efa
2019-03-04 22:25:06 +01:00
Neels Hofmeyr
cb760bdebe mgcp client: allow setting conn mode for CRCX and MDCX
Add conn_mode to struct mgcp_conn_peer, to allow setting an explicit connection
mode instead of implicit MGCP_CONN_RECV_ONLY / MGCP_CONN_RECV_SEND depending on
remote RTP port presence. Default to old behavior if this is left unset.

Rationale:

For IuUP Initialization, osmo-msc currently still uses a hack at osmo-mgw to
echo the IuUP Initialization back to the sender as an ACK. For sanity checking
reasons, this is done iff the CRCX created the CI in loopback mode. So, in
order to be able to switch to the mgcp_client_fsm in osmo-msc instead of
"manual" MGCP message composition and yet still be able to support IuUP and
IuCS, we need to be able to set the conn mode upon CRCX to loopback.

If we merged the IuUP patch (which is already on a branch somewhere) to
osmo-mgw, we wouldn't bother with this patch, but adding this conn mode choice
has these benefits:

- post-IuUP-fix osmo-msc versions can interop with older osmo-mgw IuUP for
  IuCS.

- It is conceivable that some MGCP clients prefer explicit conn modes instead
  of the implicit MGCP_CONN_RECV_ONLY / MGCP_CONN_RECV_SEND choice. This opens
  the possibility to switch between conn modes with MDCX messages.

Change-Id: I26be5c4b06a680f25f19797407ab56a5a4880ddc
2019-03-04 22:25:06 +01:00
Neels Hofmeyr
e3f8bca424 log: don't spam with "can not patch PT" messages
currently, when looking at an osmo-mgw log output with a phone call working
completely fine, you see these messages on LOGL_ERROR for each RTP packet:

20190304221420619 DRTP ERROR endpoint:0x0 can not patch PT because no suitable egress codec was found. (mgcp_network.c:761)
20190304221420639 DRTP ERROR endpoint:0x1 can not patch PT because no suitable egress codec was found. (mgcp_network.c:761)
20190304221420639 DRTP ERROR endpoint:0x0 can not patch PT because no suitable egress codec was found. (mgcp_network.c:761)
20190304221420659 DRTP ERROR endpoint:0x1 can not patch PT because no suitable egress codec was found. (mgcp_network.c:761)
20190304221420659 DRTP ERROR endpoint:0x0 can not patch PT because no suitable egress codec was found. (mgcp_network.c:761)

Put these messages on DEBUG level instead. They currently do not convey useful
information.

Possibly our MGCP clients could use some changes in behavior regarding payload
types, but since that topic is quite unresolved, let's not spam the ERROR log
level with this.

Change-Id: I4afc41fd472ec8ba887b9263fbac62de50d7cef9
2019-03-04 22:25:06 +01:00
Philipp Maier
9fc8a02196 Add option to GSM HR frames to RFC5593 representation
There are different specifications around on how a GSM-HR frame should
be encapsulated into an RTP packet. RFC5593 specifies a ToC (Table of
Contents) byte to be prepended in front of the payload data.

The two formats can be distinguished easily by their length. Then the
data can be formatted into the corresponding opposite format and vice
versa.

- Add new VTY rtp-patch options
- Add conversion function

Change-Id: Iceef19e5619f8c92dfa7c8cdecb2e9b15f0a11a1
Related: OS#3807
2019-02-25 15:06:05 +01:00
Oliver Smith
e36b775eae Inactive connection cleanup (disabled by default)
Add a watchdog timer to connections, and close these connections when
the watchdog timer expires. Kick the watchdog whenever RTP messages or
the relevant MGCP messages arrive. Add the currently remaining timeout
to "show mgcp stats" in the VTY.

This feature is disabled by default, as it is incompatible with LCLS
(connections in LCLS state appear to be inactive). Enable it with the
new "conn-timeout" VTY setting. In general, this feature can be used to
work around interoperability problems causing connections to stay open
forever, and slowly exhausting all available ports. This happened for
various reasons already.

MDCX is the only relevant MGCP message:
- CRCX creates the conn and timer
- DLCX deletes the conn and timer
- MDCX is the only remaining supported MGCP message that indicates a CI
- Can't easily generically parse a CI for all MGCP messages, parsing is
  done in handle_modify_con().

Related: OS#3429
Change-Id: I18886052e090466f73829133c24f011806cc1fe0
2019-02-06 12:01:06 +01:00
Oliver Smith
622dd61764 Cosmetic: fix spaces/tabs in mgcp_requests[]
Change-Id: I2e8ef83b231b7d7b71e533d0699afea433d635aa
2019-01-30 15:27:21 +01:00
Harald Welte
e81c1176aa Bump version: 1.4.0.90-ed0c-dirty → 1.5.0
Change-Id: I8f7c7b75f38ebd1ee48605596424af48cc1ad53d
2019-01-20 15:02:19 +01:00
Neels Hofmeyr
ed0c1aa9d5 mgcp_client: tweak some log levels INFO -> {DEBUG,ERROR}
Change-Id: Ie4ecb4b82a7a1e476c58d0a6056525733254adbb
2019-01-03 02:12:55 +01:00
Neels Hofmeyr
ac69ea9cdf mgcp_client: make domain part of endpoint configurable
So far, both osmo-msc and osmo-bsc always pass endpoint names of the form
'...@mgw' to osmo-mgw. Allow configuring the 'mgw' part.

Note that the actual way to pass a differing name is to pass a composed
'rtpbridge/*@foo' to mgcp_msg_gen() in the struct mgcp_msg. So this merely adds
a common VTY config for the domain name part, changes to clients are necessary.

- add mgcp_client_rtpbridge_wildcard() (useful for AoIP endpoints)
- add mgcp_client_endpoint_domain() (useful for SCCPlite endpoints)
- add mgcp client vty cfg 'mgw endpoint-domain NAME'

Rationale: reading pcaps becomes so much easier when each of osmo-bsc and
osmo-msc address their MGW with differing domain names. Otherwise, both will
have a '0@mgw' endpoint and it gets really confusing.

Also: our MGCP clients osmo-bsc and osmo-msc use code dup to compose the
initial 'rtpbridge/*@mgw' rtpbridge wildcard. It should be defined by this API
instead.

This will be used by:
* osmo-msc I87ac11847d1a6d165ee9a2b5d8a4978e7ac73433
* osmo-bsc I492023e9dca0233ec0a077032455d9f2e3880f78

After these, with according configuration, there can be a '0@bsc' and a '0@msc'
endpoint on two separate osmo-mgw instances:

osmo-mgw-for-bsc.cfg:
 mgcp
  domain bsc

osmo-bsc.cfg:
 msc 0
  mgw endpoint-domain bsc

osmo-mgw-for-msc.cfg:
 mgcp
  domain msc

osmo-msc.cfg:
 msc
  mgw endpoint-domain msc

There can also be '0@bsc' and '1@msc' endpoints on one single osmo-mgw instance with:

osmo-mgw.cfg:
 mgcp
  domain *

and same osmo-{bsc,msc}.cfg as above.

(By default, everything will still use '@mgw')

Change-Id: Ia662016f29dd8727d9c4626d726729641e21e1f8
2019-01-03 02:11:23 +01:00
Neels Hofmeyr
0a40379214 mgcp_client: logging tweaks
Fix typos, use osmo_sock_get_name2() to show the tx source and target IP:port,
shorten some wording.

Depends: I8ad89ac447c9c582742e70d082072bdd40a5a398 (libosmocore)
Change-Id: Iae728192f499330d16836d9435648f6b8ed213b6
2018-12-19 22:47:53 +00:00
Neels Hofmeyr
72bc8da537 mgcp_client: drop a bunch of dead code
Remove public API that makes no sense anymore and is dead code.

I see the dropped API as a dead-end initial misconception of the early mgcp
client, and it doesn't really make sense to drag this stuff along. It has not
been used by osmo-msc,-bsc for a long time now, and just confuses the reader.

It is public API, yes, and older versions of osmo-msc / osmo-bsc will not be
able to compile against this, but even if it did, the resulting MGCP client
would not work with the current osmo-mgw: this API is still based on the
premise that the MGCP client dictates the MGW endpoint numbers, a concept that
cannot be used with the current osmo-mgw. Instead, osmo-mgw expects a
wildcarded endpoint upon CRCX and assigns its own endpoint names.

Also, the bts-base configuration is unused and a legacy of when osmo-bsc_mgcp
had explicit BTS and CN sides.

Change-Id: I98a9f1f17a1c4ab20cea3b08c7d21663592134d6
2018-12-19 22:22:34 +00:00
Neels Hofmeyr
c3132fd508 mgcp_client_vty: fix missing talloc_free
If the vty client enters multiple local / remote addresses, that leaks talloc
memory of the previously set addresses. Free those first, if any, using
osmo_talloc_replace_string().

Change-Id: I331b3d53b5eb330b87d798f952077a043674d409
2018-12-19 22:19:17 +00:00
Neels Hofmeyr
96c3107daf osmo-mgw: err-log: include expected domain name
Add the expected domain name, and move the error log to where the expected
domain name is compared.

Change-Id: I59f40dc9263f686852f103ca904fc0a6702d7c8e
2018-12-19 17:58:42 +01:00
Neels Hofmeyr
ad21a0e166 check_rtp: on IP:port errors, log the IP and port
Half of those are obviously zero, but I'd rather print the raw data instead of
adding string constants, even if the condition must always lead to 0.0.0.0:0.

Rationale: I had osmo-mgw listen on 0.0.0.0 and got the error message
  DRTP ERROR endpoint:0x1 destination IP-address is invalid
which didn't convey that 0.0.0.0 is regarded as invalid.

Change-Id: I9e98d464a27038904797c5c10735a98ef5b7b9c3
2018-12-17 13:38:12 +00:00
Neels Hofmeyr
32d15cc8ba drop/replace very weird logging in mgcp_client.c
mgcp_do_write() is the final stage of writing data towards the MGCP server
(MGW). In that function, drop an unconditional iteration and copy of the MGCP
message to a static string buffer for no apparent reason besides debug logging.

Instead, use osmo_escape_str() with a limited length, which can just be an
inline format argument in the LOGP() statement. This way, the string mangling
is simpler and only gets run when DMGCP is actually on debug log level.

Change-Id: Id6877ed7fd7dbe009b2ece8792d5160d040c1aaa
2018-12-17 13:37:18 +00:00
Oliver Smith
1fd50e5306 contrib: fix makedistcheck with disabled systemd
EXTRA_DIST files need to be distributed, no matter if the systemd option
is configured or not.

Change-Id: I5014d98e06e033d04be45585b34458c90b1ced00
2018-12-06 13:54:53 +01:00
Oliver Smith
6802374574 contrib/jenkins.sh: build and publish manuals
Add new environment variables WITH_MANUALS and PUBLISH to control if
the manuals should be built and uploaded. Describe all environment vars
on top of the file.

When WITH_MANUALS is set, install osmo-gsm-manuals like any other
dependency and add --enable-manuals to the configure flags (for "make"
and "make distcheck"). Add the bin subdir of the installed files to
PATH, so osmo-gsm-manuals-check-depends can be used by ./configure.

Related: OS#3385
Change-Id: I24179b21ce4ff182649243e286c87c824b889454
2018-12-05 13:09:33 +01:00
Oliver Smith
82c1d1eb20 Fix DISTCHECK_CONFIGURE_FLAGS override
Set AM_DISTCHECK_CONFIGURE_FLAGS in Makefile.am instead of
DISTCHECK_CONFIGURE_FLAGS. This is the recommended way from the
automake manual, as otherwise the flag can't be changed by the user
anymore.

Related: OS#3718
Change-Id: Ie9db1519e89d2a8ca1e403e480f57d72fc25ab75
2018-12-04 15:36:37 +01:00
Oliver Smith
5a5169e15c jenkins.sh: remove leftover MGCP env variable
Jenkins currently runs the build twice, once with
MGCP="--enable-mgcp-transcoding" and once with
MGCP="--disable-mgcp-transcoding". The configure.ac does not have this
parameter, so let's remove the confusing variable.

This is in preparation for describing all environment variables used in
jenkins.sh (follow up commit).

Change-Id: I6223afef0f34d90170a691d7d5b1d9542c34c6b0
2018-12-03 16:52:11 +01:00
Oliver Smith
7c0b70a94e build manuals moved here from osmo-gsm-manuals.git
Moved to doc/manuals/, with full commit history, in preceding merge commit.
Now incorporate in the build system.

Build with:

$ autoreconf -fi
$ ./configure --enable-manuals
$ make

Shared files from osmo-gsm-manuals.git are found automatically if
- the repository is checked out in ../osmo-gsm-manuals; or
- if it osmo-gsm-manuals was installed with "make install"; or
- OSMO_GSM_MANUALS_DIR is set.

Related: OS#3385
Change-Id: I504f05a49721f2dfe105b6c5fd1995c4e7a0be9c
2018-11-27 18:07:37 +01:00
Neels Hofmeyr
ca6a8495e1 Merge history from osmo-gsm-manuals.git
Change-Id: Ibbc4f41f7672159d8596ceb84b60e5c849b98074
2018-11-27 18:07:16 +01:00
Neels Hofmeyr
c0a2196f91 mgw: update vty reference
Change-Id: Ib30ea8b02f8a950648b85e7ebc96a40ba5a36b34
2018-11-27 18:07:00 +01:00
Neels Hofmeyr
da026abc27 OsmoMGW: document the 'X-Osmo-IGN' MGCP extension
See also Ia6fe5ead4b601931c1bf41b29fc1b237aac37d2c in osmo-mgw.git, which
changes the initial single-character implementation to match this specification
of string tokens separated by spaces.

Change-Id: If15a88c3b5b40fd1d24ad0f94f3231f678669ab1
2018-11-27 18:07:00 +01:00
Daniel Willmann
27780313ac Add initial OsmoMGW manual
Change-Id: I1b4ff96309d280c6a63105a6e06a82ec2e7b6908
2018-11-27 18:07:00 +01:00
Daniel Willmann
e5ed946bdb mgw: Add new VTY reference
Change-Id: Ic8ca983f3358aae9362fb21ff3a111f9c4f3935b
2018-11-27 18:07:00 +01:00
Harald Welte
c319b79baa vty-ref: Update URI of docbook 5.0 schema
... to match the /etc/xml/catalog file on debian (no "www" in hostname)

Change-Id: Id9f3579c7f2bc3af13fe30b5268f249b6f59ed0d
2018-11-27 18:07:00 +01:00
Neels Hofmeyr
2818db8b33 OsmoMGW: update VTY reference
This is the first update since the libosmocore changes to the 'show
online-help' generated output. Hence the produced document now benefits from
the structural improvements:
- not repeating common commands for every node;
- using section names that match the VTY prompt.

Change-Id: I79804ec0e61cc19a679f079a083a5ea2ea3ee2de
2018-11-27 18:07:00 +01:00
Philipp Maier
0415de74b8 osmo-mgw: Add vty reference manual
add missing vty reference manual for osmo-mgw

Change-Id: If9d8cdcbbbf14b23e48af5b9ae1c56c2a38219a2
2018-11-27 18:07:00 +01:00
Neels Hofmeyr
4075b8805d Importing history from osmo-gsm-manuals.git
Change-Id: Ie9bf61a61d00a74cb327853f26dd1a7e1481fdfa
2018-11-27 18:05:58 +01:00
Stefan Sperling
8ab3fbbaf2 add DLCX command statistics to osmo-mgw
Add a counter group for DLCX commands. The group contains counters for
successful connection processing as well as various error conditions.
This provides a quick overview of DLCX failures on each trunk throughout
the lifetime of the osmo-mgw process.

The counters are displayed by 'show mgcp stats' and 'show rate-counters'

While here, rename MGCP_MDCX_FAIL_DEFERRED_BY_POLICY to
MGCP_MDCX_DEFERRED_BY_POLICY; we have decided that deferred connections
aren't failures, and this keeps names used by DLCX and MDCX in sync.

Also remove some allocation failure checks with OSMO_ASSERT(); such
checks aren't en vogue anymore.

Change-Id: Ie0dde2faf02fd68a69f986973d39b1bea367039b
Depends: I80d36181600901ae2e0f321dc02b5d54ddc94139I
Related: OS#2660
2018-11-27 14:58:21 +00:00
Neels Hofmeyr
cc39218dce fix osmo-mgw -s; fixes osmo-mgw.service using -s
Even though osmo-mgw advertises the -s option, the getopt configuration lacks
-s and that option does not work. Thus the osmo-mgw.service file that uses -s
was unable to start.

Add 's' to the getopt configuration, fix -s and hence also fix the .service
file.

Change-Id: I6f298aef73eb3486d04706910e9fdbaaebaf2481
2018-11-25 22:43:16 +01:00
Philipp Maier
8dbc9ed408 mgcp_protocol: increase buffer space for codec name in LCO
The function that parses the LCO uses an internal buffer to store the
codec name that has been issued via LCO. This buffer is only 9 byte
long, this means an 8 character string can be stored. If a codec name
exceeds this limit it gets chopped. For example "GSM-HR-08" becomes
"GSM-HR-0", which may mess up the codec negotiation.

- Increase the buffer from 9 to 17 byte.

Change-Id: I17ce7acde1f23ab1394227d74214fe2a55cd2264
Related: OS#3673
2018-11-07 11:34:59 +00:00
Stefan Sperling
ba25eab0fa add aggregated rtp connection stats to osmo-mgw
Add a counter group for aggregated RTP connection statistics.
This group contains RTP counters which aggregate values of the
ephemeral RTP counters maintained per connection (mgcp_conn).

This provides a global overview of RTP processing for each
trunk throughout the lifetime of the osmo-mgw process.

The counters are displayed by 'show mgcp stats' and 'show rate-counters'.

While here, fix a typo in an item of the mgcp_conn_rate_ctr_desc array:
"rtp:octets_rx" was displayed for outbound packes; now says "_tx".

Change-Id: I80d36181600901ae2e0f321dc02b5d54ddc94139
Related: OS#2660
2018-11-06 11:46:47 +01:00
Stefan Sperling
aa823bf24b add MDCX command statistics to osmo-mgw
Add a counter group for MDCX commands. The group contains counters for
successful connection processing as well as various error conditions.
This provides a quick overview of MDCX failures on each trunk throughout
the lifetime of the osmo-mgw process.

The counters are displayed by 'show mgcp stats' and 'show rate-counters'.

Change-Id: I79c27425ba40c3a85edc6cd846cba325d847298c
Depends: Ia2004f8063f3a50b5d7a838ebe8a784a47fcc50d
Related: OS#2660
2018-10-29 23:18:16 +00:00
Stefan Sperling
a714abfc91 add more mgcp crxc error counters
Add counters for error conditions which I overlooked in
commit 1e174875bf

Change-Id: Ia2004f8063f3a50b5d7a838ebe8a784a47fcc50d
Depends: If4f097c5e441914eaa24c7657813ebb3f9a49916
Related: OS#2660
2018-10-29 23:18:16 +00:00
Stefan Sperling
9270e91c09 use local variable for rate counters in handle_create_con()
Use a local variable to shorten the length of rate counter names.
Cosmetic only; no functional change.

Change-Id: If4f097c5e441914eaa24c7657813ebb3f9a49916
Related: OS#2660
2018-10-29 23:18:16 +00:00
Stefan Sperling
b7974e2fa4 show RTP TX/RX stats in 'mgcp show stats' output
Make the 'mgcp show stats' VTY command display TX/RX counters
for an RTP stream. This command was already showing the counter
for dropped packets from the same counter group, so it seems
natural to display other relevant counters in the group as well.

Change-Id: I1313e64d7d8b49964f21fc8d213cba6c9fb6c7cf
Related: OS#2660
2018-10-29 13:22:00 +01:00
Stefan Sperling
1e174875bf add MGCP CRCX command statistics to osmo-mgw
Add a counter group for CRCX commands. The group contains counters for
successful connection processing as well as various error conditions.
This provides a quick overview of CRCX failures on each trunk throughout
the lifetime of the osmo-mgw process.

For example, after running the TTCN3 mgw test suite, the counters show
the following values:

OsmoMGW> show rate-counters
crxc statistics:
             crcx:success:         88 (0/s 88/m 0/h 0/d) CRCX command processed successfully.
          crcx:bad_action:          0 (0/s 0/m 0/h 0/d) bad action in CRCX command.
     crcx:unhandled_param:          1 (0/s 1/m 0/h 0/d) unhandled parameter in CRCX command.
      crcx:missing_callid:          1 (0/s 1/m 0/h 0/d) missing CallId in CRCX command.
        crcx:invalid_mode:          1 (0/s 1/m 0/h 0/d) connection invalid mode in CRCX command.
      crcx:limit_exceeded:          0 (0/s 0/m 0/h 0/d) limit of concurrent connections was reached.
       crcx:unkown_callid:          0 (0/s 0/m 0/h 0/d) unknown CallId in CRCX command.
     crcx:alloc_conn_fail:          0 (0/s 0/m 0/h 0/d) connection allocation failure.
 crcx:no_remote_conn_desc:          1 (0/s 1/m 0/h 0/d) no opposite end specified for connection.
   crcx:start_rtp_failure:          0 (0/s 0/m 0/h 0/d) failure to start RTP processing.
       crcx:conn_rejected:          0 (0/s 0/m 0/h 0/d) connection rejected by policy.
OsmoMGW>

These same counters are now also shown by 'show mgcp stats'
in the context of the trunk which they belong to.

With input from Philipp Maier.

Change-Id: Ida82fc340d5c66180e5fe9a0d195e9be6dc64c61
Related: OS#2660
2018-10-29 10:17:20 +00:00
Harald Welte
c26b665c0c check_rtp_origin(): Avoid using memcmp for comparing integer types
in_addr consists only of s_addr, which is an integer type that
can be compared directly.  By avoiding memcmp() here we would have
been able to catch Coverity CID#188874 even without Coverity, and
make the code more compact...

Change-Id: Ic6105d39ae2fb4b301f87448b16763fe9f695621
2018-10-21 12:01:04 +02:00
Harald Welte
0479b2a15f check_rtp_origin(): Don't memcmp sockadd_in and in_addr
We were comparing 16 bytes (sockaddr_in) in memcmp() rather than using
four bytes (struct in_addr in mgcp conn end).

This is a good example why we should actually simply use the == (equals)
operator rather than using memcmp which treats everything as void.

Change-Id: Ic64256619ef893d625400e8b1b573ea2c629ed9c
Fixes: Coverity CID#188874
2018-10-21 11:56:05 +02:00
Pau Espin Pedrol
ff6606cacb osmux: Avoid processing further frames if conn not found
Other frames can come from known connections, so let's keep processing
each of them.

Change-Id: I09190140ba917dfada4b0952230b68e0f5f6d43d
2018-10-16 16:45:43 +02:00
Pau Espin Pedrol
407b1f186d osmux.h: Document enum osmux_state
Port for openbsc 9ae32d0d0607f270f20239b8104e09ec20352301.
Change-Id: I28978fa505d259f144457f29af4ba615aeaac74c
2018-10-16 16:44:51 +02:00
Pau Espin Pedrol
de2a4d7c22 osmux: Improve checks around activating and using enabled osmux
* Refactor code to have unified checks on all paths activating Osmux.
* Improve checkings at activation time and add logging.
* Code now enforces endp osmux status to be enabled before processing
the frame through endp->osmux.out. Before, a delayed or bad pkt could
arrive and be processed by an endp with osmux not enabled, using
endp->osmux.out that was not initialized and ended up crashing:
libosmo-netif/src/osmux.c:281:3: runtime error: member access within null pointer of type 'struct msgb'

This could also happen if a BSC started sending or we received (non legacy dummy) osmux
frames before we received the BSC CRCX ACK agreeing on osmux negotiation
and switching to ACTIVATING state.

Related: SYS#4350

Port from openbsc 4a2cc9eb0a0f9424c16b26fcb757483a39d67482.
Includes fixup from openbsc I438349bffaa46a10ad8983090a4b17aed7e00d82.
Change-Id: Iac11e447ec0d76e4e74ec982a6e3f63b35548978
2018-10-16 16:44:49 +02:00
Pau Espin Pedrol
48aff62341 osmux: allow enabling osmux only on correct activating state
State ACTIVATING is set once negotiation between the 2 parts went
successfuly.

Port from openbsc 96bd7b075a59eb051079152241b127ca944b0781.
Change-Id: Ic56eda1251be41369d869e687a1cf955df2c6d61
2018-10-16 16:43:42 +02:00
Pau Espin Pedrol
852ba86949 osmux: Make func handling dummy frames independent of endp type
Port from openbsc 8f321179747f64819d940d72d0212192f69284ca.
Change-Id: I3e16217737fd5ffb95c166c5f7344492cb6a6263
2018-10-16 16:43:34 +02:00
Pau Espin Pedrol
662fa421c6 osmux: Move parse_cid of legacy dummy frames to own function
Backport from openbsc b010f869c915016b7fa97a26621582cd89de96b0.
Change-Id: I5766165985fbfcecc63d45b9e229322bc8cedf52
2018-10-16 15:54:40 +02:00
Pau Espin Pedrol
d14163e74f osmux: Don't process regular osmux frames if disabled by cfg
Prior to this commit, the check was only done on legacy dummy frames.

Port from openbsc a42d4584fd01c9cd1021fab609bdaaafe859c13a.
Change-Id: I5b6606d72a9f5ae593a8e3ab5fbbe7e1e5a0ae11
2018-10-16 15:49:02 +02:00
Pau Espin Pedrol
11b4810142 osmux_send_dummy: Avoid logging incorrectly and sending if osmux not enabled
Port from openbsc 37a0307b6193c9b108cfd1aa2a88517a8b5cb907.
Change-Id: Iabc84cb482a425d4a6c2bb08c20b2e02a5a86b36
2018-10-16 15:45:59 +02:00
Pau Espin Pedrol
9ecceb651b mgcp: Log endpoint nr consistently as hex
Port from openbsc 078905a0603c91b227854abfa01c9e24143e39a1.
Change-Id: Idcb40e6fd561b24e111afe7463f44c43c530fac5
2018-10-16 15:36:08 +02:00
Pau Espin Pedrol
426a9d9103 osmux: Avoid initing output without enabling osmux
Otherwise we end up in a weird state where we have timers set up but
osmux is still flagged as not enabled.

Cherry-picked from openbsc cad739d2386640a68c24e3d470ddacdcaf377561.
Change-Id: I0a334842463d311bc80a980e60fb702a0a9ad610
2018-10-16 15:31:49 +02:00
Pau Espin Pedrol
17bf603222 mgcp_osmux: Use define to calculate rtp_ssrc_winlen
Since that define is already used to allocate size of osmux_cid_bitmap,
let's use it here too instead of hardcoding its value.

Change-Id: Ib2e4febee8bc6bcc035ad0a65c5c1eb94ef5e6fb
2018-09-17 13:38:46 +02:00
Pau Espin Pedrol
bcd52e5724 mgcp: Fix osmux_cid_bitmap static array size calculation
Right now it's not a big issue since OSMUX_CID_MAX is 255, so 255+1 is
256 which fits array boundaries correctly (multiple of 8). However, if
for example OSMUX_CID_MAX was modified to be 12, 12+1/8 = 1, so we'd
have an undesired memory access when accessing last 4 CIDs.

A +1 should be kept on top, because OSMUX_CID_MAX specified the maximum
number used by a CID, that is (0,OSMUX_CID_MAX), and as a result we
require OSMUX_CID_MAX+1 slots.

Change-Id: Iaf9b93712dbd2a862b01e70dd8e11893bfa6b24c
2018-09-17 13:36:36 +02:00
Pau Espin Pedrol
956242dcec Install sample cfg file to /etc/osmocom
Change-Id: I6926e989a130086f4b6c8277407377a4063f452f
2018-09-12 20:27:00 +02:00
Pau Espin Pedrol
e686675f7d Install systemd services with autotools
Change-Id: I7e4dae6b8c1685e8a673c58a843c41fa0af1b35c
2018-09-12 14:14:55 +02:00
Pau Espin Pedrol
4ca0b0d1fa debian: Remove dangling symlink to osmo-bsc-mgcp.service
osmo-bsc-mgcp was removed recently from osmo-mgw.git as it belongs to
openbsc.git.
This symlink was a leftover from that time.

Change-Id: Ifdcb266efd821eac9d90aff186400f85751e8f42
2018-09-11 03:20:34 +00:00
Neels Hofmeyr
efd645e5a8 log: avoid logging early media as error
When the peer address is still 0.0.0.0, the endpoint is not yet configured.
This commonly happens before bridging a call is complete, so instead of ERROR
logging about an invalid packet, rather INFO-log this as "early media".

Related: OS#3539
Change-Id: I335f6453bd599be76eef08fcf9e5daed071e5b6d
2018-09-10 13:37:26 +02:00
Neels Hofmeyr
f0504e86b3 comment: indicate struct type for mgcp_endpoint.conns
Change-Id: Ia65359c22da3e7b28e3f23b36446ca434ca0be8c
2018-09-07 04:14:51 +02:00
Neels Hofmeyr
e28b673fba cosmetic: mgcp_test: fix get_conn_id_from_response()
This function is implemented in such a weird way that I couldn't stop myself
from rewriting it.

Change-Id: Ib9b13d7b0e64f8ae25a7b69cbb385e7fad33d02b
2018-09-07 04:14:51 +02:00
Neels Hofmeyr
a77eade744 mgcp_conn_get(): match conn Id ('I:') despite leading zeros
The Connection Identifier is defined as a hex string, so clients may send the
ID back with or without leading zeros. Ignore all leading zeros when comparing.

A specific SCCPlite MSC is observed to DLCX with Connection Identifier with
leading zeros removed, which would mismatch pefore this patch.

Extend test_conn_id_matching() in mgcp_test.c to include leading zero tests.

Now, mgcp_conn_get() would match a valid id with *any* amount of leading zeros,
even if that far surpasses the permitted conn id length. Valid lengths of
incoming conn ids should be and is checked elsewhere.

Related: OS#3509
Change-Id: If55a64a2da47b6eff035711c08e4114d70dbec91
2018-09-07 04:14:51 +02:00
Neels Hofmeyr
6531726a02 mgcp_conn_get(): compare conn Id ('I:') case insensitively
The Connection Identifier is defined as a hex string, so clients may send the
ID back in lower case. Convert to upper case before comparing.

A specific SCCPlite MSC is observed to DLCX with Connection Identifier in lower
case, which would mismatch pefore this patch.

Add test_conn_id_matching() in mgcp_test.c to verify case insensitivity.

Cosmetic: use strcmp(), not strncmp(). In the presence of a terminating nul as
we can assume here, this makes no functional difference, but it clarifies the
code.

Related: OS#3508
Depends: Ib0ee1206b9f31d7ba25c31f8008119ac55440797 (libosmocore)
Change-Id: I8e52278c3abe9e9c8c848c2b1538bce443f68a43
2018-09-07 04:14:40 +02:00
Pau Espin Pedrol
31b4729f27 Remove libosmo-legacy-mgcp and osmo-bsc-mgcp
They are only used by openbsc.git programs and belong there.

Change-Id: Id31bef052d3f9b8aada1824d6f7f995ebd39bbfd
2018-09-06 20:39:53 +00:00
Neels Hofmeyr
a729db62a9 generate shorter 'I:' conn IDs
Reduce the number of hex chars generated as Connection Identifier from 32 to 8.

According to RFC3435 2.1.3.2 "Names of Connections", the maximum length is
indeed 32 characters, but there isn't really a benefit of using IDs of that
size. That, and:

A specific SCCPlite MSC is seen to be able to store conn IDs of up to 8 hex
characters of length. If given more than that, it will later send 'ffffffff' as
ID, e.g. in the DLCX message, causing mismatches and rejected DLCX.

Conn IDs need to be unique only within the context of one endpoint, so
producing 32 characters of ID is far beyond overkill, especially if we
currently expect exactly two IDs per endpoint.

Notice that the maximum length of conn ID that can be handled by the message
parsing and composition doesn't change, only the length that an osmo-mgw will
generate upon CRCX does.

Related: OS#3507
Change-Id: Ia290c22a91fca0e5aa44515fca6df00064aff100
2018-09-03 23:12:23 +02:00
Neels Hofmeyr
5336f57f05 fix mgcp_verify_ci(): off-by-one in max len check
MGCP_CONN_ID_MAXLEN actually includes a terminating nul, so we need to compare
strlen() against MGCP_CONN_ID_MAXLEN-1.

Log the length if it is too long.

Add MDCX_TOO_LONG_CI test to mgcp_test.c, testing a conn id of 33 characters.
Before this patch, the test returns error code 515 meaning "not found", while
now it returns 510 meaning "invalid", showing the off-by-one. Same is
illustrated by the error log ("not found" before, "too long" now), but the
error log is not verified by mgcp_test.c.

Change-Id: I8d6cc96be252bb486e94f343a8c7cae641ff9429
2018-09-03 23:08:08 +02:00
Neels Hofmeyr
eb72ff058f mgcp_verify_ci(): return meaningful error codes
Instead of just -1, return RFC3435 error codes that can be used to compose a
FAIL message response. Note that the return value stays compatible in that it
returns 0 on a valid Connection Identifier, nonzero otherwise.

The idea is to be able to distinguish between "Conn ID not found" and "Conn ID
invalid" in mgcp_test.c's expected output, in upcoming change
I8d6cc96be252bb486e94f343a8c7cae641ff9429.

Change-Id: Ifc17f2893cc4b9a865f3ffcb9888bbf1039337a6
2018-09-03 23:06:29 +02:00
Neels Hofmeyr
8a91d2c04e doc: fix mgcp_verify_ci() return val doc
Match the '\returns' doc to the actual implementation.

Change-Id: I6f89abd56ffcda8ba0276db1bc3381fa372e35a4
2018-09-03 23:05:26 +02:00
Neels Hofmeyr
55e0dcf254 mgcp_common: rename to MGCP_CONN_ID_MAXLEN
So far, MGCP_CONN_ID_LENGTH was often used as exactly the length of the
Connection Identifier. To indicate this length as a maximum, introduce the
MGCP_CONN_ID_MAXLEN and use it everywhere. Keep the old name as an alias.

Change-Id: I1117003c7614e98535d5c201d002e459c01bdc3f
2018-09-03 22:26:14 +02:00
Neels Hofmeyr
23e7bf1c00 mgcp_client: error on too long conn id
Instead of just silently truncating the conn ID if it is too long, rather
verify its length and return an error where applicable.

Adjust expected test output.

Change-Id: If2a1aab1f13e771a6705c430e3c75bd42477a23b
2018-09-03 21:32:37 +02:00
Neels Hofmeyr
10d487e13f mgcp_client_test: test long conn_id
Add a full length (32 characters according to spec) conn ID in a CRCX response,
as well as a too long one.

The too long one is currently silently truncated, a subsequent patch will
improve on that (If2a1aab1f13e771a6705c430e3c75bd42477a23b).

Change-Id: I5f2d52f086ea2d330fcce88a176488ace972bf79
2018-09-03 21:31:34 +02:00
Neels Hofmeyr
40f5033cf9 mgcp_client_test: also verify received conn_id
Include the parsed conn_id in the response cb printout to verify them in
mgcp_client_test.ok.

Change-Id: I6b9b18d4d0867febd75a4d29f8a2fcdf0553ae4c
2018-09-03 21:14:49 +02:00
Neels Hofmeyr
b1bb1fa187 mgcp_client_test: use "\r\n\r\n" instead of "\n\n"
The separator between MGCP and SDP section is typically "\r\n\r\n". For some
reason the test so far used "\n\n" instead, rather use the standard separator.

Change-Id: I41c73722e5fae00663bcf96de0b57b7155809a06
2018-09-03 21:13:19 +02:00
Neels Hofmeyr
1d121483f6 mgcp_client_test: cosmetically re-arrange reply_to() args
I want to test arbitrary length Conn IDs ('I:'), and hence don't want to pass
the conn_id as int, but rather just include it in the message string. Prepare
for that by eliminating the extra conn_id arg and just pass a params string.

Change-Id: Ib2e718dda3aa1f6e9979dee823d973dd002e2318
2018-09-03 21:13:19 +02:00
Harald Welte
124441af1f debian/rules: Don't overwrite .tarball-version
The .tarball-version file should contain the *source version* uniquely
identifying the git commit, and not the Debian package name.

With https://gerrit.osmocom.org/#/c/osmo-ci/+/10343/ there is a correct
.tarball-version file in the .tar.xz of the nightly source packages.

Change-Id: I4bf7b6124c747a0cff5562187a099c33525e109e
Related: OS#3449
2018-09-03 14:57:14 +02:00
Neels Hofmeyr
b861db97ea mgcp_test: fix get_conn_id_from_response() CI length
This function is set on conn ID length of 32 characters. Make it detect a
shorter length also when parsing 'o=-' headers. Before, this failed to
recognize a space as the end of the conn ID, now sees any non-hex char as end.

Related: OS#3507
Change-Id: I762c273bac172acb6d0aae6ea6267603ab654cbf
2018-08-30 14:23:12 +00:00
Neels Hofmeyr
08e07046f9 mgcp_test: fix log of conn_id presence
Flip logic to accurately log whether an 'I:' is included, instead of logging
the opposite.

Note that it isn't possible to log the actual conn ID, because they are random
and differ in every test run, which would collide with the fixed expected
output file mgcp_test.ok.

Change-Id: Idcd731b9daf618b97d8f7e6a776266071cd29e08
2018-08-30 14:23:12 +00:00
Philipp Maier
a5e0cf09a6 mgcp_client_fsm: switch to MGCP_CONN_RECV_SEND in add_audio()
The change Ie51cc86e90ffeca5b66bcb8f6db0d389241abe57 has replaced the
functions make_crcx_msg_bind() and make_crcx_msg_bind_connect() with
make_crcx_msg() and add_audio(). When a bidirectional connection is
needed, the user calls add_audio() to add the remaining connection
details. Unfortunately add_audio() leaves the conn_mode struct member
unchanged. Which means the connection is still at MGCP_CONN_RECV_ONLY,
which will instruct the MGW not to forward any of the received packets.

- Make sure that conn_mode is set to MGCP_CONN_RECV_SEND when
  add_audio() is called.

Change-Id: Id12de37797de5af5cc447642d2fbb1af7de680df
Closes: OS#3511
2018-08-29 14:44:52 +02:00
Neels Hofmeyr
f2388eab71 X-Osmo-IGN: rather parse items by token, not char
Adjust the X-Osmo-IGN parsing to use string tokens instead of parsing single
characters.

Reconsidering the first implementation as a poor choice, rather specify the
format of X-Osmo-IGN as any-length string tokens separated by spaces, which is
more flexible and more future proof.

See also osmo-gsm-manuals If15a88c3b5b40fd1d24ad0f94f3231f678669ab1 which
defines the X-Osmo-IGN format as string tokens, matching this patch.

In mgcp_test, add an unknown X-Osmo-IGN item. Though the output is not checked
by the testsuite.at, running manually shows the error log about the unkown
X-Osmo-IGN item.

Change-Id: Ia6fe5ead4b601931c1bf41b29fc1b237aac37d2c
2018-08-28 19:38:22 +00:00
Stefan Sperling
120865833c add VTY commands which show specific mgcp endpoints
Add VTY commands "show mgcp endpoint NAME" and
"show mgcp trunk <0-64> endpoint NAME" which
show information about specific endpoints.

Change-Id: I5330e697ec34bf215de91d44209048a8dc226d51
Related: OS#2660
2018-08-28 15:33:03 +02:00
Neels Hofmeyr
e6d8e91b3a add X-Osmo-IGN MGCP header to ignore CallID
The format is

  CRCX ...
  C: ...
  M: ...
  X-Osmo-IGN: C

So far the only ignorable element is C, i.e. the CallID. Any other items may be
added in the future.

(I initially intended to also add '@' to ignore the endpoint name's domain
part, but in the osmo-mgw code base the domain part is verified long before any
additional headers are even parsed, so sparing that refactoring for now.)

The intention is that osmo-bsc will issue "X-Osmo-IGN: C" for all SCCPlite
calls, because we are unable to retrieve the CallID that the MSC sends to
osmo-mgw for the network side of the endpoint.

Testing with a specific SCCPlite MSC, I actually observe that all CallIDs are
1, even for concurrent calls. So, an alternative hacky solution would have been
to always pass CallID == 1 for SCCPlite connections from osmo-bsc.

Related: I257ad574d8060fef19afce9798bd8a5a7f8c99fe (osmo-bsc)
Change-Id: Id7ae275ffde8ea9389270cfe3db087ee8db00b51
2018-08-25 16:47:44 +02:00
Neels Hofmeyr
475f868b7f cosmetic: drop code dup in mgcp_client_fsm.c CRCX
Both make_crcx_msg_bind() and make_crcx_msg_bind_connect() were mostly
identical. Rather, compose the CRCX bits in one common function and just add
the audio bits in another.

Prepares cosmetically for adding X-Osmo-IGN header.

Change-Id: Ie51cc86e90ffeca5b66bcb8f6db0d389241abe57
2018-08-25 14:23:10 +00:00
Neels Hofmeyr
0063ca2fb0 fix 3G hack: allow any IP for loopback and 0.0.0.0
HACK: for IuUP, we want to reply with an IuUP Initialization ACK upon the first RTP
message received. We currently hackishly accomplish that by putting the endpoint in
loopback mode and patching over the looped back RTP message to make it look like an
ack. We don't know the femto cell's IP address and port until the RAB Assignment
Response is received, but the nano3G expects an IuUP Initialization Ack before it even
sends the RAB Assignment Response. Hence, if the remote address is 0.0.0.0 and the
MGCP port is in loopback mode, allow looping back the packet to any source.

None of these are anything near nice, during call setup using a 3G femto cell,
we still lack a proper IuUP handling. See OS#2459, OS#1937. This is merely a
temporary hack to maintain 3G voice usability in a quick and dirty way.

Related: OS#3411
Change-Id: Ib25e6261855eae8ddb8d1c0b8838cc3e30332cf1
2018-08-24 10:37:03 +00:00
Neels Hofmeyr
610fda6108 cosmetic: log: fix "CallIDs does not match"
Change-Id: I16f3bf1312f913b7a7f0d9ff5c42efa645c6a5b3
2018-08-21 00:03:48 +02:00
Neels Hofmeyr
352eed09ae interpret domain '*' as 'allow any domain'
Make the 'domain NAME' vty doc more descriptive, and add the hint that '*'
means any domain.

In check_domain_name(), exit early in success if the configured domain name is
'*'.

(Do not implement other wildcard functionality for partial matches or the
like, just the single '*'.)

Related: OS#3490
Change-Id: Ie0220c88d2f5cee15f2a90390b3c4590ac61d5eb
2018-08-20 23:59:32 +02:00
Neels Hofmeyr
0a89e92a5e fix handling of "Wrong domain name" error
If no endpoint was found, assert that the cause code indicates error, so that
the remaining code path doesn't assume finding an endpoint was successful.

Also fix find_endpoint() to return an error cause (not 0) in case it finds the
domain name to be wrong.

After this, the error described in OS#3488 simply results in a CRCX failure,
not in a program crash.

Related: OS#3488
Change-Id: I87e2d76c22603d6fef89907c3cf8f7965abf35a0
2018-08-20 22:39:53 +02:00
Pau Espin Pedrol
dde80f3f61 vty: Fix typo writing bts-jitter-buffer-delay-{min,max}
Change-Id: I15ec1bd9e8d2241d29ef64cefe7ad4879ccdf898
2018-08-16 15:10:42 +02:00
Philipp Maier
9a7ccc3746 mgcp_client: check local port only once
When the user has set a local port for the mgcp client we want the
client to exit if this port is already occupied. If no port is set the
IETF default port is configured automatically. When we find this port
occupied we try up to 100 times the next port to find a useable port.

Since the for loop that controls the attempts always sets the port
config it uses for its checks it will mistakenly assume that the user
has set a port on the second cycle.

- Make sure we only check for the default port in the first cycle

Change-Id: Ic1fd1018d68fcac94961321615bfdd726465532d
2018-08-09 11:48:13 +02:00
Philipp Maier
910189d0a1 mgcp_sdp: restructure mgcp_write_response_sdp() (audio)
The function mgcp_write_response_sdp() is responsible to write the
audio port and the list with the supported payload type numbers to
the sdp response. At the moment it can only write exactly one payload
type number to the response, but in the future we may want to write
several payload type numbers to the response. Lets add a function
for that so that now.

- add add_audio() helper function to add multiple payload type
  numbers, but keep the functionality as it is for now

Change-Id: I662c725f697b2ffb1e3ad4671a445f943cd79b63
Related: OS#3442
2018-08-05 07:17:17 +00:00
Philipp Maier
8482e8374c mgcp_sdp: restructure mgcp_write_response_sdp() (rtpmap)
The function mgcp_write_response_sdp() generates the rtpmap lines in the
sdp response. Since we will likely support multiple codecs we will need
to generate several rtpmap lines. Therefore it makes sense to split up
that part in a separate function without altering the overall
functionality (yet)

- add static function add_rtpmap() to generate the rtpmap.

Change-Id: I520e2d40fe6294c88bae63dfcbc5238ef98101e2
Related: OS#3442
2018-08-05 07:17:17 +00:00
Philipp Maier
fee4fa9492 Cosmetic: remove misplaced line break
Change-Id: I7eea5454cb0567a4a162fb9796f889b2daa21af7
2018-08-03 12:24:20 +02:00
Philipp Maier
4dba769577 network: check packets before further processing
When we receive a packet, we do not really check the contents. However,
we should at least do some basic checks.

- Check for short RTP packets
- Check if the length field of RTCP packets seems plausible
- Check if the packet type of RTCP packets makes sense (IANA)

Change-Id: Id47b9eee2164c542e6b673db24974859dd0a7618
Related: OS#3444
2018-08-03 12:20:52 +02:00
Philipp Maier
da895b1151 network: do not patch PT of RTCP packets
At the moment all packets that are sent with mgcp_send are fed into
mgcp_patch_pt(). This functions corrects the payload type so that it
matches the codec configuration on the egress side. However, this
functions is only to be used with RTP packets and must not be used on
RTCP packets, which we currently do because we do not check if the
packet is RTCP or RTP.

- Check if the packet is RTP before running mgcp_patch_pt()

Change-Id: I55b8aa830e4e23f991373470bd04d4db12241c56
Related: OS#3444
2018-08-03 12:20:01 +02:00
Philipp Maier
a74c0ea2db mgcp_test: release endpoints after use
The test function test_multilple_codec() in mgcp_test.c creates a
lot of connections, but it never releases them. Just freeing the
cfg object is not enough since the UDP ports stay open and this
may interfere with other tests that also create connections
(port numbers).

- Make sure all endpoints are released when test_multilple_codec()
  is done.

Change-Id: Ic13b4368162149ec36c93f4188fa4c71166e08d5
2018-08-02 12:01:31 +00:00
Philipp Maier
6a26c16eb0 mgcp_client: increment local port number when port is in use
The IETF has designated port 2727 for call agents (clients). This
works as long as only one call agent is running. As soon as two
call agents (e.g. osmo-bsc and osmo-msc) run on the same machine.
The port numbers will collide.

To avoid such a situation we will first try the IETF port and if
we fail to use it we increment the port number until we found a
usable port. However, we should only do this if the user has not
configured a non standard port. (The rationale behind this is that
if there is a non standard port configured the choice must have
been made conciously by the user and therefor we should fail hard
so that the user gets aware of the problem.)

Change-Id: Iaa5f41fdb43ec6bf4feaefa174fd82622e37d4d0
Related: OS#2874
2018-08-02 12:38:06 +02:00
Philipp Maier
af8e00ffe8 mgcp_client: use IETF source port as for MGCP
At the moment the mgcp client uses an arbitrary port as sourceport to
exchange MGCP messages with the MGW. However, IETF has designated a
specific port as sourceport for MGCP clients (Call agents), which is
2727. See also RFC3435, capter 3.5 Transmission over UDP.

- Change MGCP_CLIENT_LOCAL_PORT_DEFAULT from 0 to 2727

Change-Id: I96de84df3a3bf623d98b057ec3f3f621a3330a8a
Closes:	OS#2874
2018-08-01 16:37:12 +02:00
Philipp Maier
6931f9a7a4 mgcp_network: translate payload type numbers in RTP packets
Since no transcoding is in place osmo-mgw forwards the incoming rtp
packets as they are (there may be minor modifications of the header) from
an ingress connection to an egress connection.

This works without problems as long as both connections use the same
payload type. For IANA defined fixed payload type numbers this is
usually the case, but for dynemic payload type numbers both ends may set
up the same codecs but with different payload type numbers.

When different payload type numbers are set up, and the packet is passed
through without modification, it will have the wrong payload type when
it is sent. The receiving end may then toss the packet since it expects
packets with the payload type it has configured.

The machanism, which is introduced with this patch looks up actual codec
inside the struct data of the ingress connection and then looks for the
matching codec in the struct data of the egress connection. When it
finds the codec there it looks up the payload type of this codec. The
header of the RTP packet is then patched with the correct payoad type.

- Add function mgcp_codec_pt_translate() to look up the payload type
- Add unit-test for function mgcp_codec_pt_translate()
- Add payload type translation to mgcp_network.c

Change-Id: I3a874e59fa07bcc2a67c376cafa197360036f539
Related: OS#2728
Related: OS#3384
2018-07-31 17:18:14 +00:00
Philipp Maier
544448abea mgcp_client_fsm: allow ptmap in mgcp_client_fsm as well
The regular version of the mgcp_client supports the configuration of of
custom payload types. In case some corner cases require a specific
dynamic paylod type number that is not according to 3GPP standards has
to be used the user can override the standard settings. However the fsm
based variant of the mgcp_client does not have that feature but it
should have it as well.

- add struct members for ptmap config.
- pass configuration values down to the underlying magcp client.

Change-Id: If176a3719dd9e888da16196d5fc0bdb53cc2a5f2
Related: OS#2728
Related: OS#3384
2018-07-31 17:18:14 +00:00
Pau Espin Pedrol
e547bdd1ce configure: Find correct libgsm's gsm.h header
Some distributions (archlinux) or versions of libgsm install gsm.h in
/usr/include/gsm/gsm.h

Since libgsm doesn't come with a pkfconfig, let's first check if gsm.h
and take the correct path in the build setup.

Change-Id: I07d3c03903e0d4bb80e843c7ed917a27b791ea53
2018-07-28 02:48:04 +02:00
Pau Espin Pedrol
d1562a7766 gitignore: Filter *.pc
Change-Id: I1e4e9b7342b23b4ef460801d61b4dd9c2fdc6dab
2018-07-27 19:23:34 +02:00
97 changed files with 6387 additions and 12218 deletions

49
.gitignore vendored
View File

@@ -2,26 +2,20 @@ 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 +39,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 +49,15 @@ 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

View File

@@ -18,13 +18,15 @@ SUBDIRS = \
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
AM_DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
@RELMAKE@
$(top_srcdir)/.version:

4
README
View File

@@ -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

View File

@@ -39,10 +39,10 @@ AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DL)
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.0.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.6.0)
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
@@ -74,39 +74,12 @@ 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([-Wnull-dereference], [CFLAGS="$CFLAGS -Wnull-dereference"])
AX_CHECK_COMPILE_FLAG([-Werror=sizeof-array-argument], [CFLAGS="$CFLAGS -Werror=sizeof-array-argument"])
AX_CHECK_COMPILE_FLAG([-Werror=sizeof-pointer-memaccess], [CFLAGS="$CFLAGS -Werror=sizeof-pointer-memaccess"])
@@ -144,6 +117,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 +182,22 @@ 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
Makefile)

View File

@@ -1 +1,3 @@
SUBDIRS = systemd
EXTRA_DIST = ipa.py

View File

@@ -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,18 @@ 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
osmo-build-dep.sh osmo-gsm-manuals
CONFIG="--enable-manuals"
fi
set +x
echo
echo
@@ -37,13 +50,18 @@ set -x
cd "$base"
autoreconf --install --force
./configure $MGCP --enable-vty-tests --enable-external-tests --enable-werror
./configure --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" \
DISTCHECK_CONFIGURE_FLAGS="--enable-vty-tests --enable-external-tests $CONFIG" \
$MAKE distcheck \
|| cat-testlogs.sh
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
make -C "$base/doc/manuals" publish
fi
$MAKE maintainer-clean
osmo-clean-workspace.sh

View File

@@ -0,0 +1,6 @@
EXTRA_DIST = osmo-mgw.service
if HAVE_SYSTEMD
systemdsystemunit_DATA = \
osmo-mgw.service
endif

View File

@@ -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

213
debian/changelog vendored
View File

@@ -1,3 +1,216 @@
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 ]

35
debian/control vendored
View File

@@ -7,7 +7,8 @@ Build-Depends: debhelper (>=9),
pkg-config,
autotools-dev,
libosmocore-dev,
libosmo-netif-dev
libosmo-netif-dev,
osmo-gsm-manuals-dev
Standards-Version: 3.9.8
Vcs-Git: git://git.osmocom.org/osmo-mgw.git
Vcs-Browser: https://git.osmocom.org/osmo-mgw/
@@ -19,7 +20,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-client6
Section: libs
Architecture: any
Multi-Arch: same
@@ -31,26 +32,14 @@ Package: libosmo-mgcp-client-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libosmo-mgcp-client3 (= ${binary:Version}), ${misc:Depends}
Depends: libosmo-mgcp-client6 (= ${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
View File

@@ -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.

View File

@@ -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

View File

@@ -1 +0,0 @@
usr/lib/*/libosmo-legacy-mgcp.so.*

View File

@@ -1,2 +0,0 @@
usr/bin/osmo-bsc_mgcp
usr/share/doc/osmo-mgw/examples/osmo-bsc_mgcp/mgcp.cfg

View File

@@ -1 +0,0 @@
../contrib/systemd/osmo-bsc-mgcp.service

1
debian/osmo-mgw-doc.install vendored Normal file
View File

@@ -0,0 +1 @@
usr/share/doc/osmo-mgw-doc/*.pdf

View File

@@ -1,2 +1,4 @@
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

View File

@@ -1 +0,0 @@
../contrib/systemd/osmo-mgw.service

11
debian/rules vendored
View File

@@ -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

View File

@@ -1,3 +1,4 @@
SUBDIRS = \
examples \
manuals \
$(NULL)

View File

@@ -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:

View File

@@ -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

18
doc/manuals/Makefile.am Normal file
View File

@@ -0,0 +1,18 @@
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
OSMO_REPOSITORY = osmo-mgw
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
endif

View File

@@ -0,0 +1,57 @@
== 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 (trunk 0) 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 which are not yet implemented. Don't use trunks other than the
"virtual" trunk 0.
.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

View File

@@ -0,0 +1,4 @@
[[counters]]
== Counters
include::./counters_generated.adoc[]

View 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

View 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>>

View File

@@ -0,0 +1,100 @@
[[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 -> OsmoMSC [label="3GPP AoIP"];
OsmoBSC -> OsmoMGW [label="MGCP"];
OsmoBTS -> OsmoMGW [label="RTP",dir=both];
OsmoMGW -> OsmoMSC [label="RTP",dir=both];
{rank=same OsmoBSC OsmoMGW}
OsmoMGW [color=red];
}
----
[[fig-msc]]
.OsmoMGW used with OsmoMSC
[graphviz]
----
digraph G {
rankdir = LR;
BTS -> BSC [label="Abis"];
BSC -> OsmoMSC [label="3GPP AoIP"];
OsmoMSC -> OsmoMGW [label="MGCP"];
BSC -> 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];
}
----
=== Software Components
OsmoMGW contains a variety of different software components, which well
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
Osmux is not yet supported in OsmoMGW.
At the moment (July 2018), OsmoMGW only implements RTP proxy / RTP bridge
type endpoints, to each of which two RTP connections can be established.
We are planning to add endpoint types for:
- classic E1/T1 timeslots (64kBps alaw/ulaw)
- classic E1/T1 16k sub-slots with TRAU frames for classic BTS support
- 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

View 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 OsmoBTS 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.

View 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>

View File

@@ -0,0 +1,33 @@
: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_extensions.adoc[]
include::./common/chapters/osmux/osmux.adoc[]
//include::{srcdir}/chapters/counters.adoc[]
include::./common/chapters/port_numbers.adoc[]
include::./common/chapters/bibliography.adoc[]
include::./common/chapters/glossary.adoc[]
include::./common/chapters/gfdl.adoc[]

View 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
View 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"

View File

@@ -0,0 +1,2 @@
<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
</vtydoc>

File diff suppressed because it is too large Load Diff

View File

@@ -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/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)

View File

@@ -1,5 +1,4 @@
SUBDIRS = \
legacy_mgcp \
mgcp_client \
mgcp \
$(NULL)

View File

@@ -1,4 +0,0 @@
noinst_HEADERS = \
mgcp_transcode.h \
vty.h \
$(NULL)

View File

@@ -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

View File

@@ -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);

View File

@@ -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 */

View File

@@ -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

View File

@@ -1,8 +0,0 @@
#pragma once
#include <osmocom/vty/command.h>
enum mgcp_vty_node {
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
TRUNK_NODE,
};

View File

@@ -81,10 +81,11 @@ 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);
/**
@@ -118,6 +119,55 @@ struct mgcp_port_range {
#define MGCP_KEEPALIVE_ONCE (-1)
#define MGCP_KEEPALIVE_NEVER 0
/* 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_REJECTED_BY_POLICY,
MGCP_CRCX_FAIL_NO_OSMUX,
MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS,
MGCP_CRCX_FAIL_CODEC_NEGOTIATION,
MGCP_CRCX_FAIL_BIND_PORT,
};
/* 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_REJECTED_BY_POLICY,
MGCP_MDCX_DEFERRED_BY_POLICY
};
/* Global MCGP DLCX related rate counters */
enum {
MGCP_DLCX_SUCCESS,
MGCP_DLCX_FAIL_WILDCARD,
MGCP_DLCX_FAIL_NO_CONN,
MGCP_DLCX_FAIL_INVALID_CALLID,
MGCP_DLCX_FAIL_INVALID_CONNID,
MGCP_DLCX_FAIL_UNHANDLED_PARAM,
MGCP_DLCX_FAIL_REJECTED_BY_POLICY,
MGCP_DLCX_DEFERRED_BY_POLICY,
};
struct mgcp_trunk_config {
struct llist_head entry;
@@ -141,6 +191,7 @@ struct mgcp_trunk_config {
/* RTP patching */
int force_constant_ssrc; /* 0: don't, 1: once */
int force_aligned_timing;
bool rfc5993_hr_convert;
/* spec handling */
int force_realloc;
@@ -155,6 +206,15 @@ struct mgcp_trunk_config {
unsigned int number_endpoints;
int vty_number_endpoints;
struct mgcp_endpoint *endpoints;
/* 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;
};
enum mgcp_role {
@@ -216,6 +276,9 @@ 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;
};
/* config management */

View File

@@ -2,5 +2,6 @@
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, 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);

View File

@@ -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) */

View File

@@ -25,6 +25,7 @@
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/rate_ctr.h>
#include <inttypes.h>
/* RTP connection related counters */
@@ -35,7 +36,36 @@ 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."}
};
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,

View File

@@ -69,7 +69,7 @@ struct mgcp_endpoint {
/*! 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 */
@@ -90,10 +90,12 @@ struct mgcp_endpoint {
/*! 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;
};
/*! Extract endpoint number for a given endpoint */
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
void mgcp_endp_release(struct mgcp_endpoint *endp);

View File

@@ -95,6 +95,9 @@ struct mgcp_rtp_codec {
int payload_type;
char *audio_name;
char *subtype_name;
bool param_present;
struct mgcp_codec_param param;
};
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
@@ -128,6 +131,7 @@ struct mgcp_rtp_end {
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;
@@ -184,9 +188,9 @@ struct mgcp_conn_rtp {
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 */
/* 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;
@@ -226,11 +230,14 @@ struct mgcp_conn {
enum mgcp_connection_mode mode_orig;
/*! connection id to identify the connection */
char id[MGCP_CONN_ID_LENGTH];
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;
@@ -294,9 +301,8 @@ int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
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,
const struct mgcp_rtp_codec **codec,
const char **fmtp_extra,
struct mgcp_conn_rtp *conn);
/* internal RTP Annex A counting */
@@ -306,6 +312,11 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta
int mgcp_set_ip_tos(int fd, int tos);
/* 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;
}
enum {
MGCP_DEST_NET = 0,
MGCP_DEST_BTS,
@@ -328,3 +339,14 @@ enum {
#define PTYPE_UNDEFINED (-1)
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);
#define LOGPENDP(endp, cat, level, fmt, args...) \
LOGP(cat, level, "endpoint:0x%x " fmt, \
endp ? ENDPOINT_NUMBER(endp) : -1, \
## args)
#define LOGPCONN(conn, cat, level, fmt, args...) \
LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
(conn)->id, \
## args)

View File

@@ -14,20 +14,22 @@ 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);
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 +37,3 @@ enum osmux_usage {
OSMUX_USAGE_ON = 1,
OSMUX_USAGE_ONLY = 2,
};

View File

@@ -5,11 +5,14 @@
#include <osmocom/mgcp_client/mgcp_common.h>
/* See also: RFC 3435, chapter 3.5 Transmission over UDP */
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0"
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 0
#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,10 @@ 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];
};
typedef unsigned int mgcp_trans_id_t;
@@ -33,7 +37,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 +47,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,8 +65,10 @@ 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 {
@@ -70,7 +80,7 @@ struct mgcp_response {
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 +97,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 +115,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);
@@ -118,8 +135,8 @@ 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);
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);
/* Invoked when an MGCP response is received or sending failed. When the
* response is passed as NULL, this indicates failure during transmission. */
@@ -132,20 +149,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 +159,7 @@ 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);

View File

@@ -0,0 +1,48 @@
/* 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);
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);
/*! 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);
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); }

View File

@@ -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,5 @@ 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);
const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info);

View File

@@ -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}/

View File

@@ -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",

View File

@@ -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)

View File

@@ -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)

View File

@@ -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));
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -20,7 +20,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=6:0:0
lib_LTLIBRARIES = \
libosmo-mgcp-client.la \
@@ -30,6 +30,7 @@ libosmo_mgcp_client_la_SOURCES = \
mgcp_client.c \
mgcp_client_vty.c \
mgcp_client_fsm.c \
mgcp_client_endpoint_fsm.c \
$(NULL)
libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION)

View File

@@ -35,9 +35,16 @@
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <limits.h>
#ifndef OSMUX_CID_MAX
#define OSMUX_CID_MAX 255 /* FIXME: use OSMUX_CID_MAX from libosmo-netif? */
#endif
/* Codec descripton for dynamic payload types (SDP) */
static const struct value_string codec_table[] = {
const struct value_string osmo_mgcpc_codec_names[] = {
{ CODEC_PCMU_8000_1, "PCMU/8000/1" },
{ CODEC_GSM_8000_1, "GSM/8000/1" },
{ CODEC_PCMA_8000_1, "PCMA/8000/1" },
@@ -81,19 +88,19 @@ enum mgcp_codecs map_str_to_codec(const char *str)
osmo_strlcpy(str_buf, extract_codec_name(str), sizeof(str_buf));
for (i = 0; i < ARRAY_SIZE(codec_table); i++) {
codec_name = extract_codec_name(codec_table[i].str);
for (i = 0; i < ARRAY_SIZE(osmo_mgcpc_codec_names); i++) {
codec_name = extract_codec_name(osmo_mgcpc_codec_names[i].str);
if (!codec_name)
continue;
if (strcmp(codec_name, str_buf) == 0)
return codec_table[i].value;
return osmo_mgcpc_codec_names[i].value;
}
return -1;
}
/* Check the ptmap for illegal mappings */
static int check_ptmap(struct ptmap *ptmap)
static int check_ptmap(const struct ptmap *ptmap)
{
/* Check if there are mappings that leave the IANA assigned dynamic
* payload type range. Under normal conditions such mappings should
@@ -122,7 +129,7 @@ error:
* \ptmap[in] ptmap_len length of the payload type map.
* \ptmap[in] codec the codec for which the payload type should be looked up.
* \returns assigned payload type */
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)
{
unsigned int i;
@@ -190,75 +197,9 @@ void mgcp_client_conf_init(struct mgcp_client_conf *conf)
.local_port = -1,
.remote_addr = NULL,
.remote_port = -1,
.first_endpoint = 0,
.last_endpoint = 0,
.bts_base = 0,
};
}
/* Test if a given endpoint id is currently in use */
static bool endpoint_in_use(uint16_t id, struct mgcp_client *client)
{
struct mgcp_inuse_endpoint *endpoint;
llist_for_each_entry(endpoint, &client->inuse_endpoints, entry) {
if (endpoint->id == id)
return true;
}
return false;
}
/*! Pick next free endpoint ID.
* \param[in,out] client MGCP client descriptor.
* \returns 0 on success, -EINVAL on error. */
int mgcp_client_next_endpoint(struct mgcp_client *client)
{
int i;
uint16_t first_endpoint = client->actual.first_endpoint;
uint16_t last_endpoint = client->actual.last_endpoint;
struct mgcp_inuse_endpoint *endpoint;
/* Use the maximum permitted range if the VTY
* configuration does not specify a range */
if (client->actual.last_endpoint == 0) {
first_endpoint = 1;
last_endpoint = 65534;
}
/* Test the permitted endpoint range for an endpoint
* number that is not in use. When a suitable endpoint
* number can be found, seize it by adding it to the
* inuse list. */
for (i=first_endpoint;i<last_endpoint;i++)
{
if (endpoint_in_use(i,client) == false) {
endpoint = talloc_zero(client, struct mgcp_inuse_endpoint);
endpoint->id = i;
llist_add_tail(&endpoint->entry, &client->inuse_endpoints);
return endpoint->id;
}
}
/* All endpoints are busy! */
return -EINVAL;
}
/*! Release a seized endpoint ID to make it available again for other calls.
* \param[in] id Endpoint ID
* \param[in,out] client MGCP client descriptor. */
/* Release a seized endpoint id to make it available again for other calls */
void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client)
{
struct mgcp_inuse_endpoint *endpoint;
struct mgcp_inuse_endpoint *endpoint_tmp;
llist_for_each_entry_safe(endpoint, endpoint_tmp, &client->inuse_endpoints, entry) {
if (endpoint->id == id) {
llist_del(&endpoint->entry);
talloc_free(endpoint);
}
}
}
static void mgcp_client_handle_response(struct mgcp_client *mgcp,
struct mgcp_response_pending *pending,
struct mgcp_response *response)
@@ -271,7 +212,7 @@ static void mgcp_client_handle_response(struct mgcp_client *mgcp,
if (pending->response_cb)
pending->response_cb(response, pending->priv);
else
LOGP(DLMGCP, LOGL_INFO, "MGCP response ignored (NULL cb)\n");
LOGP(DLMGCP, LOGL_DEBUG, "MGCP response ignored (NULL cb)\n");
talloc_free(pending);
}
@@ -326,7 +267,8 @@ static bool mgcp_line_is_valid(const char *line)
static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
{
char *pt_str;
unsigned int pt;
char *pt_end;
unsigned long int pt;
unsigned int count = 0;
unsigned int i;
@@ -350,7 +292,14 @@ static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
pt_str = strtok(NULL, " ");
if (!pt_str)
break;
pt = atoi(pt_str);
errno = 0;
pt = strtoul(pt_str, &pt_end, 0);
if ((errno == ERANGE && pt == ULONG_MAX) || (errno && !pt) ||
pt_str == pt_end)
goto response_parse_failure_pt;
if (pt >> 7) /* PT is 7 bit field, higher values not allowed */
goto response_parse_failure_pt;
/* Do not allow duplicate payload types */
for (i = 0; i < count; i++)
@@ -389,8 +338,8 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
unsigned int pt;
char codec_resp[64];
unsigned int codec;
if (strstr(line, "ptime")) {
if (sscanf(line, "a=ptime:%u", &r->ptime) != 1)
goto response_parse_failure_ptime;
@@ -419,17 +368,17 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
} else
goto response_parse_failure_rtpmap;
}
return 0;
response_parse_failure_ptime:
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse SDP parameter, invalid ptime (%s)\n", line);
return -EINVAL;
return -EINVAL;
response_parse_failure_rtpmap:
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse SDP parameter, invalid rtpmap (%s)\n", line);
return -EINVAL;
return -EINVAL;
}
/* Parse a line like "c=IN IP4 10.11.12.13" */
@@ -459,6 +408,37 @@ response_parse_failure:
return -EINVAL;
}
/*! Extract OSMUX CID from an MGCP parameter line (string).
* \param[in] line single parameter line from the MGCP message
* \returns OSMUX CID, -1 wildcard, -2 on error
* FIXME: This is a copy of function in mgcp_msg.c. Have some common.c file between both libs?
*/
static int mgcp_parse_osmux_cid(const char *line)
{
int osmux_cid;
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) { /* OSMUX_CID_MAX from libosmo-netif */
LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
osmux_cid, OSMUX_CID_MAX);
return -2;
}
LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
return osmux_cid;
}
/* A new section is marked by a double line break, check a few more
* patterns as there may be variants */
static char *mgcp_find_section_end(char *string)
@@ -550,6 +530,7 @@ static int mgcp_parse_head_param(char *result, unsigned int result_len,
char label, const char *line)
{
char label_string[4];
size_t rc;
/* Detect empty parameters */
if (strlen(line) < 4)
@@ -562,7 +543,14 @@ static int mgcp_parse_head_param(char *result, unsigned int result_len,
/* Copy payload part of the string to destinations (the label string
* is always 3 chars long) */
osmo_strlcpy(result, line + 3, result_len);
rc = osmo_strlcpy(result, line + 3, result_len);
if (rc >= result_len) {
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse MGCP response (parameter label: %c):"
" the received conn ID is too long: %zu, maximum is %u characters\n",
label, rc, result_len - 1);
return -ENOSPC;
}
return 0;
response_parse_failure:
@@ -591,12 +579,12 @@ static int parse_head_params(struct mgcp_response *r)
/* If there is an SDP body attached, prevent for_each_non_empty_line()
* into running in there, we are not yet interested in the parameters
* stored there. */
data_end = mgcp_find_section_end(data);
data_end = mgcp_find_section_end(data);
if (data_end)
*data_end = '\0';
for_each_non_empty_line(line, data_ptr) {
switch (line[0]) {
switch (toupper(line[0])) {
case 'Z':
rc = mgcp_parse_head_param(r->head.endpoint,
sizeof(r->head.endpoint),
@@ -625,6 +613,21 @@ static int parse_head_params(struct mgcp_response *r)
if (rc)
goto exit;
break;
case 'X':
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
rc = mgcp_parse_osmux_cid(line);
if (rc < 0) {
/* -1: we don't want wildcards in response. -2: error */
rc = -EINVAL;
goto exit;
}
r->head.x_osmo_osmux_use = true;
r->head.x_osmo_osmux_cid = (uint8_t) rc;
rc = 0;
break;
}
/* Ignore unknown X-headers */
break;
default:
/* skip unhandled parameters */
break;
@@ -709,11 +712,13 @@ static int mgcp_do_read(struct osmo_fd *fd)
ret = read(fd->fd, msg->data, 4096 - 128);
if (ret <= 0) {
LOGP(DLMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno));
LOGP(DLMGCP, LOGL_ERROR, "Failed to read: %s: %d='%s'\n", osmo_sock_get_name2(fd->fd),
errno, strerror(errno));
msgb_free(msg);
return -1;
} else if (ret > 4096 - 128) {
LOGP(DLMGCP, LOGL_ERROR, "Too much data: %d\n", ret);
LOGP(DLMGCP, LOGL_ERROR, "Too much data: %s: %d\n", osmo_sock_get_name2(fd->fd), ret);
msgb_free(msg);
return -1;
}
@@ -727,26 +732,15 @@ static int mgcp_do_read(struct osmo_fd *fd)
static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
{
int ret;
static char strbuf[4096];
unsigned int l = msg->len < sizeof(strbuf) ? msg->len : sizeof(strbuf);
unsigned int i;
osmo_strlcpy(strbuf, (const char*)msg->data, l);
for (i = 0; i < sizeof(strbuf); i++) {
if (strbuf[i] == '\n' || strbuf[i] == '\r') {
strbuf[i] = '\0';
break;
}
}
DEBUGP(DLMGCP, "Tx MGCP msg to MGCP GW: '%s'\n", strbuf);
LOGP(DLMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len);
LOGP(DLMGCP, LOGL_DEBUG, "Tx MGCP: %s: len=%u '%s'...\n",
osmo_sock_get_name2(fd->fd), msg->len, osmo_escape_str((const char*)msg->data, OSMO_MIN(42, msg->len)));
ret = write(fd->fd, msg->data, msg->len);
if (ret != msg->len)
LOGP(DLMGCP, LOGL_ERROR, "Failed to forward message to MGCP"
" GW: %s\n", strerror(errno));
LOGP(DLMGCP, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n",
osmo_sock_get_name2(fd->fd), errno, strerror(errno),
msg->len, osmo_escape_str((const char*)msg->data, OSMO_MIN(42, msg->len)));
return ret;
}
@@ -772,13 +766,55 @@ struct mgcp_client *mgcp_client_init(void *ctx,
mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port :
MGCP_CLIENT_REMOTE_PORT_DEFAULT;
mgcp->actual.first_endpoint = conf->first_endpoint > 0 ? (uint16_t)conf->first_endpoint : 0;
mgcp->actual.last_endpoint = conf->last_endpoint > 0 ? (uint16_t)conf->last_endpoint : 0;
mgcp->actual.bts_base = conf->bts_base > 0 ? (uint16_t)conf->bts_base : 4000;
if (osmo_strlcpy(mgcp->actual.endpoint_domain_name, conf->endpoint_domain_name,
sizeof(mgcp->actual.endpoint_domain_name))
>= sizeof(mgcp->actual.endpoint_domain_name)) {
LOGP(DLMGCP, LOGL_ERROR, "MGCP client: endpoint domain name is too long, max length is %zu: '%s'\n",
sizeof(mgcp->actual.endpoint_domain_name) - 1, conf->endpoint_domain_name);
talloc_free(mgcp);
return NULL;
}
LOGP(DLMGCP, LOGL_NOTICE, "MGCP client: using endpoint domain '@%s'\n", mgcp_client_endpoint_domain(mgcp));
return mgcp;
}
static int init_socket(struct mgcp_client *mgcp)
{
int rc;
struct osmo_wqueue *wq;
int i;
wq = &mgcp->wq;
for (i = 0; i < 100; i++) {
/* Initalize socket with the currently configured port
* number */
rc = osmo_sock_init2_ofd(&wq->bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
if (rc > 0)
return rc;
/* If there is a different port than the default port
* configured then we assume that the user has choosen
* that port conciously and we will not try to resolve
* this by silently choosing a different port. */
if (mgcp->actual.local_port != MGCP_CLIENT_LOCAL_PORT_DEFAULT && i == 0)
return -EINVAL;
/* Choose a new port number to try next */
LOGP(DLMGCP, LOGL_NOTICE,
"MGCPGW failed to bind to %s:%u, retrying with port %u\n",
mgcp->actual.local_addr, mgcp->actual.local_port, mgcp->actual.local_port + 1);
mgcp->actual.local_port++;
}
LOGP(DLMGCP, LOGL_FATAL, "MGCPGW failed to find a port to bind on %i times.\n", i);
return -EINVAL;
}
/*! Initalize client connection (opens socket only, no request is sent yet)
* \param[in,out] mgcp MGCP client descriptor.
* \returns 0 on success, -EINVAL on error. */
@@ -795,10 +831,7 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
wq = &mgcp->wq;
rc = osmo_sock_init2_ofd(&wq->bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
mgcp->actual.local_addr, mgcp->actual.local_port,
mgcp->actual.remote_addr, mgcp->actual.remote_port,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
rc = init_socket(mgcp);
if (rc < 0) {
LOGP(DLMGCP, LOGL_FATAL,
"Failed to initialize socket %s:%u -> %s:%u for MGCP GW: %s\n",
@@ -816,9 +849,7 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
wq->read_cb = mgcp_do_read;
wq->write_cb = mgcp_do_write;
LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s:%u -> %s:%u\n",
mgcp->actual.local_addr, mgcp->actual.local_port,
mgcp->actual.remote_addr, mgcp->actual.remote_port);
LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s\n", osmo_sock_get_name2(wq->bfd.fd));
return 0;
error_close_fd:
@@ -851,6 +882,36 @@ uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp)
return mgcp->remote_addr;
}
/* To compose endpoint names, usually for CRCX, use this as domain name.
* For example, snprintf("rtpbridge\*@%s", mgcp_client_endpoint_domain(mgcp)). */
const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp)
{
return mgcp->actual.endpoint_domain_name[0] ? mgcp->actual.endpoint_domain_name : "mgw";
}
static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgcp, char *name)
{
static char endpoint[MGCP_ENDPOINT_MAXLEN];
int rc;
rc = snprintf(endpoint, sizeof(endpoint), "%s@%s", name, mgcp_client_endpoint_domain(mgcp));
if (rc > sizeof(endpoint) - 1) {
LOGP(DLMGCP, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '%s@%s'\n",
sizeof(endpoint) - 1, name, mgcp_client_endpoint_domain(mgcp));
return NULL;
}
if (rc < 1) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot compose MGCP endpoint name\n");
return NULL;
}
return endpoint;
}
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp)
{
return _mgcp_client_name_append_domain(mgcp, "rtpbridge/*");
}
struct mgcp_response_pending * mgcp_client_pending_add(
struct mgcp_client *mgcp,
mgcp_trans_id_t trans_id,
@@ -908,7 +969,7 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
msgb_free(msg);
goto mgcp_tx_error;
} else
LOGP(DLMGCP, LOGL_INFO, "Queued %u bytes for MGCP GW\n",
LOGP(DLMGCP, LOGL_DEBUG, "Queued %u bytes for MGCP GW\n",
msgb_l2len(msg));
return 0;
@@ -932,10 +993,10 @@ int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id)
struct mgcp_response_pending *pending = mgcp_client_response_pending_get(mgcp, trans_id);
if (!pending) {
/*! Note: it is not harmful to cancel a transaction twice. */
LOGP(DLMGCP, LOGL_INFO, "Cannot cancel, no such transaction: %u\n", trans_id);
LOGP(DLMGCP, LOGL_ERROR, "Cannot cancel, no such transaction: %u\n", trans_id);
return -ENOENT;
}
LOGP(DLMGCP, LOGL_INFO, "Canceled transaction %u\n", trans_id);
LOGP(DLMGCP, LOGL_DEBUG, "Canceled transaction %u\n", trans_id);
talloc_free(pending);
return 0;
/*! We don't really need to clean up the wqueue: In all sane cases, the msgb has already been sent
@@ -947,54 +1008,6 @@ int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id)
*/
}
static struct msgb *mgcp_msg_from_buf(mgcp_trans_id_t trans_id,
const char *buf, int len)
{
struct msgb *msg;
if (len > (4096 - 128)) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot send to MGCP GW:"
" message too large: %d\n", len);
return NULL;
}
msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
OSMO_ASSERT(msg);
char *dst = (char*)msgb_put(msg, len);
memcpy(dst, buf, len);
msg->l2h = msg->data;
msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
return msg;
}
static struct msgb *mgcp_msg_from_str(mgcp_trans_id_t trans_id,
const char *fmt, ...)
{
static char compose[4096 - 128];
va_list ap;
int len;
OSMO_ASSERT(fmt);
va_start(ap, fmt);
len = vsnprintf(compose, sizeof(compose), fmt, ap);
va_end(ap);
if (len >= sizeof(compose)) {
LOGP(DLMGCP, LOGL_ERROR,
"Message too large: trans_id=%u len=%d\n",
trans_id, len);
return NULL;
}
if (len < 1) {
LOGP(DLMGCP, LOGL_ERROR,
"Failed to compose message: trans_id=%u len=%d\n",
trans_id, len);
return NULL;
}
return mgcp_msg_from_buf(trans_id, compose, len);
}
static mgcp_trans_id_t mgcp_client_next_trans_id(struct mgcp_client *mgcp)
{
/* avoid zero trans_id to distinguish from unset trans_id */
@@ -1003,52 +1016,6 @@ static mgcp_trans_id_t mgcp_client_next_trans_id(struct mgcp_client *mgcp)
return mgcp->next_trans_id ++;
}
struct msgb *mgcp_msg_crcx(struct mgcp_client *mgcp,
uint16_t rtp_endpoint, unsigned int call_id,
enum mgcp_connection_mode mode)
{
mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
return mgcp_msg_from_str(trans_id,
"CRCX %u %x@mgw MGCP 1.0\r\n"
"C: %x\r\n"
"L: p:20, a:AMR, nt:IN\r\n"
"M: %s\r\n"
,
trans_id,
rtp_endpoint,
call_id,
mgcp_client_cmode_name(mode));
}
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)
{
mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
return mgcp_msg_from_str(trans_id,
"MDCX %u %x@mgw MGCP 1.0\r\n"
"M: %s\r\n"
"\r\n"
"c=IN IP4 %s\r\n"
"m=audio %u RTP/AVP 255\r\n"
,
trans_id,
rtp_endpoint,
mgcp_client_cmode_name(mode),
rtp_conn_addr,
rtp_port);
}
struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
unsigned int call_id)
{
mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
return mgcp_msg_from_str(trans_id,
"DLCX %u %x@mgw MGCP 1.0\r\n"
"C: %x\r\n", trans_id, rtp_endpoint, call_id);
}
#define MGCP_CRCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
MGCP_MSG_PRESENCE_CALL_ID | \
MGCP_MSG_PRESENCE_CONN_MODE)
@@ -1076,8 +1043,8 @@ static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
rc += msgb_printf(msg, " a:");
for (i = 0; i < mgcp_msg->codecs_len; i++) {
pt = mgcp_msg->codecs[i];
codec = get_value_string_or_null(codec_table, pt);
codec = get_value_string_or_null(osmo_mgcpc_codec_names, pt);
/* Note: Use codec descriptors from enum mgcp_codecs
* in mgcp_client only! */
OSMO_ASSERT(codec);
@@ -1149,23 +1116,37 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
}
rc += msgb_printf(msg, "\r\n");
/* Add optional codec parameters (fmtp) */
if (mgcp_msg->param_present) {
for (i = 0; i < mgcp_msg->codecs_len; i++) {
/* The following is only applicable for AMR */
if (mgcp_msg->codecs[i] != CODEC_AMR_8000_1 && mgcp_msg->codecs[i] != CODEC_AMRWB_16000_1)
continue;
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
if (mgcp_msg->param.amr_octet_aligned_present && mgcp_msg->param.amr_octet_aligned)
rc += msgb_printf(msg, "a=fmtp:%u octet-align=1\r\n", pt);
else if (mgcp_msg->param.amr_octet_aligned_present && !mgcp_msg->param.amr_octet_aligned)
rc += msgb_printf(msg, "a=fmtp:%u octet-align=0\r\n", pt);
}
}
for (i = 0; i < mgcp_msg->codecs_len; i++) {
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
/* Note: Only dynamic payload type from the range 96-127
* require to be explained further via rtpmap. All others
* are implcitly definedby the number in m=audio */
if (pt >= 96 && pt <= 127) {
codec = get_value_string_or_null(codec_table, mgcp_msg->codecs[i]);
codec = get_value_string_or_null(osmo_mgcpc_codec_names, mgcp_msg->codecs[i]);
/* Note: Use codec descriptors from enum mgcp_codecs
* in mgcp_client only! */
OSMO_ASSERT(codec);
rc += msgb_printf(msg, "a=rtpmap:%u %s\r\n", pt, codec);
}
}
if (mgcp_msg->ptime)
rc += msgb_printf(msg, "a=ptime:%u\r\n", mgcp_msg->ptime);
@@ -1184,6 +1165,7 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
int rc = 0;
int rc_sdp;
bool use_sdp = false;
char buf[32];
msg->l2h = msg->data;
msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
@@ -1280,6 +1262,29 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
msgb_printf(msg, "M: %s\r\n",
mgcp_client_cmode_name(mgcp_msg->conn_mode));
/* Add X-Osmo-IGN */
if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_X_OSMO_IGN)
&& (mgcp_msg->x_osmo_ign != 0))
rc +=
msgb_printf(msg, MGCP_X_OSMO_IGN_HEADER "%s\r\n",
mgcp_msg->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID ? " C": "");
/* Add X-Osmo-Osmux */
if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID)) {
if (mgcp_msg->x_osmo_osmux_cid < -1 || mgcp_msg->x_osmo_osmux_cid > OSMUX_CID_MAX) {
LOGP(DLMGCP, LOGL_ERROR,
"Wrong Osmux CID %d, can not generate MGCP message\n",
mgcp_msg->x_osmo_osmux_cid);
msgb_free(msg);
return NULL;
}
snprintf(buf, sizeof(buf), " %d", mgcp_msg->x_osmo_osmux_cid);
rc +=
msgb_printf(msg, MGCP_X_OSMO_OSMUX_HEADER "%s\r\n",
mgcp_msg->x_osmo_osmux_cid == -1 ? " *": buf);
}
/* Add session description protocol (SDP) */
if (use_sdp
&& (mgcp_msg->verb == MGCP_VERB_CRCX

View File

@@ -0,0 +1,886 @@
/* FSM to manage multiple connections of an MGW endpoint
*
* (C) 2018-2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Neels Hofmeyr <neels@hofmeyr.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/byteswap.h>
#include <osmocom/core/tdef.h>
#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
#define LOG_CI(ci, level, fmt, args...) do { \
if (!ci || !ci->ep) \
LOGP(DLGLOBAL, level, "(unknown MGW endpoint) " fmt, ## args); \
else \
LOG_MGCPC_EP(ci->ep, level, "CI[%d] %s%s%s: " fmt, \
(int)(ci - ci->ep->ci), \
ci->label ? : "-", \
ci->mgcp_ci_str[0] ? " CI=" : "", \
ci->mgcp_ci_str[0] ? ci->mgcp_ci_str : "", \
## args); \
} while(0)
#define LOG_CI_VERB(ci, level, fmt, args...) do { \
if (ci->verb_info.addr[0]) \
LOG_CI(ci, level, "%s %s:%u: " fmt, \
osmo_mgcp_verb_name(ci->verb), ci->verb_info.addr, ci->verb_info.port, \
## args); \
else \
LOG_CI(ci, level, "%s: " fmt, \
osmo_mgcp_verb_name(ci->verb), \
## args); \
} while(0)
enum osmo_mgcpc_ep_fsm_state {
OSMO_MGCPC_EP_ST_UNUSED = 0,
OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE,
OSMO_MGCPC_EP_ST_IN_USE,
};
enum osmo_mgcpc_ep_fsm_event {
_OSMO_MGCPC_EP_EV_LAST = 0,
/* and MGW response events are allocated dynamically */
};
#define FIRST_CI_EVENT (_OSMO_MGCPC_EP_EV_LAST + (_OSMO_MGCPC_EP_EV_LAST & 1)) /* rounded up to even nr */
#define USABLE_CI ((32 - FIRST_CI_EVENT)/2)
#define EV_TO_CI_IDX(event) ((event - FIRST_CI_EVENT) / 2)
#define CI_EV_SUCCESS(ci) (FIRST_CI_EVENT + (((ci) - ci->ep->ci) * 2))
#define CI_EV_FAILURE(ci) (CI_EV_SUCCESS(ci) + 1)
static struct osmo_fsm osmo_mgcpc_ep_fsm;
/*! One connection on an endpoint, corresponding to a connection identifier (CI) as returned by the MGW.
* An endpoint has a fixed number of slots of these, which may or may not be in use.
*/
struct osmo_mgcpc_ep_ci {
struct osmo_mgcpc_ep *ep;
bool occupied;
char label[64];
struct osmo_fsm_inst *mgcp_client_fi;
bool pending;
bool sent;
enum mgcp_verb verb;
struct mgcp_conn_peer verb_info;
struct osmo_fsm_inst *notify;
uint32_t notify_success;
uint32_t notify_failure;
void *notify_data;
bool got_port_info;
struct mgcp_conn_peer rtp_info;
char mgcp_ci_str[MGCP_CONN_ID_LENGTH];
};
/*! An MGW endpoint with N connections, like "rtpbridge/23@mgw". */
struct osmo_mgcpc_ep {
/*! MGCP client connection to the MGW. */
struct mgcp_client *mgcp_client;
struct osmo_fsm_inst *fi;
/*! Endpoint string; at first this might be a wildcard, and upon the first CRCX OK response, this will reflect
* the endpoint name returned by the MGW. */
char endpoint[MGCP_ENDPOINT_MAXLEN];
/*! Timeout definitions used for this endpoint, see osmo_mgcpc_ep_fsm_timeouts. */
const struct osmo_tdef *T_defs;
/*! True as soon as the first CRCX OK is received. The endpoint name may be determined by the first CRCX
* response, so to dispatch any other messages, the FSM instance *must* wait for the first CRCX OK to arrive
* first. Once the endpoint name is pinpointed, any amount of operations may be dispatched concurrently. */
bool first_crcx_complete;
/*! Endpoint connection slots. Note that each connection has its own set of FSM event numbers to signal success
* and failure, depending on its index within this array. See CI_EV_SUCCESS and CI_EV_FAILURE. */
struct osmo_mgcpc_ep_ci ci[USABLE_CI];
};
const struct value_string osmo_mgcp_verb_names[] = {
{ MGCP_VERB_CRCX, "CRCX" },
{ MGCP_VERB_MDCX, "MDCX" },
{ MGCP_VERB_DLCX, "DLCX" },
{ MGCP_VERB_AUEP, "AUEP" },
{ MGCP_VERB_RSIP, "RSIP" },
{}
};
static void osmo_mgcpc_ep_count(struct osmo_mgcpc_ep *ep, int *occupied, int *pending_not_sent,
int *waiting_for_response);
static struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_check_ci(struct osmo_mgcpc_ep_ci *ci)
{
if (!ci)
return NULL;
if (!ci->ep)
return NULL;
if (ci < ci->ep->ci || ci >= &ci->ep->ci[USABLE_CI])
return NULL;
return ci;
}
static struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_ci_for_event(struct osmo_mgcpc_ep *ep, uint32_t event)
{
int idx;
if (event < FIRST_CI_EVENT)
return NULL;
idx = EV_TO_CI_IDX(event);
if (idx >= sizeof(ep->ci))
return NULL;
return osmo_mgcpc_ep_check_ci(&ep->ci[idx]);
}
const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep)
{
if (!ep)
return "NULL";
if (ep->endpoint[0])
return ep->endpoint;
return osmo_fsm_inst_name(ep->fi);
}
const char *mgcp_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;
}
const char *osmo_mgcpc_ep_ci_name(const struct osmo_mgcpc_ep_ci *ci)
{
const struct mgcp_conn_peer *rtp_info;
if (!ci)
return "NULL";
rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(ci);
if (rtp_info)
return mgcp_conn_peer_name(rtp_info);
return osmo_mgcpc_ep_name(ci->ep);
}
const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci)
{
if (!ci || !ci->mgcp_ci_str[0])
return NULL;
return ci->mgcp_ci_str;
}
static struct value_string osmo_mgcpc_ep_fsm_event_names[33] = {};
static char osmo_mgcpc_ep_fsm_event_name_bufs[32][32] = {};
static void fill_event_names()
{
int i;
for (i = 0; i < (ARRAY_SIZE(osmo_mgcpc_ep_fsm_event_names) - 1); i++) {
if (i < _OSMO_MGCPC_EP_EV_LAST)
continue;
if (i < FIRST_CI_EVENT || EV_TO_CI_IDX(i) > USABLE_CI) {
osmo_mgcpc_ep_fsm_event_names[i] = (struct value_string){i, "Unused"};
continue;
}
snprintf(osmo_mgcpc_ep_fsm_event_name_bufs[i], sizeof(osmo_mgcpc_ep_fsm_event_name_bufs[i]),
"MGW Response for CI #%d", EV_TO_CI_IDX(i));
osmo_mgcpc_ep_fsm_event_names[i] = (struct value_string){i, osmo_mgcpc_ep_fsm_event_name_bufs[i]};
}
}
/* T_defs is used to obtain an (Osmocom specific) T2427001: timeout for an MGCP response (note, 2427 corresponds to the
* default MGCP port in osmo-mgw). */
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init()
{
OSMO_ASSERT(osmo_fsm_register(&osmo_mgcpc_ep_fsm) == 0);
fill_event_names();
}
struct osmo_mgcpc_ep *osmo_mgcpc_ep_fi_mgwep(struct osmo_fsm_inst *fi)
{
OSMO_ASSERT(fi);
OSMO_ASSERT(fi->fsm == &osmo_mgcpc_ep_fsm);
OSMO_ASSERT(fi->priv);
return fi->priv;
}
/*! Allocate an osmo_mgcpc_ep FSM.
* MGCP messages to set up the endpoint will be sent on the given mgcp_client, as soon as the first
* osmo_mgcpc_ep_ci_request() is invoked.
*
* A typical sequence of events would be:
*
* ep = osmo_mgcpc_ep_alloc(..., mgcp_client_rtpbridge_wildcard(client));
* ci_to_ran = osmo_mgcpc_ep_ci_add(ep);
* osmo_mgcpc_ep_ci_request(ci_to_ran, MGCP_VERB_CRCX, verb_info,
* my_call_fsm, MY_EVENT_MGCP_OK, MY_EVENT_MGCP_FAIL);
* ci_to_cn = osmo_mgcpc_ep_ci_add(ep);
* osmo_mgcpc_ep_ci_request(ci_to_cn, MGCP_VERB_CRCX, verb_info,
* my_call_fsm, MY_EVENT_MGCP_OK, MY_EVENT_MGCP_FAIL);
* ...
* osmo_mgcpc_ep_ci_request(ci_to_ran, MGCP_VERB_MDCX, ...);
* ...
* osmo_mgcpc_ep_clear(ep);
* ep = NULL;
*
* \param parent Parent FSM.
* \param parent_term_event Event to dispatch to the parent on termination of this FSM instance.
* \param mgcp_client Connection to the MGW.
* \param T_defs Timeout definitions to be used for FSM states, see osmo_mgcpc_ep_fsm_timeouts.
* \param fsm_id FSM instance ID.
* \param endpoint_str_fmt The endpoint string format to send to the MGW upon the first CRCX.
* See mgcp_client_rtpbridge_wildcard() for "rtpbridge" endpoints.
*/
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, ...)
{
va_list ap;
struct osmo_fsm_inst *fi;
struct osmo_mgcpc_ep *ep;
int rc;
if (!mgcp_client)
return NULL;
fi = osmo_fsm_inst_alloc_child(&osmo_mgcpc_ep_fsm, parent, parent_term_event);
OSMO_ASSERT(fi);
osmo_fsm_inst_update_id(fi, fsm_id);
ep = talloc_zero(fi, struct osmo_mgcpc_ep);
OSMO_ASSERT(ep);
*ep = (struct osmo_mgcpc_ep){
.mgcp_client = mgcp_client,
.fi = fi,
.T_defs = T_defs,
};
fi->priv = ep;
va_start(ap, endpoint_str_fmt);
rc = vsnprintf(ep->endpoint, sizeof(ep->endpoint), endpoint_str_fmt ? : "", ap);
va_end(ap);
if (rc <= 0 || rc >= sizeof(ep->endpoint)) {
LOG_MGCPC_EP(ep, LOGL_ERROR, "Endpoint name too long or too short: %s\n",
ep->endpoint);
osmo_fsm_inst_term(ep->fi, OSMO_FSM_TERM_ERROR, 0);
return NULL;
}
return ep;
}
/*! Add a connection to an endpoint.
* Allocate a connection identifier slot in the osmo_mgcpc_ep instance, do not yet dispatch a CRCX.
* The CRCX is dispatched only upon the first osmo_mgcpc_ep_ci_request().
* \param ep Parent endpoint instance.
* \param label_fmt Label for logging.
*/
struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_ci_add(struct osmo_mgcpc_ep *ep,
const char *label_fmt, ...)
{
va_list ap;
int i;
struct osmo_mgcpc_ep_ci *ci;
for (i = 0; i < USABLE_CI; i++) {
ci = &ep->ci[i];
if (ci->occupied || ci->mgcp_client_fi)
continue;
*ci = (struct osmo_mgcpc_ep_ci){
.ep = ep,
.occupied = true,
};
if (label_fmt) {
va_start(ap, label_fmt);
vsnprintf(ci->label, sizeof(ci->label), label_fmt, ap);
va_end(ap);
}
return ci;
}
LOG_MGCPC_EP(ep, LOGL_ERROR,
"Cannot allocate another endpoint, all "
OSMO_STRINGIFY_VAL(USABLE_CI) " are in use\n");
return NULL;
}
static bool osmo_mgcpc_ep_fsm_check_state_chg_after_response(struct osmo_fsm_inst *fi);
static void on_failure(struct osmo_mgcpc_ep_ci *ci)
{
struct osmo_fsm_inst *notify = ci->notify;
uint32_t notify_failure = ci->notify_failure;
void *notify_data = ci->notify_data;
if (!ci->occupied)
return;
*ci = (struct osmo_mgcpc_ep_ci){
.ep = ci->ep,
};
/* If this check has terminated the FSM instance, don't fire any more events to prevent use-after-free problems.
* The endpoint FSM does dispatch a term event to its parent, and everything should be cleaned like that. */
if (!osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi))
return;
if (notify)
osmo_fsm_inst_dispatch(notify, notify_failure, notify_data);
}
static int update_endpoint_name(struct osmo_mgcpc_ep_ci *ci, const char *new_endpoint_name)
{
struct osmo_mgcpc_ep *ep = ci->ep;
int rc;
int i;
if (!strcmp(ep->endpoint, new_endpoint_name)) {
/* Same endpoint name, nothing to do. */
return 0;
}
/* The endpoint name should only change on the very first CRCX response. */
if (ep->first_crcx_complete) {
LOG_CI(ci, LOGL_ERROR, "Reponse returned mismatching endpoint name."
" This is endpoint %s, instead received %s\n",
ep->endpoint, new_endpoint_name);
on_failure(ci);
return -EINVAL;
}
/* This is the first CRCX response, update endpoint name. */
rc = OSMO_STRLCPY_ARRAY(ep->endpoint, new_endpoint_name);
if (rc <= 0 || rc >= sizeof(ep->endpoint)) {
LOG_CI(ci, LOGL_ERROR, "Unable to copy endpoint name %s\n", osmo_quote_str(new_endpoint_name, -1));
osmo_mgcpc_ep_ci_dlcx(ci);
on_failure(ci);
return -ENOSPC;
}
/* Make sure already pending requests use this updated endpoint name. */
for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
struct osmo_mgcpc_ep_ci *other_ci = &ep->ci[i];
if (!other_ci->occupied)
continue;
if (!other_ci->pending)
continue;
if (other_ci->sent)
continue;
OSMO_STRLCPY_ARRAY(other_ci->verb_info.endpoint, ep->endpoint);
}
return 0;
}
static void on_success(struct osmo_mgcpc_ep_ci *ci, void *data)
{
struct mgcp_conn_peer *rtp_info;
if (!ci->occupied)
return;
ci->pending = false;
switch (ci->verb) {
case MGCP_VERB_CRCX:
/* If we sent a wildcarded endpoint name on CRCX, we need to store the resulting endpoint
* name here. Also, we receive the MGW's RTP port information. */
rtp_info = data;
OSMO_ASSERT(rtp_info);
ci->got_port_info = true;
ci->rtp_info = *rtp_info;
osmo_strlcpy(ci->mgcp_ci_str, mgcp_conn_get_ci(ci->mgcp_client_fi),
sizeof(ci->mgcp_ci_str));
if (rtp_info->endpoint[0]) {
/* On errors, this instance might already be deallocated. Make sure to not access anything after
* error. */
if (update_endpoint_name(ci, rtp_info->endpoint))
return;
}
ci->ep->first_crcx_complete = true;
break;
default:
break;
}
LOG_CI(ci, LOGL_DEBUG, "received successful response to %s: RTP=%s%s\n",
osmo_mgcp_verb_name(ci->verb),
mgcp_conn_peer_name(ci->got_port_info? &ci->rtp_info : NULL),
ci->notify ? "" : " (not sending a notification)");
if (ci->notify)
osmo_fsm_inst_dispatch(ci->notify, ci->notify_success, ci->notify_data);
osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi);
}
/*! Return the MGW's RTP port information for this connection, as returned by the last CRCX/MDCX OK message. */
const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgcpc_ep_ci *ci)
{
ci = osmo_mgcpc_ep_check_ci((struct osmo_mgcpc_ep_ci*)ci);
if (!ci)
return NULL;
if (!ci->got_port_info)
return NULL;
return &ci->rtp_info;
}
/*! Return the MGW's RTP port information for this connection, as returned by the last CRCX/MDCX OK message. */
bool osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(const struct osmo_mgcpc_ep_ci *ci, struct sockaddr_storage *dest)
{
const struct mgcp_conn_peer *rtp_info;
struct sockaddr_in *sin;
rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(ci);
if (!rtp_info)
return false;
sin = (struct sockaddr_in *)dest;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = inet_addr(rtp_info->addr);
sin->sin_port = osmo_ntohs(rtp_info->port);
return true;
}
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 *rtp_info;
rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(ci);
if (!rtp_info)
return false;
if (!rtp_info->x_osmo_osmux_use)
return false;
*cid = rtp_info->x_osmo_osmux_cid;
return true;
}
static const struct osmo_tdef_state_timeout osmo_mgcpc_ep_fsm_timeouts[32] = {
[OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE] = { .T=2427001 },
};
/* Transition to a state, using the T timer defined in assignment_fsm_timeouts.
* The actual timeout value is in turn obtained from osmo_mgcpc_ep.T_defs.
* Assumes local variable fi exists. */
#define osmo_mgcpc_ep_fsm_state_chg(state) \
osmo_tdef_fsm_inst_state_chg(fi, state, osmo_mgcpc_ep_fsm_timeouts, \
((struct osmo_mgcpc_ep*)fi->priv)->T_defs, 5)
/*! Dispatch an actual CRCX/MDCX/DLCX message for this connection.
* \param ci Connection identifier as obtained from osmo_mgcpc_ep_ci_add().
* \param verb MGCP operation to dispatch.
* \param verb_info Parameters for the MGCP operation.
* \param notify Peer FSM instance to notify of completed/failed operation.
* \param event_success Which event to dispatch to 'notify' upon OK response.
* \param event_failure Which event to dispatch to 'notify' upon failure response.
* \param notify_data Data pointer to pass to the event dispatch for both success and failure.
*/
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)
{
struct osmo_mgcpc_ep *ep;
struct osmo_fsm_inst *fi;
struct osmo_mgcpc_ep_ci cleared_ci;
ci = osmo_mgcpc_ep_check_ci(ci);
if (!ci) {
LOGP(DLGLOBAL, LOGL_ERROR, "Invalid MGW endpoint request: no ci\n");
goto dispatch_error;
}
if (!verb_info && verb != MGCP_VERB_DLCX) {
LOG_CI(ci, LOGL_ERROR, "Invalid MGW endpoint request: missing verb details for %s\n",
osmo_mgcp_verb_name(verb));
goto dispatch_error;
}
if ((verb < 0) || (verb > MGCP_VERB_RSIP)) {
LOG_CI(ci, LOGL_ERROR, "Invalid MGW endpoint request: unknown verb: %s\n",
osmo_mgcp_verb_name(verb));
goto dispatch_error;
}
ep = ci->ep;
fi = ep->fi;
/* Clear volatile state by explicitly keeping those that should remain. Because we can't assign
* the char[] directly, dance through cleared_ci and copy back. */
cleared_ci = (struct osmo_mgcpc_ep_ci){
.ep = ep,
.mgcp_client_fi = ci->mgcp_client_fi,
.got_port_info = ci->got_port_info,
.rtp_info = ci->rtp_info,
.occupied = true,
/* .pending = true follows below */
.verb = verb,
.notify = notify,
.notify_success = event_success,
.notify_failure = event_failure,
.notify_data = notify_data,
};
osmo_strlcpy(cleared_ci.label, ci->label, sizeof(cleared_ci.label));
osmo_strlcpy(cleared_ci.mgcp_ci_str, ci->mgcp_ci_str, sizeof(cleared_ci.mgcp_ci_str));
*ci = cleared_ci;
LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify));
if (verb_info)
ci->verb_info = *verb_info;
if (ep->endpoint[0]) {
if (ci->verb_info.endpoint[0] && strcmp(ci->verb_info.endpoint, ep->endpoint))
LOG_CI(ci, LOGL_ERROR,
"Warning: Requested %s on endpoint %s, but this CI is on endpoint %s."
" Using the proper endpoint instead.\n",
osmo_mgcp_verb_name(verb), ci->verb_info.endpoint, ep->endpoint);
osmo_strlcpy(ci->verb_info.endpoint, ep->endpoint, sizeof(ci->verb_info.endpoint));
}
switch (ci->verb) {
case MGCP_VERB_CRCX:
if (ci->mgcp_client_fi) {
LOG_CI(ci, LOGL_ERROR, "CRCX can be called only once per MGW endpoint CI\n");
on_failure(ci);
return;
}
break;
case MGCP_VERB_MDCX:
if (!ci->mgcp_client_fi) {
LOG_CI_VERB(ci, LOGL_ERROR, "The first verb on an unused MGW endpoint CI must be CRCX, not %s\n",
osmo_mgcp_verb_name(ci->verb));
on_failure(ci);
return;
}
break;
case MGCP_VERB_DLCX:
if (!ci->mgcp_client_fi) {
LOG_CI_VERB(ci, LOGL_DEBUG, "Ignoring DLCX on unused MGW endpoint CI\n");
return;
}
break;
default:
LOG_CI(ci, LOGL_ERROR, "This verb is not supported: %s\n", osmo_mgcp_verb_name(ci->verb));
on_failure(ci);
return;
}
ci->pending = true;
LOG_CI_VERB(ci, LOGL_DEBUG, "Scheduling\n");
if (ep->fi->state != OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE)
osmo_mgcpc_ep_fsm_state_chg(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE);
return;
dispatch_error:
if (notify)
osmo_fsm_inst_dispatch(notify, event_failure, notify_data);
}
static int send_verb(struct osmo_mgcpc_ep_ci *ci)
{
int rc;
struct osmo_mgcpc_ep *ep = ci->ep;
if (!ci->occupied || !ci->pending || ci->sent)
return 0;
switch (ci->verb) {
case MGCP_VERB_CRCX:
OSMO_ASSERT(!ci->mgcp_client_fi);
LOG_CI_VERB(ci, LOGL_DEBUG, "Sending\n");
ci->mgcp_client_fi = mgcp_conn_create(ep->mgcp_client, ep->fi,
CI_EV_FAILURE(ci), CI_EV_SUCCESS(ci),
&ci->verb_info);
ci->sent = true;
if (!ci->mgcp_client_fi){
LOG_CI_VERB(ci, LOGL_ERROR, "Cannot send\n");
on_failure(ci);
return -EINVAL;
}
osmo_fsm_inst_update_id(ci->mgcp_client_fi, ci->label);
break;
case MGCP_VERB_MDCX:
OSMO_ASSERT(ci->mgcp_client_fi);
LOG_CI_VERB(ci, LOGL_DEBUG, "Sending\n");
rc = mgcp_conn_modify(ci->mgcp_client_fi, CI_EV_SUCCESS(ci), &ci->verb_info);
ci->sent = true;
if (rc) {
LOG_CI_VERB(ci, LOGL_ERROR, "Cannot send (rc=%d %s)\n", rc, strerror(-rc));
on_failure(ci);
return -EINVAL;
}
break;
case MGCP_VERB_DLCX:
LOG_CI(ci, LOGL_DEBUG, "Sending MGCP: %s %s\n",
osmo_mgcp_verb_name(ci->verb), ci->mgcp_ci_str);
/* The way this is designed, we actually need to forget all about the ci right away. */
mgcp_conn_delete(ci->mgcp_client_fi);
if (ci->notify)
osmo_fsm_inst_dispatch(ci->notify, ci->notify_success, ci->notify_data);
*ci = (struct osmo_mgcpc_ep_ci){
.ep = ep,
};
break;
default:
OSMO_ASSERT(false);
}
return 1;
}
/*! DLCX all connections, terminate the endpoint FSM and free. */
void osmo_mgcpc_ep_clear(struct osmo_mgcpc_ep *ep)
{
if (!ep)
return;
osmo_fsm_inst_term(ep->fi, OSMO_FSM_TERM_REGULAR, 0);
}
static void osmo_mgcpc_ep_count(struct osmo_mgcpc_ep *ep, int *occupied, int *pending_not_sent,
int *waiting_for_response)
{
int i;
if (occupied)
*occupied = 0;
if (pending_not_sent)
*pending_not_sent = 0;
if (waiting_for_response)
*waiting_for_response = 0;
for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
if (ci->occupied) {
if (occupied)
(*occupied)++;
} else
continue;
if (ci->pending)
LOG_CI_VERB(ci, LOGL_DEBUG, "%s\n",
ci->sent ? "waiting for response" : "waiting to be sent");
else
LOG_CI_VERB(ci, LOGL_DEBUG, "done (%s)\n", mgcp_conn_peer_name(osmo_mgcpc_ep_ci_get_rtp_info(ci)));
if (ci->pending && ci->sent)
if (waiting_for_response)
(*waiting_for_response)++;
if (ci->pending && !ci->sent)
if (pending_not_sent)
(*pending_not_sent)++;
}
}
static bool osmo_mgcpc_ep_fsm_check_state_chg_after_response(struct osmo_fsm_inst *fi)
{
int waiting_for_response;
int occupied;
struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
osmo_mgcpc_ep_count(ep, &occupied, NULL, &waiting_for_response);
LOG_MGCPC_EP(ep, LOGL_DEBUG, "CI in use: %d, waiting for response: %d\n", occupied, waiting_for_response);
if (!occupied) {
/* All CI have been released. The endpoint no longer exists. Notify the parent FSM, by
* terminating. */
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, 0);
return false;
}
if (!waiting_for_response) {
if (fi->state != OSMO_MGCPC_EP_ST_IN_USE)
osmo_mgcpc_ep_fsm_state_chg(OSMO_MGCPC_EP_ST_IN_USE);
}
return true;
}
static void osmo_mgcpc_ep_fsm_wait_mgw_response_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
static int re_enter = 0;
int rc;
int count = 0;
int i;
struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
re_enter++;
OSMO_ASSERT(re_enter < 10);
/* The first CRCX gives us the endpoint name in the CRCX response. So we must wait for the first CRCX endpoint
* response to come in before sending any other MGCP requests -- otherwise we might end up creating new
* endpoints instead of acting on the same. This FSM always sends out N requests and waits for all of them to
* complete before sending out new requests. Hence we're safe when the very first time at most one request is
* sent (which needs to be a CRCX). */
for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
/* Make sure that only CRCX get dispatched if no CRCX were sent yet. */
if (!ep->first_crcx_complete) {
if (ci->occupied && ci->verb != MGCP_VERB_CRCX)
continue;
}
rc = send_verb(&ep->ci[i]);
/* Need to be careful not to access the instance after failure. Event chains may already have
* deallocated this memory. */
if (rc < 0)
return;
if (!rc)
continue;
count++;
/* Make sure that we wait for the first CRCX response before dispatching more requests. */
if (!ep->first_crcx_complete)
break;
}
LOG_MGCPC_EP(ep, LOGL_DEBUG, "Sent messages: %d\n", count);
if (ep->first_crcx_complete)
osmo_mgcpc_ep_fsm_check_state_chg_after_response(fi);
re_enter--;
}
static void osmo_mgcpc_ep_fsm_handle_ci_events(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct osmo_mgcpc_ep_ci *ci;
struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
ci = osmo_mgcpc_ep_ci_for_event(ep, event);
if (ci) {
if (event == CI_EV_SUCCESS(ci))
on_success(ci, data);
else
on_failure(ci);
}
}
static void osmo_mgcpc_ep_fsm_in_use_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
int pending_not_sent;
struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
osmo_mgcpc_ep_count(ep, NULL, &pending_not_sent, NULL);
if (pending_not_sent)
osmo_mgcpc_ep_fsm_state_chg(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE);
}
#define S(x) (1 << (x))
static const struct osmo_fsm_state osmo_mgcpc_ep_fsm_states[] = {
[OSMO_MGCPC_EP_ST_UNUSED] = {
.name = "UNUSED",
.in_event_mask = 0,
.out_state_mask = 0
| S(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE)
,
},
[OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE] = {
.name = "WAIT_MGW_RESPONSE",
.onenter = osmo_mgcpc_ep_fsm_wait_mgw_response_onenter,
.action = osmo_mgcpc_ep_fsm_handle_ci_events,
.in_event_mask = 0xffffffff,
.out_state_mask = 0
| S(OSMO_MGCPC_EP_ST_IN_USE)
,
},
[OSMO_MGCPC_EP_ST_IN_USE] = {
.name = "IN_USE",
.onenter = osmo_mgcpc_ep_fsm_in_use_onenter,
.action = osmo_mgcpc_ep_fsm_handle_ci_events,
.in_event_mask = 0xffffffff, /* mgcp_client_fsm may send parent term anytime */
.out_state_mask = 0
| S(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE)
,
},
};
static int osmo_mgcpc_ep_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
int i;
struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
switch (fi->T) {
default:
for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
if (!ci->occupied)
continue;
if (!(ci->pending && ci->sent))
continue;
on_failure(ci);
}
return 0;
}
return 0;
}
static struct osmo_fsm osmo_mgcpc_ep_fsm = {
.name = "mgw-endp",
.states = osmo_mgcpc_ep_fsm_states,
.num_states = ARRAY_SIZE(osmo_mgcpc_ep_fsm_states),
.log_subsys = DLMGCP,
.event_names = osmo_mgcpc_ep_fsm_event_names,
.timer_cb = osmo_mgcpc_ep_fsm_timer_cb,
/* The FSM termination will automatically trigger any mgcp_client_fsm instances to DLCX. */
};

View File

@@ -41,7 +41,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 +104,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 +162,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 +206,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 +219,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);
@@ -262,6 +280,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 +404,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;
@@ -692,3 +721,25 @@ 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;
}

View File

@@ -30,7 +30,7 @@
#include <osmocom/mgcp_client/mgcp_client.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;
@@ -43,8 +43,9 @@ DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
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]);
osmo_talloc_replace_string(global_mgcp_client_ctx,
(char**)&global_mgcp_client_conf->local_addr,
argv[0]);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd,
@@ -75,8 +76,9 @@ DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
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]);
osmo_talloc_replace_string(global_mgcp_client_ctx,
(char**)&global_mgcp_client_conf->remote_addr,
argv[0]);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
@@ -99,23 +101,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 +119,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,13 +137,26 @@ ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
BTS_START_STR
UDP_PORT_STR)
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")
{
if (osmo_strlcpy(global_mgcp_client_conf->endpoint_domain_name, argv[0],
sizeof(global_mgcp_client_conf->endpoint_domain_name))
>= sizeof(global_mgcp_client_conf->endpoint_domain_name)) {
vty_out(vty, "%% Error: 'mgw endpoint-domain' name too long, max length is %zu: '%s'%s",
sizeof(global_mgcp_client_conf->endpoint_domain_name) - 1, argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
int mgcp_client_config_write(struct vty *vty, const char *indent)
{
const char *addr;
int port;
uint16_t first_endpoint;
uint16_t last_endpoint;
uint16_t bts_base;
addr = global_mgcp_client_conf->local_addr;
if (addr)
@@ -169,18 +176,9 @@ int mgcp_client_config_write(struct vty *vty, const char *indent)
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);
}
bts_base = global_mgcp_client_conf->bts_base;
if (bts_base) {
vty_out(vty, "%smgw bts-base %u%s", indent,
bts_base, VTY_NEWLINE);
}
if (global_mgcp_client_conf->endpoint_domain_name[0])
vty_out(vty, "%smgw endpoint-domain %s%s", indent,
global_mgcp_client_conf->endpoint_domain_name, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -196,6 +194,7 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
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);
install_element(node, &cfg_mgw_endpoint_domain_name_cmd);
/* deprecated 'mgcpgw' commands */
install_element(node, &cfg_mgcpgw_local_ip_cmd);

View File

@@ -56,8 +56,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 +65,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]");
@@ -99,9 +99,9 @@ void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
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)
/* Set members of struct mgcp_rtp_codec, extrapolate in missing information. Param audio_name is expected in uppercase. */
static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type, const char *audio_name,
unsigned int pt_offset, struct mgcp_codec_param *param)
{
int rate;
int channels;
@@ -219,6 +219,13 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
}
}
/* Copy over optional codec parameters */
if (param) {
codec->param = *param;
codec->param_present = true;
} else
codec->param_present = false;
return 0;
error:
/* Make sure we leave a clean codec entry on error. */
@@ -232,9 +239,10 @@ error:
* \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").
* \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)
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, struct mgcp_codec_param *param)
{
int rc;
@@ -244,7 +252,7 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud
return -EINVAL;
rc = codec_set(conn->conn, &conn->end.codecs[conn->end.codecs_assigned], payload_type, audio_name,
conn->end.codecs_assigned);
conn->end.codecs_assigned, param);
if (rc != 0)
return -EINVAL;
@@ -341,3 +349,70 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
return -EINVAL;
}
/* Compare two codecs, all parameters must match up, except for the payload type
* number. */
static bool codecs_cmp(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->audio_name, codec_b->audio_name))
return false;
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
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_cmp(codec_src, &rtp_dst->codecs[i])) {
codec_dst = &rtp_dst->codecs[i];
break;
}
}
if (!codec_dst)
return -EINVAL;
return codec_dst->payload_type;
}

View File

@@ -29,24 +29,15 @@
#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 +46,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,19 +70,18 @@ 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
@@ -99,7 +90,8 @@ static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn
static unsigned int 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;
@@ -117,23 +109,45 @@ static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn
end->maximum_packet_time = -1;
conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index);
if (!conn_rtp->rate_ctr_group)
return -1;
conn_rtp->state.in_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[IN_STREAM_ERR_TSTMP_CTR];
conn_rtp->state.out_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[OUT_STREAM_ERR_TSTMP_CTR];
rate_ctr_index++;
/* 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);
}
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->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.
* \param[in] ctx talloc context
* \param[in] endp associated endpoint
@@ -167,7 +181,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,6 +193,9 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
OSMO_ASSERT(false);
}
/* Initialize watchdog */
osmo_timer_setup(&conn->watchdog, mgcp_conn_watchdog_cb, conn);
mgcp_conn_watchdog_kick(conn);
llist_add(&conn->entry, &endp->conns);
return conn;
@@ -188,9 +208,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 +253,27 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
return NULL;
}
static void
aggregate_rtp_conn_stats(struct mgcp_trunk_config *trunk, struct mgcp_conn_rtp *conn_rtp)
{
struct rate_ctr_group *all_stats = trunk->all_rtp_conn_stats;
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
int i;
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);
for (i = 0; i < conn_stats->desc->num_ctr; i++)
rate_ctr_add(&all_stats->ctr[i], conn_stats->ctr[i].current);
rate_ctr_inc(&all_stats->ctr[RTP_NUM_CONNECTIONS]);
}
/*! free a connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection */
@@ -235,6 +293,7 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
aggregate_rtp_conn_stats(endp->tcfg, &conn->u.rtp);
mgcp_rtp_conn_cleanup(&conn->u.rtp);
break;
default:
@@ -244,6 +303,7 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
OSMO_ASSERT(false);
}
osmo_timer_del(&conn->watchdog);
llist_del(&conn->entry);
talloc_free(conn);
}

View File

@@ -36,8 +36,7 @@ const struct mgcp_endpoint_typeset ep_typeset = {
* \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

View File

@@ -82,9 +82,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,18 +91,17 @@ 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;
}
@@ -113,18 +111,15 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
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
@@ -197,9 +192,8 @@ static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints,
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));
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
"found free endpoint\n");
endp->wildcarded_req = true;
return endp;
}
@@ -219,8 +213,14 @@ static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
if (!domain_to_check)
return -EINVAL;
if (strcmp(domain_to_check+1, cfg->domain) != 0)
/* Accept any domain if configured as "*" */
if (!strcmp(cfg->domain, "*"))
return 0;
if (strcmp(domain_to_check+1, cfg->domain) != 0) {
LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s', expecting '%s'\n", mgcp, cfg->domain);
return -EINVAL;
}
return 0;
}
@@ -240,7 +240,7 @@ static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
/* 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);
*cause = -500;
return NULL;
}
@@ -320,11 +320,12 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
if (!pdata->endp) {
LOGP(DLMGCP, LOGL_ERROR,
"Unable to find Endpoint `%s'\n", elem);
OSMO_ASSERT(cause < 0);
return cause;
}
break;
case 2:
if (strcmp("MGCP", elem)) {
if (strcasecmp("MGCP", elem)) {
LOGP(DLMGCP, LOGL_ERROR,
"MGCP header parsing error\n");
return -510;
@@ -353,18 +354,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);
@@ -401,15 +411,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 +434,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

View File

@@ -15,10 +15,12 @@
#include <inttypes.h> /* for PRIu64 */
#include <netinet/in.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/talloc.h>
#include <osmocom/netif/osmux.h>
#include <osmocom/netif/rtp.h>
#include <osmocom/netif/amr.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_internal.h>
@@ -34,7 +36,7 @@ struct osmux_handle {
struct llist_head head;
struct osmux_in_handle *in;
struct in_addr rem_addr;
int rem_port;
int rem_port; /* network byte order */
int refcnt;
};
@@ -177,6 +179,12 @@ int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn)
memcpy(msg->data, buf, buf_len);
msgb_put(msg, buf_len);
if (conn->osmux.state != OSMUX_STATE_ENABLED) {
LOGPCONN(conn->conn, DLMGCP, LOGL_INFO, "forwarding RTP to Osmux conn not yet enabled, dropping (cid=%d)\n",
conn->osmux.cid);
return -1;
}
while ((ret = osmux_xfrm_input(conn->osmux.in, msg, conn->osmux.cid)) > 0) {
/* batch full, build and deliver it */
osmux_xfrm_input_deliver(conn->osmux.in);
@@ -185,111 +193,54 @@ int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn)
}
/* Lookup the endpoint that corresponds to the specified address (port) */
static struct mgcp_endpoint *
endpoint_lookup(struct mgcp_config *cfg, int cid,
struct in_addr *from_addr, int type)
static struct mgcp_conn_rtp*
osmux_conn_lookup(struct mgcp_config *cfg, uint8_t cid,
struct in_addr *from_addr)
{
struct mgcp_endpoint *endp = NULL;
struct mgcp_endpoint *endp;
struct mgcp_conn *conn = NULL;
struct mgcp_conn_rtp * conn_rtp;
int i;
struct mgcp_conn_rtp *conn_net = NULL;
struct mgcp_conn_rtp *conn_bts = NULL;
for (i=0; i<cfg->trunk.number_endpoints; i++) {
struct in_addr *this;
endp = &cfg->trunk.endpoints[i];
#if 0
if (!tmp->allocated)
continue;
#endif
llist_for_each_entry(conn, &endp->conns, entry) {
if (conn->type != MGCP_CONN_TYPE_RTP)
continue;
switch(type) {
case MGCP_DEST_NET:
/* FIXME: Get rid of CONN_ID_XXX! */
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (conn_net)
this = &conn_net->end.addr;
else
this = NULL;
break;
case MGCP_DEST_BTS:
/* FIXME: Get rid of CONN_ID_XXX! */
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
if (conn_bts)
this = &conn_bts->end.addr;
else
this = NULL;
break;
default:
/* Should not ever happen */
LOGP(DLMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type);
return NULL;
conn_rtp = &conn->u.rtp;
if (!mgcp_conn_rtp_is_osmux(conn_rtp))
continue;
if (conn_rtp->osmux.cid == cid)
return conn_rtp;
}
/* FIXME: Get rid of CONN_ID_XXX! */
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (conn_net && this && conn_net->osmux.cid == cid
&& this->s_addr == from_addr->s_addr)
return endp;
}
LOGP(DLMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n", cid);
LOGP(DLMGCP, LOGL_ERROR, "Cannot find osmux conn with cid=%d\n", cid);
return NULL;
}
static void scheduled_tx_net_cb(struct msgb *msg, void *data)
/* FIXME: this is declared and used in mgcp_network.c, but documentation of mgcp_dispatch_rtp_bridge_cb() states another enum is to be used */
enum {
MGCP_PROTO_RTP,
MGCP_PROTO_RTCP,
};
static void scheduled_from_osmux_tx_rtp_cb(struct msgb *msg, void *data)
{
struct mgcp_endpoint *endp = data;
struct mgcp_conn_rtp *conn_net = NULL;
struct mgcp_conn_rtp *conn_bts = NULL;
/* FIXME: Get rid of CONN_ID_XXX! */
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_bts || !conn_net)
return;
struct mgcp_conn_rtp *conn = data;
struct mgcp_endpoint *endp = conn->conn->endp;
struct sockaddr_in addr = {
.sin_addr = conn_net->end.addr,
.sin_port = conn_net->end.rtp_port,
};
.sin_addr = conn->end.addr,
.sin_port = conn->end.rtp_port,
}; /* FIXME: not set/used in cb */
rate_ctr_inc(&conn_bts->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
rate_ctr_add(&conn_bts->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], msg->len);
/* Send RTP data to NET */
/* FIXME: Get rid of conn_bts and conn_net! */
mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
conn_bts, conn_net);
msgb_free(msg);
}
static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
{
struct mgcp_endpoint *endp = data;
struct mgcp_conn_rtp *conn_net = NULL;
struct mgcp_conn_rtp *conn_bts = NULL;
/* FIXME: Get rid of CONN_ID_XXX! */
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_bts || !conn_net)
return;
struct sockaddr_in addr = {
.sin_addr = conn_bts->end.addr,
.sin_port = conn_bts->end.rtp_port,
};
rate_ctr_inc(&conn_net->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
rate_ctr_add(&conn_net->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], msg->len);
/* Send RTP data to BTS */
/* FIXME: Get rid of conn_bts and conn_net! */
mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
conn_net, conn_bts);
endp->type->dispatch_rtp_cb(MGCP_PROTO_RTP, &addr, (char *)msg->data, msg->len, conn->conn);
msgb_free(msg);
}
@@ -316,52 +267,47 @@ static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr)
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)
/* Updates endp osmux state and returns 0 if it can process messages, -1 otherwise */
static int endp_osmux_state_check(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
bool sending)
{
struct msgb *msg;
struct osmux_hdr *osmuxh;
struct sockaddr_in addr;
struct mgcp_config *cfg = ofd->data;
uint32_t rem;
struct mgcp_conn_rtp *conn_bts = NULL;
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);
/* FIXME: Get rid of CONN_ID_XXX! */
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
if (!conn_bts)
goto out;
if (!endp) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot find an endpoint for circuit_id=%d\n",
osmuxh->circuit_id);
goto out;
switch(conn->osmux.state) {
case OSMUX_STATE_ACTIVATING:
if (osmux_enable_conn(endp, conn, &conn->end.addr, conn->end.rtp_port) < 0) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"Could not enable osmux for conn on %s: %s\n",
sending ? "sent" : "received",
mgcp_conn_dump(conn->conn));
return -1;
}
conn_bts->osmux.stats.octets += osmux_chunk_length(msg, rem);
conn_bts->osmux.stats.chunks++;
rem = msg->len;
osmux_xfrm_output_sched(&conn_bts->osmux.out, osmuxh);
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"Osmux %s CID %u towards %s:%u is now enabled\n",
sending ? "sent" : "received",
conn->osmux.cid, inet_ntoa(conn->end.addr),
ntohs(conn->end.rtp_port));
return 0;
case OSMUX_STATE_ENABLED:
return 0;
default:
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"Osmux %s in conn %s without full negotiation, state %d\n",
sending ? "sent" : "received",
mgcp_conn_dump(conn->conn), conn->osmux.state);
return -1;
}
out:
msgb_free(msg);
}
static int osmux_legacy_dummy_parse_cid(struct sockaddr_in *addr, struct msgb *msg,
uint8_t *osmux_cid)
{
if (msg->len < 1 + sizeof(*osmux_cid)) {
LOGP(DLMGCP, LOGL_ERROR,
"Discarding truncated Osmux dummy load: %s\n", osmo_hexdump(msg->data, msg->len));
return -1;
}
/* extract the osmux CID from the dummy message */
memcpy(osmux_cid, &msg->data[1], sizeof(*osmux_cid));
return 0;
}
@@ -369,98 +315,77 @@ out:
static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
struct msgb *msg)
{
struct mgcp_endpoint *endp;
uint8_t osmux_cid;
struct mgcp_conn_rtp *conn_net = NULL;
struct mgcp_conn_rtp *conn;
if (msg->len < 1 + sizeof(osmux_cid)) {
if (osmux_legacy_dummy_parse_cid(addr, msg, &osmux_cid) < 0)
goto out;
conn = osmux_conn_lookup(cfg, osmux_cid, &addr->sin_addr);
if (!conn) {
LOGP(DLMGCP, LOGL_ERROR,
"Discarding truncated Osmux dummy load\n");
"Cannot find conn for Osmux CID %d\n", osmux_cid);
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;
}
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_net)
goto out;
if (conn_net->osmux.state == OSMUX_STATE_ENABLED)
goto out;
if (osmux_enable_conn(endp, conn_net, &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));
endp_osmux_state_check(conn->conn->endp, conn, false);
/* Only needed to punch hole in firewall, it can be dropped */
out:
msgb_free(msg);
return 0;
}
int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
#define osmux_chunk_length(msg, rem) (rem - msg->len);
static int osmux_read_fd_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;
struct mgcp_conn_rtp *conn_net = NULL;
struct mgcp_conn_rtp *conn_src;
msg = osmux_recv(ofd, &addr);
if (!msg)
return -1;
if (!cfg->osmux) {
LOGP(DLMGCP, LOGL_ERROR,
"bsc-nat wants to use Osmux but bsc did not request it\n");
goto out;
}
/* 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);
/* FIXME: Get rid of CONN_ID_XXX! */
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_net)
goto out;
if (!endp) {
conn_src = osmux_conn_lookup(cfg, osmuxh->circuit_id,
&addr.sin_addr);
if (!conn_src) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot find an endpoint for circuit_id=%d\n",
"Cannot find a src conn for circuit_id=%d\n",
osmuxh->circuit_id);
goto out;
}
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
conn_net->osmux.stats.chunks++;
rem = msg->len;
osmux_xfrm_output_sched(&conn_net->osmux.out, osmuxh);
/*conn_dst = mgcp_find_dst_conn(conn_src->conn);
if (!conn_dst) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot find a dst conn for circuit_id=%d\n",
osmuxh->circuit_id);
goto out;
}*/
if (endp_osmux_state_check(conn_src->conn->endp, conn_src, false) == 0) {
conn_src->osmux.stats.octets += osmux_chunk_length(msg, rem);
conn_src->osmux.stats.chunks++;
osmux_xfrm_output_sched(&conn_src->osmux.out, osmuxh);
}
rem = msg->len;
}
out:
msgb_free(msg);
@@ -471,22 +396,13 @@ 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.cb = osmux_read_fd_cb;
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");
LOGP(DLMGCP, LOGL_ERROR, "cannot bind OSMUX socket to %s:%u\n",
cfg->osmux_addr, cfg->osmux_port);
return ret;
}
mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp);
@@ -494,11 +410,15 @@ int osmux_init(int role, struct mgcp_config *cfg)
ret = osmo_fd_register(&osmux_fd);
if (ret < 0) {
LOGP(DLMGCP, LOGL_ERROR, "cannot register OSMUX socket\n");
LOGP(DLMGCP, LOGL_ERROR, "cannot register OSMUX socket %s\n",
osmo_sock_get_name2(osmux_fd.fd));
return ret;
}
cfg->osmux_init = 1;
LOGP(DLMGCP, LOGL_INFO, "OSMUX socket listening on %s\n",
osmo_sock_get_name2(osmux_fd.fd));
return 0;
}
@@ -506,7 +426,7 @@ int osmux_init(int role, struct mgcp_config *cfg)
* \param[in] endp mgcp endpoint (configuration)
* \param[in] conn connection to disable
* \param[in] addr IP address of remote OSMUX endpoint
* \param[in] port portnumber of the remote OSMUX endpoint
* \param[in] port portnumber of the remote OSMUX endpoint (in network byte order)
* \returns 0 on success, -1 on ERROR */
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
struct in_addr *addr, uint16_t port)
@@ -515,49 +435,51 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
* used to reconstruct the RTP flow from osmux. The RTP SSRC is
* allocated based on the circuit ID (conn_net->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
* SSRC space (2^32) by the OSMUX_CID_MAX + 1 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;
struct in_addr addr_unset = {};
static const uint32_t rtp_ssrc_winlen = UINT32_MAX / (OSMUX_CID_MAX + 1);
uint16_t osmux_dummy = endp->cfg->osmux_dummy;
/* Check if osmux is enabled for the specified connection */
if (conn->osmux.state == OSMUX_STATE_DISABLED) {
LOGP(DLMGCP, LOGL_ERROR, "OSMUX not enabled for conn:%s\n",
mgcp_conn_dump(conn->conn));
if (conn->osmux.state != OSMUX_STATE_ACTIVATING) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"conn:%s didn't negotiate Osmux, state %d\n",
mgcp_conn_dump(conn->conn), conn->osmux.state);
return -1;
}
osmux_xfrm_output_init(&conn->osmux.out,
(conn->osmux.cid * rtp_ssrc_winlen) +
(random() % rtp_ssrc_winlen));
/* Wait until we have the connection information from MDCX */
if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0) {
LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
"Osmux remote address/port still unknown\n");
return -1;
}
conn->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
if (!conn->osmux.in) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle for conn:%s\n",
mgcp_conn_dump(conn->conn));
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"Cannot allocate input osmux handle for conn:%s\n",
mgcp_conn_dump(conn->conn));
return -1;
}
if (!osmux_xfrm_input_open_circuit(conn->osmux.in, conn->osmux.cid, osmux_dummy)) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot open osmux circuit %u for conn:%s\n",
if (osmux_xfrm_input_open_circuit(conn->osmux.in, conn->osmux.cid, osmux_dummy) < 0) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"Cannot open osmux circuit %u for conn:%s\n",
conn->osmux.cid, mgcp_conn_dump(conn->conn));
return -1;
}
switch (endp->cfg->role) {
case MGCP_BSC_NAT:
conn->type = MGCP_OSMUX_BSC_NAT;
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
scheduled_tx_net_cb, endp);
break;
case MGCP_BSC:
conn->type = MGCP_OSMUX_BSC;
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
scheduled_tx_bts_cb, endp);
break;
}
osmux_xfrm_output_init2(&conn->osmux.out,
(conn->osmux.cid * rtp_ssrc_winlen) +
(random() % rtp_ssrc_winlen),
conn->end.codec->payload_type);
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
scheduled_from_osmux_tx_rtp_cb, conn);
conn->osmux.state = OSMUX_STATE_ENABLED;
@@ -566,48 +488,65 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
/*! disable OSXMUX circuit for a specified connection.
* \param[in] conn connection to disable */
void osmux_disable_conn(struct mgcp_conn_rtp *conn)
void conn_osmux_disable(struct mgcp_conn_rtp *conn)
{
if (!conn)
return;
if (conn->osmux.state != OSMUX_STATE_ENABLED)
return;
OSMO_ASSERT(conn->osmux.state != OSMUX_STATE_DISABLED);
LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n",
conn->conn->id, conn->osmux.cid);
LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
"Releasing connection using Osmux CID %u\n", conn->osmux.cid);
/* We are closing, we don't need pending RTP packets to be transmitted */
osmux_xfrm_output_set_tx_cb(&conn->osmux.out, NULL, NULL);
osmux_xfrm_output_flush(&conn->osmux.out);
if (conn->osmux.state == OSMUX_STATE_ENABLED) {
/* We are closing, we don't need pending RTP packets to be transmitted */
osmux_xfrm_output_set_tx_cb(&conn->osmux.out, NULL, NULL);
osmux_xfrm_output_flush(&conn->osmux.out);
osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
conn->osmux.state = OSMUX_STATE_DISABLED;
conn->osmux.cid = -1;
osmux_handle_put(conn->osmux.in);
osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
conn->osmux.state = OSMUX_STATE_DISABLED;
conn_osmux_release_cid(conn);
osmux_handle_put(conn->osmux.in);
}
conn_osmux_release_cid(conn);
}
/*! relase OSXMUX cid, that had been allocated to this connection.
* \param[in] conn connection with OSMUX cid to release */
void osmux_release_cid(struct mgcp_conn_rtp *conn)
void conn_osmux_release_cid(struct mgcp_conn_rtp *conn)
{
if (!conn)
return;
if (conn->osmux.state != OSMUX_STATE_ENABLED)
return;
if (conn->osmux.allocated_cid >= 0)
osmux_put_cid(conn->osmux.allocated_cid);
conn->osmux.allocated_cid = -1;
if (conn->osmux.cid_allocated)
osmux_cid_pool_put(conn->osmux.cid);
conn->osmux.cid = 0;
conn->osmux.cid_allocated = false;
}
/*! allocate OSXMUX cid to connection.
* \param[in] conn connection for which we allocate the OSMUX cid*/
void osmux_allocate_cid(struct mgcp_conn_rtp *conn)
* \param[in] conn connection for which we allocate the OSMUX cid
* \param[in] osmux_cid OSMUX cid to allocate. -1 Means take next available one.
* \returns Allocated OSMUX cid, -1 on error (no free cids avail, or selected one is already taken).
*/
int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid)
{
osmux_release_cid(conn);
conn->osmux.allocated_cid = osmux_get_cid();
if (osmux_cid != -1 && osmux_cid_pool_allocated((uint8_t) osmux_cid)) {
LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
"Osmux CID %d already allocated!\n",
osmux_cid);
return -1;
}
if (osmux_cid == -1) {
osmux_cid = osmux_cid_pool_get_next();
if (osmux_cid == -1) {
LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
"no available Osmux CID to allocate!\n");
return -1;
}
} else
osmux_cid_pool_get(osmux_cid);
conn->osmux.cid = (uint8_t) osmux_cid;
conn->osmux.cid_allocated = true;
conn->type = MGCP_OSMUX_BSC;
return osmux_cid;
}
/*! send RTP dummy packet to OSMUX connection port.
@@ -616,7 +555,8 @@ void osmux_allocate_cid(struct mgcp_conn_rtp *conn)
* \returns bytes sent, -1 on error */
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
char buf[1 + sizeof(uint8_t)];
struct osmux_hdr *osmuxh;
int buf_len;
struct in_addr addr_unset = {};
/*! The dummy packet will not be sent via the actual OSMUX connection,
@@ -628,39 +568,34 @@ int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
* endpoint may have already punched the hole in the firewall. This
* approach is simple though. */
buf[0] = MGCP_DUMMY_LOAD;
memcpy(&buf[1], &conn->osmux.cid, sizeof(conn->osmux.cid));
/* Wait until we have the connection information from MDCX */
if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0)
return 0;
if (conn->osmux.state == OSMUX_STATE_ACTIVATING) {
if (osmux_enable_conn(endp, conn, &conn->end.addr,
htons(endp->cfg->osmux_port)) < 0) {
LOGP(DLMGCP, LOGL_ERROR,
"Could not activate osmux for conn:%s\n",
mgcp_conn_dump(conn->conn));
}
LOGP(DLMGCP, LOGL_ERROR,
"Osmux CID %u for %s:%u is now enabled\n",
conn->osmux.cid, inet_ntoa(conn->end.addr),
endp->cfg->osmux_port);
}
LOGP(DLMGCP, LOGL_DEBUG,
"sending OSMUX dummy load to %s CID %u\n",
inet_ntoa(conn->end.addr), conn->osmux.cid);
if (endp_osmux_state_check(endp, conn, true) < 0)
return 0;
buf_len = sizeof(struct osmux_hdr) + osmo_amr_bytes(AMR_FT_0);
osmuxh = (struct osmux_hdr *) alloca(buf_len);
memset(osmuxh, 0, buf_len);
osmuxh->ft = OSMUX_FT_DUMMY;
osmuxh->amr_ft = AMR_FT_0;
osmuxh->circuit_id = conn->osmux.cid;
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
"sending OSMUX dummy load to %s:%u CID %u\n",
inet_ntoa(conn->end.addr), ntohs(conn->end.rtp_port), conn->osmux.cid);
return mgcp_udp_send(osmux_fd.fd, &conn->end.addr,
htons(endp->cfg->osmux_port), buf, sizeof(buf));
conn->end.rtp_port, (char*)osmuxh, buf_len);
}
/*! bsc-nat allocates/releases the OSMUX cids (Circuit IDs). */
static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1) / 8];
/* bsc-nat allocates/releases the Osmux circuit ID. +7 to round up to 8 bit boundary. */
static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1 + 7) / 8];
/*! count the number of taken OSMUX cids.
* \returns number of OSMUX cids in use */
int osmux_used_cid(void)
int osmux_cid_pool_count_used(void)
{
int i, j, used = 0;
@@ -676,7 +611,7 @@ int osmux_used_cid(void)
/*! take a free OSMUX cid.
* \returns OSMUX cid */
int osmux_get_cid(void)
int osmux_cid_pool_get_next(void)
{
int i, j;
@@ -696,10 +631,24 @@ int osmux_get_cid(void)
return -1;
}
/*! take a specific OSMUX cid.
* \param[in] osmux_cid OSMUX cid */
void osmux_cid_pool_get(uint8_t osmux_cid)
{
LOGP(DLMGCP, LOGL_DEBUG, "Allocating Osmux CID %u from pool\n", osmux_cid);
osmux_cid_bitmap[osmux_cid / 8] |= (1 << (osmux_cid % 8));
}
/*! put back a no longer used OSMUX cid.
* \param[in] osmux_cid OSMUX cid */
void osmux_put_cid(uint8_t osmux_cid)
void osmux_cid_pool_put(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));
}
/*! check if OSMUX cid is already taken */
bool osmux_cid_pool_allocated(uint8_t osmux_cid)
{
return !!(osmux_cid_bitmap[osmux_cid / 8] & (1 << (osmux_cid % 8)));
}

File diff suppressed because it is too large Load Diff

View File

@@ -26,12 +26,15 @@
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_sdp.h>
#include <errno.h>
#include <stdlib.h>
#include <limits.h>
/* A struct to store intermediate parsing results. The function
* mgcp_parse_sdp_data() is using it as temporary storage for parsing the SDP
* codec information. */
/* Two structs to store intermediate parsing results. The function
* mgcp_parse_sdp_data() is using the following two structs as temporary
* storage for parsing the SDP codec information. */
struct sdp_rtp_map {
/* the type */
int payload_type;
@@ -43,6 +46,11 @@ struct sdp_rtp_map {
int rate;
int channels;
};
struct sdp_fmtp_param {
int payload_type;
struct mgcp_codec_param param;
};
/* Helper function to extrapolate missing codec parameters in a codec mao from
* an already filled in payload_type, called from: mgcp_parse_sdp_data() */
@@ -123,7 +131,8 @@ static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
char *str;
char *str_ptr;
char *pt_str;
unsigned int pt;
char *pt_end;
unsigned long int pt;
unsigned int count = 0;
unsigned int i;
@@ -148,7 +157,14 @@ static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
if (!pt_str)
break;
pt = atoi(pt_str);
errno = 0;
pt = strtoul(pt_str, &pt_end, 0);
if ((errno == ERANGE && pt == ULONG_MAX) || (errno && !pt) ||
pt_str == pt_end)
goto error;
if (pt >> 7) /* PT is 7 bit field, higher values not allowed */
goto error;
/* Do not allow duplicate payload types */
for (i = 0; i < count; i++)
@@ -167,6 +183,92 @@ error:
return -EINVAL;
}
/* Extract fmtp parameters from SDP, called from: mgcp_parse_sdp_data() */
static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp)
{
char *str;
char *str_ptr;
char *param_str;
unsigned int pt;
unsigned int count = 0;
char delimiter;
unsigned int amr_octet_aligned;
memset(fmtp_param, 0, sizeof(*fmtp_param));
str = talloc_zero_size(ctx, strlen(sdp) + 1);
str_ptr = str;
strcpy(str_ptr, sdp);
/* Check if the input string begins with an fmtp token */
str_ptr = strstr(str_ptr, "fmtp:");
if (!str_ptr)
goto exit;
str_ptr += 5;
/* Extract payload type */
if (sscanf(str_ptr, "%u ", &pt) != 1)
goto error;
fmtp_param->payload_type = pt;
/* Advance pointer to the beginning of the parameter section and
* tokenize string */
str_ptr = strstr(str_ptr, " ");
if (!str_ptr)
goto error;
str_ptr++;
param_str = strtok(str_ptr, " ");
if (!param_str)
goto exit;
while (1) {
/* Make sure that we don't get trapped in an endless loop */
if (count > 256)
goto error;
/* Chop off delimiters ';' at the end */
delimiter = str_ptr[strlen(str_ptr) - 1];
if (delimiter == ';' || delimiter == ',')
str_ptr[strlen(str_ptr) - 1] = '\0';
/* AMR octet aligned parameter */
if (sscanf(param_str, "octet-align=%d", &amr_octet_aligned) == 1) {
fmtp_param->param.amr_octet_aligned_present = true;
fmtp_param->param.amr_octet_aligned = false;
if (amr_octet_aligned == 1)
fmtp_param->param.amr_octet_aligned = true;
}
param_str = strtok(NULL, " ");
if (!param_str)
break;
count++;
}
exit:
talloc_free(str);
return 0;
error:
talloc_free(str);
return -EINVAL;
}
/* Pick optional fmtp parameters by payload type, if there are no fmtp
* parameters, a nullpointer is returned */
static struct mgcp_codec_param *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
{
unsigned int i;
for (i = 0; i < fmtp_params_len; i++) {
if (fmtp_params[i].payload_type == pt)
return &fmtp_params[i].param;
}
return NULL;
}
/*! Analyze SDP input string.
* \param[in] endp trunk endpoint.
* \param[out] conn associated rtp connection.
@@ -180,6 +282,9 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
{
struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
unsigned int codecs_used = 0;
struct sdp_fmtp_param fmtp_params[MGCP_MAX_CODECS];
unsigned int fmtp_used = 0;
struct mgcp_codec_param *codec_param;
char *line;
unsigned int i;
void *tmp_ctx = talloc_new(NULL);
@@ -207,20 +312,31 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
/* skip these SDP attributes */
break;
case 'a':
if (sscanf(line, "a=rtpmap:%d %63s",
&payload, audio_name) == 2) {
codecs_update(tmp_ctx, codecs,
codecs_used, payload, audio_name);
} else
if (sscanf
(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) {
codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
break;
}
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) {
rtp->maximum_packet_time = ptime2;
break;
}
if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
rtp->maximum_packet_time = ptime2;
break;
}
if (strncmp("a=fmtp:", line, 6) == 0) {
rc = fmtp_from_sdp(conn->conn, &fmtp_params[fmtp_used], line);
if (rc >= 0)
fmtp_used++;
break;
}
break;
case 'm':
rc = sscanf(line, "m=audio %d RTP/AVP", &port);
@@ -262,14 +378,15 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
/* Store parsed codec information */
for (i = 0; i < codecs_used; i++) {
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line);
codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
if (rc < 0)
LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
}
talloc_free(tmp_ctx);
LOGP(DLMGCP, LOGL_NOTICE,
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
"Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
rtp->packet_duration_ms);
@@ -286,6 +403,105 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
return 0;
}
/* Add rtpmap string to the sdp payload, but only when the payload type falls
* into the dynamic payload type range */
static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name)
{
int rc;
if (payload_type >= 96 && payload_type <= 127) {
if (!audio_name)
return -EINVAL;
rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n", payload_type, audio_name);
if (rc < 0)
return -EINVAL;
}
return 0;
}
/* Add audio strings to sdp payload */
static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_types_len, int local_port)
{
int rc;
unsigned int i;
rc = msgb_printf(sdp, "m=audio %d RTP/AVP", local_port);
if (rc < 0)
return -EINVAL;
for (i = 0; i < payload_types_len; i++) {
rc = msgb_printf(sdp, " %d", payload_types[i]);
if (rc < 0)
return -EINVAL;
}
rc = msgb_printf(sdp, "\r\n");
if (rc < 0)
return -EINVAL;
return 0;
}
/* Add fmtp strings to sdp payload */
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len,
const char *fmtp_extra)
{
unsigned int i;
int rc;
int fmtp_extra_pt = -1;
char *fmtp_extra_pars = "";
/* When no fmtp parameters ara available but an fmtp extra string
* is configured, just add the fmtp extra string */
if (fmtp_params_len == 0 && fmtp_extra) {
return msgb_printf(sdp, "%s\r\n", fmtp_extra);
}
/* When there is fmtp extra configured we dissect it in order to drop
* in the configured extra parameters at the right place when
* generating the fmtp strings. */
if (fmtp_extra) {
if (sscanf(fmtp_extra, "a=fmtp:%d ", &fmtp_extra_pt) != 1)
fmtp_extra_pt = -1;
fmtp_extra_pars = strstr(fmtp_extra, " ");
if (!fmtp_extra_pars)
fmtp_extra_pars = "";
else
fmtp_extra_pars++;
}
for (i = 0; i < fmtp_params_len; i++) {
rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
/* Add amr octet align parameter */
if (fmtp_params[i].param.amr_octet_aligned_present) {
if (fmtp_params[i].param.amr_octet_aligned)
rc = msgb_printf(sdp, " octet-align=1");
else
rc = msgb_printf(sdp, " octet-align=0");
if (rc < 0)
return -EINVAL;
}
/* Append extra parameters from fmtp extra */
if (fmtp_params[i].payload_type == fmtp_extra_pt) {
rc = msgb_printf(sdp, " %s", fmtp_extra_pars);
if (rc < 0)
return -EINVAL;
}
rc = msgb_printf(sdp, "\r\n", fmtp_params[i].payload_type);
if (rc < 0)
return -EINVAL;
}
return 0;
}
/*! Generate SDP response string.
* \param[in] endp trunk endpoint.
* \param[in] conn associated rtp connection.
@@ -296,10 +512,16 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
const char *addr)
{
const struct mgcp_rtp_codec *codec;
const char *fmtp_extra;
const char *audio_name;
int payload_type;
struct sdp_fmtp_param fmtp_param;
int rc;
int payload_types[1];
int local_port;
struct sdp_fmtp_param fmtp_params[1];
unsigned int fmtp_params_len = 0;
OSMO_ASSERT(endp);
OSMO_ASSERT(conn);
@@ -308,10 +530,12 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
&payload_type, &audio_name,
&fmtp_extra,
&codec, &fmtp_extra,
(struct mgcp_conn_rtp *)conn);
audio_name = codec->audio_name;
payload_type = codec->payload_type;
rc = msgb_printf(sdp,
"v=0\r\n"
"o=- %s 23 IN IP4 %s\r\n"
@@ -323,27 +547,31 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
goto buffer_too_small;
if (payload_type >= 0) {
rc = msgb_printf(sdp, "m=audio %d RTP/AVP %d\r\n",
conn->end.local_port, payload_type);
payload_types[0] = payload_type;
if (mgcp_conn_rtp_is_osmux(conn))
local_port = endp->cfg->osmux_port;
else
local_port = conn->end.local_port;
rc = add_audio(sdp, payload_types, 1, local_port);
if (rc < 0)
goto buffer_too_small;
/* FIXME: Check if the payload type is from the static range,
* if yes, omitthe a=rtpmap since it is unnecessary */
if (audio_name && endp->tcfg->audio_send_name && (payload_type >= 96 && payload_type <= 127)) {
rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n",
payload_type, audio_name);
if (endp->tcfg->audio_send_name) {
rc = add_rtpmap(sdp, payload_type, audio_name);
if (rc < 0)
goto buffer_too_small;
}
if (fmtp_extra) {
rc = msgb_printf(sdp, "%s\r\n", fmtp_extra);
if (rc < 0)
goto buffer_too_small;
if (codec->param_present) {
fmtp_param.payload_type = payload_type;
fmtp_param.param = codec->param;
fmtp_params[0] = fmtp_param;
fmtp_params_len = 1;
}
rc = add_fmtp(sdp, fmtp_params, fmtp_params_len, fmtp_extra);
if (rc < 0)
goto buffer_too_small;
}
if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
rc = msgb_printf(sdp, "a=ptime:%u\r\n",
@@ -355,6 +583,6 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
return 0;
buffer_too_small:
LOGP(DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
return -1;
}

View File

@@ -22,6 +22,7 @@
*/
#include <osmocom/core/talloc.h>
#include <osmocom/vty/misc.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_internal.h>
@@ -31,10 +32,13 @@
#include <string.h>
#include <inttypes.h>
#include <limits.h>
#define RTCP_OMIT_STR "Drop RTCP packets in both directions\n"
#define RTP_PATCH_STR "Modify RTP packet header in both directions\n"
#define RTP_KEEPALIVE_STR "Send dummy UDP packet to net RTP destination\n"
#define RTP_TS101318_RFC5993_CONV_STR "Convert GSM-HR from TS101318 to RFC5993 and vice versa\n"
static struct mgcp_config *g_cfg = NULL;
@@ -94,13 +98,17 @@ static int config_write_mgcp(struct vty *vty)
else
vty_out(vty, " no rtcp-omit%s", VTY_NEWLINE);
if (g_cfg->trunk.force_constant_ssrc
|| g_cfg->trunk.force_aligned_timing) {
|| g_cfg->trunk.force_aligned_timing
|| g_cfg->trunk.rfc5993_hr_convert) {
vty_out(vty, " %srtp-patch ssrc%s",
g_cfg->trunk.force_constant_ssrc ? "" : "no ",
VTY_NEWLINE);
vty_out(vty, " %srtp-patch timestamp%s",
g_cfg->trunk.force_aligned_timing ? "" : "no ",
VTY_NEWLINE);
vty_out(vty, " %srtp-patch rfc5993hr%s",
g_cfg->trunk.rfc5993_hr_convert ? "" : "no ",
VTY_NEWLINE);
} else
vty_out(vty, " no rtp-patch%s", VTY_NEWLINE);
if (g_cfg->trunk.audio_payload != -1)
@@ -152,6 +160,10 @@ static int config_write_mgcp(struct vty *vty)
vty_out(vty, " osmux dummy %s%s",
g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);
}
if (g_cfg->conn_timeout)
vty_out(vty, " conn-timeout %u%s", g_cfg->conn_timeout, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -160,11 +172,19 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
struct mgcp_rtp_state *state = &conn->state;
struct mgcp_rtp_end *end = &conn->end;
struct mgcp_rtp_codec *codec = end->codec;
struct rate_ctr *tx_packets, *tx_bytes;
struct rate_ctr *rx_packets, *rx_bytes;
struct rate_ctr *dropped_packets;
tx_packets = &conn->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR];
tx_bytes = &conn->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR];
rx_packets = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
rx_bytes = &conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR];
dropped_packets = &conn->rate_ctr_group->ctr[RTP_DROPPED_PACKETS_CTR];
vty_out(vty,
" Packets Sent: %" PRIu64 " (%" PRIu64 " bytes total)%s"
" Packets Received: %" PRIu64 " (%" PRIu64 " bytes total)%s"
" Timestamp Errs: %" PRIu64 "->%" PRIu64 "%s"
" Dropped Packets: %" PRIu64 "%s"
" Payload Type: %d Rate: %u Channels: %d %s"
@@ -172,6 +192,8 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
" FPP: %d Packet Duration: %u%s"
" FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s"
" Output-Enabled: %d Force-PTIME: %d%s",
tx_packets->current, tx_bytes->current, VTY_NEWLINE,
rx_packets->current, rx_bytes->current, VTY_NEWLINE,
state->in_stream.err_ts_ctr->current,
state->out_stream.err_ts_ctr->current,
VTY_NEWLINE,
@@ -184,13 +206,47 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
end->force_output_ptime, VTY_NEWLINE);
}
static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
int verbose)
static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp, int epidx,
int trunk_nr, enum mgcp_trunk_type trunk_type, int show_stats)
{
int i;
struct mgcp_conn *conn;
vty_out(vty, "%s trunk nr %d with %d endpoints:%s",
vty_out(vty, "%s trunk %d endpoint %s%.2x:%s",
trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1", trunk_nr,
trunk_type == MGCP_TRUNK_VIRTUAL ? MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK : "",
epidx, VTY_NEWLINE);
if (llist_empty(&endp->conns)) {
vty_out(vty, " No active connections%s", VTY_NEWLINE);
return;
}
llist_for_each_entry(conn, &endp->conns, entry) {
vty_out(vty, " CONN: %s%s", mgcp_conn_dump(conn), VTY_NEWLINE);
if (show_stats) {
if (endp->cfg->conn_timeout) {
struct timeval remaining;
osmo_timer_remaining(&conn->watchdog, NULL, &remaining);
vty_out(vty, " Currently remaining timeout (seconds): %d.%06d%s",
(int)remaining.tv_sec, (int)remaining.tv_usec, VTY_NEWLINE);
}
/* FIXME: Also add verbosity for other
* connection types (E1) as soon as
* the implementation is available */
if (conn->type == MGCP_CONN_TYPE_RTP) {
dump_rtp_end(vty, &conn->u.rtp);
}
}
}
}
static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg, int show_stats)
{
int i;
vty_out(vty, "%s trunk %d with %d endpoints:%s",
cfg->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1",
cfg->trunk_nr, cfg->number_endpoints - 1, VTY_NEWLINE);
@@ -201,29 +257,35 @@ static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
for (i = 1; i < cfg->number_endpoints; ++i) {
struct mgcp_endpoint *endp = &cfg->endpoints[i];
dump_endpoint(vty, endp, i, cfg->trunk_nr, cfg->trunk_type, show_stats);
if (i < cfg->number_endpoints - 1)
vty_out(vty, "%s", VTY_NEWLINE);
}
vty_out(vty, "Endpoint 0x%.2x:%s", i, VTY_NEWLINE);
llist_for_each_entry(conn, &endp->conns, entry) {
vty_out(vty, " CONN: %s%s",
mgcp_conn_dump(conn), VTY_NEWLINE);
if (verbose) {
/* FIXME: Also add verbosity for other
* connection types (E1) as soon as
* the implementation is available */
if (conn->type == MGCP_CONN_TYPE_RTP) {
dump_rtp_end(vty, &conn->u.rtp);
}
}
}
if (show_stats && cfg->mgcp_crcx_ctr_group) {
vty_out(vty, " %s:%s", cfg->mgcp_crcx_ctr_group->desc->group_description, VTY_NEWLINE);
vty_out_rate_ctr_group_fmt(vty, " %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_crcx_ctr_group);
}
if (show_stats && cfg->mgcp_dlcx_ctr_group) {
vty_out(vty, " %s:%s", cfg->mgcp_dlcx_ctr_group->desc->group_description, VTY_NEWLINE);
vty_out_rate_ctr_group_fmt(vty, " %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_dlcx_ctr_group);
}
if (show_stats && cfg->mgcp_mdcx_ctr_group) {
vty_out(vty, " %s:%s", cfg->mgcp_mdcx_ctr_group->desc->group_description, VTY_NEWLINE);
vty_out_rate_ctr_group_fmt(vty, " %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_mdcx_ctr_group);
}
if (show_stats && cfg->all_rtp_conn_stats) {
vty_out(vty, " %s:%s", cfg->all_rtp_conn_stats->desc->group_description, VTY_NEWLINE);
vty_out_rate_ctr_group_fmt(vty, " %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->all_rtp_conn_stats);
}
}
#define SHOW_MGCP_STR "Display information about the MGCP Media Gateway\n"
DEFUN(show_mcgp, show_mgcp_cmd,
"show mgcp [stats]",
SHOW_STR
"Display information about the MGCP Media Gateway\n"
SHOW_MGCP_STR
"Include Statistics\n")
{
struct mgcp_trunk_config *trunk;
@@ -235,12 +297,78 @@ DEFUN(show_mcgp, show_mgcp_cmd,
dump_trunk(vty, trunk, show_stats);
if (g_cfg->osmux)
vty_out(vty, "Osmux used CID: %d%s", osmux_used_cid(),
vty_out(vty, "Osmux used CID: %d%s", osmux_cid_pool_count_used(),
VTY_NEWLINE);
return CMD_SUCCESS;
}
static void
dump_mgcp_endpoint(struct vty *vty, struct mgcp_trunk_config *trunk, const char *epname)
{
const size_t virt_prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
unsigned long epidx;
char *endp;
int i;
if (strncmp(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, virt_prefix_len) == 0)
epname += virt_prefix_len;
errno = 0;
epidx = strtoul(epname, &endp, 16);
if (epname[0] == '\0' || *endp != '\0') {
vty_out(vty, "endpoint name '%s' is not a hex number%s", epname, VTY_NEWLINE);
return;
}
if ((errno == ERANGE && epidx == ULONG_MAX) /* parsed value out of range */
|| epidx >= trunk->number_endpoints) {
vty_out(vty, "endpoint %.2lx not configured on trunk %d%s", epidx, trunk->trunk_nr, VTY_NEWLINE);
return;
}
for (i = 0; i < trunk->number_endpoints; ++i) {
struct mgcp_endpoint *endp = &trunk->endpoints[i];
if (i == epidx) {
dump_endpoint(vty, endp, i, trunk->trunk_nr, trunk->trunk_type, true);
break;
}
}
}
DEFUN(show_mcgp_endpoint, show_mgcp_endpoint_cmd,
"show mgcp endpoint NAME",
SHOW_STR
SHOW_MGCP_STR
"Display information about an endpoint\n" "The name of the endpoint\n")
{
struct mgcp_trunk_config *trunk;
dump_mgcp_endpoint(vty, &g_cfg->trunk, argv[0]);
llist_for_each_entry(trunk, &g_cfg->trunks, entry)
dump_mgcp_endpoint(vty, trunk, argv[0]);
return CMD_SUCCESS;
}
DEFUN(show_mcgp_trunk_endpoint, show_mgcp_trunk_endpoint_cmd,
"show mgcp trunk <0-64> endpoint NAME",
SHOW_STR
SHOW_MGCP_STR
"Display information about a trunk\n" "Trunk number\n"
"Display information about an endpoint\n" "The name of the endpoint\n")
{
struct mgcp_trunk_config *trunk;
int trunkidx = atoi(argv[0]);
trunk = find_trunk(g_cfg, trunkidx);
if (!trunk) {
vty_out(vty, "trunk %d not found%s", trunkidx, VTY_NEWLINE);
return CMD_WARNING;
}
dump_mgcp_endpoint(vty, trunk, argv[1]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp, cfg_mgcp_cmd, "mgcp", "Configure the MGCP")
{
vty->node = MGCP_NODE;
@@ -600,11 +728,28 @@ DEFUN(cfg_mgcp_no_patch_rtp_ts,
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_patch_rtp_rfc5993hr,
cfg_mgcp_patch_rtp_rfc5993hr_cmd,
"rtp-patch rfc5993hr", RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR)
{
g_cfg->trunk.rfc5993_hr_convert = true;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_no_patch_rtp_rfc5993hr,
cfg_mgcp_no_patch_rtp_rfc5993hr_cmd,
"no rtp-patch rfc5993hr", NO_STR RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR)
{
g_cfg->trunk.rfc5993_hr_convert = false;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_no_patch_rtp,
cfg_mgcp_no_patch_rtp_cmd, "no rtp-patch", NO_STR RTP_PATCH_STR)
{
g_cfg->trunk.force_constant_ssrc = 0;
g_cfg->trunk.force_aligned_timing = 0;
g_cfg->trunk.rfc5993_hr_convert = false;
return CMD_SUCCESS;
}
@@ -701,13 +846,17 @@ static int config_write_trunk(struct vty *vty)
vty_out(vty, " rtcp-omit%s", VTY_NEWLINE);
else
vty_out(vty, " no rtcp-omit%s", VTY_NEWLINE);
if (trunk->force_constant_ssrc || trunk->force_aligned_timing) {
if (trunk->force_constant_ssrc || trunk->force_aligned_timing
|| g_cfg->trunk.rfc5993_hr_convert) {
vty_out(vty, " %srtp-patch ssrc%s",
trunk->force_constant_ssrc ? "" : "no ",
VTY_NEWLINE);
vty_out(vty, " %srtp-patch timestamp%s",
trunk->force_aligned_timing ? "" : "no ",
VTY_NEWLINE);
vty_out(vty, " %srtp-patch rfc5993hr%s",
trunk->rfc5993_hr_convert ? "" : "no ",
VTY_NEWLINE);
} else
vty_out(vty, " no rtp-patch%s", VTY_NEWLINE);
if (trunk->audio_fmtp_extra)
@@ -873,12 +1022,31 @@ DEFUN(cfg_trunk_no_patch_rtp_ts,
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_patch_rtp_rfc5993hr,
cfg_trunk_patch_rtp_rfc5993hr_cmd,
"rtp-patch rfc5993hr", RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR)
{
struct mgcp_trunk_config *trunk = vty->index;
trunk->rfc5993_hr_convert = true;
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_no_patch_rtp_rfc5993hr,
cfg_trunk_no_patch_rtp_rfc5993hr_cmd,
"no rtp-patch rfc5993hr", NO_STR RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR)
{
struct mgcp_trunk_config *trunk = vty->index;
trunk->rfc5993_hr_convert = false;
return CMD_SUCCESS;
}
DEFUN(cfg_trunk_no_patch_rtp,
cfg_trunk_no_patch_rtp_cmd, "no rtp-patch", NO_STR RTP_PATCH_STR)
{
struct mgcp_trunk_config *trunk = vty->index;
trunk->force_constant_ssrc = 0;
trunk->force_aligned_timing = 0;
trunk->rfc5993_hr_convert = false;
return CMD_SUCCESS;
}
@@ -1137,14 +1305,7 @@ DEFUN(cfg_mgcp_osmux,
if (strcmp(argv[0], "off") == 0) {
g_cfg->osmux = OSMUX_USAGE_OFF;
return CMD_SUCCESS;
}
/* Since OSMUX support is not finished, we do not
* allow to turn it on yet. */
vty_out(vty, "OSMUX currently unavailable in this software version.%s", VTY_NEWLINE);
return CMD_WARNING;
#if 0
if (strcmp(argv[0], "on") == 0)
} else if (strcmp(argv[0], "on") == 0)
g_cfg->osmux = OSMUX_USAGE_ON;
else if (strcmp(argv[0], "only") == 0)
g_cfg->osmux = OSMUX_USAGE_ONLY;
@@ -1155,7 +1316,7 @@ DEFUN(cfg_mgcp_osmux,
}
return CMD_SUCCESS;
#endif
}
DEFUN(cfg_mgcp_osmux_ip,
@@ -1208,15 +1369,32 @@ DEFUN(cfg_mgcp_osmux_dummy,
DEFUN(cfg_mgcp_domain,
cfg_mgcp_domain_cmd,
"domain NAME", "domain\n" "qualified domain name\n")
"domain NAME",
"Set the domain part expected in MGCP messages' endpoint names\n"
"Qualified domain name expected in MGCP endpoint names, or '*' to accept any domain\n")
{
osmo_strlcpy(g_cfg->domain, argv[0], sizeof(g_cfg->domain));
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_conn_timeout,
cfg_mgcp_conn_timeout_cmd,
"conn-timeout <0-65534>",
"Set a time after which inactive connections (CIs) are closed. Set to 0 to disable timeout. This can be used to"
" work around interoperability problems causing connections to stay open forever, and slowly exhausting all"
" available ports. Enable keep-alive packets in MGW clients when using this option together with LCLS (OsmoBSC,"
" OsmoMSC: 'rtp keep-alive')!\n"
"Timeout value (sec.)\n")
{
g_cfg->conn_timeout = strtoul(argv[0], NULL, 10);
return CMD_SUCCESS;
}
int mgcp_vty_init(void)
{
install_element_ve(&show_mgcp_cmd);
install_element_ve(&show_mgcp_endpoint_cmd);
install_element_ve(&show_mgcp_trunk_endpoint_cmd);
install_element(ENABLE_NODE, &loop_conn_cmd);
install_element(ENABLE_NODE, &tap_rtp_cmd);
install_element(ENABLE_NODE, &free_endp_cmd);
@@ -1262,6 +1440,8 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_ts_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_ts_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_cmd);
install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_rfc5993hr_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_rfc5993hr_cmd);
install_element(MGCP_NODE, &cfg_mgcp_sdp_fmtp_extra_cmd);
install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_send_ptime_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_ptime_cmd);
@@ -1276,6 +1456,7 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
install_element(MGCP_NODE, &cfg_mgcp_domain_cmd);
install_element(MGCP_NODE, &cfg_mgcp_conn_timeout_cmd);
install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
install_node(&trunk_node, config_write_trunk);
@@ -1292,6 +1473,8 @@ int mgcp_vty_init(void)
install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ssrc_cmd);
install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ssrc_cmd);
install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ts_cmd);
install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_rfc5993hr_cmd);
install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_rfc5993hr_cmd);
install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ts_cmd);
install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_cmd);
install_element(TRUNK_NODE, &cfg_trunk_sdp_fmtp_extra_cmd);

View File

@@ -1,31 +0,0 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir) \
$(NULL)
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(LIBBCG729_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
bin_PROGRAMS = \
osmo-bsc_mgcp \
$(NULL)
osmo_bsc_mgcp_SOURCES = \
mgcp_main.c \
$(NULL)
osmo_bsc_mgcp_LDADD = \
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBBCG729_LIBS) \
$(LIBRARY_GSM) \
$(NULL)

View File

@@ -1,351 +0,0 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* The main method to drive it as a standalone process */
/*
* (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2011 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 <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <osmocom/legacy_mgcp/mgcp.h>
#include <osmocom/legacy_mgcp/mgcp_internal.h>
#include <osmocom/legacy_mgcp/vty.h>
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/logging.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/ports.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/stats.h>
#include "../../bscconfig.h"
#ifdef BUILD_MGCP_TRANSCODING
#include <osmocom/legacy_mgcp/mgcp_transcode.h>
#endif
#define _GNU_SOURCE
#include <getopt.h>
#warning "Make use of the rtp proxy code"
static struct mgcp_config *cfg;
static struct mgcp_trunk_config *reset_trunk;
static int reset_endpoints = 0;
static int daemonize = 0;
const char *openbsc_copyright =
"Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
"Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n"
"Dieter Spaar, Andreas Eversberg, Harald Welte\r\n\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n";
static char *config_file = "mgcp.cfg";
/* used by msgb and mgcp */
void *tall_bsc_ctx = NULL;
static void print_help()
{
printf("Some useful help...\n");
printf(" -h --help is printing this text.\n");
printf(" -c --config-file filename The config file to use.\n");
printf(" -s --disable-color\n");
printf(" -D --daemonize Fork the process into a background daemon\n");
printf(" -V --version Print the version number\n");
}
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"config-file", 1, 0, 'c'},
{"daemonize", 0, 0, 'D'},
{"version", 0, 0, 'V'},
{"disable-color", 0, 0, 's'},
{0, 0, 0, 0},
};
c = getopt_long(argc, argv, "hc:VD", long_options, &option_index);
if (c == -1)
break;
switch(c) {
case 'h':
print_help();
exit(0);
break;
case 'c':
config_file = talloc_strdup(tall_bsc_ctx, optarg);
break;
case 's':
log_set_use_color(osmo_stderr_target, 0);
break;
case 'V':
print_version(1);
exit(0);
break;
case 'D':
daemonize = 1;
break;
default:
/* ignore */
break;
};
}
}
/* simply remember this */
static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
{
reset_endpoints = 1;
reset_trunk = tcfg;
return 0;
}
static int read_call_agent(struct osmo_fd *fd, unsigned int what)
{
struct sockaddr_in addr;
socklen_t slen = sizeof(addr);
struct msgb *msg;
struct msgb *resp;
int i;
msg = (struct msgb *) fd->data;
/* read one less so we can use it as a \0 */
int rc = recvfrom(cfg->gw_fd.bfd.fd, msg->data, msg->data_len - 1, 0,
(struct sockaddr *) &addr, &slen);
if (rc < 0) {
perror("Gateway failed to read");
return -1;
} else if (slen > sizeof(addr)) {
fprintf(stderr, "Gateway received message from outerspace: %zu %zu\n",
(size_t) slen, sizeof(addr));
return -1;
}
/* handle message now */
msg->l2h = msgb_put(msg, rc);
resp = mgcp_handle_message(cfg, msg);
msgb_reset(msg);
if (resp) {
sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
msgb_free(resp);
}
if (reset_endpoints) {
LOGP(DLMGCP, LOGL_NOTICE,
"Asked to reset endpoints: %d/%d\n",
reset_trunk->trunk_nr, reset_trunk->trunk_type);
reset_endpoints = 0;
/* is checking in_addr.s_addr == INADDR_LOOPBACK making it more secure? */
for (i = 1; i < reset_trunk->number_endpoints; ++i)
mgcp_release_endp(&reset_trunk->endpoints[i]);
}
return 0;
}
int mgcp_vty_is_config_node(struct vty *vty, int node)
{
switch (node) {
case CONFIG_NODE:
return 0;
default:
return 1;
}
}
int mgcp_vty_go_parent(struct vty *vty)
{
switch (vty->node) {
case TRUNK_NODE:
vty->node = MGCP_NODE;
vty->index = NULL;
break;
case MGCP_NODE:
default:
if (mgcp_vty_is_config_node(vty, vty->node))
vty->node = CONFIG_NODE;
else
vty->node = ENABLE_NODE;
vty->index = NULL;
}
return vty->node;
}
static struct vty_app_info vty_info = {
.name = "OpenBSC MGCP",
.version = PACKAGE_VERSION,
.go_parent_cb = mgcp_vty_go_parent,
.is_config_node = mgcp_vty_is_config_node,
};
static const struct log_info_cat log_categories[] = {
/* DLMGCP is provided by the MGCP library */
};
const struct log_info log_info = {
.cat = log_categories,
.num_cat = ARRAY_SIZE(log_categories),
};
int main(int argc, char **argv)
{
struct sockaddr_in addr;
int on = 1, rc;
tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
msgb_talloc_ctx_init(tall_bsc_ctx, 0);
osmo_init_ignore_signals();
osmo_init_logging2(tall_bsc_ctx, &log_info);
cfg = mgcp_config_alloc();
if (!cfg)
return -1;
#ifdef BUILD_MGCP_TRANSCODING
cfg->setup_rtp_processing_cb = &mgcp_transcoding_setup;
cfg->rtp_processing_cb = &mgcp_transcoding_process_rtp;
cfg->get_net_downlink_format_cb = &mgcp_transcoding_net_downlink_format;
#endif
cfg->trunk.force_realloc = 1;
vty_info.copyright = openbsc_copyright;
vty_init(&vty_info);
logging_vty_add_cmds(NULL);
osmo_stats_vty_add_cmds(&log_info);
mgcp_vty_init();
handle_options(argc, argv);
rate_ctr_init(tall_bsc_ctx);
osmo_stats_init(tall_bsc_ctx);
rc = mgcp_parse_config(config_file, cfg, MGCP_BSC);
if (rc < 0)
return rc;
/* start telnet after reading config for vty_get_bind_addr() */
rc = telnet_init_dynif(tall_bsc_ctx, NULL,
vty_get_bind_addr(), OSMO_VTY_PORT_BSC_MGCP);
if (rc < 0)
return rc;
/* set some callbacks */
cfg->reset_cb = mgcp_rsip_cb;
/* we need to bind a socket */
if (rc == 0) {
cfg->gw_fd.bfd.when = BSC_FD_READ;
cfg->gw_fd.bfd.cb = read_call_agent;
cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
if (cfg->gw_fd.bfd.fd < 0) {
perror("Gateway failed to listen");
return -1;
}
setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(cfg->source_port);
inet_aton(cfg->source_addr, &addr.sin_addr);
if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("Gateway failed to bind");
return -1;
}
cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");
if (!cfg->gw_fd.bfd.data) {
fprintf(stderr, "Gateway memory error.\n");
return -1;
}
if (cfg->call_agent_addr) {
addr.sin_port = htons(2727);
inet_aton(cfg->call_agent_addr, &addr.sin_addr);
if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
LOGP(DLMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n",
cfg->call_agent_addr, errno);
close(cfg->gw_fd.bfd.fd);
cfg->gw_fd.bfd.fd = -1;
return -1;
}
}
if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) {
LOGP(DLMGCP, LOGL_FATAL, "Failed to register the fd\n");
return -1;
}
LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP.\n");
}
/* initialisation */
srand(time(NULL));
if (daemonize) {
rc = osmo_daemonize();
if (rc < 0) {
perror("Error during daemonize");
exit(1);
}
}
/* main loop */
while (1) {
osmo_select_main(0);
}
return 0;
}

View File

@@ -104,7 +104,7 @@ static void handle_options(int argc, char **argv)
{0, 0, 0, 0},
};
c = getopt_long(argc, argv, "hc:VD", long_options, &option_index);
c = getopt_long(argc, argv, "hc:sVD", long_options, &option_index);
if (c == -1)
break;
@@ -270,9 +270,9 @@ int main(int argc, char **argv)
vty_info.copyright = osmomgw_copyright;
vty_init(&vty_info);
logging_vty_add_cmds(NULL);
logging_vty_add_cmds();
osmo_talloc_vty_add_cmds();
osmo_stats_vty_add_cmds(&log_info);
osmo_stats_vty_add_cmds();
mgcp_vty_init();
handle_options(argc, argv);

View File

@@ -1,5 +1,4 @@
SUBDIRS = \
legacy_mgcp \
mgcp_client \
mgcp \
$(NULL)
@@ -26,7 +25,6 @@ EXTRA_DIST = \
testsuite.at \
$(srcdir)/package.m4 \
$(TESTSUITE) \
vty_test_runner.py \
$(NULL)
TESTSUITE = $(srcdir)/testsuite
@@ -39,7 +37,6 @@ if ENABLE_EXT_TESTS
python-tests: $(BUILT_SOURCES)
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
$(PYTHON) $(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v
else
python-tests: $(BUILT_SOURCES)
echo "Not running python-based tests (determined at configure-time)"

View File

@@ -1,7 +1,6 @@
enable_nat_test='@osmo_ac_build_nat@'
enable_smpp_test='@osmo_ac_build_smpp@'
enable_bsc_test='@osmo_ac_build_bsc@'
enable_mgcp_transcoding_test='@osmo_ac_mgcp_transcoding@'
enable_sgsn_test='@found_libgtp_and_libcares@'
enable_oap_test='@found_libgtp_and_libcares@'
enable_gtphub_test='@found_libgtp_and_libcares@'

View File

@@ -1,61 +0,0 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_srcdir) \
$(NULL)
AM_CFLAGS = \
-Wall \
-ggdb3 \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(LIBBCG729_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
$(NULL)
EXTRA_DIST = \
mgcp_test.ok \
mgcp_transcoding_test.ok \
$(NULL)
noinst_PROGRAMS = \
mgcp_test \
$(NULL)
if BUILD_MGCP_TRANSCODING
noinst_PROGRAMS += \
mgcp_transcoding_test \
$(NULL)
endif
mgcp_test_SOURCES = \
mgcp_test.c \
$(NULL)
mgcp_test_LDADD = \
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBRARY_DL) \
$(LIBOSMONETIF_LIBS) \
$(LIBRARY_GSM) \
-lm \
$(NULL)
mgcp_transcoding_test_SOURCES = \
mgcp_transcoding_test.c \
$(NULL)
mgcp_transcoding_test_LDADD = \
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBBCG729_LIBS) \
$(LIBRARY_DL) \
$(LIBOSMONETIF_LIBS) \
$(LIBRARY_GSM) \
-lm \
$(NULL)

File diff suppressed because it is too large Load Diff

View File

@@ -1,480 +0,0 @@
line: 'one CR'
line: 'two CR'
line: ''
line: 'one CRLF'
line: 'two CRLF'
line: ''
line: 'one LF'
line: 'two LF'
line: ''
line: 'mixed (4 lines)'
line: ''
line: ''
line: ''
Testing AUEP1
Testing AUEP2
Testing MDCX1
Testing MDCX2
Testing CRCX
Dummy packets: 1
Detected packet duration: 40
Requested packetetization period: 20-20
Connection mode: 1: RECV
Testing MDCX3
Dummy packets: 1
Packet duration not set
Requested packetization period not set
Connection mode not set
Testing MDCX4
Dummy packets: 1
Detected packet duration: 40
Requested packetetization period: 20-20
Connection mode: 3: SEND RECV
Testing MDCX4_PT1
Dummy packets: 1
Detected packet duration: 40
Requested packetetization period: 20-40
Connection mode: 3: SEND RECV
Testing MDCX4_PT2
Dummy packets: 1
Detected packet duration: 40
Requested packetetization period: 20-20
Connection mode: 3: SEND RECV
Testing MDCX4_PT3
Dummy packets: 1
Detected packet duration: 40
Requested packetization period not set
Connection mode: 3: SEND RECV
Testing MDCX4_SO
Detected packet duration: 40
Requested packetetization period: 20-20
Connection mode: 2: SEND
Testing MDCX4_RO
Dummy packets: 1
Packet duration not set
Requested packetetization period: 20-20
Connection mode: 1: RECV
Testing DLCX
Detected packet duration: 20
Requested packetization period not set
Connection mode: 0: NONE
Testing CRCX_ZYN
Dummy packets: 1
Packet duration not set
Requested packetization period not set
Connection mode: 1: RECV
Testing EMPTY
Testing SHORT1
Testing SHORT2
Testing SHORT3
Testing SHORT4
Testing RQNT1
Testing RQNT2
Testing DLCX
Detected packet duration: 20
Requested packetization period not set
Connection mode: 0: NONE
Testing CRCX
Dummy packets: 1
Detected packet duration: 40
Requested packetetization period: 20-20
Connection mode: 1: RECV
Testing MDCX3
Dummy packets: 1
Packet duration not set
Requested packetization period not set
Connection mode not set
Testing DLCX
Detected packet duration: 20
Requested packetization period not set
Connection mode: 0: NONE
Testing CRCX
Re-transmitting CRCX
Testing RQNT1
Re-transmitting RQNT1
Testing RQNT2
Re-transmitting RQNT2
Testing MDCX3
Re-transmitting MDCX3
Testing DLCX
Re-transmitting DLCX
Testing packet loss calculation.
Testing stat parsing
Parsing result: 0
Parsing result: 0
Testing packet error detection, patch SSRC.
Output SSRC changed to 11223344
In TS: 0, dTS: 0, Seq: 0
Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = 0
In TS: 160, dTS: 160, Seq: 1
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = 0
In TS: 320, dTS: 160, Seq: 2
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = 0
In TS: 320, dTS: 160, Seq: 3
Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 10, Transit = 160
In TS: 480, dTS: 160, Seq: 4
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 9, Transit = 160
In TS: 640, dTS: 160, Seq: 5
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 8, Transit = 160
In TS: 960, dTS: 320, Seq: 6
Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 18, Transit = 0
In TS: 1120, dTS: 160, Seq: 7
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 17, Transit = 0
In TS: 1280, dTS: 160, Seq: 8
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 16, Transit = 0
In TS: 1400, dTS: 120, Seq: 9
Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1
Stats: Jitter = 17, Transit = 40
In TS: 1560, dTS: 160, Seq: 10
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 16, Transit = 40
In TS: 1720, dTS: 160, Seq: 11
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 15, Transit = 40
In TS: 34688, dTS: 0, Seq: 12
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -32768
In TS: 34848, dTS: 160, Seq: 13
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -32768
In TS: 35008, dTS: 160, Seq: 14
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -32768
In TS: 35128, dTS: 120, Seq: 15
Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1
Stats: Jitter = 2, Transit = -32728
In TS: 35288, dTS: 160, Seq: 16
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 2, Transit = -32728
In TS: 35448, dTS: 160, Seq: 17
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 2, Transit = -32728
In TS: 35768, dTS: 160, Seq: 19
Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0
Stats: Jitter = 12, Transit = -32888
In TS: 35928, dTS: 160, Seq: 20
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 11, Transit = -32888
In TS: 36088, dTS: 160, Seq: 21
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 20, Transit = -33048
In TS: 36088, dTS: 160, Seq: 21
Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0
Stats: Jitter = 29, Transit = -32888
In TS: 36248, dTS: 160, Seq: 22
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 27, Transit = -32888
In TS: 36408, dTS: 160, Seq: 23
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 25, Transit = -32888
In TS: 36568, dTS: 160, Seq: 23
Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1
Stats: Jitter = 24, Transit = -32888
In TS: 36728, dTS: 160, Seq: 24
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 22, Transit = -32888
In TS: 36888, dTS: 160, Seq: 25
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 21, Transit = -32888
In TS: 160000, dTS: 0, Seq: 1000
Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -144000
In TS: 160160, dTS: 160, Seq: 1001
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -144000
In TS: 160320, dTS: 160, Seq: 1002
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -144000
Testing packet error detection.
Output SSRC changed to 11223344
In TS: 0, dTS: 0, Seq: 0
Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = 0
In TS: 160, dTS: 160, Seq: 1
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = 0
In TS: 320, dTS: 160, Seq: 2
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = 0
In TS: 320, dTS: 160, Seq: 3
Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 10, Transit = 160
In TS: 480, dTS: 160, Seq: 4
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 9, Transit = 160
In TS: 640, dTS: 160, Seq: 5
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 8, Transit = 160
In TS: 960, dTS: 320, Seq: 6
Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 18, Transit = 0
In TS: 1120, dTS: 160, Seq: 7
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 17, Transit = 0
In TS: 1280, dTS: 160, Seq: 8
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 16, Transit = 0
In TS: 1400, dTS: 120, Seq: 9
Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1
Stats: Jitter = 17, Transit = 40
In TS: 1560, dTS: 160, Seq: 10
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 16, Transit = 40
In TS: 1720, dTS: 160, Seq: 11
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 15, Transit = 40
Output SSRC changed to 10203040
In TS: 34688, dTS: 0, Seq: 12
Out TS change: 32968, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -32768
In TS: 34848, dTS: 160, Seq: 13
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -32768
In TS: 35008, dTS: 160, Seq: 14
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -32768
In TS: 35128, dTS: 120, Seq: 15
Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1
Stats: Jitter = 2, Transit = -32728
In TS: 35288, dTS: 160, Seq: 16
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 2, Transit = -32728
In TS: 35448, dTS: 160, Seq: 17
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 2, Transit = -32728
In TS: 35768, dTS: 160, Seq: 19
Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0
Stats: Jitter = 12, Transit = -32888
In TS: 35928, dTS: 160, Seq: 20
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 11, Transit = -32888
In TS: 36088, dTS: 160, Seq: 21
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 20, Transit = -33048
In TS: 36088, dTS: 160, Seq: 21
Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0
Stats: Jitter = 29, Transit = -32888
In TS: 36248, dTS: 160, Seq: 22
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 27, Transit = -32888
In TS: 36408, dTS: 160, Seq: 23
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 25, Transit = -32888
In TS: 36568, dTS: 160, Seq: 23
Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1
Stats: Jitter = 24, Transit = -32888
In TS: 36728, dTS: 160, Seq: 24
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 22, Transit = -32888
In TS: 36888, dTS: 160, Seq: 25
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 21, Transit = -32888
Output SSRC changed to 50607080
In TS: 160000, dTS: 0, Seq: 1000
Out TS change: 123112, dTS: 160, Seq change: 975, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -144000
In TS: 160160, dTS: 160, Seq: 1001
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -144000
In TS: 160320, dTS: 160, Seq: 1002
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -144000
Testing packet error detection, patch timestamps.
Output SSRC changed to 11223344
In TS: 0, dTS: 0, Seq: 0
Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = 0
In TS: 160, dTS: 160, Seq: 1
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = 0
In TS: 320, dTS: 160, Seq: 2
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = 0
In TS: 320, dTS: 160, Seq: 3
Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 10, Transit = 160
In TS: 480, dTS: 160, Seq: 4
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 9, Transit = 160
In TS: 640, dTS: 160, Seq: 5
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 8, Transit = 160
In TS: 960, dTS: 320, Seq: 6
Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 18, Transit = 0
In TS: 1120, dTS: 160, Seq: 7
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 17, Transit = 0
In TS: 1280, dTS: 160, Seq: 8
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 16, Transit = 0
In TS: 1400, dTS: 120, Seq: 9
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0
Stats: Jitter = 17, Transit = 40
In TS: 1560, dTS: 160, Seq: 10
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 16, Transit = 40
In TS: 1720, dTS: 160, Seq: 11
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 15, Transit = 40
Output SSRC changed to 10203040
In TS: 34688, dTS: 0, Seq: 12
Out TS change: 32968, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -32768
In TS: 34848, dTS: 160, Seq: 13
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -32768
In TS: 35008, dTS: 160, Seq: 14
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -32768
In TS: 35128, dTS: 120, Seq: 15
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0
Stats: Jitter = 2, Transit = -32728
In TS: 35288, dTS: 160, Seq: 16
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 2, Transit = -32728
In TS: 35448, dTS: 160, Seq: 17
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 2, Transit = -32728
In TS: 35768, dTS: 160, Seq: 19
Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0
Stats: Jitter = 12, Transit = -32888
In TS: 35928, dTS: 160, Seq: 20
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 11, Transit = -32888
In TS: 36088, dTS: 160, Seq: 21
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 20, Transit = -33048
In TS: 36088, dTS: 160, Seq: 21
Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0
Stats: Jitter = 29, Transit = -32888
In TS: 36248, dTS: 160, Seq: 22
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 27, Transit = -32888
In TS: 36408, dTS: 160, Seq: 23
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 25, Transit = -32888
In TS: 36568, dTS: 160, Seq: 23
Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1
Stats: Jitter = 24, Transit = -32888
In TS: 36728, dTS: 160, Seq: 24
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 22, Transit = -32888
In TS: 36888, dTS: 160, Seq: 25
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 21, Transit = -32888
Output SSRC changed to 50607080
In TS: 160000, dTS: 0, Seq: 1000
Out TS change: 123112, dTS: 160, Seq change: 975, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -144000
In TS: 160160, dTS: 160, Seq: 1001
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -144000
In TS: 160320, dTS: 160, Seq: 1002
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -144000
Testing packet error detection, patch SSRC, patch timestamps.
Output SSRC changed to 11223344
In TS: 0, dTS: 0, Seq: 0
Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = 0
In TS: 160, dTS: 160, Seq: 1
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = 0
In TS: 320, dTS: 160, Seq: 2
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = 0
In TS: 320, dTS: 160, Seq: 3
Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 10, Transit = 160
In TS: 480, dTS: 160, Seq: 4
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 9, Transit = 160
In TS: 640, dTS: 160, Seq: 5
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 8, Transit = 160
In TS: 960, dTS: 320, Seq: 6
Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 18, Transit = 0
In TS: 1120, dTS: 160, Seq: 7
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 17, Transit = 0
In TS: 1280, dTS: 160, Seq: 8
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 16, Transit = 0
In TS: 1400, dTS: 120, Seq: 9
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0
Stats: Jitter = 17, Transit = 40
In TS: 1560, dTS: 160, Seq: 10
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 16, Transit = 40
In TS: 1720, dTS: 160, Seq: 11
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 15, Transit = 40
In TS: 34688, dTS: 0, Seq: 12
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -32768
In TS: 34848, dTS: 160, Seq: 13
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -32768
In TS: 35008, dTS: 160, Seq: 14
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -32768
In TS: 35128, dTS: 120, Seq: 15
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0
Stats: Jitter = 2, Transit = -32728
In TS: 35288, dTS: 160, Seq: 16
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 2, Transit = -32728
In TS: 35448, dTS: 160, Seq: 17
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 2, Transit = -32728
In TS: 35768, dTS: 160, Seq: 19
Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0
Stats: Jitter = 12, Transit = -32888
In TS: 35928, dTS: 160, Seq: 20
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 11, Transit = -32888
In TS: 36088, dTS: 160, Seq: 21
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 20, Transit = -33048
In TS: 36088, dTS: 160, Seq: 21
Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0
Stats: Jitter = 29, Transit = -32888
In TS: 36248, dTS: 160, Seq: 22
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 27, Transit = -32888
In TS: 36408, dTS: 160, Seq: 23
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 25, Transit = -32888
In TS: 36568, dTS: 160, Seq: 23
Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1
Stats: Jitter = 24, Transit = -32888
In TS: 36728, dTS: 160, Seq: 24
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 22, Transit = -32888
In TS: 36888, dTS: 160, Seq: 25
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 21, Transit = -32888
In TS: 160000, dTS: 0, Seq: 1000
Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -144000
In TS: 160160, dTS: 160, Seq: 1001
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -144000
In TS: 160320, dTS: 160, Seq: 1002
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -144000
Testing multiple payload types
Testing no sequence flow on initial packet
Testing no rtpmap name
Done

View File

@@ -1,662 +0,0 @@
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <err.h>
#include <stdint.h>
#include <errno.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/application.h>
#include <osmocom/netif/rtp.h>
#include <osmocom/legacy_mgcp/mgcp.h>
#include <osmocom/legacy_mgcp/mgcp_internal.h>
#include "bscconfig.h"
#ifndef BUILD_MGCP_TRANSCODING
#error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)"
#endif
#include <osmocom/legacy_mgcp/mgcp_transcode.h>
uint8_t *audio_frame_l16[] = {
};
struct rtp_packets {
float t;
int len;
char *data;
};
struct rtp_packets audio_packets_l16[] = {
/* RTP: SeqNo=1, TS=160 */
{0.020000, 332,
"\x80\x0B\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
},
};
struct rtp_packets audio_packets_gsm[] = {
/* RTP: SeqNo=1, TS=160 */
{0.020000, 45,
"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
"\xDE"
},
};
struct rtp_packets audio_packets_gsm_invalid_size[] = {
/* RTP: SeqNo=1, TS=160 */
{0.020000, 41,
"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
"\xDE"
},
};
struct rtp_packets audio_packets_gsm_invalid_data[] = {
/* RTP: SeqNo=1, TS=160 */
{0.020000, 45,
"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
"\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE"
"\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE"
"\xEE"
},
};
struct rtp_packets audio_packets_gsm_invalid_ptype[] = {
/* RTP: SeqNo=1, TS=160 */
{0.020000, 45,
"\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
"\xDE"
},
};
struct rtp_packets audio_packets_g729[] = {
/* RTP: SeqNo=1, TS=160 */
{0.020000, 32,
"\x80\x12\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
"\xAF\xC2\x81\x40\x00\xFA\xCE\xA4\x21\x7C\xC5\xC3\x4F\xA5\x98\xF5"
"\xB2\x95\xC4\xAD"
},
};
struct rtp_packets audio_packets_pcma[] = {
/* RTP: SeqNo=1, TS=160 */
{0.020000, 172,
"\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
},
/* RTP: SeqNo=26527, TS=232640 */
{0.020000, 92,
"\x80\x08\x67\x9f\x00\x03\x8c\xc0\x04\xaa\x67\x9f\xd5\xd5\xd5\xd5"
"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
"\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\xd5\xd5\x55\x55\xd5\xd5\x55\x55"
"\xd5\xd5\xd5\x55\x55\xd5\xd5\xd5\x55\x55\xd5\xd5"
},
/* RTP: SeqNo=26528, TS=232720 */
{0.020000, 92,
"\x80\x08\x67\xa0\x00\x03\x8d\x10\x04\xaa\x67\x9f\x55\xd5\xd5\x55"
"\xd5\x55\xd5\xd5\xd5\x55\xd5\x55\xd5\xd5\x55\xd5\x55\xd5\x55\xd5"
"\x55\x55\xd5\x55\xd5\xd5\x55\x55\x55\x55\x55\xd5\xd5\x55\xd5\xd5"
"\xd5\x55\xd5\xd5\xd5\x55\x54\x55\xd5\xd5\x55\xd5\xd5\xd5\xd5\x55"
"\x54\x55\xd5\x55\xd5\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\xd4"
"\xd5\x54\x55\xd5\xd4\xd5\x54\xd5\x55\xd5\xd5\xd5"
},
};
static int audio_name_to_type(const char *name)
{
if (!strcasecmp(name, "gsm"))
return 3;
#ifdef HAVE_BCG729
else if (!strcasecmp(name, "g729"))
return 18;
#endif
else if (!strcasecmp(name, "pcma"))
return 8;
else if (!strcasecmp(name, "l16"))
return 11;
return -1;
}
int mgcp_get_trans_frame_size(void *state_, int nsamples, int dst);
static int given_configured_endpoint(int in_samples, int out_samples,
const char *srcfmt, const char *dstfmt,
void **out_ctx, struct mgcp_endpoint **out_endp)
{
int rc;
struct mgcp_rtp_end *dst_end;
struct mgcp_rtp_end *src_end;
struct mgcp_config *cfg;
struct mgcp_trunk_config *tcfg;
struct mgcp_endpoint *endp;
cfg = mgcp_config_alloc();
tcfg = talloc_zero(cfg, struct mgcp_trunk_config);
endp = talloc_zero(tcfg, struct mgcp_endpoint);
cfg->setup_rtp_processing_cb = mgcp_transcoding_setup;
cfg->rtp_processing_cb = mgcp_transcoding_process_rtp;
cfg->get_net_downlink_format_cb = mgcp_transcoding_net_downlink_format;
tcfg->endpoints = endp;
tcfg->number_endpoints = 1;
tcfg->cfg = cfg;
endp->tcfg = tcfg;
endp->cfg = cfg;
mgcp_initialize_endp(endp);
dst_end = &endp->bts_end;
dst_end->codec.payload_type = audio_name_to_type(dstfmt);
src_end = &endp->net_end;
src_end->codec.payload_type = audio_name_to_type(srcfmt);
if (out_samples) {
dst_end->codec.frame_duration_den = dst_end->codec.rate;
dst_end->codec.frame_duration_num = out_samples;
dst_end->frames_per_packet = 1;
dst_end->force_output_ptime = 1;
}
rc = mgcp_transcoding_setup(endp, dst_end, src_end);
if (rc < 0) {
printf("setup failed: %s", strerror(-rc));
abort();
}
*out_ctx = cfg;
*out_endp = endp;
return 0;
}
static int transcode_test(const char *srcfmt, const char *dstfmt,
uint8_t *src_pkts, size_t src_pkt_size)
{
char buf[4096] = {0x80, 0};
void *ctx;
struct mgcp_rtp_end *dst_end;
struct mgcp_process_rtp_state *state;
struct mgcp_endpoint *endp;
int in_size;
const int in_samples = 160;
int len, cont;
printf("== Transcoding test ==\n");
printf("converting %s -> %s\n", srcfmt, dstfmt);
given_configured_endpoint(in_samples, 0, srcfmt, dstfmt, &ctx, &endp);
dst_end = &endp->bts_end;
state = dst_end->rtp_process_data;
OSMO_ASSERT(state != NULL);
in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0);
OSMO_ASSERT(sizeof(buf) >= in_size + 12);
memcpy(buf, src_pkts, src_pkt_size);
len = src_pkt_size;
cont = mgcp_transcoding_process_rtp(endp, dst_end,
buf, &len, sizeof(buf));
if (cont < 0) {
printf("Nothing encoded due: %s\n", strerror(-cont));
talloc_free(ctx);
return -1;
}
if (len < 24) {
printf("encoded: %s\n", osmo_hexdump((unsigned char *)buf, len));
} else {
const char *str = osmo_hexdump((unsigned char *)buf, len);
int i = 0;
const int prefix = 4;
const int cutlen = 48;
int nchars = 0;
printf("encoded:\n");
do {
nchars = printf("%*s%-.*s", prefix, "", cutlen, str + i);
i += nchars - prefix;
printf("\n");
} while (nchars - prefix >= cutlen);
}
printf("counted: %d\n", cont);
talloc_free(ctx);
return 0;
}
static void test_rtp_seq_state(void)
{
char buf[4096];
int len;
int cont;
void *ctx;
struct mgcp_endpoint *endp;
struct mgcp_process_rtp_state *state;
struct rtp_hdr *hdr;
uint32_t ts_no;
uint16_t seq_no;
given_configured_endpoint(160, 0, "pcma", "l16", &ctx, &endp);
state = endp->bts_end.rtp_process_data;
OSMO_ASSERT(!state->is_running);
OSMO_ASSERT(state->next_seq == 0);
OSMO_ASSERT(state->next_time == 0);
/* initialize packet */
len = audio_packets_pcma[0].len;
memcpy(buf, audio_packets_pcma[0].data, len);
cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, len);
OSMO_ASSERT(cont >= 0);
OSMO_ASSERT(state->is_running);
OSMO_ASSERT(state->next_seq == 2);
OSMO_ASSERT(state->next_time == 240);
/* verify that the right timestamp was written */
OSMO_ASSERT(len == audio_packets_pcma[0].len);
hdr = (struct rtp_hdr *) &buf[0];
memcpy(&ts_no, &hdr->timestamp, sizeof(ts_no));
OSMO_ASSERT(htonl(ts_no) == 160);
memcpy(&seq_no, &hdr->sequence, sizeof(seq_no));
OSMO_ASSERT(htons(seq_no) == 1);
/* Check the right sequence number is written */
state->next_seq = 1234;
len = audio_packets_pcma[0].len;
memcpy(buf, audio_packets_pcma[0].data, len);
cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, len);
OSMO_ASSERT(cont >= 0);
OSMO_ASSERT(len == audio_packets_pcma[0].len);
hdr = (struct rtp_hdr *) &buf[0];
memcpy(&seq_no, &hdr->sequence, sizeof(seq_no));
OSMO_ASSERT(htons(seq_no) == 1234);
talloc_free(ctx);
}
static void test_transcode_result(void)
{
char buf[4096];
int len, res;
void *ctx;
struct mgcp_endpoint *endp;
struct mgcp_process_rtp_state *state;
{
/* from GSM to PCMA and same ptime */
given_configured_endpoint(160, 0, "gsm", "pcma", &ctx, &endp);
state = endp->bts_end.rtp_process_data;
/* result */
len = audio_packets_gsm[0].len;
memcpy(buf, audio_packets_gsm[0].data, len);
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(res == sizeof(struct rtp_hdr));
OSMO_ASSERT(state->sample_cnt == 0);
len = res;
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(res == -ENOMSG);
talloc_free(ctx);
}
{
/* from GSM to PCMA and same ptime */
given_configured_endpoint(160, 160, "gsm", "pcma", &ctx, &endp);
state = endp->bts_end.rtp_process_data;
/* result */
len = audio_packets_gsm[0].len;
memcpy(buf, audio_packets_gsm[0].data, len);
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(res == sizeof(struct rtp_hdr));
OSMO_ASSERT(state->sample_cnt == 0);
len = res;
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(res == -EAGAIN);
talloc_free(ctx);
}
{
/* from PCMA to GSM and wrong different ptime */
given_configured_endpoint(80, 160, "pcma", "gsm", &ctx, &endp);
state = endp->bts_end.rtp_process_data;
/* Add the first sample */
len = audio_packets_pcma[1].len;
memcpy(buf, audio_packets_pcma[1].data, len);
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(state->sample_cnt == 80);
OSMO_ASSERT(state->next_time == 232640);
OSMO_ASSERT(res < 0);
/* Add the second sample and it should be consumable */
len = audio_packets_pcma[2].len;
memcpy(buf, audio_packets_pcma[2].data, len);
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(state->sample_cnt == 0);
OSMO_ASSERT(state->next_time == 232640 + 80 + 160);
OSMO_ASSERT(res == sizeof(struct rtp_hdr));
talloc_free(ctx);
}
{
/* from PCMA to GSM with a big time jump */
struct rtp_hdr *hdr;
uint32_t ts;
given_configured_endpoint(80, 160, "pcma", "gsm", &ctx, &endp);
state = endp->bts_end.rtp_process_data;
/* Add the first sample */
len = audio_packets_pcma[1].len;
memcpy(buf, audio_packets_pcma[1].data, len);
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(state->sample_cnt == 80);
OSMO_ASSERT(state->next_time == 232640);
OSMO_ASSERT(state->next_seq == 26527);
OSMO_ASSERT(res < 0);
/* Add a skip to the packet to force a 'resync' */
len = audio_packets_pcma[2].len;
memcpy(buf, audio_packets_pcma[2].data, len);
hdr = (struct rtp_hdr *) &buf[0];
/* jump the time and add alignment error */
ts = ntohl(hdr->timestamp) + 123 * 80 + 2;
hdr->timestamp = htonl(ts);
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(res < 0);
OSMO_ASSERT(state->sample_cnt == 80);
OSMO_ASSERT(state->next_time == ts);
OSMO_ASSERT(state->next_seq == 26527);
/* TODO: this can create alignment errors */
/* Now attempt to consume 160 samples */
len = audio_packets_pcma[2].len;
memcpy(buf, audio_packets_pcma[2].data, len);
hdr = (struct rtp_hdr *) &buf[0];
ts += 80;
hdr->timestamp = htonl(ts);
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(res == 12);
OSMO_ASSERT(state->sample_cnt == 0);
OSMO_ASSERT(state->next_time == ts + 160);
OSMO_ASSERT(state->next_seq == 26528);
talloc_free(ctx);
}
}
static void test_transcode_change(void)
{
char buf[4096] = {0x80, 0};
void *ctx;
struct mgcp_endpoint *endp;
struct mgcp_process_rtp_state *state;
struct rtp_hdr *hdr;
int len, res;
{
/* from GSM to PCMA and same ptime */
printf("Testing Initial L16->GSM, PCMA->GSM\n");
given_configured_endpoint(160, 0, "l16", "gsm", &ctx, &endp);
endp->net_end.alt_codec = endp->net_end.codec;
endp->net_end.alt_codec.payload_type = audio_name_to_type("pcma");
state = endp->bts_end.rtp_process_data;
/* initial transcoding work */
OSMO_ASSERT(state->src_fmt == AF_L16);
OSMO_ASSERT(state->dst_fmt == AF_GSM);
OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 8);
OSMO_ASSERT(endp->net_end.codec.payload_type == 11);
/* result */
len = audio_packets_pcma[0].len;
memcpy(buf, audio_packets_pcma[0].data, len);
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
state = endp->bts_end.rtp_process_data;
OSMO_ASSERT(res == sizeof(struct rtp_hdr));
OSMO_ASSERT(state->sample_cnt == 0);
OSMO_ASSERT(state->src_fmt == AF_PCMA);
OSMO_ASSERT(state->dst_fmt == AF_GSM);
OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 11);
OSMO_ASSERT(endp->net_end.codec.payload_type == 8);
len = res;
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(res == -ENOMSG);
OSMO_ASSERT(state == endp->bts_end.rtp_process_data);
/* now check that comfort noise doesn't change anything */
len = audio_packets_pcma[1].len;
memcpy(buf, audio_packets_pcma[1].data, len);
hdr = (struct rtp_hdr *) buf;
hdr->payload_type = 12;
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(state == endp->bts_end.rtp_process_data);
OSMO_ASSERT(state->sample_cnt == 80);
OSMO_ASSERT(state->src_fmt == AF_PCMA);
OSMO_ASSERT(state->dst_fmt == AF_GSM);
OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 11);
OSMO_ASSERT(endp->net_end.codec.payload_type == 8);
talloc_free(ctx);
}
}
static int test_repacking(int in_samples, int out_samples, int no_transcode)
{
char buf[4096] = {0x80, 0};
int cc;
struct mgcp_endpoint *endp;
void *ctx;
struct mgcp_process_rtp_state *state;
int in_cnt;
int out_size;
int in_size;
uint32_t ts = 0;
uint16_t seq = 0;
const char *srcfmt = "pcma";
const char *dstfmt = no_transcode ? "pcma" : "l16";
printf("== Transcoding test ==\n");
printf("converting %s -> %s\n", srcfmt, dstfmt);
given_configured_endpoint(in_samples, out_samples, srcfmt, dstfmt, &ctx, &endp);
state = endp->bts_end.rtp_process_data;
OSMO_ASSERT(state != NULL);
in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0);
OSMO_ASSERT(sizeof(buf) >= in_size + 12);
out_size = mgcp_transcoding_get_frame_size(state, -1, 1);
OSMO_ASSERT(sizeof(buf) >= out_size + 12);
buf[1] = endp->net_end.codec.payload_type;
*(uint16_t*)(buf+2) = htons(1);
*(uint32_t*)(buf+4) = htonl(0);
*(uint32_t*)(buf+8) = htonl(0xaabbccdd);
for (in_cnt = 0; in_cnt < 16; in_cnt++) {
int cont;
int len;
/* fake PCMA data */
printf("generating %d %s input samples\n", in_samples, srcfmt);
for (cc = 0; cc < in_samples; cc++)
buf[12+cc] = cc;
*(uint16_t*)(buf+2) = htonl(seq);
*(uint32_t*)(buf+4) = htonl(ts);
seq += 1;
ts += in_samples;
cc += 12; /* include RTP header */
len = cc;
do {
cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end,
buf, &len, sizeof(buf));
if (cont == -EAGAIN) {
fprintf(stderr, "Got EAGAIN\n");
break;
}
if (cont < 0) {
printf("processing failed: %s", strerror(-cont));
abort();
}
len -= 12; /* ignore RTP header */
printf("got %d %s output frames (%d octets) count=%d\n",
len / out_size, dstfmt, len, cont);
len = cont;
} while (len > 0);
}
talloc_free(ctx);
return 0;
}
static const struct log_info_cat log_categories[] = {
};
const struct log_info log_info = {
.cat = log_categories,
.num_cat = ARRAY_SIZE(log_categories),
};
int main(int argc, char **argv)
{
int rc;
void *ctx = talloc_named_const(NULL, 0, "mgcp_transcoding_test");
osmo_init_logging2(ctx, &log_info);
printf("=== Transcoding Good Cases ===\n");
transcode_test("l16", "l16",
(uint8_t *)audio_packets_l16[0].data,
audio_packets_l16[0].len);
transcode_test("l16", "gsm",
(uint8_t *)audio_packets_l16[0].data,
audio_packets_l16[0].len);
transcode_test("l16", "pcma",
(uint8_t *)audio_packets_l16[0].data,
audio_packets_l16[0].len);
transcode_test("gsm", "l16",
(uint8_t *)audio_packets_gsm[0].data,
audio_packets_gsm[0].len);
transcode_test("gsm", "gsm",
(uint8_t *)audio_packets_gsm[0].data,
audio_packets_gsm[0].len);
transcode_test("gsm", "pcma",
(uint8_t *)audio_packets_gsm[0].data,
audio_packets_gsm[0].len);
transcode_test("pcma", "l16",
(uint8_t *)audio_packets_pcma[0].data,
audio_packets_pcma[0].len);
transcode_test("pcma", "gsm",
(uint8_t *)audio_packets_pcma[0].data,
audio_packets_pcma[0].len);
transcode_test("pcma", "pcma",
(uint8_t *)audio_packets_pcma[0].data,
audio_packets_pcma[0].len);
printf("=== Transcoding Bad Cases ===\n");
printf("Invalid size:\n");
rc = transcode_test("gsm", "pcma",
(uint8_t *)audio_packets_gsm_invalid_size[0].data,
audio_packets_gsm_invalid_size[0].len);
OSMO_ASSERT(rc < 0);
printf("Invalid data:\n");
rc = transcode_test("gsm", "pcma",
(uint8_t *)audio_packets_gsm_invalid_data[0].data,
audio_packets_gsm_invalid_data[0].len);
OSMO_ASSERT(rc < 0);
printf("Invalid payload type:\n");
rc = transcode_test("gsm", "pcma",
(uint8_t *)audio_packets_gsm_invalid_ptype[0].data,
audio_packets_gsm_invalid_ptype[0].len);
OSMO_ASSERT(rc == 0);
printf("=== Repacking ===\n");
test_repacking(160, 160, 0);
test_repacking(160, 160, 1);
test_repacking(160, 80, 0);
test_repacking(160, 80, 1);
test_repacking(160, 320, 0);
test_repacking(160, 320, 1);
test_repacking(160, 240, 0);
test_repacking(160, 240, 1);
test_repacking(160, 100, 0);
test_repacking(160, 100, 1);
test_rtp_seq_state();
test_transcode_result();
test_transcode_change();
return 0;
}

View File

@@ -1,539 +0,0 @@
=== Transcoding Good Cases ===
== Transcoding test ==
converting l16 -> l16
encoded:
80 0b 00 01 00 00 00 a0 11 22 33 44 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed
counted: 0
== Transcoding test ==
converting l16 -> gsm
encoded:
80 0b 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9
62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc
69 9c d1 f0 66 7a ec 49 7a 33 3d 0a de
counted: 12
== Transcoding test ==
converting l16 -> pcma
encoded:
80 0b 00 01 00 00 00 a0 11 22 33 44 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25
counted: 12
== Transcoding test ==
converting gsm -> l16
encoded:
80 03 00 01 00 00 00 a0 11 22 33 44 00 00 54 00
59 f0 34 20 c4 c8 b9 f8 e2 18 f1 e8 f2 28 f0 e0
46 08 4f 80 2c a0 a9 c8 80 00 c0 58 3f 80 63 c0
24 b8 fa b8 f6 88 0b a0 c8 70 a8 b0 c8 c0 3b a8
66 a0 2e 38 d1 f8 98 98 aa 18 e8 30 26 a0 37 40
37 e8 17 00 ee 50 b7 80 b1 88 de 28 18 40 45 b0
4f 48 21 d8 df 78 ae 68 ab 98 d6 b8 18 18 48 90
4e 70 27 40 e8 10 b5 b0 ac 80 d4 60 14 50 48 48
50 10 2a 00 ec 08 ba 00 af 58 d1 c0 10 60 45 c8
54 10 30 78 f1 a8 bb 18 ad 48 ce 30 0a e8 3f 30
4f 10 32 18 f6 18 bf 20 ac 30 cd 80 0b d0 43 d8
55 e0 34 a0 f5 78 bc 98 ad 98 cd c8 0a 80 40 58
51 c0 35 40 f9 60 c1 68 ac c8 cb 38 08 00 40 98
51 e0 34 d8 fa 28 c2 f0 ae 40 c7 70 02 d0 3c a8
54 78 38 a0 fc 68 c2 08 ad 50 c7 78 01 60 39 c0
51 38 3a e8 00 e8 c6 38 ab d8 c4 00 fe 08 39 18
50 30 39 50 01 d8 ca 70 b1 80 c4 c8 fc 58 36 40
51 d8 3b 08 02 80 c8 58 b0 60 c5 a8 fb d0 33 e8
4e 80 3c e0 06 10 cb 90 ae 48 c2 60 f9 58 34 08
4d a0 3a a8 06 48 cf 80 b4 60 c3 e8 f7 90 30 18
4d a0 3b 98 07 90 cf 18 b4 68 c4 88
counted: 12
== Transcoding test ==
converting gsm -> gsm
encoded:
80 03 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9
62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc
69 9c d1 f0 66 7a ec 49 7a 33 3d 0a de
counted: 0
== Transcoding test ==
converting gsm -> pcma
encoded:
80 03 00 01 00 00 00 a0 11 22 33 44 d5 a0 a3 bf
38 24 08 19 1e 1b a4 a6 b3 20 2a 3a ba ad b7 60
17 92 3e 20 3e b8 ac b2 32 2c 20 02 b6 be be 82
04 27 26 35 8d a4 a6 b5 35 21 20 31 8d a7 a6 b6
02 27 21 30 81 a7 a1 b0 06 24 21 32 85 a4 a0 bd
19 24 21 3d 90 ba a6 bc 16 25 21 3c 92 a5 a0 bf
10 25 21 3c 90 a5 a1 bf 6f 3a 21 3f 95 a5 a1 bf
62 3b 21 39 f3 bb a0 b9 79 3b 21 39 c3 b9 a1 b8
db 39 20 3b 4a b9 a1 b9 c8 3f 26 38 78 be a1 b8
f1 3e 26 38 65 bc a6 bb ed 3f 21 3b 6f bf a6 b8
ec 3d 27 3b 15 bd a6 b8 eb 3d 27 38
counted: 12
== Transcoding test ==
converting pcma -> l16
encoded:
80 08 00 01 00 00 00 a0 11 22 33 44 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00
counted: 12
== Transcoding test ==
converting pcma -> gsm
encoded:
80 08 00 01 00 00 00 a0 11 22 33 44 d4 b9 f4 5d
d9 50 5a e1 a0 cd 76 ea 52 0e 87 53 ad d4 ea a2
0a 63 ca e9 60 79 e2 2a 25 d2 c0 f3 39
counted: 12
== Transcoding test ==
converting pcma -> pcma
encoded:
80 08 00 01 00 00 00 a0 11 22 33 44 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25
counted: 0
=== Transcoding Bad Cases ===
Invalid size:
== Transcoding test ==
converting gsm -> pcma
Nothing encoded due: No message of desired type
Invalid data:
== Transcoding test ==
converting gsm -> pcma
Nothing encoded due: No message of desired type
Invalid payload type:
== Transcoding test ==
converting gsm -> pcma
encoded:
80 08 00 01 00 00 00 a0 11 22 33 44 d5 a0 a3 bf
38 24 08 19 1e 1b a4 a6 b3 20 2a 3a ba ad b7 60
17 92 3e 20 3e b8 ac b2 32 2c 20 02 b6 be be 82
04 27 26 35 8d a4 a6 b5 35 21 20 31 8d a7 a6 b6
02 27 21 30 81 a7 a1 b0 06 24 21 32 85 a4 a0 bd
19 24 21 3d 90 ba a6 bc 16 25 21 3c 92 a5 a0 bf
10 25 21 3c 90 a5 a1 bf 6f 3a 21 3f 95 a5 a1 bf
62 3b 21 39 f3 bb a0 b9 79 3b 21 39 c3 b9 a1 b8
db 39 20 3b 4a b9 a1 b9 c8 3f 26 38 78 be a1 b8
f1 3e 26 38 65 bc a6 bb ed 3f 21 3b 6f bf a6 b8
ec 3d 27 3b 15 bd a6 b8 eb 3d 27 38
counted: 12
=== Repacking ===
== Transcoding test ==
converting pcma -> l16
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
== Transcoding test ==
converting pcma -> pcma
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
== Transcoding test ==
converting pcma -> l16
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
== Transcoding test ==
converting pcma -> pcma
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
== Transcoding test ==
converting pcma -> l16
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets) count=12
== Transcoding test ==
converting pcma -> pcma
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets) count=12
== Transcoding test ==
converting pcma -> l16
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets) count=12
== Transcoding test ==
converting pcma -> pcma
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets) count=12
== Transcoding test ==
converting pcma -> l16
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
== Transcoding test ==
converting pcma -> pcma
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
Testing Initial L16->GSM, PCMA->GSM

View File

@@ -37,6 +37,7 @@
#include <dlfcn.h>
#include <time.h>
#include <math.h>
#include <ctype.h>
char *strline_r(char *str, char **saveptr);
@@ -164,7 +165,7 @@ static void test_strline(void)
#define MDCX4_PT1 \
"MDCX 18983217 1@mgw MGCP 1.0\r\n" \
"M: sendrecv\r" \
"M: SENDRECV\r" \
"C: 2\r\n" \
"I: %s\r\n" \
"L: p:20-40, a:AMR, nt:IN\r\n" \
@@ -207,8 +208,24 @@ static void test_strline(void)
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:40\r\n"
#define MDCX4_SO \
/* Test different upper/lower case in options */
#define MDCX4_PT4 \
"MDCX 18983220 1@mgw MGCP 1.0\r\n" \
"m: sendrecv\r" \
"c: 2\r\n" \
"i: %s\r\n" \
"l: A:amr, NT:IN\r\n" \
"\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 4441 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:40\r\n"
#define MDCX4_SO \
"MDCX 18983221 1@mgw MGCP 1.0\r\n" \
"M: sendonly\r" \
"C: 2\r\n" \
"I: %s\r\n" \
@@ -223,12 +240,18 @@ static void test_strline(void)
"a=ptime:40\r\n"
#define MDCX4_RO \
"MDCX 18983221 1@mgw MGCP 1.0\r\n" \
"MDCX 18983222 1@mgw MGCP 1.0\r\n" \
"M: recvonly\r" \
"C: 2\r\n" \
"I: %s\r\n" \
"L: p:20, a:AMR, nt:IN\r\n"
#define MDCX_TOO_LONG_CI \
"MDCX 18983223 1@mgw MGCP 1.0\r\n" \
"I: 123456789012345678901234567890123\n"
#define MDCX_TOO_LONG_CI_RET "510 18983223 FAIL\r\n"
#define SHORT2 "CRCX 1"
#define SHORT2_RET "510 000000 FAIL\r\n"
#define SHORT3 "CRCX 1 1@mgw"
@@ -237,7 +260,7 @@ static void test_strline(void)
#define CRCX \
"CRCX 2 1@mgw MGCP 1.0\r\n" \
"M: recvonly\r\n" \
"m: recvonly\r\n" \
"C: 2\r\n" \
"L: p:20\r\n" \
"\r\n" \
@@ -309,6 +332,32 @@ static void test_strline(void)
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
#define CRCX_X_OSMO_IGN \
"CRCX 2 1@mgw MGCP 1.0\r\n" \
"M: recvonly\r\n" \
"C: 2\r\n" \
"L: p:20\r\n" \
"X-Osmo-IGN: C foo\r\n" \
"\r\n" \
"v=0\r\n" \
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 5904 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:40\r\n"
#define CRCX_X_OSMO_IGN_RET \
"200 2 OK\r\n" \
"I: %s\r\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 16010 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:40\r\n"
#define DLCX \
"DLCX 7 1@mgw MGCP 1.0\r\n" \
"I: %s\r\n" \
@@ -434,6 +483,34 @@ static void test_strline(void)
"M: recvonly\r\n" \
"C: 2\r\n"
#define CRCX_AMR_WITH_FMTP \
"CRCX 2 7@mgw MGCP 1.0\r\n" \
"M: recvonly\r\n" \
"C: 2\r\n" \
"X\r\n" \
"L: p:20\r\n" \
"\r\n" \
"v=0\r\n" \
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 5904 RTP/AVP 111\r\n" \
"a=rtpmap:111 AMR/8000/1\r\n" \
"a=ptime:20\r\n" \
"a=fmtp:111 mode-change-capability=2; octet-align=1\r\n" \
#define CRCX_AMR_WITH_FMTP_RET \
"200 2 OK\r\n" \
"I: %s\r\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 16012 RTP/AVP 111\r\n" \
"a=rtpmap:111 AMR/8000/1\r\n" \
"a=fmtp:111 octet-align=1\r\n" \
"a=ptime:20\r\n"
#define CRCX_NO_LCO_NO_SDP_RET \
"200 2 OK\r\n" \
"I: %s\r\n" \
@@ -465,8 +542,9 @@ static const struct mgcp_test tests[] = {
{"MDCX4_PT1", MDCX4_PT1, MDCX4_RET("18983217"), 99},
{"MDCX4_PT2", MDCX4_PT2, MDCX4_RET("18983218"), 99},
{"MDCX4_PT3", MDCX4_PT3, MDCX4_RET("18983219"), 99},
{"MDCX4_SO", MDCX4_SO, MDCX4_RET("18983220"), 99},
{"MDCX4_RO", MDCX4_RO, MDCX4_RO_RET("18983221"), PTYPE_IGNORE},
{"MDCX4_PT4", MDCX4_PT4, MDCX4_RET("18983220"), 99},
{"MDCX4_SO", MDCX4_SO, MDCX4_RET("18983221"), 99},
{"MDCX4_RO", MDCX4_RO, MDCX4_RO_RET("18983222"), PTYPE_IGNORE},
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE},
{"CRCX_ZYN", CRCX_ZYN, CRCX_ZYN_RET, 97},
{"EMPTY", EMPTY, EMPTY_RET},
@@ -482,6 +560,9 @@ static const struct mgcp_test tests[] = {
"a=fmtp:126 0/1/2"},
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE,.extra_fmtp = "a=fmtp:126 0/1/2"},
{"CRCX", CRCX_NO_LCO_NO_SDP, CRCX_NO_LCO_NO_SDP_RET, 97},
{"CRCX", CRCX_X_OSMO_IGN, CRCX_X_OSMO_IGN_RET, 97},
{"MDCX_TOO_LONG_CI", MDCX_TOO_LONG_CI, MDCX_TOO_LONG_CI_RET},
{"CRCX", CRCX_AMR_WITH_FMTP, CRCX_AMR_WITH_FMTP_RET},
};
static const struct mgcp_test retransmit[] = {
@@ -515,7 +596,7 @@ static int last_endpoint = -1;
static int mgcp_test_policy_cb(struct mgcp_trunk_config *cfg, int endpoint,
int state, const char *transactio_id)
{
fprintf(stderr, "Policy CB got state %d on endpoint %d\n",
fprintf(stderr, "Policy CB got state %d on endpoint 0x%x\n",
state, endpoint);
last_endpoint = endpoint;
return MGCP_POLICY_CONT;
@@ -572,45 +653,42 @@ static void test_values(void)
MGCP_CONN_RECV_SEND);
}
/* Extract a connection ID from a response (CRCX) */
/* Extract a connection ID from a response and return in conn_id;
* if there is none, return -EINVAL and leave conn_id unchanged. */
static int get_conn_id_from_response(uint8_t *resp, char *conn_id,
unsigned int conn_id_len)
size_t conn_id_buflen)
{
char *conn_id_ptr;
int i;
bool got_conn_id = false;
const char *conn_id_start;
const char *conn_id_end;
int conn_id_len;
/* First try to get the conn_id from the I: parameter */
conn_id_ptr = strstr((char *)resp, "I: ");
if (conn_id_ptr) {
memset(conn_id, 0, conn_id_len);
memcpy(conn_id, conn_id_ptr + 3, 32);
got_conn_id = true;
} else {
/* Alternatively try to extract the conn_id from the o=- SDP
* parameter */
conn_id_ptr = strstr((char *)resp, "o=- ");
if(conn_id_ptr) {
memset(conn_id, 0, conn_id_len);
memcpy(conn_id, conn_id_ptr + 4, 32);
got_conn_id = true;
}
}
const char *header_I = "\r\nI: ";
const char *header_o = "\r\no=- ";
if (got_conn_id) {
for (i = 0; i < conn_id_len; i++) {
if (conn_id[i] == '\n' || conn_id[i] == '\r')
conn_id[i] = '\0';
}
/* Try to get the conn_id from the 'I:' or 'o=-' parameter */
if ((conn_id_start = strstr((char *)resp, header_I))) {
conn_id_start += strlen(header_I);
conn_id_end = strstr(conn_id_start, "\r\n");
} else if ((conn_id_start = strstr((char *)resp, header_o))) {
conn_id_start += strlen(header_o);
conn_id_end = strchr(conn_id_start, ' ');
} else
return -EINVAL;
/* A valid conn_id must at least contain one digit, and must
* not exceed a length of 32 digits */
OSMO_ASSERT(strlen(conn_id) <= 32);
OSMO_ASSERT(strlen(conn_id) > 0);
if (conn_id_end)
conn_id_len = conn_id_end - conn_id_start;
else
conn_id_len = strlen(conn_id_start);
OSMO_ASSERT(conn_id_len <= conn_id_buflen - 1);
return 0;
}
return -EINVAL;
/* A valid conn_id must at least contain one digit, and must
* not exceed a length of 32 digits */
OSMO_ASSERT(conn_id_len <= 32);
OSMO_ASSERT(conn_id_len > 0);
strncpy(conn_id, conn_id_start, conn_id_len);
conn_id[conn_id_len] = '\0';
return 0;
}
/* Check response, automatically patch connection ID if needed */
@@ -710,7 +788,7 @@ static void test_messages(void)
if (msg) {
rc = get_conn_id_from_response(msg->data, last_conn_id,
sizeof(last_conn_id));
if (rc)
if (rc == 0)
printf("(response contains a connection id)\n");
else
printf("(response does not contain a connection id)\n");
@@ -781,7 +859,7 @@ static void test_messages(void)
OSMO_ASSERT(last_endpoint != -1);
endp = &cfg->trunk.endpoints[last_endpoint];
fprintf(stderr, "endpoint %d: "
fprintf(stderr, "endpoint 0x%x: "
"payload type %d (expected %d)\n",
last_endpoint,
conn->end.codec->payload_type, t->ptype);
@@ -938,6 +1016,7 @@ static void test_packet_loss_calc(void)
{
int i;
struct mgcp_endpoint endp;
struct mgcp_config cfg = {0};
struct mgcp_trunk_config trunk;
printf("Testing packet loss calculation.\n");
@@ -945,6 +1024,7 @@ static void test_packet_loss_calc(void)
memset(&endp, 0, sizeof(endp));
memset(&trunk, 0, sizeof(trunk));
endp.cfg = &cfg;
endp.type = &ep_typeset.rtp;
trunk.vty_number_endpoints = 1;
trunk.endpoints = &endp;
@@ -1165,6 +1245,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
struct mgcp_trunk_config trunk;
struct mgcp_endpoint endp;
struct mgcp_config cfg = {0};
struct mgcp_rtp_state state;
struct mgcp_rtp_end *rtp;
struct sockaddr_in addr = { 0 };
@@ -1192,6 +1273,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
state.in_stream.err_ts_ctr = &test_ctr_in;
state.out_stream.err_ts_ctr = &test_ctr_out;
endp.cfg = &cfg;
endp.type = &ep_typeset.rtp;
trunk.vty_number_endpoints = 1;
@@ -1210,7 +1292,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
rtp = &conn->end;
OSMO_ASSERT(mgcp_codec_add(conn, PTYPE_UNDEFINED, "AMR/8000/1") == 0);
OSMO_ASSERT(mgcp_codec_add(conn, PTYPE_UNDEFINED, "AMR/8000/1", NULL) == 0);
rtp->codec = &rtp->codecs[0];
for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
@@ -1265,6 +1347,7 @@ static void test_multilple_codec(void)
struct in_addr addr;
struct mgcp_conn_rtp *conn = NULL;
char conn_id[256];
int i;
printf("Testing multiple payload types\n");
@@ -1398,6 +1481,9 @@ static void test_multilple_codec(void)
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 0);
for (i = 1; i < cfg->trunk.number_endpoints; i++)
mgcp_endp_release(&cfg->trunk.endpoints[i]);
talloc_free(cfg);
}
@@ -1485,25 +1571,34 @@ static void test_osmux_cid(void)
{
int id, i;
OSMO_ASSERT(osmux_used_cid() == 0);
id = osmux_get_cid();
OSMO_ASSERT(osmux_cid_pool_count_used() == 0);
id = osmux_cid_pool_get_next();
OSMO_ASSERT(id == 0);
OSMO_ASSERT(osmux_used_cid() == 1);
osmux_put_cid(id);
OSMO_ASSERT(osmux_used_cid() == 0);
OSMO_ASSERT(osmux_cid_pool_count_used() == 1);
osmux_cid_pool_get(30);
OSMO_ASSERT(osmux_cid_pool_count_used() == 2);
osmux_cid_pool_get(30);
OSMO_ASSERT(osmux_cid_pool_count_used() == 2);
osmux_cid_pool_put(id);
OSMO_ASSERT(osmux_cid_pool_count_used() == 1);
osmux_cid_pool_put(30);
OSMO_ASSERT(osmux_cid_pool_count_used() == 0);
for (i = 0; i < 256; ++i) {
id = osmux_get_cid();
id = osmux_cid_pool_get_next();
OSMO_ASSERT(id == i);
OSMO_ASSERT(osmux_used_cid() == i + 1);
OSMO_ASSERT(osmux_cid_pool_count_used() == i + 1);
}
id = osmux_get_cid();
id = osmux_cid_pool_get_next();
OSMO_ASSERT(id == -1);
for (i = 0; i < 256; ++i)
osmux_put_cid(i);
OSMO_ASSERT(osmux_used_cid() == 0);
osmux_cid_pool_put(i);
OSMO_ASSERT(osmux_cid_pool_count_used() == 0);
}
static const struct log_info_cat log_categories[] = {
@@ -1616,6 +1711,140 @@ static void test_check_local_cx_options(void *ctx)
OSMO_ASSERT(check_local_cx_options(ctx, ",,,") == -1);
}
static void test_mgcp_codec_pt_translate_pars(struct mgcp_rtp_codec *c)
{
c->rate = 8000;
c->channels = 1;
c->frame_duration_num = 23;
c->frame_duration_den = 42;
}
static void test_mgcp_codec_pt_translate(void)
{
struct mgcp_conn_rtp conn_src;
struct mgcp_conn_rtp conn_dst;
int pt_dst;
/* Setup a realistic set of codec configurations on both
* ends. AMR and HR will use different payload types. PCMU
* must use 0 on both ends since this is not a dynamic payload
* type */
test_mgcp_codec_pt_translate_pars(&conn_src.end.codecs[0]);
test_mgcp_codec_pt_translate_pars(&conn_dst.end.codecs[0]);
test_mgcp_codec_pt_translate_pars(&conn_src.end.codecs[1]);
test_mgcp_codec_pt_translate_pars(&conn_dst.end.codecs[1]);
test_mgcp_codec_pt_translate_pars(&conn_src.end.codecs[2]);
test_mgcp_codec_pt_translate_pars(&conn_dst.end.codecs[2]);
conn_src.end.codecs[0].payload_type = 112;
conn_dst.end.codecs[0].payload_type = 96;
conn_src.end.codecs[1].payload_type = 0;
conn_dst.end.codecs[1].payload_type = 0;
conn_src.end.codecs[2].payload_type = 111;
conn_dst.end.codecs[2].payload_type = 97;
conn_src.end.codecs[0].audio_name = "AMR/8000/1";
conn_dst.end.codecs[0].audio_name = "AMR/8000/1";
conn_src.end.codecs[1].audio_name = "PCMU/8000/1";
conn_dst.end.codecs[1].audio_name = "PCMU/8000/1";
conn_src.end.codecs[2].audio_name = "GSM-HR-08/8000/1";
conn_dst.end.codecs[2].audio_name = "GSM-HR-08/8000/1";
conn_src.end.codecs[0].subtype_name = "AMR";
conn_dst.end.codecs[0].subtype_name = "AMR";
conn_src.end.codecs[1].subtype_name = "PCMU";
conn_dst.end.codecs[1].subtype_name = "PCMU";
conn_src.end.codecs[2].subtype_name = "GSM-HR-08";
conn_dst.end.codecs[2].subtype_name = "GSM-HR-08";
conn_src.end.codecs_assigned = 3;
conn_dst.end.codecs_assigned = 3;
/* We expect the function to find the PT we must use when we send the
* packet out to the destination. All we know is the context for both
* connections and the payload type from the source packet */
pt_dst =
mgcp_codec_pt_translate(&conn_src, &conn_dst,
conn_src.end.codecs[0].payload_type);
OSMO_ASSERT(pt_dst == conn_dst.end.codecs[0].payload_type);
pt_dst =
mgcp_codec_pt_translate(&conn_src, &conn_dst,
conn_src.end.codecs[1].payload_type);
OSMO_ASSERT(pt_dst == conn_dst.end.codecs[1].payload_type);
pt_dst =
mgcp_codec_pt_translate(&conn_src, &conn_dst,
conn_src.end.codecs[2].payload_type);
OSMO_ASSERT(pt_dst == conn_dst.end.codecs[2].payload_type);
/* Try some constellations that must fail */
pt_dst = mgcp_codec_pt_translate(&conn_src, &conn_dst, 123);
OSMO_ASSERT(pt_dst == -EINVAL);
conn_src.end.codecs_assigned = 0;
conn_dst.end.codecs_assigned = 3;
pt_dst =
mgcp_codec_pt_translate(&conn_src, &conn_dst,
conn_src.end.codecs[0].payload_type);
OSMO_ASSERT(pt_dst == -EINVAL);
pt_dst =
mgcp_codec_pt_translate(&conn_src, &conn_dst,
conn_src.end.codecs[1].payload_type);
OSMO_ASSERT(pt_dst == -EINVAL);
pt_dst =
mgcp_codec_pt_translate(&conn_src, &conn_dst,
conn_src.end.codecs[2].payload_type);
OSMO_ASSERT(pt_dst == -EINVAL);
conn_src.end.codecs_assigned = 3;
conn_dst.end.codecs_assigned = 0;
pt_dst =
mgcp_codec_pt_translate(&conn_src, &conn_dst,
conn_src.end.codecs[0].payload_type);
OSMO_ASSERT(pt_dst == -EINVAL);
pt_dst =
mgcp_codec_pt_translate(&conn_src, &conn_dst,
conn_src.end.codecs[1].payload_type);
OSMO_ASSERT(pt_dst == -EINVAL);
pt_dst =
mgcp_codec_pt_translate(&conn_src, &conn_dst,
conn_src.end.codecs[2].payload_type);
OSMO_ASSERT(pt_dst == -EINVAL);
}
void test_conn_id_matching()
{
struct mgcp_endpoint endp = {};
struct mgcp_conn *conn;
struct mgcp_conn *conn_match;
int i;
const char *conn_id_generated = "000023AB";
const char *conn_id_request[] = {
"23AB",
"0023AB",
"000023AB",
"00000023AB",
"23ab",
"0023ab",
"000023ab",
"00000023ab",
};
printf("\nTesting %s\n", __func__);
INIT_LLIST_HEAD(&endp.conns);
conn = talloc_zero(NULL, struct mgcp_conn);
OSMO_ASSERT(conn);
osmo_strlcpy(conn->id, conn_id_generated, sizeof(conn->id));
llist_add(&conn->entry, &endp.conns);
for (i = 0; i < ARRAY_SIZE(conn_id_request); i++) {
const char *needle = conn_id_request[i];
printf("needle='%s' ", needle);
conn_match = mgcp_conn_get(&endp, needle);
OSMO_ASSERT(conn_match);
printf("found '%s'\n", conn_match->id);
OSMO_ASSERT(conn_match == conn);
}
llist_del(&conn->entry);
talloc_free(conn);
}
int main(int argc, char **argv)
{
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
@@ -1639,6 +1868,8 @@ int main(int argc, char **argv)
test_osmux_cid();
test_get_lco_identifier();
test_check_local_cx_options(ctx);
test_mgcp_codec_pt_translate();
test_conn_id_matching();
OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);

View File

@@ -22,7 +22,7 @@ AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response contains a connection id)
(response does not contain a connection id)
================================================
Testing AUEP2
@@ -34,7 +34,7 @@ AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response contains a connection id)
(response does not contain a connection id)
================================================
Testing MDCX1
@@ -46,7 +46,7 @@ MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response contains a connection id)
(response does not contain a connection id)
================================================
Testing MDCX2
@@ -58,14 +58,14 @@ MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response contains a connection id)
(response does not contain a connection id)
================================================
Testing CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 1@mgw MGCP 1.0
M: recvonly
m: recvonly
C: 2
L: p:20
@@ -79,7 +79,7 @@ a=ptime:40
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response does not contain a connection id)
(response contains a connection id)
Dummy packets: 2
================================================
@@ -93,7 +93,7 @@ I: %s
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response does not contain a connection id)
(response contains a connection id)
Dummy packets: 2
================================================
@@ -117,7 +117,7 @@ a=ptime:40
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
@@ -125,7 +125,7 @@ Testing MDCX4_PT1
Testing MDCX4_PT1
creating message from statically defined input:
---------8<---------
MDCX 18983217 1@mgw MGCP 1.0
MDCX 18983217 1@mgw MGCP 1.0
M: SENDRECV
C: 2
I: %s
@@ -141,7 +141,7 @@ a=ptime:40
---------8<---------
checking response:
using message with patched conn_id for comparison
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
@@ -165,7 +165,7 @@ a=ptime:40
a=ptime:40
---------8<---------
checking response:
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
@@ -189,14 +189,38 @@ a=ptime:40
a=rtpmap:99 AMR/8000
a=ptime:40
---------8<---------
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
================================================
Testing MDCX4_PT4
creating message from statically defined input:
---------8<---------
MDCX 18983220 1@mgw MGCP 1.0
m: sendrecv
c: 2
i: %s
l: A:amr, NT:IN
v=0
o=- %s 23 IN IP4 0.0.0.0
c=IN IP4 0.0.0.0
t=0 0
m=audio 4441 RTP/AVP 99
a=rtpmap:99 AMR/8000
a=ptime:40
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
================================================
================================================
Testing MDCX4_SO
creating message from statically defined input:
@@ -213,13 +237,13 @@ a=ptime:40
t=0 0
m=audio 4441 RTP/AVP 99
a=rtpmap:99 AMR/8000
a=ptime:40
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
================================================
Testing MDCX4_RO
@@ -228,7 +252,7 @@ L: p:20, a:AMR, nt:IN
MDCX 18983222 1@mgw MGCP 1.0
M: recvonly
C: 2
L: p:20, a:AMR, nt:IN
I: %s
L: p:20, a:AMR, nt:IN
---------8<---------
@@ -243,7 +267,7 @@ C: 2
creating message from statically defined input:
---------8<---------
DLCX 7 1@mgw MGCP 1.0
C: 2
I: %s
C: 2
---------8<---------
@@ -256,7 +280,7 @@ v=0 c=IN IP4 123.12.12.123 m=audio 5904 RTP/AVP 97 a=rtpmap:97 GSM-EFR/8000
Testing CRCX_ZYN
creating message from statically defined input:
---------8<---------
M: recvonly
CRCX 2 1@mgw MGCP 1.0
M: recvonly
C: 2
@@ -277,7 +301,7 @@ CRCX
---------8<---------
---------8<---------
================================================
Testing SHORT1
@@ -288,7 +312,7 @@ CRCX 1
---------8<---------
checking response:
using message as statically defined for comparison
(response contains a connection id)
Response matches our expectations.
(response does not contain a connection id)
================================================
@@ -299,7 +323,7 @@ CRCX 1 1@mgw
---------8<---------
checking response:
using message as statically defined for comparison
(response contains a connection id)
Response matches our expectations.
(response does not contain a connection id)
================================================
@@ -310,7 +334,7 @@ CRCX 1 1@mgw MGCP
---------8<---------
checking response:
using message as statically defined for comparison
(response contains a connection id)
Response matches our expectations.
(response does not contain a connection id)
================================================
@@ -324,7 +348,7 @@ S: D/9
Response matches our expectations.
(response does not contain a connection id)
Testing RQNT1
================================================
Testing RQNT1
creating message from statically defined input:
---------8<---------
@@ -338,7 +362,7 @@ R: D/[0-9#*](N), G/ft, fxr/t38
Response matches our expectations.
(response does not contain a connection id)
Testing RQNT2
================================================
Testing RQNT2
creating message from statically defined input:
---------8<---------
@@ -352,14 +376,14 @@ C: 2
Response matches our expectations.
(response does not contain a connection id)
Testing DLCX
================================================
Testing DLCX
creating message from statically defined input:
---------8<---------
DLCX 7 1@mgw MGCP 1.0
I: %s
C: 2
---------8<---------
---------8<---------
checking response:
using message as statically defined for comparison
@@ -373,7 +397,7 @@ a=ptime:40
CRCX 2 1@mgw MGCP 1.0
m: recvonly
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
@@ -387,7 +411,7 @@ I: %s
Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
================================================
================================================
Testing MDCX3
creating message from statically defined input:
@@ -402,7 +426,7 @@ C: 2
(response contains a connection id)
Dummy packets: 2
Testing DLCX
================================================
Testing DLCX
creating message from statically defined input:
---------8<---------
@@ -416,7 +440,7 @@ C: 2
Response matches our expectations.
(response does not contain a connection id)
Testing CRCX
================================================
Testing CRCX
creating message from statically defined input:
---------8<---------
@@ -427,6 +451,66 @@ CRCX 2 1@mgw MGCP 1.0
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
================================================
Testing CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 1@mgw MGCP 1.0
M: recvonly
C: 2
L: p:20
X-Osmo-IGN: C foo
v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:40
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
================================================
Testing MDCX_TOO_LONG_CI
creating message from statically defined input:
---------8<---------
MDCX 18983223 1@mgw MGCP 1.0
I: 123456789012345678901234567890123
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 7@mgw MGCP 1.0
M: recvonly
C: 2
X
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 111
a=rtpmap:111 AMR/8000/1
a=ptime:20
a=fmtp:111 mode-change-capability=2; octet-align=1
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
@@ -442,7 +526,7 @@ Re-transmitting CRCX
v=0
c=IN IP4 123.12.12.123
a=rtpmap:97 GSM-EFR/8000
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:40
@@ -554,7 +638,7 @@ Testing packet loss calculation.
Re-transmitting DLCX
creating message from statically defined input:
---------8<---------
I: %s
DLCX 7 1@mgw MGCP 1.0
I: %s
C: 2
@@ -1105,7 +1189,7 @@ Testing no rtpmap name
a=rtpmap:4 G723/8000
a=rtpmap:96 iLBC/8000
a=fmtp:96 mode=20
a=fmtp:97 mode=30
a=rtpmap:97 iLBC/8000
a=fmtp:97 mode=30
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
@@ -1133,4 +1217,14 @@ p:10, a:PCMU -> p:10, a:PCMU
Response matches our expectations.
Testing get_lco_identifier()
p:10, a:PCMU -> p:10, a:PCMU
p:10, a:PCMU -> p:10, a:PCMU
'XXXX, p:10, a:PCMU' -> 'p:10, a:PCMU'
'XXXX,p:10,a:PCMU' -> 'p:10,a:PCMU'
'10,a:PCMU' -> 'a:PCMU'
'10, a:PCMU' -> 'a:PCMU'
'10,a: PCMU' -> 'a: PCMU'
'10 ,a: PCMU' -> 'a: PCMU'
', a:PCMU' -> 'a:PCMU'
' a:PCMU' -> 'a:PCMU'
'' -> '(null)'
p10, aPCMU -> (null)

View File

@@ -77,14 +77,14 @@ static struct mgcp_client_conf conf;
struct mgcp_client *mgcp = NULL;
static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
int conn_id, const char *params)
const char *params)
{
static char compose[4096 - 128];
int len;
len = snprintf(compose, sizeof(compose),
"%d %u %s\r\nI: %d\n\n%s",
code, trans_id, comment, conn_id, params);
"%d %u %s\r\n%s",
code, trans_id, comment, params);
OSMO_ASSERT(len < sizeof(compose));
OSMO_ASSERT(len > 0);
@@ -102,6 +102,7 @@ void test_response_cb(struct mgcp_response *response, void *priv)
printf("response cb received:\n");
printf(" head.response_code = %d\n", response->head.response_code);
printf(" head.trans_id = %u\n", response->head.trans_id);
printf(" head.conn_id = %s\n", response->head.conn_id);
printf(" head.comment = %s\n", response->head.comment);
printf(" audio_port = %u\n", response->audio_port);
printf(" audio_ip = %s\n", response->audio_ip);
@@ -112,9 +113,9 @@ void test_response_cb(struct mgcp_response *response, void *priv)
printf(" ptmap_len = %u\n", response->ptmap_len);
for(i=0;i<response->ptmap_len;i++) {
printf(" ptmap[%u].codec = %u\n", i, response->ptmap[i].codec);
printf(" ptmap[%u].pt = %u\n", i, response->ptmap[i].pt);
printf(" ptmap[%u].pt = %u\n", i, response->ptmap[i].pt);
}
}
mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg)
@@ -134,32 +135,6 @@ mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg)
return trans_id;
}
void test_crcx(void)
{
struct msgb *msg;
mgcp_trans_id_t trans_id;
printf("\n===== %s =====\n", __func__);
if (mgcp)
talloc_free(mgcp);
mgcp = mgcp_client_init(ctx, &conf);
msg = mgcp_msg_crcx(mgcp, 23, 42, MGCP_CONN_LOOPBACK);
trans_id = dummy_mgcp_send(msg);
reply_to(trans_id, 200, "OK", 1,
"v=0\r\n"
"o=- 1 23 IN IP4 10.9.1.120\r\n"
"s=-\r\n"
"c=IN IP4 10.9.1.120\r\n"
"t=0 0\r\n"
"m=audio 16002 RTP/AVP 110 96\r\n"
"a=rtpmap:110 AMR/8000\r\n"
"a=rtpmap:96 GSM-EFR/8000\r\n"
"a=ptime:20\r\n");
}
void test_mgcp_msg(void)
{
struct msgb *msg;
@@ -180,7 +155,9 @@ void test_mgcp_msg(void)
.codecs_len = 1,
.ptmap[0].codec = CODEC_GSMEFR_8000_1,
.ptmap[0].pt = 96,
.ptmap_len = 1
.ptmap_len = 1,
.x_osmo_ign = MGCP_X_OSMO_IGN_CALLID,
.x_osmo_osmux_cid = -1, /* wildcard */
};
if (mgcp)
@@ -204,7 +181,7 @@ void test_mgcp_msg(void)
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
mgcp_msg.codecs_len = 2;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.codecs_len = 1;
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated CRCX message (three codecs, one with custom pt):\n");
@@ -214,8 +191,8 @@ void test_mgcp_msg(void)
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
mgcp_msg.codecs_len = 3;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated MDCX message:\n");
mgcp_msg.verb = MGCP_VERB_MDCX;
@@ -234,7 +211,7 @@ void test_mgcp_msg(void)
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
mgcp_msg.codecs_len = 2;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.codecs_len = 1;
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated MDCX message (three codecs, one with custom pt):\n");
@@ -245,8 +222,8 @@ void test_mgcp_msg(void)
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
mgcp_msg.codecs_len = 3;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated DLCX message:\n");
mgcp_msg.verb = MGCP_VERB_DLCX;
@@ -268,6 +245,48 @@ void test_mgcp_msg(void)
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
printf("%s\n", (char *)msg->data);
printf("Generate X-Osmo-IGN message:\n");
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.verb = MGCP_VERB_CRCX;
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE
| MGCP_MSG_PRESENCE_X_OSMO_IGN);
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
printf("%s\n", (char *)msg->data);
printf("Generate X-Osmo-Osmux message:\n");
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.verb = MGCP_VERB_CRCX;
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE
| MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID);
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
printf("%s\n", (char *)msg->data);
printf("Generate X-Osmo-Osmux message (fixed CID 2):\n");
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.verb = MGCP_VERB_CRCX;
mgcp_msg.x_osmo_osmux_cid = 2;
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE
| MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID);
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
printf("%s\n", (char *)msg->data);
printf("Generate X-Osmo-Osmux message (MDCX):\n");
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.verb = MGCP_VERB_MDCX;
mgcp_msg.x_osmo_osmux_cid = 2;
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE
| MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID);
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
printf("%s\n", (char *)msg->data);
printf("Overfolow test:\n");
mgcp_msg.verb = MGCP_VERB_MDCX;
mgcp_msg.presence =
@@ -300,7 +319,7 @@ void test_mgcp_client_cancel()
| MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE),
.ptime = 20,
.codecs[0] = CODEC_AMR_8000_1,
.codecs_len = 1
.codecs_len = 1
};
printf("\n%s():\n", __func__);
@@ -324,7 +343,7 @@ void test_mgcp_client_cancel()
OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == 0);
fprintf(stderr, "- late response gets discarded\n");
OSMO_ASSERT(reply_to(trans_id, 200, "OK", 1, "v=0\r\n") == -ENOENT);
OSMO_ASSERT(reply_to(trans_id, 200, "OK", "I: 1\r\n\r\nv=0\r\n") == -ENOENT);
fprintf(stderr, "- canceling again does nothing\n");
OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == -ENOENT);
@@ -551,7 +570,6 @@ int main(int argc, char **argv)
mgcp_client_conf_init(&conf);
test_crcx();
test_mgcp_msg();
test_mgcp_client_cancel();
test_sdp_section_start();

View File

@@ -1,6 +1,8 @@
DLMGCP MGCP client: using endpoint domain '@mgw'
DLMGCP message buffer to small, can not generate MGCP message
test_mgcp_client_cancel():
DLMGCP MGCP client: using endpoint domain '@mgw'
- composed msg with trans_id=1
- not in queue yet, cannot cancel yet
DLMGCP Cannot cancel, no such transaction: 1

View File

@@ -1,45 +1,4 @@
===== test_crcx =====
composed:
-----
CRCX 1 17@mgw MGCP 1.0
C: 2a
L: p:20, a:AMR, nt:IN
M: loopback
-----
composed response:
-----
200 1 OK
I: 1
v=0
o=- 1 23 IN IP4 10.9.1.120
s=-
c=IN IP4 10.9.1.120
t=0 0
m=audio 16002 RTP/AVP 110 96
a=rtpmap:110 AMR/8000
a=rtpmap:96 GSM-EFR/8000
a=ptime:20
-----
response cb received:
head.response_code = 200
head.trans_id = 1
head.comment = OK
audio_port = 16002
audio_ip = 10.9.1.120
ptime = 20
codecs_len = 2
codecs[0] = 112
codecs[1] = 110
ptmap_len = 2
ptmap[0].codec = 112
ptmap[0].pt = 110
ptmap[1].codec = 110
ptmap[1].pt = 96
Generated CRCX message:
CRCX 1 23@mgw MGCP 1.0
C: 2f
@@ -117,6 +76,38 @@ AUEP 8 23@mgw MGCP 1.0
Generated RSIP message:
RSIP 9 23@mgw MGCP 1.0
Generate X-Osmo-IGN message:
CRCX 11 23@mgw MGCP 1.0
C: 2f
I: 11
L: p:20, a:GSM, nt:IN
M: sendrecv
X-Osmo-IGN: C
Generate X-Osmo-Osmux message:
CRCX 13 23@mgw MGCP 1.0
C: 2f
I: 11
L: p:20, a:GSM, nt:IN
M: sendrecv
X-Osmux: *
Generate X-Osmo-Osmux message (fixed CID 2):
CRCX 15 23@mgw MGCP 1.0
C: 2f
I: 11
L: p:20, a:GSM, nt:IN
M: sendrecv
X-Osmux: 2
Generate X-Osmo-Osmux message (MDCX):
MDCX 17 23@mgw MGCP 1.0
C: 2f
I: 11
L: p:20, a:GSM, nt:IN
M: sendrecv
X-Osmux: 2
Overfolow test:
@@ -133,8 +124,8 @@ M: sendrecv
composed response:
-----
200 1 OK
I: 1
I: 1
v=0
-----

View File

@@ -1,19 +1,6 @@
AT_INIT
AT_BANNER([Regression tests.])
AT_SETUP([legacy_mgcp])
AT_KEYWORDS([legacy_mgcp])
cat $abs_srcdir/legacy_mgcp/mgcp_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/legacy_mgcp/mgcp_test], [], [expout], [ignore])
AT_CLEANUP
AT_SETUP([legacy_mgcp-trans])
AT_KEYWORDS([legacy_mgcp-trans])
AT_CHECK([test "$enable_mgcp_transcoding_test" == yes || exit 77])
cat $abs_srcdir/legacy_mgcp/mgcp_transcoding_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/legacy_mgcp/mgcp_transcoding_test], [], [expout], [ignore])
AT_CLEANUP
AT_SETUP([mgcp_client])
AT_KEYWORDS([mgcp_client])
cat $abs_srcdir/mgcp_client/mgcp_client_test.ok > expout

View File

@@ -1,171 +0,0 @@
#!/usr/bin/env python
# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
# (C) 2013 by Holger Hans Peter Freyther
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os, sys
import time
import unittest
import socket
import subprocess
import osmopy.obscvty as obscvty
import osmopy.osmoutil as osmoutil
# add $top_srcdir/contrib to find ipa.py
sys.path.append(os.path.join(sys.path[0], '..', 'contrib'))
from ipa import IPA
# to be able to find $top_srcdir/doc/...
confpath = os.path.join(sys.path[0], '..')
class TestVTYBase(unittest.TestCase):
def checkForEndAndExit(self):
res = self.vty.command("list")
#print ('looking for "exit"\n')
self.assert_(res.find(' exit\r') > 0)
#print 'found "exit"\nlooking for "end"\n'
self.assert_(res.find(' end\r') > 0)
#print 'found "end"\n'
def vty_command(self):
raise Exception("Needs to be implemented by a subclass")
def vty_app(self):
raise Exception("Needs to be implemented by a subclass")
def setUp(self):
osmo_vty_cmd = self.vty_command()[:]
config_index = osmo_vty_cmd.index('-c')
if config_index:
cfi = config_index + 1
osmo_vty_cmd[cfi] = os.path.join(confpath, osmo_vty_cmd[cfi])
try:
self.proc = osmoutil.popen_devnull(osmo_vty_cmd)
except OSError:
print >> sys.stderr, "Current directory: %s" % os.getcwd()
print >> sys.stderr, "Consider setting -b"
appstring = self.vty_app()[2]
appport = self.vty_app()[0]
self.vty = obscvty.VTYInteract(appstring, "127.0.0.1", appport)
def tearDown(self):
if self.vty:
self.vty._close_socket()
self.vty = None
osmoutil.end_proc(self.proc)
class TestVTYMGCP(TestVTYBase):
def vty_command(self):
return ["./src/osmo-bsc_mgcp/osmo-bsc_mgcp", "-c",
"doc/examples/osmo-bsc_mgcp/mgcp.cfg"]
def vty_app(self):
return (4243, "./src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "mgcp")
def testForcePtime(self):
self.vty.enable()
res = self.vty.command("show running-config")
self.assert_(res.find(' rtp force-ptime 20\r') > 0)
self.assertEquals(res.find(' no rtp force-ptime\r'), -1)
self.vty.command("configure terminal")
self.vty.command("mgcp")
self.vty.command("no rtp force-ptime")
res = self.vty.command("show running-config")
self.assertEquals(res.find(' rtp force-ptime 20\r'), -1)
self.assertEquals(res.find(' no rtp force-ptime\r'), -1)
def testOmitAudio(self):
self.vty.enable()
res = self.vty.command("show running-config")
self.assert_(res.find(' sdp audio-payload send-name\r') > 0)
self.assertEquals(res.find(' no sdp audio-payload send-name\r'), -1)
self.vty.command("configure terminal")
self.vty.command("mgcp")
self.vty.command("no sdp audio-payload send-name")
res = self.vty.command("show running-config")
self.assertEquals(res.find(' rtp sdp audio-payload send-name\r'), -1)
self.assert_(res.find(' no sdp audio-payload send-name\r') > 0)
# TODO: test it for the trunk!
def testBindAddr(self):
self.vty.enable()
self.vty.command("configure terminal")
self.vty.command("mgcp")
# enable.. disable bts-bind-ip
self.vty.command("rtp bts-bind-ip 254.253.252.250")
res = self.vty.command("show running-config")
self.assert_(res.find('rtp bts-bind-ip 254.253.252.250') > 0)
self.vty.command("no rtp bts-bind-ip")
res = self.vty.command("show running-config")
self.assertEquals(res.find(' rtp bts-bind-ip'), -1)
# enable.. disable net-bind-ip
self.vty.command("rtp net-bind-ip 254.253.252.250")
res = self.vty.command("show running-config")
self.assert_(res.find('rtp net-bind-ip 254.253.252.250') > 0)
self.vty.command("no rtp net-bind-ip")
res = self.vty.command("show running-config")
self.assertEquals(res.find(' rtp net-bind-ip'), -1)
if __name__ == '__main__':
import argparse
import sys
workdir = '.'
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", dest="verbose",
action="store_true", help="verbose mode")
parser.add_argument("-p", "--pythonconfpath", dest="p",
help="searchpath for config")
parser.add_argument("-w", "--workdir", dest="w",
help="Working directory")
parser.add_argument("test_name", nargs="*", help="(parts of) test names to run, case-insensitive")
args = parser.parse_args()
verbose_level = 1
if args.verbose:
verbose_level = 2
if args.w:
workdir = args.w
if args.p:
confpath = args.p
print "confpath %s, workdir %s" % (confpath, workdir)
os.chdir(workdir)
print "Running tests for specific VTY commands"
suite = unittest.TestSuite()
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestVTYMGCP))
if args.test_name:
osmoutil.pick_tests(suite, *args.test_name)
res = unittest.TextTestRunner(verbosity=verbose_level, stream=sys.stdout).run(suite)
sys.exit(len(res.errors) + len(res.failures))
# vim: shiftwidth=4 expandtab nocin ai