Compare commits

...

224 Commits

Author SHA1 Message Date
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 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 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 Hofmeyr 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
Neels Hofmeyr 43eed63b09 client: safely handle dealloc on event dispatch
See also the long in-code comment.

Related: OS#6302
Change-Id: I6f1c0f6a26f9cd6993dc1910a44070ec0438e636
2023-12-19 11:46:42 +00:00
Vadim Yanitskiy 7a6d9c2f72 mgcp: correctly put NUL character in mgcp_msg_terminate_nul()
As was pointed out during code review of a previously merged patch,
whenever we write something to the tailroom, we must use msgb_put().

Change-Id: Ibe1605631a94088bfd1f603f7043322ed5585ffe
2023-12-17 18:54:25 +07:00
Vadim Yanitskiy f3715dc0d3 mgcp: reserve once byte for '\0' in mgcp_do_read()
We need to be able to terminate the received string in case it was
not nul-terminated by the sender (see mgcp_msg_terminate_nul()).

Change-Id: Icc878af7f671213bb516af62cb601914d86ff808
Fixes: CID#272990
2023-12-17 11:50:44 +00:00
Vadim Yanitskiy a68a863b9d mgcp: simplify getting msgb tail in mgcp_msg_terminate_nul()
The current statement:

  msg->l2h + msgb_l2len(msg)

looks as follows, if we expand the msgb_l2len():

  msg->l2h + msgb->tail - msg->l2h

so this is basically equal to msgb->tail alone.

Change-Id: I4f4b0f792bbeef94a5449c4a5843628a703a3d54
Related: CID#272990
2023-12-17 11:50:44 +00:00
Neels Hofmeyr 18717f5247 client: move some items to internal header
There is an upcoming modification of structs mgcp_msg and mgcp_response.
When it is public API, it needs to be kept backwards compatible.

But no libosmo-mgcp-client caller (in the osmocom-cni world) has used
these structs in years and years. Everyone should use the higher level
osmo_mgcpc_* API only.

Move this legacy API to a private header, so we no longer need to worry
about compatibility there.

Related: OS#6171
Related: I798e02c6663376d3d52f4a74fc4b32411ce95bed
Change-Id: I6d8bdda5c2ffa236e94a0b6111de4dbfac923187
2023-12-08 07:02:55 +01:00
Neels Hofmeyr 677b5396cb fix possible NULL deref on early media
end.codec may be NULL, depending on whether a single matching codec
could be found or not, i.e. based on external input. So let's not crash
if an RTP packet arrives while end.codec == NULL.

Change-Id: I9bfb55a343b3f2b1459e0aba4ee71a6133b992b3
2023-12-08 06:59:37 +01:00
Neels Hofmeyr 5d0e07112f mgcp_client_test: fix function name
Change-Id: I213f9b2713ff80a16603b8342c204cb546cc782d
2023-12-08 05:57:37 +00:00
Pau Espin a5acaa68db mgcp-client: Transmit remote IP addr in CRCX if known and port=0
A client may know the IP address during CRCX but not yet the port, or it
may simply want to hint an initial IP address so that the MGW can better
guess when allocating a local IP address.

Related: SYS#6657
Change-Id: I30165dbac5e484011d0acf46af36f105954a501d
2023-12-07 11:14:53 +00:00
Pau Espin 7787a3aeae IuUP: Allow Initialization with set rem IP address and unset rem port
Do not refuse IuUP Initialization messages coming in on an RTP port if
the remote port is not yet known.
If an IUFP conn is not yet configured (pre-Initialization), allow rx
from any address or port.

An osmo-mgw client (eg. osmo-hnbgw) may wish to initially set a remote
IP address as a hint during CRCX, hence the IP address may already be
set while the port may be unset.

Related: SYS#6657
Change-Id: Idd833997abce46886e9664505b2776fa5dadc8db
2023-12-05 13:50:42 +01:00
Neels Hofmeyr dd1ddf74fc check_rtp_origin: drop special case for legacy IuUP hack
We have proper IuUP support and everything about this legacy hack should
be purged.

The purpose of this function is to validate that RTP is coming from the
expected address and port. To allow that legacy IuUP hack, which is no
longer needed, we punched a hole into this validation, by adding this
special case for loopback mode (suddenly we don't care who or what sends
RTP and bounce it back to anyone). So let's get rid of this hole that
was only needed for very early 3G voice hacking.

Instead, we permit RTP for IuUP Initialization regardless of the RTP
loopback/send/recv mode since I6c365559a7bd197349f0ea99f7a13b56a4bb580b

Related: SYS#6657
Change-Id: I158dd046fdfcb10392cde3de8cc88dd095a05b40
2023-12-05 13:50:42 +01:00
Neels Hofmeyr 5e101c9fed IuUP: allow Initialization from any address if not yet set
Do not refuse IuUP Initialization messages coming in on an RTP port if
the remote IP address is not yet known.
If an IUFP conn is not yet configured (pre-Initialization), allow rx
from any remote address if the remote IP address is not yet known.

If we refuse the IuUP Initialization, a 3G RNC may fail to set up a RAB.
We will know the remote address only *after* assigning a RAB succeeded.
So the IuUP Initialization must be allowed before knowing all addresses.

At the time of writing, CRCX for IUFP are sent to osmo-mgw in either
LOOPBACK or in RECVONLY mode:
- current osmo-msc: recvonly
- osmo-msc <= v1.10.0: loopback
- osmo-hnbgw: loopback
IuUP Initialization should work regardless of that.
See also next patch I158dd046fdfcb10392cde3de8cc88dd095a05b40

IuUP is one layer below the loopback/send/recv decision for RTP; IuUP is
always terminated at the MGW, while the AMR payload carries through.

Related: alternative patch Idd833997abce46886e9664505b2776fa5dadc8db
Related: SYS#6657
Change-Id: I6c365559a7bd197349f0ea99f7a13b56a4bb580b
2023-12-05 13:49:10 +01:00
Pau Espin f6db465082 cosmetic: Fix line indentation
Change-Id: I85e47894aa0ed54dbf22a7a88d8cbeadaad4b248
2023-12-05 13:35:41 +01:00
Pau Espin a7e6fbc220 mgcp_network: Improve err logging when rtp pkt from unexpected origin comes in
Change-Id: Id9b60395df667ae9898c23cbc2afe56ac7e8b0e5
2023-12-04 11:21:53 +01:00
Neels Hofmeyr cd132126ef systemd,manual: set LimitNOFILE=65536
A typical OS imposed limit is 1024 open FD, which can bee too low when
there are hundreds of concurrent voice calls.

In systemd service file, set a super high limit of 65536.

In osmo-mgw's user manual, add section 'Configure limits' describing
this in detail.

Related: OS#6256
Related: osmo-bsc I26c4058484b11ff1d035a919bf88824c3af14e71
Change-Id: I46512517bc3b5bb90cac7643e7ac73afba398d36
2023-12-03 02:20:48 +00:00
Andreas Eversberg 249d21eb1e Use uniform log format for default config files
Related: OS#6272
Change-Id: I82ee4ce3c961976526a792862061c237a372e31b
2023-12-01 12:48:09 +01:00
Neels Hofmeyr 0127a0608d client: replace two assertions with graceful error handling
A user reports crashes of osmo-bsc upon EV_MDCX. It turns out that there
is a lot of error reporting and a distinct possibility to get a NULL
return value because of external input. Terminate the FSM instead.

FSM termination is the proper way to report a bad error, it signals the
parent_term_evt to the FSM parent, which will then be able to act on the
failed MGCP operation.

Related: SYS#6632
Change-Id: Ia5d8a9aff565399a85a5b116d7029fedcab234e0
2023-11-30 02:31:53 +00:00
Neels Hofmeyr 8b663e4d7c mgcp_parse_audio_port_pt(): fix buffer overflow
Change-Id: I18c78d15eb1593f404b4741248225b68878b463f
2023-11-15 22:09:55 +00:00
Keith Whyte 5e5c7bc176 vty and log: also show local port for RTP conns
Before:

    CONN: (1226/rtp, id:0xD94316AD, ip:127.0.0.2, rtp:2344 rtcp:2345)

After:

    CONN: (1226/rtp C:D94316AD r=127.0.0.2:2344<->l=127.0.0.1:4002)

While changing that string, also include these changes for consistency
and readability:

- use the same r:...<->l:... format as osmo_sock_get_name().
- Instead of 'id:0x' use the actual MGCP format 'C: 9B686BE3'.
- drop the commas
- drop RTCP port: it is always RTP+1 and always an odd number.

Rationale:
The CONN pairs associated with each endpoint show remote RTP
ports. When osmo-mgw is being used by both BSC and MSC, one
side of the pair is showing the internal loop connection inside
osmo-mgw, while my intuition suggested this connection pair
is showing me the RTP port tuple of a single RTP stream. Adding
the local port to the display makes it more clear, IMHO.
Seeing the local port can also help to correlate the MGW vty
dump with a capture of RTP.

Implementation:
I first tried directly using osmo_sock_get_name_buf() on
conn->u.rtp.end.rtp.fd, but that might hide already known information
when the fd is not actively used yet (before SDP): the local address and
port would then be shown from the fd, not from
conn->u.rtp.end.local_addr/_port == hidden before the fd is set up.

Patch-By: whytek, nhofmeyr
Change-Id: Ib89a6779e1d68c6600f00699d4303f6c0ee07132
2023-11-02 18:44:16 +00:00
Neels Hofmeyr c695e9f972 add mgcp_conn_rtp_type_names[]
First use in upcoming Ib89a6779e1d68c6600f00699d4303f6c0ee07132

Change-Id: If3d8bc68a3b26d3aa0ba7eedeab67b820889ed54
2023-11-01 21:30:29 +01:00
Oliver Smith 2bdad960ff Bump version: 1.12.0.3-58d5b → 1.12.1
Change-Id: Iefb000582a139ff53c4afbf94e1299e26ceeac44
2023-09-28 15:58:57 +02:00
Pau Espin 58d5b97831 mgw: Configure IuUP if codec set during MDCX
The mgcp client may first configure the connection to use RTP-AMR, but
after setting up another call leg may find out that both legs are IuUP
and hence want to forward the IuUP between the 2 connections instead.
In that case, an MDCX with codec VND.3GPP.IUFP would be set.

Until now, osmo-mgw didn't take that scenario into account, and it was
only upgrading the rtp conn to iuup internally during CRCX.
As a result, in the mentioned scenario osmo-mgw would continue to
output RTP instead of IuUP after the MDCX with VND.3GPP.IUFP, which is
wrong.

Related: SYS#6578
Change-Id: Ic94bf90f54d8ba3e65a2cd52734867847f3a60c2
2023-09-27 16:25:58 +02:00
Pau Espin 6225b5cecb mgcp-client: Introduce API osmo_mgcpc_ep_local_name()
Change-Id: I18d7bdf650c0ec87ae16ed4944aed9f495400137
2023-09-26 12:44:11 +02:00
Pau Espin c415ed5113 mgcp-client: Fix missing include in mgcp_client_pool.h
struct vty is used as a param but it is never defined.

Change-Id: Ia27bb20a79966cb90e04720462d24a236a168ada
2023-09-22 18:03:02 +02:00
Pau Espin 730a1f28d2 Bump version: 1.11.0.51-af67-dirty → 1.12.0
Change-Id: I9b1272cacaeaf1213f9a372eda52aac560cfbde5
2023-09-12 14:48:51 +02:00
Andreas Eversberg af67178581 ASCI: Support conference briding with 1..n connections
For each RTP packet that is received from a connection, the mode is
checked whether receiving is allowed or not. If not it is discarded.

In case of "confecho" mode, the RTP is also sent by the receiving
connection.

Then a loop is used to send RTP to all sending endpoints except the one
that received the packet.

Because we have a loop that allows to have 1..n connections, we have no
maximum number of allowed connections anymore.

Change-Id: Ic99a55ab5a3a6170e940403fadd52697e99f2f3a
Related: OS#4853
2023-07-10 12:14:05 +02:00
Andreas Eversberg dc7dfd0bbd ASCI: Add new mode for voice group/broadcast call
The new mode "confecho" is similar to "sendrecv", except that it also
echoes back RTP towards the sender. This is required for voice group or
broadcast calls. Talker and listeners use the same timeslot, so that
audio must be echoed from the talker to the listeners.

It is different from "loopback", because a loopback only echoes back RTP
towards the sender, but does not forward audio through the endpoint to
the other connections. Also it does not forward RTP from senders of
other connections.

The current implementation of MGW does not support transcoding and
mixing. This means that a sending connection must not send RTP that has
been received by multiple receiving connections. The application that
uses the MGW has to set the connection modes, so that only one
connection receives RTP in case of a conference.

Change-Id: I0639c663e119d85bef1010c7aa45e2f133a9daf0
Related: OS#4853
2023-07-10 11:54:16 +02:00
Neels Hofmeyr c364f4ab5a mgcp_client: tweak extract_codec_name() implementation
Instead of calling strlen() for every loop iteration, just use strchr()
to find the position of '/'.

Change-Id: Ifc7302b6c5f9288a622e33c3e8b5fe0e7037dbdc
2023-06-30 05:25:12 +02:00
Neels Hofmeyr e9989b9e76 mgcp: fix "L: a:" header parsing: heed ";" separator
In the "L: a:" header, read the first codec name only up to the ";"
separator, and ignore the rest.

According to RFC-2705, the "L: a:" header may include multiple codecs
like "GSM-EFR;GSM" in:

  L: p:20, a:GSM-EFR;GSM, nt:IN

osmo-mgw can handle only a single codec here. Since recently, osmo-msc
is our first client that may actually send more than one codec. This
uncovered a bug that leads to failing voice calls:

* osmo-mgw parses the entire list "GSM-EFR;GSM" as a single codec name,
* puts that into the ptmap without scrutiny,
* and even sends it back in the OK response, in the *SDP* part, as a
  single "GSM-EFR;GSM" codec entry.

We do not care very much about the "a:" codec list, because we always
establish codecs via SDP later. So all we need to fix this is: parse the
first codec done correctly, and ignore the rest.

Related: OS#6081
Change-Id: I0342e85b32ed89f3a1fdb6131c3c8ded8f47a455
2023-06-30 05:25:12 +02:00
Oliver Smith 9423817311 mgcp_client: check rc of map_str_to_codec
Abort with error log when map_str_to_codec is -1, instead of writing it
as unsigned integer into ptmap[i].codec.

Related: OS#6074
Change-Id: I08b91c849d810fe3cdb72c0f6f2a558b7377deab
2023-06-28 12:21:30 +00:00
Oliver Smith 292ba1bdb1 Cosmetic: fix a typo
Change-Id: I49f72acc3a42f6c7d50aeffdb1aef312c469d9f3
2023-06-28 11:49:45 +00:00
Pau Espin 10d9f8dcf4 mgcp-client: Mark client as UP when keepalive request-interval/timeout is disabled through VTY
This way if keepalive becomes disabled for an MGW, it can be selected
again (otherwise it would become non-selectable forever, and we already
have a "blocked" config for that).

Fixes: 563386e8bb
Related: SYS#6481
Change-Id: I263d23885a1a967f844033f9810b96691b8e3b30
2023-06-27 17:38:35 +02:00
Pau Espin 4c0658955e mgcp-client: Always mark client as UP if keepalive request-interval disabled
In the previous logic, if "keepalive request-interval" was disabled
(value 0, default), then if the client was configured to send a DLCX on
startup it would end up in state UP or DOWN depending on whether the MGW
answered to that request.
As a result, an MGW that wouldn't answer would be left forever in DOWN
state since it would have been selected no more and there would be a
keepalive configured to re-mark it as UP.

Fixes: 563386e8bb
Related: SYS#6481
Change-Id: I290f7436f48418ee25179951359c76208796e279
2023-06-27 17:38:27 +02:00
Pau Espin e9164c295d mgcp_client: pool: Only pick clients with an MGCP link considered to be UP
This way the user ends up picking a working MGW instance instead of one
which is not reachable around the time.

Related: SYS#6481
Change-Id: Ia3f451d3cd97851f65074408812b1ddc68f67056
2023-06-15 16:46:50 +02:00
Pau Espin 563386e8bb mgcp-client: Add keepalive feature
The `keepalive` feature in libosmo-mgcp-client allows scheduling periodical
queries on the MGCP layer in order to make sure it is reachable and hence
obtain information on the state of the MGCP link.
This patch only uses it to print the status on the VTY, but it will be used
too in a follow-up commit by the MGW Pool when picking an MGW from the pool:
MGWs whose link is considered to be DOWN are skipped.

The feature consists of:
- A `keepalive request-interval` which will trigger a transmission of an MGCP
  AuditEndpoint command targeting endpoint with name `keepalive request-endpoint`.
  This interval is updated every time any message is transmitted in the MGCP
  link, meaning the MGCP AuditEndpoint message is only triggered if no message
  has been transmitted since `keepalive request-interval` seconds ago.
- A `keepalive timeout` considering the MGW to be non-reachable (link DOWN) if
  no message is received over that amount of time.

The `keepalive` parameters are to be preferrably configured so that
"keepalive request-interval" * 2 < "keepalive timeout".

Example VTY configuration of `keepalive` feature in libosmo-mgcp-client:
----
 mgw 0
  ...
  keepalive request-interval 20 <1>
  keepalive request-endpoint null <2>
  keepalive timeout 50 <3>
----

<1> Transmit an MGCP AuditEndpoint message to the MGW if no message has been
    sent to it over last 10 seconds
<2> The MGCP AuditEndpoint targets the `null` endpoint. This is a special
    endpoint available at OsmoMGW for those purposes, but any available
    endpoint can be configured and used instead.
<3> Consider the MGCP link to be DOWN if no message is received from the
    MGW over the last 50 seconds

NOTE: The `keepalive` feature is disabled by default, and must be explicitly
      configured in order to enable it.

Related: SYS#6481
Change-Id: I3dc74c78548d017f272da863d5282dc5e0020ca3
2023-06-15 16:46:46 +02:00
Pau Espin af0f58fac7 mgw: Allow auditing speciall 'null' endpoint
This is a special endpoint which can always be audited. This is useful
for clients who wish to submit requests to osmo-mgw periodically to find
out whether the MGW is still reachable. This endpoint will be used by
libomso-mgcp-client as default target endpoint to implement such
feature.
This "null" Endpoint is osmo-mgw specific, not described in MGCP specs.

Related: SYS#6481
Change-Id: Ia409b16e9211e6261e2e0f21288544289d6f3733
2023-06-15 11:08:50 +02:00
Pau Espin 2e411f33a0 mgcp-client: Move some static functions further above
This is a preparation commit for follow-up one, where some of these
functions need to be used in other static functions.

Related: SYS#6481
Change-Id: I3a00d8c47ec773390d9626364c4c75ca579f1508
2023-06-14 13:04:15 +02:00
Pau Espin 8e8d59ff0e mgcp_client: Introduce mgcp_client_conf_alloc(), deprecate mgcp_client_conf_init()
So far, the users of the old non-pooled API were in charge of allocating
the struct mgcp_client_conf by themselves, then init them using
mgcp_client_conf_init(). This causes a major problem, since it makes it
difficult to extend the struct mgcp_client_conf structure to add new
features, which may happen frequently.

The MGW pool API doesn't have this problem, because the struct
mgcp_client_conf is allocated as parts/fields of private structs defined
in internal headers. Only pointers to it are used in public headers.
Since it still has to internally initialize the conf fields, we still
need the API to initialize it internally, and hence why is it marked as
DEPRECTED_OUTSIDE instead of DEPRECATED.

While some programs already moved to the new MGW pool infrastructure,
they still use the old APIs to accomodate for old config files in order
to be back-compatible, hence most users of libosmo-mgcp-client are
affected.

Introduce an API to allocate the conf struct internally, which, while
still breaking the ABI, allows for a more relaxed update path where it's
possible to extend the struct mgcp_client_conf at the end.

Eventually the non pooled API should be gone and the struct
mgcp_client_conf can then be moved to a private header, but for now
let's add this small feature to avoid major ABI breakage.

Change-Id: Iba0853ed099a32cf1dde78c17e1b34343db41cfc
2023-06-14 10:59:50 +02:00
Philipp Maier 14da3a33cb mgcp_client.h: also add spec ref to the other 3gpp defined payload types
Some payload type numbers are defined in 3GPP TS 48.103, let's put the
spec reference between all of those so that it is immediately clear
where those numbers come from.

Change-Id: Ie9d949ee72286ee4de7590c99acc740011208466
2023-06-11 19:28:10 +00:00
Pau Espin ea2035326d mgcp-client: Drop unused struct mgcp_client field
Change-Id: I0b2c4a83ca0e59e54bf5e2af289e4e1fe4f0cf27
2023-06-09 11:38:00 +02:00
Philipp Maier 2bfa708527 mgcp_codec: be sensitive about IuFP when checking codecs
When two codecs are checked for convertibility we insist that
subtype_name and rate are equal since normally when those are different,
we assume a completely different codec that will require a transcoder,
which we do not have yet.

However when IuFP is used, the call agent will always negotiate IuFP as
VND.3GPP.IUFP with a rate of 16000, even though the IuFP payloads
contain regular AMR at a rate of 8000.

This means that if we detect IuFP on one side of the call leg and AMR on
the other side of the call leg, we must not insist on equal subtype_name
and rate.

This fixes the following TTCN3 testcases:
MGCP_Test.TC_two_crcx_mdcx_and_iuup_rtp
MGCP_Test.TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered

Related: OS#5461
Change-Id: I6bc1e6022efe21cb893ef213f3da35017960357d
2023-05-31 09:34:14 +00:00
Oliver Smith 72f911513e systemd: depend on networking-online.target
Related: SYS#6400
Change-Id: Ib6c78c76c5f13b9482428ce653a61b03b2aca1d3
2023-05-26 14:10:46 +02:00
Philipp Maier 3d0676a791 mgcp_network: do not deliver RTP packets with unpatched PT
When a call leg is set up, then the call agent (e.g. BSC, MSC, etc.)
will also negotiate a codec along with a payload type number. When
sending RTP packets, each RTP packet must also contain the negotiated
payload type number. To prevent the emission of RTP packets with an
incorrect payload type number, ensure that no packet is sent when
mgcp_patch_pt() fails.

Change-Id: I013a24c1e0f853557257368cfab9192d4611aafa
Related: OS#5461
2023-05-22 11:18:45 +02:00
Philipp Maier 4c4d227624 mgcp_codec: fix codec decision
Unfortunately OsmoMGW was never really tested with multiple different
codecs on either side of the connection. While OsmoMSC and OsmoBSC only
assign exactly one codec on each side this has never been a problem,
however it might become a problem when a call agent assigns multiple
codecs on one side. This has been observed in a setup where OsmoMGW had
one leg towards an external call agent. Also due to recent upgrades to
the TTCN3 tests we are now able to simulate different codecs on both
sides to pinpoint issues.

Testing has shown that OsmoMGW has difficulties with multiple codecs.
The reason for this is that the function that makes the codec decision
was not fully finished. So let's finish the codec decision function and
let's also use that decision when patching the payload type of outgoing
RTP packets.

Related: OS#5461
Change-Id: I6c3291f825488e5d8ce136aeb18450156794aeb5
2023-05-22 11:18:45 +02:00
Philipp Maier 0e8310fa6c mgcp_codec: move mgcp_codec_decide down
In a follow up patch we intend to fix mgcp_codec_decide, since the
fixed version of mgcp_codec_decide will use some functions below its
current position let's move it down before fixing it to make reviewing
the changes easier.

Related: OS#5461
Change-Id: I2f2538ff912eae4d80d3b74b766e18c4da94d6b6
2023-05-22 10:24:57 +02:00
Vadim Yanitskiy e3624562dc copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
Change-Id: I0de7650ad119d9a5d2585e2af981c5f1ff8e7058
2023-05-18 17:22:26 +07:00
Oliver Smith d5057ec7b8 debian: set compat level to 10
Related: OS#5958
Change-Id: If00d77a3b83fa7ba000a346f087a6baf1966fdff
2023-04-25 16:48:29 +02:00
Philipp Maier 72761b312b mgcp_vty: add warnings for deprecated config options
We normally warn in case someone uses a deprecated config option

Change-Id: I182f6579930a4e5ed79c8eb87501578ce7e89c15
2023-04-21 12:09:28 +02:00
Oliver Smith a312523be4 Cosmetic: mgcp_client: fix typo
Change-Id: I8e44b4bb853ebac881cb51b9546505874cf8fa45
2023-04-18 16:11:03 +02:00
Neels Hofmeyr 45fe47cea7 mgcp_client: simpler error handling
add_sdp(), add_lco():

- do not msgb_free() within these functions. Just return error, and move
  the msgb_free() to the caller.

- when failing to write to the msgb, directly return error.

Change-Id: I904d56f56053952c2ebbbe5dca744fafa32b333e
2023-04-15 00:56:06 +02:00
Neels Hofmeyr ce356eec1f mgcp_find_section_end(): skip spaces at start of SDP
Change-Id: I015e0347a268b61c0ca45d40754942f87b461c09
2023-04-15 00:10:19 +02:00
Philipp Maier cca1d180f5 mgcp_network: fix apidoc
Change-Id: I358e21e9a9ab33beb07a9adfd4a53145aadb997d
2023-04-13 14:38:31 +02:00
Oliver Smith 833ab4b083 mgcp_client_pool: add mgcp_client_pool_empty()
Check if the private struct mgcp_client_pool has an empty member_list.

Clients that support both the legacy config and the pool config can
use this to properly exit with error if pool members are configured,
but no connection to any of the pool members can be established.

Currently clients can't distinguish a config without pool members and a
config with pool members that is broken and will fall back to using the
defaults of the legacy config in both cases.

Related: OS#5993
Change-Id: I009483ac9dfd6627e414f14d43b89f40ea4644db
2023-04-04 14:27:04 +02:00
Vadim Yanitskiy d358ca14a9 tests: $(BUILT_SOURCES) is not defined, depend on osmo-mgw
Change-Id: I951ce2410614993a855336e6bb408cae1823ef9a
2023-03-31 11:56:02 +00:00
Philipp Maier 3699ef06a4 mgcp_codec: cosmetic: remove line break in api-doc
We usually do not make a final line break on our api-doc strings.

Change-Id: I025bbf36287ef014e294cc18a6435d7e2f9c1bff
2023-03-27 17:50:18 +02:00
Philipp Maier 9dd80ca1f8 mgcp_codec: refactor payload type converstion
in mgcp_codec.c we have a function mgcp_codec_pt_translate that is used
to find the equivalent payload type number on an opposite connection.
This is quite specific and the resulting payload type number may still
belong to a codec that might require some form of conversion.

Lets refactor this so that the function just finds a convertible codec
for a given connection. This also opens up other usecases. The payload
type conversion like we did it before can then be done with a few lines
in mgcp_network.c

Related: OS#5461
Change-Id: I085260a2ca8cfecdb58656b7a046c536189e238d
2023-03-27 17:50:18 +02:00
Philipp Maier 621e8666ff mgcp_codec: fix oa/bwe comparison in mgcp_codec_pt_translate()
The function mgcp_codec_pt_translate is very strict when comparing the
codecs to each other to find a matching payload type number to be used
on the egress side.

This poses a problem when one side uses AMR in bandwith-efficient, while
the other side uses AMR in octet-aligned payload type format. To the pt
translate function the difference in the payload format will appear as
if the codec were different and eventually the payload type number
cannot be translated.

since osmo-mgw offers conversion between the payload type format it
would be no problem to ignore the payload type format when making the
translation decision. The only exception here would be if one side would
announce AMR two times, the first time with octet-aligned and the second
time with bandwith-efficient format. Then we would have to use the
payload type number from the exact match. (and skip any formatconversion)

To archive such an optimized decision we will first go through the codec
lists and perform an exact match. If we don't get a match we go through
the codec lists a second time, but this time we ignore the payload
format.

Change-Id: Ifbd201a2749009a4644a29bd77e1d0fc0c124a9d
Related: OS#5461
2023-03-27 13:12:50 +02:00
Philipp Maier ec967d74f7 Revert "mgcp_codec: do not differentiate between oa and bwe when comparing codec"
This reverts commit e0058b7207. The reason
for this revert is that the solution in the reverted patch does not
cover a situation where the other side announces both payload formats at
the same time.

It could be that the end facing to a transit network announces both
formats under two different payload types. In this case no conversion
would be necessary. Depending on the input format the output would be
send to the transit network under the payload type that matches and no
conversion would happen at all.

This revert re-intruduces the problem that was fixed in the patch
before. Therefore it must be merged together with the follow up patch
(Ifbd201a2749009a4644a29bd77e1d0fc0c124a9d) that contains the proper fix.

Change-Id: I0b2854ef2397f38606fab3425be586a3d0ca27d1
Related: OS#5461
2023-03-22 17:12:44 +01:00
Philipp Maier 3ccf02a300 mgcp_e1: cosmetic: rewrite comment
Change-Id: I405cf464c052a2ab1461c3a8ba96665a3bc30341
2023-03-22 09:58:34 +00:00
Philipp Maier b6795ddfd2 mgcp_e1: rename e1_send to e1_send_ts_frame
The function name e1_send is very generic, lets make clear that one E1
timeslot frame is sent.

Change-Id: I5fba7993860a63149a13d943edbfabc4008680ef
2023-03-22 09:58:34 +00:00
Pau Espin 747fbce4ce mgcp-client: Call osmo_fd_unregister() before closing and changing bfd->fd
Change-Id: I95fbcc21a18cadd2c06608cc39b9fe8e12c8bccf
2023-03-14 11:48:27 +01:00
Vadim Yanitskiy e1da8c9d2c tests: use -no-install libtool flag to avoid ./lt-* scripts
This option should be used for any executables which are used only
for testing, or for generating other files and are consequently never
installed.  By specifying this option, we are telling Libtool that
the executable it links will only ever be executed from where it is
built in the build tree.  Libtool is usually able to considerably
speed up the link process for such executables.

Change-Id: I4bc03bdc1f2de2574558e2ad753e116486993a7f
2023-03-11 04:47:20 +07:00
Vadim Yanitskiy e78221d273 */Makefile.am: libraries shall not be in AM_LDFLAGS
Change-Id: Ie9df855ee09b0761bd617fab58ca26450ac0c754
2023-03-11 04:46:12 +07:00
Pau Espin 6c303664f5 mgcp_network: Unregister osmo_fd before closing fd
Change-Id: Ib937e56be62327f52d9cf03a07d7620080356ee8
2023-03-09 18:34:12 +01:00
Philipp Maier 544016d8e6 mgcp_e1: rewrite comment
Change-Id: I5e6db4e29ae4af1ec790a4e8b201930206cc5828
2023-02-28 18:45:31 +00:00
Philipp Maier 263a1a3e8f mgcp_e1: fix typo
Change-Id: Ida399e58b996ebab74e719ec907984e5aa9ddce4
2023-02-28 18:45:31 +00:00
Philipp Maier 30bdf94102 mgcp_client: fix sourcecode formatting
Change-Id: I55f98379ea48ffddc25186c15e4dd40762e818b6
2023-02-28 14:32:02 +01:00
Oliver Smith 0dbaaadc06 mgcp_client: mgcp_msg_gen: add more error logs
While adding CSD, this failed for me in add_lco without a descriptive
log message, so add more error messages.

Related: OS#4393
Change-Id: I4873a2db95525aab3e13046b645dd8f90e951466
2023-02-27 08:30:08 +00:00
arehbein 671c37b72b Transition to use of 'telnet_init_default'
Related: OS#5809
Change-Id: Icc57c68337d55c6594c1c36e9bf41624d11dab0a
2023-02-25 17:48:58 +01:00
Philipp Maier 2a9ba66922 mgcp_e1: be more frugal withe E1 line resources
At the moment we open an E1 timeslot when needed, but we never
close it even when it is not needed anymore. This may block other
entities from using it. Lets add some logic to detect whether the E1
timeslot is still needed and make sure that it is closed when it is no
longer needed.

Depends: libosmo-abis.git I073cfaba0d5073447842f22665e213135ea3f635
Change-Id: Ie6a32abbc5cd984f6d72a384e3b47c1b82ce7058
Related: OS#5198
2023-02-22 13:03:54 +01:00
Philipp Maier f8abd0ec0c mgcp_e1: fix log output
Its not the E1 timeslot that is updated, its the E1 line instead
(which includes the timeslot)

Change-Id: Ic72cf90ebdba1b5e395089417c6df5b962f8ffc8
2023-02-13 11:57:52 +01:00
Harald Welte 1a7048d69a cosmetic: Fix grammar suggesting reading _the_ user manual
without the *the*, the sentence sounds Russian - and it wasn't even
added by Vadim or Max :P

Change-Id: I95b66c6cc88f545eb738945233bd2e560abf4711
2023-02-10 11:47:56 +00:00
Philipp Maier c70c08cd49 mgcp_endp: cosmetic: remove unnecessary new line
Change-Id: I5892bc2e07ab16356acb4e75c6f9c31f6604d41e
2023-02-09 13:37:34 +01:00
Philipp Maier a8f13286ff mgcp_endp: cosmetic move mgcp_endp_release to the end
It is more logical to move the function mgcp_endp_release to the end
since it is used to release the endpoint when it is no longer needed for
any transmission. (Also the follow up patch requires the function to be
moved)

Change-Id: Id3166a6a817ddb9ce36ff1b375ff8ed3598a00c2
2023-02-09 13:37:03 +01:00
Philipp Maier b9e7569217 mgcp_sdp: cosmetic: remove newline
Change-Id: Ib9a03a75d9bcc4bdbc9d740edbd12c280ec1d933
2023-02-08 10:21:42 +01:00
Philipp Maier 057decfe3a mgcp_sdp: add spec reference
Change-Id: I16cd453aa986cf9efa0716257230aed8739593da
2023-02-08 09:21:12 +00:00
Pau Espin 342a9a9418 Bump version: 1.10.0.117-22f9c-dirty → 1.11.0
Change-Id: I9f1dcbbf2fdc242e8d304ec6b2a8e1e60d010c32
2023-02-07 16:58:42 +01:00
Harald Welte 22f9cf2687 update outdated vty copyright statement
Change-Id: I7af4bdecddda0b43f98855571942219e8ba00b64
2023-02-06 16:07:07 +00:00
Oliver Smith 26d6b2b5ce mgcp_client: add new clearmode codec
Set 120 as payload type, as specified in 3GPP TS 48.103 table 5.4.2.2.1.

Related: OS#4395
Related: https://www.rfc-editor.org/rfc/rfc4040#section-5
Change-Id: I55f9fe241a405935dbedc3947b0a4f4986acd5cb
2023-01-24 18:18:11 +01:00
Oliver Smith 169d50ed4a Fix various typos
Change-Id: Iba7851a5bdb0ce2ce6852a8fa035b72515d7b0a1
2023-01-24 13:23:21 +01:00
Philipp Maier 82dfb505db mgcp_network: improve coment
the comment that explains the AMR frame format check is a bit difficult
to understand, lets rephrase it.

Change-Id: I07f12c03449e1e8eda8bdd3edad6d1007f5ba48d
2023-01-19 14:28:30 +01:00
Max 9c0d105d09 ctrl: take both address and port from vty config
Change-Id: Id053bc02e0a6359e52a0f5e110128d3bb87ed151
2022-12-20 19:00:45 +03:00
Pau Espin ded02cfc29 iuup: Use osmo_amr_ft_valid() API
This is the only user outside libosmo-abis using this define, which
meaning is not clear at all. Let's simply use the related API to clearly
check the FT.

Related: SYS#6161
Change-Id: I40c7ed2e7f6a99a33b467734e33acf3d5befac0d
2022-12-19 13:24:23 +01:00
Pau Espin 0cbed362cd osmux: Use new osmux_xfrm_input API to set name on each link
Depends: libosmo-netif.git Change-Id 8bb688e4827f345416c2a4526ced956a07fcc60b
Change-Id: Id5b7d497be4b3405403d30e40e1a52f13101cf5f
2022-11-21 10:52:17 +01:00
Pau Espin 310e41cdf3 osmux: Rotate over available Osmux CID when allocating a new one
Before this patch, the free CID with the smallest number was always
selected to be used. This caused more or less the same subset of CIDs to
be used all the time, while the CIDs with bigger numbers were mostly
unused.
Let's distribute the use so that all CIDs are used roughly the same.
This has the advantage, among others, that the same CID will not be
re-used immediatelly after being freed if a new call is established.
It is useful to leave the CIDs unused for some time since the other end
peer may know of the call being tear down with some delay.
Hence if a new call is established immediately after the CID was
released, the same CID would be allocated and passed at the peer, which
would then detect that the old call (in its view still active) would
already make use of that remote CID.

