Compare commits

...

72 Commits

Author SHA1 Message Date
Oliver Smith
7d236cca56 Generate a version.h file
Related: OS#6626
Change-Id: I8460e85d50149f4bc58b6048e5596c5768010829
2025-06-23 16:48:06 +02:00
Pau Espin Pedrol
2fdedc0715 mgw: osmux: Fix heap-use-after-free
As found by Asan on a osmo-mgw running in production:
"""
 ==2238035==ERROR: AddressSanitizer: heap-use-after-free on address 0x62100043bdca at pc 0x7f9bcebaa070 bp 0x7ffcb08f2150 sp 0x7ffcb08f2148
 READ of size 2 at 0x62100043bdca thread T0
     #0 0x7f9bcebaa06f in msgb_length src/core/msgb.c:287
     #1 0x55869457a8ff in conn_osmux_send_rtp src/libosmo-mgcp/mgcp_osmux.c:245
     #2 0x558694563a86 in mgcp_dispatch_rtp_bridge_cb src/libosmo-mgcp/mgcp_network.c:1347
     #3 0x5586945570a9 in rx_rtp src/libosmo-mgcp/mgcp_network.c:1550
     #4 0x5586945570a9 in rtp_recvfrom_cb src/libosmo-mgcp/mgcp_network.c:1505
     #5 0x7f9bcebc96cc in iofd_poll_ofd_cb_recvmsg_sendmsg src/core/osmo_io_poll.c:84
     #6 0x7f9bcebcb699 in iofd_poll_ofd_cb_dispatch src/core/osmo_io_poll.c:136
     #7 0x7f9bcebd7df5 in poll_disp_fds src/core/select.c:419
     #8 0x7f9bcebd7df5 in _osmo_select_main src/core/select.c:457
     #9 0x7f9bcebd8298 in osmo_select_main src/core/select.c:496
     #10 0x558694534f2e in main src/osmo-mgw/mgw_main.c:428
"""

Related: SYS#7450
Change-Id: Id90c77aaf44422c3ed70ffb06560537e920a468c
2025-04-25 13:48:46 +02:00
Pau Espin Pedrol
4a09ace3b4 mgw: Update RTP local address on E1 endpoints too
E1 endpoints still have usually 1 RTP connection on it (they do
E1-slot<->RTP), so we still need to update the RTP addr+port binding
upon CRCX & MDCX on those endpoints.

This fixes a recent regression with setups with E1 BTS failing to
establish calls.

Fixes: b317b0056a
Change-Id: I52e91cd2d9d4b64961b76bb0fecc82494ca0d7cb
2025-03-18 11:19:45 +01:00
Pau Espin Pedrol
12c45577bb mgw: iuup: Trigger Init on CN-side CRCX if RAN-side was already initialized
Prior to this patch, osmo-mgw would wait until first IuUP Data frame
would be received on RAN-side (from HNB) to trigger active
initialization (Tx IuUP-Init) on CN-side conn towards CN.
With this patch, this happens ASAP, whenever the CN-side conn is created
by HNBGW when it receives the RAB-ASS-RESP from HNB.

Related: OS#6685
Change-Id: Iab0af88852994e73bfe6c3b27fe196fd655cce03
2025-03-11 11:13:02 +01:00
Pau Espin Pedrol
b317b0056a mgw: Move endpoint/conn updating logic to helper function
Move code logic doing actions based on endpoint state (and its conns)
out of the CRCX/MDCX MGCP handlers.
Most of that logic present in CRCX and MDCX is actually the same and can
be merged.
This way we can also make the updating logic more complex without
extending the CRCX/MDCX handlers for and more adding spaghetti code.

Change-Id: I24dd15ec76bd6e949a178a3b998b76a44ddbab50
2025-03-11 11:13:02 +01:00
Pau Espin Pedrol
a4ebbf7dbe mgw: mdcx: Fix inc of non-existing ctr in mdcx counter group
Change-Id: If849e3922c7ae864771c41ff7c0fac37ef0280de
2025-03-11 11:13:02 +01:00
Pau Espin Pedrol
8b23fb916a mgw: Rearrange last steps of CRCX and MDCX
Make code in CRCX and MDCX look closer, since it is really doing the
same.
Move the dummy ping code after everything has been done correctly, since
the ping is just a side effect of the conn being created.

Change-Id: I42acda16cb2d59a9b7aae06b7584d4dfc1e91f9e
2025-03-11 11:13:02 +01:00
Pau Espin Pedrol
c3ff38ee4c mgw: Drop rtp_processing transcoding stubs
This has never been used afaik, and it's only clogging the code and
adding extra indirect function pointer calls to hot paths for no good
reason.
Drop all of it, and re-ad whatever makes sense once someone really wants
to implement transcoding.

Change-Id: Id16af528d94612e771f94333ad5404e5cd1c3318
2025-03-11 11:13:02 +01:00
Pau Espin Pedrol
eeee783db2 mgw: CRCX: Handle codec information before initializing osmux
Same order as done during MDCX. This way we can also eg. check
configured codec is AMR before successfully entering configuring osmux.

Change-Id: If291db0c048196e6e0eee2c38e648e5a25438078
2025-03-11 11:13:02 +01:00
Pau Espin Pedrol
436f4dee17 mgw: Split DLCX read-only validations into its own function
Similar to what's already done in CRCX and MDXC.

Change-Id: I3d86f0cbeacc7c0cd69809f898714a76d3b86ed0
2025-03-11 11:13:02 +01:00
Pau Espin Pedrol
ea4aa7d3c1 mgw: Split MDCX read-only validation on conn into its own function
Add a second read-only validation step once the conn to be modified is
found in the endpoint.

Change-Id: I025ca5352eff9ee35c977c69b71934513646b1f2
2025-03-11 11:13:02 +01:00
Pau Espin Pedrol
2140c20a0b mgw: Split MDCX read-only validations into its own function
The SDP parsing is moved to a step beforehand, so that validation of
full message can be done in a subsequent step. That validation step is
moved into a function to give some air to handle_modify_con() and easily
spot the logic.

Change-Id: I469413dbe89cc4795deb50d76b434655cba9b776
2025-03-11 11:13:02 +01:00
Pau Espin Pedrol
485e085140 mgw: Increment rate_ctr MGCP_*_FAIL_UNHANDLED_PARAM during parsing
We could not do it before because we had no easy access to the command
verb being parsed. Now that we have access to it, bettter increase it
where we have exact path where stuff happens.

Change-Id: I3ea45fc1d25284b253ac9b7f0c0a925c10c994ca
2025-03-11 11:13:02 +01:00
Pau Espin Pedrol
c8b8106cb5 mgw: Split CRCX read-only validations into its own function
The SDP parsing is moved to a step beforehand, so that validation of
full message can be done in a subsequent step. That validation step is
moved into a function to give some air to handle_create_con() and easily
spot the logic.

The logic based on force_realloc is split so that code modifying the
object is moved to the later update step.

Change-Id: I639ad0a25a0af4a4637045ca8bf61e436a789426
2025-03-11 11:13:02 +01:00
Pau Espin Pedrol
c506ded3dd mgw: Split MGCP LocalConnectionOptions parsing from updating endpoint
Split current code into 2 steps, so first read-only parsing can be done,
then when all parsing and checks have been done, finally can be applied
o the object.

Change-Id: If75731fb92329dca6d092ffc7ff17cb354d28c0d
2025-03-11 10:12:29 +00:00
Mychaela N. Falconia
e96ad8f422 E1: implement dummy fill for HRv1 codec
In the absence of a proper TFO transform (TS 28.062 section
C3.2.1.1), OsmoMGW-E1 inserts constant fill frames when there is
no RTP input, or when RTP input fails conversion to TRAU-DL and
thus does not enqueue anything to the I.460 mux.

This dummy fill was previously implemented for FR and EFR codecs,
but not for HRv1.  Fix this omission.

With this change and with a fix to OsmoBSC to select 8k subslot
endpoints for TCH/H instead of 16k, OsmoMGW-E1 now supports HR
codec to the same degree to which it supports FR and EFR: far from
perfect, but mostly usable, especially if DTXu is disabled
and there is no TrFO interworking with a far end that does DTXu.

Change-Id: I188bc0a0468b19126281016f45e36f1de617e9ee
2025-03-10 19:26:57 +00:00
Mychaela N. Falconia
9253bb193f E1: change dummy fill frame generation method
In the absence of a proper TFO transform (TS 28.062 section
C3.2.1.1), OsmoMGW-E1 inserts constant fill frames when there is
no RTP input, or when RTP input fails conversion to TRAU-DL and
thus does not enqueue anything to the I.460 mux.  (We accept
TW-TS-001 and TW-TS-002 from RTP side, but bad frames cannot be
converted to TRAU-DL without a TFO transform.)

Prior to the present change, this idle fill of the DL was done
using fully hard-coded frames, i.e., the operation of converting
the desired fill to TRAU-DL bit format was done once externally
and the raw bit frames were hard-coded.  Change this approach:
use fill frames in RTP format provided by libosmocodec, and
execute osmo_rtp2trau() followed by osmo_trau_frame_encode() in
the fill frame output path just like we do for regular RTP to DL.

Depends: libosmocore.git I2c510ac62a0786c137115c45eee7a48b9736265f
Change-Id: I123f77295c56d61ff9aac5f5d64b25eca01e0d63
2025-03-10 19:26:57 +00:00
Mychaela N. Falconia
6018cc4d8a MGCP extension: add parameters for TW-TS-001 & TW-TS-002
TW-TS-001 and TW-TS-002 are enhanced RTP transport formats for
GSM FR/HR/EFR codecs that restore the lost semantics of GSM 08.60
and 08.61 TRAU-UL frames.

TW-TS-003 BSSMAP extension provides a way for a core network to
ask the BSS to use these TRAU-like enhanced RTP formats instead of
those specified in 3GPP TS 48.103; OsmoBSC already supports this
mechanism when the BSS is comprised of IP-native OsmoBTS.

However, in order to achieve the same effect when using E1-based
legacy BTS hardware, the task of generating TW-TS-001/002 RTP
packets for UL and accepting them for DL moves from OsmoBTS to
the E1-Abis-interfacing MGW.  osmo_trau2rtp() is already capable
of generating these extended RTP formats on request, but until now
there was no mechanism to signal from OsmoBSC to its associated
E1 Abis MGW whether and when these extensions should be used.

Considering that MGCP as it is used in Osmocom is essentially a
private interface between OsmoBSC or OsmoMSC masters and OsmoMGW
slaves, while the externally defined and generally interoperable
interface is 3GPP TS 48.008, possibly extended with TW-TS-003,
the sensible solution is to make a private extension to the way
FR, EFR and HR codecs are described in SDP in the context of
Osmocom-MGCP.

The SDP extension birthed in the present patch consists of an fmtp
parameter for GSM, GSM-EFR and GSM-HR-08 codecs that is structured
just like the already implemented octet-align parameter for AMR.
TW-TS-001 for FR and EFR shall be described as follows:

m=audio 1234 RTP/AVP 3 110
a=rtpmap:3 GSM/8000/1
a=fmtp:3 tw-ts-001=1
a=rtpmap:110 GSM-EFR/8000/1
a=fmtp:110 tw-ts-001=1

TW-TS-002 for HR codec shall be described as follows:

m=audio 1234 RTP/AVP 111
a=rtpmap:111 GSM-HR-08/8000/1
a=fmtp:111 tw-ts-002=1

The present patch affects two areas:

* Experimental support for the newly defined extension is added to
  OsmoMGW-E1.  This support is deemed experimental (not for
  production use) because even with this extension added, OsmoMGW-E1
  is still unable to satisfy ThemWi requirements: neither ThemWi
  RTP endpoint library nor the TFO transform of TS 28.062 section
  C.3.2.1.1 are currently available in the repertoire of libraries
  whose use is allowed in mainline OsmoCNI components, yet both are
  required.

* Support is added to libosmo-mgcp-client whereby OsmoBSC will be
  able to issue CRCX and MDCX commands to E1 Abis MGW endpoints
  with TW-TS-001 and/or TW-TS-002 enabled.  Adding the necessary
  support to OsmoBSC will allow a complete working system to be
  deployed using OsmoBSC plus tw-e1abis-mgw, a replacement for
  OsmoMGW-E1 that works by using both Osmocom and ThemWi libraries.

Related: OS#6614
Change-Id: I0d58e6d84418f50670c8ab7cf8490af3bc2f5c26
2025-03-10 19:26:57 +00:00
Pau Espin Pedrol
6d629768c7 mgw: Allocate req and pdata with talloc
This will allow allocating parsed objects as child of the request and
make sure they are freed after handling the request finishes.
Change-Id: I2b971d0c8268f4c0a30b84b54a2e5f16ada4ecdb
2025-03-10 12:17:53 +00:00
Pau Espin Pedrol
5ec1c13221 mgw: Use tall_mgw_ctx to allocate struct mgcp_config
Change-Id: Ib51f1066bb73da4472bd8967cc1b1c44e964e9e5
2025-03-10 12:17:53 +00:00
Pau Espin Pedrol
f63ef75d9f mgw: Add backpointer from pdata to req to have context available
This will also allow eg. parsing/validating differently based on the
command being parsed, or incrementing rate counters per cmd group.

Change-Id: I464258ca1a8817d58ae5c5426dfc3b7cee6763d3
2025-03-10 12:17:53 +00:00
Pau Espin Pedrol
741d0ece37 mgw: Move cfg pointer from pdata to rq
The externa object context is held in rq object, not in pdata object,
which holds parsed msg state only.

Change-Id: Ie64fb6250cc84f1e7737b5c1cf5cca314c7020ed
2025-03-10 12:17:53 +00:00
Pau Espin Pedrol
60eaa0d909 mgw: Move struct mgcp_request_data definition to header file
All the related handling structs are placed in the header file, let's
move them there. This will also allow accessing the rq object
information in other files.

Change-Id: I1b2831874c1aaad054ad19bf87646062ba9d4da4
2025-03-10 12:17:53 +00:00
Pau Espin Pedrol
8d9d1d5e72 mgw: Store Command as enum in struct mgcp_request_data
This simplifies code further, and will simpify more code since we'll
have some easy way to check for the command we are in during a request.

Change-Id: I2d4b5ddb93376b59413b34c9668c41157ab05497
2025-03-10 12:17:53 +00:00
Pau Espin Pedrol
533cf424e7 mgw: Delay osmux trunk setup to point where it is really needed
This way code is simplified by putting all osmux code together.

Change-Id: I27f7355ee99f1781fc2034a23c7cca756fb203d2
2025-03-10 12:17:53 +00:00
Pau Espin Pedrol
6f4cd1b7e1 mgw: Validate CallID before updating endp->x_osmo_ign
This allows already starting to mark a clear line in CRCX handling
function between *read-only parsing + validation* and *read-write
allocation and update* of objects.

This in turn will allow further clean up on the second mentioned step.

Change-Id: Ia41f7383171bb24f48297456935c633536aa7b05
2025-03-10 12:17:53 +00:00
Mychaela N. Falconia
9d719b5d96 cosmetic: add copy of mgcp_common.h to .gitignore
The build process copies include/osmocom/mgcp/mgcp_common.h to
include/osmocom/mgcp_client/mgcp_common.h; the former is a true
source while the latter is a build-generated file.  The copied
file needs to be included in .gitignore, otherwise it adds
noise to git status output.

Change-Id: Icf0e3f724564d87eef9351b22de7a3d3aa7e4264
2025-03-07 16:20:12 +00:00
Pau Espin Pedrol
79fd5a4f47 mgw: Early reject CRCX with X-Osmux if disabled by config
This new behavior rejects the CRCX before creating a conn, simplifying
the code and also allows getting rid of the remote_osmux_cid local variable.

Change-Id: I0245b6c02bf7a3452532e8bf0d7c33479999ce9f
2025-03-06 16:55:31 +01:00
Pau Espin Pedrol
5d0080b69d cosmetic: Drop incorrect comment
The writer of this commit wrongly interpreted conn->end to be an
mgcp_endpoint, but it's actually an struct mgcp_rtp_end, hence codecs
are set per connection as expected.

Change-Id: I773065972342b8cedc900e3d194c48775c3adb5a
2025-03-06 16:33:20 +01:00
Pau Espin Pedrol
43279942c3 Drop use of deprecated vty is_config_node() cb
This callback was drepecated and is not ever called since
libosmocore.git 70ce871532ab21955e0955d7e230eae65438f047 (release 1.3.0).

See also libosmocore.git d31de237582f6fe3315d61bb9a488d4cda92654e.

Change-Id: Ifdecb35f329c243222bc5d77f0a6d394c9413684
2025-03-04 18:16:49 +01:00
Oliver Smith
2eacae8f2e Bump version: 1.13.1.41-403d5-dirty → 1.14.0
Change-Id: Ib6f5b6ee771adb967fa6cebae03c519839693566
2025-02-12 12:32:06 +01:00
Mychaela N. Falconia
403d5f1489 E1: replace idle_tf_fr[] with a better version
The only truly correct way to pass FR/HR/EFR traffic from an
incoming RTP stream to TRAU-DL frames is to apply the transform
of TS 28.062 section C.3.2.1.1, originally specified for TFO
but also necessary in the non-3GPP-specified case of GSM TrFO
as it happens here.  Unlike the situation with EFR, a FOSS
implementation of TFO transform for FRv1 does exist in Themyscira
libgsmfr2 - however, making these Themyscira codec libraries
usable from mainline Osmocom programs by way of a proposed
libosmocore DSO plugin mechanism, followed by a major redesign
of OsmoMGW-E1 to pass all DL traffic through this TFO transform,
would be a major project, difficult to justify prior to development
of a proper TFO transform for EFR to complement the one for FRv1.
Hence no change is being made currently to the arguably flawed
general architecture of OsmoMGW-E1, which consists of passing
through unaltered all valid codec frames that are received in RTP,
and inserting fixed dummy TRAU-DL frames when no RTP-derived ones
are available.

The original dummy TRAU-DL frame for FRv1 exhibited the following
defects:

* The payload frame transmitted to the MS consisted of all zero
  bits.  Standard type-approved GSM MS would interpret this bit
  pattern as a SID frame, even though the BTS is told via C16 bit
  "this frame is not a SID".  Passing a SID is not inherently bad
  in itself in this context, but a SID with all LARc parameters
  set to 0 is a strange/poor choice.

* The setting of C-bits (defined in TS 48.060 section 5.5.1.1.1)
  was a mish-mash between TRAU-UL and TRAU-DL frame formats,
  thereby not matching the standard TRAU-DL C-bits fill produced
  by osmo_rtp2trau().

The replacement fill frame introduced here differs as follows:

* The payload bit content is the silence frame of GSM 46.011
  Table 1: certainly better than a SID with all LARc parameters
  set to 0, and arguably better than any SID at all.

* The full TRAU-DL frame (C-bits pattern, peculiar ordering of
  D-bits) was generated by passing said silence frame through
  osmo_rtp2trau(), using this ad hoc tool:

https://gitea.osmocom.org/themwi/dummy-dl-frames

Change-Id: I995824586058e4e4ed77e900d4b57e5113f9eff6
2025-02-05 21:37:23 +00:00
Mychaela N. Falconia
f227633959 E1: replace idle_tf_efr[] with a better version
The only truly correct way to pass FR/HR/EFR traffic from an
incoming RTP stream to TRAU-DL frames is to apply the transform
of TS 28.062 section C.3.2.1.1, originally specified for TFO
but also necessary in the non-3GPP-specified case of GSM TrFO
as it happens here.  However, no FOSS or even published-source
implementation of TFO transform for EFR exists at the present time
(no implementations at all are known outside of TRAU DSP firmware),
hence the general architectural approach of OsmoMGW-E1, flawed
as it is, has to remain for now.  This flawed architecture consists
of passing through unaltered all valid codec frames that are
received in RTP, and inserting fixed dummy TRAU-DL frames when no
RTP-derived ones are available.

The original dummy TRAU-DL frame for EFR exhibited the following
defects:

* D1 bit was set to 0, even though TS 48.060 section 5.5.1.1.2
  states it shall be 1;

* All 5 EFR frame CRCs were invalid: each 3-bit CRC was 000, even
  though the correct TS 48.060 section 5.5.1.1.2 CRC from an
  all-zeros payload would be 111;

* Even if the 5 CRCs were fixed, transmitting an EFR frame of all
  zero bits to the MS won't produce good results - it is a bad
  choice of dummy filler;

* The setting of C-bits (defined in TS 48.060 section 5.5.1.1.1)
  was a mish-mash between TRAU-UL and TRAU-DL frame formats,
  thereby not matching the standard TRAU-DL C-bits fill produced
  by osmo_rtp2trau().

The replacement fill frame introduced here differs as follows:

* The payload bit content is the decoder homing frame (DHF) of
  TS 46.060 section 8.2 Table 7 - the best we can do in the absence
  of a proper TFO transform for EFR;

* The full TRAU-DL frame (C-bits, CRC etc) was generated by passing
  said EFR DHF through osmo_rtp2trau(), using this ad hoc tool:

https://gitea.osmocom.org/themwi/dummy-dl-frames

Change-Id: I5a33a1c9ddf1372f91870d61b5eafac4729ee458
2025-02-05 21:03:29 +00:00
Mychaela N. Falconia
83c8846728 E1 cosmetic: reduce white space in hard-coded TRAU-DL frames
By changing the white space structure of these hard-coded frames
to only have one leading tab on each line of 8 frame bits, we make
it easier to replace these hard-coded frames with different versions
that are programmatically generated, as will be done in the following
patches.

Change-Id: I3d0c931d4dc3d916ba4c5eb081bea22d9c7144de
2025-02-05 20:08:59 +00:00
Pau Espin Pedrol
78ee99adcf mgw: MDCX: Simplify early return code paths
Change-Id: Icb03a95e34e0c7d9396fefc2e37ee33ab09daa89
2025-01-21 16:48:52 +01:00
Pau Espin Pedrol
de4090d920 mgw: Remove wrong TODO comment
The TODO was written because the author had in mind the ptr where the
codec was stored was a MGCP endpoint object, but it is actually an
rtp_end which is an object under conn_rtp, so that's fine already with
current code.

