Compare commits

..

79 Commits

Author SHA1 Message Date
Neels Hofmeyr
2c42f44d7e add libosmo-sdp: add sdp_msg.h,.c
Change-Id: Id53c2c6eea3726f63a1399ae985f8aa3344e32c8
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
53877bfb8e add libosmo-sdp: add codec_list_is_empty()
Change-Id: I5e80f5737f22dc81ac16448fc59937330d24432e
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
aca4ad4cce add libosmo-sdp: add codec_list_by_payload_type
Change-Id: I7a09b0e669d787a1ac0ec7ed7541c107fd0f32db
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
46c3882f75 add libosmo-sdp: add codec_list_intersection
Change-Id: Ie57e4384854a1152dccf9577fdb5f046a1f17e0f
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
5f8bad4b21 add libosmo-sdp: codec_list_cmp()
Change-Id: I4a2087c3c03ef11df03c00a14953b0d0b6f977f3
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
35357e80ee add libosmo-sdp: codec_list_first()
Change-Id: I43653820d8b4d07bf5d7ed25b6172fec26eb2cdf
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
6c09836faa add libosmo-sdp: codec_list_to_str()
Change-Id: I8a421ff8280db3f1ec399832f1a86c515a0888af
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
c43d76dc4d add libosmo-sdp: osmo_sdp_codec_list.h,c, first part
Change-Id: If170566c666c4f4010091bc90912b13a12f77de8
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
0051a29df9 add libosmo-sdp: sdp_codec.h,c
The primary user is osmo-msc. This API's predecessor currently lives in
osmo-msc.git and is now being moved here, in a matured form.

Besides osmo-msc, any MGCP client program may need to handle SDP codecs.

(Besides clients, osmo-mgw itself may use this SDP implementation to
untangle parsing from the MGCP server code, in some distant future.)

Change-Id: I2cec2667796dae91441ad9357d11854239880aea
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
ef5f1d3d7b add libosmo-sdp: fmtp_amr_match()
Change-Id: I0fb77c022c4fb195ba45631dda9cbd2215c02510
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
c74c3d02c8 add libosmo-sdp [1/n] add fmtp.h, sdp_strings.h
Add new noinst-library libosmo-sdp.la.

It will be used by
- osmo-mgw
- libosmo-mgcp-client
- callers of libosmo-mgcp-client may use SDP message parsing (osmo-msc)

The API will, with upcoming patches:
- parse and compose SDP fmtp strings
- manage SDP codecs and codec lists (like 'AMR:octet-align=1#112')
- parse and compose entire SDP messages

In this patch: parse and compose SDP fmtp strings.

Add libosmo-sdp build fu.
Add sdp/fmtp.h and sdp/sdp_strings.h.
Add tests/sdp/sdp_fmtp_test.c.

Change-Id: I6128852f4d249e90319f43d6cd6ed0a9a2ed0430
2025-01-14 14:01:31 +01:00
Neels Hofmeyr
7338fd833b make libosmo-mgcp.a an .la lib
.la libs aka LTLIB can include library dependencies.

Rationale: An upcoming patch adds noinst libosmo-sdp, used by both
osmo-mgw and libosmo-mgcp-client, and the cleanest way to link that is
via a library dependency. Prepare for that.

Change-Id: I5302d56e571d0da9061c275ce13d4f085c044671
2025-01-14 14:01:31 +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
Oliver Smith
9c3fad2c42 Bump version: 1.13.0.3-227a → 1.13.1
Change-Id: I8ebbaf6bff440cb233088f4bc226b80db0fd3922
2024-09-12 13:54:42 +02:00
Pau Espin Pedrol
227a800d26 tests/mgcp/mgcp_test: Add some extra asserts in code
This makes it easier to catch possible failures while running the test,
plus makes gcc on OpenSUSE_Factory happy and not warn.

Related: OS#5044
Change-Id: I01c3c84e58a11055f0e0a6955016dc2489c73379
2024-08-20 12:02:02 +02:00
Philipp Maier
24cae67f3e mgcp_network: add missing ntohs
We must use ntohs before passing the RTCP port to osmo_sockaddr_port

Related: OS#6527
Change-Id: I9773c7b1fda8c9b429723ef9f0db0b58894b28fe
2024-08-07 09:48:45 +02:00
Philipp Maier
0d47c408ac mgcp_network: use an uint16_t to store the port number
The comment in struct mgcp_rtp_end says that the RTCP port number has to
be stored in network byte order. Such a port number is 16 bit long, so
let's use an uint16_t instead of an int here.

Related: OS#6527
Change-Id: I392b07b243389a78d4ad1d784cdfdc28ec59b487
2024-08-07 09:47:42 +02:00
Oliver Smith
f1b557988d Bump version: 1.12.1.58-95e50-dirty → 1.13.0
Change-Id: I46cabfda147bf700fb29a0b8eecf54560a1d378d
2024-07-24 16:23:15 +02:00
Mychaela N. Falconia
95e504aa97 fix E1 TS output when used with osmo-e1d
The original code did E1 raw TS output by posting directly to
&ts->raw.tx_queue instead of calling e1inp_ts_send_raw();
doing so bypasses the call to driver->want_write performed in
e1inp layer.  This approach worked for DAHDI where no
select-for-write is used; however, e1inp interface to osmo-e1d
does use select-for-write, hence applications like osmo-mgw
do need to use e1inp_ts_send_raw() API in order to work
correctly.

Change-Id: I6ce9a1dea6834632faf75059e85ca9a0c25d57c2
2024-07-23 14:24:18 +00:00
Neels Hofmeyr
a5ae091fdd do not FAIL on CRCX in sendrecv mode
Currently, a CRCX in sendrecv mode results in:

  DLMGCP ERROR endpoint:rtpbridge/2@mgw CI:7F4C8EDD CRCX: selected connection mode type requires an opposite end! (mgcp_protocol.c:1090)

But it is not actually practical, nor logical to require another conn
for the 'sendrecv' ConnectionMode.

Impractical: If I want to create two conns on an endpoint that both are
in 'sendrecv' mode, I have to send two CRCX, one for each conn. At the
time of the first CRCX, there cannot be any other conn, so I am
currently forced to send a different ConnectionMode in the CRCX,
followed by another MDCX later, just to change the first conn to
sendrecv.

Illogical: In a situation where two conns are currently in sendrecv
mode, if I now DLCX one of them, I can legally reach a state with a
single conn in sendrecv mode, just as currently forbidden for CRCX.

In general, MGCP is not forcing any particular number of connections.

Simply start forwarding RTP as soon as there is a remote conn, and not
require another explicit MDCX.

Related: SYS#6974 SYS#6907
Related: osmo-ttcn3-hacks I00fd854f058f7f53e2f579e8481ca2b9253f08e3
Change-Id: Ic089485543c5c97a35c7ae24fe0f622bf57d1976
2024-07-02 01:54:45 +00:00
Neels Hofmeyr
249f799a88 mgcp_test: add CRCX for IUFP in sendrecv
Note, there are also ttcn3 tests sending the same to osmo-mgw being
submitted along with these patches, and the ttcn3 tests currently fail:
osmo-mgw rejects the connection mode "sendrecv" in the first CRCX.

Change-Id: Icdb1085b8e44ac4cff6c457163349b81e81bf765
2024-07-02 01:54:45 +00:00
Neels Hofmeyr
dc395843ff mgcp_test: test a=ptime:20, not 40
In our GSM world, the packet time is 20 ms, hence 20 is the typical
value that we should test. Maybe we can do one test with 40 at most, but
we'll most probably never see anything else than 20 in practice.

Change-Id: Ia2292198bf6e3b72912afd69607654ca77fd549d
2024-07-02 01:54:45 +00:00
Neels Hofmeyr
c5d003e09e mgcp_test.c: fix various missing '\r' and '\n'
The only valid line endings are '\n' and '\r\n'.
We usually use '\r\n' like we were in MSDOS.

MGCP, RFC3435 3.1:

   Headers and session descriptions are encoded as a set of text lines,
   separated by a carriage return and line feed character (or,
   optionally, a single line-feed character).

SDP, RFC8866 5:

   The sequence CRLF (0x0d0a) is used to end a line,
   although parsers SHOULD be tolerant and also accept lines terminated
   with a single newline character.

There should probably be tests for '\n' line endings, but mixing them in
the same MGCP message is ridiculous.

Change-Id: I6d530535a3a5f1d1a0716ab9e4a8079ba1de242e
2024-07-02 01:54:45 +00:00
Mychaela N. Falconia
e6398d886b E1: support HRv1 codec on both 16k and 8k subslots
HRv1 support in OsmoMGW-E1 was previously broken (couldn't work
on either 16k or 8k subslots) because of inconsistency: the TRAU
frame type was set to OSMO_TRAU16_FT_HR, but the TRAU sync pattern
was set to OSMO_TRAU_SYNCP_8_HR.  However, now that libosmotrau
has proper support for HRv1 TRAU frame encoding and RTP conversion
in both 16k and 8k formats, drive it correctly in OsmoMGW-E1.

While at it, change the code structure to avoid else-after-return.
Was the original code written and merged in a time before strict
linter checks?

Change-Id: Ifadbdc68905178c6ffdd673a6fb71c18610c9847
2024-06-26 17:45:49 +00:00
Neels Hofmeyr
23140d6f94 mgcp_test.c: verify osmo-mgw accepts m=audio 0
When osmo-hnbgw does not yet know the remote port, it wants to send a
CRCX to set up IuUP at the MGW, with the audio port set to zero.
Make sure osmo-mgw accepts port == 0.

Related: SYS#6907 SYS#6974
Change-Id: I42011c2e7256d372a37b6a2fe86af0153038e2d0
2024-06-14 16:14:41 +02:00
Neels Hofmeyr
36c823eef1 mgcp-client: always send 'm=audio' line
Re-add the m=audio line to SDP emitted from libosmo-mgcp-client, even if
the audio port is not set yet

Patch a5acaa68db introduced a presence
flag for the RTP audio port number. This flag, when unset, also omitted
the 'm=audio...' line completely, dropping the PT number definitions.

Correct:

  m=audio 1234 RTP/AVP 96             <--- anounce 96
  a=rtpmap:96 VND.3GPP.IUFP/16000     <--- further specify 96
  a=fmtp:96 ...                       <--- further specify 96

When m=audio is missing, we only have orphaned rtpmap and fmtp entries.
They are supposed to further specify the 96 listed in 'RTP/AVP 96',
instead they are without context:

  a=rtpmap:96 VND.3GPP.IUFP/16000
  a=fmtp:96 ...

When the presence map indicates no port known, we still need to emit the
list of PT numbers; so we're forced to send a port of 0:

  m=audio 0 RTP/AVP 96
  a=rtpmap:96 VND.3GPP.IUFP/16000
  a=fmtp:96 ...

This is an important fix for osmo-hnbgw, which sends the first CRCX with
an IUFP codec, at a time when the remote port is not yet known. osmo-mgw
requires an m=audio line to accept incoming IuUP Initializaition
requests. When m=audio is missing, osmo-mgw does not parse the IUFP
codec, and 3G voice fails completely. This mgcp-client patch will emit
valid codec config also when port == 0, fixing osmo-hnbgw voice, because
osmo-mgw will know about IUFP from the start.

Related: SYS#6907 SYS#6974
Related: osmo-mgw a5acaa68db
Change-Id: Id95b629453aec999100b5af821c6a0b9562bb597
2024-06-14 16:14:25 +02:00
Oliver Smith
fa393fa1e9 doc: example configs: fix deprecation warnings
Fix for:
  range must end at an odd port number, autocorrecting port (16000) to: 16001
  % Deprecated 'sdp audio-payload number <0-255>' config no longer has any effect
  % Deprecated 'sdp audio-payload name NAME' config no longer has any effect
  % Deprecated 'loop (0|1)' config no longer has any effect

Change-Id: I62a4fd119de48039c3c450d5323d8f9b7de8120f
2024-05-15 11:58:52 +00:00
Oliver Smith
e0fc37a437 contrib/systemd: run as osmocom user
I have verified that with AmbientCapabilities=CAP_SYS_NICE, setting
scheduling policy as described in the manual still works as expected.

Related: OS#4107
Change-Id: Ibb83c231231b39dc6732c0f375aeb3b21f3938ef
2024-05-14 15:25:21 +02:00
Oliver Smith
4aa2b0e35b contrib: remove rpm spec file
Related: https://osmocom.org/news/255
Related: OS#6446
Change-Id: I703e115a426ac1012c80d2e1576ee6dcfbe191a5
2024-05-08 14:41:07 +02:00
Pau Espin Pedrol
e01f6e7f33 iuup: Increment RTP hdr seqnr even if Tx over UDP fails
This way holes can be detected. In practice it's not much important
since it would be really strange that UDP fails for a while and then it
starts working out of the blue...

Related: SYS#6907
Change-Id: I8095f3505c859650c0b83abce405067bef745975
2024-04-24 18:57:02 +02:00
Pau Espin Pedrol
388b48d14e Fix IuUP RTP hdr seqnr field not incremented
Previous commit add osmo_io changed mgcp_udp_send() implementation from
"return sendto()", which is documented as:
"return the number of bytes sent. Otherwise, -1 shall be returned"

to "return mgcp_udp_send_msg()", which in turn calls
"return osmo_iofd_sendto_msgb()", and which is documented as:
"\returns 0 in case of success (takes msgb ownership), -1 on error
(doesn't take msgb ownership)."

So successful return code changed from >0 (bytes sent) to ==0,
but forgot to update mgcp_send_iuup() return code path check (and also
some related function documentation calling mgcp_udp_send()".

This commit fixes all the related aspects of that return code change.

Related: SYS#6907
Fixes: 352b967d1b
Change-Id: I154e1e41cd02fd4d9b88ad98fc7c4d657246c589
2024-04-24 18:56:57 +02:00
Harald Welte
352b967d1b Convert RTP/RTCP/OSMUX I/O from osmo_fd to osmo_io
Converting from osmo_fd to osmo_io allows us to switch to the new
io_uring backend and benefit from related performance benefits.

In a benchmark running 200 concurrent bi-directional voice calls with
GSM-EFR codec, I am observing:

* the code before this patch uses 40..42% of a single core on a
  Ryzen 5950X at 200 calls (=> 200 endpoints with each two connections)

* no increase in CPU utilization before/after this patch, i.e. the
  osmo_io overhead for the osmo_fd backend is insignificant compared
  to the direct osmo_fd mode before

* an almost exactly 50% reduction of CPU utilization when running the
  same osmo-mgw build with LIBOSMO_IO_BACKEND=IO_URING - top shows
  19..21% for the same workload instead of 40..42% with the OSMO_FD
  default backend.

* An increase of about 4 Megabytes in both RSS and VIRT size when
  enabling the OSMO_IO backend.  This is likely the memory-mapped rings.

No memory leakage is observed when using either of the backends.

Change-Id: I8471960d5d8088a70cf105f2f40dfa5d5458169a
2024-04-02 19:40:03 +02:00
Harald Welte
ed4da25f2f cosmetic: make linter happy
Change-Id: Iec8404061588b848f9e597bf4112d6df9597de95
2024-03-20 14:25:49 +01:00
Harald Welte
5abda312ed Change msgb ownership in processing of received msgb
The old approach was: rtp_data_net() reads a msgb from the incomging
socket, calls through whatever function chain and in the end free's it.
So none of the intermediate functions was permitted to take msgb
ownership.

This was a good choice as all processing would happen synchronously,
up to the point where that msgb was written on the output RTP socket.

Let's change this from passing msgb ownership throug the whole call
chain, through rx_rtp() to the various *_dispatch_rtp() functions.

This is required for upcoming migration to osmo_io, as in that case the
write (sendto) calls are asynchronous and hence msgb ownership needs
to be transferred.

Change-Id: I6a331f3c6b2eb51ea312ac6ef8c357185ddb79cf
2024-03-20 14:25:49 +01:00
Harald Welte
75862d3131 remove osmo_fd from mgcp_create_bind()
preparation for osmo_io

Change-Id: I4a3b66a14fdfbc867daca0f0a05f694d5e0d7b66
2024-03-20 14:25:49 +01:00
Harald Welte
8733542b13 simplify unused transcoding/processing call-back
the processing call-back is working with a raw buffer + length,
while we actually work with struct msgb.  Let's simply pass the msgb
into the call-back, and the call-back can then do what they want with
the contents of that msgb.

Change-Id: I002624f9008726e3d754d48aa2282c38e3b42953
2024-03-20 14:25:43 +01:00
Harald Welte
d7aac20cc1 remove strange loop for non-existant transcoding support
The existing support preparing the mgw for transcoding (which doesn't exist)
has some kind of method where the transcoding function might be called
multiple times in a row.  However, as it is not used, it is not entirely
clear how it was intended to work.  Let's remove this unused looping
feature which makes it hard to understand how upcoming osmo_io should
deal with it.

Change-Id: Ie1a629fd31c5ab806fc929d1e6b279c4be5b8246
2024-03-20 13:25:22 +00:00
Harald Welte
8b5361412c don't log useless "transcoding disabled" message
The entire mgw has no transcoding support.  So printing that message is
useless to begin with.  And printing it for *every RTP packet* is even
more useless.  Let's remove it.

Change-Id: If0ee2607404afc3a00665a5cf22a9e0eb62eb476
2024-03-20 12:34:38 +00:00
Harald Welte
3a971ba0d1 mgw: Add our usual SIGABRT, SIGUSR1 signal handlers
This is mostly related to talloc reports.

Change-Id: Idc35444d2b8a0bc52c267b468dfa3c1b59f9187a
2024-03-19 18:16:58 +01:00
Neels Hofmeyr
c4b90354ca mgw: do not fail MGCP on codec mismatch
Before this patch, when an CRCX+MDCX wants to set a codec list that has
no match with the codecs for the other conn of that same endpoint,
osmo-mgw returns an MGCP "FAIL" response.

When a client wants to change the codec, it has to do that one RTP port
at a time. So osmo-mgw *must* allow to configure an MGCP conn with a
codec choice that mismatches the other conn.

This is crucial to allow codec negotiation in osmo-msc: if MO has
already assigned a specific codec, and later wants to re-assign to the
codec that MT has chosen, the codec needs to be changed at osmo-mgw.

This patch is the minimal fix required to get re-assignment to a
different codec to work (via osmo-msc). There is more work to be done
about this bit of code in osmo-mgw, but keep that to a separate patch.

In detail, before this patch, we fail both
- when a side has no codecs,
- or when there is no single match between codecs of the two sides of
  the endpoint.
Remove only the second condition; after this patch, still fail when a
side has no codecs -- this allows mgcp_test.c to still pass.

Related: OS#6293
Related: osmo-msc I8760feaa8598047369ef8c3ab2673013bac8ac8a
Change-Id: I3d1163fe622bdd7dc42a485f796072524ab39db9
2024-03-19 03:38:38 +00:00
Harald Welte
28fd236044 migrate mgcp_client from osmo_wqueue to osmo_io
The new osmo_io framework means that we can [optionally] make use
of the io_uring backend, which greatly reduces the syscall load
compared to the legacy osmo_wqueue + osmo_select_main + read/write.

We only use features already present in the intiial osmo_io support
of libosmocore 1.9.0, so no entry in TODO-RELEASE is needed.

Closes: OS#5754
Related: OS#5755
Change-Id: I766224da4691695c023d4d08d042a4bbeba05e47
2024-03-07 19:44:59 +01:00
Neels Hofmeyr
17b5701f19 mgcp_test: fix false negatives in test output
If one test fails, do not print failure for all following tests as well.

Change-Id: I196880b4b34a672ef45042c25f89bc1684363567
2024-02-06 03:17:50 +01:00
Neels Hofmeyr
9bea6eb78c tweak DEBUG log
Printing the debug log line a little later will include the MGCP verb
information.

Change-Id: Icb230cf4d623cdbc4ab52bd52d2a72525c0168c7
2024-02-06 03:17:50 +01:00
Neels Hofmeyr
fd57bd5f6f mgcp_codec_decide: remove redundant lookup
We already did a lookup from conn_src[i] and found a matching
codec_conn_dst, no need to do another reverse lookup to end up at the
same conn_src[i] codec.

Change-Id: Iecc7f22c551fd17b23db434fdb177266407d2621
2024-02-06 03:14:56 +01:00
Neels Hofmeyr
d0dbda4106 drop cfg 'sdp audio fmtp-extra'
There is considerable code complexity in place for this ancient hack.

It dates back to 5ea1bc77a3
"
mgcp: Allow to freely control the a=fmtp line for experiments

In case of AMR one can specify the available codecs out-of-band. Allow
to configure this line statically in the configuration file.
"

Looking in mgcp_test.c output, the fmtp-extra tests do not even make
sense: they result in fmtp for pt=126 being added, even though there is
no payload type 126 listed in the SDP...

Related: OS#6313
Change-Id: Icee0cd1f5a751fa760d5a9deca29089e78e7eb93
2024-02-06 03:04:10 +01:00
Neels Hofmeyr
35a738214c drop get_net_downlink_format_cb
It seems to be a remnant from early openbsc_mgcp. There is only the
default implementation for this callback and it simply returns two
pointers. Simplify that.

Change-Id: I18dfd44c931540caf4ac360c08ed10e5f65b2165
2024-02-06 03:00:50 +01:00
Neels Hofmeyr
afbe872045 tests/mgcp: add update_exp target
Change-Id: I1ea7e881fe13429762bf31507d8d23fe58e241b3
2024-02-06 03:00:50 +01:00
Harald Welte
909be19285 Convert README to README.md and expand like in other projects
Let's align with structure and content of README.md in other osmo-*
projects.

Change-Id: Id9a78ccc0d10f0e3a9d832ac4c4988cfbb8ddb3c
2024-01-29 08:17:09 +01:00
Vadim Yanitskiy
8ee08c6b78 build: include README into the release tarball
Change-Id: I4e302914340467e8c047d60ab862267f4e94acad
2024-01-26 23:35:20 +07:00
neels
c053e07336 Revert "drop (now) unused code"
This reverts commit 2b30cbdfa8.

Reason for revert: Older versions of osmo-msc were actually calling map_codec_to_pt().

Change-Id: Ifff31012b327d40ed0b1559d5cf4f320784a4061
Related: https://jenkins.osmocom.org/jenkins/job/Osmocom-build-tags-against-master/1792/console
2024-01-11 19:40:27 +00:00
Neels Hofmeyr
2b30cbdfa8 drop (now) unused code
Removing the duality of codecs[] and ptmap[] in structs mgcp_msg,
mgcp_response and mgcp_conn_peer has removed the need to "map" from
codec type enum to payload type number. They are stored together now.

Remove functions that are no longer used.
None of our osmocom users of libosmo-mgcp-client call these functions.

Change-Id: I84e5285831397c992af59deee12dea8458d16cc6
2024-01-05 02:44:58 +01:00
Neels Hofmeyr
c9df010416 mgcp_client_test: add test_parse_response()
Change-Id: I842ce65a9a70f313570857b7df53727cc572b9e6
2024-01-05 02:44:58 +01:00
Neels Hofmeyr
3a3e1b57c6 client SDP: more verbose error logging
So far it was pure guess work to find out why a message fails.

Change-Id: Ibc6343db82281789004c140ba98d99e5f6f73d83
2024-01-05 02:44:58 +01:00
Neels Hofmeyr
186d4f1a0b client: allow MGCP_MAX_CODECS entries
So far we allow only MGCP_MAX_CODECS-1 entries, because the parsing exit
condition hits only after the array size check. Instead, check the array
size a bit later, just before actually adding a valid entry.

This is verified to work as expected in upcoming patch
I842ce65a9a70f313570857b7df53727cc572b9e6 that adds a new
mgcp_client_test.c section for this.

Change-Id: I9a28da85e437f118026ea71a5a708e5758fff623
2024-01-05 02:44:58 +01:00
Neels Hofmeyr
cc2f7937ed client: collapse codecs[] and ptmap[]; allow codec variants
codecs[] is an array of enum osmo_mgcp_codecs.
ptmap[] is an array of { enum osmo_mgcp_codecs, unsigned int ptmap }.

MGCP lists first a bunch of payload type numbers and then specifies them
again for details, like the numbers 112, 96, 3 in this example:

 m=audio <port> RTP/AVP 112 96 3
 a=rtpmap:112 AMR/8000
 a=rtpmap:96 VND.3GPP.IUFP/16000
 a=rtpmap:3 GSM-FR/8000

So far we keep these lists in two separate arrays:
- codecs[], codecs_len stores the 'm=audio' list
- ptmap[], ptmap_len stores the 'a=rtpmap' list (and may omit some
  elements present in codecs[])
This applies to both struct mgcp_response and struct mgcp_msg.

These are semantically identical, and the separation leads to checks,
conversions and dear hopes of correct ordering.

So let's keep only one list with both codec and payload type number in
it. The 'm=audio' list establishes the order of the pt numbers, and the
'a=rtpmap' list adds codec information to the established entries.

In the internal API structs mgcp_msg and mgcp_response, just drop the
codecs[] entirely.

In public API struct mgcp_conn_peer, keep the codecs[] array, mark it
deprecated, and provide a backwards compat conversion: if any caller
invokes mgcp_conn_create() or mgcp_conn_modify() with codecs[] still
present in the verb_info arg, then move codecs[] entries over to the
ptmap[] array in a sensible way.
(BTW, even mgcp_conn_create() and mgcp_conn_modify() are never called
from outside of libosmo-mgcp-client in any of our osmo-cni programs;
users call osmo_mgcpc_ep_ci_add() and osmo_mgcpc_ep_ci_request(), which
in turn may pass user-provided codecs[] lists on to mgcp_conn_create() or
mgcp_conn_modify().)

Tests for parsing the MGCP response are mostly missing. They will be
added in upcoming patch I842ce65a9a70f313570857b7df53727cc572b9e6,
because they will be using only the new ptmap API.

Related: OS#6171
Change-Id: I798e02c6663376d3d52f4a74fc4b32411ce95bed
2024-01-05 02:44:58 +01:00
Neels Hofmeyr
960c031a67 client: deprecate legacy API
Change-Id: I7409907dafbb2fe905fee9bc22d6870056bf3022
2024-01-05 01:44:33 +00:00
Neels Hofmeyr
4993452561 build: move mgcp/*.h to noinst_HEADERS, drop RPM libosmo-mgcp-devel
We only install the mgcp_client/ headers.

Related: OS#6300
Change-Id: Ie0f79222bd1702097c12193dcf7a0462805cfc4a
2023-12-23 07:23:33 +01:00
83 changed files with 7166 additions and 1561 deletions

View File

@@ -18,13 +18,14 @@ SUBDIRS = \
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = \
libosmo-sdp.pc \
libosmo-mgcp-client.pc \
$(NULL)
BUILT_SOURCES = $(top_srcdir)/.version
EXTRA_DIST = \
.version \
contrib/osmo-mgw.spec.in \
README.md \
debian \
git-version-gen \
osmoappdesc.py \

24
README
View File

@@ -1,24 +0,0 @@
About OsmoMGW
=============
OsmoMGW originated from the OpenBSC project, which started as a minimalistic
all-in-one implementation of the GSM Network. In 2017, OpenBSC had reached
maturity and diversity (including M3UA SIGTRAN and 3G support in the form of
IuCS and IuPS interfaces) that naturally lead to a separation of the all-in-one
approach to fully independent separate programs as in typical GSM networks.
OsmoMGW was one of the parts split off from the old openbsc.git. It originated
as a solution to merely navigate RTP streams through a NAT, but has since
matured to a Media Gateway implementation that is capable of streaming RTP for
2G (AoIP) and 3G (IuCS) GSM networks as well as (still not implemented at time
of writing) transcoding between TRAU, various RTP payloads and IuUP.
The OsmoMGW program exposes an MGCP interface towards clients like OsmoMSC and
OsmoBSC, and receives and sends RTP streams as configured via MGCP.
The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC (found
in osmo-msc.git) to instruct OsmoMGW via its MGCP service.
Find OsmoMGW issue tracker and wiki online at
https://osmocom.org/projects/osmo-mgw
https://osmocom.org/projects/osmo-mgw/wiki

102
README.md Normal file
View File

@@ -0,0 +1,102 @@
osmo-mgw - Osmocom MGW (Media GateWay) Implementation
=====================================================
This repository contains a C-language implementation of an MGW (Media
GateWay) for use [not only] within the 2G (GSM) and/or 3G (UMTS)
Cellular Network built using Osmocom CNI (Cellular Network
Infrastructure) software.
The OsmoMGW program provides an MGCP interface towards an MGCP call agent
(client) like OsmoMSC and OsmoBSC, and receives and sends RTP streams as
configured via the MGCP control plane.
This Media Gateway implementation is capable of
* streaming RTP for 2G (3GPP AoIP and Abis-over-IP)
* streaming RTP for 3G (IuCS including the IuFP protocol)
* TDM (E1/T1) based Abis interface with TRAU frames on 16k sub-slots
* basic support for LCLS (Local Call, Local Switch) related features
* various built-in translation capabilities
* between Abis TRAU frames and RTP formats
* between 2G AMR/RTP and 3G AMR/IuFP/RTP
* between bandwidth-efficient and octet-aligned AMR
* between different standards for encapsulating GSM HR codec frames in RTP
osmo-mgw is typically co-located with
* osmo-bsc (GSM BSC)
* osmo-msc (GSM/UMTS MSC)
* osmo-hnbgw (UMTS HNBGW); osmo-mgw implements RTP relay between Iuh
and IuCS interfaces
The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC
(found in osmo-msc.git) to instruct OsmoMGW via its MGCP service.
Homepage
--------
You can find the OsmoMGW issue tracker and wiki online at
<https://osmocom.org/projects/osmo-mgw> and <https://osmocom.org/projects/osmo-mgw/wiki>.
GIT Repository
--------------
You can clone from the official osmo-mgw.git repository using
git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw>
Documentation
-------------
User Manuals and VTY reference manuals are [optionally] built in PDF form
as part of the build process.
Pre-rendered PDF version of the current "master" can be found at
[User Manual](https://ftp.osmocom.org/docs/latest/osmomgw-usermanual.pdf)
as well as the [VTY Reference Manual](https://ftp.osmocom.org/docs/latest/osmomgw-vty-reference.pdf)
Mailing List
------------
Discussions related to osmo-mgw are happening on the
openbsc@lists.osmocom.org mailing list, please see
<https://lists.osmocom.org/mailman/listinfo/openbsc> for subscription
options and the list archive.
Please observe the [Osmocom Mailing List
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
when posting.
Contributing
------------
Our coding standards are described at
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
We use a gerrit based patch submission/review process for managing
contributions. Please see
<https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit> for
more details
The current patch queue for osmo-mgw can be seen at
<https://gerrit.osmocom.org/#/q/project:osmo-mgw+status:open>
History
-------
OsmoMGW originated from the OpenBSC project, which started as a minimalistic
all-in-one implementation of the GSM Network. In 2017, OpenBSC had reached
maturity and diversity (including M3UA SIGTRAN and 3G support in the form of
IuCS and IuPS interfaces) that naturally lead to a separation of the all-in-one
approach to fully independent separate programs as in typical GSM networks.
OsmoMGW was one of the parts split off from the old openbsc.git. It originated
as a solution to merely navigate RTP streams through a NAT, but has since
matured.

View File

@@ -1,42 +1,11 @@
# When cleaning up this file upon a release:
#
# - Note that the release version number is entirely unrelated to the API
# versions. A release version 5.2.3 may happily have an API version of 42:7:5.
#
# - Bump API version in src/lib*/Makefile.am files according to chapter
# "Library interface versions" of the libtool documentation.
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
#
# - Iff the 'current' API version has changed, rename debian/lib*.install
#
# API version bumping for the impatient:
# LIBVERSION=c:r:a (current:revision_of_current:backwards_compat_age)
# 5:2:4 means that
# - this implements version 5 of the API;
# - this is the 2nd (compatible) revision of API version 5;
# - this is backwards compatible to all APIs since 4 versions ago,
# i.e. callers that need API versions from 1 to 5 can use this.
#
# Bumping API versions recipe:
# If the library source code has changed at all since the last update, r++;
# If any interfaces have been added, removed, or changed since the last update, c++, r=0;
# If any interfaces have been added since the last public release, a++;
# If any interfaces have been removed or changed since the last public release, a=0.
#
#library what description / commit summary line
libosmo-mgcp-client remove public API These public API items have not been called by any of our osmo-programs
for many years. Any sane caller of libosmo-mgcp-client should use the
higher level osmo_mgcpc_* API instead. Move these to a private header:
struct mgcp_response_head
struct mgcp_response
struct mgcp_msg
mgcp_response_parse_params()
mgcp_client_tx()
mgcp_client_cancel()
mgcp_msg_gen()
mgcp_msg_trans_id()
libosmo-mgcp-client remove public API Since codecs[] has been deprecated in favor of ptmap[], there is no use
for the following functions; all known callers have been within
osmo-mgw.git:
map_codec_to_pt()
map_pt_to_codec()
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
# according to https://osmocom.org/projects/cellular-infrastructure/wiki/Make_a_new_release
# In short: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
# LIBVERSION=c:r:a
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:a.
# If any interfaces have been added since the last public release: c:r:a + 1.
# If any interfaces have been removed or changed since the last public release: c:r:0.
#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.

View File

@@ -44,13 +44,13 @@ AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DLSYM)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.4.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.5.0)
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)
CFLAGS="$CFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
CPPFLAGS="$CPPFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
@@ -187,11 +187,14 @@ AM_CONFIG_HEADER(bscconfig.h)
AC_OUTPUT(
libosmo-mgcp-client.pc
libosmo-sdp.pc
include/Makefile
include/osmocom/Makefile
include/osmocom/sdp/Makefile
include/osmocom/mgcp_client/Makefile
include/osmocom/mgcp/Makefile
src/Makefile
src/libosmo-sdp/Makefile
src/libosmo-mgcp-client/Makefile
src/libosmo-mgcp/Makefile
src/osmo-mgw/Makefile
@@ -199,10 +202,10 @@ AC_OUTPUT(
tests/atlocal
tests/mgcp_client/Makefile
tests/mgcp/Makefile
tests/sdp/Makefile
doc/Makefile
doc/examples/Makefile
doc/manuals/Makefile
contrib/Makefile
contrib/systemd/Makefile
contrib/osmo-mgw.spec
Makefile)

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

View File

@@ -1,122 +0,0 @@
#
# spec file for package osmo-mgw
#
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
# Please submit bugfixes or comments via http://bugs.opensuse.org/
#
Name: osmo-mgw
Version: @VERSION@
Release: 0
Summary: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
License: AGPL-3.0-or-later AND GPL-2.0-or-later
Group: Hardware/Mobile
URL: https://osmocom.org/projects/osmo-mgw
Source: %{name}-%{version}.tar.xz
BuildRequires: automake >= 1.9
BuildRequires: libtool >= 2
BuildRequires: pkgconfig >= 0.20
%if 0%{?suse_version}
BuildRequires: systemd-rpm-macros
%endif
BuildRequires: pkgconfig(libosmo-netif) >= 1.4.0
BuildRequires: pkgconfig(libosmocore) >= 1.9.0
BuildRequires: pkgconfig(libosmoctrl) >= 1.9.0
BuildRequires: pkgconfig(libosmogsm) >= 1.9.0
BuildRequires: pkgconfig(libosmovty) >= 1.9.0
BuildRequires: pkgconfig(libosmocoding) >= 1.9.0
BuildRequires: pkgconfig(libosmoabis) >= 1.5.0
BuildRequires: pkgconfig(libosmotrau) >= 1.5.0
%{?systemd_requires}
%description
OsmoMGW is Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks.
%package -n libosmo-mgcp-client12
Summary: Osmocom's Media Gateway Control Protocol client library
Group: System/Libraries
%description -n libosmo-mgcp-client12
Osmocom's Media Gateway Control Protocol client library.
%package -n libosmo-mgcp-client-devel
Summary: Development files for Osmocom's Media Gateway Control Protocol client library
Group: Development/Libraries/C and C++
Requires: libosmo-mgcp-client12 = %{version}
%description -n libosmo-mgcp-client-devel
Osmocom's Media Gateway Control Protocol client librarary.
This subpackage contains libraries and header files for developing
applications that want to make use of libosmo-mgcp-client.
%prep
%setup -q
%build
echo "%{version}" >.tarball-version
autoreconf -fi
%configure \
--disable-static \
--docdir=%{_docdir}/%{name} \
--with-systemdsystemunitdir=%{_unitdir}
make %{?_smp_mflags}
%install
%make_install
find %{buildroot} -type f -name "*.la" -delete -print
%check
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%post -n libosmo-mgcp-client12 -p /sbin/ldconfig
%postun -n libosmo-mgcp-client12 -p /sbin/ldconfig
%if 0%{?suse_version}
%preun
%service_del_preun osmo-mgw.service
%postun
%service_del_postun osmo-mgw.service
%pre
%service_add_pre osmo-mgw.service
%post
%service_add_post osmo-mgw.service
%endif
%files
%license COPYING
%doc AUTHORS README
%dir %{_docdir}/%{name}/examples
%dir %{_docdir}/%{name}/examples/osmo-mgw
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw.cfg
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw-abis_e1.cfg
%{_bindir}/osmo-mgw
%{_unitdir}/osmo-mgw.service
%dir %{_sysconfdir}/osmocom
%config(noreplace) %{_sysconfdir}/osmocom/osmo-mgw.cfg
%files -n libosmo-mgcp-client12
%{_libdir}/libosmo-mgcp-client.so.12*
%files -n libosmo-mgcp-client-devel
%{_libdir}/libosmo-mgcp-client.so
%{_libdir}/pkgconfig/libosmo-mgcp-client.pc
%dir %{_includedir}/osmocom
%dir %{_includedir}/osmocom/mgcp_client
%{_includedir}/osmocom/mgcp_client/*.h
%changelog

View File

@@ -9,8 +9,11 @@ LimitNOFILE=65536
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
Restart=always
User=osmocom
Group=osmocom
ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg
RestartSec=2
AmbientCapabilities=CAP_SYS_NICE
# CPU scheduling policy:
CPUSchedulingPolicy=rr
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):

91
debian/changelog vendored
View File

@@ -1,3 +1,94 @@
osmo-mgw (1.13.1) unstable; urgency=medium
[ Philipp Maier ]
* mgcp_network: use an uint16_t to store the port number
* mgcp_network: add missing ntohs
[ Pau Espin Pedrol ]
* tests/mgcp/mgcp_test: Add some extra asserts in code
-- Oliver Smith <osmith@sysmocom.de> Thu, 12 Sep 2024 13:53:19 +0200
osmo-mgw (1.13.0) unstable; urgency=medium
[ Neels Hofmeyr ]
* add mgcp_conn_rtp_type_names[]
* mgcp_parse_audio_port_pt(): fix buffer overflow
* client: replace two assertions with graceful error handling
* systemd,manual: set LimitNOFILE=65536
* IuUP: allow Initialization from any address if not yet set
* check_rtp_origin: drop special case for legacy IuUP hack
* mgcp_client_test: fix function name
* fix possible NULL deref on early media
* client: move some items to internal header
* client: safely handle dealloc on event dispatch
* build: move mgcp/*.h to noinst_HEADERS, drop RPM libosmo-mgcp-devel
* client: deprecate legacy API
* client: collapse codecs[] and ptmap[]; allow codec variants
* client: allow MGCP_MAX_CODECS entries
* client SDP: more verbose error logging
* mgcp_client_test: add test_parse_response()
* drop (now) unused code
* tests/mgcp: add update_exp target
* drop get_net_downlink_format_cb
* drop cfg 'sdp audio fmtp-extra'
* mgcp_codec_decide: remove redundant lookup
* tweak DEBUG log
* mgcp_test: fix false negatives in test output
* mgw: do not fail MGCP on codec mismatch
* mgcp-client: always send 'm=audio' line
* mgcp_test.c: verify osmo-mgw accepts m=audio 0
* mgcp_test.c: fix various missing '\r' and '\n'
* mgcp_test: test a=ptime:20, not 40
* mgcp_test: add CRCX for IUFP in sendrecv
* do not FAIL on CRCX in sendrecv mode
[ Keith Whyte ]
* vty and log: also show local port for RTP conns
[ Andreas Eversberg ]
* Use uniform log format for default config files
[ Pau Espin Pedrol ]
* mgcp_network: Improve err logging when rtp pkt from unexpected origin comes in
* cosmetic: Fix line indentation
* IuUP: Allow Initialization with set rem IP address and unset rem port
* mgcp-client: Transmit remote IP addr in CRCX if known and port=0
* Fix IuUP RTP hdr seqnr field not incremented
* iuup: Increment RTP hdr seqnr even if Tx over UDP fails
[ Vadim Yanitskiy ]
* mgcp: simplify getting msgb tail in mgcp_msg_terminate_nul()
* mgcp: reserve once byte for '\0' in mgcp_do_read()
* mgcp: correctly put NUL character in mgcp_msg_terminate_nul()
* build: include README into the release tarball
[ neels ]
* Revert "drop (now) unused code"
[ Harald Welte ]
* Convert README to README.md and expand like in other projects
* migrate mgcp_client from osmo_wqueue to osmo_io
* mgw: Add our usual SIGABRT, SIGUSR1 signal handlers
* don't log useless "transcoding disabled" message
* remove strange loop for non-existant transcoding support
* simplify unused transcoding/processing call-back
* remove osmo_fd from mgcp_create_bind()
* Change msgb ownership in processing of received msgb
* cosmetic: make linter happy
* Convert RTP/RTCP/OSMUX I/O from osmo_fd to osmo_io
[ Oliver Smith ]
* contrib: remove rpm spec file
* contrib/systemd: run as osmocom user
* doc: example configs: fix deprecation warnings
[ Mychaela N. Falconia ]
* E1: support HRv1 codec on both 16k and 8k subslots
* fix E1 TS output when used with osmo-e1d
-- Oliver Smith <osmith@sysmocom.de> Wed, 24 Jul 2024 15:44:47 +0200
osmo-mgw (1.12.1) unstable; urgency=medium
[ Pau Espin Pedrol ]

27
debian/control vendored
View File

@@ -6,10 +6,10 @@ Build-Depends: debhelper (>= 10),
dh-autoreconf,
pkg-config,
autotools-dev,
libosmocore-dev (>= 1.9.0),
libosmo-netif-dev (>= 1.4.0),
libosmo-abis-dev (>= 1.5.0),
osmo-gsm-manuals-dev (>= 1.5.0)
libosmocore-dev (>= 1.10.0),
libosmo-netif-dev (>= 1.5.0),
libosmo-abis-dev (>= 1.6.0),
osmo-gsm-manuals-dev (>= 1.6.0)
Standards-Version: 3.9.8
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
@@ -21,7 +21,7 @@ Multi-Arch: foreign
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
Package: libosmo-mgcp-client12
Package: libosmo-mgcp-client14
Section: libs
Architecture: any
Multi-Arch: same
@@ -33,9 +33,24 @@ Package: libosmo-mgcp-client-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libosmo-mgcp-client12 (= ${binary:Version}), ${misc:Depends}
Depends: libosmo-mgcp-client14 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
Package: libosmo-sdp1
Section: libs
Architecture: any
Multi-Arch: same
Pre-Depends: ${misc:Pre-Depends}
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: libosmo-sdp: Osmocom's Session Description Protocol encoder and decoder
Package: libosmo-sdp-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libosmo-sdp1 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-sdp: Osmocom's Session Description Protocol encoder and decoder
Package: osmo-mgw-doc
Architecture: all
Section: doc

19
debian/copyright vendored
View File

@@ -63,3 +63,22 @@ License: GPL-3.0+
Files: osmoappdesc.py
Copyright: 2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
License: GPL-3.0+
Files: src/libosmo-sdp/* include/osmocom/sdp/*
Copyright: 2024 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
License: GPL-3.0+
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
.
On Debian systems, the complete text of the GNU General Public License
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.

4
debian/libosmo-sdp-dev.install vendored Normal file
View File

@@ -0,0 +1,4 @@
usr/include/osmocom/sdp
usr/lib/*/libosmo-sdp.so
usr/lib/*/libosmo-sdp.a
usr/lib/*/pkgconfig/libosmo-sdp.pc

1
debian/libosmo-sdp1.install vendored Normal file
View File

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

38
debian/postinst vendored Executable file
View File

@@ -0,0 +1,38 @@
#!/bin/sh -e
case "$1" in
configure)
# Create the osmocom group and user (if it doesn't exist yet)
if ! getent group osmocom >/dev/null; then
groupadd --system osmocom
fi
if ! getent passwd osmocom >/dev/null; then
useradd \
--system \
--gid osmocom \
--home-dir /var/lib/osmocom \
--shell /sbin/nologin \
--comment "Open Source Mobile Communications" \
osmocom
fi
# Fix permissions of previous (root-owned) install (OS#4107)
if dpkg --compare-versions "$2" le "1.13.0"; then
if [ -e /etc/osmocom/osmo-mgw.cfg ]; then
chown -v osmocom:osmocom /etc/osmocom/osmo-mgw.cfg
chmod -v 0660 /etc/osmocom/osmo-mgw.cfg
fi
if [ -d /etc/osmocom ]; then
chown -v root:osmocom /etc/osmocom
chmod -v 2775 /etc/osmocom
fi
mkdir -p /var/lib/osmocom
chown -R -v osmocom:osmocom /var/lib/osmocom
fi
;;
esac
# dh_installdeb(1) will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#