Change-Id: I9dfbcc5e4b4c61ce217020e533d68fbcfa6b9f56
Related: SYS#6161
2022-11-15 18:10:28 +01:00
Max a55bfdc436 Add realtime scheduling and set priority in service file
This sets lowest realtime priority which still takes precedence over any non-realtime service.

Related: OS#5687
Change-Id: Ib1705a164b04b876f129a17c4e8353b9ddcc538e
2022-11-14 10:52:06 +00:00
Harald Welte 6ea8d7dac6 Add -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition
... if --enable-werror is used

Change-Id: Ib397f3ad748e17f20b6177ef706af65088571f70
2022-11-03 12:53:20 +01:00
Harald Welte 9befdeb673 Support building with -Werror=strict-prototypes / -Werror=old-style-definition
Unfortunately "-std=c99" is not sufficient to make gcc ignore code that
uses constructs of earlier C standards, which were abandoned in C99.

See https://lwn.net/ml/fedora-devel/Y1kvF35WozzGBpc8@redhat.com/ for
some related discussion.

Change-Id: I98a3c0d5cfda2c4b020652efb4f445f8288342b6
2022-11-03 12:53:20 +01:00
Neels Hofmeyr 5fb814b6b9 AMR->IuUP: log conversion, like for the flipside
For IuUP -> AMR, we log a message like
  Convert IuUP -> AMR OA:...
Do the same for the direction AMR -> IuUP.

Related: SYS#5092
Change-Id: I525685a7dedb6d5d1deecbd026844cbe23193fac
2022-10-28 19:04:00 +00:00
Pau Espin 9ac9cb963d Improve logging on AMR OA<->BWE conversion failure
Change-Id: I7c7dd10650c1d249c723d6781585b343004bd64d
2022-10-26 12:36:48 +02:00
Pau Espin 377d39122d osmux: Make sure RTP AMR feed to osmux is in octet-aligned mode
The Osmux implementation in libosmo-netif expects to work with RTP AMR
in octet-aligned mode. Therefore, if the peer connection received RTP
AMR in bandwidth-efficient mode, we need to convert it to octet-aligned
before feeding the packets to the osmux layer.

Related: SYS#6161
Change-Id: Ifeec44241079f7a31da12745c92bfdc4fb222f3a
2022-10-26 11:56:05 +02:00
Pau Espin d1e94c7ac3 osmux: Rename function and pass msgb directly to it
The new prefix now matches other related functions acting on an osmux
connection.
Pass the msgb to it so simplify new msgb copy.

Change-Id: I8c0121485d0c96f70fe8bcbdb150793d428183ff
2022-10-26 11:56:05 +02:00
Pau Espin 6db3f7f1cb mgw: Rename s/mgcp_send_rtp/mgcp_conn_rtp_dispatch_rtp/
The previous name is misleading since the function is not really sending
stuff over a socket, but rather handling/dispatching the incoming
message internally (and finally later it may be sent over a socket under
some conditions).

Change-Id: Idaf791997b8438a4aede50f614afa0d55ad41faa
2022-10-26 11:55:26 +02:00
Pau Espin 58c0b0a6f7 mgw: rx_rtp(): reorder checks and handlings
First validate the origin of the message, then later the content
of the message and finally execute whatever triggers are necessary.
This way contents from unknown senders are not even parsed or acted
upon, avoiding useless potential harm.

Change-Id: I011a6d7d705768c32a35cec5cd7169725a21a670
2022-10-25 18:23:09 +02:00
Pau Espin cca55247ed Rename and move func checking if amr mode is explicitly configured
The previous naming was quite confusing, since the function is not
really checking whether a conversion is needed, but rather whether the
codec has the AMR RTP mode defined explicitly and hence forced.

The previous naming didn't harm because the amr_oa_bwe_convert also
supports the conversion path OA<->OA and BE<->BE.
Hence nowadays the amr_oa_bwe_convert() function is called always if the
dst conn has its codec with AMR RTP mode explicitly set, no matter if
the src and dst conn have the same mode.

Related: SYS#6161
Change-Id: I8dce3038ebccf5e1e37e2908070a67d66693a96f
2022-10-25 18:23:08 +02:00
Pau Espin d266c374ee mgw: Log unexpected RTP AMR OA-vs-BE payload
Change-Id: Ib5ae82c01153398491b21191a8cec9969337bbbc
2022-10-25 18:23:04 +02:00
Pau Espin d358d71206 cosmetic: Clarify and fix typos in comment
Change-Id: Ibcbe7d85cf7e1912de73d59540f2dea1dfa5d98d
2022-10-25 13:09:36 +02:00
Neels Hofmeyr 5e6661eece IuUP->AMR: log whether converting to AMR OA or BE
Enhance the existing log message
  Convert IuUP -> AMR:...
to
  Convert IuUP -> AMR OA:...
(or BE) to show wether emitting Octet-Aligned or Bandwidth-Efficient.

Related: SYS#5092
Change-Id: I3672d01d2879ae8820176a46454f26a4f5f584de
2022-10-24 21:23:15 +02:00
Neels Hofmeyr c1468ef7a5 IuUP->AMR: do not patch payload type a second time
When converting IuUP to AMR/RTP, bridge_iuup_to_rtp_peer() sets the AMR
side's payload type number and then calls mgcp_send(). In mgcp_send(),
do not attempt to patch the payload type number a second time.

In mgcp_send(), skip patching payload type numbers if the source side is
IuUP. This matches exactly the case of converting IuUP to AMR/RTP.

There already is a check for IuUP, but for the wrong side. Drop that one
and explain in a comment why.

Move the comment about transcoding into the failure branch, where it is
relevant and doesn't clutter the new explanation of payload type
patching conditions.

Related: OS#5720
Related: SYS#5092
Change-Id: I7c722cd959f76bd104ae4941d182c77e5c025867
2022-10-24 21:23:15 +02:00
Neels Hofmeyr 24763eaa73 AMR->IuUP: properly translate Q -> FQC
Fix the reversed logic when composing IuUP.FQC:
AMR.Q == 1 means the frame is good.

Without this fix, all frames on IuUP are marked as bad, and no voice is
heard on the IuUP side.

Related: SYS#5092
Change-Id: I29878dd27af9ba0c9e600324c528b22940cdcc30
2022-10-24 21:23:15 +02:00
Neels Hofmeyr d8c2f31a87 AMR->IuUP: do not crash on AMR data before IuUP Init
When translating AMR to IuUP, when AMR data arrives before the IuUP side
has negotiated an IuUP Initialization, do not crash osmo-mgw, but return
failure and drop the AMR packet.

As soon as IuUP Initialization occured and RFCIs are defined, the AMR
starts to pass through to the IuUP side.

Related: SYS#5092
Change-Id: Id9efb7e523d8d9af988e4bf4f5e925839204f934
2022-10-24 21:23:02 +02:00
Pau Espin 01b812ea76 mgcp_send: Use mgcp_conn_rtp_is_iuup() helper
Change-Id: I6988337f395168b88959876fe3827d8e67b8fc00
2022-10-19 19:18:50 +02:00
Pau Espin 0807771b9e Fix misleading error log
The check is not for an AMR RTP packet. It actually only checks for RTP
header.

Change-Id: I60d7dbfeb2977bcb7102eed19e1c73c9433151a7
2022-10-19 19:06:47 +02:00
Pau Espin 103e50a561 mgcp-client: Fix no 'mgw ' prefix written in old VTY node
Recent commit dropped the 'mgw ' prefix in commands under the "mgw" VTY
node because it was redundant.
However, for old apps still not supporting the 'mgw' node, the commands
are still printed in another node and hence the 'mgw ' prefix must still
be appeneded to them, to avoid breaking backward compatibility.

Related: SYS#5987
Fixes: 8c138fe89c
Change-Id: Ifbfb71f8864b425c1b842cdee241ce279d17a8ba
2022-10-19 17:22:48 +02:00
Pau Espin 3a914e33e9 mgcp-client: Refactor system keeping old users not calling mgcp_client_pool_config_write() working
Move the regular writing logic to a subfunction, and raise the flag only
on one of them.
This simplifies the code paths but not marking stuff true and false over
the same code path.

Change-Id: I070798863f2bdc253df004a63262d4bcd529348f
2022-10-19 17:14:51 +02:00
Pau Espin 2172f402a5 mgcp-client: Fix 'mgw endpoint-range' command dropped from old VTY node
That command was already deprecated when the new commands without the
"mgw" prefix were dropped, so there was no need to create a new command
for it (since it shouldn't be used and it's currently implemented as a
NOOP).

However, the recent patch moved the install line out of the common
function, which meant the command was not available anymore on the old
node, and as a result old config files using it would fail to be parsed.

This commit moves the command back to the common function so that it is
still available for config files using the old node.

Related: SYS#5987
Fixes: 8c138fe89c
Change-Id: I800abcc9869ed097a9d28715269c42552f5aaf29
2022-10-19 16:13:23 +02:00
Pau Espin 8c138fe89c mgcp-client: Add new VTY commands under mgw node without mgw prefix
Previous existing ones are left marked as deprecated, to avoid breakage
with older configs.

Related: SYS#5987
Change-Id: Id55af13d2ecde49d968b9dca6a2f8108a17ec484
2022-10-19 12:47:33 +02:00
Pau Espin 7881f7da0b mgcp-client: Introduce API mgcp_client_pool_config_write
It is not a good idea to use a personal write_config function for the
MGW node, since it's not meant to be a 1st level (top) node.
During write-config the node function will be called at an
implementation specific time (based on the order defined by the app in
the node type enum), so there's no real way to control that the output
of the MGW node will end up under the parent configured in
mgcp_client_pool_vty_init(). As mentioned the order can be tweaked based
on how the node enums are configured, installed, etc. but that's really
a nightmare, so let's better not rely on that.

Therefore, this patch introduces a new API which the users (apps such as
OsmoBSc and OsmoMSC) can use to write the config at the required time
(when writing its own parent node).
A hack is introduced internally to detect older versions of the users
which didn't call this function to still print stuff (up to to the app
if the order is correct).

Related: SYS#5987
Change-Id: I7a620cf47886d8ecab30ce369cf123d98ab842c5
2022-10-18 17:38:44 +02:00
Pau Espin d17a9de79e mgcp-client: Convert users supporting new MGW Pool VTY node during write-config
If the user (app) already supports the MGW Pool VTY command set
(mgcp_client_pool_vty_init() was called), then if single MGW mode is in
use it's because the config file was not updated to use the new MGW Pool
node. Let's convert it to the new format to help in transitioning to the
new VTY command set.

Related: SYS#5987
Change-Id: I2f6658cc66f8bcbd4a60ee2b932bb5dd65888233
2022-10-18 12:44:52 +02:00
Pau Espin 8e74c63e15 mgcp-client: Use random free local port by default
Deprecate mgcp_client_connect2() and all the related messy system where
configured port is incremented until a free port is found.
Let's simply use local_port=0 by default and let the kernel take care of
doing all that.
The mgcp_client_connect2() is actually not being used anywhere outside
of libosmo-mgcp-client, so it's not a real problem deprecating it. We
could poitentially remove it too.

This helps in fixing a regression in recent refactoring commit
f48cd95bbc, which unified calls to
mgcp_client_connect() and in the process dropped the code path which was
setting the retry_n_ports=99.
As a result, since all libosmo-mgcp-client instances set local_port=2727
and addr=NULL by default, using MGW pooling or having several osmo-bsc
or osmo-msc processes would make creating the socket fail.

Change-Id: I5683bcf3b2c4e8058338433f2cd1b143753f6512
2022-10-17 19:22:41 +02:00
Pau Espin 80dd576ad2 mgcp-client: vty: Write deprecation warning using non-mgw nodes
Let's advise all our users to migrate to the new pooled mode, the old
VTY interface should be dropped at some point to avoid making it
overcomplex to configure MGW. There's no need to maintain the same set
of commands under 2 different places.

Change-Id: I95f717a0fcd3c4ca622e3989baa28fac1f7ec1ca
2022-10-17 17:32:31 +02:00
Oliver Smith cb922b646f mgcp_client_pool.h: add missing stdbool include
Fix for:
[   34s] In file included from vty.c:22:
[   34s] /usr/include/osmocom/mgcp_client/mgcp_client_pool.h:18:1: error: unknown type name 'bool'; did you mean '_Bool'?
[   34s]  bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member);

Related: https://obs.osmocom.org/package/live_build_log/osmocom:master/osmo-bsc-nat/CentOS_8/x86_64
Change-Id: Ic3773f49160cbb8c6f53c4f0e17b897017dec98a
2022-10-14 16:42:03 +02:00
Pau Espin 5d8b5b0935 mgcp-client: Introduce APIs to manually select mgcp_client from pool
This will be used by osmo-bsc to implement MGW-pinning for specific BTS.
This is useful for instance to keep all BTS connections targeting the
same MGW in order to make use of Osmux trunking optimizations (AMR
payload of different calls filling same underlaying UDP packet).

Related: SYS#5987
Change-Id: I75ce3e04cd3f6d9cc20d7b4fc0f51083780786c8
2022-10-13 18:47:07 +02:00
Pau Espin 35bd2523e2 mgcp-client: Rearrange internal backpointers
Let's properly store backpointers so that it is easy to access all
relation chain (pool<->pool_member<->mgcp_client).

Related: SYS#5987
Change-Id: I5ec2465075da2e3c8eabca9df60681620a7db499
2022-10-13 18:42:34 +02:00
Pau Espin f45a9b9760 mgcp-client: Rearrange order of structs and APIs in header
Move mgcp_client_pool up in the file as a preparation for next commit,
where pool_member will have a pointer to the pool added.
The related APIs on the object are also moved up in the file.

Change-Id: I9ff9c6e1e722690835c5d59b1fa87fb7d9e3120c
2022-10-13 18:42:34 +02:00
Pau Espin f48cd95bbc mgcp-client: Refactor reinit of mgcp clients
The second parameter of mgcp_client_pool_member_reinit_client() will be
removed in a follow-up patch after further refactoring.

Related: SYS#5987
Change-Id: I0ab59278629c0e7e7c62a68b54b4235df58a5d77
2022-10-13 18:42:34 +02:00
Pau Espin 7558c20973 mgcp-client: Move internal API acting on mgcp_client_pool to the correct file section
Change-Id: Ia0cccb783918eea72c4a77861dac4b60ad369b95
2022-10-13 18:42:31 +02:00
Pau Espin 6ddc79b6ed mgcp-client: Create alloc() and free() internal APIs for mgcp_client_pool_member
Related: SYS#5987
Change-Id: Ibd2bf976885c777fe944652af6fe6eb0f1820468
2022-10-13 18:39:10 +02:00
Pau Espin ff5921573a mgcp-client: Move & rename helper function outside of vty code
This way acessing of the list is kept inside the same file.

Related: SYS#5987
Change-Id: I6b26c2de4064d9c28f92452178fff170fb295576
2022-10-13 18:38:57 +02:00
Pau Espin d4ad77d326 mgcp-client: Rename internal field in mgcp_client_pool
Having a list named pool inside an object already named pool is quite
confusing. Let's rename the field so that it makes sense immediatelly
when looking at it.

Change-Id: I4062af6b619317c48223ad4577eaaad05d4eec9d
2022-10-13 18:38:43 +02:00
Pau Espin a6917fecb1 mgcp-client: Fix typo in internal function name
Change-Id: I073038f1d6c68bc8546763a2c8c791fb5a021630
2022-10-13 18:38:43 +02:00
Pau Espin 58877f04e3 mgcp-client: Avoid double iteration picking client from pool
Change-Id: I99e8be878452c5d8e2183239fbdd499dcf507e90
2022-10-13 18:38:43 +02:00
Pau Espin d4707e2185 mgcp-client: pool: Improve documentation of some internal fields
Change-Id: Ic9823ab0a3f101549452a7c7a0b1b5b21aaa837c
2022-10-13 18:38:28 +02:00
Pau Espin f633302c73 cosmetic: Fix typo in comment
Change-Id: I6b0fcd7ee13e0e712d47741258f83a6f98503e27
2022-10-13 13:20:10 +02:00
Pau Espin 36413c011f osmux: Clean up mgcp_config osmux fields
* Move all of them to an anonymous "osmux" substruct to have them all
  together.
* Rename some fields to clarify its meanings, similar to what
  was already done in osmo-bts.
* Move the port VTY config next to the ip address ones.

Change-Id: Icc1c1ce3ab3a8beb941cda7c112485a0db882a90
2022-10-13 12:36:44 +02:00
Pau Espin 833281dcb9 osmux: Introduce osmux peer-behind-nat (on|off) and rework conn activation
Change-Id: I7654ddf51d197a4107e55f4e406053b2e4a02f83
2022-10-12 17:13:04 +02:00
Pau Espin 07bbd766c5 mgcp_conn_dump(): Separate dump for osmux and iuup connections
Change-Id: Iec1f2d61e4eb14a8a3c634e2642063bea6000c04
2022-10-06 18:49:31 +02:00
Pau Espin c7c8e649f1 osmux: Erase references to bsc-nat
Change-Id: I21ab7134de577278175132fc78431a249de76731
2022-10-06 18:24:24 +02:00
Pau Espin c0e1b1aec5 osmux: Move setting OSMUX_STATE_DISABLED to initializer function
Change-Id: I7c184b17075fc3e71f1f66775e16b56a5ae91b62
2022-10-06 16:21:37 +02:00
Pau Espin 9735d21b9d osmux: Define osmux_dummy cfg as boolean
Change-Id: Ifc3384f9871ddfdbd6282c6e03bf6a2dd8a09a9e
2022-10-06 16:19:37 +02:00
Pau Espin 705565d745 osmux: Make conn_osmux_{allocate,release}_local_cid() APIs static
They are used internally in the mgcp_osmux.c file.

Change-Id: If7c83f98a94818090173a8be9d3bff9818b2ead1
2022-10-06 14:37:33 +02:00
Pau Espin 63088e0e61 osmux: Set conn->type during osmux_init_conn()
It's basically the same code path (one function calls the other), but it
makes a lot more sense to have it in the init function itself.

Change-Id: I63d7dfff451870f708f7cb710d66f4ec786c27d7
2022-10-06 14:33:00 +02:00
Pau Espin 887c93e198 osmux: Simplify and constify param passing
Parameters passed to several functions are rearrange to make code
simplier to follow, as well as dependencies between the different
functions. As a result, some parameters can be marked as const while
doing the change.

Change-Id: Idebd4a66630c16548f557632a54d6b7e1b3906fd
2022-10-06 14:20:52 +02:00
Pau Espin 2ca48823b1 Check once if remote addr is available when sending dummy packet
It's checked in all code paths being called, which makes sense since
it's impossible to send anything if the remote address is not set. Let's
check it once in a unique to simplify the code.

Change-Id: I224dbfeda17c364b85166268e2ac1e019a87edb6
2022-10-06 13:10:51 +02:00
Pau Espin e7de137b29 osmux: cosmetic: Fix indentation
Change-Id: Ic7087e7907c1078d134d7d9b11ed6a6bf1941308
2022-10-06 13:10:51 +02:00
Pau Espin c1ad7fd92b osmux: Drop unused role parameter
Change-Id: I39c2852bb60aaba4f85188c55f157e4abec47a0e
2022-10-06 13:10:51 +02:00
Pau Espin 3779919551 osmux: Add square brackets around IPv6 address to distinguish port in log line
Change-Id: I811ac5b4386f06c224f29f44f10dce4008fa7a4f
2022-10-06 13:10:41 +02:00
Pau Espin 8e0d101997 osmux: Change couple log lines to OSMUX category
Change-Id: Ia04d08099f0b1e81707699d872779108f22522bf
2022-10-06 10:31:19 +02:00
Pau Espin 654b68d602 Clean up local var pointers in mgcp_get_local_addr()
Change-Id: I12f1d38b70f97426318e948cd80f9efc2592d54b
2022-10-05 10:37:49 +02:00
Pau Espin 70c03f5e44 Add Osmux IPv6 support
A new osmux bind-ip-v6 VTY command is added, similar to what's already
available for RTP. This way the user can decide whether to support IPv4,
IPv6 or both types of IP versions.

Both IP sockets are by default disabled, and must be explicitly enabled
by setting the bind-ip in the VTY configuration.

Change-Id: I446cd7da217e9f4a74995d7784ae55dcc60a29b7
2022-10-05 10:32:42 +02:00
Pau Espin e56ec4a146 osmux: Use available API to check if remote end is known
Since recently the port is guaranteed to be placed at
CRCX/MDCX in the sockaddr, hence we can check it using the API instead
of checking manually only for the address part.

In osmux_send_dummy() we actually fix a bug where an in_addr was being
compared against a struct sockaddr.

Change-Id: I736e7f4c51e577d8eb0b96bc776f984f928b6d27
2022-10-04 14:44:48 +02:00
Pau Espin 5ffd127384 Get rid of separate rtp_port field
Let's use the port part of the struct mgcp_rtp_end->addr field instead
of keeping it separate. This makes it easier passing around and
using/checking the RTP remote address + port, and avoids confusion
having to check stuff outside of the address, with its port part
potentially unset.

Change-Id: I294eb5d85fae79bf62d36eb9e818426e187d442c
2022-10-04 14:44:44 +02:00
Pau Espin 051eb4d563 Use new libosmocore API osmo_sockaddr_is_any()
Depends: libosmocore.git Change-Id I2810a889fc14052d0e0be6a2b500ad4e5088ffa9
Change-Id: Ib2ba708bdc834359de8a7cce302e2dc1f8c64954
2022-10-04 12:51:15 +02:00
Pau Espin e740665cab Fix regression in detection of legacy dummy packets
A commit refactored this code around one year ago, which broke osmux
detection of dummy messages. This commit partially reverts the earlier
commit and rearranges the existing code to make it more robust.

Fixes: b3d14eb552
Change-Id: Iedf085932c45af5d13e04e56a4cd1082de81d869
2022-10-04 12:16:15 +02:00
Pau Espin abb9d47c90 send_dummy: Use proper condition to test if conn is osmux
Change-Id: I493e7afaf8e6edb5bf975b3097fab49986d064cf
2022-10-04 12:10:10 +02:00
Pau Espin 2dd2b33030 osmux: Log remote address upon rx of osmux pkt
Change-Id: Ie2a375711c7c0989161ae651ea6f0695799e2670
2022-10-03 18:10:55 +02:00
Pau Espin 69f15e4e95 osmux: Fill in from_addr in struct osmo_rtp_msg_ctx
Change-Id: I9783978e295b964ca914f8d90d73effa8785f10d
2022-10-03 18:04:07 +02:00
Pau Espin b0ea7976bf osmux: Match remote address in osmux_conn_lookup()
Depends: libosmo-netif.git I95433b18802f73fa70e758f4aa02128eee940d88
Change-Id: Ia717efa5f68e9412b86ef44a4c42a0715ff0e469
2022-10-03 18:04:03 +02:00
Pau Espin 9d939b6d5d osmux: Unify rtp_conn osmux type into a single type
There's no need to separate between BSS and CN side nowadays, the
different types are used nowehere. This separation dates from
osmo-bsc-nat code days.

Change-Id: I65effeddf033eb1955553e8d659c593b4e67f7bc
2022-10-03 17:16:16 +02:00
Pau Espin de805b6772 osmux: cleanup misleading code calling rtp_bridge_cb
Documentation of rtp_bridge_cb was outdated, and caused confusion when
adding initial osmux support. Let's clear all the mess.

Change-Id: I42d1f2e2919eae3b1555ca4929e571855960792e
2022-10-03 17:16:16 +02:00
Pau Espin cb25a0aa48 osmux: Drop logging of osmux internal counters
This way we have no more access to internal osmux structures.
Moreover, we already have similar information in our rate_ctrs, so
there's no need to print those there.

Change-Id: I853e118f843070ea29b19e1b0fe56b52f267437a
2022-10-03 15:57:09 +02:00
Pau Espin 72eff2cf6a Allocate struct osmux_in_handle through new libosmo-netif APIs
Change-Id: I013c99d1f915279684ce278648e9c69e39b94266
Depends: libosmo-netif.git I752ab031f935f04731bb1a354333f1682a1aa5bd
2022-10-03 15:57:09 +02:00
Pau Espin 1c5fcb0791 osmux: Keep decoding osmux pkt if a batch contains an unknown CID
Receiving an unknown CID (for which we don't have an active conn) is
totally expected at the end of the call, were some messages from the
peer may be send for a while, specially if latency is high.
If this occurs, we simply drop that batch and keep decoding the osmux
message in order to keep delivering content to other circuits.

Related: SYS#5987
Change-Id: I315949853bdcc07bef15d2e8579029cc72c427cf
2022-09-29 16:02:10 +02:00
Pau Espin 48fb176fbc osmux: Lower log level when osmux batch received for unknown CID
This is actually quite common, since our peer may be sending some osmux
packets to us a while after we have closed the conn on our side,
specially if latency is high in the network (eg satellite links).

Related: SYS#5987
Change-Id: Idffd91b68d1f98ac906d78e7fbc0e6eaa1962f9e
2022-09-29 16:02:03 +02:00
Pau Espin 4d090d85f4 osmux: Drop unneeded comment block
Change-Id: Iae04a7c4b496492ce5b363a56fdd871b210012ed
2022-09-29 16:02:03 +02:00
Pau Espin 81c5847434 cosmetic: osmux: Drop extra empty line
Change-Id: I679a62c49290dfeb9e2c63890daa67002bfe3339
2022-09-27 13:45:07 +02:00
Pau Espin d48a811f6c osmux: Allocate rate counters during initialization of osmux conn
Let's not delay allocation of rate counters until the osmux conn is
fully enabled, since we may want to count stuff before that point in
time.

Fixes crash accessing null conn->osmux.ctrg in
MGCP_Test.TC_two_crcx_and_rtp_osmux_bidir.

Change-Id: Ic31d3567f4d24e628f8983d8362e5c7c2f66ec02
2022-09-27 13:45:04 +02:00
Pau Espin b7f52b414e osmux: Rename field s/init/initialized
Change-Id: Ic48de396b1d8a0774611b4a1c2248ba79bdaf044
2022-09-25 00:14:03 +02:00
Pau Espin ea7aaf2eca vty: show per-connection Osmux VTY stats
Related: SYS#5987
Change-Id: Ieab6dcbd195c8e01a73a2a832bce78ee015ae1c3
2022-09-25 00:06:00 +02:00
Pau Espin 432ee9d150 osmux: Improve per-conn tx rate counters
Change-Id: If030f5d921bdfcfcd00b015b4a9e48bb3b04e721
2022-09-25 00:06:00 +02:00
Pau Espin b4a59475d2 osmux: Fix incorrect rate_ctr_group used in mgcp_osmux.c
During development of the counters, they were first added to the same
RTP counter group and later on split, but some places still used the
RTP related functions to increment the counters.
Let's add a seaprate set of helpers to update the correct osmux counter
group.

Fixes: 582c2bf7b0

Change-Id: Ia2e5601c7d476b79afd95032dbc019517f8209af
2022-09-25 00:05:49 +02:00
Pau Espin 2177919edb osmux: Support local CID != remote CID
So far the implementation only allowed the remote CID being the same as
the one dynamically implemented internally.

Related: SYS#6138
Change-Id: I6b03eabc0305580c9788c529bec7dda9044a008f
2022-09-23 16:55:46 +02:00
Pau Espin 87f114a1d0 Use bool type instead of int in config field
Change-Id: Ie2624a6a6848c3c88deb39760317860d8074acb9
2022-09-23 15:48:43 +02:00
Pau Espin 928a20b540 osmux: Rename field osmux usage policy and define it with proper type
Change-Id: I7f41a443f488b75df792597ec3cec8f7e97a7411
2022-09-23 15:48:20 +02:00
Pau Espin b9022497d8 cosmetic: osmux: Fix formatting of if-else brackets
Change-Id: I748d41cd110841ef121f70560e70c07de56aa9da
2022-09-22 21:47:23 +02:00
Pau Espin 582c2bf7b0 osmux: Add connection and global rate counters
Change-Id: I200a4aa3908cac0ec729d980a66f3df7f55e4da7
2022-09-22 21:44:46 +02:00
Pau Espin 663ba5c41f Use 'static const' instead of 'const static' everywhere
As requested by the linter on a new patch being submitted.

Change-Id: I692624da6a1c7f59ac2989509f074cb52d089907
2022-09-22 21:43:45 +02:00
Pau Espin daf5bcea99 mgcp_conn: rename field s/rate_ctr_group/ctrg/g
This makes all lines operating on rate groups way shorter. The "ctrg"
naming is already used in tons of places in osmocom code.

Change-Id: I745eddbf66e7d811bb73c8ae9bb54ea93073c71b
2022-09-22 21:21:12 +02:00
Pau Espin a29f155301 Fix typo in ratectr description
Change-Id: I2ac8581f13814f320767ba188ef7cf3286fa443d
2022-09-22 21:21:12 +02:00
Pau Espin 941e317c97 osmux: Get rid of static NULL talloc context
struct osmux_handle, which is shared by several rtp_conn, is
attached to the trunk object, since the socket also attaches to it.

Related: SYS#5987
Change-Id: If4980424cdb8e3dc26a23e9ee419c0a38912f38f
2022-09-22 21:21:11 +02:00
Pau Espin 3fbf035923 osmux: Clean up helper macro osmux_chunk_length()
Change-Id: I482d8c5be08610788c2ed98e3b87ae4184075e8d
2022-09-22 19:01:33 +02:00
Pau Espin 21bcf6abd8 osmux: Attach osmux to virtual trunk
It makes sense to handle one osmux socket per trunk. Since we only so
far operate one RTP/osmux trunk, let's stick to that only one for
simplicity.

Change-Id: I173266c4058d5db9479d773d7dec1304bc8f1f99
2022-09-22 18:23:05 +02:00
Pau Espin 26e810d8a9 cosmetic: mgcp_conn.h: fix indentation whitespace
Change-Id: I6b12b589d1b0a8f95a8283aac8ba0ad22026b43f
2022-09-22 17:16:20 +02:00
Pau Espin 4d1a52d463 osmux: Log sendto() error
Change-Id: Icd91b725ac6b5ef2b9307cc99d67d60efdfa32cb
2022-09-22 17:11:57 +02:00
Pau Espin 83df47cd7a cosmetic: vty: Fix indentation whitespace
Change-Id: I2f42707f815ca58690d86a956118b6661c59fda3
2022-09-21 20:07:59 +02:00
Pau Espin c5bbb78ffe osmux: set log level of expected code path to INFO
Change-Id: I5caf850b7bb19c66716139512f16568d4f8459eb
2022-09-19 16:46:02 +02:00
Harald Welte e0e5a772f4 Make osmo_mgcpc_ep_fsm_pre_term() static
This is a call-back for FSM pre-termination and it should not
be publicly exported.

Change-Id: Iaa44906baa201571f6bc3192bd3caacb272fec0b
2022-09-16 10:37:22 +00:00
Pau Espin e39ae87ea3 osmux: don't store conn ptr inside shared osmux_handle
The struct osmux_handle is a shared structure which goes together 1-1
with libosmo-netif's struct osmux_in_handle, which is common to all CIDs
to be muxed together. Hence, it makes no sense to store a specific conn
object to it, since it actually manages several of them. Hence, all the
conn specific stuff must be handled beforehand, not at osmux deliver
time.

Related: SYS#5987
Change-Id: Ie739b556e9eb7d3133e798831a09728c7cc87135
2022-09-09 18:13:45 +02:00
Pau Espin 8b0a614e43 osmux: Fix memleak on error code path
Change-Id: Ib84f78a53293799b925b645156513e129c32c705
2022-09-09 18:13:45 +02:00
Pau Espin c61664c2b2 osmux: Log refcounting of osmux_handle_list
Change-Id: Ia873e3021078976748762baaf406865149b2a090
2022-09-09 18:13:45 +02:00
Pau Espin 15e7e4f37d osmux: Use osmo_sockaddr wherever possible
This cleans up all the code, and makes it a lot easier to add IPv6 support
later on (mostly only a matter of adding an IPv6 bind address in VTY).
Similar changes were done to the Osmux code being added to osmo-bts.

Related: SYS#5987
Change-Id: I5a100fc654f88d29b2bcd85889a5a92aef3d576d
2022-09-09 18:13:40 +02:00
Pau Espin ab8371c6d7 osmux: Use better name for function which may allocate a new struct
Change-Id: I867e3f74775d97749a78c2f198452b1f2916492f
2022-09-09 16:40:57 +02:00
Pau Espin 8c777990a5 cosmetic: main: Properly format log_info_cat
Change-Id: If130d1debc1977b7f8d82f515861df2c513d55bb
2022-09-09 16:21:22 +02:00
Pau Espin ec5e85b84e Add Osmux log category
Change-Id: Ia75de7965c39f3f84164a25584d901dbdd43f10f
2022-09-09 16:04:19 +02:00
Pau Espin ab4e8663e1 mgw: Use X-Osmux string define
Change-Id: Ib437c0a6b414f1d8f08fdb90e88a61e9aed2780d
2022-09-08 15:15:37 +02:00
Pau Espin a4f783b51b cosmetic: osmux: Fix wrong indentation
Change-Id: I7675d17bb3fc827e05c197104be29eadd8ddf3f2
2022-09-07 20:04:20 +02:00
Pau Espin 479cf76e55 mgw: Fix osmux conn local IP selection
The local Osmux IP address (cfg->osmux_addr) was never applied in
generated MGCP messages. Instead, the RTP one was written into the
MGCP message.

Related: SYS#5987
Change-Id: I305f2501221e86d1eb0140446c03f36698f3194a
2022-09-07 19:42:59 +02:00
Pau Espin 33639166a2 osmux: Use new osmux APIs to let libosmo-netif alloc struct osmux_out_handle
This way we don't need to worry about implementation details (struct
size) in the future in case they change (they will).

Related: OS#5987
Requires: libosmo-netif.git Change-Id Ie8df581f375c9a183a7af60b431561bda82f6e34
Change-Id: I2d0d8c152b8f1234ddfcd74d6cb04b1818b41510
2022-09-07 08:29:35 +00:00
Max 709aad28ed Set working directory in systemd service file
By default systemd will execute service with root directory (or home directory for user instance) which might result in
attempts to create files in unexpected place. Let's set it to 'osmocom' subdir of state directory (/var/lib for system instance) instead.

Related: OS#4821
Change-Id: I7696cd92953787591e7b4777ee79c2671326b972
2022-08-30 19:45:32 +07:00
Pau Espin 5892b2e4db Use Osmux default port define from libosmo-netif
Change-Id: Ibf5fbec591307232be98cad914c0df4b2993db15
Requires: libosmo-netif.git Ibfd058bceeeaa1384a00d8fcd6d6268b445e19bd
2022-08-12 14:08:34 +02:00
Pau Espin f1f73b8e1d mgcp_osmux: Drop duplicated conn_osmux_release_cid() in code path
Change-Id: I65d9978ee93ef9b1a2e48a92617ae8de3e131a93
2022-08-12 14:06:50 +02:00
Philipp Maier 0fdc08c551 mgcp_e1: fix apidoc
Change-Id: Iaccb9b67e72d113483160e61f660ea9c4c2f53a5
2022-08-10 14:13:41 +02:00
Vadim Yanitskiy 5be57da075 libosmo-mgcp-client: add -no-undefined to *_la_LDFLAGS
Make sure that there is no undefined references in shared libraries.

Change-Id: If6aa8016026beb8bad735987f3ab6e0d114b86a5
2022-08-04 05:35:43 +07:00
Pau Espin 147184eb06 mgcp-client: Remove impossible code path
read(4096 - 128) cannot return > 4096 - 128.

Change-Id: I352c3b28c8de6482277cb84cd0f51893494dc929
2022-06-29 18:45:02 +02:00
68 changed files with 4176 additions and 2327 deletions

View File

@ -24,7 +24,7 @@ pkgconfig_DATA = \
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