Change-Id: I99d2211e81443883c45cc3fdda10e39a8c152063
2025-01-21 16:48:52 +01:00
Pau Espin Pedrol
732cadd2c8 mgw: Decouple SDP parsing step from conn obj update
This allows delaying modification of the conn before full parsing
succeeds.

Change-Id: I4a2aecb542886672c1f586c6607714e0356abe35
2025-01-21 16:48:52 +01:00
Pau Espin Pedrol
6747b60a95 mgw: DLCX: Split mgcp header pars parsing into a previous step
Do most of the initial parsing and verification in a prior step, filling
in a "parsed" struct which simplifies logic coming after for different
message types.

Change-Id: I557a3a257ddefedc479a4aff974a74c4e4e2c85d
2025-01-21 16:48:52 +01:00
Pau Espin Pedrol
90ae9ed0a2 mgw: MDCX: Split mgcp header pars parsing into a previous step
Do most of the initial parsing and verification in a prior step, filling
in a "parsed" struct which simplifies logic coming after for different
message types.
This commit only modifies stuff enough to work for MDCX.
Further work (commits) will follow for DLCX.

Change-Id: I6ecb41fc2cc737c3a161b6bc98bd08ae01909655
2025-01-21 16:48:52 +01:00
Pau Espin Pedrol
206be49c69 mgw: CRCX: Split mgcp header pars parsing into a previous step
Do most of the initial parsing and verification in a prior step, filling
in a "parsed" struct which simplifies logic coming after for different
message types.
This commit only modifies stuff enough to work for CRCX.
Further work (commits) will follow for MDCX and DLCX.

Change-Id: I3ee5158c254213203830fe9c38de11c15b4b19c1
2025-01-21 16:48:52 +01:00
Pau Espin Pedrol
45559e9230 mgw: Rename and cleanup code allocating rtp/rtcp ports in trunk
Clean up pointers passed. Rename function since it's clearly operating
on trunk related fields through mutexes.

Change-Id: Ib894afcb61609c247883d5ccdd7b8fbf29b2cbf8
2025-01-14 16:44:33 +01:00
Pau Espin Pedrol
f94b846224 mgw: Clean up code allocating conn_rtp rtp/rtcp sockets
Clean up pointers, join 2 functions only making stuff more confusing.
Rename function to make it clear it operates on a conn_rtp object.

Change-Id: I50a251b66e85ceeeccb30dcd1813863d7c754961
2025-01-14 16:44:28 +01:00
Pau Espin Pedrol
3ea3a69821 mgw: Simplify and redo code around ssrc patch feature
The previous logic to configure SSRC patching was over complex and
confusing, with even some broken logic like "patch only once", which is
not really needed. Hence, simplify the internal logic to either patch
SSRC always or don't do it, based on VTY configuration.

The "force SSRC" feature was added in
db2d431697 and further changed in
e2292f3aa1, due to the need for nanoBTS to
always keep the same SSRC on received packets. However, it makes no
sense that is actually needed only once: if remote end changed several
times, then we should keep patching in that case.

This change allows getting rid of confusing applying of settings at a
later time through mgcp_rtp_end_config(), which is no longer needed and
can be dropped, simplifying steps during CRCX/MDCX/DLCX.

Change-Id: Ib7216a775f4ad126e62b9e99aff381fd45015819
2025-01-07 11:47:56 +01:00
Pau Espin Pedrol
46ddc65626 mgw: Move several params setting to mgcp_rtp_end_init()
There's no need to set those 2 params later on, simply set them during
init() to simplify code.

Change-Id: I38e2cfbe03c1e2de48e48f30a04af746bc2368a4
2025-01-07 11:47:56 +01:00
Pau Espin Pedrol
71f313749e mgw: Move force_ptime logic outside of main CRCX func handler
Apply the forced value automatically during init, and then only
override it based on received input from MGCP when it has not been
forced by VTY config.

Change-Id: I02a92a090887caa8e05917a44c8b2351fa7b9b50
2025-01-07 11:47:56 +01:00
Pau Espin Pedrol
80a5abbbe3 mgw: Cleanup rtp_endp fields in its own function
Change-Id: I0b2ccb94129dc7aed6fe13bb20f9910a53d8a41e
2025-01-07 11:47:56 +01:00
Pau Espin Pedrol
ba812f7e86 mgw: Introduce struct mgcp_codecset struct
We do tons of operations on 3 fields (array of codecs, len of array,
selected code) which can be isolated.
Right now, we are using APIs which somehow require structs 2-3 levels of
encapsulation above the ones really required, which makes all code
totally entangled, difficult to understand and prone to errors when
changing stuff in deeply nested structs.
A prove of this is how this patch affects a lot of lines in lots of
places.

Change-Id: Id7db7ab01d56b7fa2415123b604375e48c82ab25
2025-01-07 11:47:56 +01:00
Pau Espin Pedrol
345c37a543 mgw: Introduce mgcp_rtp_end_init()
Change-Id: I29f57dfde60e3134f1db422dc63e7c58d3db7d54
2025-01-07 11:47:56 +01:00
Pau Espin Pedrol
b3457cd250 mgw: Split mgcp_rtp_end to its own file
Preparation path to add an init function and start untangling related
code.

While at it, move definition of struct mgcp_rtp_codec to the mgcp_codec.h where it belongs.

Change-Id: Id1b6cab57e44ad4859bde8212d0ac9f7146d7198
2025-01-07 11:47:56 +01:00
Pau Espin Pedrol
fcf1864db3 Rename mgcp_free_rtp_port() to mgcp_rtp_end_free_port()
While at it, moving the only clearly existing API for the same object to
the same place to have them together
(mgcp_rtp_end_remote_addr_available()).

Change-Id: Ifabd1cf69273a6d22feb65c08de590d083987d09
2025-01-07 11:47:56 +01:00
Pau Espin Pedrol
9221b67f3b mgw: Use bool instead of int in local var
Change-Id: Ie46cb2909da2ec4279e1029cfaf7747c45f84b13
2025-01-07 11:47:56 +01:00
Pau Espin Pedrol
732d595a26 mgw: CRCXMDCX/DLCX: rename conn and conn_rtp variables
It's currently difficult to gasp whether a pointer is a conn or a
conn_rtp, so rename it.

Change-Id: I75ba9969af53d3e386c4070eb15de27e7378bfdc
2025-01-07 11:47:56 +01:00
Pau Espin Pedrol
3bc9d53628 mgw: Split conn mode parsing and applying into conn
Change-Id: I456843de0be1997802905873c057a83430327f50
2025-01-07 11:47:56 +01:00
Pau Espin Pedrol
b5a4f45dbf mgw: mgcp_protocol: assert freeing last conn allows creating new conn
Change-Id: Ib8c9089a7f04bc67e5bdfb1148cfc603f0e17bb7
2025-01-07 11:47:56 +01:00
Pau Espin Pedrol
e6bafbf3e3 mgcp_endp: Add helpers accessing endp connections
This has several benefits:
* easily improving/changing the impelemntation later on (eg. by storing
  a count instead of iterating the whole list).
* Remove code complexity from mgcp_protocol.c.

Change-Id: I77c298abf0a1488b91f58c9e76507ef8add0168e
2025-01-07 11:47:56 +01:00
Pau Espin Pedrol
5c7b128166 mgw: constify mgcp_endp_avail() param
Change-Id: I31ed2f3abbcf024ee44f62e130dd4ec01611ae97
2025-01-07 11:47:39 +01:00
Pau Espin Pedrol
5469edc278 cosmetic: mgw: iuup: Update comment
Change-Id: I6e1448bf7e3bb7454754147e2b2e01844c157500
2024-12-23 13:36:31 +01:00
Pau Espin Pedrol
66952183a9 mgw: Avoid 2nd lookup of conn in endp during CRCX
There's no need to lookup (iterate over conns) for rtp_conn in endp, we
have it accessible in constant time.

While at it, simplify some accesses to the pointer after it.

Change-Id: I8316e76a1789d4a8eaa1eb89bdaf86e25dc0a1e8
2024-12-23 13:35:21 +01:00
Pau Espin Pedrol
cc97f58d96 mgw: Clean up access to conn_rtp from conn
Add a new mgcp_conn_get_conn_rtp() and use it everywhere instead of
accessing deep structure fields.

Change-Id: Iee2c19598e9570ea3b1ceba3cdfd2a5f5be2c954
2024-12-23 11:49:21 +01:00
Pau Espin Pedrol
5d6931881a mgw: mgcp_network.c: Simplify use of conn_rtp ptr
Change-Id: I5bab15fc793434173660769a8e60dae4ae4aa4c6
2024-12-23 11:49:21 +01:00
Pau Espin Pedrol
0f4be7699b mgw: Rename and move several get_conn funcs acting on endp object
These functions act on the endp object, hence prefix them with
mgcp_endp_* and move them to mgcp_endp.{c,h}.

Change-Id: Ifff01331db68998e9e23f99d8836d96022d550c2
2024-12-23 11:49:21 +01:00
Pau Espin Pedrol
546983cdbc mgw: Rename and move code freeing endp connection
All the code to free conns in an endpoint is really entanged, which also
makes it inefficient in several places because the list of conns is
iterated twice, or even ~N*2.

* Move code freeing the conn object to its own mgcp_conn_free()
  function.
* Rename functions acting on endp to be mgcp_endp_* prefix, and move
  them to mgcp_endp.{c,h}.
* Remove old mgcp_conn_free(endp, id) since it's not really needed
  anymore.

Change-Id: I9a87a07699dd8aa7856d5036e9b95a4ddf699a15
2024-12-23 11:49:21 +01:00
Pau Espin Pedrol
c04de4e7bb mgcp-client: Fix regression checking null ptr
A recent patch introduced a bug checking incorrectly for null ptr.

Related: Coverity CID#445417
Related: Coverity CID#445418
Fixes: e6ef4e7484
Change-Id: I1a458a34376851aec73c14a658cccd6132a5eb6c
2024-12-23 11:38:33 +01:00
Pau Espin Pedrol
d46cdd80de cosmetic: mgw: Fix indentation whitespace
Change-Id: Ic5b267108b5c5cba064ddddbe688705f9dc29ac8
2024-12-19 18:16:38 +01:00
Pau Espin Pedrol
6676e63c89 mgw: Drop own MGCP extension 'noanswer'
This was added in e3d16bb775 14 years ago:
"""
[mgcp] Protocol extension to not generate answers.

    For the NAT we want to send requests in a send and forget
    way and we are not interested in seeing the answers, so tell
    the gateway to not answer them.
"""

So this code is most probably related to osmo-bsc_mgcp time, and we
don't really need this feature in osmo-mgw, it really makes no sense.
Drop it.

Change-Id: Ica2d3d54cda41e0c6416913ab056843a0cd80c17
2024-12-19 18:14:19 +01:00
Pau Espin Pedrol
1f99c3aaae mgcp-cli: Fix filling in wrong local IP address of SDP Origin o=
If user (VTY) configured the local IP address to use for MGCP, and that
IP address was not the one selected by kernel routing table lookup, the
IP address filled in the MGCP message would be wrong, not matching the
one sending the MGCP message.

Change-Id: I35624db853dc1f0fee85503105960613f70473c6
2024-12-19 16:59:47 +01:00
Pau Espin Pedrol
e6ef4e7484 mgcp-cli: Improve error handling around mgcp_msg_gen() return
Change-Id: Ib9b02b7e6b37472c8e38b3380bc990a2bbac0657
2024-12-19 16:41:05 +01:00
Pau Espin Pedrol
b0fa041a28 mgcp-cli: Mark iofd ptr as NULL when freed
Change-Id: Ie3cd7b49c9cd5514f81289ac5ec1ccee14c8509c
2024-12-19 16:28:22 +01:00
Pau Espin Pedrol
e17bf77bdd mgcp_client_internal.h: Add missing header dependency
struct mgcp_client defined here has a field "struct mgcp_client_conf
actual", which is defined in osmocom/mgcp_client/mgcp_client.h.

Change-Id: I61a67c371dbcfab073fa75ba08a3cac0f46e0ac0
2024-12-19 16:16:41 +01:00
Pau Espin Pedrol
802b1fad61 mgcp-client: Fix wrong value passed to strerror()
errno is positive, hence we need to change the value of "res" being
passed to strerror().
While at it, use thread-safe variant strerror_r().

Change-Id: Ibc278199bc8f66e5521bff72bad150f44716f3eb
2024-12-12 14:05:03 +01:00
Pau Espin Pedrol
8a8973eec2 jenkins.sh: Use --disable-doxygen configure param
Change-Id: I0f7d2b212834a3dea356de45fadb81a7e7176191
2024-12-10 17:04:42 +01:00
Pau Espin Pedrol
17e7ea66e4 jenkins.sh: libosmo-netif no longer depends on libosmo-abis
Change-Id: I64c4d46eae0bc131a7b4133cb79367837cb91c3b
Depends: libosmo-abis.git Change-Id I079dc3999de508301dd37ed03e399356a58d3cab
Depends: libosmo-netif.git Change-Id I13d6e88158f6d9ce017986283183ee9c2cc68cae
2024-11-21 14:51:56 +01:00
48 changed files with 2151 additions and 1762 deletions

2
.gitignore vendored
View File

@@ -8,6 +8,8 @@ Makefile
Makefile.in
bscconfig.h
bscconfig.h.in
include/osmocom/mgcp_client/mgcp_common.h
include/osmocom/mgcp_client/version.h
src/osmo-mgw/osmo-mgw
*.*~
*.sw?

View File

@@ -9,3 +9,4 @@
#library what description / commit summary line
libosmocore bump_dep; workaround Bump libosmocore version dependency after I68328adb952ca8833ba047cb3b49ccc6f8a1f1b5
has been merged to libosmocore.git; then remove my_msgb_copy_c wrapper function.
libosmocodec bump_dep We depend on the additions of I2c510ac62a0786c137115c45eee7a48b9736265f

View File

@@ -44,13 +44,14 @@ AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DLSYM)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 2.0.0)
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 2.0.0)
CFLAGS="$CFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
CPPFLAGS="$CPPFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"

View File

@@ -22,7 +22,7 @@ export deps inst
osmo-clean-workspace.sh
mkdir "$deps" || true
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
osmo-build-dep.sh libosmocore "" --disable-doxygen
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
@@ -30,8 +30,8 @@ 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-netif "" --disable-doxygen
osmo-build-dep.sh libosmo-abis
osmo-build-dep.sh libosmo-netif
# Additional configure options and depends
CONFIG=""

49
debian/changelog vendored
View File

@@ -1,3 +1,52 @@
osmo-mgw (1.14.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* jenkins.sh: libosmo-netif no longer depends on libosmo-abis
* jenkins.sh: Use --disable-doxygen configure param
* mgcp-client: Fix wrong value passed to strerror()
* mgcp_client_internal.h: Add missing header dependency
* mgcp-cli: Mark iofd ptr as NULL when freed
* mgcp-cli: Improve error handling around mgcp_msg_gen() return
* mgcp-cli: Fix filling in wrong local IP address of SDP Origin o=
* mgw: Drop own MGCP extension 'noanswer'
* cosmetic: mgw: Fix indentation whitespace
* mgcp-client: Fix regression checking null ptr
* mgw: Rename and move code freeing endp connection
* mgw: Rename and move several get_conn funcs acting on endp object
* mgw: mgcp_network.c: Simplify use of conn_rtp ptr
* mgw: Clean up access to conn_rtp from conn
* mgw: Avoid 2nd lookup of conn in endp during CRCX
* cosmetic: mgw: iuup: Update comment
* mgw: constify mgcp_endp_avail() param
* mgcp_endp: Add helpers accessing endp connections
* mgw: mgcp_protocol: assert freeing last conn allows creating new conn
* mgw: Split conn mode parsing and applying into conn
* mgw: CRCXMDCX/DLCX: rename conn and conn_rtp variables
* mgw: Use bool instead of int in local var
* Rename mgcp_free_rtp_port() to mgcp_rtp_end_free_port()
* mgw: Split mgcp_rtp_end to its own file
* mgw: Introduce mgcp_rtp_end_init()
* mgw: Introduce struct mgcp_codecset struct
* mgw: Cleanup rtp_endp fields in its own function
* mgw: Move force_ptime logic outside of main CRCX func handler
* mgw: Move several params setting to mgcp_rtp_end_init()
* mgw: Simplify and redo code around ssrc patch feature
* mgw: Clean up code allocating conn_rtp rtp/rtcp sockets
* mgw: Rename and cleanup code allocating rtp/rtcp ports in trunk
* mgw: CRCX: Split mgcp header pars parsing into a previous step
* mgw: MDCX: Split mgcp header pars parsing into a previous step
* mgw: DLCX: Split mgcp header pars parsing into a previous step
* mgw: Decouple SDP parsing step from conn obj update
* mgw: Remove wrong TODO comment
* mgw: MDCX: Simplify early return code paths
[ Mychaela N. Falconia ]
* E1 cosmetic: reduce white space in hard-coded TRAU-DL frames
* E1: replace idle_tf_efr[] with a better version
* E1: replace idle_tf_fr[] with a better version
-- Oliver Smith <osmith@sysmocom.de> Wed, 12 Feb 2025 12:30:33 +0100
osmo-mgw (1.13.1) unstable; urgency=medium
[ Philipp Maier ]

6
debian/control vendored
View File

@@ -6,9 +6,9 @@ Build-Depends: debhelper (>= 10),
dh-autoreconf,
pkg-config,
autotools-dev,
libosmocore-dev (>= 1.10.0),
libosmo-netif-dev (>= 1.5.0),
libosmo-abis-dev (>= 1.6.0),
libosmocore-dev (>= 1.11.0),
libosmo-netif-dev (>= 1.6.0),
libosmo-abis-dev (>= 2.0.0),
osmo-gsm-manuals-dev (>= 1.6.0)
Standards-Version: 3.9.8
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw

View File

@@ -70,3 +70,85 @@ a=ptime:40
=== `X-Osmux`
See <<mgcp-extension-osmux>>
=== Non-standard SDP codec parameters
CRCX command message normally includes an SDP block that specifies the RTP
address of the connecting peer and the list of codecs accepted by that entity.
Codec specifications may include optional parameters given via `a=fmtp` lines;
one well-known parameter is `octet-align`, defined for AMR and AMR-WB codecs.
In addition to standard (RFC-defined) codec parameters (of which the
just-mentioned `octet-align` parameter is the only one implemented so far),
OsmoMGW also understands some non-standard (Osmocom-private) codec parameters
as listed below.
These Osmocom-private codec parameters are meaningful only in the context
of OsmoBSC driving an E1 Abis MGW, which may be either OsmoMGW or a specialized
third-party replacement.
The MGCP extension described in this section is meaningful only for E1
endpoints - it has absolutely no effect on `rtpbridge` endpoints!
==== `tw-ts-001` parameter for FR and EFR codecs
GSM Full Rate codec is known in SDP as `GSM`; GSM Enhanced Full Rate codec
is known in SDP as `GSM-EFR`.
When either codec is to be transported in RTP, there is a choice between
two different payload formats: RFC 3551 or TW-TS-001.
RFC 3551 is the standard in non-GSM Internet applications, and it is the
format specified in later 3GPP releases for IP-based transport of
compressed speech - however, it exhibits functional regressions compared
to the original TRAU-UL frame format of GSM 08.60.
TW-TS-001 is an enhanced alternative payload format for the same codecs
that restores the original functionality and semantics of TRAU-UL frames.
In the context of CRCX or MDCX command messages from OsmoBSC to an E1 Abis MGW,
`tw-ts-001` codec parameter (attached to either `GSM` or `GSM-EFR` codec)
instructs the MGW whether or not it should emit the extended payload format
of TW-TS-001 for GSM uplink traffic converted to RTP.
In the absence of this parameter, IETF-standard and 3GPP-standard RFC 3551
format is emitted; if TW-TS-001 output is desired, please specify
`tw-ts-001=1` on the appropriate `a=fmtp` line.
.Example: `CRCX` message requesting EFR codec with TW-TS-001 extensions
----
CRCX 2 ds/e1-0/s-2/su16-2@mgw MGCP 1.0
M: recvonly
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 1234 RTP/AVP 110
a=rtpmap:110 GSM-EFR/8000/1
a=fmtp:110 tw-ts-001=1
----
==== `tw-ts-002` parameter for HR codec
GSM Half Rate codec is known in SDP as `GSM-HR-08`.
The same dichotomy that exists between RFC 3551 and TW-TS-001 RTP payload
formats for FR and EFR codecs also exists for HR codec, except that
the two opposing alternatives are now RFC 5993 and TW-TS-002.
In the context of CRCX or MDCX command messages from OsmoBSC to an E1 Abis MGW,
`tw-ts-002` codec parameter (attached to `GSM-HR-08` codec)
instructs the MGW whether or not it should emit the extended payload format
of TW-TS-002 for GSM uplink traffic converted to RTP.
In the absence of this parameter, IETF-standard and 3GPP-standard RFC 5993
format is emitted; if TW-TS-002 output is desired, please specify
`tw-ts-002=1` on the appropriate `a=fmtp` line.
.Example: `CRCX` message requesting HR codec with TW-TS-002 extensions
----
CRCX 2 ds/e1-0/s-2/su8-2@mgw MGCP 1.0
M: recvonly
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 1234 RTP/AVP 111
a=rtpmap:111 GSM-HR-08/8000/1
a=fmtp:111 tw-ts-002=1
----

View File

@@ -1,6 +1,7 @@
noinst_HEADERS = \
vty.h \
mgcp_msg.h \
mgcp_codec.h \
mgcp_conn.h \
mgcp_stat.h \
mgcp_endp.h \
@@ -13,4 +14,5 @@ noinst_HEADERS = \
mgcp_network.h \
mgcp_protocol.h \
mgcp_iuup.h \
mgcp_rtp_end.h \
$(NULL)

View File

@@ -40,6 +40,8 @@
#include "mgcp_ratectr.h"
extern void *tall_mgw_ctx;
#define RTP_PORT_DEFAULT_RANGE_START 16002
#define RTP_PORT_DEFAULT_RANGE_END RTP_PORT_DEFAULT_RANGE_START + 64
@@ -50,11 +52,6 @@ struct mgcp_endpoint;
struct mgcp_config;
struct mgcp_trunk;
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?
@@ -133,10 +130,6 @@ struct mgcp_config {
char source_addr[INET6_ADDRSTRLEN];
char call_agent_addr[INET6_ADDRSTRLEN];
/* RTP processing */
mgcp_processing rtp_processing_cb;
mgcp_processing_setup setup_rtp_processing_cb;
struct osmo_wqueue gw_fd;
struct mgcp_port_range net_ports;

View File

@@ -1,5 +1,9 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <osmocom/mgcp/mgcp_common.h>
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
@@ -8,14 +12,37 @@
#define PTYPE_UNDEFINED (-1)
struct mgcp_conn_rtp;
struct mgcp_rtp_codec {
uint32_t rate;
int channels;
uint32_t frame_duration_num;
uint32_t frame_duration_den;
int payload_type;
char audio_name[64];
char subtype_name[64];
bool param_present;
struct mgcp_codec_param param;
};
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, const struct mgcp_codec_param *param);
int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst);
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
const char *subtype_name, unsigned int match_nr);
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec);
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec);
struct mgcp_rtp_codec *mgcp_codec_from_pt(struct mgcp_conn_rtp *conn, int payload_type);
struct mgcp_rtp_codecset {
/* currently selected audio codec */
struct mgcp_rtp_codec *codec;
/* array with assigned audio codecs to choose from (SDP) */
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
/* number of assigned audio codecs (SDP) */
unsigned int codecs_assigned;
};
void mgcp_codecset_reset(struct mgcp_rtp_codecset *cset);
void mgcp_codecset_summary(struct mgcp_rtp_codecset *cset, const char *prefix_str);
int mgcp_codecset_add_codec(struct mgcp_rtp_codecset *cset, int payload_type,
const char *audio_name, const struct mgcp_codec_param *param);
int mgcp_codecset_decide(struct mgcp_rtp_codecset *cset_src, struct mgcp_rtp_codecset *cset_dst);
const struct mgcp_rtp_codec *mgcp_codecset_pt_find_by_subtype_name(const struct mgcp_rtp_codecset *cset,
const char *subtype_name, unsigned int match_nr);
struct mgcp_rtp_codec *mgcp_codecset_find_codec_from_pt(struct mgcp_rtp_codecset *cset, int payload_type);