View File

@@ -14,15 +14,12 @@ e1_input
e1_line 0 port 0
mgcp
bind ip 127.0.0.1
rtp port-range 4002 16000
rtp port-range 4002 16001
rtp bind-ip 127.0.0.1
rtp ip-probing
rtp ip-dscp 46
bind port 2427
sdp audio payload number 98
sdp audio payload name GSM
number endpoints 512
loop 0
force-realloc 1
rtcp-omit
rtp-patch ssrc

View File

@@ -11,15 +11,12 @@ log stderr
mgcp
bind ip 127.0.0.1
rtp port-range 4002 16000
rtp port-range 4002 16001
rtp bind-ip 127.0.0.1
rtp ip-probing
rtp ip-dscp 46
bind port 2427
sdp audio payload number 98
sdp audio payload name GSM
number endpoints 512
loop 0
force-realloc 1
rtcp-omit
rtp-patch ssrc

View File

@@ -8,7 +8,11 @@ nobase_include_HEADERS = \
osmocom/mgcp_client/mgcp_client_endpoint_fsm.h \
osmocom/mgcp_client/mgcp_client_fsm.h \
osmocom/mgcp_client/mgcp_client_pool.h \
osmocom/mgcp_client/fmtp.h \
osmocom/sdp/fmtp.h \
osmocom/sdp/sdp_codec.h \
osmocom/sdp/sdp_codec_list.h \
osmocom/sdp/sdp_msg.h \
osmocom/sdp/sdp_strings.h \
$(NULL)
noinst_HEADERS = \

View File

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

View File

@@ -24,6 +24,7 @@
#include <osmocom/core/msgb.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/logging.h>
@@ -70,12 +71,10 @@ typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
/**
* Return:
* < 0 in case no audio was processed
* >= 0 in case audio was processed. The remaining payload
* length will be returned.
* >= 0 in case audio was processed.
*/
typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size);
struct mgcp_rtp_end *dst_end, struct msgb *msg);
struct mgcp_conn_rtp;
@@ -206,6 +205,5 @@ int mgcp_send_reset_ep(struct mgcp_endpoint *endp);
int mgcp_send_reset_all(struct mgcp_config *cfg);
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint8_t dscp,
uint8_t prio);
int mgcp_udp_send(int fd, const struct osmo_sockaddr *addr, const char *buf, int len);
int mgcp_create_bind(const char *source_addr, int port, uint8_t dscp, uint8_t prio);
int mgcp_udp_send(struct osmo_io_fd *iofd, const struct osmo_sockaddr *addr, const char *buf, int len);

View File

@@ -13,7 +13,6 @@ struct mgcp_conn_rtp;
void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param);
int mgcp_codec_add2(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const char *fmtp);
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);

View File

@@ -59,7 +59,7 @@ enum mgcp_x_osmo_ign {
MGCP_X_OSMO_IGN_CALLID = 1,
};
/* Deprecated. Use the new fmtp string instead. */
/* Codec parameters (communicated via SDP/fmtp) */
struct mgcp_codec_param {
bool amr_octet_aligned_present;
bool amr_octet_aligned;

View File

@@ -24,10 +24,12 @@
#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 <inttypes.h>
@@ -237,15 +239,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

@@ -138,11 +138,20 @@ 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_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

@@ -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,8 +36,7 @@ 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);

View File

@@ -4,6 +4,7 @@
#include <stdbool.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/mgcp/mgcp.h>
@@ -83,11 +84,8 @@ struct mgcp_rtp_codec {
char audio_name[64];
char subtype_name[64];
/* Deprecated. Use the new fmtp string instead. */
bool param_present;
struct mgcp_codec_param param;
char fmtp[256];
};
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
@@ -96,7 +94,7 @@ struct mgcp_rtp_end {
struct osmo_sockaddr addr;
/* in network byte order */
int rtcp_port;
uint16_t rtcp_port;
/* currently selected audio codec */
struct mgcp_rtp_codec *codec;
@@ -123,8 +121,8 @@ struct mgcp_rtp_end {
bool rfc5993_hr_convert;
/* Each end has a separate socket for RTP and RTCP */
struct osmo_fd rtp;
struct osmo_fd 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;
@@ -134,6 +132,7 @@ struct mgcp_rtp_end {
};
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);
struct mgcp_rtp_tap {
/* is this tap active (1) or not (0) */
@@ -154,7 +153,6 @@ int mgcp_dispatch_e1_bridge_cb(struct msgb *msg);
void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
struct mgcp_conn_rtp *conn);
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
struct mgcp_rtp_state *state,
struct mgcp_rtp_end *rtp_end,
@@ -162,8 +160,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
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,
char *data, int *len, int buf_size);
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,
@@ -183,7 +180,7 @@ void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *
int id, int inc);
void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
int id);
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg);
void forward_data_tap(struct osmo_io_fd *iofd, struct mgcp_rtp_tap *tap, struct msgb *msg);
uint32_t mgcp_get_current_ts(unsigned codec_rate);
int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg, bool target_is_oa);

View File

@@ -1,5 +1,8 @@
#pragma once
#include <osmocom/core/utils.h>
#include <osmocom/mgcp/mgcp_common.h>
/* Internal structure while parsing a request */
struct mgcp_parse_data {
struct mgcp_config *cfg;
@@ -28,3 +31,9 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
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

@@ -1,29 +0,0 @@
#pragma once
#include <stddef.h>
#include <stdbool.h>
#define OSMO_SDP_NAME_A "a"
#define OSMO_SDP_NAME_FMTP "fmtp"
#define OSMO_SDP_NAME_AMR_OCTET_ALIGN "octet-align"
#define OSMO_SDP_VAL_AMR_OCTET_ALIGN_0 OSMO_SDP_NAME_AMR_OCTET_ALIGN "=0"
#define OSMO_SDP_VAL_AMR_OCTET_ALIGN_1 OSMO_SDP_NAME_AMR_OCTET_ALIGN "=1"
/* "fmtp:" */
#define OSMO_SDP_PREFIX_FMTP OSMO_SDP_NAME_FMTP ":"
/* "a=fmtp:" */
#define OSMO_SDP_PREFIX_A_FMTP OSMO_SDP_NAME_A "=" OSMO_SDP_PREFIX_FMTP
bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const char *option_name);
int osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int default_value);
/* Some AMR related fmtp parameters as in https://www.rfc-editor.org/rfc/rfc4867#section-8.1 that osmo-mgw needs.*/
bool osmo_sdp_fmtp_amr_is_octet_aligned(const char *fmtp);
/*! To compose AMR related fmtp indicating octet-align.
* Usage:
* printf("%s", OSMO_SDP_AMR_SET_OCTET_ALIGN(oa_flag));
*/
#define OSMO_SDP_AMR_SET_OCTET_ALIGN(VAL) \
((VAL) ? OSMO_SDP_VAL_AMR_OCTET_ALIGN_1 : OSMO_SDP_VAL_AMR_OCTET_ALIGN_0 )

View File

@@ -77,9 +77,6 @@ struct ptmap {
/*! payload type number (96-127) */
unsigned int pt;
/*! the MGCP 'a=fmtp:N <...>' string, e.g. "mode-set=1,2,3;octet-align=0". */
char fmtp[256];
};
int ptmap_cmp(const struct ptmap *a, const struct ptmap *b);
@@ -122,5 +119,9 @@ static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
}
enum mgcp_codecs map_str_to_codec(const char *str);
unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len,
enum mgcp_codecs codec);
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
unsigned int pt);
const char *mgcp_client_name(const struct mgcp_client *mgcp);

View File

@@ -12,11 +12,7 @@
* When modifiying a connection, the endpoint and call_id members may be left
* unpopulated. The call_id field is ignored in this case. If an endpoint
* identifier is supplied it is checked against the internal state to make
* sure it is correct.
*
* CAUTION: This struct may be subject to changes and new struct members may
* be added in the future. To prevent memory conflicts it is strongly advised
* to allocate this struct dynamically using mgcp_conn_peer_alloc() */
* sure it is correct. */
struct mgcp_conn_peer {
/*! RTP connection IP-Address (optional, string e.g. "127.0.0.1") */
char addr[INET6_ADDRSTRLEN];
@@ -61,15 +57,12 @@ struct mgcp_conn_peer {
* address is set. If != MGCP_CONN_NONE, force this conn mode. */
enum mgcp_connection_mode conn_mode;
/*! Deprectated, use ptmap[].fmtp instead.
* Global codec params. In case the codec requires additional format parameters (fmtp), those can be set
* here, see also mgcp_common.h. The format parameters will be applied on all codecs where applicable. */
/*! If the codec requires additional format parameters (fmtp), those cann be set here, see also
* mgcp_common.h */
bool param_present;
struct mgcp_codec_param param;
};
struct mgcp_conn_peer *mgcp_conn_peer_alloc(void *ctx);
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_alloc() and osmo_mgcpc_ep_ci_add() instead");

View File

@@ -1,8 +1,10 @@
#pragma once
#include <osmocom/core/write_queue.h>
#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 */
@@ -13,7 +15,7 @@ struct reset_ep {
struct mgcp_client {
struct mgcp_client_conf actual;
struct osmo_wqueue wq;
struct osmo_io_fd *iofd;
mgcp_trans_id_t next_trans_id;
struct llist_head responses_pending;
struct mgcp_client_pool_member *pool_member;

View File

@@ -0,0 +1,3 @@
noinst_HEADERS = \
sdp_internal.h \
$(NULL)

View File

@@ -0,0 +1,34 @@
/* Public API for codec management in SDP messages: managing SDP fmtp strings. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const char *option_name);
int64_t osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int64_t default_value);
bool osmo_sdp_fmtp_amr_is_octet_aligned(const char *fmtp);
bool osmo_sdp_fmtp_amr_match(const char *a, const char *b);

View File

@@ -0,0 +1,126 @@
/* Public API for codec management in SDP messages. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
/* RFC-8866 5.14 and 6.6.
*
* Represent the items describing an SDP codec entry, as in:
*
* m=audio 1234 RTP/AVP <payload_type>
* a=rtpmap:<payload_type> <encoding-name>/<clock-rate>
* a=fmtp:<payload_type> <fmtp>
*
* For example:
*
* m=audio 1234 RTP/AVP 98
* a=rtpmap:98 AMR/8000
* a=fmtp:98 octet-align=1;mode-set=0,2,4,7
*/
struct osmo_sdp_codec {
/* Payload type number ("payload-type"), like 3 for GSM-FR. Limited to 0..127. */
int8_t payload_type;
/* Encoding name like "GSM", "AMR", "GSM-EFR".
*
* RFC-8866 defines no length limit on the encoding name. This API leaves it up to the caller to provide
* sufficient space, via the SDP_SIZES_* definitions.
*
* encoding-name = token
* token = 1*(token-char)
* token-char = ALPHA / DIGIT
* / "!" / "#" / "$" / "%" / "&"
* / "'" ; (single quote)
* / "*" / "+" / "-" / "." / "^" / "_"
* / "`" ; (Grave accent)
*/
char *encoding_name;
/* Samplerate ("clock-rate"), usually 8000 for GSM. */
unsigned int rate;
/* Codec parameters as supplied in SDP line 'a=fmtp:<payload-type> <format-specific-params>'. This holds only
* the 'format-specific-params' bytestring. For example, for SDP line 'a=fmtp:123 param1=val1;param2=val2', this
* holds only the , "param1=val1;param2=val2" part. For the buffer size, see fmtp_size. */
char *fmtp;
/* Entry used by osmo_sdp_codec_list. */
struct llist_head entry;
/* For future extension, always set to false. */
bool v2;
};
struct osmo_sdp_codec *osmo_sdp_codec_alloc(void *ctx);
int osmo_sdp_codec_set(struct osmo_sdp_codec *c,
int8_t payload_type, const char *encoding_name, unsigned int rate, const char *fmtp);
int osmo_sdp_codec_set_encoding_name(struct osmo_sdp_codec *c, const char *encoding_name);
int osmo_sdp_codec_set_fmtp(struct osmo_sdp_codec *c, const char *fmtp);
bool osmo_sdp_codec_is_set(const struct osmo_sdp_codec *a);
int osmo_sdp_codec_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec *codec);
char *osmo_sdp_codec_to_str_c(void *ctx, const struct osmo_sdp_codec *codec);
int osmo_sdp_codec_from_str(struct osmo_sdp_codec *dst, const char *str, int str_len);
enum osmo_sdp_cmp {
OSMO_SDP_CMP_IGNORE = 0,
OSMO_SDP_CMP_EQUIVALENT,
OSMO_SDP_CMP_EXACT,
};
/*! Indicate how to match SDP codecs to various osmo_sdp_*() functions.
* Callers may define own flags, or use predefined instances:
* osmo_sdp_codec_cmp_exact, osmo_sdp_codec_cmp_equivalent, ...
*
* For example, to trigger some action if any item has changed, set all items to true / OSMO_SDP_CMP_EXACT (see
* osmo_sdp_codec_cmp_exact).
* To find codecs that are the same between two SDP sessions, set payload_type=false and fmtp=OSMO_SDP_CMP_EQUIVALENT
* (see osmo_sdp_codec_cmp_equivalent).
* To just list all contained "AMR" codecs, set only encoding_name=true (see osmo_sdp_codec_cmp_name).
*/
struct osmo_sdp_codec_cmp_flags {
/*! true = compare payload type numbers 1:1; false = ignore. */
bool payload_type;
/*! true = compare encoding_name 1:1; false = ignore. */
bool encoding_name;
/*! true = compare rate 1:1; false = ignore. */
bool rate;
/*! OSMO_SDP_CMP_IGNORE = ignore fmtp;
* OSMO_SDP_CMP_EQUIVALENT = use osmo_sdp_fmtp_amr_match() for AMR, otherwise compare 1:1;
* OSMO_SDP_CMP_EXACT = compare 1:1.
*/
enum osmo_sdp_cmp fmtp;
};
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_exact;
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent;
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_name;
int osmo_sdp_codec_cmp(const struct osmo_sdp_codec *a, const struct osmo_sdp_codec *b,
const struct osmo_sdp_codec_cmp_flags *cmp);

View File

@@ -0,0 +1,69 @@
/* Public API for codec management in SDP messages: list of struct osmo_sdp_codec. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/sdp/sdp_codec.h>
struct osmo_sdp_codec_list {
struct llist_head list;
/* For future extension, always set to false. */
bool v2;
};
struct osmo_sdp_codec_list *osmo_sdp_codec_list_alloc(void *ctx);
void osmo_sdp_codec_list_free_items(struct osmo_sdp_codec_list *codec_list);
int8_t osmo_sdp_codec_list_get_unused_dyn_pt_nr(const struct osmo_sdp_codec_list *codec_list, int8_t suggest_pt_nr);
struct osmo_sdp_codec *osmo_sdp_codec_list_add_empty(struct osmo_sdp_codec_list *codec_list);
struct osmo_sdp_codec *osmo_sdp_codec_list_add(struct osmo_sdp_codec_list *codec_list,
const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *once, bool pick_unused_pt_nr);
int osmo_sdp_codec_list_remove(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *cmpf);
void osmo_sdp_codec_list_remove_entry(struct osmo_sdp_codec *codec);
int osmo_sdp_codec_list_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec_list *codec_list, bool summarize);
char *osmo_sdp_codec_list_to_str_c(void *ctx, const struct osmo_sdp_codec_list *codec_list, bool summarize);
struct osmo_sdp_codec *osmo_sdp_codec_list_first(const struct osmo_sdp_codec_list *list);
int osmo_sdp_codec_list_move_to_first(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *cmpf);
#define osmo_sdp_codec_list_foreach(STRUCT_SDP_CODEC_P, SDP_CODEC_LIST) \
llist_for_each_entry(STRUCT_SDP_CODEC_P, &(SDP_CODEC_LIST)->list, entry)
#define osmo_sdp_codec_list_foreach_safe(STRUCT_SDP_CODEC_P, SAFE_P, SDP_CODEC_LIST) \
llist_for_each_entry_safe(STRUCT_SDP_CODEC_P, SAFE_P, &(SDP_CODEC_LIST)->list, entry)
int osmo_sdp_codec_list_cmp(const struct osmo_sdp_codec_list *a, const struct osmo_sdp_codec_list *b,
const struct osmo_sdp_codec_cmp_flags *cmpf);
void osmo_sdp_codec_list_intersection(struct osmo_sdp_codec_list *dst, const struct osmo_sdp_codec_list *other,
const struct osmo_sdp_codec_cmp_flags *cmpf,
bool translate_payload_type_numbers);
struct osmo_sdp_codec *osmo_sdp_codec_list_by_payload_type(struct osmo_sdp_codec_list *codec_list, int8_t payload_type);
bool osmo_sdp_codec_list_is_empty(const struct osmo_sdp_codec_list *codec_list);

View File

@@ -0,0 +1,18 @@
/* Internal header for non-public API shared across .c files */
#pragma once
struct token {
const char *start;
const char *end;
};
/* Copy a string from [start,end[, return as talloc allocated under ctx in *dst.
* If *dst is non-NULL, talloc_free(*dst) first. */
void token_copy(void *ctx, char **dst, const struct token *t);
const char *token_chr(const struct token *src, char c);
const char *token_chrs(const struct token *src, const char *chrs);
void token_next(struct token *t, const char *str, const char *end, const char *separators);
const char *token_to_int64(int64_t *result, const struct token *t, int base, int min_val, int max_val);
const char *token_to_int(int *result, const struct token *t, int base, int min_val, int max_val);

View File

@@ -0,0 +1,105 @@
/* Public API for SDP message encoding and decoding */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/sdp/sdp_codec.h>
#include <osmocom/sdp/sdp_codec_list.h>
/* Media Direction Attributes "a=recvonly", "a=sendrecv", "a=sendonly", "a=inactive" RFC-8866 6.7. */
enum osmo_sdp_media_direcion_e {
OSMO_SDP_MDIR_UNSET = 0,
OSMO_SDP_MDIR_RECVONLY = 1,
OSMO_SDP_MDIR_SENDRECV = 2,
OSMO_SDP_MDIR_SENDONLY = 3,
OSMO_SDP_MDIR_INACTIVE = 4,
};
/* Session Description Protocol (SDP) message, RFC-8866. */
struct osmo_sdp_msg {
/* 5.2 Origin ("o="). */
struct {
struct osmo_sockaddr_str addr;
char *username;
char *sess_id;
char *sess_version;
} origin;
/* 5.3 Session Name ("s="). */
char *session_name;
/* 5.7 Connection Information ("c=") and port from 5.14 Media Descriptions ("m="). */
struct osmo_sockaddr_str rtp;
/* 5.9. Time Active ("t="). */
struct {
int64_t start;
int64_t stop;
} time_active;
/* 6.4 "a=ptime:<val>". */
unsigned int ptime;
/* 6.7 "a=sendrecv"... */
enum osmo_sdp_media_direcion_e media_direction;
/* List of codecs defined in the SDP message.
* This should not be NULL -- osmo_sdp_msg_alloc() returns an empty osmo_sdp_codec_list instance, ready for
* adding codecs.
* Combination of:
* - payload_type numbers from 5.14 Media Descriptions ("m="),
* - 6.6 "a=rtpmap",
* - 6.15 Format Parameters "a=fmtp".
*/
struct osmo_sdp_codec_list *codecs;
/* For future extension, always set to false. */
bool v2;
};
struct osmo_sdp_msg_decode_ret {
int rc;
/* If rc != 0 */
struct {
/* Point at the position that caused the error, in the src string. */
const char *at_input_str;
/* Nr of characters at *src_str that are relevant to the error. */
size_t at_input_str_len;
} error;
/* Pointer to the remaining part of src after parsing one SDP message.
* For example, in MGCP, there may be multiple SDP messages concatenated. */
const char *src_remain;
};
struct osmo_sdp_msg *osmo_sdp_msg_alloc(void *ctx);
struct osmo_sdp_msg *osmo_sdp_msg_decode(void *ctx, const char *src_str, int src_str_len,
struct osmo_sdp_msg_decode_ret *ret);
int osmo_sdp_msg_encode_buf(char *dst, size_t dst_size, const struct osmo_sdp_msg *sdp);
char *osmo_sdp_msg_encode_c(void *ctx, const struct osmo_sdp_msg *sdp);
int osmo_sdp_msg_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_msg *sdp, bool summarize);
char *osmo_sdp_msg_to_str_c(void *ctx, const struct osmo_sdp_msg *sdp, bool summarize);

View File

@@ -0,0 +1,39 @@
/* Central definition of string tokens used for parsing and composing SDP messages */
#pragma once
#define OSMO_SDP_STR_MEDIA "m"
#define OSMO_SDP_STR_ATTRIB "a"
#define OSMO_SDP_STR_TIME_ACTIVE "t"
#define OSMO_SDP_STR_RTPMAP "rtpmap"
#define OSMO_SDP_STR_FMTP "fmtp"
#define OSMO_SDP_STR_PTIME "ptime"
/*! "a=foo:" */
#define OSMO_SDP_A_PREFIX(STR) OSMO_SDP_STR_ATTRIB "=" STR ":"
/*! "a=fmtp:" */
#define OSMO_SDP_STR_A_FMTP OSMO_SDP_A_PREFIX(OSMO_SDP_STR_FMTP)
/* Media Direction Attributes "a=recvonly", "a=sendrecv", "a=sendonly", "a=inactive" RFC-8866 6.7. */
#define OSMO_SDP_STR_RECVONLY "recvonly"
#define OSMO_SDP_STR_SENDRECV "sendrecv"
#define OSMO_SDP_STR_SENDONLY "sendonly"
#define OSMO_SDP_STR_INACTIVE "inactive"
/* AMR related tokens */
#define OSMO_SDP_STR_AMR_OCTET_ALIGN "octet-align"
/*! "octet-align=1" */
#define OSMO_SDP_STR_AMR_OCTET_ALIGN_1 OSMO_SDP_STR_AMR_OCTET_ALIGN "=1"
/*! "octet-align=0".
* According to spec [1], "octet-align=0" is identical to omitting 'octet-align' entirely. In Osmocom practice, whether
* or not "octet-align=0" is present can make a big difference for osmo-mgw versions 1.12 and older, which do not heed
* [1].
*
* spec [1]: RFC4867, see details in description of osmo_sdp_fmtp_amr_is_octet_aligned().
*/
#define OSMO_SDP_STR_AMR_OCTET_ALIGN_0 OSMO_SDP_STR_AMR_OCTET_ALIGN "=0"

10
libosmo-sdp.pc.in Normal file
View File

@@ -0,0 +1,10 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: Osmocom Session Description Protocol coding library
Description: C Utility Library
Version: @VERSION@
Libs: -L${libdir} -losmo-sdp
Cflags: -I${includedir}/

View File

@@ -15,6 +15,7 @@ AM_CFLAGS = \
# Libraries
SUBDIRS = \
libosmo-sdp \
libosmo-mgcp-client \
libosmo-mgcp \
$(NULL)

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=13:0:1
MGCP_CLIENT_LIBVERSION=14:0:0
lib_LTLIBRARIES = \
libosmo-mgcp-client.la \
@@ -31,7 +31,6 @@ libosmo_mgcp_client_la_SOURCES = \
mgcp_client_fsm.c \
mgcp_client_endpoint_fsm.c \
mgcp_client_pool.c \
fmtp.c \
$(NULL)
libosmo_mgcp_client_la_LDFLAGS = \

View File