@ -24,3 +24,20 @@
# 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 deprecate public API New code should no longer use codecs[], instead use ptmap[].codec. There
is backwards compat code that moves codecs[] entries, if any, over to
ptmap[], so callers may migrate at own leisure.
osmo-mgw remove cfg Remove VTY config item 'sdp audio fmtp-extra' (see OS#6313)
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,15 +44,16 @@ AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DLSYM)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.7.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.7.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.7.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.7.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.2.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.3.0)
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.3.0)
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)
CFLAGS="$CFLAGS -pthread"
CFLAGS="$CFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
CPPFLAGS="$CPPFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
LDFLAGS="$LDFLAGS -pthread"
AC_ARG_ENABLE(sanitize,
@ -79,6 +80,7 @@ AC_ARG_ENABLE(werror,
if test x"$werror" = x"yes"
then
WERROR_FLAGS="-Werror"
WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
CFLAGS="$CFLAGS $WERROR_FLAGS"
@ -202,5 +204,4 @@ AC_OUTPUT(
doc/manuals/Makefile
contrib/Makefile
contrib/systemd/Makefile
contrib/osmo-mgw.spec
Makefile)

View File

@ -1,137 +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.2.0
BuildRequires: pkgconfig(libosmocore) >= 1.7.0
BuildRequires: pkgconfig(libosmoctrl) >= 1.7.0
BuildRequires: pkgconfig(libosmogsm) >= 1.7.0
BuildRequires: pkgconfig(libosmovty) >= 1.7.0
BuildRequires: pkgconfig(libosmocoding) >= 1.7.0
BuildRequires: pkgconfig(libosmoabis) >= 1.3.0
BuildRequires: pkgconfig(libosmotrau) >= 1.3.0
%{?systemd_requires}
%description
OsmoMGW is Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks.
%package -n libosmo-mgcp-client9
Summary: Osmocom's Media Gateway Control Protocol client library
Group: System/Libraries
%description -n libosmo-mgcp-client9
Osmocom's Media Gateway Control Protocol client library.
%package -n libosmo-mgcp-client-devel
Summary: Development files for Osmocom's Media Gateway Control Protocol client library
Group: Development/Libraries/C and C++
Requires: libosmo-mgcp-client9 = %{version}
%description -n libosmo-mgcp-client-devel
Osmocom's Media Gateway Control Protocol client librarary.
This subpackage contains libraries and header files for developing
applications that want to make use of libosmo-mgcp-client.
%package -n libosmo-mgcp-devel
Summary: Development files for Osmocom's Media Gateway server library
Group: Development/Libraries/C and C++
%description -n libosmo-mgcp-devel
Osmocom's Media Gateway Control Protocol server library.
This subpackage contains libraries and header files for developing
applications that want to make use of libosmo-mgcp.
%prep
%setup -q
%build
echo "%{version}" >.tarball-version
autoreconf -fi
%configure \
--disable-static \
--docdir=%{_docdir}/%{name} \
--with-systemdsystemunitdir=%{_unitdir}
make %{?_smp_mflags}
%install
%make_install
find %{buildroot} -type f -name "*.la" -delete -print
%check
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%post -n libosmo-mgcp-client9 -p /sbin/ldconfig
%postun -n libosmo-mgcp-client9 -p /sbin/ldconfig
%if 0%{?suse_version}
%preun
%service_del_preun osmo-mgw.service
%postun
%service_del_postun osmo-mgw.service
%pre
%service_add_pre osmo-mgw.service
%post
%service_add_post osmo-mgw.service
%endif
%files
%license COPYING
%doc AUTHORS README
%dir %{_docdir}/%{name}/examples
%dir %{_docdir}/%{name}/examples/osmo-mgw
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw.cfg
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw-abis_e1.cfg
%{_bindir}/osmo-mgw
%{_unitdir}/osmo-mgw.service
%dir %{_sysconfdir}/osmocom
%config(noreplace) %{_sysconfdir}/osmocom/osmo-mgw.cfg
%files -n libosmo-mgcp-client9
%{_libdir}/libosmo-mgcp-client.so.9*
%files -n libosmo-mgcp-client-devel
%{_libdir}/libosmo-mgcp-client.so
%{_libdir}/pkgconfig/libosmo-mgcp-client.pc
%dir %{_includedir}/osmocom
%dir %{_includedir}/osmocom/mgcp_client
%{_includedir}/osmocom/mgcp_client/*.h
%files -n libosmo-mgcp-devel
%dir %{_includedir}/osmocom
%dir %{_includedir}/osmocom/mgcp
%{_includedir}/osmocom/mgcp/*.h
%changelog

View File

@ -1,11 +1,24 @@
[Unit]
Description=Osmocom Media Gateway (MGW)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
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):
CPUSchedulingPriority=1
# See sched(7) for further details on real-time policies and priorities
[Install]
WantedBy=multi-user.target

215
debian/changelog vendored
View File

@ -1,3 +1,218 @@
osmo-mgw (1.12.1) unstable; urgency=medium
[ Pau Espin Pedrol ]
* mgcp-client: Fix missing include in mgcp_client_pool.h
* mgcp-client: Introduce API osmo_mgcpc_ep_local_name()
* mgw: Configure IuUP if codec set during MDCX
-- Oliver Smith <osmith@sysmocom.de> Thu, 28 Sep 2023 15:58:17 +0200
osmo-mgw (1.12.0) unstable; urgency=medium
[ Philipp Maier ]
* mgcp_sdp: add spec reference
* mgcp_sdp: cosmetic: remove newline
* mgcp_endp: cosmetic move mgcp_endp_release to the end
* mgcp_endp: cosmetic: remove unnecessary new line
* mgcp_e1: fix log output
* mgcp_e1: be more frugal withe E1 line resources
* mgcp_client: fix sourcecode formatting
* mgcp_e1: fix typo
* mgcp_e1: rewrite comment
* mgcp_e1: rename e1_send to e1_send_ts_frame
* mgcp_e1: cosmetic: rewrite comment
* Revert "mgcp_codec: do not differentiate between oa and bwe when comparing codec"
* mgcp_codec: fix oa/bwe comparison in mgcp_codec_pt_translate()
* mgcp_codec: refactor payload type converstion
* mgcp_codec: cosmetic: remove line break in api-doc
* mgcp_network: fix apidoc
* mgcp_vty: add warnings for deprecated config options
* mgcp_codec: move mgcp_codec_decide down
* mgcp_codec: fix codec decision
* mgcp_network: do not deliver RTP packets with unpatched PT
* mgcp_codec: be sensitive about IuFP when checking codecs
* mgcp_client.h: also add spec ref to the other 3gpp defined payload types
[ Harald Welte ]
* cosmetic: Fix grammar suggesting reading _the_ user manual
[ arehbein ]
* Transition to use of 'telnet_init_default'
[ Oliver Smith ]
* mgcp_client: mgcp_msg_gen: add more error logs
* mgcp_client_pool: add mgcp_client_pool_empty()
* Cosmetic: mgcp_client: fix typo
* debian: set compat level to 10
* systemd: depend on networking-online.target
* Cosmetic: fix a typo
* mgcp_client: check rc of map_str_to_codec
[ Pau Espin Pedrol ]
* mgcp_network: Unregister osmo_fd before closing fd
* mgcp-client: Call osmo_fd_unregister() before closing and changing bfd->fd
* mgcp-client: Drop unused struct mgcp_client field
* mgcp_client: Introduce mgcp_client_conf_alloc(), deprecate mgcp_client_conf_init()
* mgcp-client: Move some static functions further above
* mgw: Allow auditing speciall 'null' endpoint
* mgcp-client: Add keepalive feature
* mgcp_client: pool: Only pick clients with an MGCP link considered to be UP
* mgcp-client: Always mark client as UP if keepalive request-interval disabled
* mgcp-client: Mark client as UP when keepalive request-interval/timeout is disabled through VTY
[ Vadim Yanitskiy ]
* */Makefile.am: libraries shall not be in AM_LDFLAGS
* tests: use -no-install libtool flag to avoid ./lt-* scripts
* tests: $(BUILT_SOURCES) is not defined, depend on osmo-mgw
* copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
[ Neels Hofmeyr ]
* mgcp_find_section_end(): skip spaces at start of SDP
* mgcp_client: simpler error handling
* mgcp: fix "L: a:" header parsing: heed ";" separator
* mgcp_client: tweak extract_codec_name() implementation
[ Andreas Eversberg ]
* ASCI: Add new mode for voice group/broadcast call
* ASCI: Support conference briding with 1..n connections
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 14:48:51 +0200
osmo-mgw (1.11.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* mgcp-client: Remove impossible code path
* mgcp_osmux: Drop duplicated conn_osmux_release_cid() in code path
* Use Osmux default port define from libosmo-netif
* osmux: Use new osmux APIs to let libosmo-netif alloc struct osmux_out_handle
* mgw: Fix osmux conn local IP selection
* cosmetic: osmux: Fix wrong indentation
* mgw: Use X-Osmux string define
* Add Osmux log category
* cosmetic: main: Properly format log_info_cat
* osmux: Use better name for function which may allocate a new struct
* osmux: Use osmo_sockaddr wherever possible
* osmux: Log refcounting of osmux_handle_list
* osmux: Fix memleak on error code path
* osmux: don't store conn ptr inside shared osmux_handle
* osmux: set log level of expected code path to INFO
* cosmetic: vty: Fix indentation whitespace
* osmux: Log sendto() error
* cosmetic: mgcp_conn.h: fix indentation whitespace
* osmux: Attach osmux to virtual trunk
* osmux: Clean up helper macro osmux_chunk_length()
* osmux: Get rid of static NULL talloc context
* Fix typo in ratectr description
* mgcp_conn: rename field s/rate_ctr_group/ctrg/g
* Use 'static const' instead of 'const static' everywhere
* osmux: Add connection and global rate counters
* cosmetic: osmux: Fix formatting of if-else brackets
* osmux: Rename field osmux usage policy and define it with proper type
* Use bool type instead of int in config field
* osmux: Support local CID != remote CID
* osmux: Fix incorrect rate_ctr_group used in mgcp_osmux.c
* osmux: Improve per-conn tx rate counters
* vty: show per-connection Osmux VTY stats
* osmux: Rename field s/init/initialized
* osmux: Allocate rate counters during initialization of osmux conn
* cosmetic: osmux: Drop extra empty line
* osmux: Drop unneeded comment block
* osmux: Lower log level when osmux batch received for unknown CID
* osmux: Keep decoding osmux pkt if a batch contains an unknown CID
* Allocate struct osmux_in_handle through new libosmo-netif APIs
* osmux: Drop logging of osmux internal counters
* osmux: cleanup misleading code calling rtp_bridge_cb
* osmux: Unify rtp_conn osmux type into a single type
* osmux: Match remote address in osmux_conn_lookup()
* osmux: Fill in from_addr in struct osmo_rtp_msg_ctx
* osmux: Log remote address upon rx of osmux pkt
* send_dummy: Use proper condition to test if conn is osmux
* Fix regression in detection of legacy dummy packets
* Use new libosmocore API osmo_sockaddr_is_any()
* Get rid of separate rtp_port field
* osmux: Use available API to check if remote end is known
* Add Osmux IPv6 support
* Clean up local var pointers in mgcp_get_local_addr()
* osmux: Change couple log lines to OSMUX category
* osmux: Add square brackets around IPv6 address to distinguish port in log line
* osmux: Drop unused role parameter
* osmux: cosmetic: Fix indentation
* Check once if remote addr is available when sending dummy packet
* osmux: Simplify and constify param passing
* osmux: Set conn->type during osmux_init_conn()
* osmux: Make conn_osmux_{allocate,release}_local_cid() APIs static
* osmux: Define osmux_dummy cfg as boolean
* osmux: Move setting OSMUX_STATE_DISABLED to initializer function
* osmux: Erase references to bsc-nat
* mgcp_conn_dump(): Separate dump for osmux and iuup connections
* osmux: Introduce osmux peer-behind-nat (on|off) and rework conn activation
* osmux: Clean up mgcp_config osmux fields
* cosmetic: Fix typo in comment
* mgcp-client: pool: Improve documentation of some internal fields
* mgcp-client: Avoid double iteration picking client from pool
* mgcp-client: Fix typo in internal function name
* mgcp-client: Rename internal field in mgcp_client_pool
* mgcp-client: Move & rename helper function outside of vty code
* mgcp-client: Create alloc() and free() internal APIs for mgcp_client_pool_member
* mgcp-client: Move internal API acting on mgcp_client_pool to the correct file section
* mgcp-client: Refactor reinit of mgcp clients
* mgcp-client: Rearrange order of structs and APIs in header
* mgcp-client: Rearrange internal backpointers
* mgcp-client: Introduce APIs to manually select mgcp_client from pool
* mgcp-client: vty: Write deprecation warning using non-mgw nodes
* mgcp-client: Use random free local port by default
* mgcp-client: Convert users supporting new MGW Pool VTY node during write-config
* mgcp-client: Introduce API mgcp_client_pool_config_write
* mgcp-client: Add new VTY commands under mgw node without mgw prefix
* mgcp-client: Fix 'mgw endpoint-range' command dropped from old VTY node
* mgcp-client: Refactor system keeping old users not calling mgcp_client_pool_config_write() working
* mgcp-client: Fix no 'mgw ' prefix written in old VTY node
* Fix misleading error log
* mgcp_send: Use mgcp_conn_rtp_is_iuup() helper
* cosmetic: Clarify and fix typos in comment
* mgw: Log unexpected RTP AMR OA-vs-BE payload
* Rename and move func checking if amr mode is explicitly configured
* mgw: rx_rtp(): reorder checks and handlings
* mgw: Rename s/mgcp_send_rtp/mgcp_conn_rtp_dispatch_rtp/
* osmux: Rename function and pass msgb directly to it
* osmux: Make sure RTP AMR feed to osmux is in octet-aligned mode
* Improve logging on AMR OA<->BWE conversion failure
* osmux: Rotate over available Osmux CID when allocating a new one
* osmux: Use new osmux_xfrm_input API to set name on each link
* iuup: Use osmo_amr_ft_valid() API
[ Vadim Yanitskiy ]
* libosmo-mgcp-client: add -no-undefined to *_la_LDFLAGS
[ Philipp Maier ]
* mgcp_e1: fix apidoc
* mgcp_network: improve coment
[ Max ]
* Set working directory in systemd service file
* Add realtime scheduling and set priority in service file
* ctrl: take both address and port from vty config
[ Harald Welte ]
* Make osmo_mgcpc_ep_fsm_pre_term() static
* Support building with -Werror=strict-prototypes / -Werror=old-style-definition
* Add -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition
* update outdated vty copyright statement
[ Oliver Smith ]
* mgcp_client_pool.h: add missing stdbool include
* Fix various typos
* mgcp_client: add new clearmode codec
[ Neels Hofmeyr ]
* AMR->IuUP: do not crash on AMR data before IuUP Init
* AMR->IuUP: properly translate Q -> FQC
* IuUP->AMR: do not patch payload type a second time
* IuUP->AMR: log whether converting to AMR OA or BE
* AMR->IuUP: log conversion, like for the flipside
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 16:58:41 +0100
osmo-mgw (1.10.0) unstable; urgency=medium
[ Eric ]

2
debian/compat vendored
View File

@ -1 +1 @@
9
10

14
debian/control vendored
View File

@ -2,14 +2,14 @@ Source: osmo-mgw
Section: net
Priority: extra
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
Build-Depends: debhelper (>=9),
Build-Depends: debhelper (>= 10),
dh-autoreconf,
pkg-config,
autotools-dev,
libosmocore-dev (>= 1.7.0),
libosmo-netif-dev (>= 1.2.0),
libosmo-abis-dev (>= 1.3.0),
osmo-gsm-manuals-dev (>= 1.3.0)
libosmocore-dev (>= 1.9.0),
libosmo-netif-dev (>= 1.4.0),
libosmo-abis-dev (>= 1.5.0),
osmo-gsm-manuals-dev (>= 1.5.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-client9
Package: libosmo-mgcp-client12
Section: libs
Architecture: any
Multi-Arch: same
@ -33,7 +33,7 @@ Package: libosmo-mgcp-client-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libosmo-mgcp-client9 (= ${binary:Version}), ${misc:Depends}
Depends: libosmo-mgcp-client12 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
Package: osmo-mgw-doc

4
debian/copyright vendored
View File

@ -6,7 +6,7 @@ Files: *
Copyright: 2009-2014 On-Waves
2009-2015 Holger Hans Peter Freyther <zecke@selfish.org>
2013 Jacob Erlbeck <jerlbeck@sysmocom.de>
2016-2017 sysmocom s.m.f.c. GmbH <info@sysmocom.de>
2016-2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
License: AGPL-3.0+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@ -22,7 +22,7 @@ License: AGPL-3.0+
along with this program. If not, see <http://www.gnu.org/licenses/>.
Files: src/libosmo-mgcp-client/* include/osmocom/mgcp_client/*
Copyright: 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
Copyright: 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
2009 by Harald Welte <laforge@gnumonks.org>
2009-2011 by Holger Hans Peter Freyther

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

@ -1,20 +1,25 @@
!
! MGCP configuration example
!
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
e1_input
e1_line 0 driver dahdi
e1_line 0 port 0
mgcp
bind ip 127.0.0.1
rtp port-range 4002 16000
rtp 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

@ -1,17 +1,22 @@
!
! MGCP configuration example
!
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
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

@ -91,4 +91,10 @@ encoding/decoding for 16K and 8K subslots. Endpoints with other bitrates are
not yet useable.
NOTE: the VTY command "show mgcp" can be used to get a list of all available
endpoints (including identifiers)
endpoints (including identifiers)
=== The `null` endpoint
OsmoMGW offers a special `null@<domain>` endpoint which can be audited at all times.
This is useful for MGCP clients who wish to submit requests to OsmoMGW
periodically to find out whether it is still reachable and in a working state.

View File

@ -23,3 +23,20 @@ arguments:
Disable colors for logging to stderr. This has mostly been
deprecated by VTY based logging configuration, see <<logging>>
for more information.
=== Configure limits
When servicing hundreds of media endpoints, it may be necessary to adjust the
operating system's limit on open file descriptors for the osmo-mgw process. A
typical default limit imposed by operating systems is 1024; this would be
exceeded by, for example, about 256 active voice calls with 4 RTP/RTPC ports
each, sockets for other interfaces not considered yet.
It should be ok to set an OS limit on open file descriptors as high as 65536
for osmo-mgw, which practically rules out failure from running out of file
descriptors anywhere (<16,000 active calls).
When using systemd, the file descriptor limit may be adjusted in the service
file by the `LimitNOFILE` setting ("Number of Open FILE descriptors"). OsmoMGW
ships a systemd service file with a high LimitNOFILE setting.

View File

@ -3,10 +3,14 @@ SUBDIRS = \
$(NULL)
nobase_include_HEADERS = \
osmocom/mgcp_client/defs.h \
osmocom/mgcp_client/mgcp_client.h \
osmocom/mgcp_client/mgcp_client_endpoint_fsm.h \
osmocom/mgcp_client/mgcp_client_fsm.h \
osmocom/mgcp_client/mgcp_client_pool.h \
$(NULL)
noinst_HEADERS = \
osmocom/mgcp/mgcp.h \
osmocom/mgcp/mgcp_common.h \
osmocom/mgcp/osmux.h \

View File

@ -6,7 +6,6 @@ noinst_HEADERS = \
mgcp_endp.h \
mgcp_sdp.h \
mgcp_codec.h \
mgcp_ctrl.h \
mgcp_trunk.h \
debug.h \
mgcp_ratectr.h \

View File

@ -30,6 +30,7 @@
enum {
DRTP,
DE1,
DOSMUX,
Debug_LastEntry,
};

View File

@ -24,11 +24,13 @@
#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>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/osmux.h>
#include <arpa/inet.h>
#include <sys/types.h>
@ -69,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;
@ -137,8 +137,6 @@ struct mgcp_config {
mgcp_processing rtp_processing_cb;
mgcp_processing_setup setup_rtp_processing_cb;
mgcp_get_format get_net_downlink_format_cb;
struct osmo_wqueue gw_fd;
struct mgcp_port_range net_ports;
@ -156,24 +154,26 @@ struct mgcp_config {
enum mgcp_role role;
/* osmux translator: 0 means disabled, 1 means enabled */
int osmux;
/* addr to bind the server to */
char osmux_addr[INET6_ADDRSTRLEN];
/* The BSC-NAT may ask for enabling osmux on demand. This tells us if
* the osmux socket is already initialized.
*/
int osmux_init;
/* osmux batch factor: from 1 to 4 maximum */
int osmux_batch;
/* osmux batch size (in bytes) */
int osmux_batch_size;
/* osmux port */
uint16_t osmux_port;
/* Pad circuit with dummy messages until we see the first voice
* message.
*/
uint16_t osmux_dummy;
struct {
/* Osmux usage policy: */
enum osmux_usage usage;
/* addr to bind the server to */
char *local_addr_v4;
char *local_addr_v6;
/* osmux port */
uint16_t local_port;
/* The osmux socket is allocated on demand (1st time used).
* This tells us if the osmux socket is already initialized. */
bool initialized;
/* osmux batch factor: from 1 to 4 maximum */
int batch_factor;
/* osmux batch size (in bytes) */
int batch_size;
/* Pad circuit with dummy AMR frames if no payload to transmit is available */
bool dummy_padding;
/* Whether peer is behind NAT (Retrieve remote addr from 1st received Osmux packet) */
bool peer_behind_nat;
} osmux;
/* domain name of the media gateway */
char domain[255+1];
@ -205,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, struct osmo_sockaddr *addr, int port, 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,8 +13,9 @@ 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_decide(struct mgcp_conn_rtp *conn);
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst);
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
const char *subtype_name, unsigned int match_nr);
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec);
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec);
struct mgcp_rtp_codec *mgcp_codec_from_pt(struct mgcp_conn_rtp *conn, int payload_type);

View File

@ -47,6 +47,7 @@ enum mgcp_connection_mode {
MGCP_CONN_SEND_ONLY = 2,
MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY,
MGCP_CONN_LOOPBACK = 4 | MGCP_CONN_RECV_SEND,
MGCP_CONN_CONFECHO = 8 | MGCP_CONN_RECV_SEND,
};
#define MGCP_X_OSMO_IGN_HEADER "X-Osmo-IGN:"
@ -67,11 +68,11 @@ struct mgcp_codec_param {
/* Ensure that the msg->l2h is NUL terminated. */
static inline int mgcp_msg_terminate_nul(struct msgb *msg)
{
unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */
unsigned char *tail = msg->tail; /* char after l2 data */
if (tail[-1] == '\0')
/* nothing to do */;
else if (msgb_tailroom(msg) > 0)
tail[0] = '\0';
msgb_put_u8(msg, (uint8_t)'\0');
else if (tail[-1] == '\r' || tail[-1] == '\n')
tail[-1] = '\0';
else {

View File

@ -46,10 +46,14 @@ LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
/* Specific rtp connection type (see struct mgcp_conn_rtp) */
enum mgcp_conn_rtp_type {
MGCP_RTP_DEFAULT = 0,
MGCP_OSMUX_BSC,
MGCP_OSMUX_BSC_NAT,
MGCP_RTP_OSMUX,
MGCP_RTP_IUUP,
};
extern const struct value_string mgcp_conn_rtp_type_names[];
static inline const char *mgcp_conn_rtp_type_name(enum mgcp_conn_rtp_type val)
{
return get_value_string(mgcp_conn_rtp_type_names, val);
}
/*! Connection type, specifies which member of the union "u" in mgcp_conn
* contains a useful connection description (currently only RTP) */
@ -80,19 +84,20 @@ struct mgcp_conn_rtp {
struct {
/* Osmux state: disabled, activating, active */
enum osmux_state state;
/* Is cid holding valid data? is it allocated from pool? */
bool cid_allocated;
/* Allocated Osmux circuit ID for this conn */
uint8_t cid;
/* handle to batch messages */
/* Is local_cid holding valid data? is it allocated from pool? */
bool local_cid_allocated;
/* Allocated local Osmux circuit ID for this conn */
uint8_t local_cid;
/* Is remote_cid holding valid data? was it already received from client? */
bool remote_cid_present;
/* Received remote Osmux circuit ID for this conn */
uint8_t remote_cid;
/* handle to batch messages, shared (refcounted) among several conns */
struct osmux_in_handle *in;
/* handle to unbatch messages */
struct osmux_out_handle out;
/* statistics */
struct {
uint32_t chunks;
uint32_t octets;
} stats;
/* handle to unbatch messages, one allocated and owned per conn */
struct osmux_out_handle *out;
/* statistics: */
struct rate_ctr_group *ctrg;
} osmux;
struct {
@ -103,7 +108,7 @@ struct mgcp_conn_rtp {
struct osmo_iuup_rnl_prim *init_ind;
} iuup;
struct rate_ctr_group *rate_ctr_group;
struct rate_ctr_group *ctrg;
};
/*! MGCP connection (untyped) */
@ -145,12 +150,12 @@ struct mgcp_conn {
enum {
IN_STREAM_ERR_TSTMP_CTR,
OUT_STREAM_ERR_TSTMP_CTR,
RTP_PACKETS_RX_CTR,
RTP_OCTETS_RX_CTR,
RTP_PACKETS_TX_CTR,
RTP_OCTETS_TX_CTR,
RTP_DROPPED_PACKETS_CTR,
RTP_NUM_CONNECTIONS,
RTP_PACKETS_RX_CTR,
RTP_OCTETS_RX_CTR,
RTP_PACKETS_TX_CTR,
RTP_OCTETS_TX_CTR,
RTP_DROPPED_PACKETS_CTR,
RTP_NUM_CONNECTIONS,
};
/* RTP per-connection statistics. Instances of the corresponding rate counter group
@ -163,7 +168,7 @@ static const struct rate_ctr_desc mgcp_conn_rate_ctr_desc[] = {
[RTP_OCTETS_RX_CTR] = {"rtp:octets_rx", "Inbound rtp octets."},
[RTP_PACKETS_TX_CTR] = {"rtp:packets_tx", "Outbound rtp packets."},
[RTP_OCTETS_TX_CTR] = {"rtp:octets_tx", "Outbound rtp octets."},
[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "dropped rtp packets."}
[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "Dropped rtp packets."}
};
/* Aggregated RTP connection stats. These are updated when an RTP connection is freed.
@ -181,9 +186,49 @@ static const struct rate_ctr_desc all_rtp_conn_rate_ctr_desc[] = {
[RTP_NUM_CONNECTIONS] = {"all_rtp:num_closed_conns", "Total number of rtp connections closed."}
};
/* Osmux connection related counters */
enum {
OSMUX_CHUNKS_RX_CTR,
OSMUX_OCTETS_RX_CTR,
OSMUX_RTP_PACKETS_TX_CTR,
OSMUX_RTP_PACKETS_TX_DROPPED_CTR,
OSMUX_AMR_OCTETS_TX_CTR,
/* Only available in global stats: */
OSMUX_NUM_CONNECTIONS,
OSMUX_PACKETS_RX_CTR,
OSMUX_PACKETS_TX_CTR,
OSMUX_DROPPED_PACKETS_CTR,
};
/* RTP per-connection statistics. Instances of the corresponding rate counter group
* exist for the lifetime of an RTP connection.
* Must be kept in sync with all_rtp_conn_rate_ctr_desc below */
static const struct rate_ctr_desc mgcp_conn_osmux_rate_ctr_desc[] = {
[OSMUX_CHUNKS_RX_CTR] = {"osmux:chunks_rx", "Inbound Osmux chunks."},
[OSMUX_OCTETS_RX_CTR] = {"osmux:octets_rx", "Inbound Osmux octets."},
[OSMUX_RTP_PACKETS_TX_CTR] = {"osmux:rtp_packets_tx", "Tx outbound RTP packets to encode as Osmux."},
[OSMUX_RTP_PACKETS_TX_DROPPED_CTR] = {"osmux:rtp_packets_tx_dropped", "Dropped Tx outbound RTP packets to encode as Osmux."},
[OSMUX_AMR_OCTETS_TX_CTR] = {"osmux:amr_octets_tx", "Tx outbound AMD payload octets."},
};
/* Aggregated Osmux connection stats. These are updated when an Osmux connection is freed.
* Must be kept in sync with mgcp_conn_osmux_rate_ctr_desc above */
static const struct rate_ctr_desc all_osmux_conn_rate_ctr_desc[] = {
[OSMUX_CHUNKS_RX_CTR] = {"all_osmux:chunks_rx", "Inbound Osmux chunks."},
[OSMUX_OCTETS_RX_CTR] = {"all_osmux:octets_rx", "Inbound Osmux octets."},
[OSMUX_RTP_PACKETS_TX_CTR] = {"all_osmux:rtp_packets_tx", "Tx outbound RTP packets to encode as Osmux."},
[OSMUX_RTP_PACKETS_TX_DROPPED_CTR] = {"all_osmux:rtp_packets_tx_dropped", "Dropped Tx outbound RTP packets to encode as Osmux."},
[OSMUX_AMR_OCTETS_TX_CTR] = {"all_osmux:amr_octets_tx", "Tx outbound AMD payload octets."},
/* These last counters below do not exist in per-connection stats, only here: */
[OSMUX_NUM_CONNECTIONS] = {"all_osmux:num_closed_conns", "Total number of osmux connections closed."},
[OSMUX_PACKETS_RX_CTR] = {"all_osmux:packets_rx", "Total inbound UDP/Osmux packets."},
[OSMUX_PACKETS_TX_CTR] = {"all_osmux:packets_tx", "Total outbound UDP/Osmux packets."},
[OSMUX_DROPPED_PACKETS_CTR] = {"all_osmux:dropped_packets", "Dropped outbound UDP/Osmux packets."}
};
/* Was conn configured to handle Osmux? */
static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) {
return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT;
return conn->type == MGCP_RTP_OSMUX;
}
/* Was conn configured to handle Osmux? */

View File

@ -1,24 +0,0 @@
/*
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg,
const char *bind_addr, uint16_t port);

View File

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

View File

@ -40,8 +40,13 @@ LOGP(cat, level, "endpoint:%s " fmt, \
endp ? endp->name : "none", \
## args)
enum rtp_proto {
MGCP_PROTO_RTP,
MGCP_PROTO_RTCP,
};
struct osmo_rtp_msg_ctx {
int proto;
enum rtp_proto proto;
struct mgcp_conn_rtp *conn_src;
struct osmo_sockaddr *from_addr;
};
@ -125,10 +130,10 @@ struct mgcp_endpoint {
};
struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, unsigned int index);
void mgcp_endp_release(struct mgcp_endpoint *endp);
int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid);
void mgcp_endp_update(struct mgcp_endpoint *endp);
bool mgcp_endp_is_wildcarded(const char *epname);
bool mgcp_endp_is_null(const char *epname);
struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
const struct mgcp_trunk *trunk);
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
@ -140,3 +145,4 @@ 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);

View File

@ -4,12 +4,14 @@
#include <stdbool.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/mgcp/mgcp.h>
/* The following constant defines an RTP dummy payload that is used for
* "UDP Hole Punching" (NAT) */
static const char rtp_dummy_payload[] = { 0x23 };
#define MGCP_DUMMY_LOAD 0x23
static const char rtp_dummy_payload[] = { MGCP_DUMMY_LOAD };
/* Check if the data in a given message buffer matches the rtp dummy payload
* defined above */
@ -92,7 +94,7 @@ struct mgcp_rtp_end {
struct osmo_sockaddr addr;
/* in network byte order */
int rtp_port, rtcp_port;
int rtcp_port;
/* currently selected audio codec */
struct mgcp_rtp_codec *codec;
@ -107,7 +109,6 @@ struct mgcp_rtp_end {
int frames_per_packet;
uint32_t packet_duration_ms;
int maximum_packet_time; /* -1: not set */
char *fmtp_extra;
/* are we transmitting packets (true) or dropping (false) outbound packets */
bool output_enabled;
/* FIXME: This parameter can be set + printed, but is nowhere used! */
@ -120,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;
@ -156,11 +157,10 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
struct mgcp_rtp_state *state,
struct mgcp_rtp_end *rtp_end,
struct osmo_sockaddr *addr, struct msgb *msg);
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
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,
@ -178,5 +178,9 @@ void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp, struct mgcp_rtp_stat
void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
int id, int inc);
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg);
void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
int id);
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

@ -80,6 +80,8 @@ struct mgcp_ratectr_trunk {
struct rate_ctr_group *mgcp_dlcx_ctr_group;
/* Rate counter group which aggregates stats of individual RTP connections. */
struct rate_ctr_group *all_rtp_conn_stats;
/* Rate counter group which aggregates stats of individual Osmux connections. */
struct rate_ctr_group *all_osmux_conn_stats;
/* Rate counter group which contains stats for E1 events (only valid for E1 trunks) */
struct rate_ctr_group *e1_stats;
};

View File

@ -27,12 +27,9 @@ struct mgcp_trunk {
unsigned int trunk_nr;
enum mgcp_trunk_type trunk_type;
char *audio_fmtp_extra;
int audio_send_ptime;
int audio_send_name;
int no_audio_transcoding;
int omit_rtcp;
int keepalive_interval;
@ -66,7 +63,7 @@ struct mgcp_trunk {
/* E1 specific */
struct {
unsigned int vty_line_nr;
bool ts_in_use[NUM_E1_TS-1];
uint8_t ts_usecount[NUM_E1_TS-1];
struct osmo_i460_timeslot i460_ts[NUM_E1_TS-1];
/* Note: on an E1 line TS 0 is devoted to framing and
* alignment and therefore only NUM_E1_TS-1 timeslots

View File

@ -1,25 +1,22 @@
#pragma once
#include <stdint.h>
#include <osmocom/core/socket.h>
#include <osmocom/netif/osmux.h>
struct mgcp_conn_rtp;
struct mgcp_trunk;
struct mgcp_endpoint;
struct mgcp_conn_rtp;
#define OSMUX_PORT 1984
enum {
OSMUX_ROLE_BSC = 0,
OSMUX_ROLE_BSC_NAT,
};
int osmux_init(int role, struct mgcp_config *cfg);
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
struct osmo_sockaddr *addr, uint16_t port);
int osmux_init(struct mgcp_trunk *trunk);
int osmux_init_conn(struct mgcp_conn_rtp *conn);
int conn_osmux_enable(struct mgcp_conn_rtp *conn);
void conn_osmux_disable(struct mgcp_conn_rtp *conn);
int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid);
void conn_osmux_release_cid(struct mgcp_conn_rtp *conn);
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn);
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
int conn_osmux_event_rx_crcx_mdcx(struct mgcp_conn_rtp *conn);
int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg);
int osmux_send_dummy(struct mgcp_conn_rtp *conn);
void osmux_cid_pool_get(uint8_t osmux_cid);
int osmux_cid_pool_get_next(void);
@ -29,10 +26,14 @@ int osmux_cid_pool_count_used(void);
enum osmux_state {
OSMUX_STATE_DISABLED = 0, /* Osmux not being currently used by endp */
OSMUX_STATE_ACTIVATING, /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref osmux_enable_endpoint. */
OSMUX_STATE_ENABLED, /* Osmux was initialized by \ref osmux_enable_endpoint and can process frames */
OSMUX_STATE_ACTIVATING, /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref conn_osmux_enable. */
OSMUX_STATE_ENABLED, /* Osmux was initialized by \ref conn_osmux_enable and can process frames */
};
extern const struct value_string osmux_state_strs[];
static inline const char *osmux_state_str(enum osmux_state val)
{ return get_value_string(osmux_state_strs, val); }
enum osmux_usage {
OSMUX_USAGE_OFF = 0,
OSMUX_USAGE_ON = 1,

View File

@ -0,0 +1,7 @@
#include <osmocom/core/defs.h>
#if BUILDING_LIBOSMOMGCPCLIENT
# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT(text)
#else
# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT(text) OSMO_DEPRECATED(text)
#endif

View File

@ -3,14 +3,15 @@
#include <stdint.h>
#include <arpa/inet.h>
#include <osmocom/mgcp_client/defs.h>
#include <osmocom/mgcp_client/mgcp_common.h>
/* See also: RFC 3435, chapter 3.5 Transmission over UDP */
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT NULL /* INADDR(6)_ANY */
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 2727
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 0
#define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
#define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427
#define MGCP_CLIENT_KEEPALIVE_DEFAULT_ENDP "null"
#define MGCP_CLIENT_MGW_STR "Configure MGCP connection to Media Gateway\n"
struct msgb;
@ -35,6 +36,12 @@ struct mgcp_client_conf {
/* human readable name / description */
char *description;
struct {
uint32_t timeout_sec;
uint32_t req_interval_sec;
char req_endpoint_name[MGCP_ENDPOINT_MAXLEN];
} keepalive;
};
typedef unsigned int mgcp_trans_id_t;
@ -45,11 +52,12 @@ enum mgcp_codecs {
CODEC_GSM_8000_1 = 3,
CODEC_PCMA_8000_1 = 8,
CODEC_G729_8000_1 = 18,
CODEC_GSMEFR_8000_1 = 110,
CODEC_GSMHR_8000_1 = 111,
CODEC_AMR_8000_1 = 112,
CODEC_AMRWB_16000_1 = 113,
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_IUFP = 96,
CODEC_CLEARMODE = 120, /* 3GPP TS 48.103 table 5.4.2.2.1 */
};
/* Note: when new codec types are added, the corresponding value strings
* in mgcp_client.c (codec_table) must be updated as well. Enumerations
@ -71,27 +79,7 @@ struct ptmap {
unsigned int pt;
};
struct mgcp_response_head {
int response_code;
mgcp_trans_id_t trans_id;
char comment[MGCP_COMMENT_MAXLEN];
char conn_id[MGCP_CONN_ID_MAXLEN];
char endpoint[MGCP_ENDPOINT_MAXLEN];
bool x_osmo_osmux_use;
uint8_t x_osmo_osmux_cid;
};
struct mgcp_response {
char *body;
struct mgcp_response_head head;
uint16_t audio_port;
char audio_ip[INET6_ADDRSTRLEN];
unsigned int ptime;
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
unsigned int codecs_len;
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
};
int ptmap_cmp(const struct ptmap *a, const struct ptmap *b);
enum mgcp_verb {
MGCP_VERB_CRCX,
@ -101,38 +89,8 @@ enum mgcp_verb {
MGCP_VERB_RSIP,
};
#define MGCP_MSG_PRESENCE_ENDPOINT 0x0001
#define MGCP_MSG_PRESENCE_CALL_ID 0x0002
#define MGCP_MSG_PRESENCE_CONN_ID 0x0004
#define MGCP_MSG_PRESENCE_AUDIO_IP 0x0008
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
#define MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID 0x4000
#define MGCP_MSG_PRESENCE_X_OSMO_IGN 0x8000
struct mgcp_msg {
enum mgcp_verb verb;
/* See MGCP_MSG_PRESENCE_* constants */
uint32_t presence;
char endpoint[MGCP_ENDPOINT_MAXLEN];
unsigned int call_id;
char *conn_id;
uint16_t audio_port;
char *audio_ip;
enum mgcp_connection_mode conn_mode;
unsigned int ptime;
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
unsigned int codecs_len;
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
uint32_t x_osmo_ign;
bool x_osmo_osmux_use;
int x_osmo_osmux_cid; /* -1 is wildcard */
bool param_present;
struct mgcp_codec_param param;
};
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
struct mgcp_client_conf *mgcp_client_conf_alloc(void *ctx);
void mgcp_client_conf_init(struct mgcp_client_conf *conf) OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use mgcp_client_conf_alloc() (or even better, switch to the mgcp_client_pool API!)");
void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *conf);
int mgcp_client_config_write(struct vty *vty, const char *indent);
struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp);
@ -140,7 +98,7 @@ struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp);
struct mgcp_client *mgcp_client_init(void *ctx,
struct mgcp_client_conf *conf);
int mgcp_client_connect(struct mgcp_client *mgcp);
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports);
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports) OSMO_DEPRECATED("Use mgcp_client_connect() instead");
void mgcp_client_disconnect(struct mgcp_client *mgcp);
const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp);
@ -152,20 +110,8 @@ const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp);
const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uint8_t trunk_id, uint8_t ts,
uint8_t rate, uint8_t offset);
/* Invoked when an MGCP response is received or sending failed. When the
* response is passed as NULL, this indicates failure during transmission. */
typedef void (* mgcp_response_cb_t )(struct mgcp_response *response, void *priv);
int mgcp_response_parse_params(struct mgcp_response *r);
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
mgcp_response_cb_t response_cb, void *priv);
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
enum mgcp_connection_mode;
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);
extern const struct value_string mgcp_client_connection_mode_strs[];
static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
{

View File

@ -45,6 +45,7 @@ static inline void osmo_mgcpc_ep_ci_dlcx(struct osmo_mgcpc_ep_ci *ci)
void osmo_mgcpc_ep_clear(struct osmo_mgcpc_ep *ep);
const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep);
const char *osmo_mgcpc_ep_local_name(const struct osmo_mgcpc_ep *ep);
const char *osmo_mgcpc_ep_ci_name(const struct osmo_mgcpc_ep_ci *ci);
const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci);
struct mgcp_client *osmo_mgcpc_ep_client(const struct osmo_mgcpc_ep *ep);