View File

@@ -63,6 +63,10 @@ enum mgcp_x_osmo_ign {
struct mgcp_codec_param {
bool amr_octet_aligned_present;
bool amr_octet_aligned;
bool fr_efr_twts001_present;
bool fr_efr_twts001;
bool hr_twts002_present;
bool hr_twts002;
};
/* Ensure that the msg->l2h is NUL terminated. */

View File

@@ -24,11 +24,14 @@
#pragma once
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_network.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/iuup.h>
#include <osmocom/mgcp/mgcp_rtp_end.h>
#include <inttypes.h>
#define LOGPCONN(conn, cat, level, fmt, args...) \
@@ -237,15 +240,16 @@ static inline bool mgcp_conn_rtp_is_iuup(const struct mgcp_conn_rtp *conn)
return conn->type == MGCP_RTP_IUUP;
}
static inline struct mgcp_conn_rtp *mgcp_conn_get_conn_rtp(struct mgcp_conn *conn)
{
OSMO_ASSERT(conn->type == MGCP_CONN_TYPE_RTP);
return &conn->u.rtp;
}
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
enum mgcp_conn_type type, char *name);
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
const char *id);
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id);
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp);
void mgcp_conn_free_all(struct mgcp_endpoint *endp);
void mgcp_conn_free(struct mgcp_conn *conn);
int mgcp_conn_set_mode(struct mgcp_conn *conn, enum mgcp_connection_mode mode);
char *mgcp_conn_dump(struct mgcp_conn *conn);
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn);
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp);
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);

View File

@@ -22,6 +22,6 @@ static const uint8_t e1_rates[] = { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8
static const uint8_t e1_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs);
void mgcp_e1_endp_update(struct mgcp_endpoint *endp);
int mgcp_e1_endp_update(struct mgcp_endpoint *endp);
void mgcp_e1_endp_release(struct mgcp_endpoint *endp, uint8_t ts);
int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg);

View File

@@ -124,6 +124,7 @@ struct mgcp_endpoint {
struct osmo_fsm_inst *trau_sync_fi;
struct osmo_trau2rtp_state *trau_rtp_st;
uint8_t last_amr_ft;
uint8_t rtp_extensions;
struct mgcp_rtp_codec *last_codec;
} e1;
@@ -131,18 +132,28 @@ struct mgcp_endpoint {
struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, unsigned int index);
int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid);
void mgcp_endp_update(struct mgcp_endpoint *endp);
int mgcp_endp_update(struct mgcp_endpoint *endp, struct mgcp_conn *conn, enum mgcp_verb verb);
bool mgcp_endp_is_wildcarded(const char *epname);
bool mgcp_endp_is_null(const char *epname);
struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
const struct mgcp_trunk *trunk);
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
struct mgcp_config *cfg);
bool mgcp_endp_avail(struct mgcp_endpoint *endp);
bool mgcp_endp_avail(const struct mgcp_endpoint *endp);
unsigned int mgcp_endp_num_conns(const struct mgcp_endpoint *endp);
bool mgcp_endp_is_full(const struct mgcp_endpoint *endp);
void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
void mgcp_endp_free_conn_oldest(struct mgcp_endpoint *endp);
void mgcp_endp_free_conn_all(struct mgcp_endpoint *endp);
void mgcp_endp_strip_name(char *epname_stripped, const char *epname,
const struct mgcp_trunk *trunk);
struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname,
const struct mgcp_trunk *trunk);
void mgcp_endp_update_lco(struct mgcp_endpoint *endp, const struct mgcp_lco *lco);
void mgcp_endp_release(struct mgcp_endpoint *endp);
struct mgcp_conn *mgcp_endp_get_conn(struct mgcp_endpoint *endp, const char *id);
struct mgcp_conn *mgcp_endp_get_conn_oldest(struct mgcp_endpoint *endp);
struct mgcp_conn_rtp *mgcp_endp_get_conn_rtp(struct mgcp_endpoint *endp,
const char *id);

View File

@@ -31,3 +31,4 @@ void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp);
int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg);
int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg);
int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp);
int mgcp_conn_iuup_event_rx_crcx_mdcx(struct mgcp_conn_rtp *conn_rtp);

View File

@@ -27,6 +27,8 @@
#include <stdint.h>
#include <stdbool.h>
#include <osmocom/mgcp/mgcp_common.h>
struct mgcp_conn;
struct mgcp_parse_data;
struct mgcp_endpoint;
@@ -34,14 +36,14 @@ struct mgcp_trunk;
void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble);
int mgcp_parse_conn_mode(const char *msg, struct mgcp_endpoint *endp,
struct mgcp_conn *conn);
enum mgcp_connection_mode mgcp_parse_conn_mode(const char *msg);
int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data);
int mgcp_parse_hdr_pars(struct mgcp_parse_data *pdata);
int mgcp_parse_osmux_cid(const char *line);
bool mgcp_check_param(const struct mgcp_endpoint *endp, struct mgcp_trunk *trunk, const char *line);
bool mgcp_check_param(const char *line);
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid);

View File

@@ -74,65 +74,6 @@ struct mgcp_rtp_state {
uint32_t alt_rtp_tx_ssrc;
};
struct mgcp_rtp_codec {
uint32_t rate;
int channels;
uint32_t frame_duration_num;
uint32_t frame_duration_den;
int payload_type;
char audio_name[64];
char subtype_name[64];
bool param_present;
struct mgcp_codec_param param;
};
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
struct mgcp_rtp_end {
/* remote IP address of the RTP socket */
struct osmo_sockaddr addr;
/* in network byte order */
uint16_t rtcp_port;
/* currently selected audio codec */
struct mgcp_rtp_codec *codec;
/* array with assigned audio codecs to choose from (SDP) */
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
/* number of assigned audio codecs (SDP) */
unsigned int codecs_assigned;
/* per endpoint data */
int frames_per_packet;
uint32_t packet_duration_ms;
int maximum_packet_time; /* -1: not set */
/* are we transmitting packets (true) or dropping (false) outbound packets */
bool output_enabled;
/* FIXME: This parameter can be set + printed, but is nowhere used! */
int force_output_ptime;
/* RTP patching */
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
int force_aligned_timing;
bool rfc5993_hr_convert;
/* Each end has a separate socket for RTP and RTCP */
struct osmo_io_fd *rtp;
struct osmo_io_fd *rtcp;
/* local UDP port number of the RTP socket; RTCP is +1 */
int local_port;
/* where the endpoint RTP connection binds to, set during CRCX and
* possibly updated during MDCX */
char local_addr[INET6_ADDRSTRLEN];
};
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end);
struct mgcp_rtp_tap {
/* is this tap active (1) or not (0) */
int enabled;
@@ -150,27 +91,13 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg);
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
int mgcp_dispatch_e1_bridge_cb(struct msgb *msg);
void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
struct mgcp_conn_rtp *conn);
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
int mgcp_conn_rtp_bind_rtp_ports(struct mgcp_conn_rtp *conn, int rtp_port);
void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
struct mgcp_rtp_state *state,
struct mgcp_rtp_end *rtp_end,
struct osmo_sockaddr *addr, struct msgb *msg);
int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
/* payload processing default functions */
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct msgb *msg);
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn_dst,
struct mgcp_conn_rtp *conn_src);
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
const struct mgcp_rtp_codec **codec,
const char **fmtp_extra,
struct mgcp_conn_rtp *conn);
/* internal RTP Annex A counting */
void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
const uint16_t seq, const int32_t transit,

View File

@@ -1,30 +1,140 @@
#pragma once
/* Internal structure while parsing a request */
struct mgcp_parse_data {
struct mgcp_config *cfg;
char *epname;
char *trans;
char *save;
#include <stdint.h>
#include <sys/socket.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/socket.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_codec.h>
enum mgcp_verb {
MGCP_VERB_CRCX,
MGCP_VERB_MDCX,
MGCP_VERB_DLCX,
MGCP_VERB_AUEP,
MGCP_VERB_RQNT,
MGCP_VERB_RSIP,
};
extern const struct value_string mgcp_verb_names[];
static inline const char *mgcp_verb_name(enum mgcp_verb val)
{ return get_value_string(mgcp_verb_names, val); }
#define MGCP_PARSE_SDP_PTIME_UNSET (-1)
#define MGCP_PARSE_SDP_MAXPTIME_UNSET (-1)
#define MGCP_PARSE_SDP_RTP_PORT_UNSET (0)
struct mgcp_parse_sdp {
int ptime;
int maxptime;
int rtp_port;
struct osmo_sockaddr rem_addr; /* Only IP address, port is in rtp_port above */
struct mgcp_rtp_codecset cset;
};
static inline void mgcp_parse_sdp_init(struct mgcp_parse_sdp *sdp)
{
sdp->ptime = MGCP_PARSE_SDP_PTIME_UNSET;
sdp->maxptime = MGCP_PARSE_SDP_MAXPTIME_UNSET;
sdp->rtp_port = MGCP_PARSE_SDP_RTP_PORT_UNSET;
sdp->rem_addr = (struct osmo_sockaddr){ .u.sa.sa_family = AF_UNSPEC };
mgcp_codecset_reset(&sdp->cset);
}
/* Local connection options */
struct mgcp_lco {
char *string;
char *codec;
bool present;
char *codec; /* talloc-allocated to some parent */
int pkt_period_min; /* time in ms */
int pkt_period_max; /* time in ms */
};
static inline void mgcp_lco_init(struct mgcp_lco *lco)
{
*lco = (struct mgcp_lco){};
}
char *get_lco_identifier(const char *options);
int check_local_cx_options(void *ctx, const char *options);
#define MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET (-2)
#define MGCP_PARSE_HDR_PARS_OSMUX_CID_WILDCARD (-1)
struct mgcp_parse_hdr_pars {
const char *lco_string;
struct mgcp_lco lco;
const char *callid;
const char *connid;
enum mgcp_connection_mode mode;
int remote_osmux_cid;
bool have_sdp;
/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */
uint32_t x_osmo_ign;
};
static inline void mgcp_parse_hdr_pars_init(struct mgcp_parse_hdr_pars *hpars)
{
hpars->lco_string = NULL;
mgcp_lco_init(&hpars->lco);
hpars->callid = NULL;
hpars->connid = NULL;
hpars->mode = MGCP_CONN_NONE;
hpars->remote_osmux_cid = MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET;
hpars->have_sdp = false;
hpars->x_osmo_ign = 0;
}
/* Internal structure while parsing a request */
struct mgcp_request_data;
struct mgcp_parse_data {
struct mgcp_request_data *rq; /* backpointer to request context */
char *save;
/* MGCP Header: */
char *epname;
char *trans;
struct mgcp_parse_hdr_pars hpars;
/* MGCP Body: */
struct mgcp_parse_sdp sdp;
};
/* Request data passed to the request handler */
struct mgcp_request_data {
enum mgcp_verb verb;
/* Verb string (e.g. "MDCX") */
char name[4+1];
/* Global MGW config */
struct mgcp_config *cfg;
/* parsing results from the MGCP header (trans id, endpoint name ...) */
struct mgcp_parse_data *pdata;
/* pointer to endpoint resource (may be NULL for wildcarded requests) */
struct mgcp_endpoint *endp;
/* pointer to trunk resource */
struct mgcp_trunk *trunk;
/* set to true when the request has been classified as wildcarded */
bool wildcarded;
/* Set to true when the request is targeted at the "null" endpoint */
bool null_endp;
/* contains cause code in case of problems during endp/trunk resolution */
int mgcp_cause;
};
char *mgcp_debug_get_last_endpoint_name(void);
char *get_lco_identifier(const char *options);
int check_local_cx_options(void *ctx, const char *options);
struct mgcp_rtp_end;
struct mgcp_endpoint;
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
struct mgcp_rtp_end *rtp);
uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
const struct mgcp_rtp_end *rtp);
extern const struct value_string mgcp_connection_mode_strs[];
static inline const char *mgcp_cmode_name(enum mgcp_connection_mode mode)
{
return get_value_string(mgcp_connection_mode_strs, mode);
}

View File

@@ -43,7 +43,7 @@ enum {
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_BIND_PORT,
MGCP_MDCX_FAIL_AVAIL,
};

View File

@@ -0,0 +1,53 @@
#pragma once
#include <inttypes.h>
#include <stdbool.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_codec.h>
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
struct mgcp_rtp_end {
struct mgcp_conn_rtp *conn_rtp; /* backpointer */
/* remote IP address of the RTP socket */
struct osmo_sockaddr addr;
/* in network byte order */
uint16_t rtcp_port;
struct mgcp_rtp_codecset cset;
/* per endpoint data */
int frames_per_packet;
uint32_t packet_duration_ms;
int maximum_packet_time; /* -1: not set */
/* are we transmitting packets (true) or dropping (false) outbound packets */
bool output_enabled;
/* FIXME: This parameter can be set + printed, but is nowhere used! */
int force_output_ptime;
/* RTP patching */
bool force_constant_ssrc;
/* 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_io_fd *rtp;
struct osmo_io_fd *rtcp;
/* local UDP port number of the RTP socket; RTCP is +1 */
int local_port;
/* where the endpoint RTP connection binds to, set during CRCX and
* possibly updated during MDCX */
char local_addr[INET6_ADDRSTRLEN];
};
void mgcp_rtp_end_init(struct mgcp_rtp_end *end, struct mgcp_conn_rtp *conn_rtp);
void mgcp_rtp_end_cleanup(struct mgcp_rtp_end *end);
void mgcp_rtp_end_set_packet_duration_ms(struct mgcp_rtp_end *end, uint32_t packet_duration_ms);
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end);
void mgcp_rtp_end_free_port(struct mgcp_rtp_end *end);

View File

@@ -22,9 +22,9 @@
#pragma once
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn,
struct mgcp_parse_data *p);
struct mgcp_parse_data;
int mgcp_parse_sdp_data(struct mgcp_parse_data *p);
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
const struct mgcp_conn_rtp *conn, struct msgb *sdp,

View File

@@ -2,9 +2,10 @@
#include <osmocom/gsm/i460_mux.h>
#include <osmocom/abis/e1_input.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_network.h>
#include <osmocom/mgcp/mgcp_ratectr.h>
#define LOGPTRUNK(trunk, cat, level, fmt, args...) \
LOGP(cat, level, "trunk:%u " fmt, \
trunk ? trunk->trunk_nr : 0, \
@@ -34,7 +35,7 @@ struct mgcp_trunk {
int keepalive_interval;
/* RTP patching */
int force_constant_ssrc; /* 0: don't, 1: once */
bool force_constant_ssrc;
int force_aligned_timing;
bool rfc5993_hr_convert;
@@ -78,6 +79,7 @@ struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_tr
struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname);
int e1_trunk_nr_from_epname(unsigned int *trunk_nr, const char *epname);
struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num);
int mgcp_trunk_allocate_conn_rtp_ports(struct mgcp_trunk *trunk, struct mgcp_conn_rtp *conn_rtp);
/* The virtual trunk is always created on trunk id 0 for historical reasons,
* use this define constant as ID when allocating a virtual trunk. Other

View File

@@ -1,5 +1,6 @@
BUILT_SOURCES = \
mgcp_common.h \
version.h \
$(NULL)
noinst_HEADERS = \
@@ -11,4 +12,20 @@ mgcp_common.h: $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h
echo -e "/*\n\n DO NOT EDIT THIS FILE!\n THIS IS OVERWRITTEN DURING BUILD\n This is an automatic copy of <osmocom/mgcp/mgcp_common.h>\n\n */" > mgcp_common.h
cat $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h >> mgcp_common.h
CLEANFILES = mgcp_common.h
version.h: version.h.tpl
$(AM_V_GEN)$(MKDIR_P) $(dir $@)
$(AM_V_GEN)sed \
-e "s/{{VERSION}}/$$(echo '@VERSION@' | cut -d. -f1-3)/g" \
-e "s/{{VERSION_MAJOR}}/$$(echo '@VERSION@' | cut -d. -f1)/g" \
-e "s/{{VERSION_MINOR}}/$$(echo '@VERSION@' | cut -d. -f2)/g" \
-e "s/{{VERSION_PATCH}}/$$(echo '@VERSION@' | cut -d. -f3)/g" \
$< > $@
EXTRA_DIST = \
version.h.tpl \
$(NULL)
CLEANFILES = \
mgcp_common.h \
version.h \
$(NULL)

View File

@@ -3,6 +3,8 @@
#include <osmocom/core/osmo_io.h>
#include <osmocom/core/timer.h>
#include <osmocom/mgcp_client/mgcp_client.h>
#define MSGB_CB_MGCP_TRANS_ID 0
/* Struct that holds one endpoint name */

View File

@@ -0,0 +1,16 @@
#pragma once
#define LIBOSMO_MGCP_CLIENT_VERSION {{VERSION}}
#define LIBOSMO_MGCP_CLIENT_VERSION_STR "{{VERSION}}"
#define LIBOSMO_MGCP_CLIENT_VERSION_MAJOR {{VERSION_MAJOR}}
#define LIBOSMO_MGCP_CLIENT_VERSION_MINOR {{VERSION_MINOR}}
#define LIBOSMO_MGCP_CLIENT_VERSION_PATCH {{VERSION_PATCH}}
#define LIBOSMO_MGCP_CLIENT_VERSION_GREATER_EQUAL(major, minor, patch) \
(LIBOSMO_MGCP_CLIENT_VERSION_MAJOR > (major) || \
(LIBOSMO_MGCP_CLIENT_VERSION_MAJOR == (major) && \
LIBOSMO_MGCP_CLIENT_VERSION_MINOR > (minor)) || \
(LIBOSMO_MGCP_CLIENT_VERSION_MAJOR == (major) && \
LIBOSMO_MGCP_CLIENT_VERSION_MINOR == (minor) && \
LIBOSMO_MGCP_CLIENT_VERSION_PATCH >= (patch)))

View File

@@ -19,7 +19,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=14:0:0
MGCP_CLIENT_LIBVERSION=14:1:0
lib_LTLIBRARIES = \
libosmo-mgcp-client.la \

View File