@@ -105,6 +105,94 @@ enum mgcp_codecs map_str_to_codec(const char *str)
return -1;
}
/* Check the ptmap for illegal mappings */
static int check_ptmap(const struct ptmap *ptmap)
{
/* Check if there are mappings that leave the IANA assigned dynamic
* payload type range. Under normal conditions such mappings should
* not occur */
/* Its ok to have a 1:1 mapping in the statically defined
* range, this won't hurt */
if (ptmap->codec == ptmap->pt)
return 0;
if (ptmap->codec < 96 || ptmap->codec > 127)
goto error;
if (ptmap->pt < 96 || ptmap->pt > 127)
goto error;
return 0;
error:
LOGP(DLMGCP, LOGL_ERROR,
"ptmap contains illegal mapping: codec=%u maps to pt=%u\n",
ptmap->codec, ptmap->pt);
return -1;
}
/*! Map a codec to a payload type.
* \ptmap[in] payload pointer to payload type map with specified payload types.
* \ptmap[in] ptmap_len length of the payload type map.
* \ptmap[in] codec the codec for which the payload type should be looked up.
* \returns assigned payload type */
unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len,
enum mgcp_codecs codec)
{
unsigned int i;
/*! Note: If the payload type map is empty or the codec is not found
* in the map, then a 1:1 mapping is performed. If the codec falls
* into the statically defined range or if the mapping table isself
* tries to map to the statically defined range, then the mapping
* is also ignored and a 1:1 mapping is performed instead. */
/* we may return the codec directly since enum mgcp_codecs directly
* corresponds to the statically assigned payload types */
if (codec < 96 || codec > 127)
return codec;
for (i = 0; i < ptmap_len; i++) {
/* Skip illegal map entries */
if (check_ptmap(ptmap) == 0 && ptmap->codec == codec)
return ptmap->pt;
ptmap++;
}
/* If nothing is found, do not perform any mapping */
return codec;
}
/*! Map a payload type to a codec.
* \ptmap[in] payload pointer to payload type map with specified payload types.
* \ptmap[in] ptmap_len length of the payload type map.
* \ptmap[in] payload type for which the codec should be looked up.
* \returns codec that corresponds to the specified payload type */
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
unsigned int pt)
{
unsigned int i;
/*! Note: If the payload type map is empty or the payload type is not
* found in the map, then a 1:1 mapping is performed. If the payload
* type falls into the statically defined range or if the mapping
* table isself tries to map to the statically defined range, then
* the mapping is also ignored and a 1:1 mapping is performed
* instead. */
/* See also note in map_codec_to_pt() */
if (pt < 96 || pt > 127)
return pt;
for (i = 0; i < ptmap_len; i++) {
if (check_ptmap(ptmap) == 0 && ptmap->pt == pt)
return ptmap->codec;
ptmap++;
}
/* If nothing is found, do not perform any mapping */
return pt;
}
static void _mgcp_client_conf_init(struct mgcp_client_conf *conf)
{
/* NULL and -1 default to MGCP_CLIENT_*_DEFAULT values */
@@ -300,12 +388,11 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
{
unsigned int pt;
unsigned int i;
char codec_resp[256];
char codec_resp[64];
int rc;
#define A_PTIME "a=ptime:"
#define A_RTPMAP "a=rtpmap:"
#define A_FMTP "a=fmtp:"
if (osmo_str_startswith(line, A_PTIME)) {
if (sscanf(line, A_PTIME "%u", &r->ptime) != 1) {
@@ -314,7 +401,7 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
return -EINVAL;
}
} else if (osmo_str_startswith(line, A_RTPMAP)) {
if (sscanf(line, A_RTPMAP "%d %255s", &pt, codec_resp) != 2) {
if (sscanf(line, A_RTPMAP "%d %63s", &pt, codec_resp) != 2) {
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse SDP parameter, invalid rtpmap: %s\n", osmo_quote_str(line, -1));
return -EINVAL;
@@ -353,42 +440,6 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
.codec = rc,
};
r->ptmap_len++;
} else if (osmo_str_startswith(line, A_FMTP)) {
if (sscanf(line, A_FMTP "%d %255s", &pt, codec_resp) != 2) {
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse SDP parameter, invalid fmtp: %s\n", osmo_quote_str(line, -1));
return -EINVAL;
}
/* Earlier, a line like "m=audio 16002 RTP/AVP 98 112 3" established the desired order of payloads, now
* enrich it with actual codec information provided by "a=rtpmap:..." entries.
* For each, find the entry with the right pt number and add the info there. */
for (i = 0; i < r->ptmap_len; i++) {
if (r->ptmap[i].pt != pt)
continue;
OSMO_STRLCPY_ARRAY(r->ptmap[r->ptmap_len].fmtp, codec_resp);
return 0;
}
/* No entry was found. This is an error in the MGCP protocol, but let's just add another entry
* anyway, to not make it look like it was never there. */
LOGP(DLMGCP, LOGL_ERROR,
"error in MGCP message: 'a=fmtp:%u' has no matching entry in 'm=audio ... %u'\n",
pt, pt);
if (r->ptmap_len >= ARRAY_SIZE(r->ptmap)) {
LOGP(DLMGCP, LOGL_ERROR,
"cannot parse all codecs: can only store up to %zu rtpmap entries.\n",
ARRAY_SIZE(r->ptmap));
return -ENOSPC;
}
r->ptmap[r->ptmap_len] = (struct ptmap){
.pt = pt,
.codec = -1,
};
OSMO_STRLCPY_ARRAY(r->ptmap[r->ptmap_len].fmtp, codec_resp);
r->ptmap_len++;
}
return 0;
@@ -677,7 +728,7 @@ static struct mgcp_response_pending *mgcp_client_response_pending_get(
/* Feed an MGCP message into the receive processing.
* Parse the head and call any callback registered for the transaction id found
* in the MGCP message. This is normally called directly from the internal
* mgcp_do_read that reads from the socket connected to the MGCP gateway. This
* mgcp_read_cb that reads from the socket connected to the MGCP gateway. This
* function is published mainly to be able to feed data from the test suite.
*/
int mgcp_client_rx(struct mgcp_client *mgcp, struct msgb *msg)
@@ -730,55 +781,58 @@ error:
return rc;
}
static int mgcp_do_read(struct osmo_fd *fd)
static void mgcp_read_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg)
{
struct mgcp_client *mgcp = fd->data;
struct msgb *msg;
int ret;
struct mgcp_client *mgcp = osmo_iofd_get_data(iofd);
msg = msgb_alloc_headroom(4096, 128, "mgcp_from_gw");
if (!msg) {
LOGPMGW(mgcp, LOGL_ERROR, "Failed to allocate MGCP message.\n");
return -1;
}
/* msgb_tailroom() is basically (4096 - 128); -1 is for '\0' */
ret = read(fd->fd, msg->data, msgb_tailroom(msg) - 1);
if (ret <= 0) {
if (res <= 0) {
char errbuf[128] = "";
strerror_r(-res, errbuf, sizeof(errbuf));
LOGPMGW(mgcp, LOGL_ERROR, "Failed to read: %s: %d='%s'\n",
osmo_sock_get_name2(fd->fd), errno, strerror(errno));
osmo_iofd_get_name(iofd), res, errbuf);
msgb_free(msg);
return -1;
return;
}
msg->l2h = msgb_put(msg, ret);
ret = mgcp_client_rx(mgcp, msg);
msg->l2h = msg->head;
mgcp_client_rx(mgcp, msg);
talloc_free(msg);
return ret;
}
static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
static int mgcp_do_write(struct mgcp_client *mgcp, struct msgb *msg)
{
int ret;
struct mgcp_client *mgcp = fd->data;
LOGPMGW(mgcp, LOGL_DEBUG, "Tx MGCP: %s: len=%u '%s'...\n",
osmo_sock_get_name2(fd->fd), msg->len,
osmo_iofd_get_name(mgcp->iofd), msg->len,
osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len)));
ret = write(fd->fd, msg->data, msg->len);
if (OSMO_UNLIKELY(ret != msg->len))
LOGPMGW(mgcp, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n",
osmo_sock_get_name2(fd->fd), errno, strerror(errno),
msg->len, osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len)));
ret = osmo_iofd_write_msgb(mgcp->iofd, msg);
if (ret < 0)
msgb_free(msg);
/* Re-arm the keepalive Tx timer: */
if (mgcp->actual.keepalive.req_interval_sec > 0)
osmo_timer_schedule(&mgcp->keepalive_tx_timer, mgcp->actual.keepalive.req_interval_sec, 0);
return ret;
}
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, errbuf,
msg->len, osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len)));
}
}
static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgcp, const char *name)
{
static char endpoint[MGCP_ENDPOINT_MAXLEN];
@@ -810,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);
}
@@ -823,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);
}
@@ -890,11 +952,6 @@ struct mgcp_client *mgcp_client_init(void *ctx,
if (conf->description)
mgcp->actual.description = talloc_strdup(mgcp, conf->description);
osmo_wqueue_init(&mgcp->wq, 1024);
mgcp->wq.read_cb = mgcp_do_read;
mgcp->wq.write_cb = mgcp_do_write;
osmo_fd_setup(&mgcp->wq.bfd, -1, OSMO_FD_READ, osmo_wqueue_bfd_cb, mgcp, 0);
memcpy(&mgcp->actual.keepalive, &conf->keepalive, sizeof(conf->keepalive));
osmo_timer_setup(&mgcp->keepalive_tx_timer, mgcp_client_keepalive_tx_timer_cb, mgcp);
osmo_timer_setup(&mgcp->keepalive_rx_timer, mgcp_client_keepalive_rx_timer_cb, mgcp);
@@ -902,6 +959,11 @@ struct mgcp_client *mgcp_client_init(void *ctx,
return mgcp;
}
static const struct osmo_io_ops mgcp_clnt_ioops = {
.read_cb = mgcp_read_cb,
.write_cb = mgcp_write_cb,
};
/*! Initialize client connection (opens socket)
* \param[in,out] mgcp MGCP client descriptor.
* \returns 0 on success, -EINVAL on error. */
@@ -917,19 +979,28 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
return -EINVAL;
}
rc = osmo_sock_init2_ofd(&mgcp->wq.bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
rc = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
if (rc < 0) {
LOGPMGW(mgcp, LOGL_FATAL,
"Failed to initialize socket %s:%u -> %s:%u for MGW: %s\n",
mgcp->actual.local_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.local_port,
mgcp->actual.remote_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.remote_port,
strerror(errno));
goto error_close_fd;
goto error_free;
}
LOGPMGW(mgcp, LOGL_INFO, "MGW connection: %s\n", osmo_sock_get_name2(mgcp->wq.bfd.fd));
mgcp->iofd = osmo_iofd_setup(mgcp, rc, osmo_sock_get_name2(rc), OSMO_IO_FD_MODE_READ_WRITE,
&mgcp_clnt_ioops, mgcp);
if (!mgcp->iofd)
goto error_close_fd;
LOGPMGW(mgcp, LOGL_INFO, "MGW connection: %s\n", osmo_iofd_get_name(mgcp->iofd));
osmo_iofd_register(mgcp->iofd, -1);
osmo_iofd_set_alloc_info(mgcp->iofd, 4096, 128);
osmo_iofd_set_txqueue_max_length(mgcp->iofd, 1024);
/* If configured, send a DLCX message to the endpoints that are configured to
* be reset on startup. Usually this is a wildcarded endpoint. */
@@ -955,9 +1026,10 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
osmo_timer_schedule(&mgcp->keepalive_rx_timer, mgcp->actual.keepalive.timeout_sec, 0);
return 0;
error_close_fd:
close(mgcp->wq.bfd.fd);
mgcp->wq.bfd.fd = -1;
close(rc);
error_free:
return rc;
}
@@ -974,8 +1046,6 @@ int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports)
* \returns 0 on success, -EINVAL on error. */
void mgcp_client_disconnect(struct mgcp_client *mgcp)
{
struct osmo_wqueue *wq;
if (!mgcp) {
LOGP(DLMGCP, LOGL_FATAL, "MGCP client not initialized properly\n");
return;
@@ -986,13 +1056,10 @@ void mgcp_client_disconnect(struct mgcp_client *mgcp)
osmo_timer_del(&mgcp->keepalive_tx_timer);
mgcp->conn_up = false;
wq = &mgcp->wq;
osmo_wqueue_clear(wq);
LOGPMGW(mgcp, LOGL_INFO, "MGCP association: %s -- closed!\n", osmo_sock_get_name2(wq->bfd.fd));
if (osmo_fd_is_registered(&wq->bfd))
osmo_fd_unregister(&wq->bfd);
close(wq->bfd.fd);
wq->bfd.fd = -1;
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.
@@ -1146,10 +1213,9 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
goto mgcp_tx_error;
}
rc = osmo_wqueue_enqueue(&mgcp->wq, msg);
rc = mgcp_do_write(mgcp, msg);
if (rc) {
LOGPMGW(mgcp, LOGL_FATAL, "Could not queue message to MGW\n");
msgb_free(msg);
goto mgcp_tx_error;
} else
LOGPMGW(mgcp, LOGL_DEBUG, "Queued %u bytes for MGW\n",
@@ -1254,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)
{
@@ -1262,6 +1358,8 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
int local_ip_family, audio_ip_family;
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) { \
@@ -1277,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;
@@ -1313,33 +1409,28 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
MSGB_PRINTF_OR_RET("t=0 0\r\n");
/* Add RTP address port and codecs */
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT) {
if (mgcp_msg->audio_port == 0) {
audio_port = 0;
if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT)) {
audio_port = mgcp_msg->audio_port;
if (!audio_port) {
LOGPMGW(mgcp, LOGL_ERROR,
"Invalid port number, can not generate MGCP message\n");
return -EINVAL;
}
MSGB_PRINTF_OR_RET("m=audio %u RTP/AVP", mgcp_msg->audio_port);
for (i = 0; i < mgcp_msg->ptmap_len; i++)
MSGB_PRINTF_OR_RET(" %u", mgcp_msg->ptmap[i].pt);
MSGB_PRINTF_OR_RET("\r\n");
}
MSGB_PRINTF_OR_RET("m=audio %u RTP/AVP", audio_port);
for (i = 0; i < mgcp_msg->ptmap_len; i++)
MSGB_PRINTF_OR_RET(" %u", mgcp_msg->ptmap[i].pt);
MSGB_PRINTF_OR_RET("\r\n");
/* Add optional codec parameters (fmtp) */
for (i = 0; i < mgcp_msg->ptmap_len; i++) {
/* Add fmtp string, if any is set. */
if (mgcp_msg->ptmap[i].fmtp[0]) {
MSGB_PRINTF_OR_RET("a=fmtp:%u %s\r\n", mgcp_msg->ptmap[i].pt, mgcp_msg->ptmap[i].fmtp);
continue;
}
/* LEGACY COMPAT. Fill in fmtp with the legacy mgcp_msg->param, if any, when no individual fmtp is set
* on the codec. We only ever supported AMR fmtp in mgcp_msg->param. */
if (mgcp_msg->param_present
&& (mgcp_msg->ptmap[i].codec == CODEC_AMR_8000_1
|| mgcp_msg->ptmap[i].codec == CODEC_AMRWB_16000_1)) {
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)
@@ -1577,8 +1668,5 @@ int ptmap_cmp(const struct ptmap *a, const struct ptmap *b)
rc = OSMO_CMP(a->codec, b->codec);
if (rc)
return rc;
rc = OSMO_CMP(a->pt, b->pt);
if (rc)
return rc;
return strcmp(a->fmtp, b->fmtp);
return OSMO_CMP(a->pt, b->pt);
}

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);
@@ -624,17 +628,6 @@ static struct osmo_fsm fsm_mgcp_client = {
.log_subsys = DLMGCP,
};
/*! allocate struct to hold the description of an MGCP connection peer.
* \param[in] ctx talloc context.
* \returns newly-allocated and initialized struct mgcp_conn_peer. */
struct mgcp_conn_peer *mgcp_conn_peer_alloc(void *ctx)
{
struct mgcp_conn_peer *peer;
peer = talloc_zero(ctx, struct mgcp_conn_peer);
OSMO_ASSERT(peer);
return peer;
}
/* Provide backwards compat for deprecated conn_peer->codecs[]: when the caller passes in an mgcp_conn_peer instance
* that has codecs[] set, apply it to ptmap[] instead. */
static void mgcp_conn_peer_compat(struct mgcp_conn_peer *conn_peer)

View File

@@ -322,7 +322,7 @@ DEFUN(cfg_mgw_mgw_keepalive_req_interval,
/* If client already exists, apply the change immediately if possible: */
mgcp->actual.keepalive.req_interval_sec = atoi(argv[0]);
if (mgcp->wq.bfd.fd != -1) { /* UDP MGCP socket connected */
if (mgcp->iofd) { /* UDP MGCP socket connected */
if (mgcp->actual.keepalive.req_interval_sec > 0) {
/* Re-schedule: */
osmo_timer_schedule(&mgcp->keepalive_tx_timer, mgcp->actual.keepalive.req_interval_sec, 0);
@@ -375,7 +375,7 @@ DEFUN(cfg_mgw_mgw_keepalive_timeout,
/* If client already exists, apply the change immediately if possible: */
mgcp->actual.keepalive.timeout_sec = atoi(argv[0]);
if (mgcp->wq.bfd.fd != -1) { /* UDP MGCP socket connected */
if (mgcp->iofd) { /* UDP MGCP socket connected */
if (mgcp->actual.keepalive.timeout_sec > 0) {
/* Re-schedule: */
osmo_timer_schedule(&mgcp->keepalive_rx_timer, mgcp->actual.keepalive.timeout_sec, 0);
@@ -680,7 +680,7 @@ DEFUN(mgw_show, mgw_show_cmd, "show mgw-pool", SHOW_STR "Display information abo
const struct mgcp_client *cli = pool_member->client;
vty_out(vty, "%% MGW %s%s", mgcp_client_pool_member_name(pool_member), VTY_NEWLINE);
vty_out(vty, "%% MGCP link: %s,%s%s",
cli && cli->wq.bfd.fd != -1 ? "connected" : "disconnected",
cli && cli->iofd ? "connected" : "disconnected",
cli && cli->conn_up ?
((cli->actual.keepalive.timeout_sec > 0) ? "UP" : "MAYBE") :
"DOWN",

View File

@@ -19,15 +19,15 @@ AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
$(NULL)
noinst_LIBRARIES = \
libosmo-mgcp.a \
noinst_LTLIBRARIES = \
libosmo-mgcp.la \
$(NULL)
noinst_HEADERS = \
g711common.h \
$(NULL)
libosmo_mgcp_a_SOURCES = \
libosmo_mgcp_la_SOURCES = \
mgcp_protocol.c \
mgcp_network.c \
mgcp_vty.c \
@@ -42,5 +42,13 @@ libosmo_mgcp_a_SOURCES = \
mgcp_ratectr.c \
mgcp_e1.c \
mgcp_iuup.c \
$(top_srcdir)/src/libosmo-mgcp-client/fmtp.c \
$(NULL)
libosmo_mgcp_la_LIBADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOTRAU_LIBS) \
$(NULL)

View File

@@ -24,7 +24,6 @@
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp_client/fmtp.h>
#include <errno.h>
/* Helper function to dump codec information of a specified codec to a printable
@@ -117,9 +116,9 @@ void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
* \param[out] conn related rtp-connection.
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
* \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
* \param[in] fmtp optional codec parameters (set to NULL when unused).
* \param[in] param optional codec parameters (set to NULL when unused).
* \returns 0 on success, -EINVAL on failure. */
int mgcp_codec_add2(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const char *fmtp)
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
{
int rate;
int channels;
@@ -262,16 +261,12 @@ int mgcp_codec_add2(struct mgcp_conn_rtp *conn, int payload_type, const char *au
}
}
if (fmtp) {
OSMO_STRLCPY_ARRAY(codec->fmtp, fmtp);
if (strlen(codec->fmtp) != strlen(fmtp)) {
LOGP(DLMGCP, LOGL_ERROR, "fmtp too long: %zu > %zu\n", strlen(fmtp), strlen(codec->fmtp));
/* let's just hope what is there is still useful, worst case the call's audio doesn't work */
}
}
/* legacy */
codec->param_present = false;
/* Copy over optional codec parameters */
if (param) {
codec->param = *param;
codec->param_present = true;
} else
codec->param_present = false;
conn->end.codecs_assigned++;
return 0;
@@ -281,33 +276,23 @@ error:
return -EINVAL;
}
/*! Legacy compat, use mgcp_codec_add2() instead to be able to pass any fmtp besides AMR octet-align=1.
* Add codec configuration depending on payload type and/or codec name. This
* function uses the input parameters to extrapolate the full codec information.
* \param[out] codec configuration (caller provided memory).
* \param[out] conn related rtp-connection.
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
* \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
* \param[in] param optional codec parameters (set to NULL when unused).
* \returns 0 on success, -EINVAL on failure. */
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
{
const char *fmtp = NULL;
if (param && param->amr_octet_aligned_present)
fmtp = OSMO_SDP_AMR_SET_OCTET_ALIGN(param->amr_octet_aligned);
return mgcp_codec_add2(conn, payload_type, audio_name, fmtp);
}
/* Return true if octet-aligned is set in the given codec. Default to octet-aligned=0, i.e. bandwidth-efficient mode.
* See RFC4867 "RTP Payload Format for AMR and AMR-WB" sections "8.1. AMR Media Type Registration" and "8.2. AMR-WB
* Media Type Registration":
*
* octet-align: Permissible values are 0 and 1. If 1, octet-aligned
* operation SHALL be used. If 0 or if not present,
* bandwidth-efficient operation is employed.
*
* https://tools.ietf.org/html/rfc4867
*/
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
{
/* Legacy */
if (!codec->fmtp[0]
&& codec->param_present
&& codec->param.amr_octet_aligned_present)
return codec->param.amr_octet_aligned;
return osmo_sdp_fmtp_amr_is_octet_aligned(codec->fmtp);
if (!codec->param_present)
return false;
if (!codec->param.amr_octet_aligned_present)
return false;
return codec->param.amr_octet_aligned;
}
/* Compare two codecs, all parameters must match up */
@@ -429,26 +414,6 @@ static struct mgcp_rtp_codec *codec_find_convertible(struct mgcp_conn_rtp *conn,
int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst)
{
unsigned int i;
if (log_check_level(DLMGCP, LOGL_DEBUG)) {
LOGP(DLMGCP, LOGL_DEBUG, "%s(): src.codecs_assigned=%d dst.codecs_assigned=%d\n",
__func__,
conn_src ? conn_src->end.codecs_assigned : 0,
conn_dst ? conn_dst->end.codecs_assigned : 0);
if (conn_src) {
for (i = 0; i < conn_src->end.codecs_assigned; i++) {
struct mgcp_rtp_codec *c = &conn_src->end.codecs[i];
LOGP(DLMGCP, LOGL_DEBUG, "src.codecs[%d]: %d %s %s %s\n",
i, c->payload_type, c->audio_name, c->subtype_name, c->fmtp);
}
}
if (conn_dst) {
for (i = 0; i < conn_dst->end.codecs_assigned; i++) {
struct mgcp_rtp_codec *c = &conn_dst->end.codecs[i];
LOGP(DLMGCP, LOGL_DEBUG, "dst.codecs[%d]: %d %s %s %s\n",
i, c->payload_type, c->audio_name, c->subtype_name, c->fmtp);
}
}
}
/* 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:
@@ -490,7 +455,6 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn
}
}
LOGP(DLMGCP, LOGL_ERROR, "no matching codec found\n");
if (conn_dst->end.codecs_assigned)
conn_dst->end.codec = &conn_dst->end.codecs[0];
else
@@ -507,13 +471,13 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn
/* Check if the codec has a specific AMR mode (octet-aligned or bandwith-efficient) set. */
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec)
{
if (!codec->fmtp[0]) {
/* Legacy */
return codec->param_present && codec->param.amr_octet_aligned_present;
}
/* Just check for presence, not the actual value. */
return osmo_sdp_fmtp_get_val(NULL, 0, codec->fmtp, OSMO_SDP_NAME_AMR_OCTET_ALIGN);
if (codec->param_present == false)
return false;
if (!codec->param.amr_octet_aligned_present)
return false;
if (strcmp(codec->subtype_name, "AMR") != 0)
return false;
return true;
}
/* Find the payload type number configured for a specific codec by SDP.

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;
}
@@ -106,8 +106,8 @@ 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.fd = -1;
end->rtcp.fd = -1;
end->rtp = NULL;
end->rtcp = NULL;
memset(&end->addr, 0, sizeof(end->addr));
end->rtcp_port = 0;
@@ -137,7 +137,7 @@ 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_free_port(&conn_rtp->end);
rate_ctr_group_free(conn_rtp->ctrg);
mgcp_codec_reset_all(conn_rtp);
}
@@ -146,7 +146,7 @@ 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 +212,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 +234,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 +258,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,35 +302,31 @@ 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) };
int i;
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;
}
for (i = 0; i < conn->u.rtp.end.codecs_assigned; i++) {
struct mgcp_rtp_codec *c = &conn->u.rtp.end.codecs[i];
OSMO_STRBUF_PRINTF(sb, " %s#%d%s", c->subtype_name, c->payload_type, c->fmtp);
}
OSMO_STRBUF_PRINTF(sb, ")");
break;
@@ -420,16 +361,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

@@ -301,7 +301,6 @@ static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int
mgcp_send(endp, 1, NULL, msg, &conn_dst->u.rtp, &conn_dst->u.rtp);
msgb_free(msg);
return;
skip:
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_RX_FAIL_CTR));
@@ -325,7 +324,7 @@ static void e1_send_ts_frame(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
msgb_length(msg) > DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
#endif
/* Hand data over to the E1 stack */
msgb_enqueue(&ts->raw.tx_queue, msg);
e1inp_ts_send_raw(ts, msg);
return;
}
@@ -466,11 +465,21 @@ static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name,
{
if (strcmp(sdp_subtype_name, "GSM") == 0)
return OSMO_TRAU16_FT_FR;
else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
return OSMO_TRAU16_FT_EFR;
else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
return OSMO_TRAU16_FT_HR;
else if (strcmp(sdp_subtype_name, "AMR") == 0) {
if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0) {
if (i460_rate == OSMO_I460_RATE_16k)
return OSMO_TRAU16_FT_HR;
if (i460_rate == OSMO_I460_RATE_8k)
return OSMO_TRAU8_SPEECH;
LOGPENDP(endp, DE1, LOGL_ERROR,
"E1-TRAU-TX: unsupported or illegal I.460 rate for HR\n");
return OSMO_TRAU_FT_NONE;
}
if (strcmp(sdp_subtype_name, "AMR") == 0) {
if (i460_rate == OSMO_I460_RATE_8k) {
switch (amr_ft) {
case AMR_4_75:
@@ -488,11 +497,11 @@ static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name,
}
}
return OSMO_TRAU16_FT_AMR;
} else {
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
sdp_subtype_name);
return OSMO_TRAU_FT_NONE;
}
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
sdp_subtype_name);
return OSMO_TRAU_FT_NONE;
}
/* Determine a suitable TRAU frame type for a given codec */
@@ -501,11 +510,21 @@ static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name
{
if (strcmp(sdp_subtype_name, "GSM") == 0)
return OSMO_TRAU_SYNCP_16_FR_EFR;
else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
return OSMO_TRAU_SYNCP_16_FR_EFR;
else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
return OSMO_TRAU_SYNCP_8_HR;
else if (strcmp(sdp_subtype_name, "AMR") == 0) {
if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0) {
if (i460_rate == OSMO_I460_RATE_16k)
return OSMO_TRAU_SYNCP_16_FR_EFR;
if (i460_rate == OSMO_I460_RATE_8k)
return OSMO_TRAU_SYNCP_8_HR;
LOGPENDP(endp, DE1, LOGL_ERROR,
"E1-TRAU-TX: unsupported or illegal I.460 rate for HR\n");
return OSMO_TRAU_SYNCP_16_FR_EFR;
}
if (strcmp(sdp_subtype_name, "AMR") == 0) {
if (i460_rate == OSMO_I460_RATE_8k) {
switch (amr_ft) {
case AMR_4_75:
@@ -523,11 +542,11 @@ static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name
}
}
return OSMO_TRAU_SYNCP_16_FR_EFR;
} else {
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
sdp_subtype_name);
return OSMO_TRAU_SYNCP_16_FR_EFR;
}
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
sdp_subtype_name);
return OSMO_TRAU_SYNCP_16_FR_EFR;
}
/* Find out if a given TRAU frame type is AMR */
@@ -631,15 +650,17 @@ int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8
void 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.codec;
OSMO_ASSERT(codec);
/* Update codec information */

View File

@@ -58,7 +58,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 +446,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 +552,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 +569,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.
@@ -655,6 +673,97 @@ 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;
}
/*! release endpoint, all open connections are closed.
* \param[in] endp endpoint to release */
void mgcp_endp_release(struct mgcp_endpoint *endp)
@@ -665,7 +774,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 */

View File

@@ -311,7 +311,6 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg
};
rc = mgcp_send(conn_rtp_dst->conn->endp, true, NULL, msg, conn_rtp_src, conn_rtp_dst);
msgb_free(msg);
return rc;
}
@@ -469,7 +468,7 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
struct rtp_hdr *hdr = (struct rtp_hdr *)msgb_data(msg);
int buflen = msgb_length(msg);
char *dest_name;
int len;
int rc;
OSMO_ASSERT(conn_src);
OSMO_ASSERT(conn_dst);
@@ -506,6 +505,7 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate));
hdr->sequence = osmo_htons(rtp_state->alt_rtp_tx_sequence);
hdr->ssrc = rtp_state->alt_rtp_tx_ssrc;
rtp_state->alt_rtp_tx_sequence++;
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"process/send IuUP to %s %s rtp_port:%u rtcp_port:%u\n",
@@ -513,19 +513,17 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port));
/* Forward a copy of the RTP data to a debug ip/port */
forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out,
msg);
forward_data_tap(rtp_end->rtp, &conn_src->tap_out, msg);
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, (char *)hdr, buflen);
rc = mgcp_udp_send(rtp_end->rtp, &rtp_end->addr, (char *)hdr, buflen);
if (len <= 0)
return len;
if (rc < 0)
return rc;
rtpconn_rate_ctr_add(conn_dst, endp, RTP_PACKETS_TX_CTR, 1);
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
rtp_state->alt_rtp_tx_sequence++;
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, buflen);
return len;
return 0;
}
/* Received TNL primitive from IuUP layer FSM, transmit it further down to the
@@ -546,7 +544,7 @@ 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,
@@ -640,7 +638,7 @@ free_ret:
}
/* Build IuUP RNL Data primitive from msg containing an incoming RTP pkt from
* peer and send it down the IuUP layer towards the destination as IuUP/RTP: */
* peer and send it down the IuUP layer towards the destination as IuUP/RTP. Takes ownership of msg. */
int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg)
{
struct osmo_iuup_rnl_prim *irp;

View File

@@ -76,61 +76,24 @@ 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;
}
/* Special handling for RTP connections */
if (conn->type == MGCP_CONN_TYPE_RTP) {
conn->u.rtp.end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY);
}
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
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);
}
/* 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;
return ret;
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;
}
/*! Analyze and parse the the hader of an MGCP messeage string.
@@ -294,7 +257,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

@@ -4,6 +4,7 @@
/*
* (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
@@ -70,14 +71,20 @@ void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *
rtpconn_rate_ctr_add(conn_rtp, endp, id, 1);
}
static int rx_rtp(struct msgb *msg);
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end)
/* wrapper around libosmocore msgb_copy_c, which [at least before libosmocore.git Change-Id
* I68328adb952ca8833ba047cb3b49ccc6f8a1f1b5] doesn't copy the cb */
static inline struct msgb *mgw_msgb_copy_c(void *ctx, struct msgb *msg, const char *name)
{
return (osmo_sockaddr_port(&rtp_end->addr.u.sa) != 0) &&
(osmo_sockaddr_is_any(&rtp_end->addr) == 0);
struct msgb *msg2 = msgb_copy_c(ctx, msg, name);
if (OSMO_UNLIKELY(!msg2))
return NULL;
memcpy(msg2->cb, msg->cb, sizeof(msg2->cb));
return msg2;
}
static int rx_rtp(struct msgb *msg);
/*! 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.
@@ -404,15 +411,12 @@ static int align_rtp_timestamp_offset(const struct mgcp_endpoint *endp,
/*! dummy callback to disable transcoding (see also cfg->rtp_processing_cb).
* \param[in] associated endpoint.
* \param[in] destination RTP end.
* \param[in,out] pointer to buffer with voice data.
* \param[in] voice data length.
* \param[in] maximum size of caller provided voice data buffer.
* \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,
char *data, int *len, int buf_size)
struct msgb *msg)
{
LOGPENDP(endp, DRTP, LOGL_DEBUG, "transcoding disabled\n");
return 0;
}
@@ -785,16 +789,18 @@ static int amr_oa_check(char *data, int len)
/* Forward data to a debug tap. This is debug function that is intended for
* debugging the voice traffic with tools like gstreamer */
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
void forward_data_tap(struct osmo_io_fd *iofd, struct mgcp_rtp_tap *tap, struct msgb *msg)
{
int rc;
if (!tap->enabled)
return;
rc = sendto(fd, msgb_data(msg), msgb_length(msg), 0, (struct sockaddr *)&tap->forward,
sizeof(tap->forward));
struct msgb *msg2 = msgb_copy(msg, "RTP TAP Tx");
if (!msg2)
return;
rc = osmo_iofd_sendto_msgb(iofd, msg2, 0, &tap->forward);
if (rc < 0)
LOGP(DRTP, LOGL_ERROR,
"Forwarding tapped (debug) voice data failed.\n");
@@ -966,7 +972,7 @@ static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
return 0;
}
/*! Dispatch msg bridged from the sister conn in the endpoint.
/*! Dispatch msg bridged from the sister conn in the endpoint. Takes ownership of msgb.
* \param[in] conn_dst The destination conn that should handle and transmit the content to
* its peer outside MGW.
* \param[in] msg msgb containing an RTP pkt received by the sister conn in the endpoint,
@@ -988,8 +994,10 @@ static int mgcp_conn_rtp_dispatch_rtp(struct mgcp_conn_rtp *conn_dst, struct msg
/* Before we try to deliver the packet, we check if the destination
* port and IP-Address make sense at all. If not, we will be unable
* to deliver the packet. */
if (check_rtp_destin(conn_dst) != 0)
if (check_rtp_destin(conn_dst) != 0) {
msgb_free(msg);
return -1;
}
/* Depending on the RTP connection type, deliver the RTP packet to the
* destination connection. */
@@ -1024,39 +1032,46 @@ static int mgcp_conn_rtp_dispatch_rtp(struct mgcp_conn_rtp *conn_dst, struct msg
* be discarded, this should not happen, normally the MGCP type
* should be properly set */
LOGPENDP(endp, DRTP, LOGL_ERROR, "bad MGCP type -- data discarded!\n");
msgb_free(msg);
return -1;
}
/*! send udp packet.
* \param[in] fd associated file descriptor.
/*! send message buffer via udp socket. If it succeeds, it takes ownership of the msgb and internally calls
* msgb_free() after the aynchronous sendto() completes. In case of error, the msgb is still owned by the
* caller and must be free'd accordingly.
* \param[in] iofd associated file descriptor.
* \param[in] addr destination ip-address.
* \param[in] msg message buffer that holds the data to be send.
* \returns 0 in case of success (takes msgb ownership), -1 on error (doesn't take msgb ownership). */
static int mgcp_udp_send_msg(struct osmo_io_fd *iofd, const struct osmo_sockaddr *addr, struct msgb *msg)
{
LOGP(DRTP, LOGL_DEBUG, "sending %d bytes length packet to %s ...\n", msgb_length(msg),
osmo_sockaddr_to_str(addr));
return osmo_iofd_sendto_msgb(iofd, msg, 0, addr);
}
/*! send udp packet from raw buffer/length.
* \param[in] iofd associated file descriptor.
* \param[in] addr destination ip-address.
* \param[in] buf buffer that holds the data to be send.
* \param[in] len length of the data to be sent.
* \returns bytes sent, -1 on error. */
int mgcp_udp_send(int fd, const struct osmo_sockaddr *addr, const char *buf, int len)
* \returns 0 in case of success, -1 on error. */
int mgcp_udp_send(struct osmo_io_fd *iofd, const struct osmo_sockaddr *addr, const char *buf, int len)
{
char ipbuf[INET6_ADDRSTRLEN];
size_t addr_len;
struct msgb *msg = msgb_alloc_c(iofd, len, "mgcp_udp_send");
if (!msg)
return -ENOMEM;
memcpy(msg->tail, buf, len);
msgb_put(msg, len);
LOGP(DRTP, LOGL_DEBUG,
"sending %i bytes length packet to %s:%u ...\n", len,
osmo_sockaddr_ntop(&addr->u.sa, ipbuf),
osmo_sockaddr_port(&addr->u.sa));
if (addr->u.sa.sa_family == AF_INET6) {
addr_len = sizeof(addr->u.sin6);
} else {
addr_len = sizeof(addr->u.sin);
}
return sendto(fd, buf, len, 0, &addr->u.sa, addr_len);
return mgcp_udp_send_msg(iofd, addr, msg);
}
/*! send RTP dummy packet (to keep NAT connection open).
* \param[in] endp mcgp endpoint that holds the RTP connection.
* \param[in] conn associated RTP connection.
* \returns bytes sent, -1 on error. */
* \returns 0 in case of success, -1 on error. */
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
int rc;
@@ -1078,8 +1093,7 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
if (mgcp_conn_rtp_is_iuup(conn))
rc = mgcp_conn_iuup_send_dummy(conn);
else
rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
rtp_dummy_payload, sizeof(rtp_dummy_payload));
rc = mgcp_udp_send(conn->end.rtp, &conn->end.addr, rtp_dummy_payload, sizeof(rtp_dummy_payload));
if (rc == -1)
goto failed;
@@ -1090,10 +1104,10 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
was_rtcp = 1;
rtcp_addr = conn->end.addr;
osmo_sockaddr_set_port(&rtcp_addr.u.sa, ntohs(conn->end.rtcp_port));
rc = mgcp_udp_send(conn->end.rtcp.fd, &rtcp_addr,
rc = mgcp_udp_send(conn->end.rtcp, &rtcp_addr,
rtp_dummy_payload, sizeof(rtp_dummy_payload));
if (rc >= 0)
if (rc == 0)
return rc;
failed:
@@ -1104,7 +1118,7 @@ failed:
return -1;
}
/*! Send RTP/RTCP data to a specified destination connection.
/*! Send RTP/RTCP data to a specified destination connection. Takes ownership of msg.
* \param[in] endp associated endpoint (for configuration, logging).
* \param[in] is_rtp flag to specify if the packet is of type RTP or RTCP.
* \param[in] addr spoofed source address (set to NULL to disable).
@@ -1143,6 +1157,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
if (is_rtp && !mgcp_conn_rtp_is_iuup(conn_src)) {
if (mgcp_patch_pt(conn_dst, msg) < 0) {
LOGPENDP(endp, DRTP, LOGL_NOTICE, "unable to patch payload type RTP packet, discarding...\n");
msgb_free(msg);
return -EINVAL;
}
}
@@ -1168,73 +1183,69 @@ 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) {
int cont;
int nbytes = 0;
int buflen = msgb_length(msg);
/* Make sure we have a valid RTP header, in cases where no RTP
* header is present, we will generate one. */
gen_rtp_header(msg, rtp_end, rtp_state);
do {
/* Run transcoder */
cont = endp->trunk->cfg->rtp_processing_cb(endp, rtp_end, (char *)msgb_data(msg), &buflen, RTP_BUF_SIZE);
if (cont < 0)
break;
/* 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;
}
if (addr)
mgcp_patch_and_count(endp, rtp_state, rtp_end,
addr, msg);
if (addr)
mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, msg);
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)) {
bool oa = mgcp_codec_amr_is_octet_aligned(conn_dst->end.codec);
rc = amr_oa_bwe_convert(endp, msg, oa);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"Error in AMR octet-aligned <-> bandwidth-efficient mode conversion (target=%s)\n",
oa ? "octet-aligned" : "bandwidth-efficient");
break;
}
} else if (rtp_end->rfc5993_hr_convert &&
strcmp(conn_src->end.codec->subtype_name, "GSM-HR-08") == 0) {
rc = rfc5993_hr_convert(endp, msg);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
break;
}
if (mgcp_conn_rtp_is_iuup(conn_dst) || mgcp_conn_rtp_is_iuup(conn_src)) {
/* the iuup code will correctly transform to the correct AMR mode */
} else if (mgcp_codec_amr_align_mode_is_indicated(conn_dst->end.codec)) {
rc = amr_oa_bwe_convert(endp, msg, conn_dst->end.codec->param.amr_octet_aligned);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"Error in AMR octet-aligned <-> bandwidth-efficient mode conversion (target=%s)\n",
conn_dst->end.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) {
rc = rfc5993_hr_convert(endp, msg);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
msgb_free(msg);
return rc;
}
}
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"process/send to %s %s "
"rtp_port:%u rtcp_port:%u\n",
dest_name,
osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
);
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"process/send to %s %s "
"rtp_port:%u rtcp_port:%u\n",
dest_name,
osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
);
/* Forward a copy of the RTP data to a debug ip/port */
forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out,
msg);
/* Forward a copy of the RTP data to a debug ip/port */
forward_data_tap(rtp_end->rtp, &conn_src->tap_out, msg);
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr,
(char *)msgb_data(msg), msgb_length(msg));
len = msgb_length(msg);
if (len <= 0)
return len;
rc = mgcp_udp_send_msg(rtp_end->rtp, &rtp_end->addr, msg);
if (rc < 0) {
msgb_free(msg);
return rc;
}
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
rtp_state->alt_rtp_tx_sequence++;
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
rtp_state->alt_rtp_tx_sequence++;
nbytes += len;
buflen = cont;
} while (buflen > 0);
return nbytes;
return 0;
} else if (!trunk->omit_rtcp) {
struct osmo_sockaddr rtcp_addr = rtp_end->addr;
osmo_sockaddr_set_port(&rtcp_addr.u.sa, rtp_end->rtcp_port);
osmo_sockaddr_set_port(&rtcp_addr.u.sa, ntohs(rtp_end->rtcp_port));
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"send to %s %s rtp_port:%u rtcp_port:%u\n",
dest_name, osmo_sockaddr_ntop(&rtcp_addr.u.sa, ipbuf),
@@ -1242,19 +1253,54 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
osmo_sockaddr_port(&rtcp_addr.u.sa)
);
len = mgcp_udp_send(rtp_end->rtcp.fd, &rtcp_addr,
(char *)msgb_data(msg), msgb_length(msg));
len = msgb_length(msg);
rc = mgcp_udp_send_msg(rtp_end->rtcp, &rtcp_addr, msg);
if (rc < 0) {
msgb_free(msg);
return rc;
}
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
rtp_state->alt_rtp_tx_sequence++;
return len;
return 0;
}
msgb_free(msg);
return 0;
}
/*! determine if there's only a single recipient in endp for data received via conn_src.
* The function returns NULL in case there is no recipient, or in case there are multiple recipients.
* \param endp The MGCP endpoint whose connections to analyze
* \param conn_src The source MGCP connection [which shall not count in results]
* \returns recipient donnection if there is only one; NULL in case there are multiple */
static struct mgcp_conn *rtpbridge_get_only_recipient(struct mgcp_endpoint *endp, struct mgcp_conn *conn_src)
{
struct mgcp_conn *conn_ret = NULL;
struct mgcp_conn *conn_dst;
llist_for_each_entry(conn_dst, &endp->conns, entry) {
if (conn_dst == conn_src)
continue;
switch (conn_dst->mode) {
case MGCP_CONN_SEND_ONLY:
case MGCP_CONN_RECV_SEND:
case MGCP_CONN_CONFECHO:
if (conn_ret)
return NULL;
conn_ret = conn_dst;
break;
default:
break;
}
}
return conn_ret;
}
/*! Dispatch incoming RTP packet to opposite RTP connection.
* \param[in] msg Message buffer to bridge, coming from source connection.
* msg shall contain "struct osmo_rtp_msg_ctx *" attached in
@@ -1291,13 +1337,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);
}
@@ -1312,23 +1358,44 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
return rc;
}
/* If the mode is "confecho", send RTP back to the sender. */
if (conn->mode == MGCP_CONN_CONFECHO)
rc = mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
/* All the use cases above are 1:1 where we have one source msgb and we're sending that to one
* destination. msgb ownership had been passed to the respective _*dospatch_rtp() function.
* In the cases below, we actually [can] have multiple recipients, so we copy the original msgb
* for each of the recipients. */
/* Dispatch RTP packet to all other connection(s) that send audio. */
llist_for_each_entry(conn_dst, &endp->conns, entry) {
if (conn_dst == conn)
continue;
switch (conn_dst->mode) {
case MGCP_CONN_SEND_ONLY:
case MGCP_CONN_RECV_SEND:
case MGCP_CONN_CONFECHO:
rc = mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg);
break;
default:
break;
/* If the mode is "confecho", send RTP back to the sender. */
if (conn->mode == MGCP_CONN_CONFECHO) {
struct msgb *msg2 = mgw_msgb_copy_c(conn, msg, "RTP confecho");
if (OSMO_LIKELY(msg2))
rc = mgcp_conn_rtp_dispatch_rtp(conn_src, msg2);
}
conn_dst = rtpbridge_get_only_recipient(endp, conn);
if (OSMO_LIKELY(conn_dst)) {
/* we only have a single recipient and cann hence send the original msgb without copying */
rc = mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg);
} else {
/* Dispatch RTP packet to all other connection(s) that send audio. */
llist_for_each_entry(conn_dst, &endp->conns, entry) {
struct msgb *msg2;
if (conn_dst == conn)
continue;
switch (conn_dst->mode) {
case MGCP_CONN_SEND_ONLY:
case MGCP_CONN_RECV_SEND:
case MGCP_CONN_CONFECHO:
/* we have multiple recipients and must make copies for each recipient */
msg2 = mgw_msgb_copy_c(conn_dst, msg, "RTP Tx copy");
if (OSMO_LIKELY(msg2))
rc = mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg2);
break;
default:
break;
}
}
/* as we only sent copies in the previous llist_for_each_entry() loop, we must free the
* original one */
msgb_free(msg);
}
return rc;
}
@@ -1355,19 +1422,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.codec, msg);
}
/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
@@ -1400,7 +1467,7 @@ void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *con
}
/* Handle incoming RTP data from NET */
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
static void rtp_recvfrom_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *saddr)
{
/* NOTE: This is a generic implementation. RTP data is received. In
* case of loopback the data is just sent back to its origin. All
@@ -1411,49 +1478,34 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
struct mgcp_conn_rtp *conn_src;
struct mgcp_endpoint *endp;
struct osmo_sockaddr addr;
socklen_t slen = sizeof(addr);
char ipbuf[INET6_ADDRSTRLEN];
int ret;
enum rtp_proto proto;
struct osmo_rtp_msg_ctx *mc;
struct msgb *msg;
int rc;
conn_src = (struct mgcp_conn_rtp *)fd->data;
conn_src = (struct mgcp_conn_rtp *) osmo_iofd_get_data(iofd);
OSMO_ASSERT(conn_src);
endp = conn_src->conn->endp;
OSMO_ASSERT(endp);
msg = msgb_alloc_c(endp->trunk, RTP_BUF_SIZE, "RTP-rx");
proto = (fd == &conn_src->end.rtp)? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
proto = (iofd == conn_src->end.rtp) ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
ret = recvfrom(fd->fd, msgb_data(msg), msg->data_len, 0, (struct sockaddr *)&addr.u.sa, &slen);
if (ret <= 0) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "recvfrom error: %s\n", strerror(errno));
rc = -1;
goto out;
if (res <= 0) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "recvfrom error: %s\n", strerror(-res));
goto out_free;
}
msgb_put(msg, ret);
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n",
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s\n",
proto == MGCP_PROTO_RTP ? "RTP" : "RTCP",
msgb_length(msg), osmo_sockaddr_ntop(&addr.u.sa, ipbuf),
osmo_sockaddr_port(&addr.u.sa));
msgb_length(msg), osmo_sockaddr_to_str(saddr));
if ((proto == MGCP_PROTO_RTP && check_rtp(conn_src, msg))
|| (proto == MGCP_PROTO_RTCP && check_rtcp(conn_src, msg))) {
/* Logging happened in the two check_ functions */
rc = -1;
goto out;
goto out_free;
}
if (mgcp_is_rtp_dummy_payload(msg)) {
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx dummy packet (dropped)\n");
rc = 0;
goto out;
goto out_free;
}
/* Since the msgb remains owned and freed by this function, the msg ctx data struct can just be on the stack and
@@ -1462,7 +1514,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
*mc = (struct osmo_rtp_msg_ctx){
.proto = proto,
.conn_src = conn_src,
.from_addr = &addr,
.from_addr = (struct osmo_sockaddr *) saddr,
};
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "msg ctx: %d %p %s\n",
mc->proto, mc->conn_src,
@@ -1477,16 +1529,17 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */
/* Forward a copy of the RTP data to a debug ip/port */
forward_data_tap(fd->fd, &conn_src->tap_in, msg);
forward_data_tap(iofd, &conn_src->tap_in, msg);
rc = rx_rtp(msg);
rx_rtp(msg);
return;
out:
out_free:
msgb_free(msg);
return rc;
}
/* Note: This function is able to handle RTP and RTCP */
/* Note: This function is able to handle RTP and RTCP. msgb ownership is transferred, so this function or its
* downstream consumers must make sure to [eventually] free the msgb. */
static int rx_rtp(struct msgb *msg)
{
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
@@ -1499,25 +1552,23 @@ static int rx_rtp(struct msgb *msg)
/* Check if the origin of the RTP packet seems plausible */
if (!trunk->rtp_accept_all && check_rtp_origin(conn_src, from_addr))
return -1;
goto out_free;
/* 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)) {
bool src_oa;
/* 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)
return -1;
src_oa = mgcp_codec_amr_is_octet_aligned(conn_src->end.codec);
if (((bool)oa) != src_oa) {
goto out_free;
if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned) {
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
"rx_rtp(%u bytes): Expected RTP AMR octet-aligned=%u but got octet-aligned=%u."
" check the config of your call-agent!\n",
msgb_length(msg), src_oa, oa);
return -1;
msgb_length(msg), conn_src->end.codec->param.amr_octet_aligned, oa);
goto out_free;
}
}
@@ -1526,17 +1577,36 @@ static int rx_rtp(struct msgb *msg)
/* Execute endpoint specific implementation that handles the
* dispatching of the RTP data */
return conn->endp->type->dispatch_rtp_cb(msg);
out_free:
msgb_free(msg);
return -1;
}
static void rtp_sendto_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *daddr)
{
/* nothing; osmo_io takes care of msgb_free */
if (res < 0) {
struct mgcp_conn_rtp *conn_rtp = (struct mgcp_conn_rtp *) osmo_iofd_get_data(iofd);
int priv_nr = osmo_iofd_get_priv_nr(iofd);
char errbuf[129];
strerror_r(-res, errbuf, sizeof(errbuf));
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "%s sendto(%s) failed: %s\n", priv_nr ? "RTCP" : "RTP",
osmo_sockaddr_to_str(daddr), errbuf);
}
}
static const struct osmo_io_ops rtp_ioops = {
.recvfrom_cb = rtp_recvfrom_cb,
.sendto_cb = rtp_sendto_cb,
};
/*! bind RTP port to osmo_fd.
* \param[in] source_addr source (local) address to bind on.
* \param[in] fd associated file descriptor.
* \param[in] port to bind on.
* \param[in] dscp IP DSCP value to use.
* \param[in] prio socket priority to use.
* \returns 0 on success, -1 on ERROR. */
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint8_t dscp,
uint8_t prio)
* \returns file descriptor on success, -1 on ERROR. */
int mgcp_create_bind(const char *source_addr, int port, uint8_t dscp, uint8_t prio)
{
int rc;
@@ -1544,47 +1614,50 @@ int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint
NULL, 0, OSMO_SOCK_F_BIND | OSMO_SOCK_F_DSCP(dscp) |
OSMO_SOCK_F_PRIO(prio));
if (rc < 0) {
LOGP(DRTP, LOGL_ERROR, "failed to bind UDP port (%s:%i).\n",
LOGP(DRTP, LOGL_ERROR, "failed to bind UDP port (%s:%d).\n",
source_addr, port);
return -1;
}
fd->fd = rc;
LOGP(DRTP, LOGL_DEBUG, "created socket + bound UDP port (%s:%i).\n", source_addr, port);
LOGP(DRTP, LOGL_DEBUG, "created socket + bound UDP port (%s:%d).\n", source_addr, port);
return 0;
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) */
if (mgcp_create_bind(source_addr, &rtp_end->rtp, rtp_end->local_port,
cfg->endp_dscp, cfg->endp_priority) != 0) {
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;
if (mgcp_create_bind(source_addr, &rtp_end->rtcp, rtp_end->local_port + 1,
cfg->endp_dscp, cfg->endp_priority) != 0) {
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_fd_register(&rtp_end->rtp) != 0) {
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_fd_register(&rtp_end->rtcp) != 0) {
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);
@@ -1594,13 +1667,11 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
return 0;
cleanup3:
osmo_fd_unregister(&rtp_end->rtp);
osmo_iofd_unregister(rtp_end->rtp);
cleanup2:
close(rtp_end->rtcp.fd);
rtp_end->rtcp.fd = -1;
close(rtcp_fd);
cleanup1:
close(rtp_end->rtp.fd);
rtp_end->rtp.fd = -1;
close(rtp_fd);
cleanup0:
return -1;
}
@@ -1619,7 +1690,8 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
snprintf(name, sizeof(name), "%s-%s", conn->conn->name, conn->conn->id);
end = &conn->end;
if (end->rtp.fd != -1 || end->rtcp.fd != -1) {
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));
@@ -1632,25 +1704,43 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
}
end->local_port = rtp_port;
osmo_fd_setup(&end->rtp, -1, OSMO_FD_READ, rtp_data_net, conn, 0);
osmo_fd_setup(&end->rtcp, -1, OSMO_FD_READ, rtp_data_net, conn, 0);
end->rtp = osmo_iofd_setup(conn->conn, -1, name, OSMO_IO_FD_MODE_RECVFROM_SENDTO, &rtp_ioops, conn);
if (!end->rtp)
return -EIO;
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;
}
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);
}
/***********************
* mgcp_rtp_end
**********************/
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_free_rtp_port(struct mgcp_rtp_end *end)
void mgcp_rtp_end_free_port(struct mgcp_rtp_end *end)
{
if (end->rtp.fd != -1) {
osmo_fd_unregister(&end->rtp);
close(end->rtp.fd);
end->rtp.fd = -1;
if (end->rtp) {
osmo_iofd_free(end->rtp);
end->rtp = NULL;
}
if (end->rtcp.fd != -1) {
osmo_fd_unregister(&end->rtcp);
close(end->rtcp.fd);
end->rtcp.fd = -1;
if (end->rtcp) {
osmo_iofd_free(end->rtcp);
end->rtcp = NULL;
}
}

View File

@@ -1,6 +1,7 @@
/*
* (C) 2012-2013 by Pablo Neira Ayuso <pablo@gnumonks.org>
* (C) 2012-2013 by On Waves ehf <http://www.on-waves.com>
* (C) 2013-2024 by sysmocom - s.f.m.c. GmbH
* All rights not specifically granted under this license are reserved.
*
* This program is free software; you can redistribute it and/or modify it
@@ -13,9 +14,11 @@
#include <string.h> /* for memcpy */
#include <stdlib.h> /* for abs */
#include <inttypes.h> /* for PRIu64 */
#include <unistd.h> /* for PRIu64 */
#include <netinet/in.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/core/talloc.h>
#include <osmocom/netif/osmux.h>
@@ -30,8 +33,8 @@
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
static struct osmo_fd osmux_fd_v4;
static struct osmo_fd osmux_fd_v6;
static struct osmo_io_fd *osmux_fd_v4;
static struct osmo_io_fd *osmux_fd_v6;
static LLIST_HEAD(osmux_handle_list);
@@ -76,34 +79,31 @@ static void rtpconn_osmux_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, int id)
static void osmux_deliver_cb(struct msgb *batch_msg, void *data)
{
struct osmux_handle *handle = data;
socklen_t dest_len;
int rc, fd;
struct mgcp_trunk *trunk = (struct mgcp_trunk *)osmux_fd_v4.data;
int rc;
struct osmo_io_fd *iofd;
struct mgcp_trunk *trunk = (struct mgcp_trunk *) osmo_iofd_get_data(osmux_fd_v4);
struct rate_ctr_group *all_osmux_stats = trunk->ratectr.all_osmux_conn_stats;
switch (handle->rem_addr.u.sa.sa_family) {
case AF_INET6:
dest_len = sizeof(handle->rem_addr.u.sin6);
fd = osmux_fd_v6.fd;
iofd = osmux_fd_v6;
break;
case AF_INET:
default:
dest_len = sizeof(handle->rem_addr.u.sin);
fd = osmux_fd_v4.fd;
iofd = osmux_fd_v4;
break;
}
rc = sendto(fd, batch_msg->data, batch_msg->len, 0,
(struct sockaddr *)&handle->rem_addr.u.sa, dest_len);
rc = osmo_iofd_sendto_msgb(iofd, batch_msg, 0, &handle->rem_addr);
if (rc < 0) {
char errbuf[129];
strerror_r(errno, errbuf, sizeof(errbuf));
strerror_r(-rc, errbuf, sizeof(errbuf));
LOGP(DOSMUX, LOGL_NOTICE, "osmux sendto(%s) failed: %s\n",
osmo_sockaddr_to_str(&handle->rem_addr), errbuf);
rate_ctr_inc(rate_ctr_group_get_ctr(all_osmux_stats, OSMUX_DROPPED_PACKETS_CTR));
msgb_free(batch_msg);
} else {
rate_ctr_inc(rate_ctr_group_get_ctr(all_osmux_stats, OSMUX_PACKETS_TX_CTR));
}
msgb_free(batch_msg);
}
/* Lookup existing OSMUX handle for specified destination address. */
@@ -204,17 +204,17 @@ osmux_handle_find_or_create(const struct mgcp_trunk *trunk, const struct osmo_so
return h->in;
}
/*! send RTP packet through OSMUX connection.
/*! send RTP packet through OSMUX connection. Takes ownership of msg.
* \param[in] conn associated RTP connection
* \param[in] msg msgb containing an RTP AMR packet
* \returns 0 on success, -1 on ERROR */
int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg)
{
int ret;
struct msgb *msg2;
if (!conn->end.output_enabled) {
rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
msgb_free(msg);
return -1;
}
@@ -222,22 +222,19 @@ int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg)
LOGPCONN(conn->conn, DOSMUX, LOGL_INFO, "forwarding RTP to Osmux conn not yet enabled, dropping (cid=%d)\n",
conn->osmux.remote_cid);
rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
msgb_free(msg);
return -1;
}
/* msg is not owned by us and will be freed by the caller stack upon return: */
msg2 = msgb_copy_c(conn->conn, msg, "osmux-rtp-send");
if (!msg2)
return -1;
/* Osmux implementation works with AMR OA only, make sure we convert to it if needed: */
if (amr_oa_bwe_convert(conn->conn->endp, msg2, true) < 0) {
if (amr_oa_bwe_convert(conn->conn->endp, msg, true) < 0) {
LOGPCONN(conn->conn, DOSMUX, LOGL_ERROR,
"Error converting to AMR octet-aligned mode\n");
msgb_free(msg);
return -1;
}
while ((ret = osmux_xfrm_input(conn->osmux.in, msg2, conn->osmux.remote_cid)) > 0) {
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);
}
@@ -245,7 +242,7 @@ int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg)
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(msg2) - sizeof(struct rtp_hdr));
rtpconn_osmux_rate_ctr_add(conn, OSMUX_AMR_OCTETS_TX_CTR, msgb_length(msg) - sizeof(struct rtp_hdr));
}
return 0;
}
@@ -268,7 +265,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;
@@ -325,29 +322,7 @@ static void scheduled_from_osmux_tx_rtp_cb(struct msgb *msg, void *data)
};
endp->type->dispatch_rtp_cb(msg);
msgb_free(msg);
}
static struct msgb *osmux_recv(struct osmo_fd *ofd, struct osmo_sockaddr *addr)
{
struct msgb *msg;
socklen_t slen = sizeof(addr->u.sas);
int ret;
msg = msgb_alloc(4096, "OSMUX");
if (!msg) {
LOGP(DOSMUX, LOGL_ERROR, "cannot allocate message\n");
return NULL;
}
ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0, &addr->u.sa, &slen);
if (ret <= 0) {
msgb_free(msg);
LOGP(DOSMUX, LOGL_ERROR, "cannot receive message\n");
return NULL;
}
msgb_put(msg, ret);
return msg;
/* dispatch_rtp_cb() has taken ownership of the msgb */
}
/* To be called every time some AMR data is received on a connection
@@ -445,22 +420,16 @@ out:
}
#define osmux_chunk_length(msg, rem) ((rem) - (msg)->len)
static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
static void osmux_recvfrom_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *rem_addr)
{
struct msgb *msg;
struct osmux_hdr *osmuxh;
struct osmo_sockaddr rem_addr;
uint32_t rem;
struct mgcp_trunk *trunk = ofd->data;
struct mgcp_trunk *trunk = osmo_iofd_get_data(iofd);
struct rate_ctr_group *all_rtp_stats = trunk->ratectr.all_osmux_conn_stats;
uint32_t rem;
char addr_str[64];
msg = osmux_recv(ofd, &rem_addr);
if (!msg)
return -1;
rate_ctr_inc(rate_ctr_group_get_ctr(all_rtp_stats, OSMUX_PACKETS_RX_CTR));
osmo_sockaddr_to_str_buf(addr_str, sizeof(addr_str), &rem_addr);
osmo_sockaddr_to_str_buf(addr_str, sizeof(addr_str), rem_addr);
if (trunk->cfg->osmux.usage == OSMUX_USAGE_OFF) {
LOGP(DOSMUX, LOGL_ERROR,
@@ -470,14 +439,16 @@ static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
}
/* Catch legacy dummy message and process them separately: */
if (msg->len == 2 && msg->data[0] == MGCP_DUMMY_LOAD)
return osmux_handle_legacy_dummy(trunk, &rem_addr, msg);
if (msg->len == 2 && msg->data[0] == MGCP_DUMMY_LOAD) {
osmux_handle_legacy_dummy(trunk, rem_addr, msg);
return;
}
rem = msg->len;
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
struct mgcp_conn_rtp *conn_src;
conn_src = osmux_conn_lookup(trunk, osmuxh->circuit_id,
&rem_addr);
rem_addr);
if (!conn_src) {
LOGP(DOSMUX, LOGL_DEBUG,
"Cannot find a src conn for %s CID=%d\n",
@@ -485,7 +456,7 @@ static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
goto next;
}
if (conn_osmux_event_data_received(conn_src, &rem_addr) < 0)
if (conn_osmux_event_data_received(conn_src, rem_addr) < 0)
goto next;
mgcp_conn_watchdog_kick(conn_src->conn);
@@ -499,58 +470,94 @@ next:
}
out:
msgb_free(msg);
return 0;
}
static void osmux_sendto_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *rem_addr)
{
/* nothing; osmo_io takes care of msgb_free */
if (res < 0) {
struct mgcp_trunk *trunk = (struct mgcp_trunk *) osmo_iofd_get_data(iofd);
struct rate_ctr_group *all_osmux_stats = trunk->ratectr.all_osmux_conn_stats;
char errbuf[129];
strerror_r(-res, errbuf, sizeof(errbuf));
LOGP(DOSMUX, LOGL_NOTICE, "osmux sendto(%s) failed: %s\n", osmo_sockaddr_to_str(rem_addr), errbuf);
rate_ctr_inc(rate_ctr_group_get_ctr(all_osmux_stats, OSMUX_DROPPED_PACKETS_CTR));
}
}
static const struct osmo_io_ops osmux_ioops = {
.recvfrom_cb = osmux_recvfrom_cb,
.sendto_cb = osmux_sendto_cb,
};
int osmux_init(struct mgcp_trunk *trunk)
{
int ret;
int ret, fd;
struct mgcp_config *cfg = trunk->cfg;
/* So far we only support running on one trunk: */
OSMO_ASSERT(trunk == mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID));
osmo_fd_setup(&osmux_fd_v4, -1, OSMO_FD_READ, osmux_read_fd_cb, trunk, 0);
osmo_fd_setup(&osmux_fd_v6, -1, OSMO_FD_READ, osmux_read_fd_cb, trunk, 0);
osmux_fd_v4 = osmo_iofd_setup(trunk, -1, "osmux_fd_v4", OSMO_IO_FD_MODE_RECVFROM_SENDTO, &osmux_ioops, trunk);
if (!osmux_fd_v4)
goto out;
osmo_iofd_set_alloc_info(osmux_fd_v4, 4096, 0);
if (cfg->osmux.local_addr_v4) {
ret = mgcp_create_bind(cfg->osmux.local_addr_v4, &osmux_fd_v4, cfg->osmux.local_port,
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);
return ret;
goto out_free_v4;
}
fd = ret;
ret = osmo_fd_register(&osmux_fd_v4);
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(osmux_fd_v4.fd));
return ret;
LOGP(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(osmux_fd_v4.fd));
LOGP(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);
if (!osmux_fd_v6)
goto out_free_v4;
osmo_iofd_set_alloc_info(osmux_fd_v6, 4096, 0);
if (cfg->osmux.local_addr_v6) {
ret = mgcp_create_bind(cfg->osmux.local_addr_v6, &osmux_fd_v6, cfg->osmux.local_port,
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);
return ret;
goto out_free_v6;
}
fd = ret;
ret = osmo_fd_register(&osmux_fd_v6);
ret = osmo_iofd_register(osmux_fd_v6, fd);
if (ret < 0) {
LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv6 socket %s\n",
osmo_sock_get_name2(osmux_fd_v6.fd));
return ret;
LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv6 socket %s\n", osmo_sock_get_name2(fd));
close(fd);
goto out_free_v6;
}
LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv6 socket listening on %s\n",
osmo_sock_get_name2(osmux_fd_v6.fd));
LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv6 socket listening on %s\n", osmo_sock_get_name2(fd));
}
cfg->osmux.initialized = true;
return 0;
out_free_v6:
/* osmo_iofd_free performs unregister + close */
osmo_iofd_free(osmux_fd_v6);
osmux_fd_v6 = NULL;
out_free_v4:
/* osmo_iofd_free performs unregister + close */
osmo_iofd_free(osmux_fd_v4);
osmux_fd_v4 = NULL;
out:
return -1;
}
/*! relase OSXMUX cid, that had been allocated to this connection.
@@ -688,7 +695,7 @@ void conn_osmux_disable(struct mgcp_conn_rtp *conn)
/*! send RTP dummy packet to OSMUX connection port.
* \param[in] conn associated RTP connection
* \returns bytes sent, -1 on error */
* \returns 0 in case of success, -1 on error */
int osmux_send_dummy(struct mgcp_conn_rtp *conn)
{
char ipbuf[INET6_ADDRSTRLEN];
@@ -716,7 +723,7 @@ int osmux_send_dummy(struct mgcp_conn_rtp *conn)
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf),
osmo_sockaddr_port(&conn->end.addr.u.sa), conn->osmux.remote_cid);
return mgcp_udp_send(osmux_fd_v4.fd, &conn->end.addr, (char *)osmuxh, buf_len);
return mgcp_udp_send(osmux_fd_v4, &conn->end.addr, (char *)osmuxh, buf_len);
}
/* Keeps track of locally allocated Osmux circuit ID. +7 to round up to 8 bit boundary. */