View File

@ -29,11 +29,11 @@ struct mgcp_conn_peer {
/*! RTP packetization interval (optional) */
unsigned int ptime;
/*! RTP codec list (optional) */
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
/*! Number of codecs in RTP codec list (optional) */
unsigned int codecs_len;
/*! Deprecated. Use only ptmap[].codec in new code. */
enum mgcp_codecs codecs[MGCP_MAX_CODECS]
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[i].codec instead");
unsigned int codecs_len
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[] and ptmap_len instead");
/*! RTP payload type map (optional, only needed when payload types are
* used that differ from what IANA/3GPP defines) */
@ -64,11 +64,15 @@ struct mgcp_conn_peer {
};
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);
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
void mgcp_conn_delete(struct osmo_fsm_inst *fi);
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");
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci_request() instead");
void mgcp_conn_delete(struct osmo_fsm_inst *fi)
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci_dlcx() instead");
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi);
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi)
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci.mgcp_ci_str instead");
struct mgcp_client *mgcp_conn_get_client(struct osmo_fsm_inst *fi);
const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info);

View File

@ -1,6 +1,7 @@
#pragma once
#include <osmocom/core/write_queue.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/core/timer.h>
#define MSGB_CB_MGCP_TRANS_ID 0
@ -12,18 +13,39 @@ 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 llist_head inuse_endpoints;
struct mgcp_client_pool *pool;
struct mgcp_client_pool_member *pool_member;
struct osmo_timer_list keepalive_tx_timer;
struct osmo_timer_list keepalive_rx_timer;
bool conn_up;
};
struct mgcp_inuse_endpoint {
struct llist_head entry;
uint16_t id;
struct mgcp_response_head {
int response_code;
mgcp_trans_id_t trans_id;
char comment[MGCP_COMMENT_MAXLEN];
char conn_id[MGCP_CONN_ID_MAXLEN];
char endpoint[MGCP_ENDPOINT_MAXLEN];
bool x_osmo_osmux_use;
uint8_t x_osmo_osmux_cid;
};
struct mgcp_response {
char *body;
struct mgcp_response_head head;
uint16_t audio_port;
char audio_ip[INET6_ADDRSTRLEN];
unsigned int ptime;
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
};
/* Invoked when an MGCP response is received or sending failed. When the
* response is passed as NULL, this indicates failure during transmission. */
typedef void (*mgcp_response_cb_t)(struct mgcp_response *response, void *priv);
struct mgcp_response_pending {
struct llist_head entry;
@ -39,3 +61,41 @@ struct mgcp_response_pending * mgcp_client_pending_add(
mgcp_trans_id_t trans_id,
mgcp_response_cb_t response_cb,
void *priv);
#define MGCP_MSG_PRESENCE_ENDPOINT 0x0001
#define MGCP_MSG_PRESENCE_CALL_ID 0x0002
#define MGCP_MSG_PRESENCE_CONN_ID 0x0004
#define MGCP_MSG_PRESENCE_AUDIO_IP 0x0008
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
#define MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID 0x4000
#define MGCP_MSG_PRESENCE_X_OSMO_IGN 0x8000
struct mgcp_msg {
enum mgcp_verb verb;
/* See MGCP_MSG_PRESENCE_* constants */
uint32_t presence;
char endpoint[MGCP_ENDPOINT_MAXLEN];
unsigned int call_id;
char *conn_id;
uint16_t audio_port;
char *audio_ip;
enum mgcp_connection_mode conn_mode;
unsigned int ptime;
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
uint32_t x_osmo_ign;
bool x_osmo_osmux_use;
int x_osmo_osmux_cid; /* -1 is wildcard */
bool param_present;
struct mgcp_codec_param param;
};
int mgcp_response_parse_params(struct mgcp_response *r);
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
mgcp_response_cb_t response_cb, void *priv);
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);

View File

@ -1,11 +1,24 @@
#pragma once
#include <stdbool.h>
#include <osmocom/vty/vty.h>
struct mgcp_client;
struct mgcp_client_pool;
struct mgcp_client_pool_member;
struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx);
void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool);
int mgcp_client_pool_config_write(struct vty *vty, const char *indent);
unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool);
void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp_client *mgcp_client);
bool mgcp_client_pool_empty(const struct mgcp_client_pool *pool);
struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool);
void mgcp_client_pool_put(struct mgcp_client *mgcp_client);
struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_client_pool *pool, unsigned int nr);
struct mgcp_client *mgcp_client_pool_member_get(struct mgcp_client_pool_member *pool_member);
bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member);

View File

@ -1,9 +1,31 @@
#pragma once
/* Struct to handle a pool of MGWs. (Use _pool functions) */
struct mgcp_client_pool {
/* A pointer to a 'single' mgcp client. This is a non-pooled MGCP client that is configured using
* mgcp_client_vty_init() and actively registered by the API user using mgcp_client_pool_register_single() */
struct mgcp_client *mgcp_client_single;
/* A list that manages the pool members (see mgcp_client_pool_member->list above) */
struct llist_head member_list;
/* String to use for indentation when writing the configuration file to the VTY. This field is populated by
* mgcp_client_pool_vty_init() */
char *vty_indent;
/* VTY node specification used with this pool. This field is populated by mgcp_client_pool_vty_init() */
struct cmd_node *vty_node;
};
/* Struct to handle a member of a pool of MGWs. */
struct mgcp_client_pool_member {
/* Entry in llist mgcp_client_pool->pool. */
struct llist_head list;
/* The pool managing this object: */
struct mgcp_client_pool *pool;
/* Reference number assinged by VTY. This number is used to manage the pool from the VTY and to identify it in
* the log. */
unsigned int nr;
@ -24,22 +46,7 @@ struct mgcp_client_pool_member {
unsigned int refcount;
};
/* Struct to handle a pool of MGWs. (Use _pool functions) */
struct mgcp_client_pool {
/* A pointer to a 'single' mgcp client. This is a non-pooled MGCP client that is configured using
* mgcp_client_vty_init() and actively registered by the API user using mgcp_client_pool_register_single() */
struct mgcp_client *mgcp_client_single;
/* A list that manages the pool members (see above) */
struct llist_head pool;
/* String to use for indentation when writing the configuration file to the VTY. This field is populated by
* mgcp_client_pool_vty_init() */
char *vty_indent;
/* VTY node specification used with this pool. This field is populated by mgcp_client_pool_vty_init() */
struct cmd_node *vty_node;
};
struct mgcp_client_pool_member *mgcp_client_pool_member_alloc(struct mgcp_client_pool *pool, unsigned int nr);
void mgcp_client_pool_member_free(struct mgcp_client_pool_member *pool_member);
int mgcp_client_pool_member_reinit_client(struct mgcp_client_pool_member *pool_member);
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member);

View File

@ -13,12 +13,6 @@ AM_CFLAGS = \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
# Libraries
SUBDIRS = \
libosmo-mgcp-client \

View File

@ -14,14 +14,12 @@ AM_CFLAGS = \
$(NULL)
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
# This is not at all related to the release version, but a range of supported
# API versions. Read TODO_RELEASE in the source tree's root!
MGCP_CLIENT_LIBVERSION=10:0:1
MGCP_CLIENT_LIBVERSION=13:0:1
lib_LTLIBRARIES = \
libosmo-mgcp-client.la \
@ -35,4 +33,13 @@ libosmo_mgcp_client_la_SOURCES = \
mgcp_client_pool.c \
$(NULL)
libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION)
libosmo_mgcp_client_la_LDFLAGS = \
$(AM_LDFLAGS) \
-version-info $(MGCP_CLIENT_LIBVERSION) \
-no-undefined \
$(NULL)
libosmo_mgcp_client_la_LIBADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(NULL)

File diff suppressed because it is too large Load Diff

View File

@ -173,6 +173,30 @@ const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep)
return osmo_fsm_inst_name(ep->fi);
}
/*! Get "local endpoint name" part of the endpoint name: (local-endpoint-name@domain-name)
*
* \param ep The MGCP Endpoint
* \returns the local endpoint name if found, NULL on error.
*/
const char *osmo_mgcpc_ep_local_name(const struct osmo_mgcpc_ep *ep)
{
static char buf[1024];
const char *sep;
OSMO_ASSERT(ep);
sep = strchr(ep->endpoint, '@');
if (!sep) {
OSMO_STRLCPY_ARRAY(buf, ep->endpoint);
return buf;
}
if (sep - ep->endpoint >= sizeof(buf))
return NULL;
memcpy(buf, ep->endpoint, sep - ep->endpoint);
buf[sep - ep->endpoint] = '\0';
return buf;
}
const char *mgcp_conn_peer_name(const struct mgcp_conn_peer *info)
{
/* I'd be fine with a smaller buffer and accept truncation, but gcc possibly refuses to build if
@ -227,7 +251,7 @@ static struct value_string osmo_mgcpc_ep_fsm_event_names[33] = {};
static char osmo_mgcpc_ep_fsm_event_name_bufs[32][32] = {};
static void fill_event_names()
static void fill_event_names(void)
{
int i;
for (i = 0; i < (ARRAY_SIZE(osmo_mgcpc_ep_fsm_event_names) - 1); i++) {
@ -243,7 +267,7 @@ static void fill_event_names()
}
}
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init()
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init(void)
{
OSMO_ASSERT(osmo_fsm_register(&osmo_mgcpc_ep_fsm) == 0);
fill_event_names();
@ -509,10 +533,42 @@ static void on_success(struct osmo_mgcpc_ep_ci *ci, void *data)
mgcp_conn_peer_name(ci->got_port_info? &ci->rtp_info : NULL),
ci->notify.fi ? "" : " (not sending a notification)");
/* Below ordering is a delicate decision:
*
* We want to
* - emit the resulting event to ci->notify.fi,
* - check whether we want to tx the next pending MGCP message.
* Both these steps may terminate (=deallocate) the ep.
* So whichever one goes first may cause a use-after-free in the other.
*
* When dispatching the FSM event, we don't get an rc indicating dealloc of the FSM -- it may deallocate and we
* cannot tell. The common mechanism for that is osmo_fsm_set_dealloc_ctx(OTC_SELECT) and query the still
* allocated FSM state after termination (here we would check 'if (ci->ep != NULL)'), but we cannot assume the
* caller has actually set up an osmo_fsm_set_dealloc_ctx(). At time of writing, e.g. osmo-hnbgw does not use
* it.
*
* In osmo_mgcpc_ep_fsm_check_state_chg_after_response(), we do get an rc: false means FSM has terminated.
* On termination, the ep emits a term event to the FSM's parent.
* That may cause the notify.fi to be terminated in turn, depending on how the caller set things up.
* So: we cannot store notify.fi before, then call osmo_mgcpc_ep_fsm_check_state_chg_after_response(), and then
* emit the event, because notify.fi may have deallocated. We cannot look up whether
* osmo_mgcpc_ep_cancel_notify() has been called, because ci may have deallocated along with ci->ep.
*
* We have to skip emitting below success event in case the ep is now terminated.
* - It may be the final DLCX OK: not a problem, osmo_mgcpc_ep_ci_dlcx() has no notify args on purpose, so we do
* make all callers not set a notify event for DLCX by design. notify.fi should always be NULL when the final
* DLCX OK terminates the local endpoint state.
* - It may also be sudden termination due to a bad problem, in which case we shouldn't emit success.
* The osmo_fsm_inst.parent_term_event should suffice as feedback to the caller.
*/
if (osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi) == false) {
/* false means, the ci->ep has been terminated. */
return;
}
if (ci->notify.fi)
osmo_fsm_inst_dispatch(ci->notify.fi, ci->notify.success, ci->notify.data);
osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi);
}
/*! Return the MGW's local RTP port information for this connection, i.e. the local port that MGW is receiving on, as
@ -662,11 +718,11 @@ void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
osmo_strlcpy(cleared_ci.mgcp_ci_str, ci->mgcp_ci_str, sizeof(cleared_ci.mgcp_ci_str));
*ci = cleared_ci;
LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify.fi));
if (verb_info)
ci->verb_info = *verb_info;
LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify.fi));
if (ep->endpoint[0]) {
if (ci->verb_info.endpoint[0] && strcmp(ci->verb_info.endpoint, ep->endpoint))
LOG_CI(ci, LOGL_ERROR,
@ -997,7 +1053,7 @@ static int osmo_mgcpc_ep_fsm_timer_cb(struct osmo_fsm_inst *fi)
return 0;
}
void osmo_mgcpc_ep_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
static void osmo_mgcpc_ep_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
int i;
struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);

View File

@ -19,6 +19,7 @@
*/
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_internal.h>
#include <osmocom/mgcp_client/mgcp_client_fsm.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/fsm.h>
@ -114,12 +115,10 @@ static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info
.call_id = info->call_id,
.conn_mode = MGCP_CONN_RECV_ONLY,
.ptime = info->ptime,
.codecs_len = info->codecs_len,
.ptmap_len = info->ptmap_len,
.param_present = info->param_present
};
osmo_strlcpy(mgcp_msg->endpoint, info->endpoint, MGCP_ENDPOINT_MAXLEN);
memcpy(mgcp_msg->codecs, info->codecs, sizeof(mgcp_msg->codecs));
memcpy(mgcp_msg->ptmap, info->ptmap, sizeof(mgcp_msg->ptmap));
memcpy(&mgcp_msg->param, &info->param, sizeof(mgcp_msg->param));
@ -136,10 +135,19 @@ static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info
static void add_audio(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
{
mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT;
mgcp_msg->audio_ip = info->addr;
mgcp_msg->audio_port = info->port;
mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND;
bool ip_is_set = info->addr[0] != '\0' &&
strncmp(info->addr, "::", sizeof(info->addr)) != 0 &&
strncmp(info->addr, "0.0.0.0", sizeof(info->addr)) != 0;
if (ip_is_set) {
mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_IP;
mgcp_msg->audio_ip = info->addr;
}
if (info->port) {
mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_PORT;
mgcp_msg->audio_port = info->port;
}
if (ip_is_set && info->port)
mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND;
}
static void set_conn_mode(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *peer)
@ -163,12 +171,10 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
.audio_ip = mgcp_ctx->conn_peer_local.addr,
.audio_port = mgcp_ctx->conn_peer_local.port,
.ptime = mgcp_ctx->conn_peer_local.ptime,
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len,
.ptmap_len = mgcp_ctx->conn_peer_local.ptmap_len,
.param_present = mgcp_ctx->conn_peer_local.param_present
};
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
memcpy(mgcp_msg.ptmap, mgcp_ctx->conn_peer_local.ptmap, sizeof(mgcp_msg.ptmap));
memcpy(&mgcp_msg.param, &mgcp_ctx->conn_peer_local.param, sizeof(mgcp_ctx->conn_peer_local.param));
@ -221,8 +227,7 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
mgcp_ctx->conn_peer_local.endpoint);
make_crcx_msg(&mgcp_msg, &mgcp_ctx->conn_peer_local);
if (mgcp_ctx->conn_peer_local.port)
add_audio(&mgcp_msg, &mgcp_ctx->conn_peer_local);
add_audio(&mgcp_msg, &mgcp_ctx->conn_peer_local);
set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local);
msg = mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
@ -364,13 +369,21 @@ static void fsm_ready_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
switch (event) {
case EV_MDCX:
msg = make_mdcx_msg(mgcp_ctx);
OSMO_ASSERT(msg);
if (!msg) {
/* make_mdcx_msg() should already have logged the error */
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_resp_cb, fi);
new_state = ST_MDCX_RESP;
break;
case EV_DLCX:
msg = make_dlcx_msg(mgcp_ctx);
OSMO_ASSERT(msg);
if (!msg) {
/* make_dlcx_msg() should already have logged the error */
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_resp_cb, fi);
new_state = ST_DLCX_RESP;
break;
@ -611,6 +624,72 @@ static struct osmo_fsm fsm_mgcp_client = {
.log_subsys = DLMGCP,
};
/* 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)
{
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
if (!conn_peer->codecs_len)
return;
/* Before dropping codecs[], codecs[] would indicate the order in which the codecs should appear in SDP. ptmap[]
* would indicate payload type numbers when not using a default payload type number (may omit entries).
* Now, ptmap[] just indicates both at the same time; codecs[] should be empty, and ptmap[] lists all codecs.
* So if any codecs[] are present, recreate ptmap[] in the order of codecs[]. */
ptmap_len = 0;
for (int i = 0; i < conn_peer->codecs_len; i++) {
enum mgcp_codecs codec = conn_peer->codecs[i];
struct ptmap *found = NULL;
/* Look up whether a specific pt was indicated for this codec */
for (int p = 0; p < conn_peer->ptmap_len; p++) {
if (conn_peer->ptmap[p].codec != codec)
continue;
found = &conn_peer->ptmap[p];
break;
}
if (found) {
ptmap[ptmap_len] = *found;
} else {
ptmap[ptmap_len] = (struct ptmap){
.codec = codec,
/* some enum mgcp_codecs correspond to their standard PT nr, so for compat: */
.pt = codec,
};
}
ptmap_len++;
}
/* Are there any entries in the old ptmap that were omitted by codecs[]? */
for (int p = 0; p < conn_peer->ptmap_len; p++) {
bool exists = false;
for (int i = 0; i < ptmap_len; i++) {
if (ptmap_cmp(&ptmap[i], &conn_peer->ptmap[p]))
continue;
exists = true;
break;
}
if (exists)
continue;
if (ptmap_len >= ARRAY_SIZE(ptmap))
break;
/* Not present yet, add it to the end */
ptmap[ptmap_len] = conn_peer->ptmap[p];
ptmap_len++;
}
/* Use the new ptmap[], and clear out legacy codecs[]. */
memcpy(conn_peer->ptmap, ptmap, sizeof(conn_peer->ptmap));
conn_peer->ptmap_len = ptmap_len;
conn_peer->codecs_len = 0;
}
/*! allocate FSM, and create a new connection on the MGW.
* \param[in] mgcp MGCP client descriptor.
* \param[in] parent_fi Parent FSM instance.
@ -625,6 +704,7 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm
struct osmo_fsm_inst *fi;
struct in6_addr ip_test;
mgcp_conn_peer_compat(conn_peer);
OSMO_ASSERT(parent_fi);
OSMO_ASSERT(mgcp);
@ -664,6 +744,8 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
struct mgcp_ctx *mgcp_ctx = fi->priv;
struct in6_addr ip_test;
mgcp_conn_peer_compat(conn_peer);
OSMO_ASSERT(mgcp_ctx);
OSMO_ASSERT(conn_peer);
@ -760,7 +842,7 @@ const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info)
return buf;
}
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init()
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init(void)
{
OSMO_ASSERT(osmo_fsm_register(&fsm_mgcp_client) == 0);
}

View File

@ -18,6 +18,7 @@
*
*/
#include <asm-generic/errno.h>
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_internal.h>
#include <osmocom/mgcp_client/mgcp_client_pool_internal.h>
@ -27,32 +28,6 @@
#define LOGPPMGW(pool_member, level, fmt, args...) \
LOGP(DLMGCP, level, "MGW-pool(%s) " fmt, mgcp_client_pool_member_name(pool_member), ## args)
/* Get a human readable name for a given pool member. */
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member)
{
const struct mgcp_client *mpcp_client;
struct mgcp_client mpcp_client_dummy;
static char name[512];
const char *description;
if (!pool_member)
return "(null)";
/* It is not guranteed that a pool_member has an MGCP client. The client may not yet be initialized or the
* initalization may have been failed. In this case we will generate a dummy MGCP client to work with. */
if (!pool_member->client) {
memcpy(&mpcp_client_dummy.actual, &pool_member->conf, sizeof(mpcp_client_dummy.actual));
mpcp_client = &mpcp_client_dummy;
} else {
mpcp_client = pool_member->client;
}
description = mgcp_client_name(mpcp_client);
snprintf(name, sizeof(name), "%d:%s", pool_member->nr, description);
return name;
}
/*! Allocate MGCP client pool. This is called once on startup and before the pool is used with
* mgcp_client_pool_vty_init(). Since the pool is linked with the VTY it must exist througout the entire runtime.
* \param[in] talloc_ctx talloc context. */
@ -64,7 +39,7 @@ struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx)
if (!pool)
return NULL;
INIT_LLIST_HEAD(&pool->pool);
INIT_LLIST_HEAD(&pool->member_list);
return pool;
}
@ -77,29 +52,11 @@ unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool)
struct mgcp_client_pool_member *pool_member;
unsigned int pool_members_initialized = 0;
llist_for_each_entry(pool_member, &pool->pool, list) {
llist_for_each_entry(pool_member, &pool->member_list, list) {
/* Initialize client */
pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
if (!pool_member->client) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client initialization failed\n");
continue;
}
/* Set backpointer so that we can detect later that this MGCP client is managed
* by this pool. */
pool_member->client->pool = pool;
/* Connect client */
if (mgcp_client_connect2(pool_member->client, 0)) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client connect failed at (%s:%u)\n",
pool_member->conf.remote_addr, pool_member->conf.remote_port);
talloc_free(pool_member->client);
pool_member->client = NULL;
continue;
}
pool_members_initialized++;
if (mgcp_client_pool_member_reinit_client(pool_member) == 0)
pool_members_initialized++;
}
return pool_members_initialized;
@ -118,23 +75,43 @@ void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp
pool->mgcp_client_single = mgcp_client;
}
bool mgcp_client_pool_empty(const struct mgcp_client_pool *pool)
{
return llist_empty(&pool->member_list);
}
/*! Lookup the selected MGCP client config by its reference number */
struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_client_pool *pool, unsigned int nr)
{
struct mgcp_client_pool_member *pool_member;
llist_for_each_entry(pool_member, &pool->member_list, list) {
if (pool_member->nr == nr)
return pool_member;
}
return NULL;
}
/* Not every pool member may have a functional MGCP client, we will run through the pool once until we meet a
* pool member that is suitable (has a client, is not blocked, has a low load). */
* pool member that is suitable (is not blocked, has a client with a working link, has a low load). */
static struct mgcp_client_pool_member *mgcp_client_pool_pick(struct mgcp_client_pool *pool)
{
struct mgcp_client_pool_member *pool_member;
struct mgcp_client_pool_member *pool_member_picked = NULL;
unsigned int n_pool_members = llist_count(&pool->pool);
unsigned int n_pool_members = 0;
llist_for_each_entry(pool_member, &pool->pool, list) {
if (pool_member->blocked == false && pool_member->client) {
llist_for_each_entry(pool_member, &pool->member_list, list) {
n_pool_members++;
bool conn_up = pool_member->client && pool_member->client->conn_up;
if (pool_member->blocked == false && conn_up) {
if (!pool_member_picked)
pool_member_picked = pool_member;
else if (pool_member_picked->refcount > pool_member->refcount)
pool_member_picked = pool_member;
} else {
LOGPPMGW(pool_member, LOGL_DEBUG, "MGW pool has %u members -- MGW %u is unusable\n", n_pool_members,
pool_member->nr);
LOGPPMGW(pool_member, LOGL_DEBUG, "%s -- MGW %u is unusable (blocked=%u, cli=%u, link=%u)\n",
__func__, pool_member->nr, pool_member->blocked, !!pool_member->client, conn_up);
}
}
@ -163,26 +140,24 @@ struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool)
* by the application code. */
/* When the pool is empty, return a single MGCP client if it is registered. */
if (llist_empty(&pool->pool) && pool->mgcp_client_single) {
if (llist_empty(&pool->member_list) && pool->mgcp_client_single) {
LOGP(DLMGCP, LOGL_DEBUG, "MGW pool is empty -- using (single) MGW %s\n",
mgcp_client_name(pool->mgcp_client_single));
return pool->mgcp_client_single;
}
/* Abort when the pool is empty */
if (llist_empty(&pool->pool)) {
if (llist_empty(&pool->member_list)) {
LOGP(DLMGCP, LOGL_ERROR, "MGW pool is empty -- no MGW available!\n");
return NULL;
}
/* Pick a suitable pool member */
pool_member = mgcp_client_pool_pick(pool);
if (pool_member) {
pool_member->refcount++;
return pool_member->client;
}
if (!pool_member)
return NULL;
return NULL;
return mgcp_client_pool_member_get(pool_member);
}
/*! put an MGCP client back into the pool (decrement reference counter).
@ -194,23 +169,130 @@ struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool)
void mgcp_client_pool_put(struct mgcp_client *mgcp_client)
{
struct mgcp_client_pool_member *pool_member;
struct mgcp_client_pool *pool;
if (!mgcp_client)
return;
if (mgcp_client->pool)
pool = mgcp_client->pool;
else
if (!mgcp_client->pool_member)
return;
pool_member = mgcp_client->pool_member;
llist_for_each_entry(pool_member, &pool->pool, list) {
if (pool_member->client == mgcp_client) {
if (pool_member->refcount == 0) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGW pool member has invalid refcount\n");
return;
}
pool_member->refcount--;
}
if (pool_member->refcount == 0) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGW pool member has invalid refcount\n");
return;
}
pool_member->refcount--;
}
/***************************
* mgcp_client_pool_member:
***************************/
/*! Allocate an mgcp_client_pool_member.
* \param[in] pool MGCP client pool descriptor.
* \param[in] nr Reference number of the pool member.
*/
struct mgcp_client_pool_member *mgcp_client_pool_member_alloc(struct mgcp_client_pool *pool, unsigned int nr)
{
struct mgcp_client_pool_member *pool_member;
pool_member = talloc_zero(pool, struct mgcp_client_pool_member);
OSMO_ASSERT(pool_member);
mgcp_client_conf_init(&pool_member->conf);
pool_member->pool = pool;
pool_member->nr = nr;
llist_add_tail(&pool_member->list, &pool->member_list);
return pool_member;
}
/*! Free an mgcp_client_pool_member allocated through mgcp_client_pool_member_alloc().
* \param[in] pool_member MGCP client pool descriptor.
*
* It also frees the associated MGCP client if present.
*/
void mgcp_client_pool_member_free(struct mgcp_client_pool_member *pool_member)
{
llist_del(&pool_member->list);
if (pool_member->client) {
mgcp_client_disconnect(pool_member->client);
talloc_free(pool_member->client);
}
talloc_free(pool_member);
}
/*! Recreate and reconnect the MGCP client associated to the pool descriptor.
* \param[in] pool_member MGCP client pool descriptor.
*/
int mgcp_client_pool_member_reinit_client(struct mgcp_client_pool_member *pool_member)
{
/* Get rid of a possibly existing old MGCP client instance first */
if (pool_member->client) {
mgcp_client_disconnect(pool_member->client);
talloc_free(pool_member->client);
}
/* Initialize client */
pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
if (!pool_member->client) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client initialization failed\n");
return -EINVAL;
}
/* Set backpointer so that we can detect later that this MGCP client is managed by this pool. */
pool_member->client->pool_member = pool_member;
/* Connect client */
if (mgcp_client_connect(pool_member->client)) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client connect failed at (%s:%u)\n",
pool_member->conf.remote_addr, pool_member->conf.remote_port);
talloc_free(pool_member->client);
pool_member->client = NULL;
return -ECONNABORTED;
}
return 0;
}
/* Get a human readable name for a given pool member. */
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member)
{
const struct mgcp_client *mpcp_client;
struct mgcp_client mpcp_client_dummy;
static char name[512];
const char *description;
if (!pool_member)
return "(null)";
/* It is not guranteed that a pool_member has an MGCP client. The client may not yet be initialized or the
* initalization may have been failed. In this case we will generate a dummy MGCP client to work with. */
if (!pool_member->client) {
memcpy(&mpcp_client_dummy.actual, &pool_member->conf, sizeof(mpcp_client_dummy.actual));
mpcp_client = &mpcp_client_dummy;
} else {
mpcp_client = pool_member->client;
}
description = mgcp_client_name(mpcp_client);
snprintf(name, sizeof(name), "%d:%s", pool_member->nr, description);
return name;
}
/*! Get the MGCP client associated with the pool reference from the pool (increment reference counter).
* \param[in] pool_member MGCP client pool descriptor.
* \returns MGCP client descriptor, NULL if no member was not ready.
*/
struct mgcp_client *mgcp_client_pool_member_get(struct mgcp_client_pool_member *pool_member)
{
pool_member->refcount++;
return pool_member->client;
}
/*! Get whether the MGCP client associated with the pool reference is blocked by policy.
* \param[in] pool_member MGCP client pool descriptor.
* \returns true if blocked, false otherwise
*/
bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member)
{
return pool_member->blocked;
}

View File