@@ -786,8 +786,10 @@ static void mgcp_read_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg)
struct mgcp_client *mgcp = osmo_iofd_get_data(iofd);
if (res <= 0) {
char errbuf[128] = "";
strerror_r(-res, errbuf, sizeof(errbuf));
LOGPMGW(mgcp, LOGL_ERROR, "Failed to read: %s: %d='%s'\n",
osmo_iofd_get_name(iofd), res, strerror(res));
osmo_iofd_get_name(iofd), res, errbuf);
msgb_free(msg);
return;
@@ -822,8 +824,10 @@ static void mgcp_write_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg)
struct mgcp_client *mgcp = osmo_iofd_get_data(iofd);
if (OSMO_UNLIKELY(res != msg->len)) {
char errbuf[128] = "";
strerror_r(-res, errbuf, sizeof(errbuf));
LOGPMGW(mgcp, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n",
osmo_iofd_get_name(mgcp->iofd), res, strerror(res),
osmo_iofd_get_name(mgcp->iofd), res, errbuf,
msg->len, osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len)));
}
}
@@ -860,6 +864,10 @@ static void _mgcp_client_send_dlcx(struct mgcp_client *mgcp, const char *epname)
};
osmo_strlcpy(mgcp_msg_dlcx.endpoint, epname, sizeof(mgcp_msg_dlcx.endpoint));
msgb_dlcx = mgcp_msg_gen(mgcp, &mgcp_msg_dlcx);
if (!msgb_dlcx) {
LOGPMGW(mgcp, LOGL_ERROR, "Failed generating MGCP DLCX %s\n", epname);
return;
}
mgcp_client_tx(mgcp, msgb_dlcx, &_ignore_mgcp_response, NULL);
}
@@ -873,6 +881,10 @@ static void _mgcp_client_send_auep(struct mgcp_client *mgcp, const char *epname)
};
OSMO_STRLCPY_ARRAY(mgcp_msg_auep.endpoint, epname);
msgb_auep = mgcp_msg_gen(mgcp, &mgcp_msg_auep);
if (!msgb_auep) {
LOGPMGW(mgcp, LOGL_ERROR, "Failed generating MGCP AUEP %s\n", epname);
return;
}
mgcp_client_tx(mgcp, msgb_auep, &_ignore_mgcp_response, NULL);
}
@@ -1047,6 +1059,7 @@ void mgcp_client_disconnect(struct mgcp_client *mgcp)
osmo_iofd_txqueue_clear(mgcp->iofd);
LOGPMGW(mgcp, LOGL_INFO, "MGCP association: %s -- closed!\n", osmo_iofd_get_name(mgcp->iofd));
osmo_iofd_free(mgcp->iofd);
mgcp->iofd = NULL;
}
/*! Get the IP-Aaddress of the associated MGW as string.
@@ -1307,6 +1320,36 @@ static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
#undef MSGB_PRINTF_OR_RET
}
/* Helper function to obtain local IP address used in MGCP towards MGW,
* in string format, to fill the SDP "Origin" ("o=") field.
* return 0 on success, negative on error.
*/
static int get_mgcp_local_addr(const struct mgcp_client *mgcp, char *local_ip, size_t local_ip_len)
{
int fd;
/* Try to get the socket local IP address if available: */
if (mgcp->iofd && ((fd = osmo_iofd_get_fd(mgcp->iofd)) >= 0)) {
if (osmo_sock_get_local_ip(fd, local_ip, local_ip_len) == 0)
return 0;
/* else: continue below */
}
/* If MGCP local address was explicitly specified in config, use it: */
if (mgcp->actual.local_addr) {
osmo_strlcpy(local_ip, mgcp->actual.local_addr, local_ip_len);
return 0;
}
/* Guess our local address based on system routing towards MGW: */
OSMO_ASSERT(local_ip_len >= INET6_ADDRSTRLEN);
if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) == 0)
return 0;
LOGPMGW(mgcp, LOGL_ERROR, "Could not determine local IP-Address!\n");
return -EINVAL;
}
/* Helper function for mgcp_msg_gen(): Add SDP information to MGCP message */
static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_client *mgcp)
{
@@ -1316,6 +1359,7 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
const char *codec;
unsigned int pt;
uint16_t audio_port;
int rc;
#define MSGB_PRINTF_OR_RET(FMT, ARGS...) do { \
if (msgb_printf(msg, FMT, ##ARGS) != 0) { \
@@ -1331,11 +1375,9 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
MSGB_PRINTF_OR_RET("v=0\r\n");
/* Determine local IP-Address */
if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
LOGPMGW(mgcp, LOGL_ERROR,
"Could not determine local IP-Address!\n");
return -EINVAL;
}
rc = get_mgcp_local_addr(mgcp, local_ip, sizeof(local_ip));
if (rc < 0)
return rc;
local_ip_family = osmo_ip_str_type(local_ip);
if (local_ip_family == AF_UNSPEC)
return -EINVAL;
@@ -1384,15 +1426,32 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
/* Add optional codec parameters (fmtp) */
if (mgcp_msg->param_present) {
for (i = 0; i < mgcp_msg->ptmap_len; i++) {
/* The following is only applicable for AMR */
if (mgcp_msg->ptmap[i].codec != CODEC_AMR_8000_1
&& mgcp_msg->ptmap[i].codec != CODEC_AMRWB_16000_1)
continue;
pt = mgcp_msg->ptmap[i].pt;
if (mgcp_msg->param.amr_octet_aligned_present && mgcp_msg->param.amr_octet_aligned)
MSGB_PRINTF_OR_RET("a=fmtp:%u octet-align=1\r\n", pt);
else if (mgcp_msg->param.amr_octet_aligned_present && !mgcp_msg->param.amr_octet_aligned)
MSGB_PRINTF_OR_RET("a=fmtp:%u octet-align=0\r\n", pt);
switch (mgcp_msg->ptmap[i].codec) {
case CODEC_AMR_8000_1:
case CODEC_AMRWB_16000_1:
if (!mgcp_msg->param.amr_octet_aligned_present)
break;
MSGB_PRINTF_OR_RET("a=fmtp:%u octet-align=%d\r\n",
pt, (int)mgcp_msg->param.amr_octet_aligned);
break;
case CODEC_GSM_8000_1:
case CODEC_GSMEFR_8000_1:
if (!mgcp_msg->param.fr_efr_twts001_present)
break;
MSGB_PRINTF_OR_RET("a=fmtp:%u tw-ts-001=%d\r\n",
pt, (int)mgcp_msg->param.fr_efr_twts001);
break;
case CODEC_GSMHR_8000_1:
if (!mgcp_msg->param.hr_twts002_present)
break;
MSGB_PRINTF_OR_RET("a=fmtp:%u tw-ts-002=%d\r\n",
pt, (int)mgcp_msg->param.hr_twts002);
break;
default:
/* no parameters for the remaining codecs */
break;
}
}
}

View File

@@ -157,6 +157,7 @@ static void set_conn_mode(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *peer
mgcp_msg->conn_mode = conn_mode;
}
/* returns message buffer containing MGXP MDCX on success, NULL on error. */
static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
{
struct mgcp_msg mgcp_msg;
@@ -192,6 +193,7 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
}
/* returns message buffer containing MGXP DLCX on success, NULL on error. */
struct msgb *make_dlcx_msg(struct mgcp_ctx *mgcp_ctx)
{
struct mgcp_msg mgcp_msg;
@@ -231,8 +233,10 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local);
msg = mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
OSMO_ASSERT(msg);
if (!msg) {
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
mgcp_ctx->mgw_trans_pending = true;
rc = mgcp_client_tx(mgcp, msg, mgw_crcx_resp_cb, fi);

View File

@@ -7,6 +7,7 @@ AM_CPPFLAGS = \
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOCODEC_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
@@ -40,6 +41,7 @@ libosmo_mgcp_a_SOURCES = \
mgcp_endp.c \
mgcp_trunk.c \
mgcp_ratectr.c \
mgcp_rtp_end.c \
mgcp_e1.c \
mgcp_iuup.c \
$(NULL)

View File

@@ -28,7 +28,7 @@
/* Helper function to dump codec information of a specified codec to a printable
* string, used by dump_codec_summary() */
static char *dump_codec(struct mgcp_rtp_codec *codec)
static char *mgcp_codec_dump(struct mgcp_rtp_codec *codec)
{
static char str[256];
char *pt_str;
@@ -49,31 +49,25 @@ static char *dump_codec(struct mgcp_rtp_codec *codec)
}
/*! Dump a summary of all negotiated codecs to debug log
* \param[in] conn related rtp-connection. */
void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
* \param[in] cset related codecset.
* \param[in] prefix_str Prefix string to print during logging.
* */
void mgcp_codecset_summary(struct mgcp_rtp_codecset *cset, const char *prefix_str)
{
struct mgcp_rtp_end *rtp;
unsigned int i;
struct mgcp_rtp_codec *codec;
struct mgcp_endpoint *endp;
rtp = &conn->end;
endp = conn->conn->endp;
if (rtp->codecs_assigned == 0) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "conn:%s no codecs available\n",
mgcp_conn_dump(conn->conn));
if (cset->codecs_assigned == 0) {
LOGP(DLMGCP, LOGL_ERROR, "%s no codecs available\n", prefix_str);
return;
}
/* Store parsed codec information */
for (i = 0; i < rtp->codecs_assigned; i++) {
codec = &rtp->codecs[i];
for (i = 0; i < cset->codecs_assigned; i++) {
struct mgcp_rtp_codec *codec = &cset->codecs[i];
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s codecs[%u]:%s",
mgcp_conn_dump(conn->conn), i, dump_codec(codec));
LOGP(DLMGCP, LOGL_DEBUG, "%s codecs[%u]:%s", prefix_str, i, mgcp_codec_dump(codec));
if (codec == rtp->codec)
if (codec == cset->codec)
LOGPC(DLMGCP, LOGL_DEBUG, " [selected]");
LOGPC(DLMGCP, LOGL_DEBUG, "\n");
@@ -81,7 +75,7 @@ void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
}
/* Initalize or reset codec information with default data. */
static void codec_init(struct mgcp_rtp_codec *codec)
static void mgcp_codec_init(struct mgcp_rtp_codec *codec)
{
*codec = (struct mgcp_rtp_codec){
.payload_type = -1,
@@ -94,20 +88,20 @@ static void codec_init(struct mgcp_rtp_codec *codec)
};
}
static void codec_free(struct mgcp_rtp_codec *codec)
static void mgcp_codec_free(struct mgcp_rtp_codec *codec)
{
*codec = (struct mgcp_rtp_codec){};
}
/*! Initalize or reset codec information with default data.
* \param[out] conn related rtp-connection. */
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
void mgcp_codecset_reset(struct mgcp_rtp_codecset *cset)
{
int i;
for (i = 0; i < conn->end.codecs_assigned; i++)
codec_free(&conn->end.codecs[i]);
conn->end.codecs_assigned = 0;
conn->end.codec = NULL;
for (i = 0; i < cset->codecs_assigned; i++)
mgcp_codec_free(&cset->codecs[i]);
cset->codecs_assigned = 0;
cset->codec = NULL;
}
/*! Add codec configuration depending on payload type and/or codec name. This
@@ -118,23 +112,23 @@ void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
* \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
* \param[in] param optional codec parameters (set to NULL when unused).
* \returns 0 on success, -EINVAL on failure. */
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
int mgcp_codecset_add_codec(struct mgcp_rtp_codecset *cset, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
{
int rate;
int channels;
struct mgcp_rtp_codec *codec;
unsigned int pt_offset = conn->end.codecs_assigned;
unsigned int pt_offset = cset->codecs_assigned;
/* The amount of codecs we can store is limited, make sure we do not
* overrun this limit. */
if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
if (cset->codecs_assigned >= MGCP_MAX_CODECS)
return -EINVAL;
/* First unused entry */
codec = &conn->end.codecs[conn->end.codecs_assigned];
codec = &cset->codecs[cset->codecs_assigned];
/* Initalize the codec struct with some default data to begin with */
codec_init(codec);
mgcp_codec_init(codec);
if (payload_type != PTYPE_UNDEFINED) {
/* Make sure we do not get any reserved or undefined type numbers */
@@ -268,11 +262,11 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud
} else
codec->param_present = false;
conn->end.codecs_assigned++;
cset->codecs_assigned++;
return 0;
error:
/* Make sure we leave a clean codec entry on error. */
codec_free(codec);
mgcp_codec_free(codec);
return -EINVAL;
}
@@ -296,7 +290,7 @@ bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
}
/* Compare two codecs, all parameters must match up */
static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
static bool codecs_same(const struct mgcp_rtp_codec *codec_a, const struct mgcp_rtp_codec *codec_b)
{
/* All codec properties must match up, except the payload type number. Even though standardisd payload numbers
* exist for certain situations, the call agent may still assign them freely. Hence we must not insist on equal
@@ -325,7 +319,7 @@ static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *c
}
/* Compare two codecs, all parameters must match up, except parameters related to payload formatting (not checked). */
static bool codecs_convertible(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
static bool codecs_convertible(const struct mgcp_rtp_codec *codec_a, const struct mgcp_rtp_codec *codec_b)
{
/* OsmoMGW currently has no ability to transcode from one codec to another. However OsmoMGW is still able to
* translate between different payload formats as long as the encoded voice data itself does not change.
@@ -354,21 +348,18 @@ iufp:
return true;
}
struct mgcp_rtp_codec *mgcp_codec_find_same(struct mgcp_conn_rtp *conn, struct mgcp_rtp_codec *codec)
struct mgcp_rtp_codec *mgcp_codecset_find_same(struct mgcp_rtp_codecset *cset, const struct mgcp_rtp_codec *codec)
{
struct mgcp_rtp_end *rtp_end;
unsigned int i;
unsigned int codecs_assigned;
rtp_end = &conn->end;
/* Use the codec information from the source and try to find the equivalent of it on the destination side. In
* the first run we will look for an exact match. */
codecs_assigned = rtp_end->codecs_assigned;
codecs_assigned = cset->codecs_assigned;
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
for (i = 0; i < codecs_assigned; i++) {
if (codecs_same(codec, &rtp_end->codecs[i])) {
return &rtp_end->codecs[i];
if (codecs_same(codec, &cset->codecs[i])) {
return &cset->codecs[i];
break;
}
}
@@ -377,28 +368,26 @@ struct mgcp_rtp_codec *mgcp_codec_find_same(struct mgcp_conn_rtp *conn, struct m
}
/* For a given codec, find a convertible codec in the given connection. */
static struct mgcp_rtp_codec *codec_find_convertible(struct mgcp_conn_rtp *conn, struct mgcp_rtp_codec *codec)
static struct mgcp_rtp_codec *codecset_find_convertible(struct mgcp_rtp_codecset *cset, const struct mgcp_rtp_codec *codec)
{
struct mgcp_rtp_end *rtp_end;
unsigned int i;
unsigned int codecs_assigned;
struct mgcp_rtp_codec *codec_convertible = NULL;
rtp_end = &conn->end;
/* Use the codec information from the source and try to find the equivalent of it on the destination side. In
* the first run we will look for an exact match. */
codec_convertible = mgcp_codec_find_same(conn, codec);
codec_convertible = mgcp_codecset_find_same(cset, codec);
if (codec_convertible)
return codec_convertible;
/* In case we weren't able to find an exact match, we will try to find a match that is the same codec, but the
* payload format may be different. This alternative will require a frame format conversion (i.e. AMR bwe->oe) */
codecs_assigned = rtp_end->codecs_assigned;
codecs_assigned = cset->codecs_assigned;
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
for (i = 0; i < codecs_assigned; i++) {
if (codecs_convertible(codec, &rtp_end->codecs[i])) {
codec_convertible = &rtp_end->codecs[i];
if (codecs_convertible(codec, &cset->codecs[i])) {
codec_convertible = &cset->codecs[i];
break;
}
}
@@ -408,20 +397,20 @@ static struct mgcp_rtp_codec *codec_find_convertible(struct mgcp_conn_rtp *conn,
/*! Decide for one suitable codec on both of the given connections. In case a destination connection is not available,
* a tentative decision is made.
* \param[inout] conn_src related rtp-connection.
* \param[inout] conn_dst related destination rtp-connection (NULL if not present).
* \param[in] cset_src related codec set.
* \param[inout] cset_dst related destination codec set (NULL if not present).
* \returns 0 on success, -EINVAL on failure. */
int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst)
int mgcp_codecset_decide(struct mgcp_rtp_codecset *cset_src, struct mgcp_rtp_codecset *cset_dst)
{
unsigned int i;
/* In case no destination connection is available (yet), or in case the destination connection exists but has
* no codecs assigned, we are forced to make a simple tentative decision:
* We just use the first codec of the source connection (conn_src) */
OSMO_ASSERT(conn_src->end.codecs_assigned <= MGCP_MAX_CODECS);
if (!conn_dst || conn_dst->end.codecs_assigned == 0) {
if (conn_src->end.codecs_assigned >= 1) {
conn_src->end.codec = &conn_src->end.codecs[0];
OSMO_ASSERT(cset_src->codecs_assigned <= MGCP_MAX_CODECS);
if (!cset_dst || cset_dst->codecs_assigned == 0) {
if (cset_src->codecs_assigned >= 1) {
cset_src->codec = &cset_src->codecs[0];
return 0;
} else
return -EINVAL;
@@ -430,38 +419,38 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn
/* Compare all codecs of the source connection (conn_src) to the codecs of the destination connection (conn_dst). In case
* of a match set this codec on both connections. This would be an ideal selection since no codec conversion would be
* required. */
for (i = 0; i < conn_src->end.codecs_assigned; i++) {
struct mgcp_rtp_codec *codec_conn_src = &conn_src->end.codecs[i];
struct mgcp_rtp_codec *codec_conn_dst = mgcp_codec_find_same(conn_dst, codec_conn_src);
if (codec_conn_dst) {
for (i = 0; i < cset_src->codecs_assigned; i++) {
struct mgcp_rtp_codec *codec_cset_src = &cset_src->codecs[i];
struct mgcp_rtp_codec *codec_cset_dst = mgcp_codecset_find_same(cset_dst, codec_cset_src);
if (codec_cset_dst) {
/* We found the a codec that is exactly the same (same codec, same payload format etc.) on both
* sides. We now set this codec on both connections. */
conn_dst->end.codec = codec_conn_dst;
conn_src->end.codec = codec_conn_src;
cset_dst->codec = codec_cset_dst;
cset_src->codec = codec_cset_src;
return 0;
}
}
/* In case we could not find a codec that is exactly the same, let's at least try to find a codec that we are able
* to convert. */
for (i = 0; i < conn_src->end.codecs_assigned; i++) {
struct mgcp_rtp_codec *codec_conn_src = &conn_src->end.codecs[i];
struct mgcp_rtp_codec *codec_conn_dst = codec_find_convertible(conn_dst, codec_conn_src);
if (codec_conn_dst) {
for (i = 0; i < cset_src->codecs_assigned; i++) {
struct mgcp_rtp_codec *codec_cset_src = &cset_src->codecs[i];
struct mgcp_rtp_codec *codec_cset_dst = codecset_find_convertible(cset_dst, codec_cset_src);
if (codec_cset_dst) {
/* We found the a codec that we can convert to. Set each side to its codec. */
conn_dst->end.codec = codec_conn_dst;
conn_src->end.codec = codec_conn_src;
cset_dst->codec = codec_cset_dst;
cset_src->codec = codec_cset_src;
return 0;
}
}
if (conn_dst->end.codecs_assigned)
conn_dst->end.codec = &conn_dst->end.codecs[0];
if (cset_dst->codecs_assigned)
cset_dst->codec = &cset_dst->codecs[0];
else
return -EINVAL;
if (conn_src->end.codecs_assigned)
conn_src->end.codec = &conn_src->end.codecs[0];
if (cset_src->codecs_assigned)
cset_src->codec = &cset_src->codecs[0];
else
return -EINVAL;
@@ -488,38 +477,36 @@ bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec)
* \param match_nr Index for the match found, first being match_nr == 0. Iterate all matches by calling multiple times
* with incrementing match_nr.
* \return codec definition for that conn matching the subtype_name, or NULL if no such match_nr is found. */
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
const struct mgcp_rtp_codec *mgcp_codecset_pt_find_by_subtype_name(const struct mgcp_rtp_codecset *cset,
const char *subtype_name, unsigned int match_nr)
{
int i;
for (i = 0; i < conn->end.codecs_assigned; i++) {
if (!strcmp(conn->end.codecs[i].subtype_name, subtype_name)) {
for (i = 0; i < cset->codecs_assigned; i++) {
if (!strcmp(cset->codecs[i].subtype_name, subtype_name)) {
if (match_nr) {
match_nr--;
continue;
}
return &conn->end.codecs[i];
return &cset->codecs[i];
}
}
return NULL;
}
/*! Lookup a codec that is assigned to a connection by its payload type number.
* \param[in] conn related rtp-connection.
* \param[in] cset related codec set.
* \param[in] payload_type number of the codec to look up.
* \returns pointer to codec struct on success, NULL on failure. */
struct mgcp_rtp_codec *mgcp_codec_from_pt(struct mgcp_conn_rtp *conn, int payload_type)
struct mgcp_rtp_codec *mgcp_codecset_find_codec_from_pt(struct mgcp_rtp_codecset *cset, int payload_type)
{
struct mgcp_rtp_end *rtp_end = &conn->end;
unsigned int codecs_assigned = rtp_end->codecs_assigned;
struct mgcp_rtp_codec *codec = NULL;
size_t i;
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
OSMO_ASSERT(cset->codecs_assigned <= MGCP_MAX_CODECS);
for (i = 0; i < codecs_assigned; i++) {
if (payload_type == rtp_end->codecs[i].payload_type) {
codec = &rtp_end->codecs[i];
for (i = 0; i < cset->codecs_assigned; i++) {
if (payload_type == cset->codecs[i].payload_type) {
codec = &cset->codecs[i];
break;
}
}

View File

@@ -74,7 +74,7 @@ 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)) {
if (!mgcp_endp_get_conn_rtp(endp, id_hex)) {
osmo_strlcpy(id, id_hex, MGCP_CONN_ID_MAXLEN);
return 0;
}
@@ -88,7 +88,6 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
/* Initialize rtp connection struct with default values */
static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
{
struct mgcp_rtp_end *end = &conn_rtp->end;
/* FIXME: Each new rate counter group requires an unique index. At the
* moment we generate this index using this counter, but perhaps there
* is a more concious way to assign the indexes. */
@@ -106,17 +105,6 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
/* backpointer to the generic part of the connection */
conn->u.rtp.conn = conn;
end->rtp = NULL;
end->rtcp = NULL;
memset(&end->addr, 0, sizeof(end->addr));
end->rtcp_port = 0;
/* Set default values */
end->frames_per_packet = 0; /* unknown */
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
end->output_enabled = false;
end->maximum_packet_time = -1;
conn_rtp->ctrg = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++);
if (!conn_rtp->ctrg)
return -1;
@@ -124,9 +112,7 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
conn_rtp->state.in_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, IN_STREAM_ERR_TSTMP_CTR);
conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, OUT_STREAM_ERR_TSTMP_CTR);
/* Make sure codec table is reset */
mgcp_codec_reset_all(conn_rtp);
mgcp_rtp_end_init(&conn_rtp->end, conn_rtp);
return 0;
}
@@ -137,16 +123,15 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
conn_osmux_disable(conn_rtp);
if (mgcp_conn_rtp_is_iuup(conn_rtp))
mgcp_conn_iuup_cleanup(conn_rtp);
mgcp_free_rtp_port(&conn_rtp->end);
mgcp_rtp_end_cleanup(&conn_rtp->end);
rate_ctr_group_free(conn_rtp->ctrg);
mgcp_codec_reset_all(conn_rtp);
}
void mgcp_conn_watchdog_cb(void *data)
{
struct mgcp_conn *conn = data;
LOGPCONN(conn, DLMGCP, LOGL_ERROR, "connection timed out!\n");
mgcp_conn_free(conn->endp, conn->id);
mgcp_conn_free(conn);
}
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn)
@@ -212,58 +197,6 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
return conn;
}
/*! find a connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection
* \returns pointer to allocated connection, NULL if not found */
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) {
/* Ignore leading zeros in haystack */
for (conn_id=conn->id; *conn_id == '0'; conn_id++);
if (strcmp(conn_id, id_upper) == 0)
return conn;
}
return NULL;
}
/*! find an RTP connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection
* \returns pointer to allocated connection, NULL if not found */
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
const char *id)
{
struct mgcp_conn *conn;
conn = mgcp_conn_get(endp, id);
if (!conn)
return NULL;
if (conn->type == MGCP_CONN_TYPE_RTP)
return &conn->u.rtp;
return NULL;
}
static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp)
{
struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats;
@@ -286,21 +219,21 @@ static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_con
rate_ctr_inc(rate_ctr_group_get_ctr(all_stats, RTP_NUM_CONNECTIONS));
}
/*! free a connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection */
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
/*! free a connection
* \param[in] conn the conn to free. May be NULL.
*/
void mgcp_conn_free(struct mgcp_conn *conn)
{
struct mgcp_conn *conn;
struct mgcp_conn_rtp *conn_rtp;
conn = mgcp_conn_get(endp, id);
if (!conn)
return;
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
aggregate_rtp_conn_stats(endp, &conn->u.rtp);
mgcp_rtp_conn_cleanup(&conn->u.rtp);
conn_rtp = mgcp_conn_get_conn_rtp(conn);
aggregate_rtp_conn_stats(conn->endp, conn_rtp);
mgcp_rtp_conn_cleanup(conn_rtp);
break;
default:
/* NOTE: This should never be called with an
@@ -310,43 +243,40 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
}
osmo_timer_del(&conn->watchdog);
mgcp_endp_remove_conn(endp, conn);
mgcp_endp_remove_conn(conn->endp, conn);
/* WARN: endp may have be freed after call to mgcp_endp_remove_conn */
talloc_free(conn);
}
/*! free oldest connection in the list.
* \param[in] endp associated endpoint */
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
/*! Parse connection mode.
* \param[in] conn Connection whose mode is being set
* \param[in] mode Mode to set
* \returns 0 on success, -1 on error */
int mgcp_conn_set_mode(struct mgcp_conn *conn, enum mgcp_connection_mode mode)
{
struct mgcp_conn *conn;
OSMO_ASSERT(conn);
if (mode == MGCP_CONN_NONE) {
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
"missing connection mode\n");
return -1;
}
conn->mode = mode;
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
mgcp_cmode_name(mode), conn->mode);
if (llist_empty(&endp->conns))
return;
/* Special handling for RTP connections */
if (conn->type == MGCP_CONN_TYPE_RTP) {
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
conn_rtp->end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY);
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %u\n",
conn_rtp->end.output_enabled);
}
conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry);
if (!conn)
return;
/* The VTY might change the connection mode at any time, so we have
* to hold a copy of the original connection mode */
conn->mode_orig = conn->mode;
mgcp_conn_free(endp, conn->id);
}
/*! free all connections at once.
* \param[in] endp associated endpoint */
#if defined(__has_attribute)
#if __has_attribute(no_sanitize)
__attribute__((no_sanitize("undefined"))) /* ubsan detects a misaligned load */
#endif
#endif
void mgcp_conn_free_all(struct mgcp_endpoint *endp)
{
struct mgcp_conn *conn;
/* Drop all items in the list, might be consecutive! */
while ((conn = llist_first_entry_or_null(&endp->conns, struct mgcp_conn, entry)))
mgcp_conn_free(endp, conn->id);
return;
return 0;
}
/*! dump basic connection information to human readable string.
@@ -357,24 +287,26 @@ char *mgcp_conn_dump(struct mgcp_conn *conn)
static char str[sizeof(conn->name)+sizeof(conn->id)+256];
char ipbuf[INET6_ADDRSTRLEN];
struct osmo_strbuf sb = { .buf = str, .len = sizeof(str) };
struct mgcp_conn_rtp *conn_rtp;
if (!conn)
return "NULL";
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
conn_rtp = mgcp_conn_get_conn_rtp(conn);
OSMO_STRBUF_PRINTF(sb, "(%s/%s C:%s r=%s:%u<->l=%s:%u",
conn->name,
mgcp_conn_rtp_type_name(conn->type),
conn->id,
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf) ? : "NULL",
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa),
conn->u.rtp.end.local_addr ? : "NULL",
conn->u.rtp.end.local_port);
osmo_sockaddr_ntop(&conn_rtp->end.addr.u.sa, ipbuf) ? : "NULL",
osmo_sockaddr_port(&conn_rtp->end.addr.u.sa),
conn_rtp->end.local_addr ? : "NULL",
conn_rtp->end.local_port);
switch (conn->u.rtp.type) {
switch (conn_rtp->type) {
case MGCP_RTP_OSMUX:
OSMO_STRBUF_PRINTF(sb, " CID=%u", conn->u.rtp.osmux.local_cid);
OSMO_STRBUF_PRINTF(sb, " CID=%u", conn_rtp->osmux.local_cid);
break;
default:
break;
@@ -414,16 +346,6 @@ struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn)
return NULL;
}
/*! get oldest connection in the list.
* \param[in] endp associated endpoint */
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp)
{
if (llist_empty(&endp->conns))
return NULL;
return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
}
const struct value_string mgcp_conn_rtp_type_names[] = {
{ MGCP_RTP_DEFAULT, "rtp" },
{ MGCP_RTP_OSMUX, "osmux" },

View File

@@ -40,6 +40,7 @@
#include <osmocom/mgcp/debug.h>
#include <osmocom/mgcp/mgcp_e1.h>
#include <osmocom/codec/codec.h>
#include <osmocom/gsm/rtp_extensions.h>
#define DEBUG_BITS_MAX 80
#define DEBUG_BYTES_MAX 40
@@ -56,135 +57,6 @@ static const struct e1inp_line_ops dummy_e1_line_ops = {
.sign_link = NULL,
};
/* EFR idle frame */
static const ubit_t idle_tf_efr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1,
};
/* FR idle frame */
static const ubit_t idle_tf_fr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1,
};
/* Idle speech frame, see also GSM 08.60, chapter 3.4 */
static const ubit_t idle_tf_spch[] = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1,
};
/* If the RTP transmission has dropouts for some reason the I.460 TX-Queue may
* run empty. In order to make sure that the TRAU frame transmission continues
* we generate idle TRAU frames here. */
@@ -193,33 +65,59 @@ static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_dat
struct mgcp_endpoint *endp = user_data;
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
struct msgb *msg = msgb_alloc_c(endp->trunk, E1_TRAU_BITS_MSGB, "E1-I.460-IDLE-TX-TRAU-frame");
uint8_t *ptr;
const uint8_t *ptr_ft;
enum osmo_trau_frame_type ft;
const uint8_t *dummy_fill_pl;
unsigned dummy_fill_pl_len;
struct osmo_trau_frame tf;
int rc;
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_MUX_EMPTY_CTR));
/* Choose an appropiate idle frame type */
ft = endp->e1.trau_rtp_st->type;
switch (ft) {
/* choose dummy fill frame payload based on current codec */
switch (endp->e1.trau_rtp_st->type) {
case OSMO_TRAU16_FT_FR:
ptr_ft = idle_tf_fr;
dummy_fill_pl = osmo_gsm611_silence_frame;
dummy_fill_pl_len = GSM_FR_BYTES;
break;
case OSMO_TRAU16_FT_EFR:
ptr_ft = idle_tf_efr;
dummy_fill_pl = osmo_gsm660_homing_frame;
dummy_fill_pl_len = GSM_EFR_BYTES;
break;
case OSMO_TRAU16_FT_HR:
case OSMO_TRAU8_SPEECH:
dummy_fill_pl = osmo_gsm620_silence_frame;
dummy_fill_pl_len = GSM_HR_BYTES;
break;
default:
/* FIXME: What about 8k subslots and AMR frames? */
ptr_ft = idle_tf_spch;
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-I.460-IDLE-TX: unsupported frame type\n");
goto skip;
}
/* Put the replacement into a message buffer and enqueue it into the
* I.460 multiplexer */
ptr = msgb_put(msg, E1_TRAU_BITS);
memcpy(ptr, ptr_ft, E1_TRAU_BITS);
/* turn it into a TRAU-DL frame */
memset(&tf, 0, sizeof(tf));
tf.dir = OSMO_TRAU_DIR_DL;
rc = osmo_rtp2trau(&tf, dummy_fill_pl, dummy_fill_pl_len, endp->e1.trau_rtp_st);
if (rc < 0) {
LOGPENDP(endp, DE1, LOGL_ERROR,
"E1-I.460-IDLE-TX: error converting dummy fill frame!\n");
goto skip;
}
rc = osmo_trau_frame_encode(msgb_data(msg), msg->data_len, &tf);
if (rc < 0) {
LOGPENDP(endp, DE1, LOGL_ERROR,
"E1-I.460-IDLE-TX: error encoding dummy fill frame!\n");
goto skip;
}
msgb_put(msg, rc);
/* enqueue it into the I.460 multiplexer */
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-IDLE-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg),
osmo_ubit_dump(msgb_data(msg), msgb_length(msg) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg)));
osmo_i460_mux_enqueue(endp->e1.schan, msg);
return;
skip:
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_TX_FAIL_CTR));
msgb_free(msg);
}
/* called by I.460 de-multiplexer; feed output of I.460 demux into TRAU frame sync */
@@ -280,6 +178,7 @@ static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int
/* Convert decoded trau frame to RTP frame */
struct osmo_trau2rtp_state t2rs = {
.type = fr.type,
.rtp_extensions = endp->e1.rtp_extensions,
};
rc = osmo_trau2rtp(msgb_data(msg) + rtp_hdr_len, msg->data_len - rtp_hdr_len, &fr, &t2rs);
if (rc <= 0) {
@@ -647,18 +546,20 @@ int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8
/*! Update E1 related parameters (codec and sync pattern).
* \param[in] endp endpoint to update. */
void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
int mgcp_e1_endp_update(struct mgcp_endpoint *endp)
{
struct mgcp_conn *conn;
struct mgcp_conn_rtp *conn_rtp;
struct mgcp_rtp_codec *codec;
enum osmo_tray_sync_pat_id sync_pat_id;
/* In order to determine the codec, find the oldest connection on
* the endpoint and use its codec information. Normally on an E1
* endpoint no more than one connection should exist. */
conn = mgcp_conn_get_oldest(endp);
conn = mgcp_endp_get_conn_oldest(endp);
OSMO_ASSERT(conn);
codec = conn->u.rtp.end.codec;
conn_rtp = mgcp_conn_get_conn_rtp(conn);
codec = conn_rtp->end.cset.codec;
OSMO_ASSERT(codec);
/* Update codec information */
@@ -666,9 +567,17 @@ void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
endp->e1.last_codec = codec;
/* possible RTP extensions, codec-associated */
endp->e1.rtp_extensions = 0;
if (codec->param_present && codec->param.fr_efr_twts001)
endp->e1.rtp_extensions |= OSMO_RTP_EXT_TWTS001;
if (codec->param_present && codec->param.hr_twts002)
endp->e1.rtp_extensions |= OSMO_RTP_EXT_TWTS002;
/* Update sync pattern */
sync_pat_id = determine_trau_sync_pat(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
osmo_trau_sync_set_pat(endp->e1.trau_sync_fi, sync_pat_id);
return 0;
}
/*! Remove E1 resources from endpoint

View File

@@ -24,6 +24,7 @@
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_iuup.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
@@ -58,7 +59,7 @@ static char *gen_virtual_epname(void *ctx, const char *domain,
}
/* Generate E1 endpoint name from given numeric parameters */
static char *gen_e1_epname(void *ctx, const char *domain, unsigned int trunk_nr,
static char *gen_e1_epname(const void *ctx, const char *domain, unsigned int trunk_nr,
uint8_t ts_nr, uint8_t ss_nr)
{
unsigned int rate;
@@ -446,7 +447,7 @@ static uint8_t e1_ss_nr_from_epname(const char *epname)
/* Check if the selected E1 endpoint is avalable, which means that none of
* the overlapping endpoints are currently serving a call. (if the system
* is properly configured such a situation should never ocurr!) */
static bool endp_avail_e1(struct mgcp_endpoint *endp)
static bool endp_avail_e1(const struct mgcp_endpoint *endp)
{
/* The following map shows the overlapping of the subslots and their
* respective rates. The numbers on the right running from top to bottom
@@ -552,7 +553,7 @@ static bool endp_avail_e1(struct mgcp_endpoint *endp)
/*! check if an endpoint is available for any kind of operation.
* \param[in] endp endpoint to check.
* \returns true if endpoint is avalable, false it is blocked for any reason. */
bool mgcp_endp_avail(struct mgcp_endpoint *endp)
bool mgcp_endp_avail(const struct mgcp_endpoint *endp)
{
switch (endp->trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
@@ -569,6 +570,24 @@ bool mgcp_endp_avail(struct mgcp_endpoint *endp)
return false;
}
/*! Get number of conns in an endpoint.
* \param[in] endp endpoint to check.
* \returns Number of connections present in the endpoint. */
unsigned int mgcp_endp_num_conns(const struct mgcp_endpoint *endp)
{
return llist_count(&endp->conns);
}
/*! check if an endpoint can in current state allocate new conns.
* \param[in] endp endpoint to check.
* \returns true if more connections can be allowed on endpoint, false if it is already busy. */
bool mgcp_endp_is_full(const struct mgcp_endpoint *endp)
{
if (endp->type->max_conns == 0)
return false;
return mgcp_endp_num_conns(endp) >= endp->type->max_conns;
}
/*! claim endpoint, sets callid and activates endpoint, should be called at the
* beginning of the CRCX procedure when it is clear that a new call should be
* created.
@@ -622,20 +641,85 @@ int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid)
/*! update endpoint, updates internal endpoint specific data, should be
* after when MDCX or CRCX has been executed successuflly.
* \param[in] endp endpoint to update. */
void mgcp_endp_update(struct mgcp_endpoint *endp)
* \param[in] endp endpoint to update.
* \returns zero on success, mgcp negative error on failure. */
static int mgcp_endp_update_virtual(struct mgcp_endpoint *endp, struct mgcp_conn *conn, enum mgcp_verb verb)
{
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->type == MGCP_CONN_TYPE_RTP);
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
switch (conn_rtp->type) {
case MGCP_RTP_DEFAULT:
break;
case MGCP_RTP_OSMUX:
if (conn_osmux_event_rx_crcx_mdcx(conn_rtp) < 0) {
LOGPCONN(conn, DLMGCP, LOGL_ERROR, "CRCX: Osmux handling failed!\n");
return -500;
}
break;
case MGCP_RTP_IUUP:
return mgcp_conn_iuup_event_rx_crcx_mdcx(conn_rtp);
default:
return -523;
}
return 0;
}
/*! update endpoint, updates internal endpoint specific data, should be
* after when MDCX or CRCX has been executed successuflly.
* \param[in] endp endpoint to update.
* \returns zero on success, mgcp negative error on failure. */
int mgcp_endp_update(struct mgcp_endpoint *endp, struct mgcp_conn *conn, enum mgcp_verb verb)
{
OSMO_ASSERT(conn);
struct mgcp_trunk *trunk = endp->trunk;
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
char new_local_addr[INET6_ADDRSTRLEN];
/* CRCX: Find a local address for conn based on policy and initial SDP remote
* information, then find a free port for it.
* MDCX: msg may have provided a new remote address, which means we may need
* to update our announced IP addr and re-bind our local end. This can
* happen for instance if MGW initially provided an IPv4 during CRCX
* ACK, and now MDCX tells us the remote has an IPv6 address.
*/
if (mgcp_get_local_addr(new_local_addr, conn_rtp) < 0)
goto fail_bind_port_ret;
if (strcmp(new_local_addr, conn_rtp->end.local_addr)) {
osmo_strlcpy(conn_rtp->end.local_addr, new_local_addr, sizeof(conn_rtp->end.local_addr));
mgcp_rtp_end_free_port(&conn_rtp->end);
if (mgcp_trunk_allocate_conn_rtp_ports(trunk, conn_rtp) != 0)
goto fail_bind_port_ret;
}
/* Allocate resources */
switch (endp->trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
/* No updating initaliziation required for virtual endpoints. */
break;
return mgcp_endp_update_virtual(endp, conn, verb);
case MGCP_TRUNK_E1:
mgcp_e1_endp_update(endp);
break;
return mgcp_e1_endp_update(endp);
default:
OSMO_ASSERT(false);
}
return 0;
fail_bind_port_ret:
switch (verb) {
case MGCP_VERB_CRCX:
rate_ctr_inc(rate_ctr_group_get_ctr(trunk->ratectr.mgcp_crcx_ctr_group,
MGCP_CRCX_FAIL_BIND_PORT));
break;
case MGCP_VERB_MDCX:
rate_ctr_inc(rate_ctr_group_get_ctr(trunk->ratectr.mgcp_mdcx_ctr_group,
MGCP_MDCX_FAIL_BIND_PORT));
break;
default:
break;
}
return -500;
}
void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
@@ -655,6 +739,113 @@ void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
mgcp_endp_release(endp);
}
/*! free oldest connection in the list.
* \param[in] endp associated endpoint */
void mgcp_endp_free_conn_oldest(struct mgcp_endpoint *endp)
{
struct mgcp_conn *conn;
if (llist_empty(&endp->conns))
return;
conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry);
mgcp_conn_free(conn);
}
/*! free all connections at once.
* \param[in] endp associated endpoint */
#if defined(__has_attribute)
#if __has_attribute(no_sanitize)
__attribute__((no_sanitize("undefined"))) /* ubsan detects a misaligned load */
#endif
#endif
void mgcp_endp_free_conn_all(struct mgcp_endpoint *endp)
{
struct mgcp_conn *conn;
/* Drop all items in the list, might be consecutive! */
while ((conn = llist_first_entry_or_null(&endp->conns, struct mgcp_conn, entry)))
mgcp_conn_free(conn);
}
/*! find a connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection
* \returns pointer to allocated connection, NULL if not found */
struct mgcp_conn *mgcp_endp_get_conn(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) {
/* Ignore leading zeros in haystack */
for (conn_id = conn->id; *conn_id == '0'; conn_id++);
if (strcmp(conn_id, id_upper) == 0)
return conn;
}
return NULL;
}
/*! get oldest connection in the list.
* \param[in] endp associated endpoint */
struct mgcp_conn *mgcp_endp_get_conn_oldest(struct mgcp_endpoint *endp)
{
if (llist_empty(&endp->conns))
return NULL;
return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
}
/*! find an RTP connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection
* \returns pointer to allocated connection, NULL if not found */
struct mgcp_conn_rtp *mgcp_endp_get_conn_rtp(struct mgcp_endpoint *endp,
const char *id)
{
struct mgcp_conn *conn;
conn = mgcp_endp_get_conn(endp, id);
if (!conn)
return NULL;
if (conn->type == MGCP_CONN_TYPE_RTP)
return mgcp_conn_get_conn_rtp(conn);
return NULL;
}
/* Helps assigning a new lco structure, since "codec" is talloc allocated. */
void mgcp_endp_update_lco(struct mgcp_endpoint *endp, const struct mgcp_lco *lco)
{
/* First free old talloc allocated codec string: */
talloc_free(endp->local_options.codec);
endp->local_options.codec = NULL;
if (lco) {
endp->local_options = *lco;
if (lco->codec)
endp->local_options.codec = talloc_strdup(endp, lco->codec);
} else {
endp->local_options = (struct mgcp_lco){0};
}
}
/*! release endpoint, all open connections are closed.
* \param[in] endp endpoint to release */
void mgcp_endp_release(struct mgcp_endpoint *endp)
@@ -665,7 +856,7 @@ void mgcp_endp_release(struct mgcp_endpoint *endp)
* all connections have been removed already. In case
* that there are still connections open (e.g. when
* RSIP is executed), free them all at once. */
mgcp_conn_free_all(endp);
mgcp_endp_free_conn_all(endp);
/* We must only decrement the stat item when the endpoint as actually
* claimed. An endpoint is claimed when a call-id is set */
@@ -676,10 +867,7 @@ void mgcp_endp_release(struct mgcp_endpoint *endp)
/* Reset endpoint parameters and states */
talloc_free(endp->callid);
endp->callid = NULL;
talloc_free(endp->local_options.string);
endp->local_options.string = NULL;
talloc_free(endp->local_options.codec);
endp->local_options.codec = NULL;
mgcp_endp_update_lco(endp, NULL);
if (endp->trunk->trunk_type == MGCP_TRUNK_E1) {
uint8_t ts = e1_ts_nr_from_epname(endp->name);

View File

@@ -264,6 +264,7 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg
uint8_t *amr_data;
struct rtp_hdr *rtp_hdr;
struct amr_hdr *amr_hdr;
struct mgcp_rtp_codec *dst_codec;
int rc;
ft = osmo_amr_bytes_to_ft(msgb_l3len(msg));
@@ -275,7 +276,8 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg
}
msgb_pull_to_l3(msg);
if (mgcp_codec_amr_is_octet_aligned(conn_rtp_dst->end.codec)) {
dst_codec = conn_rtp_dst->end.cset.codec;
if (mgcp_codec_amr_is_octet_aligned(dst_codec)) {
LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR OA: ft %d, len %d\n", ft, msgb_length(msg));
amr_hdr = (struct amr_hdr *) msgb_push(msg, sizeof(struct amr_hdr));
amr_hdr->cmr = 15; /* no change */
@@ -303,7 +305,7 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg
.extension = 0,
.padding = 0,
.version = 0,
.payload_type = conn_rtp_dst->end.codec->payload_type,
.payload_type = dst_codec->payload_type,
.marker = 0,
.sequence = frame_nr,
.timestamp = 0,
@@ -502,7 +504,7 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
* ignored by the receiver, but still it's useful for debug purposes
* to set it. Moreover, it seems ip.access nano3g produces much worse
* audio output on the air side if timestamp is not set properly. */
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate));
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->cset.codec->rate));
hdr->sequence = osmo_htons(rtp_state->alt_rtp_tx_sequence);
hdr->ssrc = rtp_state->alt_rtp_tx_ssrc;
rtp_state->alt_rtp_tx_sequence++;
@@ -544,13 +546,13 @@ static int _conn_iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
msgb_pull_to_l2(msg);
rtph = (struct rtp_hdr *)msgb_push(msg, sizeof(*rtph));
/* TODO: fill rtph properly: */
/* rtph is further filled in mgcp_send_iuup() below. */
*rtph = (struct rtp_hdr){
.csrc_count = 0,
.extension = 0,
.padding = 0,
.version = 2,
.payload_type = conn_rtp_dst->end.codec->payload_type,
.payload_type = conn_rtp_dst->end.cset.codec->payload_type,
.marker = 0,
.sequence = 0,
.timestamp = 0,
@@ -645,6 +647,7 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn
struct rtp_hdr *rtph;
int rc = -1;
int iuup_length = 0;
struct mgcp_rtp_codec *src_codec;
int8_t rfci;
/* Tx RNL-DATA.req */
@@ -657,13 +660,14 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn
/* TODO: CMR handling & multiple frames handling */
if (strcmp(conn_src_rtp->end.codec->subtype_name, "AMR") != 0) {
src_codec = conn_src_rtp->end.cset.codec;
if (strcmp(src_codec->subtype_name, "AMR") != 0) {
LOG_CONN_RTP(conn_src_rtp, LOGL_ERROR,
"Bridge RTP=>IuUP: Bridging src codec %s to IuUP AMR not supported\n",
conn_src_rtp->end.codec->subtype_name);
src_codec->subtype_name);
goto free_ret;
}
if (mgcp_codec_amr_is_octet_aligned(conn_src_rtp->end.codec)) {
if (mgcp_codec_amr_is_octet_aligned(src_codec)) {
struct amr_hdr *amr_hdr = (struct amr_hdr *) msgb_data(msg);
if (msgb_length(msg) < (sizeof(*amr_hdr))) {
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE,
@@ -749,3 +753,44 @@ int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp)
return 0;
}
/* To be called every time an CRCX/MDCX is received.
* returns: 0 if conn can continue, MGCP negative code if an error ocurred during setup */
int mgcp_conn_iuup_event_rx_crcx_mdcx(struct mgcp_conn_rtp *conn_rtp)
{
struct mgcp_conn *peer_conn;
struct mgcp_conn_rtp *peer_conn_rtp;
OSMO_ASSERT(mgcp_conn_rtp_is_iuup(conn_rtp));
/* keep waiting to receive remote address through CRCX/MDCX */
if (!mgcp_rtp_end_remote_addr_available(&conn_rtp->end))
return 0;
/* Conn already IuUP-configured/initialized, nothing to be done. */
if (conn_rtp->iuup.configured)
return 0;
/* Reached this point, conn_rtp is an IuUP conn which can be configured
* and was not yet configured. If its sister conn in the endpoint was
* already configured as IuUP "passive", then we know this one can be
* configured as IuUP "active" right now. In that case, do so.
* This usually happens on an HNBGW co-located MGW, where during
* RAB-ASS-REQ the RAN-side conn (sister conn here) was already
* configured and then upon rx of RAB-ASS-RESP it sets up (CRCX) the
* CN-side IuUP conn (rt_conn here).
*/
peer_conn = mgcp_find_dst_conn(conn_rtp->conn);
/* No peer conn yet, nothing to be done. */
if (!peer_conn)
return 0;
peer_conn_rtp = mgcp_conn_get_conn_rtp(peer_conn);
if (mgcp_conn_rtp_is_iuup(peer_conn_rtp) &&
peer_conn_rtp->iuup.configured &&
!conn_rtp->iuup.active_init) {
LOG_CONN_RTP(conn_rtp, LOGL_INFO, "Sister IuUP conn in endp configured as passive, init this one as active\n");
OSMO_ASSERT(peer_conn_rtp->iuup.init_ind);
_conn_iuup_configure_as_active(conn_rtp, peer_conn_rtp->iuup.init_ind);
}
return 0;
}

View File

@@ -23,6 +23,7 @@
*/
#include <limits.h>
#include <ctype.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/osmux.h>
@@ -33,6 +34,10 @@
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
/* (same fmt as LOGPENDP()) */
#define LOG_MGCP_PDATA(PDATA, LEVEL, FMT, ARGS...) \
LOGP(DLMGCP, LEVEL, "%s: endpoint(%s) " FMT, (PDATA)->rq->name, (PDATA)->epname ? : "null-epname", ##ARGS)
/*! Display an mgcp message on the log output.
* \param[in] message mgcp message string
* \param[in] len message mgcp message string length
@@ -76,61 +81,215 @@ void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble)
/*! Parse connection mode.
* \param[in] mode as string (recvonly, sendrecv, sendonly confecho or loopback)
* \param[in] endp pointer to endpoint (only used for log output)
* \param[out] associated connection to be modified accordingly
* \returns 0 on success, -1 on error */
int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
struct mgcp_conn *conn)
* \returns MGCP_CONN_* on success, MGCP_CONN_NONE on error */
enum mgcp_connection_mode mgcp_parse_conn_mode(const char *mode)
{
int ret = 0;
if (!mode) {
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
"missing connection mode\n");
return -1;
}
if (!conn)
return -1;
if (!endp)
return -1;
if (!mode)
return MGCP_CONN_NONE;
if (strcasecmp(mode, "recvonly") == 0)
conn->mode = MGCP_CONN_RECV_ONLY;
else if (strcasecmp(mode, "sendrecv") == 0)
conn->mode = MGCP_CONN_RECV_SEND;
else if (strcasecmp(mode, "sendonly") == 0)
conn->mode = MGCP_CONN_SEND_ONLY;
else if (strcasecmp(mode, "confecho") == 0)
conn->mode = MGCP_CONN_CONFECHO;
else if (strcasecmp(mode, "loopback") == 0)
conn->mode = MGCP_CONN_LOOPBACK;
else {
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
"unknown connection mode: '%s'\n", mode);
ret = -1;
return MGCP_CONN_RECV_ONLY;
if (strcasecmp(mode, "sendrecv") == 0)
return MGCP_CONN_RECV_SEND;
if (strcasecmp(mode, "sendonly") == 0)
return MGCP_CONN_SEND_ONLY;
if (strcasecmp(mode, "confecho") == 0)
return MGCP_CONN_CONFECHO;
if (strcasecmp(mode, "loopback") == 0)
return MGCP_CONN_LOOPBACK;
return MGCP_CONN_NONE;
}
/*! Helper function for check_local_cx_options() to get a pointer of the next
* lco option identifier
* \param[in] lco string
* \returns pointer to the beginning of the LCO identifier, NULL on failure */
char *get_lco_identifier(const char *options)
{
char *ptr;
unsigned int count = 0;
/* Jump to the end of the lco identifier */
ptr = strstr(options, ":");
if (!ptr)
return NULL;
/* Walk backwards until the pointer points to the beginning of the
* lco identifier. We know that we stand at the beginning when we
* are either at the beginning of the memory or see a space or
* comma. (this is tolerant, it will accept a:10, b:11 as well as
* a:10,b:11) */
while (1) {
/* Endless loop protection */
if (count > 10000)
return NULL;
else if (ptr < options || *ptr == ' ' || *ptr == ',') {
ptr++;
break;
}
ptr--;
count++;
}
/* Special handling for RTP connections */
if (conn->type == MGCP_CONN_TYPE_RTP) {
conn->u.rtp.end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY);
/* Check if we got any result */
if (*ptr == ':')
return NULL;
return ptr;
}
/*! Check the LCO option. This function checks for multiple appearance of LCO
* options, which is illegal
* \param[in] ctx talloc context
* \param[in] lco string
* \returns 0 on success, -1 on failure */
int check_local_cx_options(void *ctx, const char *options)
{
int i;
char *options_copy;
char *lco_identifier;
char *lco_identifier_end;
char *next_lco_identifier;
char **lco_seen;
unsigned int lco_seen_n = 0;
if (!options)
return -1;
lco_seen =
(char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
options_copy = talloc_strdup(ctx, options);
lco_identifier = options_copy;
do {
/* Move the lco_identifier pointer to the beginning of the
* current lco option identifier */
lco_identifier = get_lco_identifier(lco_identifier);
if (!lco_identifier)
goto error;
/* Look ahead to the next LCO option early, since we
* will parse destructively */
next_lco_identifier = strstr(lco_identifier + 1, ",");
/* Pinch off the end of the lco field identifier name
* and see if we still got something, also check if
* there is some value after the colon. */
lco_identifier_end = strstr(lco_identifier, ":");
if (!lco_identifier_end)
goto error;
if (*(lco_identifier_end + 1) == ' '
|| *(lco_identifier_end + 1) == ','
|| *(lco_identifier_end + 1) == '\0')
goto error;
*lco_identifier_end = '\0';
if (strlen(lco_identifier) == 0)
goto error;
/* Check if we have already seen the current field identifier
* before. If yes, we must bail, an LCO must only appear once
* in the LCO string */
for (i = 0; i < lco_seen_n; i++) {
if (strcasecmp(lco_seen[i], lco_identifier) == 0)
goto error;
}
lco_seen[lco_seen_n] = lco_identifier;
lco_seen_n++;
/* The first identifier must always be found at the beginnning
* of the LCO string */
if (lco_seen[0] != options_copy)
goto error;
/* Go to the next lco option */
lco_identifier = next_lco_identifier;
} while (lco_identifier);
talloc_free(lco_seen);
talloc_free(options_copy);
return 0;
error:
talloc_free(lco_seen);
talloc_free(options_copy);
return -1;
}
/* Set the LCO from a string (see RFC 3435).
* The string is stored in the 'string' field. A NULL string is handled exactly
* like an empty string, the 'string' field is never NULL after this function
* has been called. */
static int mgcp_parse_lco(void *ctx, struct mgcp_lco *lco, const char *options)
{
const char *lco_id;
char codec[17];
char nt[17];
int len;
if (!options)
return 0;
if (strlen(options) == 0)
return 0;
lco->present = true;
/* Make sure the encoding of the LCO is consistent before we proceed */
if (check_local_cx_options(ctx, options) != 0) {
LOGP(DLMGCP, LOGL_ERROR,
"local CX options: Internal inconsistency in Local Connection Options!\n");
return -524;
}
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
lco_id = options;
while ((lco_id = get_lco_identifier(lco_id))) {
switch (tolower(lco_id[0])) {
case 'p':
if (sscanf(lco_id + 1, ":%d-%d",
&lco->pkt_period_min, &lco->pkt_period_max) == 1)
lco->pkt_period_max = lco->pkt_period_min;
break;
case 'a':
/* FIXME: LCO also supports the negotiation of more than one codec.
* (e.g. a:PCMU;G726-32) But this implementation only supports a single
* codec only. Ignoring all but the first codec. */
if (sscanf(lco_id + 1, ":%16[^,;]", codec) == 1) {
talloc_free(lco->codec);
/* MGCP header is case insensive, and we'll need
codec in uppercase when using it later: */
len = strlen(codec);
lco->codec = talloc_size(ctx, len + 1);
osmo_str_toupper_buf(lco->codec, len + 1, codec);
}
break;
case 'n':
if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1)
break;
/* else: fall through to print notice log */
default:
LOGP(DLMGCP, LOGL_NOTICE,
"LCO: unhandled option: '%c'/%d in \"%s\"\n",
*lco_id, *lco_id, options);
break;
}
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) {
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %u\n",
conn->u.rtp.end.output_enabled);
lco_id = strchr(lco_id, ',');
if (!lco_id)
break;
}
/* The VTY might change the connection mode at any time, so we have
* to hold a copy of the original connection mode */
conn->mode_orig = conn->mode;
LOGP(DLMGCP, LOGL_DEBUG,
"local CX options: lco->pkt_period_max: %d, lco->codec: %s\n",
lco->pkt_period_max, lco->codec);
return ret;
/* Check if the packetization fits the 20ms raster */
if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
LOGP(DLMGCP, LOGL_ERROR,
"local CX options: packetization interval is not a multiple of 20ms!\n");
return -535;
}
return 0;
}
/*! Analyze and parse the the hader of an MGCP messeage string.
@@ -159,8 +318,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
break;
case 2:
if (strcasecmp("MGCP", elem)) {
LOGP(DLMGCP, LOGL_ERROR,
"MGCP header parsing error\n");
LOG_MGCP_PDATA(pdata, LOGL_ERROR, "MGCP header parsing error\n");
return -510;
}
break;
@@ -173,13 +331,121 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
}
if (i != 4) {
LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n");
LOG_MGCP_PDATA(pdata, LOGL_ERROR, "MGCP status line too short.\n");
return -510;
}
return 0;
}
static bool parse_x_osmo_ign(struct mgcp_parse_data *pdata, char *line)
{
char *saveptr = NULL;
if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
return false;
line += strlen(MGCP_X_OSMO_IGN_HEADER);
while (1) {
char *token = strtok_r(line, " ", &saveptr);
line = NULL;
if (!token)
break;
if (!strcasecmp(token, "C"))
pdata->hpars.x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
else
LOG_MGCP_PDATA(pdata, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
}
return true;
}
/*! Analyze and parse the the header of an MGCP message string.
* \param[inout] pdata caller provided memory to store the parsing results.
* \returns 0 when parsing was successful, negative (MGCP cause code) on error. */
int mgcp_parse_hdr_pars(struct mgcp_parse_data *pdata)
{
struct mgcp_parse_hdr_pars *hp = &pdata->hpars;
char *line;
int rc;
mgcp_parse_hdr_pars_init(hp);
for_each_line(line, pdata->save) {
if (!mgcp_check_param(line)) {
LOG_MGCP_PDATA(pdata, LOGL_NOTICE, "wrong MGCP option format: '%s'\n", line);
continue;
}
switch (toupper(line[0])) {
case 'L':
hp->lco_string = (const char *)line + 3;
rc = mgcp_parse_lco(pdata, &hp->lco, hp->lco_string);
if (rc < 0) {
LOG_MGCP_PDATA(pdata, LOGL_NOTICE, "Invalid LocalConnectionOptions line: '%s'", line);
switch (pdata->rq->verb) {
case MGCP_VERB_CRCX:
rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_crcx_ctr_group,
MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
break;
case MGCP_VERB_MDCX:
rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_mdcx_ctr_group,
MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
break;
default:
break;
}
return rc;
}
break;
case 'C':
hp->callid = (const char *)line + 3;
break;
case 'I':
hp->connid = (const char *)line + 3;
break;
case 'M':
hp->mode = mgcp_parse_conn_mode((const char *)line + 3);
break;
case 'X':
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
hp->remote_osmux_cid = mgcp_parse_osmux_cid(line);
break;
}
if (parse_x_osmo_ign(pdata, line))
break;
/* Ignore unknown X-headers */
break;
case '\0':
hp->have_sdp = true;
goto mgcp_header_done;
default:
LOG_MGCP_PDATA(pdata, LOGL_NOTICE, "unhandled option: '%c'/%d\n", *line, *line);
switch (pdata->rq->verb) {
case MGCP_VERB_CRCX:
rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_crcx_ctr_group,
MGCP_CRCX_FAIL_UNHANDLED_PARAM));
break;
case MGCP_VERB_MDCX:
rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_mdcx_ctr_group,
MGCP_MDCX_FAIL_UNHANDLED_PARAM));
break;
case MGCP_VERB_DLCX:
rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_dlcx_ctr_group,
MGCP_DLCX_FAIL_UNHANDLED_PARAM));
break;
default:
break;
}
return -539;
}
}
mgcp_header_done:
return 0;
}
/*! 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 */
@@ -190,19 +456,19 @@ int mgcp_parse_osmux_cid(const char *line)
if (strcasecmp(line + 2, "Osmux: *") == 0) {
LOGP(DLMGCP, LOGL_DEBUG, "Parsed wilcard Osmux CID\n");
return -1;
return MGCP_PARSE_HDR_PARS_OSMUX_CID_WILDCARD;
}
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;
return MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET;
}
if (osmux_cid > OSMUX_CID_MAX) {
LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
osmux_cid, OSMUX_CID_MAX);
return -2;
return MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET;
}
LOGP(DLMGCP, LOGL_DEBUG, "MGCP client offered Osmux CID %u\n", osmux_cid);
@@ -210,20 +476,13 @@ int mgcp_parse_osmux_cid(const char *line)
}
/*! Check MGCP parameter line (string) for plausibility.
* \param[in] endp pointer to endpoint (only used for log output, may be NULL)
* \param[in] trunk pointer to trunk (only used for log output, may be NULL if endp is not NULL)
* \param[in] line single parameter line from the MGCP message
* \returns true when line seems plausible, false on error */
bool mgcp_check_param(const struct mgcp_endpoint *endp, struct mgcp_trunk *trunk, const char *line)
bool mgcp_check_param(const char *line)
{
const size_t line_len = strlen(line);
if (line[0] != '\0' && line_len < 2) {
if (endp)
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "wrong MGCP option format: '%s'\n", line);
else
LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE, "wrong MGCP option format: '%s'\n", line);
if (line[0] != '\0' && line_len < 2)
return false;
}
/* FIXME: A couple more checks wouldn't hurt... */
@@ -242,10 +501,6 @@ 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)
@@ -294,7 +549,7 @@ int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
}
/* Check if connection exists */
if (mgcp_conn_get(endp, conn_id))
if (mgcp_endp_get_conn(endp, conn_id))
return 0;
LOGPENDP(endp, DLMGCP, LOGL_ERROR,

View File

@@ -85,12 +85,6 @@ static inline struct msgb *mgw_msgb_copy_c(void *ctx, struct msgb *msg, const ch
static int rx_rtp(struct msgb *msg);
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end)
{
return (osmo_sockaddr_port(&rtp_end->addr.u.sa) != 0) &&
(osmo_sockaddr_is_any(&rtp_end->addr) == 0);
}
/*! Determine the local rtp bind IP-address.
* \param[out] addr caller provided memory to store the resulting IP-Address.
* \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters.
@@ -338,7 +332,7 @@ static int adjust_rtp_timestamp_offset(const struct mgcp_endpoint *endp,
osmo_sockaddr_ntop(&addr->u.sa, ipbuf),
osmo_sockaddr_port(&addr->u.sa));
} else {
tsdelta = rtp_end->codec->rate * 20 / 1000;
tsdelta = rtp_end->cset.codec->rate * 20 / 1000;
LOGPENDP(endp, DRTP, LOGL_NOTICE,
"Fixed packet duration and last timestamp delta "
"are not available, "
@@ -414,31 +408,6 @@ static int align_rtp_timestamp_offset(const struct mgcp_endpoint *endp,
return ts_error;
}
/*! dummy callback to disable transcoding (see also cfg->rtp_processing_cb).
* \param[in] associated endpoint.
* \param[in] destination RTP end.
* \param[in,out] msg message bufffer containing data. Function might change length.
* \returns ignores input parameters, return always 0. */
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
struct msgb *msg)
{
return 0;
}
/*! dummy callback to disable transcoding (see also cfg->setup_rtp_processing_cb).
* \param[in] associated endpoint.
* \param[in] destination RTP connnection.
* \param[in] source RTP connection.
* \returns ignores input parameters, return always 0. */
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn_dst,
struct mgcp_conn_rtp *conn_src)
{
LOGPENDP(endp, DRTP, LOGL_DEBUG, "transcoding disabled\n");
return 0;
}
void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp,
struct mgcp_rtp_state *state, const uint16_t seq,
const int32_t transit, const uint32_t ssrc,
@@ -502,11 +471,11 @@ static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
}
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
if (!conn_dst->end.codec) {
if (!conn_dst->end.cset.codec) {
LOG_CONN_RTP(conn_dst, LOGL_NOTICE, "no codec set on destination connection!\n");
return -EINVAL;
}
rtp_hdr->payload_type = (uint8_t) conn_dst->end.codec->payload_type;
rtp_hdr->payload_type = (uint8_t) conn_dst->end.cset.codec->payload_type;
return 0;
}
@@ -529,7 +498,8 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
uint32_t timestamp, ssrc;
bool marker_bit;
struct rtp_hdr *rtp_hdr;
int payload = rtp_end->codec->payload_type;
struct mgcp_rtp_codec *codec = rtp_end->cset.codec;
int payload = codec->payload_type;
unsigned int len = msgb_length(msg);
if (len < sizeof(*rtp_hdr))
@@ -538,7 +508,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
seq = ntohs(rtp_hdr->sequence);
timestamp = ntohl(rtp_hdr->timestamp);
arrival_time = mgcp_get_current_ts(rtp_end->codec->rate);
arrival_time = mgcp_get_current_ts(codec->rate);
ssrc = ntohl(rtp_hdr->ssrc);
marker_bit = !!rtp_hdr->marker;
transit = arrival_time - timestamp;
@@ -547,16 +517,16 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
if (!state->initialized) {
state->initialized = 1;
state->packet_duration = mgcp_rtp_packet_duration(endp, rtp_end);
state->in_stream.last_seq = seq - 1;
state->in_stream.ssrc = state->patch.orig_ssrc = ssrc;
state->in_stream.ssrc = ssrc;
state->in_stream.last_tsdelta = 0;
state->packet_duration =
mgcp_rtp_packet_duration(endp, rtp_end);
state->out_stream.last_seq = seq - 1;
state->out_stream.ssrc = state->patch.orig_ssrc = ssrc;
state->out_stream.last_tsdelta = 0;
state->out_stream.last_timestamp = timestamp;
state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */
state->patch.orig_ssrc = ssrc;
state->patch.patch_ssrc = rtp_end->force_constant_ssrc;
LOGPENDP(endp, DRTP, LOGL_INFO,
"initializing stream, SSRC: %u timestamp: %u "
"pkt-duration: %d, from %s:%d\n",
@@ -566,7 +536,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
osmo_sockaddr_port(&addr->u.sa));
if (state->packet_duration == 0) {
state->packet_duration =
rtp_end->codec->rate * 20 / 1000;
codec->rate * 20 / 1000;
LOGPENDP(endp, DRTP, LOGL_NOTICE,
"fixed packet duration is not available, "
"using fixed 20ms instead: %d from %s:%d\n",
@@ -583,7 +553,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
osmo_sockaddr_port(&addr->u.sa));
state->in_stream.ssrc = ssrc;
if (rtp_end->force_constant_ssrc) {
if (state->patch.patch_ssrc) {
int16_t delta_seq;
/* Always increment seqno by 1 */
@@ -599,10 +569,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
adjust_rtp_timestamp_offset(endp, state, rtp_end, addr,
delta_seq, timestamp, marker_bit);
state->patch.patch_ssrc = true;
ssrc = state->patch.orig_ssrc;
if (rtp_end->force_constant_ssrc != -1)
rtp_end->force_constant_ssrc -= 1;
LOGPENDP(endp, DRTP, LOGL_NOTICE,
"SSRC patching enabled, SSRC: %u "
@@ -822,8 +789,8 @@ static void gen_rtp_header(struct msgb *msg, struct mgcp_rtp_end *rtp_end,
return;
hdr->version = 2;
hdr->payload_type = rtp_end->codec->payload_type;
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate));
hdr->payload_type = rtp_end->cset.codec->payload_type;
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->cset.codec->rate));
hdr->sequence = osmo_htons(state->alt_rtp_tx_sequence);
hdr->ssrc = state->alt_rtp_tx_ssrc;
}
@@ -1189,34 +1156,32 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
);
} else if (is_rtp) {
struct mgcp_rtp_codec *src_codec;
struct mgcp_rtp_codec *dst_codec;
/* Make sure we have a valid RTP header, in cases where no RTP
* header is present, we will generate one. */
gen_rtp_header(msg, rtp_end, rtp_state);
/* Run transcoder */
rc = endp->trunk->cfg->rtp_processing_cb(endp, rtp_end, msg);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error %d during transcoding\n", rc);
msgb_free(msg);
return rc;
}
/* TODO: Run transcoder */
if (addr)
mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, msg);
src_codec = conn_src->end.cset.codec;
dst_codec = conn_dst->end.cset.codec;
if (mgcp_conn_rtp_is_iuup(conn_dst) || mgcp_conn_rtp_is_iuup(conn_src)) {
/* the iuup code will correctly transform to the correct AMR mode */
} else if (mgcp_codec_amr_align_mode_is_indicated(conn_dst->end.codec)) {
rc = amr_oa_bwe_convert(endp, msg, conn_dst->end.codec->param.amr_octet_aligned);
} else if (mgcp_codec_amr_align_mode_is_indicated(dst_codec)) {
rc = amr_oa_bwe_convert(endp, msg, dst_codec->param.amr_octet_aligned);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"Error in AMR octet-aligned <-> bandwidth-efficient mode conversion (target=%s)\n",
conn_dst->end.codec->param.amr_octet_aligned ? "octet-aligned" : "bandwidth-efficient");
dst_codec->param.amr_octet_aligned ? "octet-aligned" : "bandwidth-efficient");
msgb_free(msg);
return rc;
}
} else if (rtp_end->rfc5993_hr_convert &&
strcmp(conn_src->end.codec->subtype_name, "GSM-HR-08") == 0) {
strcmp(src_codec->subtype_name, "GSM-HR-08") == 0) {
rc = rfc5993_hr_convert(endp, msg);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
@@ -1343,13 +1308,13 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
* packets back to their origin. We will use the originating
* address data from the UDP packet header to patch the
* outgoing address in connection on the fly */
if (osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa) == 0) {
memcpy(&conn->u.rtp.end.addr, from_addr,
sizeof(conn->u.rtp.end.addr));
if (osmo_sockaddr_port(&conn_src->end.addr.u.sa) == 0) {
memcpy(&conn_src->end.addr, from_addr,
sizeof(conn_src->end.addr));
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
"loopback mode: implicitly using source address (%s:%u) as destination address\n",
osmo_sockaddr_ntop(&from_addr->u.sa, ipbuf),
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa));
osmo_sockaddr_port(&conn_src->end.addr.u.sa));
}
return mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
}
@@ -1428,19 +1393,19 @@ int mgcp_dispatch_e1_bridge_cb(struct msgb *msg)
* packets back to their origin. We will use the originating
* address data from the UDP packet header to patch the
* outgoing address in connection on the fly */
if (osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa) == 0) {
memcpy(&conn->u.rtp.end.addr, from_addr,
sizeof(conn->u.rtp.end.addr));
if (osmo_sockaddr_port(&conn_src->end.addr.u.sa) == 0) {
memcpy(&conn_src->end.addr, from_addr,
sizeof(conn_src->end.addr));
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
"loopback mode: implicitly using source address (%s:%u) as destination address\n",
osmo_sockaddr_ntop(&from_addr->u.sa, ipbuf),
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa));
osmo_sockaddr_port(&conn_src->end.addr.u.sa));
}
return mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
}
/* Forward to E1 */
return mgcp_e1_send_rtp(conn->endp, conn->u.rtp.end.codec, msg);
return mgcp_e1_send_rtp(conn->endp, conn_src->end.cset.codec, msg);
}
/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
@@ -1562,18 +1527,18 @@ static int rx_rtp(struct msgb *msg)
/* Handle AMR frame format conversion (octet-aligned vs. bandwith-efficient) */
if (mc->proto == MGCP_PROTO_RTP
&& conn_src->end.codec
&& mgcp_codec_amr_align_mode_is_indicated(conn_src->end.codec)) {
&& conn_src->end.cset.codec
&& mgcp_codec_amr_align_mode_is_indicated(conn_src->end.cset.codec)) {
/* Make sure that the incoming AMR frame format matches the frame format that the call agent has
* communicated via SDP when the connection was created/modfied. */
int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg));
if (oa < 0)
goto out_free;
if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned) {
if (((bool)oa) != conn_src->end.cset.codec->param.amr_octet_aligned) {
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
"rx_rtp(%u bytes): Expected RTP AMR octet-aligned=%u but got octet-aligned=%u."
" check the config of your call-agent!\n",
msgb_length(msg), conn_src->end.codec->param.amr_octet_aligned, oa);
msgb_length(msg), conn_src->end.cset.codec->param.amr_octet_aligned, oa);
goto out_free;
}
}
@@ -1629,78 +1594,23 @@ int mgcp_create_bind(const char *source_addr, int port, uint8_t dscp, uint8_t pr
return rc;
}
/* Bind RTP and RTCP port (helper function for mgcp_bind_net_rtp_port()) */
static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
struct mgcp_rtp_end *rtp_end, struct mgcp_endpoint *endp)
{
int rc, rtp_fd, rtcp_fd;
/* NOTE: The port that is used for RTCP is the RTP port incremented by one
* (e.g. RTP-Port = 16000 ==> RTCP-Port = 16001) */
rc = mgcp_create_bind(source_addr, rtp_end->local_port, cfg->endp_dscp, cfg->endp_priority);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"failed to create RTP port: %s:%d\n",
source_addr, rtp_end->local_port);
goto cleanup0;
}
rtp_fd = rc;
rc = mgcp_create_bind(source_addr, rtp_end->local_port + 1, cfg->endp_dscp, cfg->endp_priority);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"failed to create RTCP port: %s:%d\n",
source_addr, rtp_end->local_port + 1);
goto cleanup1;
}
rtcp_fd = rc;
if (osmo_iofd_register(rtp_end->rtp, rtp_fd) < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"failed to register RTP port %d\n",
rtp_end->local_port);
goto cleanup2;
}
if (osmo_iofd_register(rtp_end->rtcp, rtcp_fd) != 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"failed to register RTCP port %d\n",
rtp_end->local_port + 1);
goto cleanup3;
}
return 0;
cleanup3:
osmo_iofd_unregister(rtp_end->rtp);
cleanup2:
close(rtcp_fd);
cleanup1:
close(rtp_fd);
cleanup0:
return -1;
}
/*! bind RTP port to endpoint/connection.
* \param[in] endp endpoint that holds the RTP connection.
* \param[in] rtp_port port number to bind on.
* \param[in] conn associated RTP connection.
* \param[in] rtp_port port number to bind on.
* \returns 0 on success, -1 on ERROR. */
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
struct mgcp_conn_rtp *conn)
int mgcp_conn_rtp_bind_rtp_ports(struct mgcp_conn_rtp *conn_rtp, int rtp_port)
{
char name[512];
struct mgcp_rtp_end *end;
struct mgcp_conn *conn = conn_rtp->conn;
struct mgcp_config *cfg = conn->endp->trunk->cfg;
struct mgcp_rtp_end *end = &conn_rtp->end;
int rc, rtp_fd, rtcp_fd;
snprintf(name, sizeof(name), "%s-%s", conn->conn->name, conn->conn->id);
end = &conn->end;
snprintf(name, sizeof(name), "%s-%s", conn->name, conn->id);
if ((end->rtp && osmo_iofd_get_fd(end->rtp) != -1) ||
(end->rtcp && osmo_iofd_get_fd(end->rtcp) != -1)) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "%u was already bound on conn:%s\n",
rtp_port, mgcp_conn_dump(conn->conn));
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "%u was already bound\n", rtp_port);
/* Double bindings should never occour! Since we always allocate
* connections dynamically and free them when they are not
* needed anymore, there must be no previous binding leftover.
@@ -1710,33 +1620,55 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
}
end->local_port = rtp_port;
end->rtp = osmo_iofd_setup(conn->conn, -1, name, OSMO_IO_FD_MODE_RECVFROM_SENDTO, &rtp_ioops, conn);
end->rtp = osmo_iofd_setup(conn, -1, name, OSMO_IO_FD_MODE_RECVFROM_SENDTO, &rtp_ioops, conn_rtp);
if (!end->rtp)
return -EIO;
goto free_iofd_ret;
osmo_iofd_set_alloc_info(end->rtp, RTP_BUF_SIZE, 0);
end->rtcp = osmo_iofd_setup(conn->conn, -1, name, OSMO_IO_FD_MODE_RECVFROM_SENDTO, &rtp_ioops, conn);
if (!end->rtcp) {
osmo_iofd_free(end->rtp);
end->rtp = NULL;
return -EIO;
}
end->rtcp = osmo_iofd_setup(conn, -1, name, OSMO_IO_FD_MODE_RECVFROM_SENDTO, &rtp_ioops, conn_rtp);
if (!end->rtcp)
goto free_iofd_ret;
osmo_iofd_set_alloc_info(end->rtcp, RTP_BUF_SIZE, 0);
osmo_iofd_set_priv_nr(end->rtcp, 1); /* we use priv_nr as identifier for RTCP */
return bind_rtp(endp->trunk->cfg, conn->end.local_addr, end, endp);
}
/* NOTE: The port that is used for RTCP is the RTP port incremented by one
* (e.g. RTP-Port = 16000 ==> RTCP-Port = 16001) */
/*! free allocated RTP and RTCP ports.
* \param[in] end RTP end */
void mgcp_free_rtp_port(struct mgcp_rtp_end *end)
{
if (end->rtp) {
osmo_iofd_free(end->rtp);
end->rtp = NULL;
rc = mgcp_create_bind(end->local_addr, end->local_port, cfg->endp_dscp, cfg->endp_priority);
if (rc < 0) {
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "failed to create RTP port: %s:%d\n", end->local_addr, end->local_port);
goto free_iofd_ret;
}
rtp_fd = rc;
rc = mgcp_create_bind(end->local_addr, end->local_port + 1, cfg->endp_dscp, cfg->endp_priority);
if (rc < 0) {
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "failed to create RTCP port: %s:%d\n", end->local_addr, end->local_port + 1);
goto cleanup1;
}
rtcp_fd = rc;
if (osmo_iofd_register(end->rtp, rtp_fd) < 0) {
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "failed to register RTP port %d\n", end->local_port);
goto cleanup2;
}
if (end->rtcp) {
osmo_iofd_free(end->rtcp);
end->rtcp = NULL;
if (osmo_iofd_register(end->rtcp, rtcp_fd) != 0) {
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "failed to register RTCP port %d\n", end->local_port + 1);
goto cleanup3;
}
return 0;
cleanup3:
osmo_iofd_unregister(end->rtp);
cleanup2:
close(rtcp_fd);
cleanup1:
close(rtp_fd);
free_iofd_ret:
osmo_iofd_free(end->rtcp);
end->rtcp = NULL;
osmo_iofd_free(end->rtp);
end->rtp = NULL;
return -EIO;
}