View File

@@ -59,6 +59,16 @@ char *mgcp_debug_get_last_endpoint_name(void)
return debug_last_endpoint_name;
}
const struct value_string mgcp_connection_mode_strs[] = {
{ MGCP_CONN_NONE, "none" },
{ MGCP_CONN_RECV_SEND, "sendrecv" },
{ MGCP_CONN_SEND_ONLY, "sendonly" },
{ MGCP_CONN_RECV_ONLY, "recvonly" },
{ MGCP_CONN_CONFECHO, "confecho" },
{ MGCP_CONN_LOOPBACK, "loopback" },
{ 0, NULL }
};
/* A combination of LOGPENDP and LOGPTRUNK that automatically falls back to
* LOGPTRUNK when the endp parameter is NULL */
#define LOGPEPTR(endp, trunk, cat, level, fmt, args...) \
@@ -172,7 +182,7 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
/* Find the "sister" connection */
llist_for_each_entry(_conn, &endp->conns, entry) {
if (_conn->id != conn->conn->id) {
conn_src = &_conn->u.rtp;
conn_src = mgcp_conn_get_conn_rtp(_conn);
break;
}
}
@@ -397,23 +407,6 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
rq.null_endp = mgcp_endp_is_null(pdata.epname);
if (!rq.null_endp)
rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
if (rq.endp) {
struct mgcp_conn *c;
int count = 0;
llist_for_each_entry(c, &rq.endp->conns, entry) {
LOGP(DLMGCP, LOGL_DEBUG,
"%s: endp=%s conn %s\n",
rq.name,
rq.endp->name,
mgcp_conn_dump(c));
count++;
}
if (!count)
LOGP(DLMGCP, LOGL_DEBUG,
"%s: endp=%s no conns\n",
rq.name,
rq.endp->name);
}
rq.mgcp_cause = rc;
if (!rq.endp) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
@@ -446,7 +439,6 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
/* Check if we have to retransmit a response from a previous transaction */
if (pdata.trans && rq.endp->last_trans && strcmp(rq.endp->last_trans, pdata.trans) == 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_RETRANSMITTED));
LOGP(DLMGCP, LOGL_DEBUG, "%s: retransmission\n", rq.name);
return create_retransmission_response(rq.endp);
}
}
@@ -735,7 +727,7 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
{
struct mgcp_trunk *trunk = endp->trunk;
int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
bool patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
rtp->force_aligned_timing = trunk->force_aligned_timing;
rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
@@ -837,7 +829,7 @@ static int handle_codec_info(struct mgcp_conn_rtp *conn,
/* Try to find an destination RTP connection that we can include in the codec decision. */
conn_dst = mgcp_find_dst_conn(conn->conn);
if (conn_dst && conn_dst->type == MGCP_CONN_TYPE_RTP)
conn_dst_rtp = &conn_dst->u.rtp;
conn_dst_rtp = mgcp_conn_get_conn_rtp(conn_dst);
else
conn_dst_rtp = NULL;
@@ -888,11 +880,11 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
int error_code = 400;
const char *local_options = NULL;
const char *callid = NULL;
const char *mode = NULL;
enum mgcp_connection_mode mode = MGCP_CONN_NONE;
char *line;
int have_sdp = 0, remote_osmux_cid = -2;
struct mgcp_conn_rtp *conn = NULL;
struct mgcp_conn *_conn = NULL;
struct mgcp_conn *conn = NULL;
struct mgcp_conn_rtp *conn_rtp = NULL;
char conn_name[512];
int rc;
@@ -940,7 +932,7 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
return create_err_response(rq->trunk, NULL, 523, "CRCX", pdata->trans);
break;
case 'M':
mode = (const char *)line + 3;
mode = mgcp_parse_conn_mode((const char *)line + 3);
break;
case 'X':
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
@@ -977,15 +969,15 @@ mgcp_header_done:
return create_err_response(endp, endp, 516, "CRCX", pdata->trans);
}
if (!mode) {
if (mode == MGCP_CONN_NONE) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"CRCX: insufficient parameters, missing mode\n");
"CRCX: insufficient parameters, invalid mode\n");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
return create_err_response(endp, endp, 517, "CRCX", pdata->trans);
}
/* Check if we are able to accept the creation of another connection */
if (endp->type->max_conns > 0 && llist_count(&endp->conns) >= endp->type->max_conns) {
if (mgcp_endp_is_full(endp)) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"CRCX: endpoint full, max. %i connections allowed!\n",
endp->type->max_conns);
@@ -993,7 +985,8 @@ mgcp_header_done:
/* There is no more room for a connection, make some
* room by blindly tossing the oldest of the two two
* connections */
mgcp_conn_free_oldest(endp);
mgcp_endp_free_conn_oldest(endp);
OSMO_ASSERT(!mgcp_endp_is_full(endp));
} else {
/* There is no more room for a connection, leave
* everything as it is and return with an error */
@@ -1032,36 +1025,35 @@ mgcp_header_done:
}
snprintf(conn_name, sizeof(conn_name), "%s", callid);
_conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
if (!_conn) {
conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
if (!conn) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"CRCX: unable to allocate RTP connection\n");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_ALLOC_CONN));
goto error2;
}
conn = mgcp_conn_get_rtp(endp, _conn->id);
OSMO_ASSERT(conn);
if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
if (mgcp_conn_set_mode(conn, mode) < 0) {
error_code = 517;
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
goto error2;
}
conn_rtp = mgcp_conn_get_conn_rtp(conn);
OSMO_ASSERT(conn_rtp);
/* If X-Osmux (remote CID) was received (-1 is wilcard), alloc next avail CID as local CID */
if (remote_osmux_cid >= -1) {
if (osmux_init_conn(conn) < 0) {
if (osmux_init_conn(conn_rtp) < 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
goto error2;
}
if (remote_osmux_cid >= 0) {
conn->osmux.remote_cid_present = true;
conn->osmux.remote_cid = remote_osmux_cid;
conn_rtp->osmux.remote_cid_present = true;
conn_rtp->osmux.remote_cid = remote_osmux_cid;
}
} else if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_ONLY) {
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
"CRCX: osmux only and no osmux offered\n");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
goto error2;
@@ -1072,7 +1064,7 @@ mgcp_header_done:
rc = set_local_cx_options(trunk->endpoints,
&endp->local_options, local_options);
if (rc != 0) {
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
"CRCX: invalid local connection options!\n");
error_code = rc;
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
@@ -1081,8 +1073,8 @@ mgcp_header_done:
}
/* Handle codec information and decide for a suitable codec */
rc = handle_codec_info(conn, rq, have_sdp, true);
mgcp_codec_summary(conn);
rc = handle_codec_info(conn_rtp, rq, have_sdp, true);
mgcp_codec_summary(conn_rtp);
if (rc) {
error_code = rc;
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
@@ -1090,72 +1082,60 @@ mgcp_header_done:
}
/* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */
/* TODO: "codec" probably needs to be moved from endp to conn */
if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0) {
rc = mgcp_conn_iuup_init(conn);
if (conn_rtp->type == MGCP_RTP_DEFAULT && strcmp(conn_rtp->end.codec->subtype_name, "VND.3GPP.IUFP") == 0) {
rc = mgcp_conn_iuup_init(conn_rtp);
}
if (pdata->cfg->force_ptime) {
conn->end.packet_duration_ms = pdata->cfg->force_ptime;
conn->end.force_output_ptime = 1;
conn_rtp->end.packet_duration_ms = pdata->cfg->force_ptime;
conn_rtp->end.force_output_ptime = 1;
}
mgcp_rtp_end_config(endp, 0, &conn->end);
/* check connection mode setting */
if (conn->conn->mode != MGCP_CONN_LOOPBACK
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
&& osmo_sockaddr_port(&conn->end.addr.u.sa) == 0) {
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
"CRCX: selected connection mode type requires an opposite end!\n");
error_code = 527;
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
goto error2;
}
mgcp_rtp_end_config(endp, 0, &conn_rtp->end);
/* Find a local address for conn based on policy and initial SDP remote
information, then find a free port for it */
if (mgcp_get_local_addr(conn->end.local_addr, conn) < 0) {
if (mgcp_get_local_addr(conn_rtp->end.local_addr, conn_rtp) < 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
goto error2;
}
if (allocate_port(endp, conn) != 0) {
if (allocate_port(endp, conn_rtp) != 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
goto error2;
}
if (setup_rtp_processing(endp, conn) != 0) {
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
if (setup_rtp_processing(endp, conn_rtp) != 0) {
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
"CRCX: could not start RTP processing!\n");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
goto error2;
}
/* Notify Osmux conn that CRCX was received */
if (mgcp_conn_rtp_is_osmux(conn)) {
if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "CRCX: Osmux handling failed!\n");
if (mgcp_conn_rtp_is_osmux(conn_rtp)) {
if (conn_osmux_event_rx_crcx_mdcx(conn_rtp) < 0) {
LOGPCONN(conn, DLMGCP, LOGL_ERROR, "CRCX: Osmux handling failed!\n");
goto error2;
}
}
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
"CRCX: Creating connection: port: %u\n", conn->end.local_port);
LOGPCONN(conn, DLMGCP, LOGL_DEBUG,
"CRCX: Creating connection: port: %u\n", conn_rtp->end.local_port);
/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
if (conn->mode & MGCP_CONN_RECV_ONLY &&
trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
send_dummy(endp, conn);
send_dummy(endp, conn_rtp);
LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
LOGPCONN(conn, DLMGCP, LOGL_NOTICE,
"CRCX: connection successfully created\n");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
mgcp_endp_update(endp);
/* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
bool add_epname = rq->wildcarded && trunk->trunk_type == MGCP_TRUNK_VIRTUAL;
return create_response_with_sdp(endp, conn, "CRCX", pdata->trans, add_epname, true);
return create_response_with_sdp(endp, conn_rtp, "CRCX", pdata->trans, add_epname, true);
error2:
mgcp_endp_release(endp);
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
@@ -1172,13 +1152,13 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
struct rate_ctr_group *rate_ctrs;
char new_local_addr[INET6_ADDRSTRLEN];
int error_code = 500;
int silent = 0;
int have_sdp = 0;
char *line;
const char *local_options = NULL;
const char *mode = NULL;
struct mgcp_conn_rtp *conn = NULL;
const char *conn_id = NULL;
enum mgcp_connection_mode mode = MGCP_CONN_NONE;
struct mgcp_conn *conn = NULL;
struct mgcp_conn_rtp *conn_rtp = NULL;
const char *conn_id = NULL;
int remote_osmux_cid = -2;
int rc;
@@ -1205,7 +1185,7 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "MDCX: selected endpoint not available!\n");
return create_err_response(rq->trunk, NULL, 501, "MDCX", pdata->trans);
}
if (llist_count(&endp->conns) <= 0) {
if (mgcp_endp_num_conns(endp) <= 0) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"MDCX: endpoint is not holding a connection.\n");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
@@ -1235,10 +1215,7 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
local_options = (const char *)line + 3;
break;
case 'M':
mode = (const char *)line + 3;
break;
case 'Z':
silent = strcasecmp("noanswer", line + 3) == 0;
mode = mgcp_parse_conn_mode((const char *)line + 3);
break;
case 'X':
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
@@ -1272,29 +1249,30 @@ mgcp_header_done:
return create_err_response(endp, endp, 515, "MDCX", pdata->trans);
}
conn = mgcp_conn_get_rtp(endp, conn_id);
conn = mgcp_endp_get_conn(endp, conn_id);
if (!conn) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
}
mgcp_conn_watchdog_kick(conn->conn);
mgcp_conn_watchdog_kick(conn);
if (mode) {
if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
if (mode != MGCP_CONN_NONE) {
if (mgcp_conn_set_mode(conn, mode) < 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
error_code = 517;
goto error3;
}
} else
conn->conn->mode = conn->conn->mode_orig;
} else {
conn->mode = conn->mode_orig;
}
/* Set local connection options, if present */
if (local_options) {
rc = set_local_cx_options(trunk->endpoints,
&endp->local_options, local_options);
if (rc != 0) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
"MDCX: invalid local connection options!\n");
error_code = rc;
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
@@ -1302,49 +1280,41 @@ mgcp_header_done:
}
}
conn_rtp = mgcp_conn_get_conn_rtp(conn);
OSMO_ASSERT(conn_rtp);
/* Handle codec information and decide for a suitable codec */
rc = handle_codec_info(conn, rq, have_sdp, false);
mgcp_codec_summary(conn);
rc = handle_codec_info(conn_rtp, rq, have_sdp, false);
mgcp_codec_summary(conn_rtp);
if (rc) {
error_code = rc;
goto error3;
}
/* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */
/* TODO: "codec" probably needs to be moved from endp to conn */
if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0)
rc = mgcp_conn_iuup_init(conn);
if (conn_rtp->type == MGCP_RTP_DEFAULT && strcmp(conn_rtp->end.codec->subtype_name, "VND.3GPP.IUFP") == 0)
rc = mgcp_conn_iuup_init(conn_rtp);
/* check connection mode setting */
if (conn->conn->mode != MGCP_CONN_LOOPBACK
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
&& !mgcp_rtp_end_remote_addr_available(&conn->end)) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"MDCX: selected connection mode type requires an opposite end!\n");
error_code = 527;
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
goto error3;
}
if (mgcp_conn_rtp_is_osmux(conn)) {
OSMO_ASSERT(conn->osmux.local_cid_allocated);
if (mgcp_conn_rtp_is_osmux(conn_rtp)) {
OSMO_ASSERT(conn_rtp->osmux.local_cid_allocated);
if (remote_osmux_cid < -1) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
"MDCX: Failed to parse Osmux CID!\n");
goto error3;
} else if (remote_osmux_cid == -1) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
"MDCX: wilcard in MDCX is not supported!\n");
goto error3;
} else if (conn->osmux.remote_cid_present &&
remote_osmux_cid != conn->osmux.remote_cid) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"MDCX: changing already allocated CID is not supported!\n");
goto error3;
} else if (conn_rtp->osmux.remote_cid_present &&
remote_osmux_cid != conn_rtp->osmux.remote_cid) {
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
"MDCX: changing already allocated CID is not supported!\n");
goto error3;
} else {
conn->osmux.remote_cid_present = true;
conn->osmux.remote_cid = remote_osmux_cid;
if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
conn_rtp->osmux.remote_cid_present = true;
conn_rtp->osmux.remote_cid = remote_osmux_cid;
if (conn_osmux_event_rx_crcx_mdcx(conn_rtp) < 0) {
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
"MDCX: Osmux handling failed!\n");
goto error3;
}
@@ -1355,50 +1325,44 @@ mgcp_header_done:
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) < 0) {
if (mgcp_get_local_addr(new_local_addr, conn_rtp) < 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
goto error3;
}
if (strcmp(new_local_addr, conn->end.local_addr)) {
osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
mgcp_free_rtp_port(&conn->end);
if (allocate_port(endp, conn) != 0) {
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 (allocate_port(endp, conn_rtp) != 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
goto error3;
}
}
if (setup_rtp_processing(endp, conn) != 0) {
if (setup_rtp_processing(endp, conn_rtp) != 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
goto error3;
}
mgcp_rtp_end_config(endp, 1, &conn->end);
mgcp_rtp_end_config(endp, 1, &conn_rtp->end);
/* modify */
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
"MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
LOGPCONN(conn, DLMGCP, LOGL_DEBUG,
"MDCX: modified conn:%s\n", mgcp_conn_dump(conn));
/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
if (conn->mode & MGCP_CONN_RECV_ONLY &&
trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
send_dummy(endp, conn);
send_dummy(endp, conn_rtp);
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
if (silent)
goto out_silent;
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
LOGPCONN(conn, DLMGCP, LOGL_NOTICE,
"MDCX: connection successfully modified\n");
mgcp_endp_update(endp);
return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
return create_response_with_sdp(endp, conn_rtp, "MDCX", pdata->trans, false, false);
error3:
return create_err_response(endp, endp, error_code, "MDCX", pdata->trans);
out_silent:
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
return NULL;
}
/* DLCX command handler, processes the received command */
@@ -1409,11 +1373,10 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
struct mgcp_endpoint *endp = rq->endp;
struct rate_ctr_group *rate_ctrs;
int error_code = 400;
int silent = 0;
char *line;
char stats[1048];
const char *conn_id = NULL;
struct mgcp_conn_rtp *conn = NULL;
struct mgcp_conn *conn = NULL;
unsigned int i;
/* NOTE: In this handler we can not take it for granted that the endp
@@ -1449,7 +1412,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
if (rq->wildcarded) {
int num_conns = 0;
for (i = 0; i < trunk->number_endpoints; i++) {
num_conns += llist_count(&trunk->endpoints[i]->conns);
num_conns += mgcp_endp_num_conns(trunk->endpoints[i]);
mgcp_endp_release(trunk->endpoints[i]);
}
rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
@@ -1493,9 +1456,6 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
goto error3;
}
break;
case 'Z':
silent = strcasecmp("noanswer", line + 3) == 0;
break;
default:
LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n",
line[0], line[0]);
@@ -1514,7 +1474,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
* that we drop all connections on that specific endpoint at once.
* (See also RFC3435 Section F.7) */
if (!conn_id) {
int num_conns = llist_count(&endp->conns);
int num_conns = mgcp_endp_num_conns(endp);
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
"DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
num_conns);
@@ -1531,39 +1491,33 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
}
/* Find the connection */
conn = mgcp_conn_get_rtp(endp, conn_id);
conn = mgcp_endp_get_conn(endp, conn_id);
if (!conn) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
goto error3;
}
/* save the statistics of the current connection */
mgcp_format_stats(stats, sizeof(stats), conn->conn);
mgcp_format_stats(stats, sizeof(stats), conn);
/* delete connection */
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
mgcp_conn_dump(conn->conn));
mgcp_conn_free(endp, conn_id);
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
mgcp_conn_dump(conn));
mgcp_conn_free(conn);
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
"DLCX: connection successfully deleted\n");
/* When all connections are closed, the endpoint will be released
* in order to be ready to be used by another call. */
if (llist_count(&endp->conns) <= 0) {
if (mgcp_endp_num_conns(endp) <= 0) {
mgcp_endp_release(endp);
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
}
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
if (silent)
goto out_silent;
return create_ok_resp_with_param(endp, endp, 250, "DLCX", pdata->trans, stats);
error3:
return create_err_response(endp, endp, error_code, "DLCX", pdata->trans);
out_silent:
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
return NULL;
}
/* RSIP command handler, processes the received command */
@@ -1671,7 +1625,7 @@ static void mgcp_keepalive_timer_cb(void *_trunk)
llist_for_each_entry(conn, &endp->conns, entry) {
if (conn->type == MGCP_CONN_TYPE_RTP &&
conn->mode == MGCP_CONN_RECV_ONLY)
send_dummy(endp, &conn->u.rtp);
send_dummy(endp, mgcp_conn_get_conn_rtp(conn));
}
}