@ -1,5 +1,5 @@
/* MGCP client interface to quagga VTY */
/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
* (C) 2009 by Harald Welte <laforge@gnumonks.org>
* (C) 2009-2011 by Holger Hans Peter Freyther
@ -28,14 +28,16 @@
#include <osmocom/vty/command.h>
#include <osmocom/vty/misc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/timer.h>
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_internal.h>
#include <osmocom/mgcp_client/mgcp_client_pool_internal.h>
#include <osmocom/mgcp_client/mgcp_client_pool.h>
#define MGW_STR MGCP_CLIENT_MGW_STR
/* Only common (non-pooled) VTY connands will use this talloc context. All
/* Only common (non-pooled) VTY commands will use this talloc context. All
* pooled VTY commands will use the pool (global_mgcp_client_pool) as
* talloc context. */
static void *global_mgcp_client_ctx = NULL;
@ -47,17 +49,46 @@ static struct mgcp_client_conf *global_mgcp_client_conf = NULL;
/* Pointer to the MGCP pool that is managed by mgcp_client_pool_vty_init() */
static struct mgcp_client_pool *global_mgcp_client_pool = NULL;
struct mgcp_client_conf *get_mgcp_client_config(struct vty *vty)
static struct mgcp_client_conf *get_mgcp_client_config(struct vty *vty)
{
if (global_mgcp_client_pool && vty->node == global_mgcp_client_pool->vty_node->node)
return vty->index;
else
return global_mgcp_client_conf;
/* Global single MGCP config, deprecated: */
vty_out(vty, "%% MGCP commands outside of 'mgw' nodes are deprecated. "
"You should consider reading the User Manual and migrating to 'mgw' node.%s",
VTY_NEWLINE);
return global_mgcp_client_conf;
}
static struct mgcp_client *get_mgcp_client(struct vty *vty)
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
struct mgcp_client_pool_member *pool_member;
if (global_mgcp_client_pool && vty->node == global_mgcp_client_pool->vty_node->node) {
llist_for_each_entry(pool_member, &global_mgcp_client_pool->member_list, list) {
/* Find matching the conf pointer: */
if (&pool_member->conf != conf)
continue;
return pool_member->client;
}
}
/* Global single MGCP config, deprecated: */
vty_out(vty, "%% MGCP commands outside of 'mgw' nodes are deprecated. "
"You should consider reading the User Manual and migrating to 'mgw' node.%s",
VTY_NEWLINE);
/* There's no way to obtain the struct mgcp_client in old interface, but anyway it's deprecated. */
return NULL;
}
DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
"mgw local-ip " VTY_IPV46_CMD,
MGW_STR "local bind to connect to MGW from\n"
"local-ip " VTY_IPV46_CMD,
"local bind to connect to MGW from\n"
"local bind IPv4 address\n"
"local bind IPv6 address\n")
{
@ -72,10 +103,16 @@ ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd,
"mgcpgw local-ip A.B.C.D",
MGW_STR "local bind to connect to MGCP gateway with\n"
"local bind IP address\n")
ALIAS_DEPRECATED(cfg_mgw_local_ip,
cfg_mgw_mgw_local_ip_cmd,
"mgw local-ip " VTY_IPV46_CMD,
MGW_STR "local bind to connect to MGW from\n"
"local bind IPv4 address\n"
"local bind IPv6 address\n")
DEFUN(cfg_mgw_local_port, cfg_mgw_local_port_cmd,
"mgw local-port <0-65535>",
MGW_STR "local port to connect to MGW from\n"
"local-port <0-65535>",
"local port to connect to MGW from\n"
"local bind port\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
@ -87,10 +124,15 @@ ALIAS_DEPRECATED(cfg_mgw_local_port, cfg_mgcpgw_local_port_cmd,
"mgcpgw local-port <0-65535>",
MGW_STR "local bind to connect to MGCP gateway with\n"
"local bind port\n")
ALIAS_DEPRECATED(cfg_mgw_local_port,
cfg_mgw_mgw_local_port_cmd,
"mgw local-port <0-65535>",
MGW_STR "local port to connect to MGW from\n"
"local bind port\n")
DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
"mgw remote-ip " VTY_IPV46_CMD,
MGW_STR "remote IP address to reach the MGW at\n"
"remote-ip " VTY_IPV46_CMD,
"remote IP address to reach the MGW at\n"
"remote IPv4 address\n"
"remote IPv6 address\n")
{
@ -104,10 +146,16 @@ ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
"mgcpgw remote-ip A.B.C.D",
MGW_STR "remote bind to connect to MGCP gateway with\n"
"remote bind IP address\n")
ALIAS_DEPRECATED(cfg_mgw_remote_ip,
cfg_mgw_mgw_remote_ip_cmd,
"mgw remote-ip " VTY_IPV46_CMD,
MGW_STR "remote IP address to reach the MGW at\n"
"remote IPv4 address\n"
"remote IPv6 address\n")
DEFUN(cfg_mgw_remote_port, cfg_mgw_remote_port_cmd,
"mgw remote-port <0-65535>",
MGW_STR "remote port to reach the MGW at\n"
"remote-port <0-65535>",
"remote port to reach the MGW at\n"
"remote port\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
@ -119,8 +167,13 @@ ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
"mgcpgw remote-port <0-65535>",
MGW_STR "remote bind to connect to MGCP gateway with\n"
"remote bind port\n")
ALIAS_DEPRECATED(cfg_mgw_remote_port,
cfg_mgw_mgw_remote_port_cmd,
"mgw remote-port <0-65535>",
MGW_STR "remote port to reach the MGW at\n"
"remote port\n")
DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
DEFUN_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgw_mgw_endpoint_range_cmd,
"mgw endpoint-range <1-65534> <1-65534>",
MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n"
"-\n" "-\n")
@ -130,7 +183,7 @@ DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
VTY_NEWLINE);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
ALIAS_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
"mgcpgw endpoint-range <1-65534> <1-65534>",
MGW_STR "usable range of endpoint identifiers\n"
"set first useable endpoint identifier\n"
@ -158,8 +211,8 @@ ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
DEFUN(cfg_mgw_endpoint_domain_name,
cfg_mgw_endpoint_domain_name_cmd,
"mgw endpoint-domain NAME",
MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
"endpoint-domain NAME",
"Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
"Domain name, should be alphanumeric.\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
@ -172,11 +225,16 @@ DEFUN(cfg_mgw_endpoint_domain_name,
}
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_endpoint_domain_name,
cfg_mgw_mgw_endpoint_domain_name_cmd,
"mgw endpoint-domain NAME",
MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
"Domain name, should be alphanumeric.\n")
DEFUN(cfg_mgw_reset_ep_name,
cfg_mgw_reset_ep_name_cmd,
"mgw reset-endpoint NAME",
MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
"reset-endpoint NAME",
"Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
"e.g. 'rtpbridge/*'\n"
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
{
@ -214,11 +272,17 @@ DEFUN(cfg_mgw_reset_ep_name,
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_reset_ep_name,
cfg_mgw_mgw_reset_ep_name_cmd,
"mgw reset-endpoint NAME",
MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
"e.g. 'rtpbridge/*'\n"
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
DEFUN(cfg_mgw_no_reset_ep_name,
cfg_mgw_no_reset_ep_name_cmd,
"no mgw reset-endpoint NAME",
NO_STR MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
"no reset-endpoint NAME",
NO_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
{
struct reset_ep *reset_ep;
@ -235,6 +299,96 @@ DEFUN(cfg_mgw_no_reset_ep_name,
vty_out(vty, "%% no such endpoint name configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
ALIAS_DEPRECATED(cfg_mgw_no_reset_ep_name,
cfg_mgw_mgw_no_reset_ep_name_cmd,
"no mgw reset-endpoint NAME",
NO_STR MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
DEFUN(cfg_mgw_mgw_keepalive_req_interval,
cfg_mgw_mgw_keepalive_req_interval_cmd,
"keepalive request-interval <0-4294967295>",
"Monitor if the MGCP link against MGW is still usable\n"
"Send an MGCP command to the MGW at given interval if no other commands are sent\n"
"The interval at which send MGCP commands (s), 0 to disable\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
struct mgcp_client *mgcp = get_mgcp_client(vty);
conf->keepalive.req_interval_sec = atoi(argv[0]);
if (!mgcp)
return CMD_SUCCESS;
/* If client already exists, apply the change immediately if possible: */
mgcp->actual.keepalive.req_interval_sec = atoi(argv[0]);
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);
} else {
if (osmo_timer_pending(&mgcp->keepalive_tx_timer))
osmo_timer_del(&mgcp->keepalive_tx_timer);
/* Assume link is UP by default, so that this MGW can be selected: */
mgcp->conn_up = true;
}
} /* else: wait until connect() to do first scheduling */
return CMD_SUCCESS;
}
DEFUN(cfg_mgw_mgw_keepalive_req_endpoint,
cfg_mgw_mgw_keepalive_req_endpoint_cmd,
"keepalive request-endpoint NAME",
"Monitor if the MGCP link against MGW is still usable\n"
"Use a given endpoint name when sending an MGCP command to the MGW for keepalive purposes\n"
"The name of the endpoint to use\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
struct mgcp_client *mgcp = get_mgcp_client(vty);
OSMO_STRLCPY_ARRAY(conf->keepalive.req_endpoint_name, argv[0]);
if (!mgcp)
return CMD_SUCCESS;
/* If client already exists, apply the change immediately if possible: */
OSMO_STRLCPY_ARRAY(mgcp->actual.keepalive.req_endpoint_name, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgw_mgw_keepalive_timeout,
cfg_mgw_mgw_keepalive_timeout_cmd,
"keepalive timeout <0-4294967295>",
"Monitor if the MGCP link against MGW is still usable\n"
"Consider the link to the MGW to be down after time without receiving any message from it\n"
"The timeout (s), 0 to disable\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
struct mgcp_client *mgcp = get_mgcp_client(vty);
conf->keepalive.timeout_sec = atoi(argv[0]);
if (!mgcp)
return CMD_SUCCESS;
/* If client already exists, apply the change immediately if possible: */
mgcp->actual.keepalive.timeout_sec = atoi(argv[0]);
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);
} else {
if (osmo_timer_pending(&mgcp->keepalive_rx_timer))
osmo_timer_del(&mgcp->keepalive_rx_timer);
/* Assume link is UP by default, so that this MGW can be selected: */
mgcp->conn_up = true;
}
} /* else: wait until connect() to do first scheduling */
return CMD_SUCCESS;
}
static int config_write(struct vty *vty, const char *indent, struct mgcp_client_conf *conf)
{
@ -242,33 +396,47 @@ static int config_write(struct vty *vty, const char *indent, struct mgcp_client_
int port;
struct reset_ep *reset_ep;
if (conf->description)
/* If caller doesn't the MGW pool API (mgcp_client_pool_vty_init was never called),
* then the "mgw" cmd prefix must be added since the old node always contained it.
*/
const char *mgw_prefix = global_mgcp_client_pool ? "" : "mgw ";
if (conf->description) /* description never had "mgw" prefix even on old node: */
vty_out(vty, "%sdescription %s%s", indent, conf->description, VTY_NEWLINE);
addr = conf->local_addr;
if (addr)
vty_out(vty, "%smgw local-ip %s%s", indent, addr,
VTY_NEWLINE);
vty_out(vty, "%s%slocal-ip %s%s", indent, mgw_prefix, addr, VTY_NEWLINE);
port = conf->local_port;
if (port >= 0)
vty_out(vty, "%smgw local-port %u%s", indent,
vty_out(vty, "%s%slocal-port %u%s", indent, mgw_prefix,
(uint16_t)port, VTY_NEWLINE);
addr = conf->remote_addr;
if (addr)
vty_out(vty, "%smgw remote-ip %s%s", indent, addr,
VTY_NEWLINE);
vty_out(vty, "%s%sremote-ip %s%s", indent, mgw_prefix, addr, VTY_NEWLINE);
port = conf->remote_port;
if (port >= 0)
vty_out(vty, "%smgw remote-port %u%s", indent,
vty_out(vty, "%s%sremote-port %u%s", indent, mgw_prefix,
(uint16_t)port, VTY_NEWLINE);
if (conf->endpoint_domain_name[0])
vty_out(vty, "%smgw endpoint-domain %s%s", indent,
vty_out(vty, "%s%sendpoint-domain %s%s", indent, mgw_prefix,
conf->endpoint_domain_name, VTY_NEWLINE);
llist_for_each_entry(reset_ep, &conf->reset_epnames, list)
vty_out(vty, "%smgw reset-endpoint %s%s", indent, reset_ep->name, VTY_NEWLINE);
vty_out(vty, "%s%sreset-endpoint %s%s", indent, mgw_prefix, reset_ep->name, VTY_NEWLINE);
if (conf->keepalive.req_interval_sec != 0)
vty_out(vty, "%s%skeepalive request-interval %u%s", indent, mgw_prefix,
conf->keepalive.req_interval_sec, VTY_NEWLINE);
if (strncmp(conf->keepalive.req_endpoint_name, MGCP_CLIENT_KEEPALIVE_DEFAULT_ENDP,
sizeof(conf->keepalive.req_endpoint_name)) != 0)
vty_out(vty, "%s%skeepalive request-endpoint %s%s", indent, mgw_prefix,
conf->keepalive.req_endpoint_name, VTY_NEWLINE);
if (conf->keepalive.timeout_sec != 0)
vty_out(vty, "%s%skeepalive timeout %u%s", indent, mgw_prefix,
conf->keepalive.timeout_sec, VTY_NEWLINE);
return CMD_SUCCESS;
}
@ -279,6 +447,11 @@ static int config_write(struct vty *vty, const char *indent, struct mgcp_client_
* \returns CMD_SUCCESS on success, CMD_WARNING on error */
int mgcp_client_config_write(struct vty *vty, const char *indent)
{
/* If caller supports MGW pool API (mgcp_client_pool_vty_init was
* called), then skip printing any config in this node and print it when
* the whole 'mgw' node is printed. */
if (global_mgcp_client_pool)
return CMD_SUCCESS;
return config_write(vty, indent, global_mgcp_client_conf);
}
@ -286,15 +459,18 @@ static void vty_init_common(void *talloc_ctx, int node)
{
global_mgcp_client_ctx = talloc_ctx;
install_lib_element(node, &cfg_mgw_local_ip_cmd);
install_lib_element(node, &cfg_mgw_local_port_cmd);
install_lib_element(node, &cfg_mgw_remote_ip_cmd);
install_lib_element(node, &cfg_mgw_remote_port_cmd);
install_lib_element(node, &cfg_mgw_endpoint_range_cmd);
install_lib_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
install_lib_element(node, &cfg_mgw_endpoint_domain_name_cmd);
install_lib_element(node, &cfg_mgw_reset_ep_name_cmd);
install_lib_element(node, &cfg_mgw_no_reset_ep_name_cmd);
/* deprecated 'mgw' commands ('mgw' prepended as first arg) */
install_lib_element(node, &cfg_mgw_mgw_local_ip_cmd);
install_lib_element(node, &cfg_mgw_mgw_local_port_cmd);
install_lib_element(node, &cfg_mgw_mgw_remote_ip_cmd);
install_lib_element(node, &cfg_mgw_mgw_remote_port_cmd);
install_lib_element(node, &cfg_mgw_mgw_endpoint_range_cmd);
install_lib_element(node, &cfg_mgw_mgw_endpoint_domain_name_cmd);
install_lib_element(node, &cfg_mgw_mgw_reset_ep_name_cmd);
install_lib_element(node, &cfg_mgw_mgw_no_reset_ep_name_cmd);
install_lib_element(node, &cfg_mgw_mgw_keepalive_req_interval_cmd);
install_lib_element(node, &cfg_mgw_mgw_keepalive_req_endpoint_cmd);
install_lib_element(node, &cfg_mgw_mgw_keepalive_timeout_cmd);
osmo_fsm_vty_add_cmds();
}
@ -319,38 +495,62 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
vty_init_common(talloc_ctx, node);
}
static int config_write_pool(struct vty *vty)
/* Mark whether user called mgcp_client_pool_config_write() and hence support new API */
static bool mgcp_client_pool_config_write_called = false;
static int _mgcp_client_pool_config_write(struct vty *vty, const char *indent)
{
struct mgcp_client_pool *pool = global_mgcp_client_pool;
struct mgcp_client_pool_member *pool_member;
unsigned int indent_buf_len = strlen(pool->vty_indent) + 1 + 1;
char *indent = talloc_zero_size(vty, indent_buf_len);
unsigned int subindent_buf_len;
char *subindent;
snprintf(indent, indent_buf_len, "%s ", pool->vty_indent);
if (!indent)
indent = pool->vty_indent ? : "";
subindent_buf_len = strlen(indent) + 1 + 1;
subindent = talloc_zero_size(vty, subindent_buf_len);
llist_for_each_entry(pool_member, &pool->pool, list) {
vty_out(vty, "%smgw %u%s", pool->vty_indent, pool_member->nr, VTY_NEWLINE);
config_write(vty, indent, &pool_member->conf);
snprintf(subindent, subindent_buf_len, "%s ", indent);
llist_for_each_entry(pool_member, &pool->member_list, list) {
vty_out(vty, "%smgw %u%s", indent, pool_member->nr, VTY_NEWLINE);
config_write(vty, subindent, &pool_member->conf);
}
talloc_free(indent);
/* MGW pool API is supported by user (global_mgcp_client_pool is set
* because mgcp_client_pool_vty_init was called). If single MGW was
* configured through old VTY and no mgw in the new MGW pool VTY is
* replacing it, then output the single MGW converted to the new MGW
* pool VTY. */
if (llist_empty(&pool->member_list) && pool->mgcp_client_single) {
vty_out(vty, "%smgw 0%s", indent, VTY_NEWLINE);
config_write(vty, subindent, global_mgcp_client_conf);
}
talloc_free(subindent);
return CMD_SUCCESS;
}
/* Lookup the selected MGCP client config by its reference number */
static struct mgcp_client_pool_member *pool_member_by_nr(unsigned int nr)
/* Deprecated, used for backward compatibility with older users which didn't call
* mgcp_client_pool_config_write(): */
static int config_write_pool(struct vty *vty)
{
struct mgcp_client_pool_member *pool_member = NULL;
struct mgcp_client_pool_member *pool_member_tmp;
if (mgcp_client_pool_config_write_called)
return CMD_SUCCESS;
llist_for_each_entry(pool_member_tmp, &global_mgcp_client_pool->pool, list) {
if (pool_member_tmp->nr == nr) {
pool_member = pool_member_tmp;
break;
}
}
return _mgcp_client_pool_config_write(vty, NULL);
}
return pool_member;
/*! Write out MGCP client config to VTY.
* \param[in] vty VTY to which we should print.
* \param[in] indent string used for indentation (e.g. " ").
If NULL, indentation passed during mgcp_client_pool_vty_init() will be used.
* \returns CMD_SUCCESS on success, CMD_WARNING on error */
int mgcp_client_pool_config_write(struct vty *vty, const char *indent)
{
/* Tell internal node write function that the user supports calling proper API: */
mgcp_client_pool_config_write_called = true;
return _mgcp_client_pool_config_write(vty, indent);
}
DEFUN_ATTR(cfg_mgw,
@ -359,13 +559,10 @@ DEFUN_ATTR(cfg_mgw,
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member;
pool_member = pool_member_by_nr(nr);
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
if (!pool_member) {
pool_member = talloc_zero(global_mgcp_client_pool, struct mgcp_client_pool_member);
pool_member = mgcp_client_pool_member_alloc(global_mgcp_client_pool, nr);
OSMO_ASSERT(pool_member);
mgcp_client_conf_init(&pool_member->conf);
pool_member->nr = nr;
llist_add_tail(&pool_member->list, &global_mgcp_client_pool->pool);
}
vty->index = &pool_member->conf;
@ -382,7 +579,7 @@ DEFUN_ATTR(cfg_no_mgw,
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member;
pool_member = pool_member_by_nr(nr);
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
if (!pool_member) {
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
@ -395,12 +592,7 @@ DEFUN_ATTR(cfg_no_mgw,
return CMD_WARNING;
}
llist_del(&pool_member->list);
if (pool_member->client) {
mgcp_client_disconnect(pool_member->client);
talloc_free(pool_member->client);
}
talloc_free(pool_member);
mgcp_client_pool_member_free(pool_member);
return CMD_SUCCESS;
}
@ -412,7 +604,7 @@ DEFUN_ATTR(mgw_reconnect, mgw_reconnect_cmd,
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member = NULL;
pool_member = pool_member_by_nr(nr);
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
if (!pool_member) {
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
@ -425,32 +617,10 @@ DEFUN_ATTR(mgw_reconnect, mgw_reconnect_cmd,
return CMD_WARNING;
}
/* Get rid of a possibly existing old MGCP client instance first */
if (pool_member->client) {
mgcp_client_disconnect(pool_member->client);
talloc_free(pool_member->client);
}
/* Create a new MGCP client instance with the current config */
pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
if (!pool_member->client) {
LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s initalization failed\n",
mgcp_client_pool_member_name(pool_member));
vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s",
mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
/* Set backpointer so that we can detect later that this MGCP client is managed by this pool. */
pool_member->client->pool = global_mgcp_client_pool;
/* Connect client */
if (mgcp_client_connect(pool_member->client)) {
if (mgcp_client_pool_member_reinit_client(pool_member) < 0) {
LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s connect failed at (%s:%u)\n",
mgcp_client_pool_member_name(pool_member), pool_member->conf.remote_addr,
pool_member->conf.remote_port);
talloc_free(pool_member->client);
pool_member->client = NULL;
vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s",
mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE);
return CMD_WARNING;
@ -466,7 +636,7 @@ DEFUN_ATTR(mgw_block, mgw_block_cmd,
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member = NULL;
pool_member = pool_member_by_nr(nr);
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
if (!pool_member) {
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
@ -483,7 +653,7 @@ DEFUN_ATTR(mgw_unblock, mgw_unblock_cmd,
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member = NULL;
pool_member = pool_member_by_nr(nr);
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
if (!pool_member) {
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
@ -493,22 +663,27 @@ DEFUN_ATTR(mgw_unblock, mgw_unblock_cmd,
return CMD_SUCCESS;
}
DEFUN(mgw_show, mgw_snow_cmd, "show mgw-pool", SHOW_STR "Display information about the MGW-Pool\n")
DEFUN(mgw_show, mgw_show_cmd, "show mgw-pool", SHOW_STR "Display information about the MGW-Pool\n")
{
vty_out(vty, "%% MGW-Pool:%s", VTY_NEWLINE);
struct mgcp_client_pool_member *pool_member;
if (llist_empty(&global_mgcp_client_pool->pool) && global_mgcp_client_pool->mgcp_client_single) {
if (llist_empty(&global_mgcp_client_pool->member_list) && global_mgcp_client_pool->mgcp_client_single) {
vty_out(vty, "%% (pool is empty, single MGCP client will be used)%s", VTY_NEWLINE);
return CMD_SUCCESS;
} else if (llist_empty(&global_mgcp_client_pool->pool)) {
} else if (llist_empty(&global_mgcp_client_pool->member_list)) {
vty_out(vty, "%% (pool is empty)%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
llist_for_each_entry(pool_member, &global_mgcp_client_pool->pool, list) {
llist_for_each_entry(pool_member, &global_mgcp_client_pool->member_list, list) {
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-client: %s%s", pool_member->client ? "connected" : "disconnected",
vty_out(vty, "%% MGCP link: %s,%s%s",
cli && cli->iofd ? "connected" : "disconnected",
cli && cli->conn_up ?
((cli->actual.keepalive.timeout_sec > 0) ? "UP" : "MAYBE") :
"DOWN",
VTY_NEWLINE);
vty_out(vty, "%% service: %s%s", pool_member->blocked ? "blocked" : "unblocked", VTY_NEWLINE);
vty_out(vty, "%% ongoing calls: %u%s", pool_member->refcount, VTY_NEWLINE);
@ -520,7 +695,8 @@ DEFUN(mgw_show, mgw_snow_cmd, "show mgw-pool", SHOW_STR "Display information abo
* (called once at startup by the application process).
* \param[in] parent_node identifier of the parent node on which the mgw node appears.
* \param[in] mgw_node identifier that should be used with the newly installed MGW node.
* \param[in] indent indentation string to match the indentation in the VTY config
* \param[in] indent indentation string to match the indentation in the VTY config.
If NULL, it must be passed explicitly each time mgcp_client_pool_config_write() is called.
* \param[in] pool user provided memory to store the configured MGCP client (MGW) pool. */
void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool)
{
@ -528,11 +704,12 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent
OSMO_ASSERT(pool);
/* Never allow this function to be called twice on the same pool */
OSMO_ASSERT(!pool->vty_indent);
OSMO_ASSERT(!pool->vty_node);
pool->vty_indent = talloc_strdup(pool, indent);
OSMO_ASSERT(pool->vty_indent);
if (indent) {
pool->vty_indent = talloc_strdup(pool, indent);
OSMO_ASSERT(pool->vty_indent);
}
pool->vty_node = talloc_zero(pool, struct cmd_node);
OSMO_ASSERT(pool->vty_node);
pool->vty_node->node = mgw_node;
@ -542,8 +719,19 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent
install_lib_element(parent_node, &cfg_mgw_cmd);
install_lib_element(parent_node, &cfg_no_mgw_cmd);
/* Note: config_write_pool is deprecated and user is expected to
* manually call mgcp_client_pool_config_write() when printing the VTY
* config */
install_node(pool->vty_node, config_write_pool);
vty_init_common(pool, mgw_node);
install_lib_element(mgw_node, &cfg_mgw_local_ip_cmd);
install_lib_element(mgw_node, &cfg_mgw_local_port_cmd);
install_lib_element(mgw_node, &cfg_mgw_remote_ip_cmd);
install_lib_element(mgw_node, &cfg_mgw_remote_port_cmd);
install_lib_element(mgw_node, &cfg_mgw_rtp_bts_base_port_cmd);
install_lib_element(mgw_node, &cfg_mgw_endpoint_domain_name_cmd);
install_lib_element(mgw_node, &cfg_mgw_reset_ep_name_cmd);
install_lib_element(mgw_node, &cfg_mgw_no_reset_ep_name_cmd);
install_element(mgw_node, &cfg_description_cmd);
@ -551,7 +739,7 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent
install_lib_element(ENABLE_NODE, &mgw_block_cmd);
install_lib_element(ENABLE_NODE, &mgw_unblock_cmd);
install_lib_element_ve(&mgw_snow_cmd);
install_lib_element_ve(&mgw_show_cmd);
global_mgcp_client_pool = pool;
}

View File

@ -16,12 +16,6 @@ AM_CFLAGS = \
$(NULL)
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOTRAU_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
@ -45,7 +39,6 @@ libosmo_mgcp_a_SOURCES = \
mgcp_stat.c \
mgcp_endp.c \
mgcp_trunk.c \
mgcp_ctrl.c \
mgcp_ratectr.c \
mgcp_e1.c \
mgcp_iuup.c \

View File

@ -276,75 +276,6 @@ error:
return -EINVAL;
}
/* Check if the given codec is applicable on the specified endpoint
* Helper function for mgcp_codec_decide() */
static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec)
{
/* A codec name must be set, if not, this might mean that the codec
* (payload type) that was assigned is unknown to us so we must stop
* here. */
if (!strlen(codec->subtype_name))
return false;
/* FIXME: implement meaningful checks to make sure that the given codec
* is compatible with the given endpoint */
return true;
}
/*! Decide for one suitable codec
* \param[in] conn related rtp-connection.
* \returns 0 on success, -EINVAL on failure. */
int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
{
struct mgcp_rtp_end *rtp;
unsigned int i;
struct mgcp_endpoint *endp;
bool codec_assigned = false;
endp = conn->conn->endp;
rtp = &conn->end;
/* This function works on the results the SDP/LCO parser has extracted
* from the MGCP message. The goal is to select a suitable codec for
* the given connection. When transcoding is available, the first codec
* from the codec list is taken without further checking. When
* transcoding is not available, then the choice must be made more
* carefully. Each codec in the list is checked until one is found that
* is rated compatible. The rating is done by the helper function
* is_codec_compatible(), which does the actual checking. */
for (i = 0; i < rtp->codecs_assigned; i++) {
/* When no transcoding is available, avoid codecs that would
* require transcoding. */
if (endp->trunk->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n",
rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name);
continue;
}
rtp->codec = &rtp->codecs[i];
codec_assigned = true;
break;
}
/* FIXME: To the reviewes: This is problematic. I do not get why we
* need to reset the packet_duration_ms depending on the codec
* selection. I thought it were all 20ms? Is this to address some
* cornercase. (This piece of code was in the code path before,
* together with the note: "TODO/XXX: Store this per codec and derive
* it on use" */
if (codec_assigned) {
if (rtp->maximum_packet_time >= 0
&& rtp->maximum_packet_time * rtp->codec->frame_duration_den >
rtp->codec->frame_duration_num * 1500)
rtp->packet_duration_ms = 0;
return 0;
}
return -EINVAL;
}
/* Return true if octet-aligned is set in the given codec. Default to octet-aligned=0, i.e. bandwidth-efficient mode.
* See RFC4867 "RTP Payload Format for AMR and AMR-WB" sections "8.1. AMR Media Type Registration" and "8.2. AMR-WB
* Media Type Registration":
@ -364,10 +295,15 @@ bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
return codec->param.amr_octet_aligned;
}
/* Compare two codecs, all parameters must match up, except for the payload type
* number. */
/* Compare two codecs, all parameters must match up */
static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
{
/* All codec properties must match up, except the payload type number. Even though standardisd payload numbers
* exist for certain situations, the call agent may still assign them freely. Hence we must not insist on equal
* payload type numbers. Also the audio_name is not checked since it is already parsed into subtype_name, rate,
* and channels, which are checked. */
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
return false;
if (codec_a->rate != codec_b->rate)
return false;
if (codec_a->channels != codec_b->channels)
@ -376,61 +312,172 @@ static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *c
return false;
if (codec_a->frame_duration_den != codec_b->frame_duration_den)
return false;
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
return false;
/* Note: AMR allows to set the RTP payload format to octet-aligned or bandwith-efficient (octet-aligned=0)
* via SDP. This difference concerns payload format only, but not the actual codec. It is not a difference
* within the meaning of this function. */
/* AMR payload may be formatted in two different payload formats, it is still the same codec but since the
* formatting of the payload is different, conversation is required, so we must treat it as a different
* codec here. */
if (strcmp(codec_a->subtype_name, "AMR") == 0) {
if (mgcp_codec_amr_is_octet_aligned(codec_a) != mgcp_codec_amr_is_octet_aligned(codec_b))
return false;
}
return true;
}
/*! Translate a given payload type number that belongs to the packet of a
* source connection to the equivalent payload type number that matches the
* configuration of a destination connection.
* \param[in] conn_src related source rtp-connection.
* \param[in] conn_dst related destination rtp-connection.
* \param[in] payload_type number from the source packet or source connection.
* \returns translated payload type number on success, -EINVAL on failure. */
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type)
/* Compare two codecs, all parameters must match up, except parameters related to payload formatting (not checked). */
static bool codecs_convertible(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
{
struct mgcp_rtp_end *rtp_src;
struct mgcp_rtp_end *rtp_dst;
struct mgcp_rtp_codec *codec_src = NULL;
struct mgcp_rtp_codec *codec_dst = NULL;
/* OsmoMGW currently has no ability to transcode from one codec to another. However OsmoMGW is still able to
* translate between different payload formats as long as the encoded voice data itself does not change.
* Therefore we must insist on equal codecs but still allow different payload formatting. */
/* In 3G IuUP, AMR may be encapsulated in IuFP, this means even though the codec name and negotiated rate is
* different, the formatting can still be converted by OsmoMGW. Therefore we won't insist on equal
* subtype_name and rate if we detect IuFP and AMR is used on the same tandem. */
if (strcmp(codec_a->subtype_name, "AMR") == 0 && strcmp(codec_b->subtype_name, "VND.3GPP.IUFP") == 0)
goto iufp;
if (strcmp(codec_a->subtype_name, "VND.3GPP.IUFP") == 0 && strcmp(codec_b->subtype_name, "AMR") == 0)
goto iufp;
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
return false;
if (codec_a->rate != codec_b->rate)
return false;
iufp:
if (codec_a->channels != codec_b->channels)
return false;
if (codec_a->frame_duration_num != codec_b->frame_duration_num)
return false;
if (codec_a->frame_duration_den != codec_b->frame_duration_den)
return false;
return true;
}
struct mgcp_rtp_codec *mgcp_codec_find_same(struct mgcp_conn_rtp *conn, struct mgcp_rtp_codec *codec)
{
struct mgcp_rtp_end *rtp_end;
unsigned int i;
unsigned int codecs_assigned;
rtp_src = &conn_src->end;
rtp_dst = &conn_dst->end;
rtp_end = &conn->end;
/* Find the codec information that is used on the source side */
codecs_assigned = rtp_src->codecs_assigned;
/* Use the codec information from the source and try to find the equivalent of it on the destination side. In
* the first run we will look for an exact match. */
codecs_assigned = rtp_end->codecs_assigned;
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
for (i = 0; i < codecs_assigned; i++) {
if (payload_type == rtp_src->codecs[i].payload_type) {
codec_src = &rtp_src->codecs[i];
if (codecs_same(codec, &rtp_end->codecs[i])) {
return &rtp_end->codecs[i];
break;
}
}
if (!codec_src)
return -EINVAL;
/* Use the codec information from the source and try to find the
* equivalent of it on the destination side */
codecs_assigned = rtp_dst->codecs_assigned;
return NULL;
}
/* For a given codec, find a convertible codec in the given connection. */
static struct mgcp_rtp_codec *codec_find_convertible(struct mgcp_conn_rtp *conn, struct mgcp_rtp_codec *codec)
{
struct mgcp_rtp_end *rtp_end;
unsigned int i;
unsigned int codecs_assigned;
struct mgcp_rtp_codec *codec_convertible = NULL;
rtp_end = &conn->end;
/* Use the codec information from the source and try to find the equivalent of it on the destination side. In
* the first run we will look for an exact match. */
codec_convertible = mgcp_codec_find_same(conn, codec);
if (codec_convertible)
return codec_convertible;
/* In case we weren't able to find an exact match, we will try to find a match that is the same codec, but the
* payload format may be different. This alternative will require a frame format conversion (i.e. AMR bwe->oe) */
codecs_assigned = rtp_end->codecs_assigned;
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
for (i = 0; i < codecs_assigned; i++) {
if (codecs_same(codec_src, &rtp_dst->codecs[i])) {
codec_dst = &rtp_dst->codecs[i];
if (codecs_convertible(codec, &rtp_end->codecs[i])) {
codec_convertible = &rtp_end->codecs[i];
break;
}
}
if (!codec_dst)
return codec_convertible;
}
/*! Decide for one suitable codec on both of the given connections. In case a destination connection is not available,
* a tentative decision is made.
* \param[inout] conn_src related rtp-connection.
* \param[inout] conn_dst related destination rtp-connection (NULL if not present).
* \returns 0 on success, -EINVAL on failure. */
int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst)
{
unsigned int i;
/* In case no destination connection is available (yet), or in case the destination connection exists but has
* no codecs assigned, we are forced to make a simple tentative decision:
* We just use the first codec of the source connection (conn_src) */
OSMO_ASSERT(conn_src->end.codecs_assigned <= MGCP_MAX_CODECS);
if (!conn_dst || conn_dst->end.codecs_assigned == 0) {
if (conn_src->end.codecs_assigned >= 1) {
conn_src->end.codec = &conn_src->end.codecs[0];
return 0;
} else
return -EINVAL;
}
/* Compare all codecs of the source connection (conn_src) to the codecs of the destination connection (conn_dst). In case
* of a match set this codec on both connections. This would be an ideal selection since no codec conversion would be
* required. */
for (i = 0; i < conn_src->end.codecs_assigned; i++) {
struct mgcp_rtp_codec *codec_conn_src = &conn_src->end.codecs[i];
struct mgcp_rtp_codec *codec_conn_dst = mgcp_codec_find_same(conn_dst, codec_conn_src);
if (codec_conn_dst) {
/* We found the a codec that is exactly the same (same codec, same payload format etc.) on both
* sides. We now set this codec on both connections. */
conn_dst->end.codec = codec_conn_dst;
conn_src->end.codec = codec_conn_src;
return 0;
}
}
/* In case we could not find a codec that is exactly the same, let's at least try to find a codec that we are able
* to convert. */
for (i = 0; i < conn_src->end.codecs_assigned; i++) {
struct mgcp_rtp_codec *codec_conn_src = &conn_src->end.codecs[i];
struct mgcp_rtp_codec *codec_conn_dst = codec_find_convertible(conn_dst, codec_conn_src);
if (codec_conn_dst) {
/* We found the a codec that we can convert to. Set each side to its codec. */
conn_dst->end.codec = codec_conn_dst;
conn_src->end.codec = codec_conn_src;
return 0;
}
}
if (conn_dst->end.codecs_assigned)
conn_dst->end.codec = &conn_dst->end.codecs[0];
else
return -EINVAL;
return codec_dst->payload_type;
if (conn_src->end.codecs_assigned)
conn_src->end.codec = &conn_src->end.codecs[0];
else
return -EINVAL;
return 0;
}
/* 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->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.
@ -440,8 +487,7 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp
* \param subtype_name SDP codec name without parameters (e.g. "AMR").
* \param match_nr Index for the match found, first being match_nr == 0. Iterate all matches by calling multiple times
* with incrementing match_nr.
* \return codec definition for that conn matching the subtype_name, or NULL if no such match_nr is found.
*/
* \return codec definition for that conn matching the subtype_name, or NULL if no such match_nr is found. */
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
const char *subtype_name, unsigned int match_nr)
{
@ -457,3 +503,26 @@ const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn
}
return NULL;
}
/*! Lookup a codec that is assigned to a connection by its payload type number.
* \param[in] conn related rtp-connection.
* \param[in] payload_type number of the codec to look up.
* \returns pointer to codec struct on success, NULL on failure. */
struct mgcp_rtp_codec *mgcp_codec_from_pt(struct mgcp_conn_rtp *conn, int payload_type)
{
struct mgcp_rtp_end *rtp_end = &conn->end;
unsigned int codecs_assigned = rtp_end->codecs_assigned;
struct mgcp_rtp_codec *codec = NULL;
size_t i;
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
for (i = 0; i < codecs_assigned; i++) {
if (payload_type == rtp_end->codecs[i].payload_type) {
codec = &rtp_end->codecs[i];
break;
}
}
return codec;
}

View File

@ -37,7 +37,7 @@
#include <osmocom/core/timer.h>
#include <ctype.h>
const static struct rate_ctr_group_desc rate_ctr_group_desc = {
static const struct rate_ctr_group_desc rate_ctr_group_desc = {
.group_name_prefix = "conn_rtp",
.group_description = "rtp connection statistics",
.class_id = 1,
@ -95,17 +95,21 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
static atomic_uint rate_ctr_index = 0;
conn_rtp->type = MGCP_RTP_DEFAULT;
conn_rtp->osmux.cid_allocated = false;
conn_rtp->osmux.cid = 0;
/* Osmux specific defaults, only used if conn is later on Osmux-enabled: */
conn_rtp->osmux.state = OSMUX_STATE_DISABLED;
conn_rtp->osmux.local_cid_allocated = false;
conn_rtp->osmux.local_cid = 0;
conn_rtp->osmux.remote_cid_present = false;
conn_rtp->osmux.remote_cid = 0;
/* backpointer to the generic part of the connection */
conn->u.rtp.conn = conn;
end->rtp.fd = -1;
end->rtcp.fd = -1;
end->rtp_port = end->rtcp_port = 0;
talloc_free(end->fmtp_extra);
end->fmtp_extra = NULL;
end->rtp = NULL;
end->rtcp = NULL;
memset(&end->addr, 0, sizeof(end->addr));
end->rtcp_port = 0;
/* Set default values */
end->frames_per_packet = 0; /* unknown */
@ -113,12 +117,12 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
end->output_enabled = false;
end->maximum_packet_time = -1;
conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++);
if (!conn_rtp->rate_ctr_group)
conn_rtp->ctrg = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++);
if (!conn_rtp->ctrg)
return -1;
conn_rtp->state.in_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->rate_ctr_group, IN_STREAM_ERR_TSTMP_CTR);
conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->rate_ctr_group, OUT_STREAM_ERR_TSTMP_CTR);
conn_rtp->state.in_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, IN_STREAM_ERR_TSTMP_CTR);
conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, OUT_STREAM_ERR_TSTMP_CTR);
/* Make sure codec table is reset */
mgcp_codec_reset_all(conn_rtp);
@ -134,7 +138,7 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
if (mgcp_conn_rtp_is_iuup(conn_rtp))
mgcp_conn_iuup_cleanup(conn_rtp);
mgcp_free_rtp_port(&conn_rtp->end);
rate_ctr_group_free(conn_rtp->rate_ctr_group);
rate_ctr_group_free(conn_rtp->ctrg);
mgcp_codec_reset_all(conn_rtp);
}
@ -167,8 +171,8 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
struct mgcp_conn *conn;
int rc;
/* Do not allow more then two connections */
if (llist_count(&endp->conns) >= endp->type->max_conns)
/* Do not allow more than the maximum number of connections */
if (endp->type->max_conns > 0 && llist_count(&endp->conns) >= endp->type->max_conns)
return NULL;
/* Create new connection and add it to the list */
@ -263,7 +267,7 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp)
{
struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats;
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
struct rate_ctr_group *conn_stats = conn_rtp->ctrg;
if (all_stats == NULL || conn_stats == NULL)
return;
@ -352,29 +356,37 @@ 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) };
if (!conn) {
snprintf(str, sizeof(str), "(null connection)");
return str;
}
if (!conn)
return "NULL";
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
/* Dump RTP connection */
snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, "
"rtp:%u rtcp:%u)",
conn->name,
conn->id,
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
ntohs(conn->u.rtp.end.rtp_port),
ntohs(conn->u.rtp.end.rtcp_port));
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);
switch (conn->u.rtp.type) {
case MGCP_RTP_OSMUX:
OSMO_STRBUF_PRINTF(sb, " CID=%u", conn->u.rtp.osmux.local_cid);
break;
default:
break;
}
OSMO_STRBUF_PRINTF(sb, ")");
break;
default:
/* Should not happen, we should be able to dump
* every possible connection type. */
snprintf(str, sizeof(str), "(unknown connection type)");
break;
return "(unknown connection type)";
}
return str;
@ -411,3 +423,10 @@ struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp)
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" },
{ MGCP_RTP_IUUP, "iuup" },
{}
};

View File

@ -1,36 +0,0 @@
/*
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/ctrl/control_if.h>
#include <osmocom/mgcp/mgcp.h>
static int mgw_ctrl_node_lookup(void *data, vector vline, int *node_type,
void **node_data, int *i)
{
return 0;
}
struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg,
const char *bind_addr, uint16_t port)
{
return ctrl_interface_setup_dynip2(cfg, bind_addr, port, mgw_ctrl_node_lookup,
_LAST_CTRL_NODE);
}

View File

@ -222,7 +222,7 @@ static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_dat
osmo_i460_mux_enqueue(endp->e1.schan, msg);
}
/* called by I.460 de-multeiplexer; feed output of I.460 demux into TRAU frame sync */
/* called by I.460 de-multiplexer; feed output of I.460 demux into TRAU frame sync */
static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data, const ubit_t *bits,
unsigned int num_bits)
{
@ -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));
@ -309,8 +308,8 @@ skip:
return;
}
/* Function to handle outgoing E1 traffic */
static void e1_send(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
/* handle outgoing E1 traffic */
static void e1_send_ts_frame(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
{
struct msgb *msg = msgb_alloc_c(trunk, E1_TS_BYTES, "E1-TX-timeslot-bytes");
uint8_t *ptr;
@ -359,19 +358,17 @@ static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
osmo_i460_demux_in(&trunk->e1.i460_ts[ts->num - 1], msgb_data(msg), msgb_length(msg));
/* Trigger sending of pending E1 traffic */
e1_send(ts, trunk);
e1_send_ts_frame(ts, trunk);
/* e1inp_rx_ts() does not free() msgb */
/* e1inp_rx_ts(), the caller of this callback does not free() msgb. */
msgb_free(msg);
}
static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
static int e1_open(struct mgcp_trunk *trunk, uint8_t ts_nr)
{
/*! Each timeslot needs only to be configured once. The Timeslot then
* stays open and permanently receives data. It is then up to the
* I.460 demultiplexer to add/remove subchannels as needed. It is
* allowed to call this function multiple times since we check if the
* timeslot is already configured. */
/*! One E1 timeslot may serve multiple I.460 subslots. The timeslot is opened as soon as an I.460 subslot is
* opened and will stay open until the last I.460 subslot is closed (see e1_close below). This function must
* be called any time a new I.460 subslot is opened in order to maintain constancy of the ts_usecount counter. */
struct e1inp_line *e1_line;
int rc;
@ -379,12 +376,14 @@ static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
cfg = trunk->cfg;
if (trunk->e1.ts_in_use[ts_nr - 1]) {
LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u already set up, skipping...\n", ts_nr);
if (trunk->e1.ts_usecount[ts_nr - 1] > 0) {
LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u already set up and in use by %u subslot(s), using it as it is...\n",
ts_nr, trunk->e1.ts_usecount[ts_nr - 1]);
trunk->e1.ts_usecount[ts_nr - 1]++;
return 0;
}
/* Get E1 line */
/* Find E1 line */
e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
if (!e1_line) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "no such E1 line %u - check VTY config!\n",
@ -401,12 +400,61 @@ static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
}
rc = e1inp_line_update(e1_line);
if (rc < 0) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 timeslot %u.\n", ts_nr);
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 line %u.\n", ts_nr);
return -EINVAL;
}
LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u set up successfully.\n", ts_nr);
trunk->e1.ts_in_use[ts_nr - 1] = true;
trunk->e1.ts_usecount[ts_nr - 1]++;
OSMO_ASSERT(trunk->e1.ts_usecount[ts_nr - 1] == 1);
return 0;
}
static int e1_close(struct mgcp_trunk *trunk, uint8_t ts_nr)
{
/* See also comment above (e1_open). This function must be called any time an I.460 subslot is closed */
struct e1inp_line *e1_line;
int rc;
OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
cfg = trunk->cfg;
if (trunk->e1.ts_usecount[ts_nr - 1] > 1) {
trunk->e1.ts_usecount[ts_nr - 1]--;
LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u still in use by %u other subslot(s), leaving it open...\n",
ts_nr, trunk->e1.ts_usecount[ts_nr - 1]);
return 0;
} else if (trunk->e1.ts_usecount[ts_nr - 1] == 0) {
/* This should not be as it means we close the timeslot too often. */
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "E1 timeslot %u already closed, leaving it as it is...\n", ts_nr);
return -EINVAL;
}
/* Find E1 line */
e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
if (!e1_line) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "no such E1 line %u - check VTY config!\n",
trunk->e1.vty_line_nr);
return -EINVAL;
}
/* Release E1 timeslot */
rc = e1inp_ts_config_none(&e1_line->ts[ts_nr - 1], e1_line);
if (rc < 0) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to disable E1 timeslot %u.\n", ts_nr);
return -EINVAL;
}
rc = e1inp_line_update(e1_line);
if (rc < 0) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 line %u.\n", trunk->e1.vty_line_nr);
return -EINVAL;
}
LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u closed.\n", ts_nr);
trunk->e1.ts_usecount[ts_nr - 1]--;
OSMO_ASSERT(trunk->e1.ts_usecount[ts_nr - 1] == 0);
return 0;
}
@ -496,7 +544,7 @@ static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
}
}
/* !Equip E1 endpoint with I.460 mux resources.
/*! Equip E1 endpoint with I.460 mux and E1 timeslot resources.
* \param[in] endp endpoint to equip
* \param[in] ts E1 timeslot number.
* \param[in] ss E1 subslot number.
@ -517,7 +565,7 @@ int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8
endp->e1.last_amr_ft = AMR_4_75;
/* Set up E1 line / timeslot */
rc = e1_init(endp->trunk, ts);
rc = e1_open(endp->trunk, ts);
if (rc != 0)
return -EINVAL;
@ -604,9 +652,15 @@ void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
}
/*! Remove E1 resources from endpoint
* \param[in] endp endpoint to release. */
void mgcp_e1_endp_release(struct mgcp_endpoint *endp)
* \param[in] endp endpoint to release.
* \param[in] ts E1 timeslot number. */
void mgcp_e1_endp_release(struct mgcp_endpoint *endp, uint8_t ts)
{
/* Guard against multiple calls. In case we don't see a subchannel anymore we can safely assume that all work
* is done. */
if (!(endp->e1.schan || endp->e1.trau_rtp_st || endp->e1.trau_sync_fi))
return;
LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n");
if (endp->e1.schan)
@ -615,8 +669,10 @@ void mgcp_e1_endp_release(struct mgcp_endpoint *endp)
talloc_free(endp->e1.trau_rtp_st);
if (endp->e1.trau_sync_fi)
osmo_fsm_inst_term(endp->e1.trau_sync_fi, OSMO_FSM_TERM_REGULAR, NULL);
memset(&endp->e1, 0, sizeof(endp->e1));
/* Close E1 timeslot */
e1_close(endp->trunk, ts);
}
/*! Accept RTP message buffer with RTP data and enqueue voice data for E1 transmit.

View File

@ -38,7 +38,6 @@
const struct mgcp_endpoint_typeset ep_typeset = {
/* Specify endpoint properties for RTP endpoint */
.rtp = {
.max_conns = 2,
.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
.cleanup_cb = mgcp_cleanup_rtp_bridge_cb,
},
@ -110,36 +109,6 @@ struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk,
return endp;
}
/*! release endpoint, all open connections are closed.
* \param[in] endp endpoint to release */
void mgcp_endp_release(struct mgcp_endpoint *endp)
{
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
/* Normally this function should only be called when
* all connections have been removed already. In case
* that there are still connections open (e.g. when
* RSIP is executed), free them all at once. */
mgcp_conn_free_all(endp);
/* We must only decrement the stat item when the endpoint as actually
* claimed. An endpoint is claimed when a call-id is set */
if (endp->callid)
osmo_stat_item_dec(osmo_stat_item_group_get_item(endp->trunk->stats.common,
TRUNK_STAT_ENDPOINTS_USED), 1);
/* Reset endpoint parameters and states */
talloc_free(endp->callid);
endp->callid = NULL;
talloc_free(endp->local_options.string);
endp->local_options.string = NULL;
talloc_free(endp->local_options.codec);
endp->local_options.codec = NULL;
if (endp->trunk->trunk_type == MGCP_TRUNK_E1)
mgcp_e1_endp_release(endp);
}
/* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or
* "ds/e1-") and write the epname without the prefix back to the memory
* pointed at by epname. (per trunk the prefix is the same for all endpoints,
@ -187,7 +156,6 @@ static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
}
}
/*! Convert all characters in epname to lowercase and strip trunk prefix and
* endpoint name suffix (domain name) from epname. The result is written to
* to the memory pointed at by epname_stripped. The expected size of the
@ -260,6 +228,17 @@ bool mgcp_endp_is_wildcarded(const char *epname)
return false;
}
/*! Check if the given epname refers to a "null" endpoint.
* \param[in] epname endpoint name to check
* \returns true if epname refers to "null"" endpoint, else false. */
bool mgcp_endp_is_null(const char *epname)
{
if (strncasecmp(epname, "null@", 5) == 0)
return true;
return false;
}
/*! Find an endpoint by its name on a specified trunk.
* \param[out] cause pointer to store cause code, can be NULL.
* \param[in] epname endpoint name to lookup.
@ -675,3 +654,36 @@ void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
if (llist_empty(&endp->conns))
mgcp_endp_release(endp);
}
/*! release endpoint, all open connections are closed.
* \param[in] endp endpoint to release */
void mgcp_endp_release(struct mgcp_endpoint *endp)
{
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
/* Normally this function should only be called when
* all connections have been removed already. In case
* that there are still connections open (e.g. when
* RSIP is executed), free them all at once. */
mgcp_conn_free_all(endp);
/* We must only decrement the stat item when the endpoint as actually
* claimed. An endpoint is claimed when a call-id is set */
if (endp->callid)
osmo_stat_item_dec(osmo_stat_item_group_get_item(endp->trunk->stats.common,
TRUNK_STAT_ENDPOINTS_USED), 1);
/* Reset endpoint parameters and states */
talloc_free(endp->callid);
endp->callid = NULL;
talloc_free(endp->local_options.string);
endp->local_options.string = NULL;
talloc_free(endp->local_options.codec);
endp->local_options.codec = NULL;
if (endp->trunk->trunk_type == MGCP_TRUNK_E1) {
uint8_t ts = e1_ts_nr_from_epname(endp->name);
mgcp_e1_endp_release(endp, ts);
}
}