View File

@@ -211,6 +211,7 @@ osmux_handle_find_or_create(const struct mgcp_trunk *trunk, const struct osmo_so
int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg)
{
int ret;
size_t msg_len;
if (!conn->end.output_enabled) {
rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
@@ -234,15 +235,17 @@ int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg)
return -1;
}
msg_len = msgb_length(msg);
while ((ret = osmux_xfrm_input(conn->osmux.in, msg, conn->osmux.remote_cid)) > 0) {
/* batch full, build and deliver it */
osmux_xfrm_input_deliver(conn->osmux.in);
}
/* NOTE: At this point msg is now owned by osmux subsystem and may have been potentially freed. */
if (ret < 0) {
rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
} else {
rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_CTR);
rtpconn_osmux_rate_ctr_add(conn, OSMUX_AMR_OCTETS_TX_CTR, msgb_length(msg) - sizeof(struct rtp_hdr));
rtpconn_osmux_rate_ctr_add(conn, OSMUX_AMR_OCTETS_TX_CTR, msg_len - sizeof(struct rtp_hdr));
}
return 0;
}
@@ -265,7 +268,7 @@ osmux_conn_lookup(const struct mgcp_trunk *trunk, uint8_t local_cid, const struc
if (conn->type != MGCP_CONN_TYPE_RTP)
continue;
conn_rtp = &conn->u.rtp;
conn_rtp = mgcp_conn_get_conn_rtp(conn);
if (!mgcp_conn_rtp_is_osmux(conn_rtp))
continue;
@@ -495,6 +498,9 @@ int osmux_init(struct mgcp_trunk *trunk)
int ret, fd;
struct mgcp_config *cfg = trunk->cfg;
if (cfg->osmux.initialized)
return 0;
/* So far we only support running on one trunk: */
OSMO_ASSERT(trunk == mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID));
@@ -507,19 +513,19 @@ int osmux_init(struct mgcp_trunk *trunk)
ret = mgcp_create_bind(cfg->osmux.local_addr_v4, cfg->osmux.local_port,
cfg->endp_dscp, cfg->endp_priority);
if (ret < 0) {
LOGP(DOSMUX, LOGL_ERROR, "Cannot bind OSMUX IPv4 socket to %s:%u\n",
cfg->osmux.local_addr_v4, cfg->osmux.local_port);
LOGPTRUNK(trunk, DOSMUX, LOGL_ERROR, "Cannot bind OSMUX IPv4 socket to %s:%u\n",
cfg->osmux.local_addr_v4, cfg->osmux.local_port);
goto out_free_v4;
}
fd = ret;
ret = osmo_iofd_register(osmux_fd_v4, fd);
if (ret < 0) {
LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv4 socket %s\n", osmo_sock_get_name2(fd));
LOGPTRUNK(trunk, DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv4 socket %s\n", osmo_sock_get_name2(fd));
close(fd);
goto out_free_v4;
}
LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv4 socket listening on %s\n", osmo_sock_get_name2(fd));
LOGPTRUNK(trunk, DOSMUX, LOGL_INFO, "OSMUX IPv4 socket listening on %s\n", osmo_sock_get_name2(fd));
}
osmux_fd_v6 = osmo_iofd_setup(trunk, -1, "osmux_fd_v6", OSMO_IO_FD_MODE_RECVFROM_SENDTO, &osmux_ioops, trunk);
@@ -531,8 +537,8 @@ int osmux_init(struct mgcp_trunk *trunk)
ret = mgcp_create_bind(cfg->osmux.local_addr_v6, cfg->osmux.local_port,
cfg->endp_dscp, cfg->endp_priority);
if (ret < 0) {
LOGP(DOSMUX, LOGL_ERROR, "Cannot bind OSMUX IPv6 socket to [%s]:%u\n",
cfg->osmux.local_addr_v6, cfg->osmux.local_port);
LOGPTRUNK(trunk, DOSMUX, LOGL_ERROR, "Cannot bind OSMUX IPv6 socket to [%s]:%u\n",
cfg->osmux.local_addr_v6, cfg->osmux.local_port);
goto out_free_v6;
}
fd = ret;
@@ -543,9 +549,10 @@ int osmux_init(struct mgcp_trunk *trunk)
close(fd);
goto out_free_v6;
}
LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv6 socket listening on %s\n", osmo_sock_get_name2(fd));
LOGPTRUNK(trunk, DOSMUX, LOGL_INFO, "OSMUX IPv6 socket listening on %s\n", osmo_sock_get_name2(fd));
}
cfg->osmux.initialized = true;
LOGPTRUNK(trunk, DOSMUX, LOGL_NOTICE, "OSMUX socket has been set up\n");
return 0;
out_free_v6:
@@ -557,6 +564,7 @@ out_free_v4:
osmo_iofd_free(osmux_fd_v4);
osmux_fd_v4 = NULL;
out:
LOGPTRUNK(trunk, DOSMUX, LOGL_ERROR, "Cannot init OSMUX\n");
return -1;
}
@@ -651,7 +659,7 @@ int conn_osmux_enable(struct mgcp_conn_rtp *conn)
osmux_xfrm_output_set_rtp_ssrc(conn->osmux.out,
(conn->osmux.remote_cid * rtp_ssrc_winlen) +
(random() % rtp_ssrc_winlen));
osmux_xfrm_output_set_rtp_pl_type(conn->osmux.out, conn->end.codec->payload_type);
osmux_xfrm_output_set_rtp_pl_type(conn->osmux.out, conn->end.cset.codec->payload_type);
osmux_xfrm_output_set_tx_cb(conn->osmux.out,
scheduled_from_osmux_tx_rtp_cb, conn);