View File

@@ -34,7 +34,6 @@
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp_client/fmtp.h>
#include <errno.h>
#include <stdlib.h>
@@ -56,7 +55,7 @@ struct sdp_rtp_map {
};
struct sdp_fmtp_param {
int payload_type;
const char *fmtp;
struct mgcp_codec_param param;
};
@@ -196,7 +195,11 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp
{
char *str;
char *str_ptr;
char *param_str;
unsigned int pt;
unsigned int count = 0;
char delimiter;
unsigned int amr_octet_aligned;
memset(fmtp_param, 0, sizeof(*fmtp_param));
@@ -215,13 +218,40 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp
goto error;
fmtp_param->payload_type = pt;
/* Advance pointer to the beginning of the parameter section */
/* Advance pointer to the beginning of the parameter section and
* tokenize string */
str_ptr = strstr(str_ptr, " ");
if (!str_ptr)
goto error;
str_ptr++;
fmtp_param->fmtp = talloc_strdup(ctx, str_ptr);
param_str = strtok(str_ptr, " ");
if (!param_str)
goto exit;
while (1) {
/* Make sure that we don't get trapped in an endless loop */
if (count > 256)
goto error;
/* Chop off delimiters ';' at the end */
delimiter = str_ptr[strlen(str_ptr) - 1];
if (delimiter == ';' || delimiter == ',')
str_ptr[strlen(str_ptr) - 1] = '\0';
/* AMR octet aligned parameter (see also RFC 3267, section 8.3) */
if (sscanf(param_str, "octet-align=%d", &amr_octet_aligned) == 1) {
fmtp_param->param.amr_octet_aligned_present = true;
fmtp_param->param.amr_octet_aligned = false;
if (amr_octet_aligned == 1)
fmtp_param->param.amr_octet_aligned = true;
}
param_str = strtok(NULL, " ");
if (!param_str)
break;
count++;
}
exit:
talloc_free(str);
@@ -269,13 +299,13 @@ static int audio_ip_from_sdp(struct osmo_sockaddr *dst_addr, char *sdp)
/* Pick optional fmtp parameters by payload type, if there are no fmtp
* parameters, a nullpointer is returned */
static const char *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
static struct mgcp_codec_param *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
{
unsigned int i;
for (i = 0; i < fmtp_params_len; i++) {
if (fmtp_params[i].payload_type == pt)
return fmtp_params[i].fmtp;
return &fmtp_params[i].param;
}
return NULL;
@@ -296,6 +326,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
unsigned int codecs_used = 0;
struct sdp_fmtp_param fmtp_params[MGCP_MAX_CODECS];
unsigned int fmtp_used = 0;
struct mgcp_codec_param *codec_param;
char ipbuf[INET6_ADDRSTRLEN];
char *line;
unsigned int i;
@@ -390,8 +421,8 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
/* Store parsed codec information */
for (i = 0; i < codecs_used; i++) {
const char *fmtp = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
rc = mgcp_codec_add2(conn, codecs[i].payload_type, codecs[i].map_line, fmtp);
codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
if (rc < 0)
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "failed to add codec\n");
}
@@ -405,12 +436,10 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
if (codecs_used == 0)
LOGPC(DLMGCP, LOGL_NOTICE, "none");
for (i = 0; i < codecs_used; i++) {
LOGPC(DLMGCP, LOGL_NOTICE, " %d=%s%s%s%s",
LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
rtp->codecs[i].payload_type,
strlen(rtp->codecs[i].subtype_name) ? rtp->codecs[i].subtype_name : "unknown",
rtp->codecs[i].fmtp[0] ? ",fmtp='" : "",
rtp->codecs[i].fmtp,
rtp->codecs[i].fmtp[0] ? "'" : "");
strlen(rtp->codecs[i].subtype_name) ? rtp->codecs[i].subtype_name : "unknown");
LOGPC(DLMGCP, LOGL_NOTICE, " ");
}
LOGPC(DLMGCP, LOGL_NOTICE, "\n");
@@ -418,34 +447,35 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
}
/* Add all codecs related lines to SDP payload */
static int add_codecs(struct msgb *sdp, const struct mgcp_conn_rtp *conn)
/* Add rtpmap string to the sdp payload, but only when the payload type falls
* into the dynamic payload type range */
static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name)
{
int rc;
if (payload_type >= 96 && payload_type <= 127) {
if (!audio_name)
return -EINVAL;
rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n", payload_type, audio_name);
if (rc < 0)
return -EINVAL;
}
return 0;
}
/* Add audio strings to sdp payload */
static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_types_len, int local_port)
{
int rc;
unsigned int i;
int local_port;
struct mgcp_trunk *trunk = conn->conn->endp->trunk;
if (!conn->end.codecs_assigned)
return 0;
/* Compose 'm=audio 1234 RTP/AVP 112 96 3' line, with
* - local RTP port
* - a list of all assigned payload type numbers
*/
if (mgcp_conn_rtp_is_osmux(conn))
local_port = trunk->cfg->osmux.local_port;
else
local_port = conn->end.local_port;
rc = msgb_printf(sdp, "m=audio %d RTP/AVP", local_port);
if (rc < 0)
return -EINVAL;
for (i = 0; i < conn->end.codecs_assigned; i++) {
const struct mgcp_rtp_codec *c = &conn->end.codecs[i];
rc = msgb_printf(sdp, " %d", c->payload_type);
for (i = 0; i < payload_types_len; i++) {
rc = msgb_printf(sdp, " %d", payload_types[i]);
if (rc < 0)
return -EINVAL;
}
@@ -454,45 +484,31 @@ static int add_codecs(struct msgb *sdp, const struct mgcp_conn_rtp *conn)
if (rc < 0)
return -EINVAL;
/* Compose 'a=rtpmap:N FOO' lines for codecs in above list that require it.
* e.g. GSM-FR is implicitly defined by payload type number 3, so it is enough to list 3 above;
* AMR needs a line like 'a=rtpmap:112 AMR/8000/1' in addition to listing 112 above.
*/
for (i = 0; i < conn->end.codecs_assigned; i++) {
const struct mgcp_rtp_codec *c = &conn->end.codecs[i];
if (!c->audio_name[0])
continue;
return 0;
}
/* Dynamic payload type numbers need explicit rtpmap defining the codec by "subtype name" like "AMR" or
* "GSM-HR-08". Others are defined implicitly, like GSM-FR by payload type number 3.
*
* Also, if the trunk is configured as "no sdp audio-payload send-name", omit all rtpmap lines.
*/
if (c->payload_type >= 96 && c->payload_type <= 127
&& trunk->audio_send_name) {
if (msgb_printf(sdp, "a=rtpmap:%d %s\r\n", c->payload_type, c->audio_name) < 0)
/* Add fmtp strings to sdp payload */
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
{
unsigned int i;
int rc;
for (i = 0; i < fmtp_params_len; i++) {
rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
if (rc < 0)
return -EINVAL;
/* Add amr octet align parameter */
if (fmtp_params[i].param.amr_octet_aligned_present) {
if (fmtp_params[i].param.amr_octet_aligned)
rc = msgb_printf(sdp, " octet-align=1");
else
rc = msgb_printf(sdp, " octet-align=0");
if (rc < 0)
return -EINVAL;
}
/* Compose 'a=fmtp:N foo=bar' line if fmtp is defined for this codec.
* e.g. AMR has fmtp like 'octet-align=1', 'mode-set=0,2,4,7'.
*/
if (c->fmtp[0]) {
if (msgb_printf(sdp, OSMO_SDP_PREFIX_A_FMTP "%d %s\r\n", c->payload_type, c->fmtp) < 0)
return -EINVAL;
}
else if (c->param_present) {
/* Legacy */
if (msgb_printf(sdp, OSMO_SDP_PREFIX_A_FMTP "%d %s\r\n", c->payload_type,
OSMO_SDP_AMR_SET_OCTET_ALIGN(c->param.amr_octet_aligned))
< 0)
return -EINVAL;
}
}
if (conn->end.packet_duration_ms > 0 && conn->conn->endp->trunk->audio_send_ptime) {
rc = msgb_printf(sdp, "a=ptime:%u\r\n",
conn->end.packet_duration_ms);
rc = msgb_printf(sdp, "\r\n");
if (rc < 0)
return -EINVAL;
}
@@ -510,7 +526,15 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
const char *addr)
{
const struct mgcp_rtp_codec *codec;
const char *audio_name;
int payload_type;
struct sdp_fmtp_param fmtp_param;
int rc;
int payload_types[1];
int local_port;
struct sdp_fmtp_param fmtp_params[1];
unsigned int fmtp_params_len = 0;
bool addr_is_v6;
OSMO_ASSERT(endp);
@@ -518,6 +542,11 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
OSMO_ASSERT(sdp);
OSMO_ASSERT(addr);
codec = conn->end.codec;
audio_name = codec->audio_name;
payload_type = codec->payload_type;
addr_is_v6 = osmo_ip_str_type(addr) == AF_INET6;
rc = msgb_printf(sdp,
@@ -532,14 +561,43 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
if (rc < 0)
goto buffer_too_small;
/* Add all codecs related SDP lines */
rc = add_codecs(sdp, conn);
if (rc < 0)
goto buffer_too_small;
if (payload_type >= 0) {
payload_types[0] = payload_type;
if (mgcp_conn_rtp_is_osmux(conn))
local_port = endp->trunk->cfg->osmux.local_port;
else
local_port = conn->end.local_port;
rc = add_audio(sdp, payload_types, 1, local_port);
if (rc < 0)
goto buffer_too_small;
if (endp->trunk->audio_send_name) {
rc = add_rtpmap(sdp, payload_type, audio_name);
if (rc < 0)
goto buffer_too_small;
}
if (codec->param_present) {
fmtp_param.payload_type = payload_type;
fmtp_param.param = codec->param;
fmtp_params[0] = fmtp_param;
fmtp_params_len = 1;
}
rc = add_fmtp(sdp, fmtp_params, fmtp_params_len);
if (rc < 0)
goto buffer_too_small;
}
if (conn->end.packet_duration_ms > 0 && endp->trunk->audio_send_ptime) {
rc = msgb_printf(sdp, "a=ptime:%u\r\n",
conn->end.packet_duration_ms);
if (rc < 0)
goto buffer_too_small;
}
return 0;
buffer_too_small:
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "SDP message too large for buffer\n");
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
return -1;
}

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

@@ -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);
}
}
}
@@ -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);
mgcp_rtp_end_config(endp, 1, &conn_rtp->end);
} 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

@@ -0,0 +1,38 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir) \
$(NULL)
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(TALLOC_CFLAGS) \
$(NULL)
SDP_LIBVERSION=1:0:0
# 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!
lib_LTLIBRARIES = \
libosmo-sdp.la \
$(NULL)
libosmo_sdp_la_SOURCES = \
sdp_codec.c \
sdp_codec_list.c \
sdp_msg.c \
sdp_internal.c \
fmtp.c \
$(NULL)
libosmo_sdp_la_LDFLAGS = \
$(AM_LDFLAGS) \
-version-info $(SDP_LIBVERSION) \
-no-undefined \
-export-symbols-regex '^osmo_' \
$(NULL)
libosmo_sdp_la_LIBADD = \
$(LIBOSMOCORE_LIBS) \
$(NULL)

View File

@@ -1,5 +1,5 @@
/*
* (C) 2023-2015 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* (C) 2023-2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Neels Hofmeyr
@@ -22,12 +22,27 @@
#include <ctype.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/mgcp_client/fmtp.h>
static const char *fmtp_next_option(const char *fmtp)
#include <osmocom/sdp/sdp_strings.h>
#include <osmocom/sdp/fmtp.h>
/* End of current fmtp parameter. Return a pointer to the next ';' character, if present, or the terminating '\0'. */
static const char *osmo_sdp_fmtp_end(const char *fmtp)
{
for (; fmtp && *fmtp && *fmtp != ';'; fmtp++);
for (; fmtp && isspace(*fmtp); fmtp++);
if (!fmtp)
return NULL;
for (; *fmtp && *fmtp != ';'; fmtp++);
return fmtp;
}
/* Start of next fmtp parameter. Return a pointer to the first character of the next fmtp parameter's name, or the
* terminating '\0'. */
static const char *osmo_sdp_fmtp_next(const char *fmtp)
{
if (!fmtp)
return NULL;
fmtp = osmo_sdp_fmtp_end(fmtp);
for (; *fmtp && (*fmtp == ';' || isspace(*fmtp)); fmtp++);
return fmtp;
}
@@ -37,11 +52,14 @@ static const char *fmtp_next_option(const char *fmtp)
*
* const char *fmtp_vals = "octet-align=1;mode-set=0,2,4,7";
*
* char res[23];
* if (osmo_sdp_fmtp_get_val(res, sizeof(res), fmtp_vals, "mode-set"))
* use_modeset(res);
* else
* char mode_set_str[23];
* if (osmo_sdp_fmtp_get_val(mode_set_str, sizeof(mode_set_str), fmtp_vals, "mode-set")) {
* // option 'mode-set' is present, now mode_set_str == "0,2,4,7"
* use_modeset(mode_set_str);
* } else {
* // if 'mode-set' were not present...
* use_modeset(MY_DEFAULT_MODESET);
* }
*
* \param[out] val Buffer to write the option's value to.
* \param[in] val_size Space available in val.
@@ -54,7 +72,7 @@ bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const c
const char *pos = fmtp;
const char *end;
int option_name_len = strlen(option_name);
for (; pos && *pos; pos = fmtp_next_option(pos)) {
for (; pos && *pos; pos = osmo_sdp_fmtp_next(pos)) {
if (!osmo_str_startswith(pos, option_name))
continue;
pos += option_name_len;
@@ -67,7 +85,7 @@ bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const c
if (!pos || !*pos)
return false;
end = fmtp_next_option(pos);
end = osmo_sdp_fmtp_end(pos);
OSMO_ASSERT(end);
if (val && val_size)
osmo_strlcpy(val, pos, OSMO_MIN(val_size, end - pos + 1));
@@ -86,17 +104,16 @@ bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const c
* \param[in] default_value If option_name is not present or cannot be parsed as integer, return this instead.
* \return the integer value when the option was found and actually an integer, default_value otherwise.
*/
int osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int default_value)
int64_t osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int64_t default_value)
{
char val[128];
if (!osmo_sdp_fmtp_get_val(val, sizeof(val), fmtp, option_name))
return default_value;
if (!val[0])
return default_value;
int i;
if (osmo_str_to_int(&i, val, 10, 0, 1)) {
int64_t i;
if (osmo_str_to_int64(&i, val, 10, INT64_MIN, INT64_MAX)) {
/* error parsing number */
LOGP(DLMGCP, LOGL_ERROR, "Invalid number in fmtp parameter '%s': '%s'\n", option_name, val);
return default_value;
}
return i;
@@ -116,5 +133,61 @@ int osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int default
*/
bool osmo_sdp_fmtp_amr_is_octet_aligned(const char *fmtp)
{
return osmo_sdp_fmtp_get_int(fmtp, OSMO_SDP_NAME_AMR_OCTET_ALIGN, 0) == 1;
return osmo_sdp_fmtp_get_int(fmtp, OSMO_SDP_STR_AMR_OCTET_ALIGN, 0) == 1;
}
static void strip_whitespace(char *str)
{
char *i = str;
char *o = str;
for (; *i; i++, o++) {
while (isspace(*i))
i++;
*o = *i;
if (!*i)
break;
}
}
/* Return true when the two AMR type fmtp strings can be considered equivalent.
* - Omission of octet-align is equivalent to having octet-align=0 present (0 is the default).
* - Omission of 'mode-set' means, match any and all codec modes. So if either a or b have no 'mode-set', it's a match.
* If both have 'mode-set' present, they must be identical to match. Do not sort the mode-set string, but strip
* whitespace.
* - TODO all other parameters are currently completely ignored.
*/
bool osmo_sdp_fmtp_amr_match(const char *a, const char *b)
{
char a_modeset[32] = {};
char b_modeset[32] = {};
bool a_ok;
bool b_ok;
if (!a)
a = "";
if (!b)
b = "";
/* octet-align=1. Omission means octet-align=0 */
if (osmo_sdp_fmtp_amr_is_octet_aligned(a) != osmo_sdp_fmtp_amr_is_octet_aligned(b))
return false;
/* mode-set=0,1,2,3,4,5,6,7 */
a_ok = osmo_sdp_fmtp_get_val(a_modeset, sizeof(a_modeset), a, "mode-set");
b_ok = osmo_sdp_fmtp_get_val(b_modeset, sizeof(b_modeset), b, "mode-set");
if (a_ok && b_ok) {
/* Strip whitespace: We don't know what remote SDP peers may throw at us. There could be whitespace
* around the separators like 'mode-set=2,3 ; octet-align=1', which may show up here as whitespace in
* the value string as "2,3 ", which would mismatch "2,3". */
strip_whitespace(a_modeset);
strip_whitespace(b_modeset);
if (strcmp(a_modeset, b_modeset))
return false;
}
/* TODO: treat other AMR traits, see RFC4867 8.1. Maybe generically match all values that are present?
* So far we have no need for other values than octet-align and mode-set. */
/* No mismatch found, it's a match */
return true;
}

251
src/libosmo-sdp/sdp_codec.c Normal file
View File

@@ -0,0 +1,251 @@
/* Codec management in SDP messages. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <ctype.h>
#include <osmocom/core/utils.h>
#include <osmocom/sdp/fmtp.h>
#include <osmocom/sdp/sdp_codec.h>
#include <osmocom/sdp/sdp_internal.h>
struct osmo_sdp_codec *osmo_sdp_codec_alloc(void *ctx)
{
return talloc_zero(ctx, struct osmo_sdp_codec);
}
int osmo_sdp_codec_set(struct osmo_sdp_codec *c,
int8_t payload_type, const char *encoding_name, unsigned int rate, const char *fmtp)
{
c->rate = rate;
osmo_sdp_codec_set_encoding_name(c, encoding_name);
osmo_sdp_codec_set_fmtp(c, fmtp);
c->payload_type = payload_type;
return 0;
}
int osmo_sdp_codec_set_encoding_name(struct osmo_sdp_codec *c, const char *encoding_name)
{
osmo_talloc_replace_string(c, &c->encoding_name, encoding_name);
return 0;
}
int osmo_sdp_codec_set_fmtp(struct osmo_sdp_codec *c, const char *fmtp)
{
osmo_talloc_replace_string(c, &c->fmtp, fmtp);
return 0;
}
bool osmo_sdp_codec_is_set(const struct osmo_sdp_codec *a)
{
return a && a->encoding_name && a->encoding_name[0];
}
int osmo_sdp_codec_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec *codec)
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
if (!codec) {
OSMO_STRBUF_PRINTF(sb, "NULL");
return sb.chars_needed;
}
if (codec->encoding_name && codec->encoding_name[0])
OSMO_STRBUF_PRINTF(sb, "%s", codec->encoding_name);
if (codec->rate != 8000)
OSMO_STRBUF_PRINTF(sb, "/%u", codec->rate);
if (codec->fmtp && codec->fmtp[0])
OSMO_STRBUF_PRINTF(sb, ":%s", codec->fmtp);
OSMO_STRBUF_PRINTF(sb, "#%d", codec->payload_type);
return sb.chars_needed;
}
char *osmo_sdp_codec_to_str_c(void *ctx, const struct osmo_sdp_codec *codec)
{
OSMO_NAME_C_IMPL(ctx, 32, "osmo_sdp_codec_to_str_c-ERROR", osmo_sdp_codec_to_str_buf, codec)
}
/*! Parse a codec string as from osmo_sdp_codec_to_str_buf() back to an osmo_sdp_codec struct.
* Write the parsed result to *dst, using ctx as talloc parent.
* The input string is like <encoding_name>[:<fmtp-string>][#<payload-type-nr>]
* for example:
* "FOO:my-fmtp=1;my-other-fmtp=2#42"
* Note that ';' are separators only within the fmtp string. This function does not separate those. In above example,
* the fmtp string part is "my-fmtp=val;my-other-fmtp=val2" and ends up in dst->ftmp as-is.
* Parse at most str_len characters, or the entire string when str_len < 0 or str_len > strlen(str).
* Return 0 on success, negative on failure. */
int osmo_sdp_codec_from_str(struct osmo_sdp_codec *dst, const char *str, int str_len)
{
const char *pos = str;
const char *str_end = str + (str_len >= 0 ? str_len : strlen(str));
const char *p2;
struct token token_encoding_name = {};
struct token token_rate = {};
struct token token_fmtp = {};
struct token token_payload_type = {};
struct token *new_t = NULL;
/* start with the encoding name */
struct token *t = &token_encoding_name;
t->start = pos;
for (; pos < str_end; pos++) {
new_t = NULL;
switch (*pos) {
case '/':
new_t = &token_rate;
break;
case ':':
new_t = &token_fmtp;
break;
case '#':
/* count this '#' only if there is no other one following. It might be part of a fmtp. */
for (p2 = pos + 1; p2 < str_end; p2++)
if (*p2 == '#')
break;
if (p2 < str_end && *p2 == '#')
break;
/* This is the last '#' in the string. Count it only when a digit follows. */
if (!isdigit(pos[1]))
break;
new_t = &token_payload_type;
break;
default:
break;
}
if (!new_t)
continue;
/* If we already have a token for a start character, don't start it again. These may be part of a fmtp
* string. */
if (new_t == t)
continue;
t->end = pos;
t = new_t;
t->start = pos + 1;
}
t->end = pos;
token_copy(dst, &dst->encoding_name, &token_encoding_name);
if (token_rate.start)
dst->rate = atoi(token_rate.start);
else
dst->rate = 8000;
token_copy(dst, &dst->fmtp, &token_fmtp);
if (token_payload_type.start)
dst->payload_type = atoi(token_payload_type.start);
return 0;
}
/* Compare both payload type number and fmtp string 1:1 */
const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_exact = {
.payload_type = true,
.encoding_name = true,
.rate = true,
.fmtp = OSMO_SDP_CMP_EXACT,
};
/* Ignore payload type number; compare fmtp string by meaning when possible, else 1:1 */
const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent = {
.payload_type = false,
.encoding_name = true,
.rate = true,
.fmtp = OSMO_SDP_CMP_EQUIVALENT,
};
/* Compare only encoding name */
const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_name = {
.payload_type = false,
.encoding_name = true,
.rate = false,
.fmtp = OSMO_SDP_CMP_IGNORE,
};
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent;
static inline int strcmp_safe(const char *a, const char *b)
{
return strcmp(a ? : "", b ? : "");
}
/*! Compare encoding name, rate and fmtp, returning cmp result: -1 if a < b, 0 if a == b, 1 if a > b.
* Compare as defined in 'cmp':
* If cmpf->payload_type is false, ignore payload_type numbers.
* If cmpf->rate is false, ignore rate.
* If cmpf->fmtp is OSMO_SDP_CMP_IGNORE, ignore fmtp strings.
* If cmpf->fmtp is OSMO_SDP_CMP_EXACT, use strcmp() to match fmtp 1:1.
* If cmpf->fmtp is OSMO_SDP_CMP_EQUIVALENT, use specific fmtp knowledge to match equivalent entries;
* - AMR fmtp matching is done by osmo_sdp_fmtp_amr_match().
* - for all others, still compare fmtp 1:1.
*/
int osmo_sdp_codec_cmp(const struct osmo_sdp_codec *a, const struct osmo_sdp_codec *b,
const struct osmo_sdp_codec_cmp_flags *cmpf)
{
int cmp;
if (a == b)
return 0;
if (!a)
return -1;
if (!b)
return 1;
if (!cmpf)
cmpf = &osmo_sdp_codec_cmp_exact;
if (cmpf->encoding_name) {
cmp = strcmp_safe(a->encoding_name, b->encoding_name);
if (cmp)
return cmp;
}
if (cmpf->rate) {
cmp = OSMO_CMP(a->rate, b->rate);
if (cmp)
return cmp;
}
switch (cmpf->fmtp) {
default:
case OSMO_SDP_CMP_EXACT:
cmp = strcmp_safe(a->fmtp, b->fmtp);
break;
case OSMO_SDP_CMP_EQUIVALENT:
/* In case of AMR, allow logical matching; we only need to do that if the strings differ. */
cmp = strcmp_safe(a->fmtp, b->fmtp);
if (cmp
&& !strcmp_safe("AMR", a->encoding_name)
&& osmo_sdp_fmtp_amr_match(a->fmtp, b->fmtp))
cmp = 0;
break;
case OSMO_SDP_CMP_IGNORE:
cmp = 0;
break;
}
if (cmp)
return cmp;
if (cmpf->payload_type)
cmp = OSMO_CMP(a->payload_type, b->payload_type);
return cmp;
}

View File

@@ -0,0 +1,367 @@
/* Codec management in SDP messages. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <string.h>
#include <osmocom/core/utils.h>
#include <osmocom/sdp/sdp_codec_list.h>
struct osmo_sdp_codec_list *osmo_sdp_codec_list_alloc(void *ctx)
{
struct osmo_sdp_codec_list *codec_list = talloc_zero(ctx, struct osmo_sdp_codec_list);
INIT_LLIST_HEAD(&codec_list->list);
return codec_list;
}
/*! Free all items contained in this list, do not free the list itself (leave an empty list). */
void osmo_sdp_codec_list_free_items(struct osmo_sdp_codec_list *codec_list)
{
struct osmo_sdp_codec *c;
while ((c = osmo_sdp_codec_list_first(codec_list))) {
osmo_sdp_codec_list_remove_entry(c);
talloc_free(c);
}
}
struct osmo_sdp_codec *osmo_sdp_codec_list_add_empty(struct osmo_sdp_codec_list *codec_list)
{
struct osmo_sdp_codec *c = osmo_sdp_codec_alloc(codec_list);
llist_add_tail(&c->entry, &codec_list->list);
return c;
}
int8_t osmo_sdp_codec_list_get_unused_dyn_pt_nr(const struct osmo_sdp_codec_list *codec_list, int8_t suggest_pt_nr)
{
bool present[127 - 96 + 1] = {};
const struct osmo_sdp_codec *c;
bool suggest_pt_nr_exists = false;
int i;
osmo_sdp_codec_list_foreach (c, codec_list) {
if (c->payload_type >= 96 && c->payload_type <= 127)
present[c->payload_type - 96] = true;
if (c->payload_type == suggest_pt_nr)
suggest_pt_nr_exists = true;
}
if (!suggest_pt_nr_exists)
return suggest_pt_nr;
/* The desired number is already taken, see which of the dynamic types is not taken yet */
for (i = 96; i <= 127; i++) {
/* For dynamic allocations, skip these predefined numbers, taken from enum mgcp_codecs:
* CODEC_GSMEFR_8000_1 = 110, 3GPP TS 48.103 table 5.4.2.2.1
* CODEC_GSMHR_8000_1 = 111, 3GPP TS 48.103 table 5.4.2.2.1
* CODEC_AMR_8000_1 = 112, 3GPP TS 48.103 table 5.4.2.2.1
* CODEC_AMRWB_16000_1 = 113, 3GPP TS 48.103 table 5.4.2.2.1
* CODEC_CLEARMODE = 120, 3GPP TS 48.103 table 5.4.2.2.1
*/
if (i >= 110 && i <= 113)
continue;
else if (i == 120)
continue;
if (!present[i - 96])
return i;
}
return -1;
}
/*! Allocate a new entry in codec_list and copy codec's values to it.
* If once is NULL, unconditionally add a new codec entry.
* If once is non-NULL, do not add a new entry when the list already contains a matching entry; for determining a match,
* use the once->flags. For example, if once = &osmo_sdp_codec_cmp_equivalent, look up if codec_list has a similar
* codec, and add the new entry only if it is not listed.
* See osmo_sdp_codec_cmp() and osmo_sdp_fmtp_amr_match() for details.
* Return the new entry, or the equivalent entry already present in the list.
*/
struct osmo_sdp_codec *osmo_sdp_codec_list_add(struct osmo_sdp_codec_list *codec_list,
const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *once, bool pick_unused_pt_nr)
{
struct osmo_sdp_codec *new_entry;
int8_t payload_type;
if (once) {
struct osmo_sdp_codec *c;
osmo_sdp_codec_list_foreach (c, codec_list)
if (!osmo_sdp_codec_cmp(codec, c, once))
return c;
}
/* Adjust payload_type number? */
payload_type = codec->payload_type;
if (pick_unused_pt_nr)
payload_type = osmo_sdp_codec_list_get_unused_dyn_pt_nr(codec_list, payload_type);
/* Take provided values, possibly modified payload_type */
new_entry = osmo_sdp_codec_list_add_empty(codec_list);
osmo_sdp_codec_set(new_entry, payload_type, codec->encoding_name, codec->rate, codec->fmtp);
return new_entry;
}
/*! Remove and free all entries from the codec_list that match the given codec according to osmo_sdp_codec_cmp(cmpf).
* Return the number of entries freed. */
int osmo_sdp_codec_list_remove(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *cmpf)
{
struct osmo_sdp_codec *i, *j;
int count = 0;
osmo_sdp_codec_list_foreach_safe (i, j, codec_list) {
if (osmo_sdp_codec_cmp(i, codec, cmpf))
continue;
osmo_sdp_codec_list_remove_entry(i);
talloc_free(i);
count++;
}
return count;
}
/*! Unlink an osmo_sdp_codec from an osmo_sdp_codec_list, if the codec instance is part of a list. Do not free the
* struct osmo_sdp_codec.
*/
void osmo_sdp_codec_list_remove_entry(struct osmo_sdp_codec *codec)
{
/* The codec is not part of a list in these cases:
* After talloc_zero(), next == NULL.
* After llist_del(), next == LLIST_POISON1. */
if (codec->entry.next != NULL
&& codec->entry.next != (struct llist_head *)LLIST_POISON1)
llist_del(&codec->entry);
}
static inline int strcmp_safe(const char *a, const char *b)
{
return strcmp(a ? : "", b ? : "");
}
/*! Short single-line representation of a list of SDP audio codecs, convenient for logging.
* If summarize == true, collapse variants of the same encoding_name (in practice, don't show all of the various AMR
* fmtp permutations). If summarize == false, print each and every codec in full.
*/
int osmo_sdp_codec_list_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec_list *codec_list, bool summarize)
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
const struct osmo_sdp_codec *codec;
bool first;
if (llist_empty(&codec_list->list)) {
OSMO_STRBUF_PRINTF(sb, "(no-codecs)");
return sb.chars_needed;
}
if (!summarize) {
first = true;
osmo_sdp_codec_list_foreach (codec, codec_list) {
if (!first)
OSMO_STRBUF_PRINTF(sb, " ");
OSMO_STRBUF_APPEND(sb, osmo_sdp_codec_to_str_buf, codec);
first = false;
}
return sb.chars_needed;
}
/* summarize */
first = true;
osmo_sdp_codec_list_foreach (codec, codec_list) {
const struct osmo_sdp_codec *c2;
int count = 0;
bool various_pt = false;
/* When this encoding name has been handled before, skip it now. */
osmo_sdp_codec_list_foreach (c2, codec_list) {
if (c2 == codec)
break;
if (!strcmp_safe(codec->encoding_name, c2->encoding_name)) {
count = 1;
break;
}
}
if (count)
continue;
/* Not seen this encoding_name before, count total occurences */
count = 0;
osmo_sdp_codec_list_foreach (c2, codec_list) {
if (!strcmp_safe(codec->encoding_name, c2->encoding_name)) {
count++;
if (codec->payload_type != c2->payload_type)
various_pt = true;
}
}
if (!first)
OSMO_STRBUF_PRINTF(sb, " ");
if (count > 1)
OSMO_STRBUF_PRINTF(sb, "%d*", count);
OSMO_STRBUF_PRINTF(sb, "%s", codec->encoding_name);
if (!various_pt)
OSMO_STRBUF_PRINTF(sb, "#%d", codec->payload_type);
first = false;
}
return sb.chars_needed;
}
char *osmo_sdp_codec_list_to_str_c(void *ctx, const struct osmo_sdp_codec_list *codec_list, bool summarize)
{
OSMO_NAME_C_IMPL(ctx, 128, "osmo_sdp_codec_list_to_str_c-ERROR", osmo_sdp_codec_list_to_str_buf, codec_list, summarize)
}
/*! Return first entry, or NULL if the list is empty. */
struct osmo_sdp_codec *osmo_sdp_codec_list_first(const struct osmo_sdp_codec_list *list)
{
return llist_first_entry_or_null(&list->list, struct osmo_sdp_codec, entry);
}
/*! Move entries matching 'codec' to the front of the list. Matching is done via osmo_sdp_codec_cmp(cmpf).
* Return the number of matches that are now at the front of the list.
*/
int osmo_sdp_codec_list_move_to_first(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *cmpf)
{
struct llist_head *head = &codec_list->list;
struct osmo_sdp_codec *i, *j;
int matches_found = 0;
osmo_sdp_codec_list_foreach_safe (i, j, codec_list) {
if (osmo_sdp_codec_cmp(codec, i, cmpf))
continue;
/* It's a match, move to the head */
osmo_sdp_codec_list_remove_entry(i);
llist_add(&i->entry, head);
matches_found++;
/* If more matches show up later, add them *after* the one just moved to the front. */
head = &i->entry;
}
return matches_found;
}
/*! Compare two lists of SDP codecs, returning cmp result: -1 if a < b, 0 if a == b, 1 if a > b.
* The two lists are compared in order, item by item, using osmo_sdp_codec_cmp(cmpf).
*/
int osmo_sdp_codec_list_cmp(const struct osmo_sdp_codec_list *a, const struct osmo_sdp_codec_list *b,
const struct osmo_sdp_codec_cmp_flags *cmpf)
{
const struct llist_head *a_start;
const struct llist_head *a_pos;
const struct llist_head *b_start;
const struct llist_head *b_pos;
int cmp;
/* NULL pointer == empty list */
if (a && llist_empty(&a->list))
a = NULL;
if (b && llist_empty(&a->list))
b = NULL;
/* are one or both empty? */
if (a == b)
return 0;
if (!a)
return -1;
if (!b)
return 1;
/* compare item by item */
a_start = &a->list;
a_pos = a_start->next;
b_start = &b->list;
b_pos = b_start->next;
for (; a_pos != a_start; a_pos = a_pos->next, b_pos = b_pos->next) {
const struct osmo_sdp_codec *codec_a;
const struct osmo_sdp_codec *codec_b;
if (b_pos == b_start) {
/* there is an entry in a, but b has already ended. mismatch. */
return 1;
}
codec_a = llist_entry(a_pos, struct osmo_sdp_codec, entry);
codec_b = llist_entry(b_pos, struct osmo_sdp_codec, entry);
cmp = osmo_sdp_codec_cmp(codec_a, codec_b, cmpf);
if (cmp)
return cmp;
}
if (b_pos != b_start) {
/* 'a' has ended, but 'b' has more items. mismatch. */
return -1;
}
/* full match. */
return 0;
}
/*! Leave only those codecs in 'dst' that are also present in 'other'.
* The matching is made by osmo_sdp_codec_cmp(cmpf).
* If translate_payload_type_numbers has an effect if 'dst' and 'other' have mismatching payload_type numbers for the
* same SDP codec descriptions. If translate_payload_type_numbers is true, take the payload_type numbers from 'other'.
* If false, keep payload_type numbers in 'dst' unchanged. */
void osmo_sdp_codec_list_intersection(struct osmo_sdp_codec_list *dst, const struct osmo_sdp_codec_list *other,
const struct osmo_sdp_codec_cmp_flags *cmpf,
bool translate_payload_type_numbers)
{
struct osmo_sdp_codec *i, *j;
osmo_sdp_codec_list_foreach_safe (i, j, dst) {
struct osmo_sdp_codec *o;
struct osmo_sdp_codec *match = NULL;
osmo_sdp_codec_list_foreach (o, other) {
if (osmo_sdp_codec_cmp(i, o, cmpf))
continue;
match = o;
break;
}
if (!match) {
osmo_sdp_codec_list_remove_entry(i);
talloc_free(i);
continue;
}
if (translate_payload_type_numbers)
i->payload_type = match->payload_type;
}
}
/* Find an entry for the given payload_type number in the given list of codecs. */
struct osmo_sdp_codec *osmo_sdp_codec_list_by_payload_type(struct osmo_sdp_codec_list *codec_list, int8_t payload_type)
{
struct osmo_sdp_codec *codec;
osmo_sdp_codec_list_foreach(codec, codec_list) {
if (codec->payload_type == payload_type)
return codec;
}
return NULL;
}
bool osmo_sdp_codec_list_is_empty(const struct osmo_sdp_codec_list *codec_list)
{
if (!codec_list)
return true;
return llist_empty(&codec_list->list);
}