View File

@ -98,7 +98,11 @@ static int8_t _conn_iuup_amr_ft_2_rfci(struct mgcp_conn_rtp *conn_rtp, uint8_t f
uint8_t rfci_cnt = 0;
unsigned match_bytes = (unsigned)osmo_amr_bytes(ft);
struct osmo_iuup_rnl_prim *irp = conn_rtp->iuup.init_ind;
OSMO_ASSERT(irp);
if (!irp) {
/* No IuUP Initialization has occured on the IuUP side yet. Return error and drop the RTP data, until
* the IuUP Initialization has configured the link. */
return -1;
}
/* TODO: cache this somehow */
for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) {
@ -270,9 +274,9 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg
return ft;
}
msgb_pull_to_l3(msg);
LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR: ft %d, len %d\n", ft, msgb_l3len(msg));
if (mgcp_codec_amr_is_octet_aligned(conn_rtp_dst->end.codec)) {
LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR OA: ft %d, len %d\n", ft, msgb_length(msg));
amr_hdr = (struct amr_hdr *) msgb_push(msg, sizeof(struct amr_hdr));
amr_hdr->cmr = 15; /* no change */
amr_hdr->f = 0;
@ -291,6 +295,7 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg
amr_data[1] |= ((ft & 0x1) << 7) | (((!fqc) & 0x1) << 6);
amr_length = (osmo_amr_bits(ft) + 10 + 7) / 8;
msgb_trim(msg, amr_length);
LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR BE: ft %d, len %zd\n", ft, amr_length);
}
rtp_hdr = (struct rtp_hdr *) msgb_push(msg, sizeof(*rtp_hdr));
*rtp_hdr = (struct rtp_hdr){
@ -306,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;
}
@ -342,8 +346,7 @@ static int _conn_iuup_rx_rnl_data(struct mgcp_conn_rtp *conn_rtp_src, struct osm
return bridge_iuup_to_iuup_peer(conn_rtp_src, conn_rtp_dst, irp);
case MGCP_RTP_DEFAULT:
return bridge_iuup_to_rtp_peer(conn_rtp_src, conn_rtp_dst, irp);
case MGCP_OSMUX_BSC:
case MGCP_OSMUX_BSC_NAT:
case MGCP_RTP_OSMUX:
default:
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
"Forward of IuUP payload to RTP connection type %u not supported!\n",
@ -465,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);
@ -490,7 +493,7 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
"rtp_port:%u rtcp_port:%u\n",
dest_name,
osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
);
return 0;
}
@ -502,27 +505,25 @@ 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",
dest_name, osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port));
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, rtp_end->rtp_port,
(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
@ -618,9 +619,8 @@ int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg)
force_output_enabled = true;
/* Fill in the peer address so that we can send Init-ACK back: */
prev_rem_addr = conn_rtp_src->end.addr;
prev_rem_rtp_port = conn_rtp_src->end.rtp_port;
prev_rem_rtp_port = osmo_sockaddr_port(&conn_rtp_src->end.addr.u.sa);
conn_rtp_src->end.addr = *mc->from_addr;
conn_rtp_src->end.rtp_port = htons(osmo_sockaddr_port(&mc->from_addr->u.sa));
}
rc = _conn_iuup_rtp_pl_up(conn_rtp_src, msg);
@ -628,7 +628,7 @@ int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg)
if (force_output_enabled) {
conn_rtp_src->end.output_enabled = prev_output_enabled;
conn_rtp_src->end.addr = prev_rem_addr;
conn_rtp_src->end.rtp_port = prev_rem_rtp_port;
osmo_sockaddr_set_port(&conn_rtp_src->end.addr.u.sa, prev_rem_rtp_port);
}
return rc;
@ -638,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;
@ -670,7 +670,7 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn
"Bridge RTP=>IuUP: too short for AMR OA hdr (%u)\n", msgb_length(msg));
goto free_ret;
}
if (amr_hdr->ft >= AMR_FT_MAX) {
if (!osmo_amr_ft_valid(amr_hdr->ft)) {
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR OA ft=%u\n", amr_hdr->ft);
goto free_ret;
}
@ -678,19 +678,22 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn
LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR OA ft=%u\n", amr_hdr->ft);
goto free_ret;
}
irp->u.data.fqc = amr_hdr->q;
irp->u.data.fqc = amr_hdr->q ? IUUP_FQC_FRAME_GOOD : IUUP_FQC_FRAME_BAD;
irp->u.data.rfci = rfci;
msgb_pull(msg, 2);
LOGP(DLMGCP, LOGL_DEBUG, "Convert AMR OA -> IuUP: ft %d -> rfci %d len %d\n",
amr_hdr->ft, rfci, msgb_length(msg));
} else {
uint8_t *amr_bwe_hdr = (uint8_t *) msgb_data(msg);
int8_t ft;
uint8_t q;
if (msgb_length(msg) < 2) {
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE,
"Bridge RTP=>IuUP: too short for AMR BE hdr (%u)\n", msgb_length(msg));
goto free_ret;
}
ft = ((amr_bwe_hdr[0] & 0x07) << 1) | ((amr_bwe_hdr[1] & 0x80) >> 7);
if (ft >= AMR_FT_MAX) {
if (!osmo_amr_ft_valid(ft)) {
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR BE ft=%u\n", ft);
goto free_ret;
}
@ -698,7 +701,8 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn
LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR BE ft=%u\n", ft);
goto free_ret;
}
irp->u.data.fqc = ((amr_bwe_hdr[1] & 0x40) >> 6);
q = amr_bwe_hdr[1] & 0x40;
irp->u.data.fqc = q ? IUUP_FQC_FRAME_GOOD : IUUP_FQC_FRAME_BAD;
irp->u.data.rfci = rfci;
rc = iuup_length = osmo_amr_bwe_to_iuup(msgb_data(msg), msgb_length(msg));
if (rc < 0) {
@ -706,6 +710,8 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn
return rc;
}
msgb_trim(msg, iuup_length);
LOGP(DLMGCP, LOGL_DEBUG, "Convert AMR BE -> IuUP: ft %d -> rfci %d len %d\n",
ft, rfci, msgb_length(msg));
}
irp->oph.msg->l3h = msgb_put(irp->oph.msg, msgb_length(msg));

View File

@ -75,7 +75,7 @@ void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble)
}
/*! Parse connection mode.
* \param[in] mode as string (recvonly, sendrecv, sendonly or loopback)
* \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 */
@ -100,6 +100,8 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
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 {
@ -202,7 +204,7 @@ int mgcp_parse_osmux_cid(const char *line)
osmux_cid, OSMUX_CID_MAX);
return -2;
}
LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
LOGP(DLMGCP, LOGL_DEBUG, "MGCP client offered Osmux CID %u\n", osmux_cid);
return osmux_cid;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -47,6 +47,7 @@
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_iuup.h>
#include <osmocom/mgcp/debug.h>
/* Contains the last successfully resolved endpoint name. This variable is used
* for the unit-tests to verify that the endpoint was correctly resolved. */
@ -85,6 +86,9 @@ struct mgcp_request_data {
/* set to true when the request has been classified as wildcarded */
bool wildcarded;
/* Set to true when the request is targeted at the "null" endpoint */
bool null_endp;
/* contains cause code in case of problems during endp/trunk resolution */
int mgcp_cause;
};
@ -150,8 +154,7 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
switch (conn->type) {
case MGCP_RTP_DEFAULT:
case MGCP_OSMUX_BSC:
case MGCP_OSMUX_BSC_NAT:
case MGCP_RTP_OSMUX:
case MGCP_RTP_IUUP:
break;
default:
@ -301,7 +304,7 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
/* Attach optional OSMUX parameters */
if (mgcp_conn_rtp_is_osmux(conn)) {
rc = msgb_printf(sdp, "X-Osmux: %u\r\n", conn->osmux.cid);
rc = msgb_printf(sdp, MGCP_X_OSMO_OSMUX_HEADER " %u\r\n", conn->osmux.local_cid);
if (rc < 0)
goto error;
}
@ -324,8 +327,13 @@ error:
* osmux connection, send the dummy packet via OSMUX */
static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
if (conn->osmux.state != OSMUX_STATE_DISABLED)
osmux_send_dummy(endp, conn);
/* Avoid sending dummy packet if the remote address was not yet
* configured through CRCX/MDCX: */
if (!mgcp_rtp_end_remote_addr_available(&conn->end))
return;
if (mgcp_conn_rtp_is_osmux(conn))
osmux_send_dummy(conn);
else
mgcp_send_dummy(endp, conn);
}
@ -385,7 +393,10 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
/* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */
rq.pdata = &pdata;
rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname);
rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
if (!rq.wildcarded)
rq.null_endp = mgcp_endp_is_null(pdata.epname);
if (!rq.null_endp)
rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
rq.mgcp_cause = rc;
if (!rq.endp) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
@ -402,14 +413,14 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
rq.name, pdata.epname);
return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
}
} else {
} else if (!rq.null_endp) {
/* If the endpoint name suggests that the request refers to a specific endpoint, then the
* request cannot be handled and we must stop early. */
LOGP(DLMGCP, LOGL_NOTICE,
"%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
pdata.epname, -rq.mgcp_cause);
return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
}
} /* else: Handle special "null" endpoint below (with rq.endp=NULL, rq.trunk=NULL) */
} else {
osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name));
rq.trunk = rq.endp->trunk;
@ -455,6 +466,11 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq)
{
LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
/* Auditing "null" endpoint is allowed for keepalive purposes. There's no rq->endp nor rq->trunk in this case. */
if (rq->null_endp)
return create_ok_response(rq->pdata->cfg, NULL, 200, "AUEP", rq->pdata->trans);
if (!rq->endp || !mgcp_endp_avail(rq->endp)) {
LOGPENDP(rq->endp, DLMGCP, LOGL_ERROR, "AUEP: selected endpoint not available!\n");
return create_err_response(rq->trunk, NULL, 501, "AUEP", rq->pdata->trans);
@ -656,8 +672,8 @@ static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
case 'a':
/* FIXME: LCO also supports the negotiation of more than one codec.
* (e.g. a:PCMU;G726-32) But this implementation only supports a single
* codec only. */
if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
* codec only. Ignoring all but the first codec. */
if (sscanf(lco_id + 1, ":%16[^,;]", codec) == 1) {
talloc_free(lco->codec);
/* MGCP header is case insensive, and we'll need
codec in uppercase when using it later: */
@ -709,7 +725,7 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
"Configuring RTP endpoint: local port %d%s%s\n",
ntohs(rtp->rtp_port),
osmo_sockaddr_port(&rtp->addr.u.sa),
rtp->force_aligned_timing ? ", force constant timing" : "",
rtp->force_constant_ssrc ? ", force constant ssrc" : "");
}
@ -740,12 +756,12 @@ uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
*/
static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
{
if (!endp->trunk->cfg->osmux_init) {
if (osmux_init(OSMUX_ROLE_BSC, endp->trunk->cfg) < 0) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
if (!endp->trunk->cfg->osmux.initialized) {
if (osmux_init(endp->trunk) < 0) {
LOGPENDP(endp, DOSMUX, LOGL_ERROR, "Cannot init OSMUX\n");
return -3;
}
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
LOGPENDP(endp, DOSMUX, LOGL_NOTICE, "OSMUX socket has been set up\n");
}
return mgcp_parse_osmux_cid(line);
@ -756,6 +772,9 @@ static int handle_codec_info(struct mgcp_conn_rtp *conn,
struct mgcp_request_data *rq, int have_sdp, bool crcx)
{
struct mgcp_endpoint *endp = rq->endp;
struct mgcp_conn *conn_dst;
struct mgcp_conn_rtp *conn_dst_rtp;
int rc;
char *cmd;
@ -797,8 +816,15 @@ static int handle_codec_info(struct mgcp_conn_rtp *conn,
goto error;
}
/* 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;
else
conn_dst_rtp = NULL;
/* Make codec decision */
if (mgcp_codec_decide(conn) != 0)
if (mgcp_codec_decide(conn, conn_dst_rtp) != 0)
goto error;
return 0;
@ -840,13 +866,13 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
struct mgcp_parse_data *pdata = rq->pdata;
struct mgcp_trunk *trunk = rq->trunk;
struct mgcp_endpoint *endp = rq->endp;
struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
struct rate_ctr_group *rate_ctrs;
int error_code = 400;
const char *local_options = NULL;
const char *callid = NULL;
const char *mode = NULL;
char *line;
int have_sdp = 0, osmux_cid = -2;
int have_sdp = 0, remote_osmux_cid = -2;
struct mgcp_conn_rtp *conn = NULL;
struct mgcp_conn *_conn = NULL;
char conn_name[512];
@ -854,6 +880,14 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
if (rq->null_endp) {
/* trunk not available so rate_ctr aren't available either. */
LOGP(DLMGCP, LOGL_ERROR, "CRCX: Not allowed in 'null' endpoint!\n");
return create_err_response(pdata->cfg, NULL, 502, "CRCX", pdata->trans);
}
rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
/* we must have a free ep */
if (!endp) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
@ -893,9 +927,9 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
case 'X':
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
/* If osmux is disabled, just skip setting it up */
if (!rq->endp->trunk->cfg->osmux)
if (rq->endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF)
break;
osmux_cid = mgcp_osmux_setup(endp, line);
remote_osmux_cid = mgcp_osmux_setup(endp, line);
break;
}
@ -933,7 +967,7 @@ mgcp_header_done:
}
/* Check if we are able to accept the creation of another connection */
if (llist_count(&endp->conns) >= endp->type->max_conns) {
if (endp->type->max_conns > 0 && llist_count(&endp->conns) >= endp->type->max_conns) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"CRCX: endpoint full, max. %i connections allowed!\n",
endp->type->max_conns);
@ -998,16 +1032,17 @@ mgcp_header_done:
goto error2;
}
/* Annotate Osmux circuit ID and set it to negotiating state until this
* is fully set up from the dummy load. */
conn->osmux.state = OSMUX_STATE_DISABLED;
if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */
conn->osmux.state = OSMUX_STATE_ACTIVATING;
if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) {
/* 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) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
goto error2;
}
} else if (endp->trunk->cfg->osmux == OSMUX_USAGE_ONLY) {
if (remote_osmux_cid >= 0) {
conn->osmux.remote_cid_present = true;
conn->osmux.remote_cid = remote_osmux_cid;
}
} else if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_ONLY) {
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));
@ -1020,7 +1055,7 @@ mgcp_header_done:
&endp->local_options, local_options);
if (rc != 0) {
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
"CRCX: inavlid local connection options!\n");
"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));
goto error2;
@ -1041,9 +1076,6 @@ mgcp_header_done:
rc = mgcp_conn_iuup_init(conn);
}
conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
trunk->audio_fmtp_extra);
if (pdata->cfg->force_ptime) {
conn->end.packet_duration_ms = pdata->cfg->force_ptime;
conn->end.force_output_ptime = 1;
@ -1054,7 +1086,7 @@ mgcp_header_done:
/* check connection mode setting */
if (conn->conn->mode != MGCP_CONN_LOOPBACK
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
&& conn->end.rtp_port == 0) {
&& 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;
@ -1064,7 +1096,10 @@ mgcp_header_done:
/* Find a local address for conn based on policy and initial SDP remote
information, then find a free port for it */
mgcp_get_local_addr(conn->end.local_addr, conn);
if (mgcp_get_local_addr(conn->end.local_addr, conn) < 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
goto error2;
}
if (allocate_port(endp, conn) != 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
goto error2;
@ -1077,13 +1112,20 @@ mgcp_header_done:
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");
goto error2;
}
}
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
"CRCX: Creating connection: port: %u\n", conn->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 &&
mgcp_rtp_end_remote_addr_available(&conn->end) &&
trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
send_dummy(endp, conn);
@ -1108,7 +1150,7 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
struct mgcp_parse_data *pdata = rq->pdata;
struct mgcp_trunk *trunk = rq->trunk;
struct mgcp_endpoint *endp = rq->endp;
struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
struct rate_ctr_group *rate_ctrs;
char new_local_addr[INET6_ADDRSTRLEN];
int error_code = 500;
int silent = 0;
@ -1118,11 +1160,19 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
const char *mode = NULL;
struct mgcp_conn_rtp *conn = NULL;
const char *conn_id = NULL;
int osmux_cid = -2;
int remote_osmux_cid = -2;
int rc;
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
if (rq->null_endp) {
/* trunk not available so rate_ctr aren't available either. */
LOGP(DLMGCP, LOGL_ERROR, "MDCX: Not allowed in 'null' endpoint!\n");
return create_err_response(pdata->cfg, NULL, 502, "MDCX", pdata->trans);
}
rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
/* Prohibit wildcarded requests */
if (rq->wildcarded) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
@ -1174,9 +1224,9 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
case 'X':
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
/* If osmux is disabled, just skip setting it up */
if (!endp->trunk->cfg->osmux)
if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF)
break;
osmux_cid = mgcp_osmux_setup(endp, line);
remote_osmux_cid = mgcp_osmux_setup(endp, line);
break;
}
/* Ignore unknown X-headers */
@ -1240,6 +1290,10 @@ mgcp_header_done:
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);
/* check connection mode setting */
if (conn->conn->mode != MGCP_CONN_LOOPBACK
@ -1253,30 +1307,39 @@ mgcp_header_done:
}
if (mgcp_conn_rtp_is_osmux(conn)) {
OSMO_ASSERT(conn->osmux.cid_allocated);
if (osmux_cid < -1) {
OSMO_ASSERT(conn->osmux.local_cid_allocated);
if (remote_osmux_cid < -1) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"MDCX: Failed to parse Osmux CID!\n");
goto error3;
} else if (osmux_cid == -1) {
} else if (remote_osmux_cid == -1) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"MDCX: wilcard in MDCX is not supported!\n");
goto error3;
} else if (osmux_cid != (int) conn->osmux.cid) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"MDCX: changing already allocated CID 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 {
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,
"MDCX: Osmux handling failed!\n");
goto error3;
}
}
/* TODO: In the future (when we have recvCID!=sendCID), we need to
tell Osmux code that osmux_cid is to be used as sendCID for
that conn. */
}
/* MDCX may have provided a new remote address, which means we may need
to update our announced IP addr and re-bind our local end. This can
happen for instance if MGW initially provided an IPv4 during CRCX
ACK, and now MDCX tells us the remote has an IPv6 address. */
mgcp_get_local_addr(new_local_addr, conn);
if (mgcp_get_local_addr(new_local_addr, conn) < 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);
@ -1300,7 +1363,6 @@ mgcp_header_done:
/* 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 &&
mgcp_rtp_end_remote_addr_available(&conn->end) &&
trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
send_dummy(endp, conn);
@ -1326,7 +1388,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
struct mgcp_parse_data *pdata = rq->pdata;
struct mgcp_trunk *trunk = rq->trunk;
struct mgcp_endpoint *endp = rq->endp;
struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
struct rate_ctr_group *rate_ctrs;
int error_code = 400;
int silent = 0;
char *line;
@ -1336,10 +1398,18 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
unsigned int i;
/* NOTE: In this handler we can not take it for granted that the endp
* pointer will be populated, however a trunk is always guaranteed. */
* pointer will be populated, however a trunk is always guaranteed (except for 'null' endp).
*/
LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: deleting connection(s) ...\n");
if (rq->null_endp) {
/* trunk not available so rate_ctr aren't available either. */
LOGP(DLMGCP, LOGL_ERROR, "DLCX: Not allowed in 'null' endpoint!\n");
return create_err_response(pdata->cfg, NULL, 502, "DLCX", pdata->trans);
}
rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
if (endp && !mgcp_endp_avail(endp)) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
@ -1490,6 +1560,12 @@ static struct msgb *handle_rsip(struct mgcp_request_data *rq)
LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
if (rq->null_endp) {
/* trunk not available so rate_ctr aren't available either. */
LOGP(DLMGCP, LOGL_ERROR, "RSIP: Not allowed in 'null' endpoint!\n");
return create_err_response(rq->pdata->cfg, NULL, 502, "RSIP", rq->pdata->trans);
}
if (rq->pdata->cfg->reset_cb)
rq->pdata->cfg->reset_cb(rq->endp->trunk);
return NULL;
@ -1515,6 +1591,12 @@ static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
if (rq->null_endp) {
/* trunk not available so rate_ctr aren't available either. */
LOGP(DLMGCP, LOGL_ERROR, "RQNT: Not allowed in 'null' endpoint!\n");
return create_err_response(rq->pdata->cfg, NULL, 502, "RQNT", rq->pdata->trans);
}
for_each_line(line, rq->pdata->save) {
switch (toupper(line[0])) {
case 'S':
@ -1569,8 +1651,7 @@ static void mgcp_keepalive_timer_cb(void *_trunk)
struct mgcp_endpoint *endp = trunk->endpoints[i];
llist_for_each_entry(conn, &endp->conns, entry) {
if (conn->type == MGCP_CONN_TYPE_RTP &&
conn->mode == MGCP_CONN_RECV_ONLY &&
mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end))
conn->mode == MGCP_CONN_RECV_ONLY)
send_dummy(endp, &conn->u.rtp);
}
}
@ -1624,13 +1705,10 @@ struct mgcp_config *mgcp_config_alloc(void)
cfg->source_port = 2427;
osmo_strlcpy(cfg->source_addr, "0.0.0.0", sizeof(cfg->source_addr));
osmo_strlcpy(cfg->osmux_addr, "0.0.0.0", sizeof(cfg->osmux_addr));
cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
INIT_LLIST_HEAD(&cfg->trunks);
/* Allocate virtual trunk */

View File