File diff suppressed because it is too large Load Diff

View File

@@ -93,7 +93,7 @@ static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
[MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS] = { "mdcx:conn_opt", "connection options invalid." },
[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC] =
{ "mdcx:no_remote_conn_desc", "no opposite end specified for connection." },
[MGCP_MDCX_FAIL_START_RTP] = { "mdcx:start_rtp_failure", "failure to start RTP processing." },
[MGCP_MDCX_FAIL_BIND_PORT] = { "mdcx:bind_port", "port bind failure." },
[MGCP_MDCX_FAIL_AVAIL] = { "mdcx:unavailable", "endpoint unavailable." },
};

View File

@@ -0,0 +1,104 @@
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* (C) 2013-2024 by sysmocom - s.f.m.c. GmbH
* 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 <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <osmocom/core/select.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/mgcp/mgcp_rtp_end.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
/***********************
* mgcp_rtp_end
**********************/
void mgcp_rtp_end_init(struct mgcp_rtp_end *end, struct mgcp_conn_rtp *conn_rtp)
{
struct mgcp_trunk *trunk = conn_rtp->conn->endp->trunk;
struct mgcp_config *cfg = trunk->cfg;
end->conn_rtp = conn_rtp;
end->rtp = NULL;
end->rtcp = NULL;
memset(&end->addr, 0, sizeof(end->addr));
end->rtcp_port = 0;
/* Set default values */
end->frames_per_packet = 0; /* unknown */
end->output_enabled = false;
end->maximum_packet_time = -1;
end->force_aligned_timing = trunk->force_aligned_timing;
end->force_constant_ssrc = trunk->force_constant_ssrc;
end->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
if (cfg->force_ptime) {
end->packet_duration_ms = cfg->force_ptime;
end->force_output_ptime = 1;
} else {
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
}
/* Make sure codec table is reset */
mgcp_codecset_reset(&end->cset);
}
void mgcp_rtp_end_cleanup(struct mgcp_rtp_end *end)
{
mgcp_rtp_end_free_port(end);
mgcp_codecset_reset(&end->cset);
}
void mgcp_rtp_end_set_packet_duration_ms(struct mgcp_rtp_end *end, uint32_t packet_duration_ms)
{
if (end->force_output_ptime)
return;
end->packet_duration_ms = packet_duration_ms;
}
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end)
{
return (osmo_sockaddr_port(&rtp_end->addr.u.sa) != 0) &&
(osmo_sockaddr_is_any(&rtp_end->addr) == 0);
}
/*! free allocated RTP and RTCP ports.
* \param[in] end RTP end */
void mgcp_rtp_end_free_port(struct mgcp_rtp_end *end)
{
if (end->rtp) {
osmo_iofd_free(end->rtp);
end->rtp = NULL;
}
if (end->rtcp) {
osmo_iofd_free(end->rtcp);
end->rtcp = NULL;
}
}