View File

@@ -0,0 +1,121 @@
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stddef.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/sdp/sdp_internal.h>
/* Copy a string from t->start to t->end, return as talloc allocated under ctx in *dst.
* If *dst is non-NULL, talloc_free(*dst) first. */
void token_copy(void *ctx, char **dst, const struct token *t)
{
size_t len;
if (*dst)
talloc_free(*dst);
if (!t->start || !(t->end > t->start)) {
*dst = NULL;
return;
}
len = t->end - t->start;
*dst = talloc_size(ctx, len + 1);
osmo_strlcpy(*dst, t->start, len + 1);
talloc_set_name_const(*dst, *dst);
}
const char *token_chr(const struct token *src, char c)
{
const char *pos;
for (pos = src->start; *pos && pos < src->end; pos++) {
if (*pos == c)
return pos;
}
return NULL;
}
const char *token_chrs(const struct token *src, const char *chrs)
{
const char *pos;
for (pos = src->start; pos && *pos && pos < src->end; pos++) {
if (strchr(chrs, *pos))
return pos;
}
return NULL;
}
void token_next(struct token *t, const char *str, const char *end, const char *separators)
{
t->start = str;
while (strchr(separators, *t->start) && t->start < end)
t->start++;
t->end = t->start;
while (!strchr(separators, *t->end) && t->end < end)
t->end++;
}
/* Convert the token to an integer, and return the character afer the integer string that was parsed.
* The difference to osmo_str_to_int64() is that this guarantees to only access the memory from t->start to t->end. */
const char *token_to_int64(int64_t *result, const struct token *t, int base, int min_val, int max_val)
{
/* copy the number section over to a temporary buffer, because t->end may terminate the string anywhere, and
* functions like strtoll (which osmo_str_to_int64() uses) are not able to stay within a strict buffer length
* unless the string is zero terminated at the buffer boundary. */
char buf[32];
const char *int_end = t->start;
if (int_end < t->end && *int_end == '-')
int_end++;
while (int_end < t->end && isdigit(*int_end))
int_end++;
if (int_end - t->start >= sizeof(buf))
return NULL;
osmo_strlcpy(buf, t->start, int_end - t->start + 1);
if (osmo_str_to_int64(result, buf, base, min_val, max_val) != 0)
return NULL;
return int_end;
}
/* Convenience: like token_to_int64() but with a plain int. */
const char *token_to_int(int *result, const struct token *t, int base, int min_val, int max_val)
{
int64_t val;
const char *rc = token_to_int64(&val, t, base, min_val, max_val);
if (val < INT_MIN) {
if (result)
*result = INT_MIN;
return NULL;
}
if (val > INT_MAX) {
if (result)
*result = INT_MAX;
return NULL;
}
if (result)
*result = (int)val;
return rc;
}

517
src/libosmo-sdp/sdp_msg.c Normal file
View File

@@ -0,0 +1,517 @@
/* Implementation for SDP message encoding and decoding */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <inttypes.h>
#include <errno.h>
#include <ctype.h>
#include <limits.h>
#include <osmocom/core/utils.h>
#include <osmocom/sdp/sdp_msg.h>
#include <osmocom/sdp/sdp_strings.h>
#include <osmocom/sdp/sdp_internal.h>
static const char * const mdir_str[] = {
[OSMO_SDP_MDIR_UNSET] = "-",
[OSMO_SDP_MDIR_SENDONLY] = OSMO_SDP_STR_SENDONLY,
[OSMO_SDP_MDIR_RECVONLY] = OSMO_SDP_STR_RECVONLY,
[OSMO_SDP_MDIR_SENDRECV] = OSMO_SDP_STR_SENDRECV,
[OSMO_SDP_MDIR_INACTIVE] = OSMO_SDP_STR_INACTIVE,
};
/*! Convert struct osmo_sdp_msg to the actual SDP protocol representation. */
int osmo_sdp_msg_encode_buf(char *dst, size_t dst_size, const struct osmo_sdp_msg *sdp)
{
const struct osmo_sdp_codec *codec;
struct osmo_strbuf sb = { .buf = dst, .len = dst_size };
const char *oip;
char oipv;
const char *ip;
char ipv;
if (!sdp) {
OSMO_STRBUF_PRINTF(sb, "%s", "");
return sb.chars_needed;
}
oip = sdp->origin.addr.ip[0] ? sdp->origin.addr.ip : "0.0.0.0";
oipv = (osmo_ip_str_type(oip) == AF_INET6) ? '6' : '4';
ip = sdp->rtp.ip[0] ? sdp->rtp.ip : "0.0.0.0";
ipv = (osmo_ip_str_type(oip) == AF_INET6) ? '6' : '4';
OSMO_STRBUF_PRINTF(sb,
"v=0\r\n"
"o=%s %s %s IN IP%c %s\r\n"
"s=%s\r\n"
"c=IN IP%c %s\r\n"
"t=%"PRId64" %"PRId64"\r\n"
"m=audio %d RTP/AVP",
sdp->origin.username ? : "libosmo-sdp",
sdp->origin.sess_id ? : "0", sdp->origin.sess_version ? : "0",
oipv, oip,
sdp->session_name ? : "-",
ipv, ip,
sdp->time_active.start,
sdp->time_active.stop,
sdp->rtp.port);
/* Append all payload type numbers to 'm=audio <port> RTP/AVP 3 4 112' line */
osmo_sdp_codec_list_foreach(codec, sdp->codecs)
OSMO_STRBUF_PRINTF(sb, " %d", codec->payload_type);
OSMO_STRBUF_PRINTF(sb, "\r\n");
/* Add details for all codecs */
osmo_sdp_codec_list_foreach(codec, sdp->codecs) {
if (!osmo_sdp_codec_is_set(codec))
continue;
OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_RTPMAP) "%d %s/%d\r\n", codec->payload_type, codec->encoding_name,
codec->rate > 0 ? codec->rate : 8000);
if (codec->fmtp && codec->fmtp[0])
OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_FMTP) "%d %s\r\n", codec->payload_type, codec->fmtp);
}
if (sdp->ptime)
OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_PTIME) "%d\r\n", sdp->ptime);
if (sdp->media_direction != OSMO_SDP_MDIR_UNSET && sdp->media_direction < ARRAY_SIZE(mdir_str))
OSMO_STRBUF_PRINTF(sb, "a=%s\r\n", mdir_str[sdp->media_direction]);
return sb.chars_needed;
}
char *osmo_sdp_msg_encode_c(void *ctx, const struct osmo_sdp_msg *sdp)
{
OSMO_NAME_C_IMPL(ctx, 256, "osmo_sdp_msg_to_str_c-ERROR", osmo_sdp_msg_encode_buf, sdp)
}
/* Return the first line ending (or the end of the string) at or after the given string position. */
static const char *get_line_end(const struct token *src)
{
const char *line_end = token_chrs(src, "\r\n");
if (!line_end)
return src->end;
return line_end;
}
/* See if str starts with an attribute like "rtpmap:" or "sendrecv\r\n".
* For example:
* token_is_attrib({"foo: bar"}, "foo", ':') --> " bar"
* token_is_attrib({"sendrev\n"}, "sendrecv", 0) --> "\n"
* token_is_attrib({"sendrev\n"}, "foo", 0) --> NULL
* On mismatch, return NULL.
* On match, return the string after the expect_next_char.
* For expect_next_char == 0, match both the end of the string as well as a line ending character ('\r' or '\n'), and in
* both cases return the string directly after attrib_name.
*/
static const char *token_is_attrib(const struct token *str, const char *attrib_name, char expect_next_char)
{
const char *next_c;
int attrib_name_len = strlen(attrib_name);
if (str->start + attrib_name_len > str->end)
return NULL;
if (!osmo_str_startswith(str->start, attrib_name))
return NULL;
next_c = str->start + strlen(attrib_name);
if (next_c > str->end)
return NULL;
if (next_c == str->end) {
if (!expect_next_char)
return str->end;
return NULL;
}
if (expect_next_char == *next_c)
return next_c + 1;
/* Treat expect_next_char == \0 as equivalent with line end */
if (!expect_next_char && strchr("\r\n", *next_c))
return next_c;
return NULL;
}
static enum osmo_sdp_media_direcion_e check_for_media_direction(const struct token *str)
{
int i;
for (i = 0; i < ARRAY_SIZE(mdir_str); i++) {
if (i == OSMO_SDP_MDIR_UNSET)
continue;
if (token_is_attrib(str, mdir_str[i], 0))
return i;
}
return OSMO_SDP_MDIR_UNSET;
}
static struct osmo_sdp_codec *find_or_create_payload_type(struct osmo_sdp_msg *sdp, unsigned int payload_type)
{
struct osmo_sdp_codec *codec;
codec = osmo_sdp_codec_list_by_payload_type(sdp->codecs, payload_type);
if (!codec) {
codec = osmo_sdp_codec_list_add_empty(sdp->codecs);
codec->payload_type = payload_type;
codec->rate = 8000;
}
return codec;
}
static void next_token(struct token *t, const char *str, const char *end)
{
token_next(t, str, end, " \t");
}
/* parse a line like 'a=rtpmap:0 PCMU/8000', 'a=fmtp:112 octet-align=1; mode-set=4', 'a=ptime:20'.
* The src should point at the character after 'a=', e.g. at the start of 'rtpmap', 'fmtp', 'ptime'
*/
static int sdp_parse_attrib(struct osmo_sdp_msg *sdp, const struct token *src)
{
unsigned int payload_type;
struct osmo_sdp_codec *codec;
enum osmo_sdp_media_direcion_e mdir;
const char *line_end = get_line_end(src);
const char *next;
int nr;
struct token t;
if ((next = token_is_attrib(src, OSMO_SDP_STR_RTPMAP, ':'))) {
/* "a=rtpmap:96 AMR/8000" */
next_token(&t, next, line_end);
next = token_to_int(&nr, &t, 10, 0, 127);
if (next != t.end)
return -EINVAL;
payload_type = nr;
token_next(&t, next, line_end, " \t/");
if (t.start >= t.end)
return -EINVAL;
next = t.end;
codec = find_or_create_payload_type(sdp, payload_type);
token_copy(codec, &codec->encoding_name, &t);
token_next(&t, next, line_end, " \t/");
if (t.start >= t.end) {
/* There should be a "/8000" here. If it is missing, let's not be strict about it. */
codec->rate = 8000;
} else {
next = token_to_int(&nr, &t, 10, 0, INT_MAX);
if (next != t.end)
return -EINVAL;
}
/* optional channel number, i.e. another "/1" */
token_next(&t, t.end, line_end, " \t/");
if (t.end > t.start) {
int channels;
next = token_to_int(&channels, &t, 10, 0, INT_MAX);
if (next != t.end)
return -EINVAL;
if (channels != 1)
return -ENOTSUP;
}
}
else if ((next = token_is_attrib(src, OSMO_SDP_STR_FMTP, ':'))) {
/* "a=fmtp:112 octet-align=1;mode-set=0,1,2,3" */
next_token(&t, next, line_end);
next = token_to_int(&nr, &t, 10, 0, 127);
if (next != t.end)
return -EINVAL;
payload_type = nr;
codec = find_or_create_payload_type(sdp, payload_type);
/* "octet-align..." token */
next_token(&t, next, line_end);
if (t.start >= line_end)
return -EINVAL;
t.end = line_end;
token_copy(codec, &codec->fmtp, &t);
}
else if ((next = token_is_attrib(src, OSMO_SDP_STR_PTIME, ':'))) {
/* "a=ptime:20" */
next_token(&t, next, line_end);
next = token_to_int(&nr, &t, 10, 1, INT_MAX);
if (!next)
return -EINVAL;
/* RFC8866 6.4: it could also be a 'real' nr, but we don't support that. */
if (next < t.end)
return -ENOTSUP;
sdp->ptime = nr;
}
/* "a=sendrecv" ... */
else if ((mdir = check_for_media_direction(src)) != OSMO_SDP_MDIR_UNSET) {
sdp->media_direction = mdir;
}
return 0;
}
static const struct value_string fixed_payload_types[] = {
{ 0, "PCMU" },
{ 3, "GSM" },
{ 8, "PCMA" },
{ 18, "G729" },
{ 110, "GSM-EFR" },
{ 111, "GSM-HR-08" },
{ 112, "AMR" },
{ 113, "AMR-WB" },
{}
};
/* Parse a line like 'm=audio 16398 RTP/AVP 0 3 8 96 112', starting after the '=' */
static int sdp_parse_media_description(struct osmo_sdp_msg *sdp, const struct token *src)
{
unsigned int port;
struct token t;
const char *line_end = get_line_end(src);
if (sscanf(src->start, "audio %u RTP/AVP", &port) < 1)
return -ENOTSUP;
if (port > 0xffff)
return -EINVAL;
sdp->rtp.port = port;
/* skip "audio 12345 RTP/AVP ", i.e. 3 tokens on */
next_token(&t, src->start, line_end);
next_token(&t, t.end, line_end);
next_token(&t, t.end, line_end);
/* first payload type number */
next_token(&t, t.end, line_end);
/* Parse listing of payload type numbers after "RTP/AVP" */
while (t.start < line_end) {
unsigned int payload_type;
struct osmo_sdp_codec *codec;
const char *encoding_name;
if (sscanf(t.start, "%u", &payload_type) < 1)
return -EINVAL;
codec = find_or_create_payload_type(sdp, payload_type);
/* Fill in encoding name for fixed payload types */
encoding_name = get_value_string_or_null(fixed_payload_types, codec->payload_type);
if (encoding_name)
osmo_talloc_replace_string(codec, &codec->encoding_name, encoding_name);
next_token(&t, t.end, line_end);
}
return 0;
}
/* parse a line like 'c=IN IP4 192.168.11.151' starting after the '=' */
static int sdp_parse_connection_info(struct osmo_sdp_msg *sdp, const struct token *src)
{
char ipv[10];
char addr_str[INET6_ADDRSTRLEN];
if (sscanf(src->start, "IN %s %s", ipv, addr_str) < 2)
return -EINVAL;
if (strcmp(ipv, "IP4") && strcmp(ipv, "IP6"))
return -ENOTSUP;
return osmo_sockaddr_str_from_str(&sdp->rtp, addr_str, sdp->rtp.port);
}
/* parse a line like 'o=jdoe 3724394400 3724394405 IN IP4 198.51.100.1' starting after the '=' */
static int sdp_parse_origin(struct osmo_sdp_msg *sdp, const struct token *src)
{
struct token t;
char addr_str[INET6_ADDRSTRLEN + 1] = {};
const char *line_end = get_line_end(src);
next_token(&t, src->start, line_end);
token_copy(sdp, &sdp->origin.username, &t);
next_token(&t, t.end, line_end);
token_copy(sdp, &sdp->origin.sess_id, &t);
next_token(&t, t.end, line_end);
token_copy(sdp, &sdp->origin.sess_version, &t);
next_token(&t, t.end, line_end);
if (strncmp("IN", t.start, t.end - t.start))
return -ENOTSUP;
next_token(&t, t.end, line_end);
if (strncmp("IP4", t.start, t.end - t.start)
&& strncmp("IP6", t.start, t.end - t.start))
return -ENOTSUP;
next_token(&t, t.end, line_end);
osmo_strlcpy(addr_str, t.start, OSMO_MIN(sizeof(addr_str), t.end - t.start + 1));
return osmo_sockaddr_str_from_str(&sdp->origin.addr, addr_str, 0);
}
static int sdp_parse_session_name(struct osmo_sdp_msg *sdp, const struct token *src)
{
struct token t = *src;
t.end = get_line_end(src);
if (sdp->session_name)
talloc_free(sdp->session_name);
if (t.start >= t.end)
sdp->session_name = NULL;
else
token_copy(sdp, &sdp->session_name, &t);
return 0;
}
struct osmo_sdp_msg *osmo_sdp_msg_alloc(void *ctx)
{
struct osmo_sdp_msg *sdp;
sdp = talloc_zero(ctx, struct osmo_sdp_msg);
sdp->codecs = osmo_sdp_codec_list_alloc(sdp);
return sdp;
}
/* Parse SDP string into struct osmo_sdp_msg. Return 0 on success, negative on error.
* Return a new osmo_sdp_msg instance allocated from ctx, or NULL on error.
* When NULL is returned and if ret is non-NULL, details of the error are returned in ret->*.
*/
struct osmo_sdp_msg *osmo_sdp_msg_decode(void *ctx, const char *src_str, int src_str_len,
struct osmo_sdp_msg_decode_ret *ret)
{
struct osmo_sdp_msg *sdp;
bool found_message_start = false;
struct token src = {
.start = src_str,
.end = src_str + (src_str_len < 0 ? strlen(src_str) : src_str_len),
};
if (ret)
*ret = (struct osmo_sdp_msg_decode_ret){};
sdp = osmo_sdp_msg_alloc(ctx);
for (; src.start && src.start < src.end && *src.start; src.start++) {
char attrib;
int rc = 0;
struct token t;
if (*src.start == '\r' || *src.start == '\n')
continue;
t.start = src.start;
t.end = get_line_end(&src);
if (!found_message_start) {
/* An SDP message must start with a line saying "v=0". */
if (strncmp("v=0", t.start, t.end - t.start)) {
/* report the error */
if (ret) {
*ret = (struct osmo_sdp_msg_decode_ret){
.rc = -EINVAL,
.error = {
.at_input_str = t.start,
.at_input_str_len = t.end - t.start,
},
.src_remain = t.end,
};
}
talloc_free(sdp);
return NULL;
}
found_message_start = true;
goto next_line;
}
/* Expecting only lines starting with 'X='. Not being too strict about it is probably alright. */
if (t.start + 1 >= t.end || t.start[1] != '=')
goto next_line;
attrib = *t.start;
t.start += 2;
switch (attrib) {
/* a=... */
case 'a':
rc = sdp_parse_attrib(sdp, &t);
break;
case 'm':
rc = sdp_parse_media_description(sdp, &t);
break;
case 'c':
rc = sdp_parse_connection_info(sdp, &t);
break;
case 'o':
rc = sdp_parse_origin(sdp, &t);
break;
case 's':
rc = sdp_parse_session_name(sdp, &t);
break;
default:
/* ignore any other parameters */
break;
}
if (rc) {
if (ret) {
/* shift back to include the 'x=' part as well */
t.start -= 2;
*ret = (struct osmo_sdp_msg_decode_ret){
.rc = rc,
.error = {
.at_input_str = t.start,
.at_input_str_len = t.end - t.start,
},
.src_remain = t.end,
};
}
talloc_free(sdp);
return NULL;
}
next_line:
src.start = t.end;
}
return sdp;
}
/*! Short single-line representation of an SDP message, convenient for logging.
* To obtain a valid SDP message, use osmo_sdp_msg_encode_buf() instead.
*/
int osmo_sdp_msg_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_msg *sdp, bool summarize)
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
if (!sdp) {
OSMO_STRBUF_PRINTF(sb, "NULL");
return sb.chars_needed;
}
OSMO_STRBUF_PRINTF(sb, OSMO_SOCKADDR_STR_FMT, OSMO_SOCKADDR_STR_FMT_ARGS(&sdp->rtp));
OSMO_STRBUF_PRINTF(sb, "{");
OSMO_STRBUF_APPEND(sb, osmo_sdp_codec_list_to_str_buf, sdp->codecs, summarize);
OSMO_STRBUF_PRINTF(sb, "}");
return sb.chars_needed;
}
char *osmo_sdp_msg_to_str_c(void *ctx, const struct osmo_sdp_msg *sdp, bool summarize)
{
OSMO_NAME_C_IMPL(ctx, 128, "sdp_msg_to_str_c-ERROR", osmo_sdp_msg_to_str_buf, sdp, summarize)
}

View File

@@ -25,12 +25,6 @@ osmo_mgw_SOURCES = \
$(NULL)
osmo_mgw_LDADD = \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOTRAU_LIBS) \
$(NULL)

View File

@@ -280,6 +280,31 @@ int mgcp_vty_go_parent(struct vty *vty)
return vty->node;
}
static void signal_handler(int signum)
{
fprintf(stdout, "signal %u received\n", signum);
switch (signum) {
case SIGABRT:
/* in case of abort, we want to obtain a talloc report and
* then run default SIGABRT handler, who will generate coredump
* and abort the process. abort() should do this for us after we
* return, but program wouldn't exit if an external SIGABRT is
* received.
*/
talloc_report(tall_vty_ctx, stderr);
talloc_report_full(tall_mgw_ctx, stderr);
signal(SIGABRT, SIG_DFL);
raise(SIGABRT);
break;
case SIGUSR1:
talloc_report(tall_vty_ctx, stderr);
talloc_report_full(tall_mgw_ctx, stderr);
break;
default:
break;
}
}
static struct vty_app_info vty_info = {
.name = "OsmoMGW",
@@ -328,6 +353,8 @@ int main(int argc, char **argv)
msgb_talloc_ctx_init(tall_mgw_ctx, 0);
signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
osmo_init_ignore_signals();
osmo_init_logging2(tall_mgw_ctx, &log_info);
libosmo_abis_init(tall_mgw_ctx);

View File

@@ -1,6 +1,7 @@
SUBDIRS = \
mgcp_client \
mgcp \
sdp \
$(NULL)
# The `:;' works around a Bash 3.2 bug when the output is not writeable.

View File

@@ -34,7 +34,7 @@ mgcp_test_SOURCES = \
$(NULL)
mgcp_test_LDADD = \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \

View File

@@ -103,7 +103,7 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16002 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define MDCX3A_RET \
"200 18983215 OK\r\n" \
@@ -115,7 +115,7 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16002 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define MDCX3_FMTP_RET \
"200 18983215 OK\r\n" \
@@ -127,40 +127,49 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16006 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define MDCX4_ADDR0000 \
"MDCX 18983216 1@mgw MGCP 1.0\r\n" \
"M: sendrecv\r" \
"M: sendrecv\r\n" \
"C: 2\r\n" \
"I: %s\r\n" \
"L: p:20, a:AMR, nt:IN\r\n" \
"\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 4441 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define MDCX4_ADDR0000_RET \
"527 18983216 FAIL\r\n"
"200 18983216 OK\r\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 16002 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:20\r\n"
#define MDCX4 \
"MDCX 18983217 1@mgw MGCP 1.0\r\n" \
"M: sendrecv\r" \
"M: sendrecv\r\n" \
"C: 2\r\n" \
"I: %s\r\n" \
"L: p:20, a:AMR, nt:IN\r\n" \
"\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
"c=IN IP4 5.6.7.8\r\n" \
"t=0 0\r\n" \
"m=audio 4441 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define MDCX4_RET(Ident) \
"200 " Ident " OK\r\n" \
@@ -172,7 +181,7 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16002 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define MDCX4_RO_RET(Ident) \
"200 " Ident " OK\r\n" \
@@ -184,87 +193,87 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16002 RTP/AVP 112\r\n" \
"a=rtpmap:112 AMR\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define MDCX4_PT1 \
"MDCX 18983218 1@mgw MGCP 1.0\r\n" \
"M: SENDRECV\r" \
"M: SENDRECV\r\n" \
"C: 2\r\n" \
"I: %s\r\n" \
"L: p:20-40, a:AMR, nt:IN\r\n" \
"\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
"c=IN IP4 5.6.7.8\r\n" \
"t=0 0\r\n" \
"m=audio 4441 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define MDCX4_PT2 \
"MDCX 18983219 1@mgw MGCP 1.0\r\n" \
"M: sendrecv\r" \
"M: sendrecv\r\n" \
"C: 2\r\n" \
"I: %s\r\n" \
"L: p:20-20, a:AMR, nt:IN\r\n" \
"\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
"c=IN IP4 5.6.7.8\r\n" \
"t=0 0\r\n" \
"m=audio 4441 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define MDCX4_PT3 \
"MDCX 18983220 1@mgw MGCP 1.0\r\n" \
"M: sendrecv\r" \
"M: sendrecv\r\n" \
"C: 2\r\n" \
"I: %s\r\n" \
"L: a:AMR, nt:IN\r\n" \
"\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
"c=IN IP4 5.6.7.8\r\n" \
"t=0 0\r\n" \
"m=audio 4441 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
/* Test different upper/lower case in options */
#define MDCX4_PT4 \
"MDCX 18983221 1@mgw MGCP 1.0\r\n" \
"m: sendrecv\r" \
"m: sendrecv\r\n" \
"c: 2\r\n" \
"i: %s\r\n" \
"l: A:amr, NT:IN\r\n" \
"\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
"c=IN IP4 5.6.7.8\r\n" \
"t=0 0\r\n" \
"m=audio 4441 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define MDCX4_SO \
"MDCX 18983222 1@mgw MGCP 1.0\r\n" \
"M: sendonly\r" \
"M: sendonly\r\n" \
"C: 2\r\n" \
"I: %s\r\n" \
"L: p:20, a:AMR, nt:IN\r\n" \
"\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
"c=IN IP4 5.6.7.8\r\n" \
"t=0 0\r\n" \
"m=audio 4441 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define MDCX4_RO \
"MDCX 18983223 1@mgw MGCP 1.0\r\n" \
"M: recvonly\r" \
"M: recvonly\r\n" \
"C: 2\r\n" \
"I: %s\r\n" \
"L: p:20, a:AMR, nt:IN\r\n"
@@ -297,7 +306,7 @@ static void test_strline(void)
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 5904 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define CRCX_RET \
"200 2 OK\r\n" \
@@ -310,7 +319,7 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16002 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define CRCX_RET_NO_RTPMAP \
"200 2 OK\r\n" \
@@ -322,7 +331,7 @@ static void test_strline(void)
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 16002 RTP/AVP 97\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define CRCX_FMTP_RET \
"200 2 OK\r\n" \
@@ -335,17 +344,17 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16006 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define CRCX_ZYN \
"CRCX 2 1@mgw MGCP 1.0\r" \
"M: recvonly\r" \
"CRCX 2 1@mgw MGCP 1.0\r\n" \
"M: recvonly\r\n" \
"C: 2\r\n" \
"\n" \
"v=0\r" \
"c=IN IP4 123.12.12.123\r" \
"m=audio 5904 RTP/AVP 97\r" \
"a=rtpmap:97 GSM-EFR/8000\r"
"\r\n" \
"v=0\r\n" \
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 5904 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n"
#define CRCX_ZYN_RET \
"200 2 OK\r\n" \
@@ -371,7 +380,7 @@ static void test_strline(void)
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 5904 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define CRCX_X_OSMO_IGN_RET \
"200 2 OK\r\n" \
@@ -384,7 +393,95 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16010 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define CRCX_PORT_0 \
"CRCX 3 1@mgw MGCP 1.0\r\n" \
"m: recvonly\r\n" \
"C: 2\r\n" \
"L: p:20\r\n" \
"\r\n" \
"v=0\r\n" \
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 0 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
#define CRCX_PORT_0_RET \
"200 3 OK\r\n" \
"I: %s\r\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 16014 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
#define CRCX_PORT_0_IUFP \
"CRCX 4 1@mgw MGCP 1.0\r\n" \
"m: recvonly\r\n" \
"C: 2\r\n" \
"L: p:20\r\n" \
"\r\n" \
"v=0\r\n" \
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 0 RTP/AVP 96\r\n" \
"a=rtpmap:96 VND.3GPP.IUFP/16000\r\n" \
"a=ptime:20\r\n"
#define CRCX_PORT_0_IUFP_RET \
"200 4 OK\r\n" \
"I: %s\r\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 16016 RTP/AVP 96\r\n" \
"a=rtpmap:96 VND.3GPP.IUFP/16000\r\n" \
"a=ptime:20\r\n"
/* Do a CRCX in m=sendrecv */
#define CRCX_PORT_0_IUFP_SENDRECV \
"CRCX 4 1@mgw MGCP 1.0\r\n" \
"M: sendrecv\r\n" \
"C: 2\r\n" \
"L: p:20\r\n" \
"\r\n" \
"v=0\r\n" \
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 0 RTP/AVP 96\r\n" \
"a=rtpmap:96 VND.3GPP.IUFP/16000\r\n" \
"a=ptime:20\r\n"
/* Do a CRCX using sendrecv mode in the SDP part */
#define CRCX_PORT_0_IUFP_SENDRECV2 \
"CRCX 4 1@mgw MGCP 1.0\r\n" \
"C: 2\r\n" \
"L: p:20\r\n" \
"\r\n" \
"v=0\r\n" \
"c=IN IP4 123.12.12.123\r\n" \
"a=sendrecv\r\n" \
"m=audio 0 RTP/AVP 96\r\n" \
"a=rtpmap:96 VND.3GPP.IUFP/16000\r\n" \
"a=ptime:20\r\n"
/* Do a CRCX entirely omitting a mode, i.e. implcit sendrecv */
#define CRCX_PORT_0_IUFP_SENDRECV3 \
"CRCX 4 1@mgw MGCP 1.0\r\n" \
"C: 2\r\n" \
"L: p:20\r\n" \
"\r\n" \
"v=0\r\n" \
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 0 RTP/AVP 96\r\n" \
"a=rtpmap:96 VND.3GPP.IUFP/16000\r\n" \
"a=ptime:20\r\n"
#define DLCX \
"DLCX 7 1@mgw MGCP 1.0\r\n" \
@@ -441,7 +538,7 @@ static void test_strline(void)
"m=audio 5904 RTP/AVP 18 97\r\n" \
"a=rtpmap:18 G729/8000\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define CRCX_MULT_2 \
"CRCX 2 2@mgw MGCP 1.0\r\n" \
@@ -456,7 +553,7 @@ static void test_strline(void)
"a=rtpmap:18 G729/8000\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=rtpmap:101 FOO/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define CRCX_MULT_3 \
"CRCX 2 3@mgw MGCP 1.0\r\n" \
@@ -471,7 +568,7 @@ static void test_strline(void)
"a=rtpmap:18 G729/8000\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=rtpmap:101 FOO/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define CRCX_MULT_4 \
"CRCX 2 4@mgw MGCP 1.0\r\n" \
@@ -486,7 +583,7 @@ static void test_strline(void)
"a=rtpmap:18 G729/8000\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=rtpmap:101 FOO/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define CRCX_MULT_GSM_EXACT \
"CRCX 259260421 5@mgw MGCP 1.0\r\n" \
@@ -550,7 +647,7 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16012 RTP/AVP 111\r\n" \
"a=rtpmap:111 AMR/8000/1\r\n" \
"a=fmtp:111 mode-change-capability=2; octet-align=1\r\n" \
"a=fmtp:111 octet-align=1\r\n" \
"a=ptime:20\r\n"
#define CRCX_NO_LCO_NO_SDP_RET \
@@ -575,7 +672,7 @@ static void test_strline(void)
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 5904 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:40\r\n"
"a=ptime:20\r\n"
#define CRCX_NULL_RET "502 2 FAIL\r\n"
@@ -623,139 +720,11 @@ static const struct mgcp_test tests[] = {
{"MDCX_NULL", MDCX_NULL, MDCX_NULL_RET},
{"DLCX_NULL", DLCX_NULL, DLCX_NULL_RET},
{"RQNT_NULL", RQNT_NULL, RQNT_NULL_RET},
{
"CRCX_EXPLICIT_EP",
/* CRCX for a new endpoint 8@mgw, not using the '*@mgw' wildcard */
"CRCX 101 rtpbridge/8@mgw MGCP 1.0\r\n"
"m: recvonly\r\n"
"C: 2\r\n"
"L: p:20\r\n"
"\r\n"
"v=0\r\n"
"c=IN IP4 1.2.3.4\r\n"
"m=audio 1234 RTP/AVP 112\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n",
"200 101 OK\r\n"
"I: %s\r\n"
"\r\n"
"v=0\r\n"
"o=- %s 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 0.0.0.0\r\n"
"t=0 0\r\n"
"m=audio 16014 RTP/AVP 112\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n"
},
{
"CRCX_TWO_PAYLOADS_1",
"CRCX 102 rtpbridge/*@mgw MGCP 1.0\r\n"
"m: recvonly\r\n"
"C: 2\r\n"
"L: p:20\r\n"
"\r\n"
"v=0\r\n"
"c=IN IP4 1.2.3.4\r\n"
"m=audio 1234 RTP/AVP 112 3\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n",
"200 102 OK\r\n"
"Z: rtpbridge/2@mgw\r\n"
"I: %s\r\n"
"\r\n"
"v=0\r\n"
"o=- %s 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 0.0.0.0\r\n"
"t=0 0\r\n"
"m=audio 16016 RTP/AVP 112 3\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n"
},
{
"CRCX_TWO_PAYLOADS_2",
"CRCX 103 rtpbridge/2@mgw MGCP 1.0\r\n"
"m: recvonly\r\n"
"C: 2\r\n"
"L: p:20\r\n"
"\r\n"
"v=0\r\n"
"c=IN IP4 1.2.3.4\r\n"
"m=audio 1234 RTP/AVP 3 112\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n",
"200 103 OK\r\n"
"I: %s\r\n"
"\r\n"
"v=0\r\n"
"o=- %s 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 0.0.0.0\r\n"
"t=0 0\r\n"
"m=audio 16018 RTP/AVP 3 112\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n"
},
{
"CRCX_THREE_PAYLOADS_1",
"CRCX 104 rtpbridge/*@mgw MGCP 1.0\r\n"
"m: recvonly\r\n"
"C: 4\r\n"
"L: p:20\r\n"
"\r\n"
"v=0\r\n"
"c=IN IP4 1.2.3.4\r\n"
"m=audio 1234 RTP/AVP 112 3 111\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1;mode-set=0,2,4,7\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=ptime:20\r\n",
"200 104 OK\r\n"
"Z: rtpbridge/3@mgw\r\n"
"I: %s\r\n"
"\r\n"
"v=0\r\n"
"o=- %s 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 0.0.0.0\r\n"
"t=0 0\r\n"
"m=audio 16020 RTP/AVP 112 3 111\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1;mode-set=0,2,4,7\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=ptime:20\r\n"
},
{
"CRCX_THREE_PAYLOADS_2",
"CRCX 105 rtpbridge/3@mgw MGCP 1.0\r\n"
"m: recvonly\r\n"
"C: 4\r\n"
"L: p:20\r\n"
"\r\n"
"v=0\r\n"
"c=IN IP4 1.2.3.4\r\n"
"m=audio 1234 RTP/AVP 3 112 113\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1;mode-set=0,2,4,7\r\n"
"a=rtpmap:113 AMR/8000\r\n"
"a=fmtp:113 octet-align=1;mode-set=0,2,4\r\n"
"a=ptime:20\r\n",
"200 105 OK\r\n"
"I: %s\r\n"
"\r\n"
"v=0\r\n"
"o=- %s 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 0.0.0.0\r\n"
"t=0 0\r\n"
"m=audio 16022 RTP/AVP 3 112 113\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1;mode-set=0,2,4,7\r\n"
"a=rtpmap:113 AMR/8000\r\n"
"a=fmtp:113 octet-align=1;mode-set=0,2,4\r\n"
"a=ptime:20\r\n"
},
{"CRCX_PORT_0", CRCX_PORT_0, CRCX_PORT_0_RET, 97},
{"CRCX_PORT_0_IUFP", CRCX_PORT_0_IUFP, CRCX_PORT_0_IUFP_RET, 96},
{"CRCX_PORT_0_IUFP_SENDRECV", CRCX_PORT_0_IUFP_SENDRECV, CRCX_PORT_0_IUFP_RET, 96},
{"CRCX_PORT_0_IUFP_SENDRECV2", CRCX_PORT_0_IUFP_SENDRECV2, CRCX_PORT_0_IUFP_RET, 96},
{"CRCX_PORT_0_IUFP_SENDRECV3", CRCX_PORT_0_IUFP_SENDRECV3, CRCX_PORT_0_IUFP_RET, 96},
};
static const struct mgcp_test retransmit[] = {
@@ -786,12 +755,13 @@ static struct msgb *create_msg(const char *str, const char *conn_id)
static int dummy_packets = 0;
/* override and forward */
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen)
int osmo_iofd_sendto_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int flags, const struct osmo_sockaddr *addr)
{
uint32_t dest_host =
htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr);
int dest_port = htons(((struct sockaddr_in *)dest_addr)->sin_port);
htonl(((struct sockaddr_in *)addr)->sin_addr.s_addr);
int dest_port = htons(((struct sockaddr_in *)addr)->sin_port);
const uint8_t *buf = msgb_data(msg);
size_t len = msgb_length(msg);
if (len == sizeof(rtp_dummy_payload)
&& memcmp(buf, rtp_dummy_payload, sizeof(rtp_dummy_payload)) == 0) {
@@ -805,6 +775,8 @@ ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
OSMO_ASSERT(dest_host);
OSMO_ASSERT(dest_port);
msgb_free(msg);
return len;
}
@@ -956,7 +928,7 @@ static void test_messages(void)
struct msgb *msg;
printf("\n================================================\n");
printf("Testing %s() %s\n", __func__, t->name);
printf("Testing %s\n", t->name);
dummy_packets = 0;
@@ -971,7 +943,6 @@ static void test_messages(void)
}
} else if (check_response(msg->data, t->exp_resp) != 0) {
printf("%s failed.\n", t->name);
fflush(stdout);
OSMO_ASSERT(false);
}
@@ -994,7 +965,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);
@@ -1061,9 +1032,6 @@ static void test_messages(void)
/* Reset them again for next test */
conn->end.codec->payload_type = PTYPE_NONE;
}
fflush(stdout);
fflush(stderr);
}
mgcp_endpoints_release(trunk);
@@ -1092,7 +1060,7 @@ static void test_retransmission(void)
struct msgb *msg;
printf("\n================================================\n");
printf("Testing %s() %s\n", __func__, t->name);
printf("Testing %s\n", t->name);
inp = create_msg(t->req, last_conn_id);
msg = mgcp_handle_message(cfg, inp);
@@ -1122,9 +1090,6 @@ static void test_retransmission(void)
OSMO_ASSERT(false);
}
msgb_free(msg);
fflush(stdout);
fflush(stderr);
}
mgcp_endpoints_release(trunk);
@@ -1243,7 +1208,9 @@ static void test_packet_loss_calc(void)
_conn =
mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
"test-connection");
conn = mgcp_conn_get_rtp(&endp, _conn->id);
OSMO_ASSERT(_conn);
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);
@@ -1263,7 +1230,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);
@@ -1494,7 +1461,7 @@ 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;
@@ -1546,7 +1513,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);
}
@@ -1581,7 +1548,7 @@ 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);
@@ -1597,7 +1564,7 @@ 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);
@@ -1618,7 +1585,7 @@ 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);
@@ -1634,7 +1601,7 @@ 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);
@@ -1650,7 +1617,7 @@ 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);
@@ -1662,7 +1629,7 @@ 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(osmo_sockaddr_port(&conn->end.addr.u.sa) == 16434);
@@ -1679,7 +1646,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';
@@ -1693,7 +1660,7 @@ 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);
@@ -1722,7 +1689,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);
@@ -1753,7 +1720,6 @@ static void test_no_cycle(void)
talloc_free(cfg);
}
/* Set audio_send_name=0 and verify that a=rtpmap: entries are omitted. */
static void test_no_name(void)
{
struct mgcp_trunk *trunk;
@@ -2366,7 +2332,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);
@@ -2472,13 +2438,6 @@ int main(int argc, char **argv)
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
osmo_init_logging2(ctx, &log_info);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
log_set_print_level(osmo_stderr_target, 1);
log_set_print_category(osmo_stderr_target, 1);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 1);
log_set_category_filter(osmo_stderr_target, DLMGCP, true, LOGL_DEBUG);
test_strline();
test_values();