@ -43,7 +43,7 @@ static const struct rate_ctr_desc mgcp_general_ctr_desc[] = {
{ "mgcp:err_rx_no_endpoint", "can't find MGCP endpoint, probably we've used all allocated endpoints." },
};
const static struct rate_ctr_group_desc mgcp_general_ctr_group_desc = {
static const struct rate_ctr_group_desc mgcp_general_ctr_group_desc = {
.group_name_prefix = "mgcp",
.group_description = "mgcp general statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
@ -71,7 +71,7 @@ static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
[MGCP_CRCX_FAIL_CLAIM] = { "crcx:claim", "endpoint can not be claimed." },
};
const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
static const struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
.group_name_prefix = "crcx",
.group_description = "crxc statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
@ -97,7 +97,7 @@ static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
[MGCP_MDCX_FAIL_AVAIL] = { "mdcx:unavailable", "endpoint unavailable." },
};
const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
static const struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
.group_name_prefix = "mdcx",
.group_description = "mdcx statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
@ -116,7 +116,7 @@ static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = {
[MGCP_DLCX_FAIL_AVAIL] = { "dlcx:unavailable", "endpoint unavailable." },
};
const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
static const struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
.group_name_prefix = "dlcx",
.group_description = "dlcx statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
@ -130,7 +130,7 @@ static const struct rate_ctr_desc e1_rate_ctr_desc[] = {
[E1_I460_TRAU_MUX_EMPTY_CTR] = { "e1:i460", "Outbound I.460 MUX queue empty." }
};
const static struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
static const struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
.group_name_prefix = "e1",
.group_description = "e1 statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
@ -138,7 +138,7 @@ const static struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
.ctr_desc = e1_rate_ctr_desc
};
const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
static const struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
.group_name_prefix = "all_rtp_conn",
.group_description = "aggregated statistics for all rtp connections",
.class_id = 1,
@ -146,6 +146,14 @@ const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
.ctr_desc = all_rtp_conn_rate_ctr_desc
};
static const struct rate_ctr_group_desc all_osmux_conn_rate_ctr_group_desc = {
.group_name_prefix = "all_osmux_conn",
.group_description = "aggregated statistics for all osmux connections",
.class_id = 1,
.num_ctr = ARRAY_SIZE(all_osmux_conn_rate_ctr_desc),
.ctr_desc = all_osmux_conn_rate_ctr_desc
};
/*! allocate global rate counters
* (called once at startup).
* \param[in] cfg mgw configuration for which the rate counters are allocated.
@ -191,6 +199,7 @@ int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk)
static atomic_uint mdcx_rate_ctr_index = 0;
static atomic_uint dlcx_rate_ctr_index = 0;
static atomic_uint all_rtp_conn_rate_ctr_index = 0;
static atomic_uint all_osmux_conn_rate_ctr_index = 0;
char ctr_name[256];
if (ratectr->mgcp_crcx_ctr_group == NULL) {
@ -229,6 +238,15 @@ int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk)
trunk->trunk_nr);
rate_ctr_group_set_name(ratectr->all_rtp_conn_stats, ctr_name);
}
if (ratectr->all_osmux_conn_stats == NULL) {
ratectr->all_osmux_conn_stats = rate_ctr_group_alloc(trunk, &all_osmux_conn_rate_ctr_group_desc,
all_osmux_conn_rate_ctr_index++);
if (!ratectr->all_osmux_conn_stats)
return -EINVAL;
snprintf(ctr_name, sizeof(ctr_name), "%s-%u:osmux_conn", mgcp_trunk_type_strs_str(trunk->trunk_type),
trunk->trunk_nr);
rate_ctr_group_set_name(ratectr->all_osmux_conn_stats, ctr_name);
}
/* E1 specific */
if (trunk->trunk_type == MGCP_TRUNK_E1 && ratectr->e1_stats == NULL) {
@ -265,6 +283,10 @@ void mgcp_ratectr_trunk_free(struct mgcp_trunk *trunk)
rate_ctr_group_free(ratectr->all_rtp_conn_stats);
ratectr->all_rtp_conn_stats = NULL;
}
if (ratectr->all_osmux_conn_stats) {
rate_ctr_group_free(ratectr->all_osmux_conn_stats);
ratectr->all_osmux_conn_stats = NULL;
}
/* E1 specific */
if (ratectr->e1_stats) {

View File

@ -239,13 +239,12 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp
if (delimiter == ';' || delimiter == ',')
str_ptr[strlen(str_ptr) - 1] = '\0';
/* AMR octet aligned parameter */
/* 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, " ");
@ -384,7 +383,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
case 'm':
rc = sscanf(line, "m=audio %d RTP/AVP", &port);
if (rc == 1) {
rtp->rtp_port = htons(port);
osmo_sockaddr_set_port(&rtp->addr.u.sa, port);
rtp->rtcp_port = htons(port + 1);
}
@ -432,7 +431,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
"Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
ntohs(rtp->rtp_port), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf),
osmo_sockaddr_port(&rtp->addr.u.sa), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf),
rtp->packet_duration_ms);
if (codecs_used == 0)
LOGPC(DLMGCP, LOGL_NOTICE, "none");
@ -489,34 +488,10 @@ static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_
}
/* Add fmtp strings to sdp payload */
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len,
const char *fmtp_extra)
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
{
unsigned int i;
int rc;
int fmtp_extra_pt = -1;
char *fmtp_extra_pars = "";
/* When no fmtp parameters ara available but an fmtp extra string
* is configured, just add the fmtp extra string */
if (fmtp_params_len == 0 && fmtp_extra) {
return msgb_printf(sdp, "%s\r\n", fmtp_extra);
}
/* When there is fmtp extra configured we dissect it in order to drop
* in the configured extra parameters at the right place when
* generating the fmtp strings. */
if (fmtp_extra) {
if (sscanf(fmtp_extra, "a=fmtp:%d ", &fmtp_extra_pt) != 1)
fmtp_extra_pt = -1;
fmtp_extra_pars = strstr(fmtp_extra, " ");
if (!fmtp_extra_pars)
fmtp_extra_pars = "";
else
fmtp_extra_pars++;
}
for (i = 0; i < fmtp_params_len; i++) {
rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
@ -533,13 +508,6 @@ static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsign
return -EINVAL;
}
/* Append extra parameters from fmtp extra */
if (fmtp_params[i].payload_type == fmtp_extra_pt) {
rc = msgb_printf(sdp, " %s", fmtp_extra_pars);
if (rc < 0)
return -EINVAL;
}
rc = msgb_printf(sdp, "\r\n");
if (rc < 0)
return -EINVAL;
@ -559,7 +527,6 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
const char *addr)
{
const struct mgcp_rtp_codec *codec;
const char *fmtp_extra;
const char *audio_name;
int payload_type;
struct sdp_fmtp_param fmtp_param;
@ -575,10 +542,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
OSMO_ASSERT(sdp);
OSMO_ASSERT(addr);
/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
endp->trunk->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
&codec, &fmtp_extra,
(struct mgcp_conn_rtp *)conn);
codec = conn->end.codec;
audio_name = codec->audio_name;
payload_type = codec->payload_type;
@ -601,7 +565,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
payload_types[0] = payload_type;
if (mgcp_conn_rtp_is_osmux(conn))
local_port = endp->trunk->cfg->osmux_port;
local_port = endp->trunk->cfg->osmux.local_port;
else
local_port = conn->end.local_port;
rc = add_audio(sdp, payload_types, 1, local_port);
@ -620,7 +584,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
fmtp_params[0] = fmtp_param;
fmtp_params_len = 1;
}
rc = add_fmtp(sdp, fmtp_params, fmtp_params_len, fmtp_extra);
rc = add_fmtp(sdp, fmtp_params, fmtp_params_len);
if (rc < 0)
goto buffer_too_small;
}

View File

@ -39,7 +39,7 @@ __attribute__((no_sanitize("undefined")))
void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss)
{
struct mgcp_rtp_state *state = &conn->state;
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
*expected = state->stats.cycles + state->stats.max_seq;
*expected = *expected - state->stats.base_seq + 1;
@ -80,10 +80,10 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
int ploss;
int nchars;
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
struct rate_ctr *octets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_RX_CTR);
struct rate_ctr *packets_tx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_TX_CTR);
struct rate_ctr *octets_tx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_TX_CTR);
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
struct rate_ctr *octets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_RX_CTR);
struct rate_ctr *packets_tx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_TX_CTR);
struct rate_ctr *octets_tx = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_TX_CTR);
calc_loss(conn, &expected, &ploss);
jitter = calc_jitter(&conn->state);
@ -99,7 +99,7 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
str += nchars;
str_len -= nchars;
if (conn->conn->endp->trunk->cfg->osmux != OSMUX_USAGE_OFF) {
if (conn->conn->endp->trunk->cfg->osmux.usage != OSMUX_USAGE_OFF) {
/* Error Counter */
nchars = snprintf(str, str_len,
"\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64,
@ -112,9 +112,12 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
str_len -= nchars;
if (conn->osmux.state == OSMUX_STATE_ENABLED) {
struct rate_ctr *osmux_chunks_rx, *osmux_octets_rx;
osmux_chunks_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_CHUNKS_RX_CTR);
osmux_octets_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_OCTETS_RX_CTR);
snprintf(str, str_len,
"\r\nX-Osmux-ST: CR=%u, BR=%u",
conn->osmux.stats.chunks, conn->osmux.stats.octets);
"\r\nX-Osmux-ST: CR=%" PRIu64 ", BR=%" PRIu64,
osmux_chunks_rx->current, osmux_octets_rx->current);
}
}

View File