View File

@@ -199,7 +199,7 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp
unsigned int pt;
unsigned int count = 0;
char delimiter;
unsigned int amr_octet_aligned;
unsigned int param_val;
memset(fmtp_param, 0, sizeof(*fmtp_param));
@@ -239,12 +239,30 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp
if (delimiter == ';' || delimiter == ',')
str_ptr[strlen(str_ptr) - 1] = '\0';
/* AMR octet aligned parameter (see also RFC 3267, section 8.3) */
if (sscanf(param_str, "octet-align=%d", &amr_octet_aligned) == 1) {
/* We support the following codec parameters:
*
* AMR: octet-align parameter of RFC 4867 section 8.3;
* FR & EFR: tw-ts-001 parameter, Osmocom private;
* HR: tw-ts-002 parameter, Osmocom private.
*
* tw-ts-001 and tw-ts-002 Osmocom-private parameters are used
* only in the context of OsmoBSC driving an E1 MGW endpoint.
*/
if (sscanf(param_str, "octet-align=%d", &param_val) == 1) {
fmtp_param->param.amr_octet_aligned_present = true;
fmtp_param->param.amr_octet_aligned = false;
if (amr_octet_aligned == 1)
if (param_val == 1)
fmtp_param->param.amr_octet_aligned = true;
} else if (sscanf(param_str, "tw-ts-001=%d", &param_val) == 1) {
fmtp_param->param.fr_efr_twts001_present = true;
fmtp_param->param.fr_efr_twts001 = false;
if (param_val == 1)
fmtp_param->param.fr_efr_twts001 = true;
} else if (sscanf(param_str, "tw-ts-002=%d", &param_val) == 1) {
fmtp_param->param.hr_twts002_present = true;
fmtp_param->param.hr_twts002 = false;
if (param_val == 1)
fmtp_param->param.hr_twts002 = true;
}
param_str = strtok(NULL, " ");
@@ -312,16 +330,13 @@ static struct mgcp_codec_param *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_
}
/*! Analyze SDP input string.
* \param[in] endp trunk endpoint.
* \param[out] conn associated rtp connection.
* \param[out] caller provided memory to store the parsing results.
* \param[inout] p provided memory to store the parsing results.
*
* Note: In conn (conn->end) the function returns the packet duration,
* rtp port, rtcp port and the codec information.
* \returns 0 on success, -1 on failure. */
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
int mgcp_parse_sdp_data(struct mgcp_parse_data *p)
{
OSMO_ASSERT(p);
struct mgcp_parse_sdp *sdp = &p->sdp;
struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
unsigned int codecs_used = 0;
struct sdp_fmtp_param fmtp_params[MGCP_MAX_CODECS];
@@ -331,19 +346,14 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
char *line;
unsigned int i;
void *tmp_ctx = talloc_new(NULL);
struct mgcp_rtp_end *rtp;
int payload_type;
int ptime, ptime2 = 0;
char audio_name[64];
int port, rc;
OSMO_ASSERT(endp);
OSMO_ASSERT(conn);
OSMO_ASSERT(p);
rtp = &conn->end;
memset(&codecs, 0, sizeof(codecs));
mgcp_parse_sdp_init(sdp);
for_each_line(line, p->save) {
switch (line[0]) {
@@ -361,19 +371,19 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
if (sscanf(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
if (ptime2 > 0 && ptime2 != ptime)
rtp->packet_duration_ms = 0;
sdp->ptime = 0;
else
rtp->packet_duration_ms = ptime;
sdp->ptime = ptime;
break;
}
if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
rtp->maximum_packet_time = ptime2;
sdp->maxptime = ptime2;
break;
}
if (strncmp("a=fmtp:", line, 6) == 0) {
rc = fmtp_from_sdp(conn->conn, &fmtp_params[fmtp_used], line);
rc = fmtp_from_sdp(tmp_ctx, &fmtp_params[fmtp_used], line);
if (rc >= 0)
fmtp_used++;
break;
@@ -382,33 +392,23 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
break;
case 'm':
rc = sscanf(line, "m=audio %d RTP/AVP", &port);
if (rc == 1) {
osmo_sockaddr_set_port(&rtp->addr.u.sa, port);
rtp->rtcp_port = htons(port + 1);
}
if (rc == 1)
sdp->rtp_port = port;
rc = pt_from_sdp(conn->conn, codecs,
ARRAY_SIZE(codecs), line);
rc = pt_from_sdp(tmp_ctx, codecs, ARRAY_SIZE(codecs), line);
if (rc > 0)
codecs_used = rc;
break;
case 'c':
if (audio_ip_from_sdp(&rtp->addr, line) < 0) {
if (audio_ip_from_sdp(&sdp->rem_addr, line) < 0) {
talloc_free(tmp_ctx);
return -1;
}
break;
default:
if (endp)
/* TODO: Check spec: We used the bare endpoint number before,
* now we use the endpoint name as a whole? Is this allowed? */
LOGP(DLMGCP, LOGL_NOTICE,
"Unhandled SDP option: '%c'/%d on %s\n",
line[0], line[0], endp->name);
else
LOGP(DLMGCP, LOGL_NOTICE,
"Unhandled SDP option: '%c'/%d\n",
line[0], line[0]);
LOGP(DLMGCP, LOGL_NOTICE,
"Unhandled SDP option: '%c'/%d on %s\n",
line[0], line[0], p->epname);
break;
}
}
@@ -422,23 +422,22 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
/* Store parsed codec information */
for (i = 0; i < codecs_used; i++) {
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);
rc = mgcp_codecset_add_codec(&sdp->cset, codecs[i].payload_type, codecs[i].map_line, codec_param);
if (rc < 0)
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "failed to add codec\n");
LOGP(DLMGCP, LOGL_NOTICE, "%s: failed to add codec\n", p->epname);
}
talloc_free(tmp_ctx);
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
"Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
osmo_sockaddr_port(&rtp->addr.u.sa), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf),
rtp->packet_duration_ms);
LOGP(DLMGCP, LOGL_NOTICE,
"%s: Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
p->epname, sdp->rtp_port, osmo_sockaddr_ntop(&sdp->rem_addr.u.sa, ipbuf), sdp->ptime);
if (codecs_used == 0)
LOGPC(DLMGCP, LOGL_NOTICE, "none");
for (i = 0; i < codecs_used; i++) {
LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
rtp->codecs[i].payload_type,
strlen(rtp->codecs[i].subtype_name) ? rtp->codecs[i].subtype_name : "unknown");
sdp->cset.codecs[i].payload_type,
strlen(sdp->cset.codecs[i].subtype_name) ? sdp->cset.codecs[i].subtype_name : "unknown");
LOGPC(DLMGCP, LOGL_NOTICE, " ");
}
LOGPC(DLMGCP, LOGL_NOTICE, "\n");
@@ -542,7 +541,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
OSMO_ASSERT(sdp);
OSMO_ASSERT(addr);
codec = conn->end.codec;
codec = conn->end.cset.codec;
audio_name = codec->audio_name;
payload_type = codec->payload_type;
@@ -578,7 +577,23 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
goto buffer_too_small;
}
if (codec->param_present) {
/* Include a=fmtp line in MGCP response only for AMR
* octet-align parameter, not for tw-ts-* parameters.
* Rationale:
*
* - tw-ts-* parameters exist meaningfully only for E1
* endpoints driven by OsmoBSC, not in any other
* context. libosmo-mgcp-client used by OsmoBSC
* completely ignores all fmtp lines, has no code
* to parse them at all.
*
* - The SDP we return is supposed to indicate what
* _we_ accept, conceptually independent from what
* the remote accepts. And we always accept TW-TS-001
* and TW-TS-002 RTP formats going to E1 DL, whether
* or not we send them in UL per client request.
*/
if (codec->param_present && codec->param.amr_octet_aligned_present) {
fmtp_param.payload_type = payload_type;
fmtp_param.param = codec->param;
fmtp_params[0] = fmtp_param;

View File

@@ -141,7 +141,7 @@ void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn)
* keep this option open: */
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
mgcp_format_stats_rtp(str, str_len, &conn->u.rtp);
mgcp_format_stats_rtp(str, str_len, mgcp_conn_get_conn_rtp(conn));
break;
default:
break;

View File

@@ -306,3 +306,43 @@ struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigne
return NULL;
}
/* Try to find a free port by attempting to bind on it. Also handle the
* counter that points on the next free port. Since we have a pointer
* to the next free port, binding should in work on the first attempt in
* general. In case of failure the next port is tried until the whole port
* range is tried once. */
int mgcp_trunk_allocate_conn_rtp_ports(struct mgcp_trunk *trunk, struct mgcp_conn_rtp *conn_rtp)
{
int i;
struct mgcp_port_range *range;
unsigned int tries;
OSMO_ASSERT(trunk);
OSMO_ASSERT(conn_rtp);
range = &trunk->cfg->net_ports;
pthread_mutex_lock(&range->lock);
/* attempt to find a port */
tries = (range->range_end - range->range_start) / 2;
for (i = 0; i < tries; ++i) {
int rc;
if (range->last_port >= range->range_end)
range->last_port = range->range_start;
rc = mgcp_conn_rtp_bind_rtp_ports(conn_rtp, range->last_port);
range->last_port += 2;
if (rc == 0) {
pthread_mutex_unlock(&range->lock);
return 0;
}
}
pthread_mutex_unlock(&range->lock);
LOGPCONN(conn_rtp->conn, DLMGCP, LOGL_ERROR,
"Allocating a RTP/RTCP port failed %u times.\n", tries);
return -1;
}