View File

@@ -13,7 +13,7 @@ line: ''
line: ''
================================================
Testing test_messages() AUEP1
Testing AUEP1
creating message from statically defined input:
---------8<---------
AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0
@@ -25,7 +25,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
Testing test_messages() AUEP2
Testing AUEP2
creating message from statically defined input:
---------8<---------
AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0
@@ -37,7 +37,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
Testing test_messages() MDCX1
Testing MDCX1
creating message from statically defined input:
---------8<---------
MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0
@@ -49,7 +49,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
Testing test_messages() MDCX2
Testing MDCX2
creating message from statically defined input:
---------8<---------
MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0
@@ -61,7 +61,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
Testing test_messages() CRCX
Testing CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 1@mgw MGCP 1.0
@@ -73,7 +73,7 @@ v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:40
a=ptime:20
---------8<---------
checking response:
@@ -83,7 +83,7 @@ Response matches our expectations.
Dummy packets: 2
================================================
Testing test_messages() MDCX3
Testing MDCX3
creating message from statically defined input:
---------8<---------
MDCX 18983215 1@mgw MGCP 1.0
@@ -97,44 +97,46 @@ Response matches our expectations.
Dummy packets: 2
================================================
Testing test_messages() MDCX4_ADDR000
Testing MDCX4_ADDR000
creating message from statically defined input:
---------8<---------
MDCX 18983216 1@mgw MGCP 1.0
M: sendrecv
M: sendrecv
C: 2
I: %s
L: p:20, a:AMR, nt:IN
L: p:20, a:AMR, nt:IN
v=0
o=- %s 23 IN IP4 0.0.0.0
c=IN IP4 0.0.0.0
t=0 0
m=audio 4441 RTP/AVP 99
a=rtpmap:99 AMR/8000
a=rtpmap:99 AMR/8000
a=ptime:20
---------8<---------
checking response:
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
Response matches our expectations.
(response contains a connection id)
================================================
================================================
Testing MDCX4
creating message from statically defined input:
---------8<---------
MDCX 18983217 1@mgw MGCP 1.0
MDCX 18983217 1@mgw MGCP 1.0
M: sendrecv
C: 2
I: %s
L: p:20, a:AMR, nt:IN
I: %s
v=0
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
t=0 0
m=audio 4441 RTP/AVP 99
a=rtpmap:99 AMR/8000
m=audio 4441 RTP/AVP 99
a=ptime:20
---------8<---------
checking response:
@@ -144,21 +146,22 @@ Response matches our expectations.
Dummy packets: 2
================================================
Testing MDCX4_PT1
creating message from statically defined input:
---------8<---------
MDCX 18983218 1@mgw MGCP 1.0
---------8<---------
M: SENDRECV
C: 2
I: %s
L: p:20-40, a:AMR, nt:IN
C: 2
v=0
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
t=0 0
m=audio 4441 RTP/AVP 99
a=rtpmap:99 AMR/8000
t=0 0
a=ptime:20
---------8<---------
checking response:
@@ -168,21 +171,22 @@ Response matches our expectations.
Dummy packets: 2
================================================
Dummy packets: 2
Testing MDCX4_PT2
creating message from statically defined input:
---------8<---------
MDCX 18983219 1@mgw MGCP 1.0
creating message from statically defined input:
M: sendrecv
C: 2
I: %s
L: p:20-20, a:AMR, nt:IN
M: sendrecv
v=0
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
t=0 0
m=audio 4441 RTP/AVP 99
a=rtpmap:99 AMR/8000
c=IN IP4 5.6.7.8
a=ptime:20
---------8<---------
checking response:
@@ -192,21 +196,22 @@ Response matches our expectations.
Dummy packets: 2
================================================
(response contains a connection id)
Testing MDCX4_PT3
creating message from statically defined input:
---------8<---------
MDCX 18983220 1@mgw MGCP 1.0
Testing test_messages() MDCX4_PT3
M: sendrecv
C: 2
I: %s
L: a:AMR, nt:IN
MDCX 18983220 1@mgw MGCP 1.0
v=0
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
t=0 0
m=audio 4441 RTP/AVP 99
a=rtpmap:99 AMR/8000
o=- %s 23 IN IP4 5.6.7.8
a=ptime:20
---------8<---------
checking response:
@@ -216,21 +221,22 @@ Response matches our expectations.
Dummy packets: 2
================================================
Response matches our expectations.
Testing MDCX4_PT4
creating message from statically defined input:
---------8<---------
MDCX 18983221 1@mgw MGCP 1.0
================================================
m: sendrecv
c: 2
i: %s
l: A:amr, NT:IN
---------8<---------
v=0
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
t=0 0
m=audio 4441 RTP/AVP 99
a=rtpmap:99 AMR/8000
v=0
a=ptime:20
---------8<---------
checking response:
@@ -240,21 +246,22 @@ Response matches our expectations.
Dummy packets: 2
================================================
using message with patched conn_id for comparison
Testing MDCX4_SO
creating message from statically defined input:
---------8<---------
MDCX 18983222 1@mgw MGCP 1.0
M: sendonly
C: 2
I: %s
L: p:20, a:AMR, nt:IN
creating message from statically defined input:
v=0
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
t=0 0
m=audio 4441 RTP/AVP 99
a=rtpmap:99 AMR/8000
a=ptime:20
---------8<---------
checking response:
@@ -263,11 +270,12 @@ Response matches our expectations.
(response contains a connection id)
================================================
---------8<---------
Testing MDCX4_RO
creating message from statically defined input:
---------8<---------
MDCX 18983223 1@mgw MGCP 1.0
(response contains a connection id)
M: recvonly
C: 2
I: %s
L: p:20, a:AMR, nt:IN
@@ -279,7 +287,7 @@ Response matches our expectations.
Dummy packets: 2
================================================
---------8<---------
Testing DLCX
creating message from statically defined input:
---------8<---------
DLCX 7 1@mgw MGCP 1.0
@@ -293,12 +301,18 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
Testing CRCX_ZYN
creating message from statically defined input:
---------8<---------
using message as statically defined for comparison
CRCX 2 1@mgw MGCP 1.0
M: recvonly
C: 2
v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
(response does not contain a connection id)
---------8<---------
checking response:
using message with patched conn_id for comparison
@@ -307,7 +321,7 @@ Response matches our expectations.
Dummy packets: 2
================================================
Testing EMPTY
creating message from statically defined input:
---------8<---------
@@ -315,7 +329,7 @@ creating message from statically defined input:
---------8<---------
================================================
Response matches our expectations.
Testing SHORT1
creating message from statically defined input:
---------8<---------
CRCX
@@ -327,7 +341,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
================================================
Testing SHORT2
creating message from statically defined input:
---------8<---------
CRCX 1
@@ -338,7 +352,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
Testing SHORT3
creating message from statically defined input:
---------8<---------
CRCX 1 1@mgw
@@ -349,7 +363,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
Testing SHORT4
creating message from statically defined input:
---------8<---------
CRCX 1 1@mgw MGCP
@@ -360,7 +374,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
Testing RQNT1
creating message from statically defined input:
---------8<---------
RQNT 186908780 1@mgw MGCP 1.0
@@ -374,7 +388,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
creating message from statically defined input:
Testing RQNT2
creating message from statically defined input:
---------8<---------
RQNT 186908781 1@mgw MGCP 1.0
@@ -388,7 +402,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
creating message from statically defined input:
Testing DLCX
creating message from statically defined input:
---------8<---------
DLCX 7 1@mgw MGCP 1.0
@@ -402,7 +416,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
creating message from statically defined input:
Testing CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 1@mgw MGCP 1.0
@@ -414,7 +428,7 @@ v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
================================================
a=ptime:20
---------8<---------
checking response:
@@ -424,7 +438,7 @@ Response matches our expectations.
Dummy packets: 2
================================================
c=IN IP4 123.12.12.123
Testing MDCX3
creating message from statically defined input:
---------8<---------
MDCX 18983215 1@mgw MGCP 1.0
@@ -438,7 +452,7 @@ Response matches our expectations.
Dummy packets: 2
================================================
creating message from statically defined input:
Testing DLCX
creating message from statically defined input:
---------8<---------
DLCX 7 1@mgw MGCP 1.0
@@ -452,7 +466,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
creating message from statically defined input:
Testing CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 6@mgw MGCP 1.0
@@ -466,7 +480,7 @@ Response matches our expectations.
(response contains a connection id)
================================================
creating message from statically defined input:
Testing CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 1@mgw MGCP 1.0
@@ -479,7 +493,7 @@ v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
Testing test_messages() CRCX
a=ptime:20
---------8<---------
checking response:
@@ -489,7 +503,7 @@ Response matches our expectations.
Dummy packets: 2
================================================
c=IN IP4 123.12.12.123
Testing MDCX_TOO_LONG_CI
creating message from statically defined input:
---------8<---------
MDCX 18983224 1@mgw MGCP 1.0
@@ -502,7 +516,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
Testing test_messages() MDCX_TOO_LONG_CI
Testing CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 7@mgw MGCP 1.0
@@ -526,7 +540,7 @@ Response matches our expectations.
Dummy packets: 2
================================================
m=audio 5904 RTP/AVP 111
Testing AUEP_NULL
creating message from statically defined input:
---------8<---------
AUEP 18983215 null@mgw MGCP 1.0
@@ -538,7 +552,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
================================================
Testing CRCX_NULL
creating message from statically defined input:
---------8<---------
CRCX 2 null@mgw MGCP 1.0
@@ -550,7 +564,7 @@ v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
================================================
a=ptime:20
---------8<---------
checking response:
@@ -559,7 +573,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
v=0
Testing MDCX_NULL
creating message from statically defined input:
---------8<---------
MDCX 9 null@mgw MGCP 1.0
@@ -572,7 +586,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
Testing test_messages() MDCX_NULL
Testing DLCX_NULL
creating message from statically defined input:
---------8<---------
DLCX 8 null@mgw MGCP 1.0
@@ -586,7 +600,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
creating message from statically defined input:
Testing RQNT_NULL
creating message from statically defined input:
---------8<---------
RQNT 186908782 null@mgw MGCP 1.0
@@ -600,18 +614,18 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
creating message from statically defined input:
Testing CRCX_PORT_0
creating message from statically defined input:
---------8<---------
X: B244F267488
CRCX 3 1@mgw MGCP 1.0
m: recvonly
C: 2
L: p:20
v=0
Response matches our expectations.
(response does not contain a connection id)
c=IN IP4 123.12.12.123
m=audio 0 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:20
---------8<---------
@@ -619,21 +633,20 @@ checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
L: p:20
================================================
c=IN IP4 1.2.3.4
Testing CRCX_PORT_0_IUFP
creating message from statically defined input:
---------8<---------
a=ptime:20
CRCX 4 1@mgw MGCP 1.0
m: recvonly
C: 2
L: p:20
v=0
(response contains a connection id)
Dummy packets: 2
c=IN IP4 123.12.12.123
m=audio 0 RTP/AVP 96
a=rtpmap:96 VND.3GPP.IUFP/16000
a=ptime:20
---------8<---------
@@ -641,21 +654,20 @@ checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
L: p:20
================================================
c=IN IP4 1.2.3.4
Testing CRCX_PORT_0_IUFP_SENDRECV
creating message from statically defined input:
---------8<---------
a=ptime:20
CRCX 4 1@mgw MGCP 1.0
M: sendrecv
C: 2
L: p:20
v=0
(response contains a connection id)
Dummy packets: 2
c=IN IP4 123.12.12.123
m=audio 0 RTP/AVP 96
a=rtpmap:96 VND.3GPP.IUFP/16000
a=ptime:20
---------8<---------
@@ -663,23 +675,20 @@ checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
L: p:20
================================================
c=IN IP4 1.2.3.4
Testing CRCX_PORT_0_IUFP_SENDRECV2
creating message from statically defined input:
---------8<---------
a=ptime:20
---------8<---------
CRCX 4 1@mgw MGCP 1.0
C: 2
L: p:20
v=0
(response contains a connection id)
Dummy packets: 2
================================================
Testing test_messages() CRCX_THREE_PAYLOADS_1
c=IN IP4 123.12.12.123
a=sendrecv
m=audio 0 RTP/AVP 96
a=rtpmap:96 VND.3GPP.IUFP/16000
a=ptime:20
---------8<---------
@@ -687,24 +696,19 @@ checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
v=0
================================================
a=rtpmap:112 AMR/8000
Testing CRCX_PORT_0_IUFP_SENDRECV3
creating message from statically defined input:
---------8<---------
a=ptime:20
---------8<---------
CRCX 4 1@mgw MGCP 1.0
C: 2
L: p:20
v=0
(response contains a connection id)
Dummy packets: 2
================================================
Testing test_messages() CRCX_THREE_PAYLOADS_2
creating message from statically defined input:
c=IN IP4 123.12.12.123
m=audio 0 RTP/AVP 96
a=rtpmap:96 VND.3GPP.IUFP/16000
a=ptime:20
---------8<---------
@@ -712,10 +716,9 @@ checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
c=IN IP4 1.2.3.4
================================================
a=fmtp:112 octet-align=1;mode-set=0,2,4,7
Testing CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 1@mgw MGCP 1.0
@@ -727,7 +730,7 @@ v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
================================================
a=ptime:20
---------8<---------
checking response:
@@ -745,7 +748,7 @@ v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
Response matches our expectations.
a=ptime:20
---------8<---------
checking response:
@@ -753,7 +756,7 @@ using message with patched conn_id for comparison
Response matches our expectations.
================================================
Testing RQNT1
creating message from statically defined input:
---------8<---------
RQNT 186908780 1@mgw MGCP 1.0
@@ -777,7 +780,7 @@ using message as statically defined for comparison
Response matches our expectations.
================================================
Re-transmitting RQNT1
Testing RQNT2
creating message from statically defined input:
---------8<---------
RQNT 186908781 1@mgw MGCP 1.0
@@ -801,7 +804,7 @@ using message as statically defined for comparison
Response matches our expectations.
================================================
Re-transmitting RQNT2
Testing MDCX3
creating message from statically defined input:
---------8<---------
MDCX 18983215 1@mgw MGCP 1.0
@@ -823,7 +826,7 @@ using message with patched conn_id for comparison
Response matches our expectations.
================================================
Response matches our expectations.
Testing DLCX
creating message from statically defined input:
---------8<---------
DLCX 7 1@mgw MGCP 1.0
@@ -857,7 +860,7 @@ v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
Response matches our expectations.
a=ptime:20
---------8<---------
creating message from statically defined input:
@@ -1311,7 +1314,7 @@ c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 18 97
a=rtpmap:18 G729/8000
a=rtpmap:97 GSM-EFR/8000
creating message from statically defined input:
a=ptime:20
---------8<---------
creating message from statically defined input:
@@ -1328,7 +1331,7 @@ m=audio 5904 RTP/AVP 18 97 101
a=rtpmap:18 G729/8000
a=rtpmap:97 GSM-EFR/8000
a=rtpmap:101 FOO/8000
---------8<---------
a=ptime:20
---------8<---------
creating message from statically defined input:
@@ -1345,7 +1348,7 @@ m=audio 5904 RTP/AVP
a=rtpmap:18 G729/8000
a=rtpmap:97 GSM-EFR/8000
a=rtpmap:101 FOO/8000
---------8<---------
a=ptime:20
---------8<---------
creating message from statically defined input:
@@ -1362,7 +1365,7 @@ m=audio 5904 RTP/AVP 18
a=rtpmap:18 G729/8000
a=rtpmap:97 GSM-EFR/8000
a=rtpmap:101 FOO/8000
---------8<---------
a=ptime:20
---------8<---------
creating message from statically defined input:
@@ -1444,7 +1447,7 @@ v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
Testing no sequence flow on initial packet
a=ptime:20
---------8<---------
checking response:

View File

@@ -571,6 +571,57 @@ static void test_map_str_to_codec(void)
OSMO_ASSERT(map_str_to_codec("AMR-WB####################################################################################################################") == -1);
}
static void test_map_codec_to_pt_and_map_pt_to_codec(void)
{
struct ptmap ptmap[10];
unsigned int ptmap_len;
unsigned int i;
ptmap[0].codec = CODEC_GSMEFR_8000_1;
ptmap[0].pt = 96;
ptmap[1].codec = CODEC_GSMHR_8000_1;
ptmap[1].pt = 97;
ptmap[2].codec = CODEC_AMR_8000_1;
ptmap[2].pt = 98;
ptmap[3].codec = CODEC_AMRWB_16000_1;
ptmap[3].pt = 99;
ptmap_len = 4;
/* Mappings that are covered by the table */
for (i = 0; i < ptmap_len; i++)
printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
for (i = 0; i < ptmap_len; i++)
printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
printf("\n");
/* Map some codecs/payload types from the static range, result must
* always be a 1:1 mapping */
printf(" %u => %u\n", CODEC_PCMU_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMU_8000_1));
printf(" %u => %u\n", CODEC_GSM_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_GSM_8000_1));
printf(" %u => %u\n", CODEC_PCMA_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMA_8000_1));
printf(" %u => %u\n", CODEC_G729_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_G729_8000_1));
printf(" %u <= %u\n", CODEC_PCMU_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMU_8000_1));
printf(" %u <= %u\n", CODEC_GSM_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_GSM_8000_1));
printf(" %u <= %u\n", CODEC_PCMA_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMA_8000_1));
printf(" %u <= %u\n", CODEC_G729_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_G729_8000_1));
printf("\n");
/* Try to do mappings from statically defined range to danymic range and vice versa. This
* is illegal and should result into a 1:1 mapping */
ptmap[3].codec = CODEC_AMRWB_16000_1;
ptmap[3].pt = 2;
ptmap[4].codec = CODEC_PCMU_8000_1;
ptmap[4].pt = 100;
ptmap_len = 5;
/* Apply all mappings again, the illegal ones we defined should result into 1:1 mappings */
for (i = 0; i < ptmap_len; i++)
printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
for (i = 0; i < ptmap_len; i++)
printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
printf("\n");
}
void test_mgcp_client_e1_epname(void)
{
char *epname;
@@ -857,6 +908,7 @@ int main(int argc, char **argv)
test_mgcp_msg();
test_mgcp_client_cancel();
test_sdp_section_start();
test_map_codec_to_pt_and_map_pt_to_codec();
test_map_str_to_codec();
test_mgcp_client_e1_epname();

View File

@@ -128,6 +128,10 @@ test_sdp_section_start() test [18]:
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 \r\n"
DLMGCP Failed to parse MGCP response header (audio ip)
got rc=-22
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw'
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su128-0@mgw), rate(128)/offset(0) combination is invalid!
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su8-16@mgw), rate(8)/offset(16) combination is invalid!

View File

@@ -181,6 +181,35 @@ test_sdp_section_start() test [16]:
test_sdp_section_start() test [17]:
test_sdp_section_start() test [18]:
110 => 96
111 => 97
112 => 98
113 => 99
96 <= 110
97 <= 111
98 <= 112
99 <= 113
0 => 0
3 => 3
8 => 8
18 => 18
0 <= 0
3 <= 3
8 <= 8
18 <= 18
110 => 96
111 => 97
112 => 98
113 => 113
0 => 0
96 <= 110
97 <= 111
98 <= 112
2 <= 2
100 <= 100
ds/e1-1/s-15/su64-0@mgw
ds/e1-2/s-14/su32-0@mgw
ds/e1-3/s-13/su32-4@mgw

63
tests/sdp/Makefile.am Normal file
View File