@ -111,17 +111,12 @@ static int config_write_mgcp(struct vty *vty)
VTY_NEWLINE);
} else
vty_out(vty, " no rtp-patch%s", VTY_NEWLINE);
if (trunk->audio_fmtp_extra)
vty_out(vty, " sdp audio fmtp-extra %s%s",
trunk->audio_fmtp_extra, VTY_NEWLINE);
vty_out(vty, " %ssdp audio-payload send-ptime%s",
trunk->audio_send_ptime ? "" : "no ", VTY_NEWLINE);
vty_out(vty, " %ssdp audio-payload send-name%s",
trunk->audio_send_name ? "" : "no ", VTY_NEWLINE);
vty_out(vty, " number endpoints %u%s",
trunk->v.vty_number_endpoints, VTY_NEWLINE);
vty_out(vty, " %sallow-transcoding%s",
trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE);
if (strlen(g_cfg->call_agent_addr))
vty_out(vty, " call-agent ip %s%s", g_cfg->call_agent_addr,
VTY_NEWLINE);
@ -129,7 +124,7 @@ static int config_write_mgcp(struct vty *vty)
vty_out(vty, " rtp force-ptime %d%s", g_cfg->force_ptime,
VTY_NEWLINE);
switch (g_cfg->osmux) {
switch (g_cfg->osmux.usage) {
case OSMUX_USAGE_ON:
vty_out(vty, " osmux on%s", VTY_NEWLINE);
break;
@ -141,17 +136,23 @@ static int config_write_mgcp(struct vty *vty)
vty_out(vty, " osmux off%s", VTY_NEWLINE);
break;
}
if (g_cfg->osmux) {
vty_out(vty, " osmux bind-ip %s%s",
g_cfg->osmux_addr, VTY_NEWLINE);
if (g_cfg->osmux.usage != OSMUX_USAGE_OFF) {
if (g_cfg->osmux.local_addr_v4)
vty_out(vty, " osmux bind-ip %s%s",
g_cfg->osmux.local_addr_v4, VTY_NEWLINE);
if (g_cfg->osmux.local_addr_v6)
vty_out(vty, " osmux bind-ip-v6 %s%s",
g_cfg->osmux.local_addr_v6, VTY_NEWLINE);
vty_out(vty, " osmux batch-factor %d%s",
g_cfg->osmux_batch, VTY_NEWLINE);
g_cfg->osmux.batch_factor, VTY_NEWLINE);
vty_out(vty, " osmux batch-size %u%s",
g_cfg->osmux_batch_size, VTY_NEWLINE);
g_cfg->osmux.batch_size, VTY_NEWLINE);
vty_out(vty, " osmux port %u%s",
g_cfg->osmux_port, VTY_NEWLINE);
g_cfg->osmux.local_port, VTY_NEWLINE);
vty_out(vty, " osmux dummy %s%s",
g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);
g_cfg->osmux.dummy_padding ? "on" : "off", VTY_NEWLINE);
vty_out(vty, " osmux peer-behind-nat %s%s",
g_cfg->osmux.peer_behind_nat ? "on" : "off", VTY_NEWLINE);
}
if (g_cfg->conn_timeout)
@ -169,11 +170,11 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
struct rate_ctr *rx_packets, *rx_bytes;
struct rate_ctr *dropped_packets;
tx_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_TX_CTR);
tx_bytes = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_TX_CTR);
rx_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
rx_bytes = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_RX_CTR);
dropped_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_DROPPED_PACKETS_CTR);
tx_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_TX_CTR);
tx_bytes = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_TX_CTR);
rx_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
rx_bytes = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_RX_CTR);
dropped_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_DROPPED_PACKETS_CTR);
vty_out(vty,
" Packets Sent: %" PRIu64 " (%" PRIu64 " bytes total)%s"
@ -183,20 +184,44 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
" Payload Type: %d Rate: %u Channels: %d %s"
" Frame Duration: %u Frame Denominator: %u%s"
" FPP: %d Packet Duration: %u%s"
" FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s"
" Audio-Name: %s Sub-Type: %s%s"
" Output-Enabled: %d Force-PTIME: %d%s",
tx_packets->current, tx_bytes->current, VTY_NEWLINE,
rx_packets->current, rx_bytes->current, VTY_NEWLINE,
state->in_stream.err_ts_ctr->current,
state->out_stream.err_ts_ctr->current,
VTY_NEWLINE,
VTY_NEWLINE,
dropped_packets->current, VTY_NEWLINE,
codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
codec->frame_duration_num, codec->frame_duration_den,
VTY_NEWLINE, end->frames_per_packet, end->packet_duration_ms,
VTY_NEWLINE, end->fmtp_extra, codec->audio_name,
VTY_NEWLINE, codec->audio_name,
codec->subtype_name, VTY_NEWLINE, end->output_enabled,
end->force_output_ptime, VTY_NEWLINE);
if (mgcp_conn_rtp_is_osmux(conn)) {
struct rate_ctr *rx_chunks, *rx_octets, *rtp_tx, *rtp_tx_dropped, *octets_tx;
rx_chunks = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_CHUNKS_RX_CTR);
rx_octets = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_OCTETS_RX_CTR);
rtp_tx = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_RTP_PACKETS_TX_CTR);
rtp_tx_dropped = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
octets_tx = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_AMR_OCTETS_TX_CTR);
vty_out(vty,
" Osmux:%s"
" State: %s%s"
" Local CID: %d%s"
" Remote CID: %d%s"
" Chunks received: %" PRIu64 " (%" PRIu64 " bytes total)%s"
" RTP Packets encoded (Tx): %" PRIu64 " (%" PRIu64 " AMR octets total)%s"
" AMR payloads Dropped (Tx): %" PRIu64 "%s",
VTY_NEWLINE, osmux_state_str(conn->osmux.state), VTY_NEWLINE,
conn->osmux.local_cid_allocated ? conn->osmux.local_cid : -1, VTY_NEWLINE,
conn->osmux.remote_cid_present ? conn->osmux.remote_cid : -1, VTY_NEWLINE,
rx_chunks->current, rx_octets->current, VTY_NEWLINE,
rtp_tx->current, octets_tx->current, VTY_NEWLINE,
rtp_tx_dropped->current, VTY_NEWLINE
);
}
}
static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp,
@ -289,6 +314,14 @@ static void dump_ratectr_trunk(struct vty *vty, struct mgcp_trunk *trunk)
" %25n: %10c (%S/s %M/m %H/h %D/d) %d",
ratectr->all_rtp_conn_stats);
}
if (ratectr->all_osmux_conn_stats) {
vty_out(vty, " %s:%s",
ratectr->all_osmux_conn_stats->desc->group_description,
VTY_NEWLINE);
vty_out_rate_ctr_group_fmt(vty,
" %25n: %10c (%S/s %M/m %H/h %D/d) %d",
ratectr->all_osmux_conn_stats);
}
if (ratectr->e1_stats && trunk->trunk_type == MGCP_TRUNK_E1) {
vty_out(vty, " %s:%s",
@ -342,7 +375,7 @@ static int mgcp_show(struct vty *vty, int argc, const char **argv,
llist_for_each_entry(trunk, &g_cfg->trunks, entry)
dump_trunk(vty, trunk, show_stats, active_only);
if (g_cfg->osmux)
if (g_cfg->osmux.usage != OSMUX_USAGE_OFF)
vty_out(vty, "Osmux used CID: %d%s", osmux_cid_pool_count_used(),
VTY_NEWLINE);
@ -475,6 +508,7 @@ DEFUN_DEPRECATED(cfg_mgcp_bind_early,
BIND_STR
"Bind local ports on start up\n" "Bind on demand\n" "Bind on startup\n")
{
vty_out(vty, "%% Deprecated 'bind early (0|1)' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@ -644,53 +678,40 @@ DEFUN_USRATTR(cfg_mgcp_no_rtp_force_ptime,
return CMD_SUCCESS;
}
DEFUN_USRATTR(cfg_mgcp_sdp_fmtp_extra,
cfg_mgcp_sdp_fmtp_extra_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"sdp audio fmtp-extra .NAME",
"Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n"
"Extra Information\n")
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
OSMO_ASSERT(trunk);
char *txt = argv_concat(argv, argc, 0);
if (!txt)
return CMD_WARNING;
osmo_talloc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt);
talloc_free(txt);
return CMD_SUCCESS;
}
DEFUN_USRATTR(cfg_mgcp_allow_transcoding,
cfg_mgcp_allow_transcoding_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"allow-transcoding", "Allow transcoding\n")
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
OSMO_ASSERT(trunk);
trunk->no_audio_transcoding = 0;
return CMD_SUCCESS;
}
DEFUN_USRATTR(cfg_mgcp_no_allow_transcoding,
cfg_mgcp_no_allow_transcoding_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"no allow-transcoding", NO_STR "Allow transcoding\n")
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
OSMO_ASSERT(trunk);
trunk->no_audio_transcoding = 1;
return CMD_SUCCESS;
}
#define SDP_STR "SDP File related options\n"
#define AUDIO_STR "Audio payload options\n"
DEFUN_DEPRECATED(cfg_mgcp_sdp_fmtp_extra,
cfg_mgcp_sdp_fmtp_extra_cmd,
"sdp audio fmtp-extra .NAME",
SDP_STR AUDIO_STR "Deprecated, without effect since osmo-mgw v1.13\n" "Deprecated, without effect\n")
{
vty_out(vty, "%% deprecated: the config option 'sdp audio fmtp-extra' has been removed.%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_mgcp_allow_transcoding,
cfg_mgcp_allow_transcoding_cmd,
"allow-transcoding", "Allow transcoding\n")
{
vty_out(vty, "%% Deprecated 'allow-transcoding' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_mgcp_no_allow_transcoding,
cfg_mgcp_no_allow_transcoding_cmd,
"no allow-transcoding", NO_STR "Allow transcoding\n")
{
vty_out(vty, "%% Deprecated 'no allow-transcoding' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_mgcp_sdp_payload_number,
cfg_mgcp_sdp_payload_number_cmd,
"sdp audio-payload number <0-255>",
SDP_STR AUDIO_STR "Number\n" "Payload number\n")
{
vty_out(vty, "%% Deprecated 'sdp audio-payload number <0-255>' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@ -704,6 +725,7 @@ DEFUN_DEPRECATED(cfg_mgcp_sdp_payload_name,
"sdp audio-payload name NAME",
SDP_STR AUDIO_STR "Name\n" "Payload name\n")
{
vty_out(vty, "%% Deprecated 'sdp audio-payload name NAME' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@ -764,6 +786,7 @@ DEFUN_DEPRECATED(cfg_mgcp_loop,
"loop (0|1)",
"Loop audio for all endpoints on main trunk\n" "Don't Loop\n" "Loop\n")
{
vty_out(vty, "%% Deprecated 'loop (0|1)' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@ -1028,30 +1051,17 @@ static int config_write_trunk(struct vty *vty)
VTY_NEWLINE);
} else
vty_out(vty, " no rtp-patch%s", VTY_NEWLINE);
if (trunk->audio_fmtp_extra)
vty_out(vty, " sdp audio fmtp-extra %s%s",
trunk->audio_fmtp_extra, VTY_NEWLINE);
vty_out(vty, " %sallow-transcoding%s",
trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE);
}
return CMD_SUCCESS;
}
DEFUN_USRATTR(cfg_trunk_sdp_fmtp_extra,
DEFUN_DEPRECATED(cfg_trunk_sdp_fmtp_extra,
cfg_trunk_sdp_fmtp_extra_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"sdp audio fmtp-extra .NAME",
"Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n"
"Extra Information\n")
SDP_STR AUDIO_STR "Deprecated, without effect since osmo-mgw v1.13\n" "Deprecated, without effect\n")
{
struct mgcp_trunk *trunk = vty->index;
char *txt = argv_concat(argv, argc, 0);
if (!txt)
return CMD_WARNING;
osmo_talloc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt);
talloc_free(txt);
vty_out(vty, "%% deprecated: the config option 'sdp audio fmtp-extra' has been removed.%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@ -1060,6 +1070,7 @@ DEFUN_DEPRECATED(cfg_trunk_payload_number,
"sdp audio-payload number <0-255>",
SDP_STR AUDIO_STR "Number\n" "Payload Number\n")
{
vty_out(vty, "%% Deprecated 'sdp audio-payload number <0-255>' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@ -1072,6 +1083,7 @@ DEFUN_DEPRECATED(cfg_trunk_payload_name,
"sdp audio-payload name NAME",
SDP_STR AUDIO_STR "Payload\n" "Payload Name\n")
{
vty_out(vty, "%% Deprecated 'sdp audio-payload name NAME' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@ -1084,6 +1096,7 @@ DEFUN_DEPRECATED(cfg_trunk_loop,
"loop (0|1)",
"Loop audio for all endpoints on this trunk\n" "Don't Loop\n" "Loop\n")
{
vty_out(vty, "%% Deprecated 'loop (0|1)' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@ -1281,23 +1294,19 @@ DEFUN_ATTR(cfg_trunk_no_rtp_keepalive,
return CMD_SUCCESS;
}
DEFUN_USRATTR(cfg_trunk_allow_transcoding,
cfg_trunk_allow_transcoding_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"allow-transcoding", "Allow transcoding\n")
DEFUN_DEPRECATED(cfg_trunk_allow_transcoding,
cfg_trunk_allow_transcoding_cmd,
"allow-transcoding", "Allow transcoding\n")
{
struct mgcp_trunk *trunk = vty->index;
trunk->no_audio_transcoding = 0;
vty_out(vty, "%% Deprecated 'allow-transcoding' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN_USRATTR(cfg_trunk_no_allow_transcoding,
cfg_trunk_no_allow_transcoding_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"no allow-transcoding", NO_STR "Allow transcoding\n")
DEFUN_DEPRECATED(cfg_trunk_no_allow_transcoding,
cfg_trunk_no_allow_transcoding_cmd,
"no allow-transcoding", NO_STR "Allow transcoding\n")
{
struct mgcp_trunk *trunk = vty->index;
trunk->no_audio_transcoding = 1;
vty_out(vty, "%% Deprecated 'no allow-transcoding' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@ -1539,12 +1548,12 @@ DEFUN(cfg_mgcp_osmux,
OSMO_ASSERT(trunk);
if (strcmp(argv[0], "off") == 0) {
g_cfg->osmux = OSMUX_USAGE_OFF;
g_cfg->osmux.usage = OSMUX_USAGE_OFF;
return CMD_SUCCESS;
} else if (strcmp(argv[0], "on") == 0)
g_cfg->osmux = OSMUX_USAGE_ON;
g_cfg->osmux.usage = OSMUX_USAGE_ON;
else if (strcmp(argv[0], "only") == 0)
g_cfg->osmux = OSMUX_USAGE_ONLY;
g_cfg->osmux.usage = OSMUX_USAGE_ONLY;
return CMD_SUCCESS;
@ -1552,12 +1561,29 @@ DEFUN(cfg_mgcp_osmux,
DEFUN(cfg_mgcp_osmux_ip,
cfg_mgcp_osmux_ip_cmd,
"osmux bind-ip " VTY_IPV46_CMD,
"osmux bind-ip " VTY_IPV4_CMD,
OSMUX_STR IP_STR
"IPv4 Address to bind to\n")
{
osmo_talloc_replace_string(g_cfg, &g_cfg->osmux.local_addr_v4, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_osmux_ip_v6,
cfg_mgcp_osmux_ip_v6_cmd,
"osmux bind-ip-v6 " VTY_IPV6_CMD,
OSMUX_STR IP_STR
"IPv4 Address to bind to\n"
"IPv6 Address to bind to\n")
{
osmo_strlcpy(g_cfg->osmux_addr, argv[0], sizeof(g_cfg->osmux_addr));
osmo_talloc_replace_string(g_cfg, &g_cfg->osmux.local_addr_v6, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_osmux_port,
cfg_mgcp_osmux_port_cmd,
"osmux port <1-65535>", OSMUX_STR "port\n" "UDP port\n")
{
g_cfg->osmux.local_port = atoi(argv[0]);
return CMD_SUCCESS;
}
@ -1566,7 +1592,7 @@ DEFUN(cfg_mgcp_osmux_batch_factor,
"osmux batch-factor <1-8>",
OSMUX_STR "Batching factor\n" "Number of messages in the batch\n")
{
g_cfg->osmux_batch = atoi(argv[0]);
g_cfg->osmux.batch_factor = atoi(argv[0]);
return CMD_SUCCESS;
}
@ -1575,15 +1601,7 @@ DEFUN(cfg_mgcp_osmux_batch_size,
"osmux batch-size <1-65535>",
OSMUX_STR "batch size\n" "Batch size in bytes\n")
{
g_cfg->osmux_batch_size = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_osmux_port,
cfg_mgcp_osmux_port_cmd,
"osmux port <1-65535>", OSMUX_STR "port\n" "UDP port\n")
{
g_cfg->osmux_port = atoi(argv[0]);
g_cfg->osmux.batch_size = atoi(argv[0]);
return CMD_SUCCESS;
}
@ -1594,9 +1612,24 @@ DEFUN(cfg_mgcp_osmux_dummy,
"Disable dummy padding\n")
{
if (strcmp(argv[0], "on") == 0)
g_cfg->osmux_dummy = 1;
g_cfg->osmux.dummy_padding = true;
else if (strcmp(argv[0], "off") == 0)
g_cfg->osmux_dummy = 0;
g_cfg->osmux.dummy_padding = false;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_osmux_peer_behind_nat,
cfg_mgcp_osmux_peer_behind_nat_cmd,
"osmux peer-behind-nat (on|off)",
OSMUX_STR "Define whether peer is behind NAT\n"
"Peer is behind NAT\n"
"Peer is NOT behind NAT\n")
{
if (strcmp(argv[0], "on") == 0)
g_cfg->osmux.peer_behind_nat = true;
else if (strcmp(argv[0], "off") == 0)
g_cfg->osmux.peer_behind_nat = false;
return CMD_SUCCESS;
}
@ -1686,10 +1719,12 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_name_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_v6_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_factor_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_size_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_peer_behind_nat_cmd);
install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
install_element(MGCP_NODE, &cfg_mgcp_domain_cmd);
@ -1734,9 +1769,9 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
int rc;
struct mgcp_trunk *trunk;
cfg->osmux_port = OSMUX_PORT;
cfg->osmux_batch = 4;
cfg->osmux_batch_size = OSMUX_BATCH_DEFAULT_MAX;
cfg->osmux.local_port = OSMUX_DEFAULT_PORT;
cfg->osmux.batch_factor = 4;
cfg->osmux.batch_size = OSMUX_BATCH_DEFAULT_MAX;
g_cfg = cfg;
rc = vty_read_config_file(config_file, NULL);

View File

@ -41,7 +41,6 @@
#include <osmocom/mgcp/debug.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_ctrl.h>
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
@ -51,7 +50,7 @@
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/socket.h>
#include <osmocom/ctrl/control_if.h>
#include <osmocom/ctrl/control_vty.h>
#include <osmocom/vty/telnet_interface.h>
@ -83,7 +82,7 @@ static int daemonize = 0;
const char *osmomgw_copyright =
"Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
"Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
"Copyright (C) 2017-2022 by sysmocom s.f.m.c. GmbH\r\n"
"Contributions by Pablo Neira Ayuso, Jacob Erlbeck, Neels Hofmeyr\r\n"
"Philipp Maier\r\n\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
@ -95,7 +94,7 @@ static char *config_file = "osmo-mgw.cfg";
/* used by msgb and mgcp */
void *tall_mgw_ctx = NULL;
static void print_help()
static void print_help(void)
{
printf("Some useful options:\n");
printf(" -h --help is printing this text.\n");
@ -281,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",
@ -295,13 +319,22 @@ static const struct log_info_cat log_categories[] = {
.name = "DRTP",
.description = "RTP stream handling",
.color = "\033[1;30m",
.enabled = 1,.loglevel = LOGL_NOTICE,
.enabled = 1,
.loglevel = LOGL_NOTICE,
},
[DE1] = {
.name = "DE1",
.description = "E1 line handling",
.color = "\033[1;31m",
.enabled = 1,.loglevel = LOGL_NOTICE,
.enabled = 1,
.loglevel = LOGL_NOTICE,
},
[DOSMUX] = {
.name = "DOSMUX",
.description = "Osmux (Osmocom RTP multiplexing)",
.color = "\033[1;32m",
.enabled = 1,
.loglevel = LOGL_NOTICE,
},
};
@ -320,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);
@ -352,12 +387,11 @@ int main(int argc, char **argv)
return rc;
/* start telnet after reading config for vty_get_bind_addr() */
rc = telnet_init_dynif(tall_mgw_ctx, NULL,
vty_get_bind_addr(), OSMO_VTY_PORT_MGW);
rc = telnet_init_default(tall_mgw_ctx, NULL, OSMO_VTY_PORT_MGW);
if (rc < 0)
return rc;
cfg->ctrl = mgw_ctrl_interface_setup(cfg, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);
cfg->ctrl = ctrl_interface_setup(cfg, OSMO_CTRL_PORT_MGW, NULL);
if (!cfg->ctrl) {
fprintf(stderr, "Failed to init the control interface on %s:%u. Exiting\n",
ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);

View File

@ -34,11 +34,11 @@ DISTCLEANFILES = \
$(NULL)
if ENABLE_EXT_TESTS
python-tests: $(BUILT_SOURCES)
python-tests: $(top_builddir)/src/osmo-mgw/osmo-mgw
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
else
python-tests: $(BUILT_SOURCES)
python-tests:
echo "Not running python-based tests (determined at configure-time)"
endif

View File

@ -18,6 +18,7 @@ AM_CFLAGS = \
AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
-no-install \
$(NULL)
EXTRA_DIST = \
@ -44,3 +45,6 @@ mgcp_test_LDADD = \
$(LIBOSMONETIF_LIBS) \
-lm \
$(NULL)
update_exp:
$(builddir)/mgcp_test >$(srcdir)/mgcp_test.ok

View File

@ -77,6 +77,8 @@ static void test_strline(void)
#define AUEP1_RET "500 158663169 FAIL\r\n"
#define AUEP2 "AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0\r\n"
#define AUEP2_RET "500 18983213 FAIL\r\n"
#define AUEP_NULL "AUEP 18983215 null@mgw MGCP 1.0\r\n"
#define AUEP_NULL_RET "200 18983215 OK\r\n"
#define EMPTY "\r\n"
#define EMPTY_RET NULL
#define SHORT "CRCX \r\n"
@ -125,7 +127,6 @@ 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=fmtp:126 0/1/2\r\n" \
"a=ptime:40\r\n"
#define MDCX4_ADDR0000 \
@ -274,6 +275,12 @@ static void test_strline(void)
#define MDCX_TOO_LONG_CI_RET "510 18983224 FAIL\r\n"
#define MDCX_NULL \
"MDCX 9 null@mgw MGCP 1.0\r\n" \
"I: %s\n"
#define MDCX_NULL_RET "502 9 FAIL\r\n"
#define SHORT2 "CRCX 1"
#define SHORT2_RET "510 000000 FAIL\r\n"
#define SHORT3 "CRCX 1 1@mgw"
@ -328,7 +335,6 @@ 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=fmtp:126 0/1/2\r\n" \
"a=ptime:40\r\n"
#define CRCX_ZYN \
@ -392,6 +398,13 @@ static void test_strline(void)
#define DLCX_RET_OSMUX DLCX_RET \
"X-Osmo-CP: EC TI=0, TO=0\r\n"
#define DLCX_NULL \
"DLCX 8 null@mgw MGCP 1.0\r\n" \
"I: %s\r\n" \
"C: 2\r\n"
#define DLCX_NULL_RET "502 8 FAIL\r\n"
#define RQNT \
"RQNT 186908780 1@mgw MGCP 1.0\r\n" \
"X: B244F267488\r\n" \
@ -405,6 +418,13 @@ static void test_strline(void)
#define RQNT1_RET "200 186908780 OK\r\n"
#define RQNT2_RET "200 186908781 OK\r\n"
#define RQNT_NULL \
"RQNT 186908782 null@mgw MGCP 1.0\r\n" \
"X: B244F267488\r\n" \
"S: D/9\r\n"
#define RQNT_NULL_RET "502 186908782 FAIL\r\n"
#define PTYPE_IGNORE 0 /* == default initializer */
#define PTYPE_NONE 128
#define PTYPE_NYI PTYPE_NONE
@ -545,12 +565,25 @@ static void test_strline(void)
"m=audio 16008 RTP/AVP 0\r\n" \
"a=ptime:20\r\n"
#define CRCX_NULL \
"CRCX 2 null@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 5904 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:40\r\n"
#define CRCX_NULL_RET "502 2 FAIL\r\n"
struct mgcp_test {
const char *name;
const char *req;
const char *exp_resp;
int ptype;
const char *extra_fmtp;
};
static const struct mgcp_test tests[] = {
@ -578,14 +611,18 @@ static const struct mgcp_test tests[] = {
{"RQNT1", RQNT, RQNT1_RET},
{"RQNT2", RQNT2, RQNT2_RET},
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE},
{"CRCX", CRCX, CRCX_FMTP_RET, 97,.extra_fmtp = "a=fmtp:126 0/1/2"},
{"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE,.extra_fmtp =
"a=fmtp:126 0/1/2"},
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE,.extra_fmtp = "a=fmtp:126 0/1/2"},
{"CRCX", CRCX, CRCX_FMTP_RET, 97},
{"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE},
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE},
{"CRCX", CRCX_NO_LCO_NO_SDP, CRCX_NO_LCO_NO_SDP_RET, 97},
{"CRCX", CRCX_X_OSMO_IGN, CRCX_X_OSMO_IGN_RET, 97},
{"MDCX_TOO_LONG_CI", MDCX_TOO_LONG_CI, MDCX_TOO_LONG_CI_RET},
{"CRCX", CRCX_AMR_WITH_FMTP, CRCX_AMR_WITH_FMTP_RET},
{"AUEP_NULL", AUEP_NULL, AUEP_NULL_RET},
{"CRCX_NULL", CRCX_NULL, CRCX_NULL_RET},
{"MDCX_NULL", MDCX_NULL, MDCX_NULL_RET},
{"DLCX_NULL", DLCX_NULL, DLCX_NULL_RET},
{"RQNT_NULL", RQNT_NULL, RQNT_NULL_RET},
};
static const struct mgcp_test retransmit[] = {
@ -616,12 +653,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) {
@ -635,6 +673,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;
}
@ -790,9 +830,6 @@ static void test_messages(void)
dummy_packets = 0;
osmo_talloc_replace_string(cfg, &trunk->audio_fmtp_extra,
t->extra_fmtp);
inp = create_msg(t->req, last_conn_id);
msg = mgcp_handle_message(cfg, inp);
msgb_free(inp);
@ -838,7 +875,7 @@ static void test_messages(void)
if (endp->local_options.pkt_period_min ||
endp->local_options.pkt_period_max)
printf
("Requested packetetization period: "
("Requested packetization period: "
"%d-%d\n",
endp->local_options.pkt_period_min,
endp->
@ -1071,7 +1108,7 @@ static void test_packet_loss_calc(void)
"test-connection");
conn = mgcp_conn_get_rtp(&endp, _conn->id);
state = &conn->state;
packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
state->stats.initialized = 1;
state->stats.base_seq = pl_test_dat[i].base_seq;
@ -1467,7 +1504,6 @@ static void test_multilple_codec(void)
/* Allocate 5@mgw and let osmo-mgw pick a codec from the list */
last_endpoint[0] = '\0';
inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);
trunk->no_audio_transcoding = 1;
resp = mgcp_handle_message(cfg, inp);
OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
sizeof(conn_id)) == 0);
@ -1492,7 +1528,7 @@ static void test_multilple_codec(void)
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 3);
OSMO_ASSERT(conn->end.rtp_port == htons(16434));
OSMO_ASSERT(osmo_sockaddr_port(&conn->end.addr.u.sa) == 16434);
memset(&addr, 0, sizeof(addr));
inet_aton("8.8.8.8", &addr);
OSMO_ASSERT(conn->end.addr.u.sa.sa_family == AF_INET);
@ -1511,7 +1547,6 @@ static void test_multilple_codec(void)
last_endpoint[0] = '\0';
inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);
trunk->no_audio_transcoding = 0;
resp = mgcp_handle_message(cfg, inp);
OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
sizeof(conn_id)) == 0);
@ -1632,7 +1667,8 @@ static void test_osmux_cid(void)
for (i = 0; i < 256; ++i) {
id = osmux_cid_pool_get_next();
OSMO_ASSERT(id == i);
/* We called osmux_cid_pool_get_next() above, so next CID is i+1. */
OSMO_ASSERT(id == ((i + 1) & 0xff));
OSMO_ASSERT(osmux_cid_pool_count_used() == i + 1);
}
@ -1768,26 +1804,25 @@ static const struct mgcp_codec_param amr_param_octet_aligned_unset = {
.amr_octet_aligned_present = false,
};
struct testcase_mgcp_codec_pt_translate_codec {
struct testcase_mgcp_codec_decide_codec {
int payload_type;
const char *audio_name;
const struct mgcp_codec_param *param;
int expect_rc;
};
struct testcase_mgcp_codec_pt_translate_expect {
bool end;
struct testcase_mgcp_codec_decide_expect {
int payload_type_map[2];
};
struct testcase_mgcp_codec_pt_translate {
struct testcase_mgcp_codec_decide {
const char *descr;
/* two conns on an endpoint, each with N configured codecs */
struct testcase_mgcp_codec_pt_translate_codec codecs[2][10];
struct testcase_mgcp_codec_pt_translate_expect expect[32];
struct testcase_mgcp_codec_decide_codec codecs[2][10];
struct testcase_mgcp_codec_decide_expect expect[2];
};
static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translate_cases[] = {
static const struct testcase_mgcp_codec_decide test_mgcp_codec_find_convertible_cases[] = {
{
.descr = "same order, but differing payload type numbers",
.codecs = {
@ -1804,10 +1839,7 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
},
.expect = {
{ .payload_type_map = {112, 96}, },
{ .payload_type_map = {0, 0}, },
{ .payload_type_map = {111, 97} },
{ .payload_type_map = {123, -EINVAL} },
{ .end = true },
{ .payload_type_map = {112, 96}, },
},
},
{
@ -1825,11 +1857,8 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
},
},
.expect = {
{ .payload_type_map = {112, 96}, },
{ .payload_type_map = {0, 0}, },
{ .payload_type_map = {111, 97} },
{ .payload_type_map = {123, -EINVAL} },
{ .end = true },
{ .payload_type_map = {111, 97}, },
},
},
{
@ -1847,10 +1876,8 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
},
},
.expect = {
{ .payload_type_map = {96, 97}, },
{ .payload_type_map = {97, 96}, },
{ .payload_type_map = {0, 0}, },
{ .end = true },
{ .payload_type_map = {96, 97}, },
},
},
{
@ -1866,10 +1893,8 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
},
},
.expect = {
{ .payload_type_map = {112, -EINVAL}, },
{ .payload_type_map = {0, -EINVAL}, },
{ .payload_type_map = {111, -EINVAL} },
{ .end = true },
{ .payload_type_map = {-EINVAL, -EINVAL}, },
{ .payload_type_map = {-EINVAL, 96}, },
},
},
{
@ -1886,28 +1911,60 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
},
.expect = {
{ .payload_type_map = {112, -EINVAL}, },
{ .payload_type_map = {0, -EINVAL}, },
{ .payload_type_map = {111, -EINVAL} },
{ .end = true },
{ .payload_type_map = {-EINVAL, -EINVAL}, },
},
},
{
.descr = "test AMR with differing octet-aligned settings",
.descr = "test AMR with differing octet-aligned settings (both <-> both)",
.codecs = {
{
{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
},
{
{ 122, "AMR/8000", &amr_param_octet_aligned_false, },
{ 121, "AMR/8000", &amr_param_octet_aligned_true, },
},
},
.expect = {
{ .payload_type_map = {111, 121}, },
{ .payload_type_map = {112, 122} },
},
},
{
.descr = "test AMR with differing octet-aligned settings (oa <-> both)",
.codecs = {
{
{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
},
{
{ 122, "AMR/8000", &amr_param_octet_aligned_false, },
{ 121, "AMR/8000", &amr_param_octet_aligned_true, },
},
},
.expect = {
{ .payload_type_map = {111, 122}, },
{ .end = true },
{ .payload_type_map = {111, 121}, },
{ .payload_type_map = {111, 121}, },
},
},
{
.descr = "test AMR with missing octet-aligned settings (defaults to 0)",
.descr = "test AMR with differing octet-aligned settings (bwe <-> both)",
.codecs = {
{
{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
},
{
{ 122, "AMR/8000", &amr_param_octet_aligned_false, },
{ 121, "AMR/8000", &amr_param_octet_aligned_true, },
},
},
.expect = {
{ .payload_type_map = {112, 122}, },
{ .payload_type_map = {112, 122}, },
},
},
{
.descr = "test AMR with missing octet-aligned settings (oa <-> unset)",
.codecs = {
{
{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
@ -1918,22 +1975,54 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
},
.expect = {
{ .payload_type_map = {111, 122}, },
{ .end = true },
{ .payload_type_map = {111, 122}, },
},
},
{
.descr = "test AMR with NULL param (defaults to 0)",
.descr = "test AMR with missing octet-aligned settings (bwe <-> unset)",
.codecs = {
{
{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
{ 111, "AMR/8000", &amr_param_octet_aligned_false, },
},
{
{ 122, "AMR/8000", &amr_param_octet_aligned_unset, },
},
},
.expect = {
{ .payload_type_map = {111, 122}, },
{ .payload_type_map = {111, 122}, },
},
},
{
.descr = "test AMR with NULL param (oa <-> null)",
.codecs = {
{
{ 112, "AMR/8000", &amr_param_octet_aligned_true, },
},
{
{ 122, "AMR/8000", NULL, },
},
},
.expect = {
{ .payload_type_map = {111, 122}, },
{ .end = true },
/* Note: Both 111, anbd 112 will translate to 122. The translation from 112 */
{ .payload_type_map = {112, 122} },
{ .payload_type_map = {112, 122}, },
},
},
{
.descr = "test AMR with NULL param (bwe <-> null)",
.codecs = {
{
{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
},
{
{ 122, "AMR/8000", NULL, },
},
},
.expect = {
/* Note: Both 111, anbd 112 will translate to 122. The translation from 112 */
{ .payload_type_map = {112, 122} },
{ .payload_type_map = {112, 122}, },
},
},
{
@ -1951,11 +2040,8 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
},
},
.expect = {
{ .payload_type_map = {112, 96}, },
{ .payload_type_map = {0, 0}, },
{ .payload_type_map = {111, 97} },
{ .payload_type_map = {123, -EINVAL} },
{ .end = true },
{ .payload_type_map = {111, 97}, },
},
},
{
@ -1973,11 +2059,8 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
},
},
.expect = {
{ .payload_type_map = {112, 96}, },
{ .payload_type_map = {0, 0}, },
{ .payload_type_map = {111, 97} },
{ .payload_type_map = {123, -EINVAL} },
{ .end = true },
{ .payload_type_map = {111, 97}, },
},
},
{
@ -1995,45 +2078,98 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
},
.expect = {
{ .payload_type_map = {111, 121}, },
{ .payload_type_map = {112, -EINVAL} },
{ .payload_type_map = {113, -EINVAL} },
{ .end = true },
{ .payload_type_map = {111, 121} },
},
},
};
static void test_mgcp_codec_pt_translate(void)
static bool codec_decision(struct mgcp_conn_rtp *conn, unsigned int index_conn_src, unsigned int index_conn_dst,
const struct testcase_mgcp_codec_decide_expect *expect)
{
bool ok = true;
int payload_type_conn_src;
int payload_type_conn_dst;
printf(" - mgcp_codec_decide(&conn[%u], &conn[%u]):\n", index_conn_src, index_conn_dst);
if (mgcp_codec_decide(&conn[index_conn_src], &conn[index_conn_dst]) != 0) {
if (expect->payload_type_map[index_conn_src] == -EINVAL
&& expect->payload_type_map[index_conn_dst] == -EINVAL)
printf(" codec decision failed (expected)!\n");
else {
printf(" ERROR: codec decision failed!\n");
ok = false;
}
} else {
printf(" Codec decision result:\n");
if (conn[index_conn_src].end.codec) {
payload_type_conn_src = conn[index_conn_src].end.codec->payload_type;
printf(" conn[%u]: codec:%s, pt:%d\n",
index_conn_src, conn[index_conn_src].end.codec->subtype_name, payload_type_conn_src);
} else {
payload_type_conn_src = -EINVAL;
printf(" conn[%u]: codec:none, pt:none\n", index_conn_src);
}
if (conn[index_conn_dst].end.codec) {
payload_type_conn_dst = conn[index_conn_dst].end.codec->payload_type;
printf(" conn[%u]: codec:%s, pt:%d\n",
index_conn_dst, conn[index_conn_dst].end.codec->subtype_name,
payload_type_conn_dst);
} else {
payload_type_conn_dst = -EINVAL;
printf(" conn[%u]: codec:none, pt:none\n", index_conn_dst);
}
if (payload_type_conn_src != expect->payload_type_map[index_conn_src]) {
printf(" ERROR: conn[%u] unexpected codec decision, expected pt=%d, got pt=%d\n",
index_conn_src, expect->payload_type_map[index_conn_src], payload_type_conn_src);
ok = false;
}
if (payload_type_conn_dst != expect->payload_type_map[index_conn_dst]) {
printf(" ERROR: conn[%u] unexpected codec decision, expected pt=%d, got pt=%d\n",
index_conn_dst, expect->payload_type_map[index_conn_dst],
payload_type_conn_dst);
ok = false;
}
}
return ok;
}
static void test_mgcp_codec_decide(void)
{
int i;
bool ok = true;
printf("\nTesting mgcp_codec_pt_translate()\n");
bool ok_all = true;
printf("\nTesting mgcp_codec_find_convertible()\n");
for (i = 0; i < ARRAY_SIZE(test_mgcp_codec_pt_translate_cases); i++) {
const struct testcase_mgcp_codec_pt_translate *t = &test_mgcp_codec_pt_translate_cases[i];
struct mgcp_conn_rtp conn[2] = {};
for (i = 0; i < ARRAY_SIZE(test_mgcp_codec_find_convertible_cases); i++) {
const struct testcase_mgcp_codec_decide *t = &test_mgcp_codec_find_convertible_cases[i];
struct mgcp_conn_rtp conn[2] = { };
int rc;
int conn_i;
int c;
bool ok = true;
printf("#%d: %s\n", i, t->descr);
/* Build testvector (add codecs to conn, set properties etc... */
for (conn_i = 0; conn_i < 2; conn_i++) {
printf(" - add codecs on conn%d:\n", conn_i);
for (c = 0; c < ARRAY_SIZE(t->codecs[conn_i]); c++) {
const struct testcase_mgcp_codec_pt_translate_codec *codec = &t->codecs[conn_i][c];
const struct testcase_mgcp_codec_decide_codec *codec = &t->codecs[conn_i][c];
if (!codec->audio_name)
break;
rc = mgcp_codec_add(&conn[conn_i], codec->payload_type, codec->audio_name, codec->param);
rc = mgcp_codec_add(&conn[conn_i], codec->payload_type, codec->audio_name,
codec->param);
printf(" %2d: %3d %s%s -> rc=%d\n", c, codec->payload_type, codec->audio_name,
codec->param ?
(codec->param->amr_octet_aligned_present?
(codec->param->amr_octet_aligned ?
" octet-aligned=1" : " octet-aligned=0")
: " octet-aligned=unset")
: "",
rc);
(codec->param->amr_octet_aligned_present ?
(codec->param->amr_octet_aligned ? " octet-aligned=1" : " octet-aligned=0")
: " octet-aligned=unset")
: "", rc);
if (rc != codec->expect_rc) {
printf(" ERROR: expected rc=%d\n", codec->expect_rc);
ok = false;
@ -2043,42 +2179,26 @@ static void test_mgcp_codec_pt_translate(void)
printf(" (none)\n");
}
for (c = 0; c < ARRAY_SIZE(t->expect); c++) {
const struct testcase_mgcp_codec_pt_translate_expect *expect = &t->expect[c];
int result;
/* Run codec decision and check expectation */
if (!codec_decision(conn, 0, 1, &t->expect[0]))
ok = false;
if (expect->end)
break;
if (!codec_decision(conn, 1, 0, &t->expect[1]))
ok = false;
result = mgcp_codec_pt_translate(&conn[0], &conn[1], expect->payload_type_map[0]);
printf(" - mgcp_codec_pt_translate(conn0, conn1, %d) -> %d\n",
expect->payload_type_map[0], result);
if (result != expect->payload_type_map[1]) {
printf(" ERROR: expected -> %d\n", expect->payload_type_map[1]);
ok = false;
}
if (ok)
printf(" ===> SUCCESS: codec decision as expected!\n");
else
printf(" ===> FAIL: unexpected codec decision!\n");
/* If the expected result is an error, don't do reverse map test */
if (expect->payload_type_map[1] < 0)
continue;
result = mgcp_codec_pt_translate(&conn[1], &conn[0], expect->payload_type_map[1]);
printf(" - mgcp_codec_pt_translate(conn1, conn0, %d) -> %d\n",
expect->payload_type_map[1], result);
if (result != expect->payload_type_map[0]) {
printf(" ERROR: expected -> %d\n", expect->payload_type_map[0]);
ok = false;
}
}
for (conn_i = 0; conn_i < 2; conn_i++)
mgcp_codec_reset_all(&conn[conn_i]);
if (!ok)
ok_all = false;
}
OSMO_ASSERT(ok);
OSMO_ASSERT(ok_all);
}
void test_conn_id_matching()
void test_conn_id_matching(void)
{
struct mgcp_endpoint endp = {};
struct mgcp_conn *conn;
@ -2118,7 +2238,7 @@ void test_conn_id_matching()
talloc_free(conn);
}
void test_e1_trunk_nr_from_epname()
void test_e1_trunk_nr_from_epname(void)
{
unsigned int trunk_nr;
int rc;
@ -2168,7 +2288,7 @@ void test_e1_trunk_nr_from_epname()
return;
}
void test_mgcp_is_rtp_dummy_payload()
void test_mgcp_is_rtp_dummy_payload(void)
{
/* realistic rtp packet */
static const char rtp_payload[] =
@ -2232,7 +2352,7 @@ int main(int argc, char **argv)
test_osmux_cid();
test_get_lco_identifier();
test_check_local_cx_options(ctx);
test_mgcp_codec_pt_translate();
test_mgcp_codec_decide();
test_conn_id_matching();
test_e1_trunk_nr_from_epname();
test_mgcp_is_rtp_dummy_payload();

View File

@ -525,6 +525,80 @@ Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
================================================
Testing AUEP_NULL
creating message from statically defined input:
---------8<---------
AUEP 18983215 null@mgw MGCP 1.0
---------8<---------
checking response:
using message as statically defined for comparison
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
m: recvonly
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:40
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing MDCX_NULL
creating message from statically defined input:
---------8<---------
MDCX 9 null@mgw MGCP 1.0
I: %s
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing DLCX_NULL
creating message from statically defined input:
---------8<---------
DLCX 8 null@mgw MGCP 1.0
I: %s
C: 2
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing RQNT_NULL
creating message from statically defined input:
---------8<---------
RQNT 186908782 null@mgw MGCP 1.0
X: B244F267488
S: D/9
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing CRCX
creating message from statically defined input:
@ -1276,7 +1350,7 @@ p:10, a:PCMU -> p:10, a:PCMU
p10, aPCMU -> (null)
'10,a :PCMU' -> '(null)'
Testing mgcp_codec_pt_translate()
Testing mgcp_codec_find_convertible()
#0: same order, but differing payload type numbers
- add codecs on conn0:
0: 112 AMR/8000/1 octet-aligned=1 -> rc=0
@ -1286,13 +1360,15 @@ Testing mgcp_codec_pt_translate()
0: 96 AMR/8000/1 octet-aligned=1 -> rc=0
1: 0 PCMU/8000/1 -> rc=0
2: 97 GSM-HR-08/8000/1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
- mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:112
conn[1]: codec:AMR, pt:96
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:96
conn[0]: codec:AMR, pt:112
===> SUCCESS: codec decision as expected!
#1: different order and different payload type numbers
- add codecs on conn0:
0: 0 PCMU/8000/1 -> rc=0
@ -1302,13 +1378,15 @@ Testing mgcp_codec_pt_translate()
0: 97 GSM-HR-08/8000/1 -> rc=0
1: 0 PCMU/8000/1 -> rc=0
2: 96 AMR/8000/1 octet-aligned=1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
- mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:PCMU, pt:0
conn[1]: codec:PCMU, pt:0
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:GSM-HR-08, pt:97
conn[0]: codec:GSM-HR-08, pt:111
===> SUCCESS: codec decision as expected!
#2: both sides have the same payload_type numbers assigned to differing codecs
- add codecs on conn0:
0: 0 PCMU/8000/1 -> rc=0
@ -1318,12 +1396,15 @@ Testing mgcp_codec_pt_translate()
0: 97 GSM-HR-08/8000/1 -> rc=0
1: 0 PCMU/8000/1 -> rc=0
2: 96 AMR/8000/1 octet-aligned=1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 96) -> 97
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 96
- mgcp_codec_pt_translate(conn0, conn1, 97) -> 96
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 97
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:PCMU, pt:0
conn[1]: codec:PCMU, pt:0
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:GSM-HR-08, pt:97
conn[0]: codec:GSM-HR-08, pt:96
===> SUCCESS: codec decision as expected!
#3: conn0 has no codecs
- add codecs on conn0:
(none)
@ -1331,9 +1412,13 @@ Testing mgcp_codec_pt_translate()
0: 96 AMR/8000/1 octet-aligned=1 -> rc=0
1: 0 PCMU/8000/1 -> rc=0
2: 97 GSM-HR-08/8000/1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
- mgcp_codec_decide(&conn[0], &conn[1]):
codec decision failed (expected)!
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:96
conn[0]: codec:none, pt:none
===> SUCCESS: codec decision as expected!
#4: conn1 has no codecs
- add codecs on conn0:
0: 112 AMR/8000/1 octet-aligned=1 -> rc=0
@ -1341,31 +1426,116 @@ Testing mgcp_codec_pt_translate()
2: 111 GSM-HR-08/8000/1 -> rc=0
- add codecs on conn1:
(none)
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
#5: test AMR with differing octet-aligned settings
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:112
conn[1]: codec:none, pt:none
- mgcp_codec_decide(&conn[1], &conn[0]):
codec decision failed (expected)!
===> SUCCESS: codec decision as expected!
#5: test AMR with differing octet-aligned settings (both <-> both)
- add codecs on conn0:
0: 111 AMR/8000 octet-aligned=1 -> rc=0
1: 112 AMR/8000 octet-aligned=0 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 octet-aligned=0 -> rc=0
1: 121 AMR/8000 octet-aligned=1 -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:111
conn[1]: codec:AMR, pt:121
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:112
===> SUCCESS: codec decision as expected!
#6: test AMR with differing octet-aligned settings (oa <-> both)
- add codecs on conn0:
0: 111 AMR/8000 octet-aligned=1 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 octet-aligned=0 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 122
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 111
#6: test AMR with missing octet-aligned settings (defaults to 0)
1: 121 AMR/8000 octet-aligned=1 -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:111
conn[1]: codec:AMR, pt:121
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:121
conn[0]: codec:AMR, pt:111
===> SUCCESS: codec decision as expected!
#7: test AMR with differing octet-aligned settings (bwe <-> both)
- add codecs on conn0:
0: 112 AMR/8000 octet-aligned=0 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 octet-aligned=0 -> rc=0
1: 121 AMR/8000 octet-aligned=1 -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:112
conn[1]: codec:AMR, pt:122
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:112
===> SUCCESS: codec decision as expected!
#8: test AMR with missing octet-aligned settings (oa <-> unset)
- add codecs on conn0:
0: 111 AMR/8000 octet-aligned=1 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 octet-aligned=unset -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 122
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 111
#7: test AMR with NULL param (defaults to 0)
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:111
conn[1]: codec:AMR, pt:122
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:111
===> SUCCESS: codec decision as expected!
#9: test AMR with missing octet-aligned settings (bwe <-> unset)
- add codecs on conn0:
0: 111 AMR/8000 octet-aligned=1 -> rc=0
0: 111 AMR/8000 octet-aligned=0 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 octet-aligned=unset -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:111
conn[1]: codec:AMR, pt:122
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:111
===> SUCCESS: codec decision as expected!
#10: test AMR with NULL param (oa <-> null)
- add codecs on conn0:
0: 112 AMR/8000 octet-aligned=1 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 122
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 111
#8: match FOO/8000/1 and FOO/8000 as identical, single channel is implicit
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:112
conn[1]: codec:AMR, pt:122
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:112
===> SUCCESS: codec decision as expected!
#11: test AMR with NULL param (bwe <-> null)
- add codecs on conn0:
0: 112 AMR/8000 octet-aligned=0 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:112
conn[1]: codec:AMR, pt:122
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:112
===> SUCCESS: codec decision as expected!
#12: match FOO/8000/1 and FOO/8000 as identical, single channel is implicit
- add codecs on conn0:
0: 0 PCMU/8000/1 -> rc=0
1: 111 GSM-HR-08/8000/1 -> rc=0
@ -1374,14 +1544,16 @@ Testing mgcp_codec_pt_translate()
0: 97 GSM-HR-08/8000 -> rc=0
1: 0 PCMU/8000 -> rc=0
2: 96 AMR/8000 octet-aligned=1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
- mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
#9: match FOO/8000/1 and FOO as identical, 8k and single channel are implicit
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:PCMU, pt:0
conn[1]: codec:PCMU, pt:0
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:GSM-HR-08, pt:97
conn[0]: codec:GSM-HR-08, pt:111
===> SUCCESS: codec decision as expected!
#13: match FOO/8000/1 and FOO as identical, 8k and single channel are implicit
- add codecs on conn0:
0: 0 PCMU/8000/1 -> rc=0
1: 111 GSM-HR-08/8000/1 -> rc=0
@ -1390,14 +1562,16 @@ Testing mgcp_codec_pt_translate()
0: 97 GSM-HR-08 -> rc=0
1: 0 PCMU -> rc=0
2: 96 AMR octet-aligned=1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
- mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
#10: test whether channel number matching is waterproof
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:PCMU, pt:0
conn[1]: codec:PCMU, pt:0
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:GSM-HR-08, pt:97
conn[0]: codec:GSM-HR-08, pt:111
===> SUCCESS: codec decision as expected!
#14: test whether channel number matching is waterproof
- add codecs on conn0:
0: 111 GSM-HR-08/8000 -> rc=0
1: 112 GSM-HR-08/8000/2 -> rc=-22
@ -1405,10 +1579,15 @@ Testing mgcp_codec_pt_translate()
- add codecs on conn1:
0: 122 GSM-HR-08/8000/2 -> rc=-22
1: 121 GSM-HR-08/8000/1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 121
- mgcp_codec_pt_translate(conn1, conn0, 121) -> 111
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 113) -> -22
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:GSM-HR-08, pt:111
conn[1]: codec:GSM-HR-08, pt:121
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:GSM-HR-08, pt:121
conn[0]: codec:GSM-HR-08, pt:111
===> SUCCESS: codec decision as expected!
Testing test_conn_id_matching
needle='23AB' found '000023AB'

View File

@ -15,6 +15,7 @@ AM_CFLAGS = \
AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
-no-install \
$(NULL)
EXTRA_DIST = \

View File

@ -73,7 +73,7 @@ static struct msgb *from_str(const char *str)
return msg;
}
static struct mgcp_client_conf conf;
static struct mgcp_client_conf *conf;
struct mgcp_client *mgcp = NULL;
static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
@ -107,9 +107,6 @@ void test_response_cb(struct mgcp_response *response, void *priv)
printf(" audio_port = %u\n", response->audio_port);
printf(" audio_ip = %s\n", response->audio_ip);
printf(" ptime = %u\n", response->ptime);
printf(" codecs_len = %u\n", response->codecs_len);
for(i=0;i<response->codecs_len;i++)
printf(" codecs[%u] = %u\n", i, response->codecs[i]);
printf(" ptmap_len = %u\n", response->ptmap_len);
for(i=0;i<response->ptmap_len;i++) {
printf(" ptmap[%u].codec = %u\n", i, response->ptmap[i].codec);
@ -149,12 +146,11 @@ void test_mgcp_msg(void)
.conn_id = "11",
.conn_mode = MGCP_CONN_RECV_SEND,
.ptime = 20,
.codecs[0] = CODEC_GSM_8000_1,
.codecs[1] = CODEC_AMR_8000_1,
.codecs[2] = CODEC_GSMEFR_8000_1,
.codecs_len = 1,
.ptmap[0].codec = CODEC_GSMEFR_8000_1,
.ptmap[0].pt = 96,
.ptmap = {
{ .codec = CODEC_GSM_8000_1, .pt = CODEC_GSM_8000_1 },
{ .codec = CODEC_AMR_8000_1, .pt = CODEC_AMR_8000_1 },
{ .codec = CODEC_GSMEFR_8000_1, .pt = 96 },
},
.ptmap_len = 1,
.x_osmo_ign = MGCP_X_OSMO_IGN_CALLID,
.x_osmo_osmux_cid = -1, /* wildcard */
@ -162,7 +158,7 @@ void test_mgcp_msg(void)
if (mgcp)
talloc_free(mgcp);
mgcp = mgcp_client_init(ctx, &conf);
mgcp = mgcp_client_init(ctx, conf);
printf("\n");
@ -179,9 +175,9 @@ void test_mgcp_msg(void)
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
mgcp_msg.codecs_len = 2;
mgcp_msg.ptmap_len = 2;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.codecs_len = 1;
mgcp_msg.ptmap_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated CRCX message (three codecs, one with custom pt):\n");
@ -189,9 +185,9 @@ void test_mgcp_msg(void)
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
mgcp_msg.codecs_len = 3;
mgcp_msg.ptmap_len = 3;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.codecs_len = 1;
mgcp_msg.ptmap_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated MDCX message:\n");
@ -209,9 +205,9 @@ void test_mgcp_msg(void)
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
mgcp_msg.codecs_len = 2;
mgcp_msg.ptmap_len = 2;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.codecs_len = 1;
mgcp_msg.ptmap_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated MDCX message (three codecs, one with custom pt):\n");
@ -220,9 +216,9 @@ void test_mgcp_msg(void)
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
mgcp_msg.codecs_len = 3;
mgcp_msg.ptmap_len = 3;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.codecs_len = 1;
mgcp_msg.ptmap_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated DLCX message:\n");
@ -315,7 +311,7 @@ void test_mgcp_msg(void)
msgb_free(msg);
}
void test_mgcp_client_cancel()
void test_mgcp_client_cancel(void)
{
mgcp_trans_id_t trans_id;
struct msgb *msg;
@ -330,8 +326,10 @@ void test_mgcp_client_cancel()
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID
| MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE),
.ptime = 20,
.codecs[0] = CODEC_AMR_8000_1,
.codecs_len = 1
.ptmap = {
{ .codec = CODEC_AMR_8000_1, .pt = CODEC_AMR_8000_1 },
},
.ptmap_len = 1
};
printf("\n%s():\n", __func__);
@ -339,7 +337,7 @@ void test_mgcp_client_cancel()
if (mgcp)
talloc_free(mgcp);
mgcp = mgcp_client_init(ctx, &conf);
mgcp = mgcp_client_init(ctx, conf);
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
trans_id = mgcp_msg_trans_id(msg);
@ -487,7 +485,7 @@ static struct sdp_section_start_test sdp_section_start_tests[] = {
},
};
void test_sdp_section_start()
void test_sdp_section_start(void)
{
int i;
int failures = 0;
@ -531,7 +529,7 @@ void test_sdp_section_start()
OSMO_ASSERT(!failures);
}
static void test_map_pt_to_codec(void)
static void test_map_str_to_codec(void)
{
/* Full form */
OSMO_ASSERT(map_str_to_codec("PCMU/8000/1") == CODEC_PCMU_8000_1);
@ -630,7 +628,7 @@ void test_mgcp_client_e1_epname(void)
if (mgcp)
talloc_free(mgcp);
mgcp = mgcp_client_init(ctx, &conf);
mgcp = mgcp_client_init(ctx, conf);
/* Valid endpoint names */
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 1, 15, 64, 0);
@ -675,6 +673,214 @@ void test_mgcp_client_e1_epname(void)
OSMO_ASSERT(epname == NULL);
}
struct parse_response_test {
const char *body;
int expect_rc;
struct mgcp_response expect_params;
};
static struct parse_response_test parse_response_tests[] = {
{
.body = "200 2 OK\r\n"
"I: foo\r\n"
"\r\n"
"v=0\r\n"
"o=- name 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 1.2.3.4\r\n"
"t=0 0\r\n"
"m=audio 23 RTP/AVP 112 3\r\n" /* <-- implicit: 3 = GSM-FR */
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n",
.expect_rc = 0,
.expect_params = {
.audio_port = 23,
.audio_ip = "1.2.3.4",
.ptmap = {
{ .codec = CODEC_AMR_8000_1, .pt = 112 },
{ .codec = CODEC_GSM_8000_1, .pt = 3 },
},
.ptmap_len = 2,
},
},
{
.body = "200 2 OK\r\n"
"I: foo\r\n"
"\r\n"
"v=0\r\n"
"o=- name 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 1.2.3.4\r\n"
"t=0 0\r\n"
"m=audio 23 RTP/AVP 112 3\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=rtpmap:3 GSM/8000\r\n" /* 3 == GSM-FR implicitly, is an explicit entry a problem? */
"a=ptime:20\r\n",
.expect_rc = 0,
.expect_params = {
.audio_port = 23,
.audio_ip = "1.2.3.4",
.ptmap = {
{ .codec = CODEC_AMR_8000_1, .pt = 112 },
{ .codec = CODEC_GSM_8000_1, .pt = 3 }, /* no, not a problem */
},
.ptmap_len = 2,
},
},
{
.body = "200 2 OK\r\n"
"I: foo\r\n"
"\r\n"
"v=0\r\n"
"o=- name 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 1.2.3.4\r\n"
"t=0 0\r\n"
"m=audio 23 RTP/AVP 3\r\n" /* <-- 112 is missing here. Will it still appear? */
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n",
.expect_rc = 0,
.expect_params = {
.audio_port = 23,
.audio_ip = "1.2.3.4",
.ptmap = {
{ .codec = CODEC_GSM_8000_1, .pt = 3 },
{ .codec = CODEC_AMR_8000_1, .pt = 112 }, /* <-- yes, it was added to the end. */
},
.ptmap_len = 2,
},
},
{
/* test MGCP_MAX_CODECS */
.body = "200 2 OK\r\n"
"I: foo\r\n"
"\r\n"
"v=0\r\n"
"o=- name 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 1.2.3.4\r\n"
"t=0 0\r\n"
"m=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110\r\n" /* <-- 10 codecs max */
"a=rtpmap:101 AMR/8000\r\n"
"a=rtpmap:102 AMR/8000\r\n"
"a=rtpmap:103 AMR/8000\r\n"
"a=rtpmap:104 AMR/8000\r\n"
"a=rtpmap:105 AMR/8000\r\n"
"a=rtpmap:106 AMR/8000\r\n"
"a=rtpmap:107 AMR/8000\r\n"
"a=rtpmap:108 AMR/8000\r\n"
"a=rtpmap:109 AMR/8000\r\n"
"a=rtpmap:110 AMR/8000\r\n"
"a=ptime:20\r\n",
.expect_rc = 0,
.expect_params = {
.audio_port = 23,
.audio_ip = "1.2.3.4",
.ptmap = {
{ .codec = CODEC_AMR_8000_1, .pt = 101 },
{ .codec = CODEC_AMR_8000_1, .pt = 102 },
{ .codec = CODEC_AMR_8000_1, .pt = 103 },
{ .codec = CODEC_AMR_8000_1, .pt = 104 },
{ .codec = CODEC_AMR_8000_1, .pt = 105 },
{ .codec = CODEC_AMR_8000_1, .pt = 106 },
{ .codec = CODEC_AMR_8000_1, .pt = 107 },
{ .codec = CODEC_AMR_8000_1, .pt = 108 },
{ .codec = CODEC_AMR_8000_1, .pt = 109 },
{ .codec = CODEC_AMR_8000_1, .pt = 110 },
},
.ptmap_len = 10,
},
},
{
/* test MGCP_MAX_CODECS */
.body = "200 2 OK\r\n"
"I: foo\r\n"
"\r\n"
"v=0\r\n"
"o=- name 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 1.2.3.4\r\n"
"t=0 0\r\n"
"m=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110 3\r\n" /* <-- 11 > MGCP_MAX_CODECS */
"a=rtpmap:101 AMR/8000\r\n"
"a=rtpmap:102 AMR/8000\r\n"
"a=rtpmap:103 AMR/8000\r\n"
"a=rtpmap:104 AMR/8000\r\n"
"a=rtpmap:105 AMR/8000\r\n"
"a=rtpmap:106 AMR/8000\r\n"
"a=rtpmap:107 AMR/8000\r\n"
"a=rtpmap:108 AMR/8000\r\n"
"a=rtpmap:109 AMR/8000\r\n"
"a=rtpmap:110 AMR/8000\r\n"
"a=ptime:20\r\n",
.expect_rc = -EINVAL,
},
};
static void test_parse_response(void)
{
int i;
int failures = 0;
for (i = 0; i < ARRAY_SIZE(parse_response_tests); i++) {
int rc;
struct parse_response_test *t = &parse_response_tests[i];
struct mgcp_response *r = talloc_zero(ctx, struct mgcp_response);
int p;
r->body = talloc_strdup(r, t->body);
//printf("\n%s() test [%d]:\n", __func__, i);
fprintf(stderr, "\n%s() test [%d]:\n", __func__, i);
fprintf(stderr, "body: \"%s\"\n", osmo_escape_str(r->body, -1));
rc = mgcp_response_parse_params(r);
fprintf(stderr, "got rc=%d\n", rc);
if (rc != t->expect_rc) {
fprintf(stderr, "FAIL: Expected rc=%d\n", t->expect_rc);
failures++;
}
if (rc) {
talloc_free(r);
continue;
}
fprintf(stderr, "got audio_ip=\"%s\"\n", r->audio_ip);
if (strcmp(r->audio_ip, t->expect_params.audio_ip)) {
fprintf(stderr, "FAIL: Expected audio_ip=\"%s\"\n", t->expect_params.audio_ip);
failures++;
}
fprintf(stderr, "got audio_port=%u\n", r->audio_port);
if (r->audio_port != t->expect_params.audio_port) {
fprintf(stderr, "FAIL: Expected audio_port=%u\n", t->expect_params.audio_port);
failures++;
}
for (p = 0; p < r->ptmap_len; p++) {
struct ptmap *got = &r->ptmap[p];
struct ptmap *expect = NULL;
fprintf(stderr, " %d %s\n", got->pt, osmo_mgcpc_codec_name(got->codec));
if (p >= t->expect_params.ptmap_len) {
fprintf(stderr, " - ERROR: too many codec entries\n");
failures++;
continue;
}
expect = &t->expect_params.ptmap[p];
if (ptmap_cmp(got, expect)) {
fprintf(stderr, " - ERROR: expected: %d %s\n",
expect->pt, osmo_mgcpc_codec_name(expect->codec));
failures++;
}
}
talloc_free(r);
}
OSMO_ASSERT(!failures);
}
static const struct log_info_cat log_categories[] = {
};
@ -697,15 +903,17 @@ int main(int argc, char **argv)
log_set_category_filter(osmo_stderr_target, DLMGCP, 1, LOGL_DEBUG);
mgcp_client_conf_init(&conf);
conf = mgcp_client_conf_alloc(ctx);
test_mgcp_msg();
test_mgcp_client_cancel();
test_sdp_section_start();
test_map_codec_to_pt_and_map_pt_to_codec();
test_map_pt_to_codec();
test_map_str_to_codec();
test_mgcp_client_e1_epname();
test_parse_response();
printf("Done\n");
fprintf(stderr, "Done\n");
return EXIT_SUCCESS;

View File

@ -1,5 +1,6 @@
DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw'
DLMGCP MGW(mgw) Message buffer to small, can not generate MGCP message (SDP)
DLMGCP MGW(mgw) Message buffer too small, can not generate MGCP message (SDP)
DLMGCP MGW(mgw) Failed to add SDP, can not generate MGCP message
test_mgcp_client_cancel():
DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw'
@ -10,6 +11,7 @@ DLMGCP MGW(mgw) Cannot cancel, no such transaction: 1
- cancel succeeds
DLMGCP MGW(mgw) Canceled transaction 1
- late response gets discarded
DLMGCP MGW(mgw) MGCP link to MGW now considered UP
DLMGCP MGW(mgw) MGCP client: Rx 200 1 OK
DLMGCP MGW(mgw) Cannot find matching MGCP transaction for trans_id 1
- canceling again does nothing
@ -135,4 +137,51 @@ DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su128-0@mgw),
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su8-16@mgw), rate(8)/offset(16) combination is invalid!
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-0/su8-2@mgw), E1-timeslot number (0) is invalid!
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-64/su8-2@mgw), E1-timeslot number (64) is invalid!
test_parse_response() test [0]:
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 112 3\r\na=rtpmap:112 AMR/8000\r\na=ptime:20\r\n"
got rc=0
got audio_ip="1.2.3.4"
got audio_port=23
112 AMR/8000/1
3 GSM/8000/1
test_parse_response() test [1]:
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 112 3\r\na=rtpmap:112 AMR/8000\r\na=rtpmap:3 GSM/8000\r\na=ptime:20\r\n"
got rc=0
got audio_ip="1.2.3.4"
got audio_port=23
112 AMR/8000/1
3 GSM/8000/1
test_parse_response() test [2]:
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 3\r\na=rtpmap:112 AMR/8000\r\na=ptime:20\r\n"
DLMGCP error in MGCP message: 'a=rtpmap:112' has no matching entry in 'm=audio ... 112'
got rc=0
got audio_ip="1.2.3.4"
got audio_port=23
3 GSM/8000/1
112 AMR/8000/1
test_parse_response() test [3]:
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110\r\na=rtpmap:101 AMR/8000\r\na=rtpmap:102 AMR/8000\r\na=rtpmap:103 AMR/8000\r\na=rtpmap:104 AMR/8"
got rc=0
got audio_ip="1.2.3.4"
got audio_port=23
101 AMR/8000/1
102 AMR/8000/1
103 AMR/8000/1
104 AMR/8000/1
105 AMR/8000/1
106 AMR/8000/1
107 AMR/8000/1
108 AMR/8000/1
109 AMR/8000/1
110 AMR/8000/1
test_parse_response() test [4]:
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110 3\r\na=rtpmap:101 AMR/8000\r\na=rtpmap:102 AMR/8000\r\na=rtpmap:103 AMR/8000\r\na=rtpmap:104 AMR"
DLMGCP SDP: can parse only up to 10 payload type numbers
DLMGCP Failed to parse SDP parameter payload types (RTP/AVP)
got rc=-22
Done