View File

@@ -165,7 +165,7 @@ 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 mgcp_rtp_codec *codec = end->cset.codec;
struct rate_ctr *tx_packets, *tx_bytes;
struct rate_ctr *rx_packets, *rx_bytes;
struct rate_ctr *dropped_packets;
@@ -254,7 +254,8 @@ static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp,
* connection types (E1) as soon as
* the implementation is available */
if (conn->type == MGCP_CONN_TYPE_RTP) {
dump_rtp_end(vty, &conn->u.rtp);
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
dump_rtp_end(vty, conn_rtp);
}
}
}
@@ -856,7 +857,7 @@ DEFUN_USRATTR(cfg_mgcp_patch_rtp_ssrc,
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
OSMO_ASSERT(trunk);
trunk->force_constant_ssrc = 1;
trunk->force_constant_ssrc = true;
return CMD_SUCCESS;
}
@@ -867,7 +868,7 @@ DEFUN_USRATTR(cfg_mgcp_no_patch_rtp_ssrc,
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
OSMO_ASSERT(trunk);
trunk->force_constant_ssrc = 0;
trunk->force_constant_ssrc = false;
return CMD_SUCCESS;
}
@@ -922,7 +923,7 @@ DEFUN_USRATTR(cfg_mgcp_no_patch_rtp,
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
OSMO_ASSERT(trunk);
trunk->force_constant_ssrc = 0;
trunk->force_constant_ssrc = false;
trunk->force_aligned_timing = 0;
trunk->rfc5993_hr_convert = false;
return CMD_SUCCESS;
@@ -1196,7 +1197,7 @@ DEFUN_USRATTR(cfg_trunk_patch_rtp_ssrc,
"rtp-patch ssrc", RTP_PATCH_STR "Force a fixed SSRC\n")
{
struct mgcp_trunk *trunk = vty->index;
trunk->force_constant_ssrc = 1;
trunk->force_constant_ssrc = true;
return CMD_SUCCESS;
}
@@ -1206,7 +1207,7 @@ DEFUN_USRATTR(cfg_trunk_no_patch_rtp_ssrc,
"no rtp-patch ssrc", NO_STR RTP_PATCH_STR "Force a fixed SSRC\n")
{
struct mgcp_trunk *trunk = vty->index;
trunk->force_constant_ssrc = 0;
trunk->force_constant_ssrc = false;
return CMD_SUCCESS;
}
@@ -1256,7 +1257,7 @@ DEFUN_USRATTR(cfg_trunk_no_patch_rtp,
"no rtp-patch", NO_STR RTP_PATCH_STR)
{
struct mgcp_trunk *trunk = vty->index;
trunk->force_constant_ssrc = 0;
trunk->force_constant_ssrc = false;
trunk->force_aligned_timing = 0;
trunk->rfc5993_hr_convert = false;
return CMD_SUCCESS;
@@ -1357,10 +1358,11 @@ DEFUN(loop_conn,
endp = trunk->endpoints[endp_no];
int loop = atoi(argv[2]);
llist_for_each_entry(conn, &endp->conns, entry) {
if (conn->type == MGCP_CONN_TYPE_RTP)
if (conn->type == MGCP_CONN_TYPE_RTP) {
/* Handle it like a MDCX, switch on SSRC patching if enabled */
mgcp_rtp_end_config(endp, 1, &conn->u.rtp.end);
else {
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
conn_rtp->state.patch.patch_ssrc = true;
} else {
/* FIXME: Introduce support for other connection (E1)
* types when implementation is available */
vty_out(vty, "%%Can't enable SSRC patching,"
@@ -1418,7 +1420,7 @@ DEFUN(tap_rtp,
endp = trunk->endpoints[endp_no];
conn_id = argv[2];
conn = mgcp_conn_get_rtp(endp, conn_id);
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
if (!conn) {
vty_out(vty, "Conn ID %s is invalid.%s",
conn_id, VTY_NEWLINE);

View File

@@ -27,6 +27,7 @@ osmo_mgw_SOURCES = \
osmo_mgw_LDADD = \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCODEC_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCTRL_LIBS) \

View File

@@ -249,17 +249,6 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
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) {
@@ -269,7 +258,7 @@ int mgcp_vty_go_parent(struct vty *vty)
break;
case MGCP_NODE:
default:
if (mgcp_vty_is_config_node(vty, vty->node))
if (vty->node != CONFIG_NODE)
vty->node = CONFIG_NODE;
else
vty->node = ENABLE_NODE;
@@ -310,7 +299,6 @@ static struct vty_app_info vty_info = {
.name = "OsmoMGW",
.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[] = {

View File

@@ -36,6 +36,7 @@ mgcp_test_SOURCES = \
mgcp_test_LDADD = \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCODEC_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \

View File

@@ -43,6 +43,8 @@
#include <math.h>
#include <ctype.h>
void *tall_mgw_ctx;
char *strline_r(char *str, char **saveptr);
const char *strline_test_data =
@@ -965,7 +967,7 @@ static void test_messages(void)
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
OSMO_ASSERT(endp);
conn = mgcp_conn_get_rtp(endp, "1");
conn = mgcp_endp_get_conn_rtp(endp, "1");
if (conn) {
OSMO_ASSERT(conn);
@@ -1023,14 +1025,14 @@ static void test_messages(void)
fprintf(stderr, "endpoint:%s: "
"payload type %d (expected %d)\n",
last_endpoint,
conn->end.codec->payload_type, t->ptype);
conn->end.cset.codec->payload_type, t->ptype);
if (t->ptype != PTYPE_IGNORE)
OSMO_ASSERT(conn->end.codec->payload_type ==
OSMO_ASSERT(conn->end.cset.codec->payload_type ==
t->ptype);
/* Reset them again for next test */
conn->end.codec->payload_type = PTYPE_NONE;
conn->end.cset.codec->payload_type = PTYPE_NONE;
}
}
@@ -1209,7 +1211,7 @@ static void test_packet_loss_calc(void)
mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
"test-connection");
OSMO_ASSERT(_conn);
conn = mgcp_conn_get_rtp(&endp, _conn->id);
conn = mgcp_endp_get_conn_rtp(&endp, _conn->id);
OSMO_ASSERT(conn);
state = &conn->state;
packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
@@ -1230,7 +1232,7 @@ static void test_packet_loss_calc(void)
pl_test_dat[i].expected);
}
mgcp_conn_free_all(&endp);
mgcp_endp_free_conn_all(&endp);
}
talloc_free(trunk);
@@ -1461,13 +1463,13 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
_conn = mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
"test-connection");
OSMO_ASSERT(_conn);
conn = mgcp_conn_get_rtp(&endp, _conn->id);
conn = mgcp_endp_get_conn_rtp(&endp, _conn->id);
OSMO_ASSERT(conn);
rtp = &conn->end;
OSMO_ASSERT(mgcp_codec_add(conn, PTYPE_UNDEFINED, "AMR/8000/1", NULL) == 0);
rtp->codec = &rtp->codecs[0];
OSMO_ASSERT(mgcp_codecset_add_codec(&conn->end.cset, PTYPE_UNDEFINED, "AMR/8000/1", NULL) == 0);
rtp->cset.codec = &rtp->cset.codecs[0];
for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
struct rtp_packet_info *info = test_rtp_packets1 + i;
@@ -1479,7 +1481,6 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
OSMO_ASSERT(info->len >= 0);
msg->l3h = msgb_put(msg, info->len);
memcpy((char*)msgb_l3(msg), info->data, info->len);
mgcp_rtp_end_config(&endp, 1, rtp);
mgcp_patch_and_count(&endp, &state, rtp, &addr, msg);
@@ -1513,7 +1514,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
}
force_monotonic_time_us = -1;
mgcp_conn_free_all(&endp);
mgcp_endp_free_conn_all(&endp);
talloc_free(trunk);
talloc_free(cfg);
}
@@ -1548,9 +1549,9 @@ static void test_multilple_codec(void)
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/1@mgw") == 0);
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
OSMO_ASSERT(endp);
conn = mgcp_conn_get_rtp(endp, conn_id);
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 18);
OSMO_ASSERT(conn->end.cset.codec->payload_type == 18);
/* Allocate 2@mgw with three codecs, last one ignored */
last_endpoint[0] = '\0';
@@ -1564,9 +1565,9 @@ static void test_multilple_codec(void)
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/2@mgw") == 0);
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
OSMO_ASSERT(endp);
conn = mgcp_conn_get_rtp(endp, conn_id);
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 18);
OSMO_ASSERT(conn->end.cset.codec->payload_type == 18);
/* Allocate 3@mgw with no codecs, check for PT == 0 */
/* Note: It usually makes no sense to leave the payload type list
@@ -1585,9 +1586,9 @@ static void test_multilple_codec(void)
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/3@mgw") == 0);
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
OSMO_ASSERT(endp);
conn = mgcp_conn_get_rtp(endp, conn_id);
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 0);
OSMO_ASSERT(conn->end.cset.codec->payload_type == 0);
/* Allocate 4@mgw with a single codec */
last_endpoint[0] = '\0';
@@ -1601,9 +1602,9 @@ static void test_multilple_codec(void)
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/4@mgw") == 0);
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
OSMO_ASSERT(endp);
conn = mgcp_conn_get_rtp(endp, conn_id);
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 18);
OSMO_ASSERT(conn->end.cset.codec->payload_type == 18);
/* Allocate 5@mgw and let osmo-mgw pick a codec from the list */
last_endpoint[0] = '\0';
@@ -1617,9 +1618,9 @@ static void test_multilple_codec(void)
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
OSMO_ASSERT(endp);
conn = mgcp_conn_get_rtp(endp, conn_id);
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 0);
OSMO_ASSERT(conn->end.cset.codec->payload_type == 0);
inp = create_msg(MDCX_NAT_DUMMY, conn_id);
last_endpoint[0] = '\0';
@@ -1629,9 +1630,9 @@ static void test_multilple_codec(void)
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
OSMO_ASSERT(endp);
conn = mgcp_conn_get_rtp(endp, conn_id);
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 3);
OSMO_ASSERT(conn->end.cset.codec->payload_type == 3);
OSMO_ASSERT(osmo_sockaddr_port(&conn->end.addr.u.sa) == 16434);
memset(&addr, 0, sizeof(addr));
inet_aton("8.8.8.8", &addr);
@@ -1646,7 +1647,7 @@ static void test_multilple_codec(void)
talloc_free(endp->last_response);
talloc_free(endp->last_trans);
endp->last_response = endp->last_trans = NULL;
conn = mgcp_conn_get_rtp(endp, conn_id);
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
OSMO_ASSERT(!conn);
last_endpoint[0] = '\0';
@@ -1660,9 +1661,9 @@ static void test_multilple_codec(void)
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
OSMO_ASSERT(endp);
conn = mgcp_conn_get_rtp(endp, conn_id);
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 0);
OSMO_ASSERT(conn->end.cset.codec->payload_type == 0);
mgcp_endpoints_release(trunk);
talloc_free(cfg);
@@ -1689,7 +1690,7 @@ static void test_no_cycle(void)
_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP,
"test-connection");
OSMO_ASSERT(_conn);
conn = mgcp_conn_get_rtp(endp, _conn->id);
conn = mgcp_endp_get_conn_rtp(endp, _conn->id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->state.stats.initialized == 0);
@@ -2195,7 +2196,7 @@ static bool codec_decision(struct mgcp_conn_rtp *conn, unsigned int index_conn_s
int payload_type_conn_dst;
printf(" - mgcp_codec_decide(&conn[%u], &conn[%u]):\n", index_conn_src, index_conn_dst);
if (mgcp_codec_decide(&conn[index_conn_src], &conn[index_conn_dst]) != 0) {
if (mgcp_codecset_decide(&conn[index_conn_src].end.cset, &conn[index_conn_dst].end.cset) != 0) {
if (expect->payload_type_map[index_conn_src] == -EINVAL
&& expect->payload_type_map[index_conn_dst] == -EINVAL)
printf(" codec decision failed (expected)!\n");
@@ -2205,19 +2206,19 @@ static bool codec_decision(struct mgcp_conn_rtp *conn, unsigned int index_conn_s
}
} else {
printf(" Codec decision result:\n");
if (conn[index_conn_src].end.codec) {
payload_type_conn_src = conn[index_conn_src].end.codec->payload_type;
if (conn[index_conn_src].end.cset.codec) {
payload_type_conn_src = conn[index_conn_src].end.cset.codec->payload_type;
printf(" conn[%u]: codec:%s, pt:%d\n",
index_conn_src, conn[index_conn_src].end.codec->subtype_name, payload_type_conn_src);
index_conn_src, conn[index_conn_src].end.cset.codec->subtype_name, payload_type_conn_src);
} else {
payload_type_conn_src = -EINVAL;
printf(" conn[%u]: codec:none, pt:none\n", index_conn_src);
}
if (conn[index_conn_dst].end.codec) {
payload_type_conn_dst = conn[index_conn_dst].end.codec->payload_type;
if (conn[index_conn_dst].end.cset.codec) {
payload_type_conn_dst = conn[index_conn_dst].end.cset.codec->payload_type;
printf(" conn[%u]: codec:%s, pt:%d\n",
index_conn_dst, conn[index_conn_dst].end.codec->subtype_name,
index_conn_dst, conn[index_conn_dst].end.cset.codec->subtype_name,
payload_type_conn_dst);
} else {
payload_type_conn_dst = -EINVAL;
@@ -2265,8 +2266,8 @@ static void test_mgcp_codec_decide(void)
if (!codec->audio_name)
break;
rc = mgcp_codec_add(&conn[conn_i], codec->payload_type, codec->audio_name,
codec->param);
rc = mgcp_codecset_add_codec(&conn[conn_i].end.cset, codec->payload_type,
codec->audio_name, codec->param);
printf(" %2d: %3d %s%s -> rc=%d\n", c, codec->payload_type, codec->audio_name,
codec->param ?
@@ -2324,7 +2325,7 @@ void test_conn_id_matching(void)
INIT_LLIST_HEAD(&endp.conns);
conn = talloc_zero(NULL, struct mgcp_conn);
conn = talloc_zero(tall_mgw_ctx, struct mgcp_conn);
OSMO_ASSERT(conn);
osmo_strlcpy(conn->id, conn_id_generated, sizeof(conn->id));
llist_add(&conn->entry, &endp.conns);
@@ -2332,7 +2333,7 @@ void test_conn_id_matching(void)
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);
conn_match = mgcp_endp_get_conn(&endp, needle);
OSMO_ASSERT(conn_match);
printf("found '%s'\n", conn_match->id);
OSMO_ASSERT(conn_match == conn);