@@ -0,0 +1,63 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir)/include \
-I$(top_srcdir) \
$(NULL)
AM_CFLAGS = \
-Wall \
-ggdb3 \
$(LIBOSMOCORE_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(COVERAGE_LDFLAGS) \
-no-install \
$(NULL)
EXTRA_DIST = \
sdp_fmtp_test.ok \
sdp_fmtp_test.err \
sdp_codec_test.ok \
sdp_codec_test.err \
$(NULL)
check_PROGRAMS = \
sdp_fmtp_test \
sdp_codec_test \
sdp_msg_test \
$(NULL)
sdp_fmtp_test_SOURCES = \
sdp_fmtp_test.c \
$(NULL)
sdp_fmtp_test_LDADD = \
$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
$(NULL)
sdp_codec_test_SOURCES = \
sdp_codec_test.c \
$(NULL)
sdp_codec_test_LDADD = \
$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
$(LIBOSMOCORE_LIBS) \
$(NULL)
sdp_msg_test_SOURCES = \
sdp_msg_test.c \
$(NULL)
sdp_msg_test_LDADD = \
$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
$(LIBOSMOCORE_LIBS) \
$(NULL)
update_exp:
$(builddir)/sdp_fmtp_test >$(srcdir)/sdp_fmtp_test.ok 2>$(srcdir)/sdp_fmtp_test.err
$(builddir)/sdp_codec_test >$(srcdir)/sdp_codec_test.ok 2>$(srcdir)/sdp_codec_test.err
$(builddir)/sdp_msg_test >$(srcdir)/sdp_msg_test.ok 2>$(srcdir)/sdp_msg_test.err

645
tests/sdp/sdp_codec_test.c Normal file
View File

@@ -0,0 +1,645 @@
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/sdp/sdp_codec_list.h>
#include <osmocom/sdp/fmtp.h>
void *test_ctx = NULL;
static void report_callback(const void *ptr, int depth, int max_depth, int is_ref, void *priv)
{
const char *name = talloc_get_name(ptr);
printf(" |%*s%3zu %s\n", depth, "", talloc_total_blocks(ptr), name);
}
/* Print a talloc report that is reproducible for test output verification. It contains no pointer addresses. */
#define report(CTX) _report(CTX, #CTX)
static void _report(void *ctx, const char *label)
{
fflush(stdout);
fflush(stderr);
printf("%s\n", label);
talloc_report_depth_cb(ctx, 0, 100, report_callback, NULL);
fflush(stdout);
}
struct codec_test {
struct osmo_sdp_codec set;
int expect_rc;
const char *expect_str;
bool expect_is_set;
};
struct codec_test codec_tests[] = {
{
.set = { 23, "encoding-name", 8000, NULL },
.expect_str = "encoding-name#23",
.expect_is_set = true,
},
{
.set = { 112, "AMR", 8000, "octet-align=1;mode-set=0,2,4" },
.expect_str = "AMR:octet-align=1;mode-set=0,2,4#112",
.expect_is_set = true,
},
{
.set = { 96, "AMR", 8000, "mode-set=0,2,4;octet-align=1" },
.expect_str = "AMR:mode-set=0,2,4;octet-align=1#96",
.expect_is_set = true,
},
{
.set = { 114, "AMR", 8000, "mode-set=0,2,4" },
.expect_str = "AMR:mode-set=0,2,4#114",
.expect_is_set = true,
},
{
.set = { 97, "AMR", 8000, "mode-set=0,2,4;octet-align=0" },
.expect_str = "AMR:mode-set=0,2,4;octet-align=0#97",
.expect_is_set = true,
},
{
.set = { 98, "AMR", 8000, "octet-align=1" },
.expect_str = "AMR:octet-align=1#98",
.expect_is_set = true,
},
{
.set = { 96, "AMR-WB", 16000 },
.expect_str = "AMR-WB/16000#96",
.expect_is_set = true,
},
{
.set = { 3, "GSM", 8000 },
.expect_str = "GSM#3",
.expect_is_set = true,
},
{
.set = { },
.expect_str = "/0#0",
.expect_is_set = false,
},
{
.set = { 112, NULL, 8000, "octet-align=1" },
.expect_str = ":octet-align=1#112",
.expect_is_set = false,
},
{
.set = { 112, "", 8000, "octet-align=1" },
.expect_str = ":octet-align=1#112",
.expect_is_set = false,
},
};
void test_codec(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
struct codec_test *t;
struct codec_test *t2;
printf("\n\n--- %s()\n", __func__);
printf("- osmo_sdp_codec_set():\n");
for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) {
struct osmo_sdp_codec *codec = osmo_sdp_codec_alloc(ctx);
char *str;
bool is_set;
osmo_sdp_codec_set(codec, t->set.payload_type, t->set.encoding_name, t->set.rate, t->set.fmtp);
str = osmo_sdp_codec_to_str_c(ctx, codec);
printf("osmo_sdp_codec_set [%d] '%s'\n", (int)(t - codec_tests), str);
if (strcmp(str, t->expect_str))
printf(" *** ERROR: expected '%s'\n", t->expect_str);
if (!osmo_sdp_codec_cmp(codec, &t->set, &osmo_sdp_codec_cmp_exact))
printf(" osmo_sdp_codec_cmp() ok\n");
else
printf(" osmo_sdp_codec_cmp() *** ERROR: mismatches original values\n");
is_set = osmo_sdp_codec_is_set(codec);
printf(" osmo_sdp_codec_is_set() = %s\n", is_set ? "true" : "false");
if (is_set != t->expect_is_set)
printf(" *** ERROR: expected is_set = %s\n", t->expect_is_set ? "true" : "false");
if (is_set != osmo_sdp_codec_is_set(&t->set))
printf(" *** ERROR: is_set(copy) != is_set(orig)\n");
talloc_free(str);
talloc_free(codec);
if (talloc_total_blocks(ctx) != 1)
printf(" *** ERROR: ctx has %zu items, should be 1\n", talloc_total_blocks(ctx));
}
printf("\n- osmo_sdp_codec_cmp(equivalent):\n");
for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) {
for (t2 = codec_tests; (t2 - codec_tests) < ARRAY_SIZE(codec_tests); t2++) {
int cmp = osmo_sdp_codec_cmp(&t->set, &t2->set, &osmo_sdp_codec_cmp_equivalent);
int reverse_cmp = osmo_sdp_codec_cmp(&t2->set, &t->set, &osmo_sdp_codec_cmp_equivalent);
printf(" %s %s %s %s %s\n",
osmo_sdp_codec_to_str_c(ctx, &t->set),
(cmp == 0) ? "=="
: ((cmp < 0) ? "<" : ">"),
osmo_sdp_codec_to_str_c(ctx, &t2->set),
(reverse_cmp == 0) ? "=="
: ((reverse_cmp < 0) ? "<" : ">"),
osmo_sdp_codec_to_str_c(ctx, &t->set));
if (reverse_cmp != -cmp)
printf(" *** ERROR: osmo_sdp_codec_cmp(reverse args) == %d, expected %d\n",
reverse_cmp, -cmp);
talloc_free_children(ctx);
}
}
printf("\n- osmo_sdp_codec_from_str():\n");
for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) {
struct osmo_sdp_codec *codec = osmo_sdp_codec_alloc(ctx);
int rc = osmo_sdp_codec_from_str(codec, t->expect_str, -1);
printf(" osmo_sdp_codec_from_str('%s') rc=%d",
t->expect_str, rc);
if (!rc) {
printf(" res=%s", osmo_sdp_codec_to_str_c(ctx, codec));
rc = osmo_sdp_codec_cmp(codec, &t->set, &osmo_sdp_codec_cmp_exact);
if (rc)
printf(" *** ERROR: osmo_sdp_codec_cmp(res,orig) = %d", rc);
}
printf("\n");
talloc_free_children(ctx);
}
talloc_free(ctx);
}
void test_codec_list(void)
{
void *list_ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
int rc;
struct osmo_sdp_codec *codec;
const struct osmo_sdp_codec all_codecs[] = {
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
};
struct osmo_sdp_codec_list *codec_list;
printf("\n\n--- %s()\n", __func__);
codec_list = osmo_sdp_codec_list_alloc(list_ctx);
printf("osmo_sdp_codec_list_first() = %s\n",
osmo_sdp_codec_to_str_c(print_ctx, osmo_sdp_codec_list_first(codec_list)));
report(list_ctx);
for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, false);
printf("[%d] osmo_sdp_codec_list_add(%s)\n", i, osmo_sdp_codec_to_str_c(print_ctx, added));
}
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
printf("osmo_sdp_codec_list_first() = %s\n",
osmo_sdp_codec_to_str_c(print_ctx, osmo_sdp_codec_list_first(codec_list)));
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("\n");
printf("- add same entries again with once=exact, nothing should change\n");
for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i],
&osmo_sdp_codec_cmp_exact, false);
printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added));
}
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("\n");
printf("- add same entries again with once=NULL, duplicates are added\n");
for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, false);
printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added));
}
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("\n");
printf("- add same entries again with once=NULL,pick_unused_pt_nr=true, duplicates are added with new #nr\n");
for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, true);
printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added));
}
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("\n");
printf("- remove all 'GSM#3' entries, with osmo_sdp_codec_cmp_exact\n");
rc = osmo_sdp_codec_list_remove(codec_list, &all_codecs[1], &osmo_sdp_codec_cmp_exact);
printf(" osmo_sdp_codec_list_remove() = %d\n", rc);
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("- remove all 'GSM' entries, with osmo_sdp_codec_cmp_equivalent\n");
rc = osmo_sdp_codec_list_remove(codec_list, &all_codecs[1], &osmo_sdp_codec_cmp_equivalent);
printf(" osmo_sdp_codec_list_remove() = %d\n", rc);
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
rc = osmo_sdp_codec_list_move_to_first(codec_list, &all_codecs[0], &osmo_sdp_codec_cmp_equivalent);
printf("- osmo_sdp_codec_list_move_to_first('%s', equivalent) = %d\n",
osmo_sdp_codec_to_str_c(print_ctx, &all_codecs[0]), rc);
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("- osmo_sdp_codec_list_free_items()\n");
osmo_sdp_codec_list_free_items(codec_list);
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
printf(" %d entries\n", i);
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
talloc_free(print_ctx);
talloc_free(list_ctx);
}
static struct osmo_sdp_codec_list *init_codec_list(void *ctx, const struct osmo_sdp_codec *init_array)
{
struct osmo_sdp_codec_list *dst = osmo_sdp_codec_list_alloc(ctx);
for (; osmo_sdp_codec_is_set(init_array); init_array++)
osmo_sdp_codec_list_add(dst, init_array, NULL, false);
return dst;
}
void test_codec_list_cmp(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
const struct osmo_sdp_codec codec_a[] = {
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
{}
};
const struct osmo_sdp_codec codec_b[][5] = {
/* same */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* different payload_type */
{
{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* AMR fmtp in different order */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "mode-set=0,2,4;octet-align=1" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* different AMR mode-set */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=7" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* empty AMR mode-set */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* different AMR octet-align */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=0;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* omitted AMR octet-align is identical to octet-align=0 */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* different order */
{
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
},
/* one less item */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
},
/* one more item */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
},
};
const struct osmo_sdp_codec_cmp_flags *test_cmpf[] = {
&osmo_sdp_codec_cmp_name,
&osmo_sdp_codec_cmp_equivalent,
&osmo_sdp_codec_cmp_exact,
};
printf("\n\n--- %s()\n", __func__);
for (i = 0; i < ARRAY_SIZE(codec_b); i++) {
struct osmo_sdp_codec_list *list_a = init_codec_list(ctx, codec_a);
struct osmo_sdp_codec_list *list_b = init_codec_list(ctx, codec_b[i]);
int j;
printf("A = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
printf("B = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
for (j = 0; j < ARRAY_SIZE(test_cmpf); j++) {
const struct osmo_sdp_codec_cmp_flags *cmpf = test_cmpf[j];
int cmp = osmo_sdp_codec_list_cmp(list_a, list_b, cmpf);
int reverse_cmp = osmo_sdp_codec_list_cmp(list_b, list_a, cmpf);
printf(" cmpf[%d]: payload_type=%s rate=%s fmtp=%d: A %s B %s A\n",
j,
cmpf->payload_type ? "true" : "false",
cmpf->rate ? "true" : "false",
cmpf->fmtp,
(cmp == 0) ? "=="
: ((cmp < 0) ? "<" : ">"),
(reverse_cmp == 0) ? "=="
: ((reverse_cmp < 0) ? "<" : ">"));
if (reverse_cmp != -cmp)
printf(" *** ERROR: osmo_sdp_codec_list_cmp(reverse args) == %d, expected %d\n",
reverse_cmp, -cmp);
}
talloc_free(list_a);
talloc_free(list_b);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak:\n");
report(ctx);
}
printf("\n");
talloc_free_children(print_ctx);
}
talloc_free(print_ctx);
talloc_free(ctx);
}
void test_codec_list_intersection(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
const struct osmo_sdp_codec codec_a[] = {
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
{}
};
const struct osmo_sdp_codec codec_b[][5] = {
/* same */
{
{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* same in different order */
{
{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
},
/* two matches */
{
{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
},
/* no match */
{
{ .payload_type = 97, .encoding_name = "AMR-WB", .rate = 16000 },
{ .payload_type = 98, .encoding_name = "FOO", .rate = 8000 },
{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
},
};
printf("\n\n--- %s()\n", __func__);
for (i = 0; i < ARRAY_SIZE(codec_b); i++) {
struct osmo_sdp_codec_list *list_a = init_codec_list(ctx, codec_a);
struct osmo_sdp_codec_list *list_b = init_codec_list(ctx, codec_b[i]);
printf("A = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
printf("B = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
osmo_sdp_codec_list_intersection(list_a, list_b, &osmo_sdp_codec_cmp_equivalent, false);
printf("osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)\n = %s\n",
osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
talloc_free(list_a);
list_a = init_codec_list(ctx, codec_a);
osmo_sdp_codec_list_intersection(list_a, list_b, &osmo_sdp_codec_cmp_equivalent, true);
printf("osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)\n = %s\n",
osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
talloc_free(list_a);
list_a = init_codec_list(ctx, codec_a);
osmo_sdp_codec_list_intersection(list_b, list_a, &osmo_sdp_codec_cmp_equivalent, false);
printf("osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)\n = %s\n",
osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
talloc_free(list_a);
list_a = init_codec_list(ctx, codec_a);
osmo_sdp_codec_list_intersection(list_b, list_a, &osmo_sdp_codec_cmp_equivalent, true);
printf("osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)\n = %s\n",
osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
talloc_free(list_a);
talloc_free(list_b);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak:\n");
report(ctx);
}
printf("\n");
talloc_free_children(print_ctx);
}
talloc_free(print_ctx);
talloc_free(ctx);
}
struct my_obj {
struct osmo_sdp_codec *codec;
struct osmo_sdp_codec_list *codec_list;
};
struct my_obj *my_obj_alloc(void *ctx)
{
struct my_obj *o = talloc_zero(ctx, struct my_obj);
o->codec_list = osmo_sdp_codec_list_alloc(o);
return o;
}
void test_obj_members(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
struct osmo_sdp_codec *codec;
struct my_obj *o;
printf("\n\n--- %s()\n", __func__);
o = my_obj_alloc(ctx);
o->codec = osmo_sdp_codec_alloc(o);
osmo_sdp_codec_set(o->codec, 96, "AMR", 8000, "octet-align=1");
printf("o->codec = %s\n", osmo_sdp_codec_to_str_c(print_ctx, o->codec));
report(ctx);
osmo_sdp_codec_list_add(o->codec_list, o->codec, false, false);
osmo_sdp_codec_list_add(o->codec_list, o->codec, false, true);
i = 0;
osmo_sdp_codec_list_foreach(codec, o->codec_list) {
printf("o->codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(ctx);
printf("talloc_free(o)\n");
talloc_free(o);
report(ctx);
talloc_free(ctx);
talloc_free(print_ctx);
}
typedef void (*test_func_t)(void);
test_func_t test_func[] = {
test_codec,
test_codec_list,
test_codec_list_cmp,
test_codec_list_intersection,
test_obj_members,
};
int main(void)
{
int i;
test_ctx = talloc_named_const(NULL, 0, "sdp_codec_test");
for (i = 0; i < ARRAY_SIZE(test_func); i++) {
test_func[i]();
if (talloc_total_blocks(test_ctx) != 1) {
talloc_report_full(test_ctx, stderr);
printf("ERROR after test %d: memory leak\n", i);
return -1;
}
}
talloc_free(test_ctx);
return 0;
}

View File

538
tests/sdp/sdp_codec_test.ok Normal file
View File

@@ -0,0 +1,538 @@
--- test_codec()
- osmo_sdp_codec_set():
osmo_sdp_codec_set [0] 'encoding-name#23'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [1] 'AMR:octet-align=1;mode-set=0,2,4#112'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [2] 'AMR:mode-set=0,2,4;octet-align=1#96'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [3] 'AMR:mode-set=0,2,4#114'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [4] 'AMR:mode-set=0,2,4;octet-align=0#97'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [5] 'AMR:octet-align=1#98'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [6] 'AMR-WB/16000#96'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [7] 'GSM#3'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [8] '/0#0'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = false
osmo_sdp_codec_set [9] ':octet-align=1#112'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = false
osmo_sdp_codec_set [10] ':octet-align=1#112'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = false
- osmo_sdp_codec_cmp(equivalent):
encoding-name#23 == encoding-name#23 == encoding-name#23
encoding-name#23 > AMR:octet-align=1;mode-set=0,2,4#112 < encoding-name#23
encoding-name#23 > AMR:mode-set=0,2,4;octet-align=1#96 < encoding-name#23
encoding-name#23 > AMR:mode-set=0,2,4#114 < encoding-name#23
encoding-name#23 > AMR:mode-set=0,2,4;octet-align=0#97 < encoding-name#23
encoding-name#23 > AMR:octet-align=1#98 < encoding-name#23
encoding-name#23 > AMR-WB/16000#96 < encoding-name#23
encoding-name#23 > GSM#3 < encoding-name#23
encoding-name#23 > /0#0 < encoding-name#23
encoding-name#23 > :octet-align=1#112 < encoding-name#23
encoding-name#23 > :octet-align=1#112 < encoding-name#23
AMR:octet-align=1;mode-set=0,2,4#112 < encoding-name#23 > AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4#114 < AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1#98 == AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 < AMR-WB/16000#96 > AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 < GSM#3 > AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 > /0#0 < AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112
AMR:mode-set=0,2,4;octet-align=1#96 < encoding-name#23 > AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4#114 < AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1#98 == AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 < AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 < GSM#3 > AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 > /0#0 < AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4#114 < encoding-name#23 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 < AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 < AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 < AMR:octet-align=1#98 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 < AMR-WB/16000#96 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 < GSM#3 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 > /0#0 < AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 > :octet-align=1#112 < AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 > :octet-align=1#112 < AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4;octet-align=0#97 < encoding-name#23 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 < AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1#98 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 < AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 < GSM#3 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 > /0#0 < AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97
AMR:octet-align=1#98 < encoding-name#23 > AMR:octet-align=1#98
AMR:octet-align=1#98 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1#98
AMR:octet-align=1#98 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1#98
AMR:octet-align=1#98 > AMR:mode-set=0,2,4#114 < AMR:octet-align=1#98
AMR:octet-align=1#98 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1#98
AMR:octet-align=1#98 == AMR:octet-align=1#98 == AMR:octet-align=1#98
AMR:octet-align=1#98 < AMR-WB/16000#96 > AMR:octet-align=1#98
AMR:octet-align=1#98 < GSM#3 > AMR:octet-align=1#98
AMR:octet-align=1#98 > /0#0 < AMR:octet-align=1#98
AMR:octet-align=1#98 > :octet-align=1#112 < AMR:octet-align=1#98
AMR:octet-align=1#98 > :octet-align=1#112 < AMR:octet-align=1#98
AMR-WB/16000#96 < encoding-name#23 > AMR-WB/16000#96
AMR-WB/16000#96 > AMR:octet-align=1;mode-set=0,2,4#112 < AMR-WB/16000#96
AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=1#96 < AMR-WB/16000#96
AMR-WB/16000#96 > AMR:mode-set=0,2,4#114 < AMR-WB/16000#96
AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR-WB/16000#96
AMR-WB/16000#96 > AMR:octet-align=1#98 < AMR-WB/16000#96
AMR-WB/16000#96 == AMR-WB/16000#96 == AMR-WB/16000#96
AMR-WB/16000#96 < GSM#3 > AMR-WB/16000#96
AMR-WB/16000#96 > /0#0 < AMR-WB/16000#96
AMR-WB/16000#96 > :octet-align=1#112 < AMR-WB/16000#96
AMR-WB/16000#96 > :octet-align=1#112 < AMR-WB/16000#96
GSM#3 < encoding-name#23 > GSM#3
GSM#3 > AMR:octet-align=1;mode-set=0,2,4#112 < GSM#3
GSM#3 > AMR:mode-set=0,2,4;octet-align=1#96 < GSM#3
GSM#3 > AMR:mode-set=0,2,4#114 < GSM#3
GSM#3 > AMR:mode-set=0,2,4;octet-align=0#97 < GSM#3
GSM#3 > AMR:octet-align=1#98 < GSM#3
GSM#3 > AMR-WB/16000#96 < GSM#3
GSM#3 == GSM#3 == GSM#3
GSM#3 > /0#0 < GSM#3
GSM#3 > :octet-align=1#112 < GSM#3
GSM#3 > :octet-align=1#112 < GSM#3
/0#0 < encoding-name#23 > /0#0
/0#0 < AMR:octet-align=1;mode-set=0,2,4#112 > /0#0
/0#0 < AMR:mode-set=0,2,4;octet-align=1#96 > /0#0
/0#0 < AMR:mode-set=0,2,4#114 > /0#0
/0#0 < AMR:mode-set=0,2,4;octet-align=0#97 > /0#0
/0#0 < AMR:octet-align=1#98 > /0#0
/0#0 < AMR-WB/16000#96 > /0#0
/0#0 < GSM#3 > /0#0
/0#0 == /0#0 == /0#0
/0#0 < :octet-align=1#112 > /0#0
/0#0 < :octet-align=1#112 > /0#0
:octet-align=1#112 < encoding-name#23 > :octet-align=1#112
:octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4#114 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112
:octet-align=1#112 < AMR:octet-align=1#98 > :octet-align=1#112
:octet-align=1#112 < AMR-WB/16000#96 > :octet-align=1#112
:octet-align=1#112 < GSM#3 > :octet-align=1#112
:octet-align=1#112 > /0#0 < :octet-align=1#112
:octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
:octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
:octet-align=1#112 < encoding-name#23 > :octet-align=1#112
:octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4#114 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112
:octet-align=1#112 < AMR:octet-align=1#98 > :octet-align=1#112
:octet-align=1#112 < AMR-WB/16000#96 > :octet-align=1#112
:octet-align=1#112 < GSM#3 > :octet-align=1#112
:octet-align=1#112 > /0#0 < :octet-align=1#112
:octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
:octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
- osmo_sdp_codec_from_str():
osmo_sdp_codec_from_str('encoding-name#23') rc=0 res=encoding-name#23
osmo_sdp_codec_from_str('AMR:octet-align=1;mode-set=0,2,4#112') rc=0 res=AMR:octet-align=1;mode-set=0,2,4#112
osmo_sdp_codec_from_str('AMR:mode-set=0,2,4;octet-align=1#96') rc=0 res=AMR:mode-set=0,2,4;octet-align=1#96
osmo_sdp_codec_from_str('AMR:mode-set=0,2,4#114') rc=0 res=AMR:mode-set=0,2,4#114
osmo_sdp_codec_from_str('AMR:mode-set=0,2,4;octet-align=0#97') rc=0 res=AMR:mode-set=0,2,4;octet-align=0#97
osmo_sdp_codec_from_str('AMR:octet-align=1#98') rc=0 res=AMR:octet-align=1#98
osmo_sdp_codec_from_str('AMR-WB/16000#96') rc=0 res=AMR-WB/16000#96
osmo_sdp_codec_from_str('GSM#3') rc=0 res=GSM#3
osmo_sdp_codec_from_str('/0#0') rc=0 res=/0#0
osmo_sdp_codec_from_str(':octet-align=1#112') rc=0 res=:octet-align=1#112
osmo_sdp_codec_from_str(':octet-align=1#112') rc=0 res=:octet-align=1#112
--- test_codec_list()
osmo_sdp_codec_list_first() = NULL
list_ctx
| 2 test_codec_list
| 1 struct osmo_sdp_codec_list
[0] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112)
[1] osmo_sdp_codec_list_add(GSM#3)
[2] osmo_sdp_codec_list_add(GSM-HR-08#111)
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM#3
codec_list[2] = GSM-HR-08#111
osmo_sdp_codec_list_first() = AMR:octet-align=1;mode-set=0,2,4#112
list_ctx
| 9 test_codec_list
| 8 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'AMR#112 GSM#3 GSM-HR-08#111'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
- add same entries again with once=exact, nothing should change
[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112)
[] osmo_sdp_codec_list_add(GSM#3)
[] osmo_sdp_codec_list_add(GSM-HR-08#111)
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM#3
codec_list[2] = GSM-HR-08#111
list_ctx
| 9 test_codec_list
| 8 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'AMR#112 GSM#3 GSM-HR-08#111'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
- add same entries again with once=NULL, duplicates are added
[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112)
[] osmo_sdp_codec_list_add(GSM#3)
[] osmo_sdp_codec_list_add(GSM-HR-08#111)
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM#3
codec_list[2] = GSM-HR-08#111
codec_list[3] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[4] = GSM#3
codec_list[5] = GSM-HR-08#111
list_ctx
| 16 test_codec_list
| 15 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'2*AMR#112 2*GSM#3 2*GSM-HR-08#111'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
- add same entries again with once=NULL,pick_unused_pt_nr=true, duplicates are added with new #nr
[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#96)
[] osmo_sdp_codec_list_add(GSM#97)
[] osmo_sdp_codec_list_add(GSM-HR-08#98)
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM#3
codec_list[2] = GSM-HR-08#111
codec_list[3] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[4] = GSM#3
codec_list[5] = GSM-HR-08#111
codec_list[6] = AMR:octet-align=1;mode-set=0,2,4#96
codec_list[7] = GSM#97
codec_list[8] = GSM-HR-08#98
list_ctx
| 23 test_codec_list
| 22 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'3*AMR 3*GSM 3*GSM-HR-08'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98'
- remove all 'GSM#3' entries, with osmo_sdp_codec_cmp_exact
osmo_sdp_codec_list_remove() = 2
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM-HR-08#111
codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[3] = GSM-HR-08#111
codec_list[4] = AMR:octet-align=1;mode-set=0,2,4#96
codec_list[5] = GSM#97
codec_list[6] = GSM-HR-08#98
list_ctx
| 19 test_codec_list
| 18 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'3*AMR 3*GSM-HR-08 GSM#97'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98'
- remove all 'GSM' entries, with osmo_sdp_codec_cmp_equivalent
osmo_sdp_codec_list_remove() = 1
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM-HR-08#111
codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[3] = GSM-HR-08#111
codec_list[4] = AMR:octet-align=1;mode-set=0,2,4#96
codec_list[5] = GSM-HR-08#98
list_ctx
| 17 test_codec_list
| 16 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'3*AMR 3*GSM-HR-08'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM-HR-08#98'
- osmo_sdp_codec_list_move_to_first('AMR:octet-align=1;mode-set=0,2,4#112', equivalent) = 3
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#96
codec_list[3] = GSM-HR-08#111
codec_list[4] = GSM-HR-08#111
codec_list[5] = GSM-HR-08#98
list_ctx
| 17 test_codec_list
| 16 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'3*AMR 3*GSM-HR-08'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 AMR:octet-align=1;mode-set=0,2,4#112 AMR:octet-align=1;mode-set=0,2,4#96 GSM-HR-08#111 GSM-HR-08#111 GSM-HR-08#98'
- osmo_sdp_codec_list_free_items()
0 entries
list_ctx
| 2 test_codec_list
| 1 struct osmo_sdp_codec_list
osmo_sdp_codec_list_to_str_c(summarize=true):
'(no-codecs)'
osmo_sdp_codec_list_to_str_c(summarize=false):
'(no-codecs)'
--- test_codec_list_cmp()
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
cmpf[2]: payload_type=true rate=true fmtp=2: A == B == A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#96 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:mode-set=0,2,4;octet-align=1#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=7#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=0;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112
cmpf[0]: payload_type=false rate=false fmtp=0: A < B > A
cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3
cmpf[0]: payload_type=false rate=false fmtp=0: A > B < A
cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 GSM-EFR#110
cmpf[0]: payload_type=false rate=false fmtp=0: A < B > A
cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
--- test_codec_list_intersection()
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
= AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
= AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
= AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
= AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = GSM-HR-08#98 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
= AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
= AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
= GSM-HR-08#98 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
= GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = GSM-HR-08#98 GSM-EFR#110 GSM#97
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
= GSM#3 GSM-HR-08#111
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
= GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
= GSM-HR-08#98 GSM#97
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
= GSM-HR-08#111 GSM#3
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR-WB/16000#97 FOO#98 GSM-EFR#110
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
= (no-codecs)
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
= (no-codecs)
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
= (no-codecs)
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
= (no-codecs)
--- test_obj_members()
o->codec = AMR:octet-align=1#96
ctx
| 6 test_obj_members
| 5 struct my_obj
| 3 struct osmo_sdp_codec
| 1 octet-align=1
| 1 AMR
| 1 struct osmo_sdp_codec_list
o->codec_list[0] = AMR:octet-align=1#96
o->codec_list[1] = AMR:octet-align=1#97
ctx
| 12 test_obj_members
| 11 struct my_obj
| 3 struct osmo_sdp_codec
| 1 octet-align=1
| 1 AMR
| 7 struct osmo_sdp_codec_list
| 3 struct osmo_sdp_codec
| 1 octet-align=1
| 1 AMR
| 3 struct osmo_sdp_codec
| 1 octet-align=1
| 1 AMR
talloc_free(o)
ctx
| 1 test_obj_members

126
tests/sdp/sdp_fmtp_test.c Normal file
View File

@@ -0,0 +1,126 @@
#include <inttypes.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/sdp/fmtp.h>
struct get_val_test {
const char *fmtp_string;
const char *val_name;
bool expect_rc;
const char *expect_val;
};
const struct get_val_test get_val_tests[] = {
{
"foo=123;bar=success;baz=456", "foo",
true, "123"
},
{
"foo=123;bar=success;baz=456", "bar",
true, "success"
},
{
"foo=123;bar=success;baz=456", "baz",
true, "456"
},
};
void test_get_val(void)
{
int i;
printf("\n--- %s()\n", __func__);
for (i = 0; i < ARRAY_SIZE(get_val_tests); i++) {
const struct get_val_test *t = &get_val_tests[i];
char val[128] = {};
bool rc = osmo_sdp_fmtp_get_val(val, sizeof(val), t->fmtp_string, t->val_name);
bool ok;
printf("osmo_sdp_fmtp_get_val('%s', '%s') rc=%s",
t->fmtp_string, t->val_name,
rc ? "true" : "false");
if (rc)
printf(" val='%s'", val);
ok = true;
if (rc != t->expect_rc) {
printf(" ERROR: expected rc=%s", t->expect_rc ? "true" : "false");
ok = false;
}
if (t->expect_val && strcmp(val, t->expect_val)) {
printf(" ERROR: expected val='%s'", t->expect_val);
ok = false;
}
if (ok)
printf(" ok");
printf("\n");
}
printf("\n--- %s() DONE\n", __func__);
}
struct get_int_test {
const char *fmtp_string;
const char *val_name;
int64_t defval;
int64_t expect_rc;
};
const struct get_int_test get_int_tests[] = {
{
"foo=123;bar=success;baz=456", "foo", -1,
123
},
{
"foo=123;bar=success;baz=456", "bar", -1,
-1
},
{
"foo=123;bar=success;baz=456", "baz", -1,
456
},
};
void test_get_int(void)
{
int i;
printf("\n--- %s()\n", __func__);
for (i = 0; i < ARRAY_SIZE(get_int_tests); i++) {
const struct get_int_test *t = &get_int_tests[i];
int64_t rc = osmo_sdp_fmtp_get_int(t->fmtp_string, t->val_name, t->defval);
printf("osmo_sdp_fmtp_get_int('%s', '%s') rc=%"PRId64,
t->fmtp_string, t->val_name, rc);
if (rc != t->expect_rc) {
printf(" ERROR: expected rc=%"PRId64, t->expect_rc);
}
else {
printf(" ok");
}
printf("\n");
}
printf("\n--- %s() DONE\n", __func__);
}
static const struct log_info_cat log_categories[] = {
};
const struct log_info log_info = {
.cat = log_categories,
.num_cat = ARRAY_SIZE(log_categories),
};
int main(void)
{
void *ctx = talloc_named_const(NULL, 1, "sdp_fmtp_test");
osmo_init_logging2(ctx, &log_info);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
test_get_val();
test_get_int();
return 0;
}

View File

View File

@@ -0,0 +1,14 @@
--- test_get_val()
osmo_sdp_fmtp_get_val('foo=123;bar=success;baz=456', 'foo') rc=true val='123' ok
osmo_sdp_fmtp_get_val('foo=123;bar=success;baz=456', 'bar') rc=true val='success' ok
osmo_sdp_fmtp_get_val('foo=123;bar=success;baz=456', 'baz') rc=true val='456' ok
--- test_get_val() DONE
--- test_get_int()
osmo_sdp_fmtp_get_int('foo=123;bar=success;baz=456', 'foo') rc=123 ok
osmo_sdp_fmtp_get_int('foo=123;bar=success;baz=456', 'bar') rc=-1 ok
osmo_sdp_fmtp_get_int('foo=123;bar=success;baz=456', 'baz') rc=456 ok
--- test_get_int() DONE

769
tests/sdp/sdp_msg_test.c Normal file
View File

@@ -0,0 +1,769 @@
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/sdp/sdp_msg.h>
void *test_ctx = NULL;
static void report_callback(const void *ptr, int depth, int max_depth, int is_ref, void *priv)
{
const char *name = talloc_get_name(ptr);
printf(" |%*s%3zu %s\n", depth, "", talloc_total_blocks(ptr), name);
}
/* Print a talloc report that is reproducible for test output verification. It contains no pointer addresses. */
#define report(CTX) _report(CTX, #CTX)
static void _report(void *ctx, const char *label)
{
fflush(stdout);
fflush(stderr);
printf("%s\n", label);
talloc_report_depth_cb(ctx, 0, 100, report_callback, NULL);
fflush(stdout);
}
static void dump_sdp(const char *str, const char *prefix)
{
while (str && *str) {
const char *line_end = str;
while (*line_end && *line_end != '\r' && *line_end != '\n')
line_end++;
while (*line_end == '\r' || *line_end == '\n')
line_end++;
printf("%s%s\n", prefix, osmo_escape_str(str, line_end - str));
str = line_end;
}
}
struct sdp_msg_test_data {
const char *sdp_input;
const char *expect_sdp_str;
};
static const struct sdp_msg_test_data sdp_msg_tests[] = {
{
"v=0\r\n"
"o=- 5628250 5628250 IN IP4 192.168.11.121\r\n"
"s=-\r\n"
"c=IN IP4 192.168.11.121\r\n"
"t=0 0\r\n"
"m=audio 10020 RTP/AVP 18 0 2 4 8 96 97 98 100 101\r\n"
"a=rtpmap:18 G729/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:2 G726-32/8000\r\n"
"a=rtpmap:4 G723/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:96 G726-40/8000\r\n"
"a=rtpmap:97 G726-24/8000\r\n"
"a=rtpmap:98 G726-16/8000\r\n"
"a=rtpmap:100 NSE/8000\r\n"
"a=fmtp:100 192-193\r\n"
"a=rtpmap:101 telephone-event/8000\r\n"
"a=fmtp:101 0-15\r\n"
"a=ptime:20\r\n"
"a=sendrecv\r\n"
,
},
{
"v=0\r\n"
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
"s=FooBar\r\n"
"c=IN IP4 192.168.11.151\r\n"
"t=0 0\r\n"
"m=audio 16398 RTP/AVP 98\r\n"
"a=rtpmap:98 AMR/8000\r\n"
"a=fmtp:98 octet-align=1; mode-set=4\r\n"
"a=ptime:20\r\n"
"a=rtcp:16399 IN IP4 192.168.11.151\r\n"
,
"v=0\r\n"
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
"s=FooBar\r\n"
"c=IN IP4 192.168.11.151\r\n"
"t=0 0\r\n"
"m=audio 16398 RTP/AVP 98\r\n"
"a=rtpmap:98 AMR/8000\r\n"
"a=fmtp:98 octet-align=1; mode-set=4\r\n"
"a=ptime:20\r\n"
/* The rtcp line is dropped, not supported yet */
},
{
"v=0\r\n"
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
"s=FooBar\r\n"
"c=IN IP4 192.168.11.140\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
"a=rtpmap:18 G729/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:4 G723/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:101 telephone-event/8000\r\n"
"a=fmtp:101 0-15\r\n"
"a=sendrecv\r\n"
"a=rtcp:30437\r\n"
"a=ptime:20\r\n"
,
"v=0\r\n"
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
"s=FooBar\r\n"
"c=IN IP4 192.168.11.140\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
"a=rtpmap:18 G729/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:4 G723/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:101 telephone-event/8000\r\n"
"a=fmtp:101 0-15\r\n"
/* a=sendrecv ends up further below */
/* The rtcp line is dropped, not supported yet */
"a=ptime:20\r\n"
"a=sendrecv\r\n"
,
},
{
"v=0\r\n"
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
"s=FooBar\r\n"
"c=IN IP4 192.168.11.140\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
"a=rtpmap:18 G729/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:4 G723/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:101 telephone-event/8000\r\n"
"a=fmtp:101 0-15\r\n"
"a=recvonly\r\n"
"a=rtcp:30437\r\n"
"a=ptime:20\r\n"
,
"v=0\r\n"
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
"s=FooBar\r\n"
"c=IN IP4 192.168.11.140\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
"a=rtpmap:18 G729/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:4 G723/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:101 telephone-event/8000\r\n"
"a=fmtp:101 0-15\r\n"
/* a=recvonly ends up further below */
/* The rtcp line is dropped, not supported yet */
"a=ptime:20\r\n"
"a=recvonly\r\n"
,
},
{
"v=0\r\n"
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
"s=FooBar\r\n"
"c=IN IP4 192.168.11.140\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
"a=rtpmap:18 G729/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:4 G723/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:101 telephone-event/8000\r\n"
"a=fmtp:101 0-15\r\n"
"a=ptime:20\r\n"
"a=sendonly\r\n"
,
},
{
"v=0\r\n"
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
"s=FooBar\r\n"
"c=IN IP4 192.168.11.140\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
"a=rtpmap:18 G729/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:4 G723/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:101 telephone-event/8000\r\n"
"a=fmtp:101 0-15\r\n"
"a=ptime:20\r\n"
"a=inactive\r\n"
,
},
};
static void test_parse_and_compose(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
bool ok = true;
printf("\n\n%s\n", __func__);
for (i = 0; i < ARRAY_SIZE(sdp_msg_tests); i++) {
const struct sdp_msg_test_data *t = &sdp_msg_tests[i];
struct osmo_sdp_msg *sdp_msg;
char str[1024];
const char *expect;
struct osmo_sdp_msg_decode_ret ret;
printf("\n[%d]\n", i);
dump_sdp(t->sdp_input, "sdp input: ");
sdp_msg = osmo_sdp_msg_decode(ctx, t->sdp_input, -1, &ret);
if (ret.rc) {
printf("ERROR: %s at %s\n", strerror(abs(ret.rc)),
osmo_quote_cstr_c(print_ctx, ret.error.at_input_str, ret.error.at_input_str_len));
ok = false;
}
printf("parsed SDP message %s\n", osmo_sdp_msg_to_str_c(print_ctx, sdp_msg, false));
osmo_sdp_msg_encode_buf(str, sizeof(str), sdp_msg);
dump_sdp(str, "osmo_sdp_msg_encode_buf: ");
expect = t->expect_sdp_str ? : t->sdp_input;
if (strcmp(str, expect)) {
int j;
ok = false;
printf("ERROR:\n");
dump_sdp(expect, "expect: ");
for (j = 0; expect[j]; j++) {
if (expect[j] != str[j]) {
printf("ERROR at position %d, at:\n", j);
dump_sdp(str + j, " mismatch: ");
break;
}
}
} else
printf("[%d] ok\n", i);
report(ctx);
printf("talloc_free(sdp_msg)\n");
talloc_free(sdp_msg);
report(ctx);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak\n");
talloc_free_children(ctx);
}
printf("\n");
}
OSMO_ASSERT(ok);
talloc_free(ctx);
talloc_free(print_ctx);
}
struct intersect_test_data {
const char *descr;
const char *sdp_msg_a;
const char *sdp_msg_b;
const char *expect_intersection;
};
#define SDP_1 \
"v=0\r\n" \
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
"s=GSM Call\r\n" \
"c=IN IP4 23.42.23.42\r\n" \
"t=0 0\r\n" \
"m=audio 30436 RTP/AVP 112 3 111 110\r\n" \
"a=rtpmap:112 AMR/8000\r\n" \
"a=fmtp:112 octet-align=1\r\n" \
"a=rtpmap:3 GSM/8000\r\n" \
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
"a=rtpmap:110 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
#define SDP_2 \
"v=0\r\n" \
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
"s=GSM Call\r\n" \
"c=IN IP4 23.42.23.42\r\n" \
"t=0 0\r\n" \
"m=audio 30436 RTP/AVP 112 110\r\n" \
"a=rtpmap:112 AMR/8000\r\n" \
"a=fmtp:112 octet-align=1\r\n" \
"a=rtpmap:110 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
#define SDP_3 \
"v=0\r\n" \
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
"s=GSM Call\r\n" \
"c=IN IP4 23.42.23.42\r\n" \
"t=0 0\r\n" \
"m=audio 30436 RTP/AVP 3 111 123\r\n" \
"a=rtpmap:3 GSM/8000\r\n" \
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
"a=rtpmap:123 FOO/8000\r\n" \
"a=ptime:20\r\n"
#define SDP_4 \
"v=0\r\n" \
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
"s=GSM Call\r\n" \
"c=IN IP4 23.42.23.42\r\n" \
"t=0 0\r\n" \
"m=audio 30436 RTP/AVP 3 111\r\n" \
"a=rtpmap:3 GSM/8000\r\n" \
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
"a=ptime:20\r\n"
#define SDP_5 \
"v=0\r\n" \
"o=libosmo-sdp 0 0 IN IP4 0.0.0.0\r\n" \
"s=GSM Call\r\n" \
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 0 RTP/AVP 112 113 110 3 111\r\n" \
"a=rtpmap:112 AMR/8000\r\n" \
"a=fmtp:112 octet-align=1;mode-set=0,1,2,3\r\n" \
"a=rtpmap:113 AMR-WB/8000\r\n" \
"a=fmtp:113 octet-align=1\r\n" \
"a=rtpmap:110 GSM-EFR/8000\r\n" \
"a=rtpmap:3 GSM/8000\r\n" \
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
"a=ptime:20\r\n"
static const struct intersect_test_data intersect_tests[] = {
{
.descr = "identical codecs lead to no change",
.sdp_msg_a = SDP_1,
.sdp_msg_b =
"v=0\r\n"
"c=IN IP4 5.6.7.8\r\n"
"m=audio 12345 RTP/AVP 112 3 111 110\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
,
.expect_intersection = SDP_1,
},
{
.descr = "identical codecs in different order also lead to no change",
.sdp_msg_a = SDP_1,
.sdp_msg_b =
"v=0\r\n"
"c=IN IP4 5.6.7.8\r\n" \
"m=audio 12345 RTP/AVP 3 110 111 112\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
,
.expect_intersection = SDP_1,
},
{
.descr = "identical codecs with mismatching payload type numbers also lead to no change",
.sdp_msg_a = SDP_1,
.sdp_msg_b =
"v=0\r\n"
"c=IN IP4 5.6.7.8\r\n" \
"m=audio 12345 RTP/AVP 96 97 98 99\r\n"
"a=rtpmap:96 GSM/8000\r\n"
"a=rtpmap:97 GSM-EFR/8000\r\n"
"a=rtpmap:98 GSM-HR-08/8000\r\n"
"a=rtpmap:99 AMR/8000\r\n"
"a=fmtp:99 octet-align=1\r\n"
,
.expect_intersection = SDP_1,
},
{
.descr = "identical codecs plus some extra codecs also lead to no change" ,
.sdp_msg_a = SDP_1,
.sdp_msg_b =
"v=0\r\n"
"c=IN IP4 5.6.7.8\r\n" \
"m=audio 12345 RTP/AVP 8 0 96 97 98 99\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:96 GSM/8000\r\n"
"a=rtpmap:97 GSM-EFR/8000\r\n"
"a=rtpmap:98 GSM-HR-08/8000\r\n"
"a=rtpmap:99 AMR/8000\r\n"
"a=fmtp:99 octet-align=1\r\n"
,
.expect_intersection = SDP_1,
},
{
.descr = "some codecs removed",
.sdp_msg_a = SDP_1,
.sdp_msg_b = SDP_2,
.expect_intersection = SDP_2,
},
{
.descr = "other codecs removed",
.sdp_msg_a = SDP_1,
.sdp_msg_b = SDP_3,
.expect_intersection = SDP_4,
},
{
.descr = "all codecs removed",
.sdp_msg_a = SDP_1,
.sdp_msg_b =
"v=0\r\n"
"s=empty"
,
.expect_intersection =
"v=0\r\n"
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
"s=GSM Call\r\n"
"c=IN IP4 23.42.23.42\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP\r\n"
"a=ptime:20\r\n"
},
{
.descr = "some real world test case",
.sdp_msg_a = SDP_5,
.sdp_msg_b = SDP_5,
.expect_intersection = SDP_5,
},
};
static const char *sdp_msg_logstr(const struct osmo_sdp_msg *sdp_msg)
{
static char buf[1024];
osmo_sdp_msg_encode_buf(buf, sizeof(buf), sdp_msg);
return buf;
}
static void test_intersect(void)
{
int i;
bool ok = true;
void *ctx = talloc_named_const(test_ctx, 0, __func__);
printf("\n\n%s\n", __func__);
for (i = 0; i < ARRAY_SIZE(intersect_tests); i++) {
const struct intersect_test_data *t = &intersect_tests[i];
struct osmo_sdp_msg *sdp_msg_a = NULL;
struct osmo_sdp_msg *sdp_msg_b = NULL;
char str[1024];
printf("\n[%d] %s\n", i, t->descr);
dump_sdp(t->sdp_msg_a, "SDP A: ");
dump_sdp(t->sdp_msg_b, " SDP B: ");
sdp_msg_a = osmo_sdp_msg_decode(ctx, t->sdp_msg_a, -1, NULL);
if (!sdp_msg_a) {
printf("ERROR parsing SDP A\n");
break;
}
dump_sdp(sdp_msg_logstr(sdp_msg_a), "parsed SDP A: ");
struct osmo_sdp_msg_decode_ret r;
sdp_msg_b = osmo_sdp_msg_decode(ctx, t->sdp_msg_b, -1, &r);
if (!sdp_msg_b) {
printf("ERROR parsing SDP B\n");
break;
}
dump_sdp(sdp_msg_logstr(sdp_msg_b), "parsed SDP B: ");
osmo_sdp_codec_list_intersection(sdp_msg_a->codecs, sdp_msg_b->codecs,
&osmo_sdp_codec_cmp_equivalent,
false);
osmo_sdp_msg_encode_buf(str, sizeof(str), sdp_msg_a);
dump_sdp(str, "intersection(a,b): ");
if (strcmp(str, t->expect_intersection)) {
int j;
ok = false;
printf("ERROR:\n");
dump_sdp(t->expect_intersection, "expect_intersection: ");
for (j = 0; t->expect_intersection[j]; j++) {
if (t->expect_intersection[j] != str[j]) {
printf("ERROR at position %d, at:\n", j);
dump_sdp(str + j, " mismatch: ");
break;
}
}
} else
printf("[%d] ok\n", i);
report(ctx);
printf("talloc_free(sdp_msg_a)\n");
talloc_free(sdp_msg_a);
report(ctx);
printf("talloc_free(sdp_msg_b)\n");
talloc_free(sdp_msg_b);
report(ctx);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak\n");
talloc_free_children(ctx);
}
printf("\n");
}
OSMO_ASSERT(ok);
talloc_free(ctx);
}
struct sdp_select_test_data {
const char *sdp;
const struct osmo_sdp_codec_cmp_flags *cmpf;
const struct osmo_sdp_codec select;
const char *expect_sdp;
};
static const struct osmo_sdp_codec_cmp_flags pt_only = { .payload_type = true };
static const struct sdp_select_test_data sdp_select_tests[] = {
{
"v=0\r\n"
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
"s=GSM Call\r\n"
"c=IN IP4 23.42.23.42\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
"a=ptime:20\r\n"
,
&pt_only,
{ .payload_type = 112, },
NULL
},
{
"v=0\r\n"
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
"s=GSM Call\r\n"
"c=IN IP4 23.42.23.42\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
"a=ptime:20\r\n"
,
&pt_only,
{ .payload_type = 3, },
"v=0\r\n"
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
"s=GSM Call\r\n"
"c=IN IP4 23.42.23.42\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 3 112 111 110\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
"a=ptime:20\r\n"
},
{
"v=0\r\n"
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
"s=GSM Call\r\n"
"c=IN IP4 23.42.23.42\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
"a=ptime:20\r\n"
,
&pt_only,
{ .payload_type = 111, },
"v=0\r\n"
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
"s=GSM Call\r\n"
"c=IN IP4 23.42.23.42\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 111 112 3 110\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
"a=ptime:20\r\n"
},
{
"v=0\r\n"
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
"s=GSM Call\r\n"
"c=IN IP4 23.42.23.42\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
"a=ptime:20\r\n"
,
&pt_only,
{ .payload_type = 110, },
"v=0\r\n"
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
"s=GSM Call\r\n"
"c=IN IP4 23.42.23.42\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 110 112 3 111\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=ptime:20\r\n"
},
};
static void test_select(void)
{
int i;
bool ok = true;
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
printf("\n\n%s\n", __func__);
for (i = 0; i < ARRAY_SIZE(sdp_select_tests); i++) {
const struct sdp_select_test_data *t = &sdp_select_tests[i];
struct osmo_sdp_msg *sdp_msg;
char buf[1024];
const char *expect_sdp;
printf("\n[%d]\n", i);
sdp_msg = osmo_sdp_msg_decode(ctx, t->sdp, -1, NULL);
if (!sdp_msg) {
printf("ERROR parsing SDP\n");
break;
}
printf("SDP: %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, sdp_msg->codecs, false));
printf("Select: %s\n", osmo_sdp_codec_to_str_c(print_ctx, &t->select));
osmo_sdp_codec_list_move_to_first(sdp_msg->codecs, &t->select, t->cmpf);
printf("SDP: %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, sdp_msg->codecs, false));
osmo_sdp_msg_encode_buf(buf, sizeof(buf), sdp_msg);
expect_sdp = t->expect_sdp ? : t->sdp;
if (strcmp(buf, expect_sdp)) {
int j;
ok = false;
printf("ERROR:\n");
dump_sdp(buf, "selection result: ");
dump_sdp(expect_sdp, "expect result: ");
for (j = 0; expect_sdp[j]; j++) {
if (expect_sdp[j] != buf[j]) {
printf("ERROR at position %d, at:\n", j);
dump_sdp(buf + j, " mismatch: ");
break;
}
}
} else
printf("[%d] ok\n", i);
report(ctx);
printf("talloc_free(sdp_msg)\n");
talloc_free(sdp_msg);
report(ctx);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak\n");
talloc_free_children(ctx);
}
printf("\n");
talloc_free_children(print_ctx);
}
OSMO_ASSERT(ok);
talloc_free(ctx);
talloc_free(print_ctx);
}
struct my_obj {
struct osmo_sdp_msg *sdp_msg;
};
static struct my_obj *my_obj_alloc(void *ctx)
{
struct my_obj *o = talloc_zero(ctx, struct my_obj);
return o;
}
static void test_obj_members(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
struct my_obj *o;
printf("\n\n--- %s()\n", __func__);
o = my_obj_alloc(ctx);
o->sdp_msg = osmo_sdp_msg_alloc(o);
printf("o->sdp_msg = '%s'\n", osmo_sdp_msg_encode_c(print_ctx, o->sdp_msg));
report(ctx);
const struct osmo_sdp_codec all_codecs[] = {
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
};
for (i = 0; i < ARRAY_SIZE(all_codecs); i++)
osmo_sdp_codec_list_add(o->sdp_msg->codecs, &all_codecs[i], false, false);
printf("o->sdp_msg = '%s'\n", osmo_sdp_msg_encode_c(print_ctx, o->sdp_msg));
report(ctx);
printf("talloc_free(o)\n");
talloc_free(o);
report(ctx);
talloc_free(ctx);
talloc_free(print_ctx);
}
typedef void (*test_func_t)(void);
static const test_func_t test_func[] = {
test_parse_and_compose,
test_intersect,
test_select,
test_obj_members,
};
int main(void)
{
int i;
test_ctx = talloc_named_const(NULL, 0, "sdp_codec_test");
for (i = 0; i < ARRAY_SIZE(test_func); i++) {
test_func[i]();
if (talloc_total_blocks(test_ctx) != 1) {
talloc_report_full(test_ctx, stderr);
printf("ERROR after test %d: memory leak\n", i);
return -1;
}
}
talloc_free(test_ctx);
return 0;
}

View File

1321
tests/sdp/sdp_msg_test.ok Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -13,3 +13,24 @@ AT_KEYWORDS([mgcp])
cat $abs_srcdir/mgcp/mgcp_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
AT_CLEANUP
AT_SETUP([sdp_fmtp])
AT_KEYWORDS([sdp_fmtp])
cat $abs_srcdir/sdp/sdp_fmtp_test.ok > expout
cat $abs_srcdir/sdp/sdp_fmtp_test.err > experr
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_fmtp_test], [], [expout], [experr])
AT_CLEANUP
AT_SETUP([sdp_codec])
AT_KEYWORDS([sdp_codec])
cat $abs_srcdir/sdp/sdp_codec_test.ok > expout
cat $abs_srcdir/sdp/sdp_codec_test.err > experr
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_codec_test], [], [expout], [experr])
AT_CLEANUP
AT_SETUP([sdp_msg])
AT_KEYWORDS([sdp_msg])
cat $abs_srcdir/sdp/sdp_msg_test.ok > expout
cat $abs_srcdir/sdp/sdp_msg_test.err > experr
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_msg_test], [], [expout], [experr])
AT_CLEANUP