Compare commits

..

140 Commits

Author SHA1 Message Date
Max
9c0d105d09 ctrl: take both address and port from vty config
Change-Id: Id053bc02e0a6359e52a0f5e110128d3bb87ed151
2022-12-20 19:00:45 +03:00
Pau Espin Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
9ac9cb963d Improve logging on AMR OA<->BWE conversion failure
Change-Id: I7c7dd10650c1d249c723d6781585b343004bd64d
2022-10-26 12:36:48 +02:00
Pau Espin Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
d266c374ee mgw: Log unexpected RTP AMR OA-vs-BE payload
Change-Id: Ib5ae82c01153398491b21191a8cec9969337bbbc
2022-10-25 18:23:04 +02:00
Pau Espin Pedrol
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 Pedrol
01b812ea76 mgcp_send: Use mgcp_conn_rtp_is_iuup() helper
Change-Id: I6988337f395168b88959876fe3827d8e67b8fc00
2022-10-19 19:18:50 +02:00
Pau Espin Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
a6917fecb1 mgcp-client: Fix typo in internal function name
Change-Id: I073038f1d6c68bc8546763a2c8c791fb5a021630
2022-10-13 18:38:43 +02:00
Pau Espin Pedrol
58877f04e3 mgcp-client: Avoid double iteration picking client from pool
Change-Id: I99e8be878452c5d8e2183239fbdd499dcf507e90
2022-10-13 18:38:43 +02:00
Pau Espin Pedrol
d4707e2185 mgcp-client: pool: Improve documentation of some internal fields
Change-Id: Ic9823ab0a3f101549452a7c7a0b1b5b21aaa837c
2022-10-13 18:38:28 +02:00
Pau Espin Pedrol
f633302c73 cosmetic: Fix typo in comment
Change-Id: I6b0fcd7ee13e0e712d47741258f83a6f98503e27
2022-10-13 13:20:10 +02:00
Pau Espin Pedrol
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 Pedrol
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 Pedrol
07bbd766c5 mgcp_conn_dump(): Separate dump for osmux and iuup connections
Change-Id: Iec1f2d61e4eb14a8a3c634e2642063bea6000c04
2022-10-06 18:49:31 +02:00
Pau Espin Pedrol
c7c8e649f1 osmux: Erase references to bsc-nat
Change-Id: I21ab7134de577278175132fc78431a249de76731
2022-10-06 18:24:24 +02:00
Pau Espin Pedrol
c0e1b1aec5 osmux: Move setting OSMUX_STATE_DISABLED to initializer function
Change-Id: I7c184b17075fc3e71f1f66775e16b56a5ae91b62
2022-10-06 16:21:37 +02:00
Pau Espin Pedrol
9735d21b9d osmux: Define osmux_dummy cfg as boolean
Change-Id: Ifc3384f9871ddfdbd6282c6e03bf6a2dd8a09a9e
2022-10-06 16:19:37 +02:00
Pau Espin Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
e7de137b29 osmux: cosmetic: Fix indentation
Change-Id: Ic7087e7907c1078d134d7d9b11ed6a6bf1941308
2022-10-06 13:10:51 +02:00
Pau Espin Pedrol
c1ad7fd92b osmux: Drop unused role parameter
Change-Id: I39c2852bb60aaba4f85188c55f157e4abec47a0e
2022-10-06 13:10:51 +02:00
Pau Espin Pedrol
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 Pedrol
8e0d101997 osmux: Change couple log lines to OSMUX category
Change-Id: Ia04d08099f0b1e81707699d872779108f22522bf
2022-10-06 10:31:19 +02:00
Pau Espin Pedrol
654b68d602 Clean up local var pointers in mgcp_get_local_addr()
Change-Id: I12f1d38b70f97426318e948cd80f9efc2592d54b
2022-10-05 10:37:49 +02:00
Pau Espin Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
2dd2b33030 osmux: Log remote address upon rx of osmux pkt
Change-Id: Ie2a375711c7c0989161ae651ea6f0695799e2670
2022-10-03 18:10:55 +02:00
Pau Espin Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
4d090d85f4 osmux: Drop unneeded comment block
Change-Id: Iae04a7c4b496492ce5b363a56fdd871b210012ed
2022-09-29 16:02:03 +02:00
Pau Espin Pedrol
81c5847434 cosmetic: osmux: Drop extra empty line
Change-Id: I679a62c49290dfeb9e2c63890daa67002bfe3339
2022-09-27 13:45:07 +02:00
Pau Espin Pedrol
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 Pedrol
b7f52b414e osmux: Rename field s/init/initialized
Change-Id: Ic48de396b1d8a0774611b4a1c2248ba79bdaf044
2022-09-25 00:14:03 +02:00
Pau Espin Pedrol
ea7aaf2eca vty: show per-connection Osmux VTY stats
Related: SYS#5987
Change-Id: Ieab6dcbd195c8e01a73a2a832bce78ee015ae1c3
2022-09-25 00:06:00 +02:00
Pau Espin Pedrol
432ee9d150 osmux: Improve per-conn tx rate counters
Change-Id: If030f5d921bdfcfcd00b015b4a9e48bb3b04e721
2022-09-25 00:06:00 +02:00
Pau Espin Pedrol
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 Pedrol
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 Pedrol
87f114a1d0 Use bool type instead of int in config field
Change-Id: Ie2624a6a6848c3c88deb39760317860d8074acb9
2022-09-23 15:48:43 +02:00
Pau Espin Pedrol
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 Pedrol
b9022497d8 cosmetic: osmux: Fix formatting of if-else brackets
Change-Id: I748d41cd110841ef121f70560e70c07de56aa9da
2022-09-22 21:47:23 +02:00
Pau Espin Pedrol
582c2bf7b0 osmux: Add connection and global rate counters
Change-Id: I200a4aa3908cac0ec729d980a66f3df7f55e4da7
2022-09-22 21:44:46 +02:00
Pau Espin Pedrol
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 Pedrol
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 Pedrol
a29f155301 Fix typo in ratectr description
Change-Id: I2ac8581f13814f320767ba188ef7cf3286fa443d
2022-09-22 21:21:12 +02:00
Pau Espin Pedrol
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 Pedrol
3fbf035923 osmux: Clean up helper macro osmux_chunk_length()
Change-Id: I482d8c5be08610788c2ed98e3b87ae4184075e8d
2022-09-22 19:01:33 +02:00
Pau Espin Pedrol
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 Pedrol
26e810d8a9 cosmetic: mgcp_conn.h: fix indentation whitespace
Change-Id: I6b12b589d1b0a8f95a8283aac8ba0ad22026b43f
2022-09-22 17:16:20 +02:00
Pau Espin Pedrol
4d1a52d463 osmux: Log sendto() error
Change-Id: Icd91b725ac6b5ef2b9307cc99d67d60efdfa32cb
2022-09-22 17:11:57 +02:00
Pau Espin Pedrol
83df47cd7a cosmetic: vty: Fix indentation whitespace
Change-Id: I2f42707f815ca58690d86a956118b6661c59fda3
2022-09-21 20:07:59 +02:00
Pau Espin Pedrol
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 Pedrol
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 Pedrol
8b0a614e43 osmux: Fix memleak on error code path
Change-Id: Ib84f78a53293799b925b645156513e129c32c705
2022-09-09 18:13:45 +02:00
Pau Espin Pedrol
c61664c2b2 osmux: Log refcounting of osmux_handle_list
Change-Id: Ia873e3021078976748762baaf406865149b2a090
2022-09-09 18:13:45 +02:00
Pau Espin Pedrol
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 Pedrol
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 Pedrol
8c777990a5 cosmetic: main: Properly format log_info_cat
Change-Id: If130d1debc1977b7f8d82f515861df2c513d55bb
2022-09-09 16:21:22 +02:00
Pau Espin Pedrol
ec5e85b84e Add Osmux log category
Change-Id: Ia75de7965c39f3f84164a25584d901dbdd43f10f
2022-09-09 16:04:19 +02:00
Pau Espin Pedrol
ab4e8663e1 mgw: Use X-Osmux string define
Change-Id: Ib437c0a6b414f1d8f08fdb90e88a61e9aed2780d
2022-09-08 15:15:37 +02:00
Pau Espin Pedrol
a4f783b51b cosmetic: osmux: Fix wrong indentation
Change-Id: I7675d17bb3fc827e05c197104be29eadd8ddf3f2
2022-09-07 20:04:20 +02:00
Pau Espin Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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 Pedrol
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
Pau Espin Pedrol
cfef36837e Bump version: 1.9.0.26-c9466-dirty → 1.10.0
Change-Id: If6d23bd0628cebf6cfa8fca0ae8a91278264e2a3
2022-06-28 18:50:25 +02:00
Harald Welte
c946605208 update git URLs (git -> https; gitea)
Change-Id: I122aa939df79663a4f226565b32524b2bfdf7c68
2022-06-18 12:18:10 +02:00
Pau Espin Pedrol
a02faff1ac iuup: Check for IuUP Initialization retrans
Since libosmocore.git Change-Id
I5cb740702805693cc7f0a550e2e093f9bfdd507c, the IuUP stack can send INIT
event more than once, it sends one each time an IuUP Initialization
message is received.
This is done since potentially a peer could send an Initialization
message at any time with a different subflow size configuration. So
ideally we should update all osmo-mgw state regarding codecs, and
forward the Init starting the procedure on the other conn of the
endpoint.
However, this scenario is most probably not going to happen right now
and it would be a lot of work to implement and test,
and subsequent INITs we received will almost surely come from
retransmissions of the initial Initialization message, which means
content will not really change.
Hence, it makes sense to simply drop the receive message (the IuUP stack
already takes care of re-ACKing it) and let the endpoint state continue
with its ongoing procedures.

Related: SYS#4705
Change-Id: Ib97bc6f57d265622e24a776b96f0a82c25d33d39
2022-06-13 15:57:39 +02:00
Pau Espin Pedrol
ce055d5ac4 iuup: Fix caps in logging message
Change-Id: Icf836f770f22a9460378f91ef037997a73464faa
2022-05-25 18:34:06 +02:00
Pau Espin Pedrol
452f2ba5bd IuUP: Support RFCI ID != RFCI Index
The initially merged IuUP API and implementation in libosmocore assumed
that RFCI with ID was always in the position of its ID inside the list
of RFCIs. This was the case for messages sent by ip.access nano3g as well
as our own osmocom implementation. However it was noticed that other nodes
from other vendors actually use other order, as allowed by the IuUP message
format.
Hence, we need to break the assumption and provide explicit ID
information in the list.

NOTICE: This commit implies an API change when using libosmogsm.
However, the previous API was never available in any libosmogsm release,
and only available in both libosmogsm and osmo-mgw master, so we are
only breaking compatibility between different master versions, which is
acceptable.

Related: SYS#5969
Depends: libosmocore.git Change-Id Ib21cee2e30bf83dff4e167f79541796007af9845
Change-Id: I40ebf36ad37f5196751caf2297a340e538ad28bc
2022-05-25 13:37:28 +02:00
Vadim Yanitskiy
0b6faa4521 libosmo-mgcp: e1: fix memleaks in e1_recv_cb()
Change-Id: I4be9e6d09b34e792f24c9f09d19dce15b9dfbe3f
Fixes: OS#5533
2022-04-18 02:29:42 +03:00
Vadim Yanitskiy
1c69fb0f14 tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
When using 'check_PROGRAMS', autoconf/automake generates smarter
Makefiles, so that the test programs are not being compiled during
the normal 'make all', but only during 'make check'.

Change-Id: I938669a78b4afa808ca4f741aee9919944aeb7f6
2022-04-13 19:55:34 +03:00
Philipp Maier
069dd16b67 mgcp_network: fix typo RTPC -> RTCP
Change-Id: I3274441a1bf6f4f015f01017ef03451b7f79310a
2022-03-30 17:05:56 +02:00
Philipp Maier
e144275757 mgcp_network: do not try to convert RTCP packets
Make sure that RTCP packets do not enter the code path where AMR OA and BWE
is converted. The conversion will fail and the RTCP packet will be
dropped.

Change-Id: Ic850344d8b5f7710d12e4553a4033b733dced52b
Related: SYS#5902
2022-03-30 17:05:56 +02:00
Philipp Maier
e0058b7207 mgcp_codec: do not differentiate between oa and bwe when comparing codec
AMR that has the payload format bandwith-efficient is the same codec as
AMR that has the payload format octet-aligned. Its the same codec, and a
comparison of the codec info with the function codecs_same() should
return true (=equal).

The affected function codecs_same() is used by mgcp_codec_pt_translate().
When the egress payload type number is looked up, the ingress and egress
codec information is compared. When one end is using AMR in
bandwith-efficient format and the other end is using it in
octet-alingned format. Then the codec still must be recognized as the
same codec. Othersiwse the payload type number translation would not
work, even though the codec is the same on both sides.

Change-Id: I64731570c287a75d39c79c10e1bc09a37bdd54d6
Related: SYS#5834
2022-02-16 17:17:45 +01:00
Pau Espin Pedrol
bb3ccdea1a Initial IuUP support using proper FSMs
Related: OS#1937
Depends: libosmocore Change-Id I63ee780b4aa162ea097410b234e73984000c0965
Change-Id: I6694a21480b25ab8f35d375295be6601ce38e31d
2022-02-07 17:50:31 +00:00
Pau Espin Pedrol
2799ff9bb9 Make function amr_is_octet_aligned publicly available
it will be used by mgcp_iuup.c in follow-up patch.

Change-Id: Iffaf90c1f713feef0c609a7581a346f5f28141d9
2022-01-18 14:56:34 +00:00
Pau Espin Pedrol
fed5feafd3 Drop unneeded ax_check_compile_flag.m4
The macro is no longer used since
172f5acfce.

Change-Id: I2daa73b960a9f3ae7babb8b44dc343aaaf3b57aa
2022-01-11 17:46:41 +00:00
Philipp Maier
1de5ed6f97 mgcp_client: add new codec IUFP as VND.3GPP.IUFP
3GPP TS 25.414 5.1.3.3.1.6 specifies that IuUP can use RTP as transport.
The payload type is specified from 96-127, which shall be ignored on the
receiving end anyway.

The payload type number we use shall be 96 by default.

Change-Id: Ifd1210a897743396899f34457c96e6fd2109c6b3
Related: SYS#5152
2022-01-04 15:50:39 +01:00
Pau Espin Pedrol
80751d850b cosmetic: mgcp_codec.c: Fix typo in comment
Change-Id: Ic93c9bcf6d3a12cc42fdfee2be97662adf068409
2022-01-03 15:26:27 +01:00
Pau Espin Pedrol
2c40164ff0 Define mgcp_rtp_end.output_enabled as bool
Change-Id: I55f7796ef774f86050041f2c5e3a2f8f7d1f56df
2022-01-03 12:29:39 +01:00
Pau Espin Pedrol
e308202285 mgcp_network.c: Fix byte alignment of CRC Header for ACK Initialization
The Header CRC field is 6 bits, not 8, and spans bits 7-2.

Fixes: ebb05c1f90
Change-Id: I9a8179813d451948bfa02443894fdd2313dfc4a0
2021-12-28 21:05:47 +00:00
Alexander Couzens
b4a067c9fb doc/overview: fix wrong project page link
Change-Id: Ie221099bf1ac278729817ae88773cfc3a709ffba
2021-12-25 20:17:09 +01:00
Pau Espin Pedrol
8029b12146 cosmetic: Rename variable payload=>payload_type
Using "payload" there is misleading, the proper naming is payload type,
a well known term for RTP.

Change-Id: Ifcad63b0ba5068acd555960c71c3ad1489a2b870
2021-12-23 16:22:32 +01:00
Pau Espin Pedrol
ebb05c1f90 mgcp_network.c: Set proper CRC Header for ACK Initialization
Discovered while debugging wireshark CRC calculation and implementing
new IuUP code in libosmocore.

Change-Id: Ic8350d1f9a9e5dcefeb787462d267bfac08d778f
2021-12-16 16:11:09 +01:00
Oliver Smith
bc3f3b40fe treewide: remove FSF address
Remove the paragraph about writing to the Free Software Foundation's
mailing address. The FSF has changed addresses in the past, and may do
so again. In 2021 this is not useful, let's rather have a bit less
boilerplate at the start of source files.

Change-Id: I2a623f67e116d5e56091ae5860ca2a305c57e50a
2021-12-14 12:50:59 +00:00
Philipp Maier
fae09f4562 configuration: point out difference between trunk-nr and e1 line nr
When configuring osmo-mgw the user may choose an arbitrary trunk number
for the E1 trunk and sets a line number that must match the number of
the physical line that is used with the particular trunk. This is easy
to confuse, so lets add a note to the maual that mekes this clear.

Change-Id: I4b647a60d21cae99663a8258d6636ec8a7609d97
Related OS#5308

Change-Id: Ide27fda6d9ee2627bb544d21aa65161eace35a34
2021-11-25 15:44:57 +01:00
Eric
7c0fe31697 fix mgcp_conn_free_all ubsan complaints
ubsan still complains about a unaligned load that can't be explained, so
silence it:

/mgw-threads/install/include/osmocom/core/linuxlist.h:171:15: runtime
error: member access within misaligned address 0x612000000249 for type
'const struct llist_head', which requires 8 byte alignment
0x612000000249: note: pointer points here
00 00 00  48 02 00 00 20 61 00 00  48 02 00 00 20 61 00 00  60 02 00 00
a0 62 00 00  80 1f 49 00 00
              ^
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior
/mgw-threads/install/include/osmocom/core/linuxlist.h:171:15 in
/mgw-threads/install/include/osmocom/core/linuxlist.h:171:15: runtime
error: load of misaligned address 0x612000000249 for type 'struct
llist_head *const', which requires 8 byte alignment
0x612000000249: note: pointer points here
00 00 00  48 02 00 00 20 61 00 00  48 02 00 00 20 61 00 00  60 02 00 00
a0 62 00 00  80 1f 49 00 00
              ^
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior
/mgw-threads/install/include/osmocom/core/linuxlist.h:171:15 in
mgcp_conn.c:303:17: runtime error: member access within misaligned
address 0x612000000249 for type 'struct llist_head', which requires 8
byte alignment
0x612000000249: note: pointer points here
00 00 00  48 02 00 00 20 61 00 00  48 02 00 00 20 61 00 00  60 02 00 00
a0 62 00 00  80 1f 49 00 00
              ^
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior
mgcp_conn.c:303:17 in
mgcp_conn.c:303:17: runtime error: load of misaligned address
0x612000000249 for type 'struct llist_head *', which requires 8 byte
alignment
0x612000000249: note: pointer points here
00 00 00  48 02 00 00 20 61 00 00  48 02 00 00 20 61 00 00  60 02 00 00
a0 62 00 00  80 1f 49 00 00
              ^
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior
mgcp_conn.c:303:17 in
mgcp_conn.c:304:30: runtime error: member access within misaligned
address 0x4800006120000002 for type 'struct mgcp_conn', which requires 8
byte alignment
0x4800006120000002: note: pointer points here
<memory cannot be printed>
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior
mgcp_conn.c:304:30 in
AddressSanitizer:DEADLYSIGNAL
=================================================================
==223426==ERROR: AddressSanitizer: SEGV on unknown address (pc
0x0000004553f7 bp 0x7ffda5855080 sp 0x7ffda5855040 T0)
==223426==The signal is caused by a READ memory access.
==223426==Hint: this fault was caused by a dereference of a high value
address (see register values below).  Disassemble the provided pc to
learn which register was used.
/mgw-threads/osmo-mgw/src/libosmo-mgcp/mgcp_conn.c:199:14
/mgw-threads/osmo-mgw/src/libosmo-mgcp/mgcp_conn.c:258:9
/mgw-threads/osmo-mgw/src/libosmo-mgcp/mgcp_conn.c:304:3
/mgw-threads/osmo-mgw/src/libosmo-mgcp/mgcp_endp.c:124:2
/mgw-threads/osmo-mgw/tests/mgcp/mgcp_test.c:670:3
/mgw-threads/osmo-mgw/tests/mgcp/mgcp_test.c:923:2
/mgw-threads/osmo-mgw/tests/mgcp/mgcp_test.c:2248:2
/build/glibc-eX1tMB/glibc-2.31/csu/../csu/libc-start.c:308:16
(/mgw-threads/osmo-mgw/tests/mgcp/mgcp_test+0x404c2d)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV
/mgw-threads/osmo-mgw/src/libosmo-mgcp/mgcp_conn.c:199:14 in
mgcp_conn_get
==223426==ABORTING

Change-Id: Ifd056eeb88966df164c07b9165b25faa4edbaadb
2021-11-17 21:20:08 +00:00
Eric
98aef217c6 clang-format: remove foreach macros
We don't really care about the kernel style after all and the linter
complains about proper formatting, so remove all for-like macros so
we don't accidentally apply kernel style formatting.

Change-Id: Ia1b5848b31470b694a2031ad83e84f3132212f94
2021-11-17 21:20:08 +00:00
Eric
e03e34f8bb add a lock-free bounded spsc interthread queue
Not entirely wait-free: allows waiting on a fd, and in general fd
notifications for poll-loop integration between threads.

Change-Id: I4f17042baf76d086ce6b20eb99402dc64c22c657
2021-11-17 21:20:08 +00:00
Eric
ee6958c9a8 rework message handling
This was previously broken and a free endpoint was requirted to dlcx *,
additionaly globally handling this is difficult due to different
response
codes, so just do it in the functions, they know best.

Change-Id: I8cbbe5936067ea1caa7935e8d14908ac5c4010bd
2021-11-17 21:20:08 +00:00
Eric
fbcf4a6f6c adjust mgcp response context
This patch also prepares for threading.

Change-Id: Id17f51d8bc0d1ba26f7fca72b1679ffadc9d6dc8
2021-11-17 21:20:08 +00:00
56 changed files with 2673 additions and 1677 deletions

View File

@@ -64,451 +64,6 @@ DisableFormat: false
ExperimentalAutoDetectBinPacking: false
#FixNamespaceComments: false # Unknown to clang-format-4.0
# Taken from:
# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \
# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \
# | sort | uniq
ForEachMacros:
- 'apei_estatus_for_each_section'
- 'ata_for_each_dev'
- 'ata_for_each_link'
- '__ata_qc_for_each'
- 'ata_qc_for_each'
- 'ata_qc_for_each_raw'
- 'ata_qc_for_each_with_internal'
- 'ax25_for_each'
- 'ax25_uid_for_each'
- '__bio_for_each_bvec'
- 'bio_for_each_bvec'
- 'bio_for_each_bvec_all'
- 'bio_for_each_integrity_vec'
- '__bio_for_each_segment'
- 'bio_for_each_segment'
- 'bio_for_each_segment_all'
- 'bio_list_for_each'
- 'bip_for_each_vec'
- 'bitmap_for_each_clear_region'
- 'bitmap_for_each_set_region'
- 'blkg_for_each_descendant_post'
- 'blkg_for_each_descendant_pre'
- 'blk_queue_for_each_rl'
- 'bond_for_each_slave'
- 'bond_for_each_slave_rcu'
- 'bpf_for_each_spilled_reg'
- 'btree_for_each_safe128'
- 'btree_for_each_safe32'
- 'btree_for_each_safe64'
- 'btree_for_each_safel'
- 'card_for_each_dev'
- 'cgroup_taskset_for_each'
- 'cgroup_taskset_for_each_leader'
- 'cpufreq_for_each_entry'
- 'cpufreq_for_each_entry_idx'
- 'cpufreq_for_each_valid_entry'
- 'cpufreq_for_each_valid_entry_idx'
- 'css_for_each_child'
- 'css_for_each_descendant_post'
- 'css_for_each_descendant_pre'
- 'device_for_each_child_node'
- 'displayid_iter_for_each'
- 'dma_fence_chain_for_each'
- 'do_for_each_ftrace_op'
- 'drm_atomic_crtc_for_each_plane'
- 'drm_atomic_crtc_state_for_each_plane'
- 'drm_atomic_crtc_state_for_each_plane_state'
- 'drm_atomic_for_each_plane_damage'
- 'drm_client_for_each_connector_iter'
- 'drm_client_for_each_modeset'
- 'drm_connector_for_each_possible_encoder'
- 'drm_for_each_bridge_in_chain'
- 'drm_for_each_connector_iter'
- 'drm_for_each_crtc'
- 'drm_for_each_crtc_reverse'
- 'drm_for_each_encoder'
- 'drm_for_each_encoder_mask'
- 'drm_for_each_fb'
- 'drm_for_each_legacy_plane'
- 'drm_for_each_plane'
- 'drm_for_each_plane_mask'
- 'drm_for_each_privobj'
- 'drm_mm_for_each_hole'
- 'drm_mm_for_each_node'
- 'drm_mm_for_each_node_in_range'
- 'drm_mm_for_each_node_safe'
- 'flow_action_for_each'
- 'for_each_acpi_dev_match'
- 'for_each_active_dev_scope'
- 'for_each_active_drhd_unit'
- 'for_each_active_iommu'
- 'for_each_aggr_pgid'
- 'for_each_available_child_of_node'
- 'for_each_bio'
- 'for_each_board_func_rsrc'
- 'for_each_bvec'
- 'for_each_card_auxs'
- 'for_each_card_auxs_safe'
- 'for_each_card_components'
- 'for_each_card_dapms'
- 'for_each_card_pre_auxs'
- 'for_each_card_prelinks'
- 'for_each_card_rtds'
- 'for_each_card_rtds_safe'
- 'for_each_card_widgets'
- 'for_each_card_widgets_safe'
- 'for_each_cgroup_storage_type'
- 'for_each_child_of_node'
- 'for_each_clear_bit'
- 'for_each_clear_bit_from'
- 'for_each_cmsghdr'
- 'for_each_compatible_node'
- 'for_each_component_dais'
- 'for_each_component_dais_safe'
- 'for_each_comp_order'
- 'for_each_console'
- 'for_each_cpu'
- 'for_each_cpu_and'
- 'for_each_cpu_not'
- 'for_each_cpu_wrap'
- 'for_each_dapm_widgets'
- 'for_each_dev_addr'
- 'for_each_dev_scope'
- 'for_each_dma_cap_mask'
- 'for_each_dpcm_be'
- 'for_each_dpcm_be_rollback'
- 'for_each_dpcm_be_safe'
- 'for_each_dpcm_fe'
- 'for_each_drhd_unit'
- 'for_each_dss_dev'
- 'for_each_dtpm_table'
- 'for_each_efi_memory_desc'
- 'for_each_efi_memory_desc_in_map'
- 'for_each_element'
- 'for_each_element_extid'
- 'for_each_element_id'
- 'for_each_endpoint_of_node'
- 'for_each_evictable_lru'
- 'for_each_fib6_node_rt_rcu'
- 'for_each_fib6_walker_rt'
- 'for_each_free_mem_pfn_range_in_zone'
- 'for_each_free_mem_pfn_range_in_zone_from'
- 'for_each_free_mem_range'
- 'for_each_free_mem_range_reverse'
- 'for_each_func_rsrc'
- 'for_each_hstate'
- 'for_each_if'
- 'for_each_iommu'
- 'for_each_ip_tunnel_rcu'
- 'for_each_irq_nr'
- 'for_each_link_codecs'
- 'for_each_link_cpus'
- 'for_each_link_platforms'
- 'for_each_lru'
- 'for_each_matching_node'
- 'for_each_matching_node_and_match'
- 'for_each_member'
- 'for_each_memcg_cache_index'
- 'for_each_mem_pfn_range'
- '__for_each_mem_range'
- 'for_each_mem_range'
- '__for_each_mem_range_rev'
- 'for_each_mem_range_rev'
- 'for_each_mem_region'
- 'for_each_migratetype_order'
- 'for_each_msi_entry'
- 'for_each_msi_entry_safe'
- 'for_each_msi_vector'
- 'for_each_net'
- 'for_each_net_continue_reverse'
- 'for_each_netdev'
- 'for_each_netdev_continue'
- 'for_each_netdev_continue_rcu'
- 'for_each_netdev_continue_reverse'
- 'for_each_netdev_feature'
- 'for_each_netdev_in_bond_rcu'
- 'for_each_netdev_rcu'
- 'for_each_netdev_reverse'
- 'for_each_netdev_safe'
- 'for_each_net_rcu'
- 'for_each_new_connector_in_state'
- 'for_each_new_crtc_in_state'
- 'for_each_new_mst_mgr_in_state'
- 'for_each_new_plane_in_state'
- 'for_each_new_private_obj_in_state'
- 'for_each_node'
- 'for_each_node_by_name'
- 'for_each_node_by_type'
- 'for_each_node_mask'
- 'for_each_node_state'
- 'for_each_node_with_cpus'
- 'for_each_node_with_property'
- 'for_each_nonreserved_multicast_dest_pgid'
- 'for_each_of_allnodes'
- 'for_each_of_allnodes_from'
- 'for_each_of_cpu_node'
- 'for_each_of_pci_range'
- 'for_each_old_connector_in_state'
- 'for_each_old_crtc_in_state'
- 'for_each_old_mst_mgr_in_state'
- 'for_each_oldnew_connector_in_state'
- 'for_each_oldnew_crtc_in_state'
- 'for_each_oldnew_mst_mgr_in_state'
- 'for_each_oldnew_plane_in_state'
- 'for_each_oldnew_plane_in_state_reverse'
- 'for_each_oldnew_private_obj_in_state'
- 'for_each_old_plane_in_state'
- 'for_each_old_private_obj_in_state'
- 'for_each_online_cpu'
- 'for_each_online_node'
- 'for_each_online_pgdat'
- 'for_each_pci_bridge'
- 'for_each_pci_dev'
- 'for_each_pci_msi_entry'
- 'for_each_pcm_streams'
- 'for_each_physmem_range'
- 'for_each_populated_zone'
- 'for_each_possible_cpu'
- 'for_each_present_cpu'
- 'for_each_prime_number'
- 'for_each_prime_number_from'
- 'for_each_process'
- 'for_each_process_thread'
- 'for_each_prop_codec_conf'
- 'for_each_prop_dai_codec'
- 'for_each_prop_dai_cpu'
- 'for_each_prop_dlc_codecs'
- 'for_each_prop_dlc_cpus'
- 'for_each_prop_dlc_platforms'
- 'for_each_property_of_node'
- 'for_each_registered_fb'
- 'for_each_requested_gpio'
- 'for_each_requested_gpio_in_range'
- 'for_each_reserved_mem_range'
- 'for_each_reserved_mem_region'
- 'for_each_rtd_codec_dais'
- 'for_each_rtd_components'
- 'for_each_rtd_cpu_dais'
- 'for_each_rtd_dais'
- 'for_each_set_bit'
- 'for_each_set_bit_from'
- 'for_each_set_clump8'
- 'for_each_sg'
- 'for_each_sg_dma_page'
- 'for_each_sg_page'
- 'for_each_sgtable_dma_page'
- 'for_each_sgtable_dma_sg'
- 'for_each_sgtable_page'
- 'for_each_sgtable_sg'
- 'for_each_sibling_event'
- 'for_each_subelement'
- 'for_each_subelement_extid'
- 'for_each_subelement_id'
- '__for_each_thread'
- 'for_each_thread'
- 'for_each_unicast_dest_pgid'
- 'for_each_vsi'
- 'for_each_wakeup_source'
- 'for_each_zone'
- 'for_each_zone_zonelist'
- 'for_each_zone_zonelist_nodemask'
- 'fwnode_for_each_available_child_node'
- 'fwnode_for_each_child_node'
- 'fwnode_graph_for_each_endpoint'
- 'gadget_for_each_ep'
- 'genradix_for_each'
- 'genradix_for_each_from'
- 'hash_for_each'
- 'hash_for_each_possible'
- 'hash_for_each_possible_rcu'
- 'hash_for_each_possible_rcu_notrace'
- 'hash_for_each_possible_safe'
- 'hash_for_each_rcu'
- 'hash_for_each_safe'
- 'hctx_for_each_ctx'
- 'hlist_bl_for_each_entry'
- 'hlist_bl_for_each_entry_rcu'
- 'hlist_bl_for_each_entry_safe'
- 'hlist_for_each'
- 'hlist_for_each_entry'
- 'hlist_for_each_entry_continue'
- 'hlist_for_each_entry_continue_rcu'
- 'hlist_for_each_entry_continue_rcu_bh'
- 'hlist_for_each_entry_from'
- 'hlist_for_each_entry_from_rcu'
- 'hlist_for_each_entry_rcu'
- 'hlist_for_each_entry_rcu_bh'
- 'hlist_for_each_entry_rcu_notrace'
- 'hlist_for_each_entry_safe'
- 'hlist_for_each_entry_srcu'
- '__hlist_for_each_rcu'
- 'hlist_for_each_safe'
- 'hlist_nulls_for_each_entry'
- 'hlist_nulls_for_each_entry_from'
- 'hlist_nulls_for_each_entry_rcu'
- 'hlist_nulls_for_each_entry_safe'
- 'i3c_bus_for_each_i2cdev'
- 'i3c_bus_for_each_i3cdev'
- 'ide_host_for_each_port'
- 'ide_port_for_each_dev'
- 'ide_port_for_each_present_dev'
- 'idr_for_each_entry'
- 'idr_for_each_entry_continue'
- 'idr_for_each_entry_continue_ul'
- 'idr_for_each_entry_ul'
- 'in_dev_for_each_ifa_rcu'
- 'in_dev_for_each_ifa_rtnl'
- 'inet_bind_bucket_for_each'
- 'inet_lhash2_for_each_icsk_rcu'
- 'key_for_each'
- 'key_for_each_safe'
- 'klp_for_each_func'
- 'klp_for_each_func_safe'
- 'klp_for_each_func_static'
- 'klp_for_each_object'
- 'klp_for_each_object_safe'
- 'klp_for_each_object_static'
- 'kunit_suite_for_each_test_case'
- 'kvm_for_each_memslot'
- 'kvm_for_each_vcpu'
- 'list_for_each'
- 'list_for_each_codec'
- 'list_for_each_codec_safe'
- 'list_for_each_continue'
- 'list_for_each_entry'
- 'list_for_each_entry_continue'
- 'list_for_each_entry_continue_rcu'
- 'list_for_each_entry_continue_reverse'
- 'list_for_each_entry_from'
- 'list_for_each_entry_from_rcu'
- 'list_for_each_entry_from_reverse'
- 'list_for_each_entry_lockless'
- 'list_for_each_entry_rcu'
- 'list_for_each_entry_reverse'
- 'list_for_each_entry_safe'
- 'list_for_each_entry_safe_continue'
- 'list_for_each_entry_safe_from'
- 'list_for_each_entry_safe_reverse'
- 'list_for_each_entry_srcu'
- 'list_for_each_prev'
- 'list_for_each_prev_safe'
- 'list_for_each_safe'
- 'llist_for_each'
- 'llist_for_each_entry'
- 'llist_for_each_entry_safe'
- 'llist_for_each_safe'
- 'mci_for_each_dimm'
- 'media_device_for_each_entity'
- 'media_device_for_each_intf'
- 'media_device_for_each_link'
- 'media_device_for_each_pad'
- 'nanddev_io_for_each_page'
- 'netdev_for_each_lower_dev'
- 'netdev_for_each_lower_private'
- 'netdev_for_each_lower_private_rcu'
- 'netdev_for_each_mc_addr'
- 'netdev_for_each_uc_addr'
- 'netdev_for_each_upper_dev_rcu'
- 'netdev_hw_addr_list_for_each'
- 'nft_rule_for_each_expr'
- 'nla_for_each_attr'
- 'nla_for_each_nested'
- 'nlmsg_for_each_attr'
- 'nlmsg_for_each_msg'
- 'nr_neigh_for_each'
- 'nr_neigh_for_each_safe'
- 'nr_node_for_each'
- 'nr_node_for_each_safe'
- 'of_for_each_phandle'
- 'of_property_for_each_string'
- 'of_property_for_each_u32'
- 'pci_bus_for_each_resource'
- 'pcl_for_each_chunk'
- 'pcl_for_each_segment'
- 'pcm_for_each_format'
- 'ping_portaddr_for_each_entry'
- 'plist_for_each'
- 'plist_for_each_continue'
- 'plist_for_each_entry'
- 'plist_for_each_entry_continue'
- 'plist_for_each_entry_safe'
- 'plist_for_each_safe'
- 'pnp_for_each_card'
- 'pnp_for_each_dev'
- 'protocol_for_each_card'
- 'protocol_for_each_dev'
- 'queue_for_each_hw_ctx'
- 'radix_tree_for_each_slot'
- 'radix_tree_for_each_tagged'
- 'rb_for_each'
- 'rbtree_postorder_for_each_entry_safe'
- 'rdma_for_each_block'
- 'rdma_for_each_port'
- 'rdma_umem_for_each_dma_block'
- 'resource_list_for_each_entry'
- 'resource_list_for_each_entry_safe'
- 'rhl_for_each_entry_rcu'
- 'rhl_for_each_rcu'
- 'rht_for_each'
- 'rht_for_each_entry'
- 'rht_for_each_entry_from'
- 'rht_for_each_entry_rcu'
- 'rht_for_each_entry_rcu_from'
- 'rht_for_each_entry_safe'
- 'rht_for_each_from'
- 'rht_for_each_rcu'
- 'rht_for_each_rcu_from'
- '__rq_for_each_bio'
- 'rq_for_each_bvec'
- 'rq_for_each_segment'
- 'scsi_for_each_prot_sg'
- 'scsi_for_each_sg'
- 'sctp_for_each_hentry'
- 'sctp_skb_for_each'
- 'shdma_for_each_chan'
- '__shost_for_each_device'
- 'shost_for_each_device'
- 'sk_for_each'
- 'sk_for_each_bound'
- 'sk_for_each_entry_offset_rcu'
- 'sk_for_each_from'
- 'sk_for_each_rcu'
- 'sk_for_each_safe'
- 'sk_nulls_for_each'
- 'sk_nulls_for_each_from'
- 'sk_nulls_for_each_rcu'
- 'snd_array_for_each'
- 'snd_pcm_group_for_each_entry'
- 'snd_soc_dapm_widget_for_each_path'
- 'snd_soc_dapm_widget_for_each_path_safe'
- 'snd_soc_dapm_widget_for_each_sink_path'
- 'snd_soc_dapm_widget_for_each_source_path'
- 'tb_property_for_each'
- 'tcf_exts_for_each_action'
- 'udp_portaddr_for_each_entry'
- 'udp_portaddr_for_each_entry_rcu'
- 'usb_hub_for_each_child'
- 'v4l2_device_for_each_subdev'
- 'v4l2_m2m_for_each_dst_buf'
- 'v4l2_m2m_for_each_dst_buf_safe'
- 'v4l2_m2m_for_each_src_buf'
- 'v4l2_m2m_for_each_src_buf_safe'
- 'virtio_device_for_each_vq'
- 'while_for_each_ftrace_op'
- 'xa_for_each'
- 'xa_for_each_marked'
- 'xa_for_each_range'
- 'xa_for_each_start'
- 'xas_for_each'
- 'xas_for_each_conflict'
- 'xas_for_each_marked'
- 'xbc_array_for_each_value'
- 'xbc_for_each_key_value'
- 'xbc_node_for_each_array_value'
- 'xbc_node_for_each_child'
- 'xbc_node_for_each_key_value'
- 'zorro_for_each_dev'
- 'for_each_line'
- 'for_each_non_empty_line'
#IncludeBlocks: Preserve # Unknown to clang-format-5.0
IncludeCategories:
- Regex: '.*'

View File

@@ -1,6 +1,6 @@
To run the configuration parsing and output (VTY) test suite, first install
git://git.osmocom.org/python/osmo-python-tests
https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests
and pass the following configure options here:

View File

@@ -24,3 +24,6 @@
# If any interfaces have been removed or changed since the last public release, a=0.
#
#library what description / commit summary line
libosmo-netif >1.2.0 OSMUX_DEFAULT_PORT, osmux_xfrm_output_*, osmux_xfrm_input_*
libosmocore >1.7.0 osmo_sockaddr_is_any()
libmgcp-client NEW APIs mgcp_client_pool_member_...(), mgcp_client_pool_config_write()

View File

@@ -44,13 +44,13 @@ AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DLSYM)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.1.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.2.0)
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.2.0)
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)
CFLAGS="$CFLAGS -pthread"
LDFLAGS="$LDFLAGS -pthread"
@@ -79,6 +79,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"
@@ -112,7 +113,7 @@ if test "x$enable_ext_tests" = "xyes" ; then
AM_PATH_PYTHON
AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes)
if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.])
AC_MSG_ERROR([Please install https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests to run the VTY/CTRL tests.])
fi
fi
AC_MSG_CHECKING([whether to enable VTY/CTRL tests])

View File

@@ -15,10 +15,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
"""

View File

@@ -29,14 +29,14 @@ BuildRequires: pkgconfig >= 0.20
%if 0%{?suse_version}
BuildRequires: systemd-rpm-macros
%endif
BuildRequires: pkgconfig(libosmo-netif) >= 1.1.0
BuildRequires: pkgconfig(libosmocore) >= 1.6.0
BuildRequires: pkgconfig(libosmoctrl) >= 1.6.0
BuildRequires: pkgconfig(libosmogsm) >= 1.6.0
BuildRequires: pkgconfig(libosmovty) >= 1.6.0
BuildRequires: pkgconfig(libosmocoding) >= 1.6.0
BuildRequires: pkgconfig(libosmoabis) >= 1.2.0
BuildRequires: pkgconfig(libosmotrau) >= 1.2.0
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

View File

@@ -1,13 +1,18 @@
[Unit]
Description=Osmocom Media Gateway (MGW)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
Restart=always
ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg
RestartSec=2
# 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

41
debian/changelog vendored
View File

@@ -1,15 +1,46 @@
osmo-mgw (1.9.2) unstable; urgency=medium
osmo-mgw (1.10.0) unstable; urgency=medium
* systemd: depend on networking-online.target
[ Eric ]
* adjust mgcp response context
* rework message handling
* add a lock-free bounded spsc interthread queue
* clang-format: remove foreach macros
* fix mgcp_conn_free_all ubsan complaints
-- Oliver Smith <osmith@sysmocom.de> Fri, 26 May 2023 14:57:36 +0200
[ Philipp Maier ]
* configuration: point out difference between trunk-nr and e1 line nr
* mgcp_client: add new codec IUFP as VND.3GPP.IUFP
* mgcp_codec: do not differentiate between oa and bwe when comparing codec
* mgcp_network: do not try to convert RTCP packets
* mgcp_network: fix typo RTPC -> RTCP
osmo-mgw (1.9.1) unstable; urgency=medium
[ Oliver Smith ]
* treewide: remove FSF address
[ Pau Espin Pedrol ]
* mgcp_network.c: Set proper CRC Header for ACK Initialization
* cosmetic: Rename variable payload=>payload_type
* mgcp_network.c: Fix byte alignment of CRC Header for ACK Initialization
* Define mgcp_rtp_end.output_enabled as bool
* cosmetic: mgcp_codec.c: Fix typo in comment
* Drop unneeded ax_check_compile_flag.m4
* Make function amr_is_octet_aligned publicly available
* Initial IuUP support using proper FSMs
* IuUP: Support RFCI ID != RFCI Index
* iuup: Fix caps in logging message
* iuup: Check for IuUP Initialization retrans
[ Alexander Couzens ]
* doc/overview: fix wrong project page link
[ Vadim Yanitskiy ]
* tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
* libosmo-mgcp: e1: fix memleaks in e1_recv_cb()
-- Harald Welte <laforge@osmocom.org> Mon, 18 Apr 2022 10:58:50 +0200
[ Harald Welte ]
* update git URLs (git -> https; gitea)
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 28 Jun 2022 18:50:25 +0200
osmo-mgw (1.9.0) unstable; urgency=medium

12
debian/control vendored
View File

@@ -6,13 +6,13 @@ Build-Depends: debhelper (>=9),
dh-autoreconf,
pkg-config,
autotools-dev,
libosmocore-dev (>= 1.6.0),
libosmo-netif-dev (>= 1.1.0),
libosmo-abis-dev (>= 1.2.0),
osmo-gsm-manuals-dev (>= 1.2.0)
libosmocore-dev (>= 1.7.0),
libosmo-netif-dev (>= 1.2.0),
libosmo-abis-dev (>= 1.3.0),
osmo-gsm-manuals-dev (>= 1.3.0)
Standards-Version: 3.9.8
Vcs-Git: git://git.osmocom.org/osmo-mgw.git
Vcs-Browser: https://git.osmocom.org/osmo-mgw/
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
Homepage: https://osmocom.org/projects/osmo-mgw
Package: osmo-mgw

2
debian/copyright vendored
View File

@@ -1,6 +1,6 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: osmo-mgw
Source: git://git.osmocom.org/osmo-mgw
Source: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
Files: *
Copyright: 2009-2014 On-Waves

View File

@@ -91,6 +91,11 @@ the IP related aspects is nearly identical to the configuration of the virtual
trunk. However, it is important that the user assigns one of the E1 line numbers
that were configured under the e1_input node.
NOTE: The endpoint name that is used on MGCP level will include the trunk number,
not the E1 line number. For simplicity (and compatibility with OsmoBSC) it is
recommended to use equal numbers for trunk and E1 line. However, if required any
E1 line can be mapped flexible on any trunk as long as the mapping is bijective.
.Example: A typical configuration with one E1 trunk
----
e1_input

View File

@@ -107,8 +107,8 @@ We are planning to add further endpoint types for:
You can find the OsmoMGW issue tracker and wiki online at
- https://osmocom.org/projects/osmomgw
- https://osmocom.org/projects/osmomgw/wiki
- https://osmocom.org/projects/osmo-mgw
- https://osmocom.org/projects/osmo-mgw/wiki
RFC 3435 for MGCP is located at

View File

@@ -6,11 +6,11 @@ noinst_HEADERS = \
mgcp_endp.h \
mgcp_sdp.h \
mgcp_codec.h \
mgcp_ctrl.h \
mgcp_trunk.h \
debug.h \
mgcp_ratectr.h \
mgcp_e1.h \
mgcp_network.h \
mgcp_protocol.h \
mgcp_iuup.h \
$(NULL)

View File

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

View File

@@ -29,6 +29,7 @@
#include <osmocom/core/logging.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/osmux.h>
#include <arpa/inet.h>
#include <sys/types.h>
@@ -156,24 +157,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];
@@ -207,4 +210,4 @@ 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_udp_send(int fd, const struct osmo_sockaddr *addr, const char *buf, int len);

View File

@@ -17,3 +17,5 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
const char *subtype_name, unsigned int match_nr);
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);

View File

@@ -28,6 +28,7 @@
#include <osmocom/mgcp/osmux.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/gsm/iuup.h>
#include <inttypes.h>
#define LOGPCONN(conn, cat, level, fmt, args...) \
@@ -45,8 +46,8 @@ 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,
};
/*! Connection type, specifies which member of the union "u" in mgcp_conn
@@ -78,22 +79,31 @@ 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 rate_ctr_group *rate_ctr_group;
struct {
struct osmo_iuup_instance *iui;
bool active_init; /* true: Send IuUP Init */
int rfci_id_no_data; /* RFCI Id for RFCI NO_DATA (-1 if not available) */
bool configured;
struct osmo_iuup_rnl_prim *init_ind;
} iuup;
struct rate_ctr_group *ctrg;
};
/*! MGCP connection (untyped) */
@@ -135,12 +145,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
@@ -153,7 +163,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.
@@ -171,9 +181,55 @@ 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? */
static inline bool mgcp_conn_rtp_is_iuup(const struct mgcp_conn_rtp *conn)
{
return conn->type == MGCP_RTP_IUUP;
}
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,

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

View File

@@ -1,7 +1,11 @@
/* IuUP connection functionalitites */
/*
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Pau Espin Pedrol
*
* 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
@@ -16,9 +20,14 @@
* 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);
#include <osmocom/core/msgb.h>
struct mgcp_conn_rtp;
int mgcp_conn_iuup_init(struct mgcp_conn_rtp *conn_rtp);
void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp);
int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg);
int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg);
int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp);

View File

@@ -9,7 +9,8 @@
/* 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 */
@@ -70,8 +71,6 @@ struct mgcp_rtp_state {
* data is just re-used) */
uint16_t alt_rtp_tx_sequence;
uint32_t alt_rtp_tx_ssrc;
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
};
struct mgcp_rtp_codec {
@@ -94,7 +93,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;
@@ -110,8 +109,8 @@ struct mgcp_rtp_end {
uint32_t packet_duration_ms;
int maximum_packet_time; /* -1: not set */
char *fmtp_extra;
/* are we transmitting packets (1) or dropping (0) outbound packets */
int output_enabled;
/* are we transmitting packets (true) or dropping (false) outbound packets */
bool output_enabled;
/* FIXME: This parameter can be set + printed, but is nowhere used! */
int force_output_ptime;
@@ -158,7 +157,7 @@ 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,
@@ -177,3 +176,12 @@ void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
const uint16_t seq, const int32_t transit,
const uint32_t ssrc, const bool marker_bit);
void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
int id, int inc);
void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
int id);
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg);
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

@@ -0,0 +1,52 @@
/*
* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild
*
* 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 <stdatomic.h>
#include <stdbool.h>
#include <stdlib.h>
struct spsc {
atomic_uint readptr;
atomic_uint writeptr;
int efd_r, efd_w; /* eventfds used to block/notify readers/writers */
int count;
int size_per_buf;
void *buf; /* buffer size count*size_per_buf */
uintptr_t data[0]; /* count sized array of pointers to size_per_buf chunks in buf array*/
};
struct qchan {
struct spsc *a;
struct spsc *b;
};
bool spsc_push(struct spsc *q, void *elem);
bool spsc_pop(struct spsc *q, void *elem);
ssize_t spsc_prep_pop(struct spsc *q);
int spsc_get_a_rdfd(struct qchan *q);
struct qchan spsc_chan_init(void *talloc_ctx, unsigned int count, unsigned int size_per_buf);
struct qchan spsc_chan_init_ex(void *talloc_ctx, unsigned int count, unsigned int size_per_buf, bool blockr_a,
bool blockw_a, bool blockr_b, bool blockw_b);
void spsc_chan_close(struct qchan *q);

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

@@ -7,7 +7,7 @@
/* 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
@@ -49,6 +49,7 @@ enum mgcp_codecs {
CODEC_GSMHR_8000_1 = 111,
CODEC_AMR_8000_1 = 112,
CODEC_AMRWB_16000_1 = 113,
CODEC_IUFP = 96,
};
/* Note: when new codec types are added, the corresponding value strings
* in mgcp_client.c (codec_table) must be updated as well. Enumerations
@@ -139,7 +140,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);

View File

@@ -16,7 +16,7 @@ struct mgcp_client {
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 mgcp_inuse_endpoint {

View File

@@ -1,11 +1,21 @@
#pragma once
#include <stdbool.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);
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

@@ -1,74 +0,0 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
#
# DESCRIPTION
#
# Check whether the given FLAG works with the current language's compiler
# or gives an error. (Warnings, however, are ignored)
#
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
# success/failure.
#
# If EXTRA-FLAGS is defined, it is added to the current language's default
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
# force the compiler to issue an error when a bad flag is given.
#
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
#
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
#
# LICENSE
#
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 4
AC_DEFUN([AX_CHECK_COMPILE_FLAG],
[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
[AS_VAR_SET(CACHEVAR,[yes])],
[AS_VAR_SET(CACHEVAR,[no])])
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
AS_VAR_IF(CACHEVAR,yes,
[m4_default([$2], :)],
[m4_default([$3], :)])
AS_VAR_POPDEF([CACHEVAR])dnl
])dnl AX_CHECK_COMPILE_FLAGS

View File

@@ -21,7 +21,7 @@ AM_LDFLAGS = \
# This is not at all related to the release version, but a range of supported
# API versions. Read TODO_RELEASE in the source tree's root!
MGCP_CLIENT_LIBVERSION=9:0:0
MGCP_CLIENT_LIBVERSION=10:0:1
lib_LTLIBRARIES = \
libosmo-mgcp-client.la \
@@ -35,4 +35,8 @@ 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)

View File

@@ -59,6 +59,7 @@ const struct value_string osmo_mgcpc_codec_names[] = {
{ CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" },
{ CODEC_AMR_8000_1, "AMR/8000/1" },
{ CODEC_AMRWB_16000_1, "AMR-WB/16000/1" },
{ CODEC_IUFP, "VND.3GPP.IUFP/16000" },
{ 0, NULL },
};
@@ -441,7 +442,7 @@ static 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, "MGW offered Osmux CID %u\n", osmux_cid);
return osmux_cid;
}
@@ -725,10 +726,6 @@ static int mgcp_do_read(struct osmo_fd *fd)
msgb_free(msg);
return -1;
} else if (ret > 4096 - 128) {
LOGPMGW(mgcp, LOGL_ERROR, "Too much data: %s: %d\n", osmo_sock_get_name2(fd->fd), ret);
msgb_free(msg);
return -1;
}
msg->l2h = msgb_put(msg, ret);
@@ -772,7 +769,7 @@ struct mgcp_client *mgcp_client_init(void *ctx,
mgcp->actual.local_addr = conf->local_addr ? conf->local_addr :
MGCP_CLIENT_LOCAL_ADDR_DEFAULT;
mgcp->actual.local_port = conf->local_port >= 0 ? (uint16_t)conf->local_port :
mgcp->actual.local_port = conf->local_port > 0 ? (uint16_t)conf->local_port :
MGCP_CLIENT_LOCAL_PORT_DEFAULT;
mgcp->actual.remote_addr = conf->remote_addr ? conf->remote_addr :
@@ -802,49 +799,6 @@ struct mgcp_client *mgcp_client_init(void *ctx,
return mgcp;
}
static int init_socket(struct mgcp_client *mgcp, unsigned int retry_n_ports)
{
int rc;
struct osmo_wqueue *wq;
unsigned int i;
wq = &mgcp->wq;
for (i = 0; i < retry_n_ports + 1; i++) {
/* Initialize socket with the currently configured port number */
rc = osmo_sock_init2_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
if (rc > 0)
return rc;
/* If there is a different port than the default port configured then we assume that the user has
* chosen that port conciously and we will not try to resolve this by silently choosing a different
* port. */
if (mgcp->actual.local_port != MGCP_CLIENT_LOCAL_PORT_DEFAULT && i == 0)
return -EINVAL;
if (i == retry_n_ports) {
/* Last try failed */
LOGPMGW(mgcp, LOGL_NOTICE, "Failed to bind to %s:%d -- check configuration!\n",
mgcp->actual.local_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.local_port);
if (retry_n_ports == 0)
return -EINVAL;
} else {
/* Choose a new port number to try next */
LOGPMGW(mgcp, LOGL_NOTICE,
"Failed to bind to %s:%d, retrying with port %d -- check configuration!\n",
mgcp->actual.local_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.local_port,
mgcp->actual.local_port + 1);
mgcp->actual.local_port++;
}
}
LOGPMGW(mgcp, LOGL_FATAL, "Failed to find a port to bind on %u times -- check configuration!\n", i);
return -EINVAL;
}
/* Safely ignore the MGCP response to the DLCX sent via _mgcp_client_send_dlcx() */
static void _ignore_mgcp_response(struct mgcp_response *response, void *priv) { }
@@ -881,9 +835,8 @@ static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgc
/*! Initialize client connection (opens socket)
* \param[in,out] mgcp MGCP client descriptor.
* \param[in] retry_n_ports number of consecutive local ports that should be used to retry on failure.
* \returns 0 on success, -EINVAL on error. */
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports)
int mgcp_client_connect(struct mgcp_client *mgcp)
{
struct osmo_wqueue *wq;
int rc;
@@ -902,7 +855,9 @@ int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports)
osmo_fd_setup(&wq->bfd, -1, OSMO_FD_READ, osmo_wqueue_bfd_cb, mgcp, 0);
rc = init_socket(mgcp, retry_n_ports);
rc = osmo_sock_init2_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
if (rc < 0) {
LOGPMGW(mgcp, LOGL_FATAL,
"Failed to initialize socket %s:%u -> %s:%u for MGW: %s\n",
@@ -928,12 +883,12 @@ error_close_fd:
return rc;
}
/*! Initialize client connection (opens socket)
/*! DEPRECATED: Initialize client connection (opens socket)
* \param[in,out] mgcp MGCP client descriptor.
* \returns 0 on success, -EINVAL on error. */
int mgcp_client_connect(struct mgcp_client *mgcp)
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports)
{
return mgcp_client_connect2(mgcp, 99);
return mgcp_client_connect(mgcp);
}
/*! Terminate client connection

View File

@@ -227,7 +227,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 +243,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();
@@ -997,7 +997,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

@@ -760,7 +760,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,37 @@ void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp
pool->mgcp_client_single = mgcp_client;
}
/*! 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). */
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) {
llist_for_each_entry(pool_member, &pool->member_list, list) {
n_pool_members++;
if (pool_member->blocked == false && pool_member->client) {
if (!pool_member_picked)
pool_member_picked = pool_member;
else if (pool_member_picked->refcount > pool_member->refcount)
pool_member_picked = pool_member;
} else {
LOGPPMGW(pool_member, LOGL_DEBUG, "MGW pool has %u members -- MGW %u is unusable\n", n_pool_members,
pool_member->nr);
LOGPPMGW(pool_member, LOGL_DEBUG, "%s -- MGW %u is unusable (blocked=%u, cli=%u)\n",
__func__, pool_member->nr, pool_member->blocked, !!pool_member->client);
}
}
@@ -163,26 +134,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 +163,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

@@ -32,10 +32,11 @@
#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;
@@ -51,13 +52,18 @@ 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 User Manual and migrating to 'mgw' node.%s",
VTY_NEWLINE);
return global_mgcp_client_conf;
}
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 +78,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 +99,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 +121,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 +142,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 +158,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 +186,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 +200,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 +247,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 +274,11 @@ 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")
static int config_write(struct vty *vty, const char *indent, struct mgcp_client_conf *conf)
{
@@ -242,33 +286,36 @@ 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);
return CMD_SUCCESS;
}
@@ -279,6 +326,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 +338,15 @@ 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);
osmo_fsm_vty_add_cmds();
}
@@ -319,38 +371,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 +435,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 +455,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 +468,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 +480,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 +493,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 +512,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 +529,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,20 +539,20 @@ 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) {
vty_out(vty, "%% MGW %s%s", mgcp_client_pool_member_name(pool_member), VTY_NEWLINE);
vty_out(vty, "%% mgcp-client: %s%s", pool_member->client ? "connected" : "disconnected",
VTY_NEWLINE);
@@ -520,7 +566,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 +575,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 +590,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 +610,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

@@ -45,7 +45,7 @@ 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 \
$(NULL)

View File

@@ -14,10 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
static inline int val_seg(int val)

View File

@@ -345,6 +345,18 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
return -EINVAL;
}
/* 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;
}
/* 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":
@@ -355,7 +367,7 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
*
* https://tools.ietf.org/html/rfc4867
*/
static bool amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
{
if (!codec->param_present)
return false;
@@ -378,10 +390,10 @@ static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *c
return false;
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
return false;
if (!strcmp(codec_a->subtype_name, "AMR")) {
if (amr_is_octet_aligned(codec_a) != amr_is_octet_aligned(codec_b))
return false;
}
/* 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. */
return true;
}
@@ -417,7 +429,7 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp
if (!codec_src)
return -EINVAL;
/* Use the codec infrmation from the source and try to find the
/* 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;
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);

View File

@@ -30,12 +30,14 @@
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_iuup.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/core/rate_ctr.h>
#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,
@@ -93,30 +95,36 @@ 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;
memset(&end->addr, 0, sizeof(end->addr));
end->rtcp_port = 0;
talloc_free(end->fmtp_extra);
end->fmtp_extra = NULL;
/* Set default values */
end->frames_per_packet = 0; /* unknown */
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
end->output_enabled = 0;
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);
@@ -129,8 +137,10 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
{
if (mgcp_conn_rtp_is_osmux(conn_rtp))
conn_osmux_disable(conn_rtp);
if (mgcp_conn_rtp_is_iuup(conn_rtp))
mgcp_conn_iuup_cleanup(conn_rtp);
mgcp_free_rtp_port(&conn_rtp->end);
rate_ctr_group_free(conn_rtp->rate_ctr_group);
rate_ctr_group_free(conn_rtp->ctrg);
mgcp_codec_reset_all(conn_rtp);
}
@@ -259,7 +269,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;
@@ -325,6 +335,11 @@ void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
/*! free all connections at once.
* \param[in] endp associated endpoint */
#if defined(__has_attribute)
#if __has_attribute(no_sanitize)
__attribute__((no_sanitize("undefined"))) /* ubsan detects a misaligned load */
#endif
#endif
void mgcp_conn_free_all(struct mgcp_endpoint *endp)
{
struct mgcp_conn *conn;
@@ -351,14 +366,38 @@ char *mgcp_conn_dump(struct mgcp_conn *conn)
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));
switch (conn->u.rtp.type) {
case MGCP_RTP_DEFAULT:
/* 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),
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa),
ntohs(conn->u.rtp.end.rtcp_port));
break;
case MGCP_RTP_OSMUX:
snprintf(str, sizeof(str), "(%s/osmux, id:0x%s, ip:%s, "
"port:%u CID:%u)",
conn->name, conn->id,
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa),
conn->u.rtp.osmux.local_cid);
break;
case MGCP_RTP_IUUP:
snprintf(str, sizeof(str), "(%s/iuup, id:0x%s, ip:%s, "
"port:%u)",
conn->name, conn->id,
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa));
break;
default:
/* Should not happen, we should be able to dump
* every possible connection type. */
snprintf(str, sizeof(str), "(unknown conn_rtp connection type %u)",
conn->u.rtp.type);
break;
}
break;
default:

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

@@ -496,7 +496,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 resources.
* \param[in] endp endpoint to equip
* \param[in] ts E1 timeslot number.
* \param[in] ss E1 subslot number.

View File

@@ -0,0 +1,753 @@
/*
* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All rights not specifically granted under this license are reserved.
*
* Author: Pau Espin Pedrol
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*/
#include <stdint.h>
#include <osmocom/core/byteswap.h>
#include <osmocom/gsm/iuup.h>
#include <osmocom/netif/rtp.h>
#include <osmocom/netif/amr.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_iuup.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_network.h>
#include <osmocom/mgcp/debug.h>
#define MGW_IUUP_MSGB_SIZE 4096
static const struct osmo_iuup_rnl_config def_configure_req = {
.transparent = false,
.active = true,
.supported_versions_mask = 0x0003,
.num_rfci = 0,
.num_subflows = 0,
.IPTIs_present = false,
.t_init = { .t_ms = IUUP_TIMER_INIT_T_DEFAULT, .n_max = IUUP_TIMER_INIT_N_DEFAULT },
.t_ta = { .t_ms = IUUP_TIMER_TA_T_DEFAULT, .n_max = IUUP_TIMER_TA_N_DEFAULT },
.t_rc = { .t_ms = IUUP_TIMER_RC_T_DEFAULT, .n_max = IUUP_TIMER_RC_N_DEFAULT },
};
/* Find a destination connection. */
static struct mgcp_conn *_find_dst_conn(struct mgcp_conn *conn)
{
/* NOTE: This code path runs every time an RTP packet is received. The
* function mgcp_find_dst_conn() we use to determine the detination
* connection will iterate the connection list inside the endpoint.
* Since list iterations are quite costly, we will figure out the
* destination only once and use the optional private data pointer of
* the connection to cache the destination connection pointer. */
struct mgcp_conn *conn_dst;
if (!conn->priv) {
conn_dst = mgcp_find_dst_conn(conn);
conn->priv = conn_dst;
} else {
conn_dst = (struct mgcp_conn *)conn->priv;
}
return conn_dst;
}
/* Find RFCI containing all 0 sizes, -1 if not found. irp is an Initialization.ind prim */
static int _find_rfci_no_data(struct osmo_iuup_rnl_prim *irp)
{
int i;
uint8_t rfci_cnt = 0;
/* Find RFCI containing NO_DATA: */
for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) {
struct osmo_iuup_rfci *rfci = &irp->u.status.u.initialization.rfci[i];
int j;
bool is_no_data;
if (!rfci->used)
continue;
rfci_cnt++;
is_no_data = true;
for (j = 0; j < irp->u.status.u.initialization.num_subflows; j++) {
if (rfci->subflow_sizes[j]) {
is_no_data = false;
break;
}
}
if (is_no_data)
return rfci->id;
/* early loop termination: */
if (rfci_cnt == irp->u.status.u.initialization.num_subflows)
break;
}
return -1;
}
/* Lookup RFCI to use for specific AMR codec type. -1 if none found */
static int8_t _conn_iuup_amr_ft_2_rfci(struct mgcp_conn_rtp *conn_rtp, uint8_t ft)
{
int8_t i;
uint8_t rfci_cnt = 0;
unsigned match_bytes = (unsigned)osmo_amr_bytes(ft);
struct osmo_iuup_rnl_prim *irp = conn_rtp->iuup.init_ind;
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++) {
struct osmo_iuup_rfci *rfci = &irp->u.status.u.initialization.rfci[i];
int j;
unsigned num_bits;
if (!rfci->used)
continue;
rfci_cnt++;
num_bits = 0;
for (j = 0; j < irp->u.status.u.initialization.num_subflows; j++)
num_bits += rfci->subflow_sizes[j];
if (match_bytes == (num_bits + 7)/8)
return rfci->id;
/* early loop termination: */
if (rfci_cnt == irp->u.status.u.initialization.num_subflows)
break;
}
return -1;
}
/* Helper function to configure IuUP layer FSM as Init-Passive, based on default config */
static int _conn_iuup_configure_as_passive(struct mgcp_conn_rtp *conn_rtp)
{
struct osmo_iuup_rnl_prim *irp;
int rc;
conn_rtp->iuup.active_init = false;
/* Tx CONFIG.req */
irp = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
irp->u.config = def_configure_req;
irp->u.config.active = conn_rtp->iuup.active_init;
if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp)) == 0)
conn_rtp->iuup.configured = true;
else
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed configuring IuUP layer\n");
return rc;
}
/* Helper function to configure IuUP layer FSM as Init-Active, based on received
* RNL Status-Init primitive from the sister IuUP connection we will bridge to. */
static int _conn_iuup_configure_as_active(struct mgcp_conn_rtp *conn_rtp, struct osmo_iuup_rnl_prim *init_ind)
{
struct osmo_iuup_rnl_prim *irp = init_ind;
struct osmo_iuup_rnl_prim *irp2;
struct msgb *msg;
bool prev_output_enabled;
int rc;
conn_rtp->iuup.active_init = true;
/* Find RFCI containing NO_DATA: */
conn_rtp->iuup.rfci_id_no_data = _find_rfci_no_data(init_ind);
/* Copy over the rfci_id_no_data, since we reuse the same subflow set: */
msg = msgb_copy_c(conn_rtp->conn, irp->oph.msg, "iuup-init-copy");
conn_rtp->iuup.init_ind = (struct osmo_iuup_rnl_prim *)msgb_data(msg);
conn_rtp->iuup.init_ind->oph.msg = msg;
/* Tx CONFIG.req */
irp2 = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
irp2->u.config.transparent = false;
irp2->u.config.active = conn_rtp->iuup.active_init;
irp2->u.config.data_pdu_type = irp->u.status.u.initialization.data_pdu_type;
irp2->u.config.supported_versions_mask = def_configure_req.supported_versions_mask;
irp2->u.config.num_rfci = irp->u.status.u.initialization.num_rfci;
irp2->u.config.num_subflows = irp->u.status.u.initialization.num_subflows;
irp2->u.config.IPTIs_present = irp->u.status.u.initialization.IPTIs_present;
memcpy(irp2->u.config.rfci, irp->u.status.u.initialization.rfci, sizeof(irp2->u.config.rfci));
irp2->u.config.t_init = def_configure_req.t_init;
irp2->u.config.t_ta = def_configure_req.t_ta;
irp2->u.config.t_rc = def_configure_req.t_rc;
/* We need to force allowance of RTP containing Init-ACK back: */
prev_output_enabled = conn_rtp->end.output_enabled;
conn_rtp->end.output_enabled = true;
if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp2)) == 0)
conn_rtp->iuup.configured = true;
else
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed configuring IuUP layer\n");
conn_rtp->end.output_enabled = prev_output_enabled;
return rc;
}
/* Helper function to push an RTP+IuUP pkt up to the IuUP layer FSM through the
* TNL primitive interface. */
static int _conn_iuup_rtp_pl_up(struct mgcp_conn_rtp *conn_rtp, struct msgb *msg)
{
/* Send RTP payload (IuUP) up the stack: */
struct osmo_iuup_tnl_prim *itp;
int rc;
msg->l2h = msgb_data(msg) + sizeof(struct rtp_hdr);
itp = osmo_iuup_tnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, MGW_IUUP_MSGB_SIZE);
itp->oph.msg->l2h = msgb_put(itp->oph.msg, msgb_l2len(msg));
memcpy(itp->oph.msg->l2h, msgb_l2(msg), msgb_l2len(msg));
if ((rc = osmo_iuup_tnl_prim_up(conn_rtp->iuup.iui, itp)) != 0) {
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed passing IuUP-Init to IuUP layer\n");
}
return rc;
}
static int check_rtp_iuup(const struct mgcp_conn_rtp *conn_rtp, struct msgb *msg)
{
size_t min_size = sizeof(struct rtp_hdr);
/* Check there's at least 2 bytes of RTP payload (IuUP header). This is
** mainly to avoid 0-byte payload copy cases */
if (msgb_length(msg) < sizeof(struct rtp_hdr) + 2) {
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "RTP-IuUP packet too short (%u < %zu)\n",
msgb_length(msg), min_size);
return -1;
}
return 0;
}
/* Bridge received IuUP packet in conn_rtp_src to conn_rtp_dst, an IuUP sister
* conn in the endpoint. The function takes ownsership of the irp */
static int bridge_iuup_to_iuup_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mgcp_conn_rtp *conn_rtp_dst, struct osmo_iuup_rnl_prim *irp)
{
int rc;
/* If we are not configured and we received bridged data, it means
* conn_rtp_src is already configured and INITed, and we can infer
* conn_rtp_src is Init-passive (RNC side), so conn_rtp_dst needs to be
* configured as INIT-active: */
if (!conn_rtp_dst->iuup.configured) {
OSMO_ASSERT(conn_rtp_src->iuup.init_ind);
rc = _conn_iuup_configure_as_active(conn_rtp_dst, conn_rtp_src->iuup.init_ind);
if (rc < 0) {
msgb_free(irp->oph.msg);
return rc;
}
}
/* We simply forward the msg, without freeing it: */
talloc_steal(conn_rtp_dst->conn, irp->oph.msg);
irp->oph.operation = PRIM_OP_REQUEST;
if ((rc = osmo_iuup_rnl_prim_down(conn_rtp_dst->iuup.iui, irp)) != 0)
LOG_CONN_RTP(conn_rtp_dst, LOGL_ERROR, "Failed Tx data down to IuUP layer\n");
return rc;
}
/* Bridge received IuUP packet in conn_rtp_src to conn_rtp_dst, an RTP (no IuUP)
* sister conn in the endpoint. The function takes ownsership of the irp */
static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mgcp_conn_rtp *conn_rtp_dst, struct osmo_iuup_rnl_prim *irp)
{
/* FIXME: We probably need transcoding here?! Or at least look up AMR modes and translate to related RFCI */
uint8_t frame_nr = irp->u.data.frame_nr;
uint8_t fqc = irp->u.data.fqc;
struct msgb *msg = irp->oph.msg;
ssize_t amr_length = 0;
int ft;
uint8_t *amr_data;
struct rtp_hdr *rtp_hdr;
struct amr_hdr *amr_hdr;
int rc;
ft = osmo_amr_bytes_to_ft(msgb_l3len(msg));
if (ft < 0) {
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
"Unknown AMR format for size %u\n", msgb_l3len(msg));
msgb_free(msg);
return ft;
}
msgb_pull_to_l3(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;
amr_hdr->q = !fqc;
amr_hdr->ft = ft & 0xff;
amr_hdr->pad1 = 0;
amr_hdr->pad2 = 0;
} else {
OSMO_ASSERT(msgb_tailroom(msg) >= 2);
msgb_put(msg, 2);
osmo_amr_iuup_to_bwe(msgb_data(msg), msgb_length(msg) - 2, msgb_length(msg) + 2);
/* fill bwe header */
amr_data = msgb_data(msg);
/* CMR no change | follow bit | ft (3 of 4 bits) */
amr_data[0] = 15 << 4 | (0 << 3) | (ft >> 1);
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){
.csrc_count = 0,
.extension = 0,
.padding = 0,
.version = 0,
.payload_type = conn_rtp_dst->end.codec->payload_type,
.marker = 0,
.sequence = frame_nr,
.timestamp = 0,
.ssrc = 0
};
rc = mgcp_send(conn_rtp_dst->conn->endp, true, NULL, msg, conn_rtp_src, conn_rtp_dst);
msgb_free(msg);
return rc;
}
/* Handle RNL Data primitive received from the IuUP layer FSM: Bridge it to the
* sister connection in the endpoint: */
static int _conn_iuup_rx_rnl_data(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp)
{
struct mgcp_conn *conn_dst;
struct mgcp_conn_rtp *conn_rtp_dst;
int rc;
conn_dst = _find_dst_conn(conn_rtp_src->conn);
/* There is no destination conn, stop here */
if (!conn_dst) {
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_DEBUG,
"no connection to forward an incoming IuUP payload to\n");
rc = -1;
goto free_ret;
}
/* The destination conn is not an RTP/IuUP connection */
if (conn_dst->type != MGCP_CONN_TYPE_RTP) {
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
"unable to find suitable destination conn\n");
rc = -1;
goto free_ret;
}
conn_rtp_dst = &conn_dst->u.rtp;
switch (conn_rtp_dst->type) {
case MGCP_RTP_IUUP:
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_RTP_OSMUX:
default:
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
"Forward of IuUP payload to RTP connection type %u not supported!\n",
conn_rtp_dst->type);
rc = 0;
}
free_ret:
msgb_free(irp->oph.msg);
return rc;
}
/* Handle RNL Status-Init primitive received from the IuUP layer FSM.
* Potentially configure sister conn as IuUP Init-Active: */
static int _conn_iuup_rx_rnl_status_init(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp)
{
struct mgcp_conn *conn_dst;
struct mgcp_conn_rtp *conn_rtp_dst;
int rc = 0;
struct msgb *msg;
if (conn_rtp_src->iuup.init_ind) {
/* We received more than one IuUP Initialization. It's probably
* a retransmission, so simply ignore it (lower layers take care
* of ACKing it). */
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_INFO,
"Ignoring potential IuUP Initialization retrans\n");
return 0;
}
msg = msgb_copy_c(conn_rtp_src->conn, irp->oph.msg, "iuup-init-copy");
conn_rtp_src->iuup.init_ind = (struct osmo_iuup_rnl_prim *)msgb_data(msg);
conn_rtp_src->iuup.init_ind->oph.msg = msg;
/* Find RFCI containing NO_DATA: */
conn_rtp_src->iuup.rfci_id_no_data = _find_rfci_no_data(irp);
conn_dst = _find_dst_conn(conn_rtp_src->conn);
/* If not yet there, peer will potentially be IuUP-Initialized later
* when we attempt to bridge audio towards it. See bridge_iuup_to_iuup_peer() */
if (!conn_dst)
return 0;
conn_rtp_dst = &conn_dst->u.rtp;
if (!mgcp_conn_rtp_is_iuup(conn_rtp_dst))
return 0; /* Nothing to do */
/* We received IuUP parameters on the peer (RNC), Init actively this conn (against CN): */
if (!conn_rtp_dst->iuup.configured)
rc = _conn_iuup_configure_as_active(conn_rtp_dst, irp);
return rc;
}
/* Handle RNL Status primitives received from the IuUP layer FSM: */
static int _conn_iuup_rx_rnl_status(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp)
{
int rc;
switch (irp->u.status.procedure) {
case IUUP_PROC_INIT:
rc = _conn_iuup_rx_rnl_status_init(conn_rtp_src, irp);
break;
case IUUP_PROC_RATE_CTRL:
case IUUP_PROC_TIME_ALIGN:
case IUUP_PROC_ERR_EVENT:
default:
LOG_CONN_RTP(conn_rtp_src, LOGL_ERROR,
"Received IuUP RNL STATUS procedure type %u not handled\n",
irp->u.status.procedure);
rc = 0;
}
return rc;
}
/* Received RNL primitive from the IuUP layer FSM containing IuUP Status or
* data. Continue pushing it up the stack, either IuUP Status or Data: */
static int _conn_iuup_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
struct mgcp_conn_rtp *conn_rtp_src = ctx;
struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
struct msgb *msg = oph->msg;
int rc;
switch (OSMO_PRIM_HDR(&irp->oph)) {
case OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION):
/* we pass ownsership of msg here: */
rc = _conn_iuup_rx_rnl_data(conn_rtp_src, irp);
break;
case OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION):
rc = _conn_iuup_rx_rnl_status(conn_rtp_src, irp);
msgb_free(msg);
break;
default:
msgb_free(msg);
OSMO_ASSERT(false);
}
return rc;
}
/*! Send |RTP+IuUP| data down the stack of the specified destination connection.
* \param[in] endp associated endpoint (for configuration, logging).
* \param[in] buf buffer that contains the |RTP+IuUP| data.
* \param[in] len length of the buffer that contains the |RTP+IuUP| data.
* \param[in] conn_src associated source connection.
* \param[in] conn_dst associated destination connection.
* \returns 0 on success, -1 on ERROR. */
static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst)
{
/*! When no destination connection is available (e.g. when only one
* connection in loopback mode exists), then the source connection
* shall be specified as destination connection */
struct mgcp_rtp_end *rtp_end;
struct mgcp_rtp_state *rtp_state;
char ipbuf[INET6_ADDRSTRLEN];
struct rtp_hdr *hdr = (struct rtp_hdr *)msgb_data(msg);
int buflen = msgb_length(msg);
char *dest_name;
int len;
OSMO_ASSERT(conn_src);
OSMO_ASSERT(conn_dst);
LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering IuUP packet...\n");
/* Note: In case of loopback configuration, both, the source and the
* destination will point to the same connection. */
rtp_end = &conn_dst->end;
rtp_state = &conn_src->state;
dest_name = conn_dst->conn->name;
/* Ensure we have an alternative SSRC in case we need it, see also
* gen_rtp_header() */
if (rtp_state->alt_rtp_tx_ssrc == 0)
rtp_state->alt_rtp_tx_ssrc = rand();
if (!rtp_end->output_enabled) {
rtpconn_rate_ctr_add(conn_dst, endp, RTP_DROPPED_PACKETS_CTR, 1);
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"output disabled, drop to %s %s "
"rtp_port:%u rtcp_port:%u\n",
dest_name,
osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
);
return 0;
}
/* Specs say, in IuUP, the RTP seqnum and timestamp should actually be
* ignored by the receiver, but still it's useful for debug purposes
* to set it. Moreover, it seems ip.access nano3g produces much worse
* audio output on the air side if timestamp is not set properly. */
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate));
hdr->sequence = osmo_htons(rtp_state->alt_rtp_tx_sequence);
hdr->ssrc = rtp_state->alt_rtp_tx_ssrc;
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),
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);
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, (char *)hdr, buflen);
if (len <= 0)
return len;
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++;
return len;
}
/* Received TNL primitive from IuUP layer FSM, transmit it further down to the
* socket towards destination peer. */
static int _conn_iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
struct mgcp_conn_rtp *conn_rtp_dst = ctx;
struct mgcp_conn *conn_dst = conn_rtp_dst->conn;
struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
struct mgcp_conn *conn_src;
struct msgb *msg;
struct rtp_hdr *rtph;
OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
msg = oph->msg;
talloc_steal(conn_rtp_dst->conn, msg);
msgb_pull_to_l2(msg);
rtph = (struct rtp_hdr *)msgb_push(msg, sizeof(*rtph));
/* TODO: fill rtph properly: */
*rtph = (struct rtp_hdr){
.csrc_count = 0,
.extension = 0,
.padding = 0,
.version = 2,
.payload_type = conn_rtp_dst->end.codec->payload_type,
.marker = 0,
.sequence = 0,
.timestamp = 0,
.ssrc = 0
};
/* The destination of the destination conn is the source conn, right? */
conn_src = _find_dst_conn(conn_dst);
if (!conn_src) {
LOG_CONN_RTP(conn_rtp_dst, LOGL_NOTICE,
"Couldn't find source conn for IuUP dst conn\n");
/* If there's no sister connection we are either still
* initializing (so we want to send back Init (ACK)), or we are
* probably in loopback mode anyway, so use dst as src. */
conn_src = conn_dst;
}
return mgcp_send_iuup(conn_dst->endp, msg, &conn_src->u.rtp, conn_rtp_dst);
}
/* Used to upgrade a regular RTP connection (MGCP_RTP_DEFAULT) to become a IuUP
* connection (MGCP_RTP_IUUP) */
int mgcp_conn_iuup_init(struct mgcp_conn_rtp *conn_rtp)
{
conn_rtp->type = MGCP_RTP_IUUP;
conn_rtp->iuup.iui = osmo_iuup_instance_alloc(conn_rtp->conn, conn_rtp->conn->id);
OSMO_ASSERT(conn_rtp->iuup.iui);
osmo_iuup_instance_set_user_prim_cb(conn_rtp->iuup.iui, _conn_iuup_user_prim_cb, conn_rtp);
osmo_iuup_instance_set_transport_prim_cb(conn_rtp->iuup.iui, _conn_iuup_transport_prim_cb, conn_rtp);
conn_rtp->iuup.rfci_id_no_data = -1;
return 0;
}
/* Cleanup specific IuUP connection (MGCP_RTP_IUUP) state, allocated by mgcp_conn_iuup_init() */
void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp)
{
osmo_iuup_instance_free(conn_rtp->iuup.iui);
conn_rtp->iuup.iui = NULL;
}
/* Received RTP+IuUP pkt from socket of conn_rtp_src, build a TNL primitive to
* push it further up the stack to the IuUP layer FSM to handle and/or bridge it */
int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg)
{
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
struct mgcp_conn_rtp *conn_rtp_src = mc->conn_src;
int rc = 0;
bool force_output_enabled = false;
bool prev_output_enabled;
struct osmo_sockaddr prev_rem_addr;
uint16_t prev_rem_rtp_port;
OSMO_ASSERT(mgcp_conn_rtp_is_iuup(conn_rtp_src));
if ((rc = check_rtp_iuup(conn_rtp_src, msg)) < 0)
goto free_ret;
if (!conn_rtp_src->iuup.configured) {
/* We received the first message without sending any, the peer is the active side (RNC). */
rc = _conn_iuup_configure_as_passive(conn_rtp_src);
if (rc < 0)
goto free_ret;
/* We need to force allowance of RTP containing Init-ACK back: */
prev_output_enabled = conn_rtp_src->end.output_enabled;
conn_rtp_src->end.output_enabled = true;
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 = osmo_sockaddr_port(&conn_rtp_src->end.addr.u.sa);
conn_rtp_src->end.addr = *mc->from_addr;
}
rc = _conn_iuup_rtp_pl_up(conn_rtp_src, msg);
if (force_output_enabled) {
conn_rtp_src->end.output_enabled = prev_output_enabled;
conn_rtp_src->end.addr = prev_rem_addr;
osmo_sockaddr_set_port(&conn_rtp_src->end.addr.u.sa, prev_rem_rtp_port);
}
return rc;
free_ret:
msgb_free(msg);
return rc;
}
/* 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: */
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;
struct rtp_hdr *rtph;
int rc = -1;
int iuup_length = 0;
int8_t rfci;
/* Tx RNL-DATA.req */
rtph = (struct rtp_hdr *)msgb_data(msg);
msgb_pull(msg, sizeof(*rtph));
/* FIXME: validate amr packets */
irp = osmo_iuup_rnl_prim_alloc(conn_dest_rtp->conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
irp->u.data.frame_nr = htons(rtph->sequence) % 16;
/* TODO: CMR handling & multiple frames handling */
if (strcmp(conn_src_rtp->end.codec->subtype_name, "AMR") != 0) {
LOG_CONN_RTP(conn_src_rtp, LOGL_ERROR,
"Bridge RTP=>IuUP: Bridging src codec %s to IuUP AMR not supported\n",
conn_src_rtp->end.codec->subtype_name);
goto free_ret;
}
if (mgcp_codec_amr_is_octet_aligned(conn_src_rtp->end.codec)) {
struct amr_hdr *amr_hdr = (struct amr_hdr *) msgb_data(msg);
if (msgb_length(msg) < (sizeof(*amr_hdr))) {
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE,
"Bridge RTP=>IuUP: too short for AMR OA hdr (%u)\n", msgb_length(msg));
goto free_ret;
}
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;
}
if ((rfci = _conn_iuup_amr_ft_2_rfci(conn_dest_rtp, amr_hdr->ft)) < 0) {
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 ? 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 (!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;
}
if ((rfci = _conn_iuup_amr_ft_2_rfci(conn_dest_rtp, ft)) < 0) {
LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR BE ft=%u\n", ft);
goto free_ret;
}
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) {
LOG_CONN_RTP(conn_dest_rtp, LOGL_ERROR, "Bridge RTP=>IuUP: Failed convert the RTP/AMR to IuUP payload\n");
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));
memcpy(irp->oph.msg->l3h, msgb_data(msg), msgb_length(msg));
if ((rc = osmo_iuup_rnl_prim_down(conn_dest_rtp->iuup.iui, irp)) != 0)
LOG_CONN_RTP(conn_dest_rtp, LOGL_ERROR, "Bridge RTP=>IuUP: Failed Tx RTP payload down the IuUP layer\n");
return rc;
free_ret:
msgb_free(irp->oph.msg);
return -1;
}
/* Build IuUP RNL Data primitive from msg containing dummy content and send it
* down the IuUP layer towards the destination as IuUP/RTP: */
int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp)
{
struct osmo_iuup_rnl_prim *irp;
int rc;
if (conn_rtp->iuup.rfci_id_no_data == -1) {
LOG_CONN_RTP(conn_rtp, LOGL_NOTICE, "No RFCI NO_DATA found, unable to send dummy packet\n");
return -ENOTSUP;
}
irp = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
irp->u.data.frame_nr = 0;
irp->u.data.fqc = IUUP_FQC_FRAME_GOOD;
irp->u.data.rfci = conn_rtp->iuup.rfci_id_no_data;
irp->oph.msg->l3h = irp->oph.msg->tail;
if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp)) != 0) {
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed Tx RTP dummy payload down the IuUP layer\n");
return -EINVAL;
}
return 0;
}

View File

@@ -110,8 +110,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
/* Special handling for RTP connections */
if (conn->type == MGCP_CONN_TYPE_RTP) {
conn->u.rtp.end.output_enabled =
conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
conn->u.rtp.end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY);
}
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
@@ -121,7 +120,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
/* Special handling für RTP connections */
if (conn->type == MGCP_CONN_TYPE_RTP) {
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %d\n",
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %u\n",
conn->u.rtp.end.output_enabled);
}
@@ -203,7 +202,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;
}

View File

@@ -48,21 +48,16 @@
#include <osmocom/mgcp/debug.h>
#include <osmocom/codec/codec.h>
#include <osmocom/mgcp/mgcp_e1.h>
#include <osmocom/mgcp/mgcp_iuup.h>
#define RTP_SEQ_MOD (1 << 16)
#define RTP_MAX_DROPOUT 3000
#define RTP_MAX_MISORDER 100
enum rtp_proto {
MGCP_PROTO_RTP,
MGCP_PROTO_RTCP,
};
static void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
int id, int inc)
{
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
struct rate_ctr_group *conn_stats = conn_rtp->ctrg;
struct rate_ctr_group *mgw_stats = endp->trunk->ratectr.all_rtp_conn_stats;
/* add to both the per-connection and the global stats */
@@ -70,49 +65,68 @@ static void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_end
rate_ctr_add(rate_ctr_group_get_ctr(mgw_stats, id), inc);
}
static void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, int id)
void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, int id)
{
rtpconn_rate_ctr_add(conn_rtp, endp, id, 1);
}
static int rx_rtp(struct msgb *msg);
static bool addr_is_any(const struct osmo_sockaddr *osa)
{
if (osa->u.sa.sa_family == AF_INET6) {
struct in6_addr ip6_any = IN6ADDR_ANY_INIT;
return memcmp(&osa->u.sin6.sin6_addr,
&ip6_any, sizeof(ip6_any)) == 0;
} else {
return osa->u.sin.sin_addr.s_addr == 0;
}
}
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end)
{
return rtp_end->rtp_port && !addr_is_any(&rtp_end->addr);
return (osmo_sockaddr_port(&rtp_end->addr.u.sa) != 0) &&
(osmo_sockaddr_is_any(&rtp_end->addr) == 0);
}
/*! Determine the local rtp bind IP-address.
* \param[out] addr caller provided memory to store the resulting IP-Address.
* \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters.
* \ returns 0 on success, -1 if no local address could be provided.
*
* The local bind IP-address is automatically selected by probing the
* IP-Address of the interface that is pointing towards the remote IP-Address,
* if no remote IP-Address is known yet, the statically configured
* IP-Addresses are used as fallback. */
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
{
struct mgcp_endpoint *endp;
const struct mgcp_endpoint *endp = conn->conn->endp;
const struct mgcp_config *cfg = endp->trunk->cfg;
char ipbuf[INET6_ADDRSTRLEN];
int rc;
endp = conn->conn->endp;
bool rem_addr_set = !addr_is_any(&conn->end.addr);
char *bind_addr;
bool rem_addr_set = osmo_sockaddr_is_any(&conn->end.addr) == 0;
const char *bind_addr;
/* Osmux: No smart IP addresses allocation is supported yet. Simply
* return the one set in VTY config: */
if (mgcp_conn_rtp_is_osmux(conn)) {
if (rem_addr_set) {
/* Match IP version with what was requested from remote: */
bind_addr = conn->end.addr.u.sa.sa_family == AF_INET6 ?
cfg->osmux.local_addr_v6 :
cfg->osmux.local_addr_v4;
} else {
/* Choose any of the bind addresses, preferring v6 over v4 if available: */
bind_addr = cfg->osmux.local_addr_v6;
if (!bind_addr)
bind_addr = cfg->osmux.local_addr_v4;
}
if (!bind_addr) {
LOGPCONN(conn->conn, DOSMUX, LOGL_ERROR,
"Unable to locate local Osmux address, check your configuration! v4=%u v6=%u remote_known=%s\n",
!!cfg->osmux.local_addr_v4,
!!cfg->osmux.local_addr_v6,
rem_addr_set ? osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf) : "no");
return -1;
}
LOGPCONN(conn->conn, DOSMUX, LOGL_DEBUG,
"Using configured osmux bind ip as local bind ip %s\n",
bind_addr);
osmo_strlcpy(addr, bind_addr, INET6_ADDRSTRLEN);
return 0;
}
/* Try probing the local IP-Address */
if (endp->trunk->cfg->net_ports.bind_addr_probe && rem_addr_set) {
if (cfg->net_ports.bind_addr_probe && rem_addr_set) {
rc = osmo_sock_local_ip(addr, osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf));
if (rc < 0)
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
@@ -121,22 +135,22 @@ void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
"selected local rtp bind ip %s by probing using remote ip %s\n",
addr, osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf));
return;
return 0;
}
}
/* Select from preconfigured IP-Addresses. We don't have bind_addr for Osmux (yet?). */
/* Select from preconfigured IP-Addresses. */
if (rem_addr_set) {
/* Check there is a bind IP for the RTP traffic configured,
* if so, use that IP-Address */
bind_addr = conn->end.addr.u.sa.sa_family == AF_INET6 ?
endp->trunk->cfg->net_ports.bind_addr_v6 :
endp->trunk->cfg->net_ports.bind_addr_v4;
cfg->net_ports.bind_addr_v6 :
cfg->net_ports.bind_addr_v4;
} else {
/* Choose any of the bind addresses, preferring v6 over v4 */
bind_addr = endp->trunk->cfg->net_ports.bind_addr_v6;
bind_addr = cfg->net_ports.bind_addr_v6;
if (!strlen(bind_addr))
bind_addr = endp->trunk->cfg->net_ports.bind_addr_v4;
bind_addr = cfg->net_ports.bind_addr_v4;
}
if (strlen(bind_addr)) {
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
@@ -146,17 +160,18 @@ void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
/* No specific bind IP is configured for the RTP traffic, so
* assume the IP where we listen for incoming MGCP messages
* as bind IP */
bind_addr = endp->trunk->cfg->source_addr;
bind_addr = cfg->source_addr;
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
"using mgcp bind ip as local rtp bind ip: %s\n", bind_addr);
}
osmo_strlcpy(addr, bind_addr, INET6_ADDRSTRLEN);
return 0;
}
/* This does not need to be a precision timestamp and
* is allowed to wrap quite fast. The returned value is
* 1/codec_rate seconds. */
static uint32_t get_current_ts(unsigned codec_rate)
uint32_t mgcp_get_current_ts(unsigned codec_rate)
{
struct timespec tp;
uint64_t ret;
@@ -529,7 +544,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
seq = ntohs(rtp_hdr->sequence);
timestamp = ntohl(rtp_hdr->timestamp);
arrival_time = get_current_ts(rtp_end->codec->rate);
arrival_time = mgcp_get_current_ts(rtp_end->codec->rate);
ssrc = ntohl(rtp_hdr->ssrc);
marker_bit = !!rtp_hdr->marker;
transit = arrival_time - timestamp;
@@ -676,7 +691,7 @@ static int rfc5993_hr_convert(struct mgcp_endpoint *endp, struct msgb *msg)
{
struct rtp_hdr *rtp_hdr;
if (msgb_length(msg) < sizeof(struct rtp_hdr)) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n",
LOGPENDP(endp, DRTP, LOGL_ERROR, "RTP packet too short (%d < %zu)\n",
msgb_length(msg), sizeof(struct rtp_hdr));
return -EINVAL;
}
@@ -702,11 +717,18 @@ static int rfc5993_hr_convert(struct mgcp_endpoint *endp, struct msgb *msg)
return 0;
}
/* For AMR RTP two framing modes are defined RFC3267. There is a bandwith
/*! Convert msg to AMR RTP framing mode specified by target_is_oa.
* \param[in] endp MGCP Endpoint where this message belongs to (used for logging purposes)
* \param[in] msg Message containing an AMR RTP payload (in octet-aligned or bandwidth-efficient format).
* \param[in] target_is_oa the target framing mode that msg will contain after this function succeeds.
* \returns The size of the new RTP AMR content on success, negative on error.
*
* For AMR RTP two framing modes are defined RFC3267. There is a bandwidth
* efficient encoding scheme where all fields are packed together one after
* another and an octet aligned mode where all fields are aligned to octet
* boundaries. This function is used to convert between the two modes */
static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
* boundaries. This function is used to convert between the two modes.
*/
int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
bool target_is_oa)
{
/* NOTE: the msgb has an allocated length of RTP_BUF_SIZE, so there is
@@ -715,6 +737,7 @@ static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
struct rtp_hdr *rtp_hdr;
unsigned int payload_len;
int rc;
bool orig_is_oa;
if (msgb_length(msg) < sizeof(struct rtp_hdr)) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n", msgb_length(msg), sizeof(struct rtp_hdr));
@@ -722,10 +745,10 @@ static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
}
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
payload_len = msgb_length(msg) - sizeof(struct rtp_hdr);
orig_is_oa = osmo_amr_is_oa(rtp_hdr->data, payload_len);
if (osmo_amr_is_oa(rtp_hdr->data, payload_len)) {
if (orig_is_oa) {
if (!target_is_oa)
/* Input data is oa an target format is bwe
* ==> convert */
@@ -747,27 +770,16 @@ static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
}
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"AMR RTP packet conversion failed\n");
"RTP AMR packet conversion %s->%s failed: %s\n",
orig_is_oa ? "OA" : "BWE",
target_is_oa ? "OA" : "BWE",
osmo_hexdump(rtp_hdr->data, payload_len));
return -EINVAL;
}
return msgb_trim(msg, rc + sizeof(struct rtp_hdr));
}
/* Check if a conversion between octet-aligned and bandwith-efficient mode is
* indicated. */
static bool amr_oa_bwe_convert_indicated(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;
}
/* Return whether an RTP packet with AMR payload is in octet-aligned mode.
* Return 0 if in bandwidth-efficient mode, 1 for octet-aligned mode, and negative if the RTP data is invalid. */
static int amr_oa_check(char *data, int len)
@@ -789,7 +801,7 @@ static int amr_oa_check(char *data, int len)
/* Forward data to a debug tap. This is debug function that is intended for
* debugging the voice traffic with tools like gstreamer */
static void forward_data(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
{
int rc;
@@ -815,7 +827,7 @@ static void gen_rtp_header(struct msgb *msg, struct mgcp_rtp_end *rtp_end,
hdr->version = 2;
hdr->payload_type = rtp_end->codec->payload_type;
hdr->timestamp = osmo_htonl(get_current_ts(rtp_end->codec->rate));
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate));
hdr->sequence = osmo_htons(state->alt_rtp_tx_sequence);
hdr->ssrc = state->alt_rtp_tx_ssrc;
}
@@ -826,7 +838,7 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad
{
char ipbuf[INET6_ADDRSTRLEN];
if (addr_is_any(&conn->end.addr)) {
if (osmo_sockaddr_is_any(&conn->end.addr) != 0) {
switch (conn->conn->mode) {
case MGCP_CONN_LOOPBACK:
/* HACK: for IuUP, we want to reply with an IuUP Initialization ACK upon the first RTP
@@ -875,14 +887,14 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad
* the same as the remote port where we transmit outgoing RTP traffic
* to (set by MDCX). We use this to check the origin of the data for
* plausibility. */
if (ntohs(conn->end.rtp_port) != osmo_sockaddr_port(&addr->u.sa) &&
if (osmo_sockaddr_port(&conn->end.addr.u.sa) != osmo_sockaddr_port(&addr->u.sa) &&
ntohs(conn->end.rtcp_port) != osmo_sockaddr_port(&addr->u.sa)) {
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
"data from wrong source port: %d, ",
osmo_sockaddr_port(&addr->u.sa));
LOGPC(DRTP, LOGL_ERROR,
"expected: %d for RTP or %d for RTCP\n",
ntohs(conn->end.rtp_port), ntohs(conn->end.rtcp_port));
osmo_sockaddr_port(&conn->end.addr.u.sa), ntohs(conn->end.rtcp_port));
LOGPCONN(conn->conn, DRTP, LOGL_ERROR, "packet tossed\n");
return -1;
}
@@ -895,28 +907,29 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad
static int check_rtp_destin(struct mgcp_conn_rtp *conn)
{
char ipbuf[INET6_ADDRSTRLEN];
bool ip_is_any = addr_is_any(&conn->end.addr);
bool ip_is_any = osmo_sockaddr_is_any(&conn->end.addr) != 0;
uint16_t port = osmo_sockaddr_port(&conn->end.addr.u.sa);
/* Note: it is legal to create a connection but never setting a port
* and IP-address for outgoing data. */
if (ip_is_any && conn->end.rtp_port == 0) {
if (ip_is_any && port == 0) {
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
"destination IP-address and rtp port is not (yet) known (%s:%u)\n",
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), conn->end.rtp_port);
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), port);
return -1;
}
if (ip_is_any) {
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
"destination IP-address is invalid (%s:%u)\n",
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), conn->end.rtp_port);
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), port);
return -1;
}
if (conn->end.rtp_port == 0) {
if (port == 0) {
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
"destination rtp port is invalid (%s:%u)\n",
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), conn->end.rtp_port);
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), port);
return -1;
}
@@ -975,14 +988,21 @@ static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
* the length is because we currently handle IUUP packets as RTP
* packets, so they must pass this check, if we weould be more
* strict here, we would possibly break 3G. (see also FIXME note
* below */
* below.*/
return 0;
}
/* Send RTP data. Possible options are standard RTP packet
* transmission or trsmission via an osmux connection */
static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
/*! Dispatch msg bridged from the sister conn in the endpoint.
* \param[in] conn_dst The destination conn that should handle and transmit the content to
* its peer outside MGW.
* \param[in] msg msgb containing an RTP pkt received by the sister conn in the endpoint,
* \returns bytes sent, -1 on error.
*
* Possible options are standard RTP packet transmission, transmission
* via IuUP or transmission via an osmux connection.
*/
static int mgcp_conn_rtp_dispatch_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
{
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
enum rtp_proto proto = mc->proto;
@@ -1007,12 +1027,24 @@ static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
"using mgcp_send() to forward data directly\n");
return mgcp_send(endp, proto == MGCP_PROTO_RTP,
mc->from_addr, msg, conn_src, conn_dst);
case MGCP_OSMUX_BSC_NAT:
case MGCP_OSMUX_BSC:
case MGCP_RTP_OSMUX:
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"endpoint type is MGCP_OSMUX_BSC_NAT, "
"endpoint type is MGCP_RTP_OSMUX, "
"using osmux_xfrm_to_osmux() to forward data through OSMUX\n");
return osmux_xfrm_to_osmux((char*)msgb_data(msg), msgb_length(msg), conn_dst);
return conn_osmux_send_rtp(conn_dst, msg);
case MGCP_RTP_IUUP:
if (proto == MGCP_PROTO_RTP) {
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"endpoint type is MGCP_RTP_IUUP, "
"using mgcp_conn_iuup_send_rtp() to forward data over IuUP\n");
return mgcp_conn_iuup_send_rtp(conn_src, conn_dst, msg);
}
/* RTCP: we forward as usual for regular RTP connection */
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"endpoint type is MGCP_RTP_IUUP and proto!=MGCP_PROTO_RTP, "
"using mgcp_send() to forward data directly\n");
return mgcp_send(endp, false,
mc->from_addr, msg, conn_src, conn_dst);
}
/* If the data has not been handled/forwarded until here, it will
@@ -1026,26 +1058,22 @@ static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
/*! send udp packet.
* \param[in] fd associated file descriptor.
* \param[in] addr destination ip-address.
* \param[in] port destination UDP port (network byte order).
* \param[in] buf buffer that holds the data to be send.
* \param[in] len length of the data to be sent.
* \returns bytes sent, -1 on error. */
int mgcp_udp_send(int fd, struct osmo_sockaddr *addr, int port, const char *buf, int len)
int mgcp_udp_send(int fd, const struct osmo_sockaddr *addr, const char *buf, int len)
{
char ipbuf[INET6_ADDRSTRLEN];
size_t addr_len;
bool is_ipv6 = addr->u.sa.sa_family == AF_INET6;
LOGP(DRTP, LOGL_DEBUG,
"sending %i bytes length packet to %s:%u ...\n", len,
osmo_sockaddr_ntop(&addr->u.sa, ipbuf),
ntohs(port));
osmo_sockaddr_port(&addr->u.sa));
if (is_ipv6) {
addr->u.sin6.sin6_port = port;
if (addr->u.sa.sa_family == AF_INET6) {
addr_len = sizeof(addr->u.sin6);
} else {
addr->u.sin.sin_port = port;
addr_len = sizeof(addr->u.sin);
}
@@ -1060,6 +1088,7 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
int rc;
int was_rtcp = 0;
struct osmo_sockaddr rtcp_addr;
OSMO_ASSERT(endp);
OSMO_ASSERT(conn);
@@ -1073,8 +1102,11 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
if (check_rtp_destin(conn) != 0)
goto failed;
rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
conn->end.rtp_port, rtp_dummy_payload, sizeof(rtp_dummy_payload));
if (mgcp_conn_rtp_is_iuup(conn))
rc = mgcp_conn_iuup_send_dummy(conn);
else
rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
rtp_dummy_payload, sizeof(rtp_dummy_payload));
if (rc == -1)
goto failed;
@@ -1083,8 +1115,10 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
return rc;
was_rtcp = 1;
rc = mgcp_udp_send(conn->end.rtcp.fd, &conn->end.addr,
conn->end.rtcp_port, rtp_dummy_payload, sizeof(rtp_dummy_payload));
rtcp_addr = conn->end.addr;
osmo_sockaddr_set_port(&rtcp_addr.u.sa, ntohs(conn->end.rtcp_port));
rc = mgcp_udp_send(conn->end.rtcp.fd, &rtcp_addr,
rtp_dummy_payload, sizeof(rtp_dummy_payload));
if (rc >= 0)
return rc;
@@ -1130,17 +1164,21 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
else
LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering RTCP packet...\n");
/* FIXME: It is legal that the payload type on the egress connection is
* different from the payload type that has been negotiated on the
* ingress connection. Essentially the codecs are the same so we can
* match them and patch the payload type. However, if we can not find
* the codec pendant (everything ist equal except the PT), we are of
* course unable to patch the payload type. A situation like this
* should not occur if transcoding is consequently avoided. Until
* we have transcoding support in osmo-mgw we can not resolve this. */
if (is_rtp) {
/* Patch the payload type number: translate from conn_src to conn_dst.
* Do not patch for IuUP, where the correct payload type number is already set in bridge_iuup_to_rtp_peer():
* IuUP -> AMR: calls this function, skip patching if conn_src is IuUP.
* {AMR or IuUP} -> IuUP: calls mgcp_udp_send() directly, skipping this function: No need to examine dst. */
if (is_rtp && !mgcp_conn_rtp_is_iuup(conn_src)) {
rc = mgcp_patch_pt(conn_src, conn_dst, msg);
if (rc < 0) {
/* FIXME: It is legal that the payload type on the egress connection is
* different from the payload type that has been negotiated on the
* ingress connection. Essentially the codecs are the same so we can
* match them and patch the payload type. However, if we can not find
* the codec pendant (everything ist equal except the PT), we are of
* course unable to patch the payload type. A situation like this
* should not occur if transcoding is consequently avoided. Until
* we have transcoding support in osmo-mgw we can not resolve this. */
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"can not patch PT because no suitable egress codec was found.\n");
}
@@ -1164,7 +1202,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
"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)
);
} else if (is_rtp) {
int cont;
@@ -1185,12 +1223,15 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
mgcp_patch_and_count(endp, rtp_state, rtp_end,
addr, msg);
if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
if (mgcp_conn_rtp_is_iuup(conn_dst) || mgcp_conn_rtp_is_iuup(conn_src)) {
/* the iuup code will correctly transform to the correct AMR mode */
} else if (mgcp_codec_amr_align_mode_is_indicated(conn_dst->end.codec)) {
rc = amr_oa_bwe_convert(endp, msg,
conn_dst->end.codec->param.amr_octet_aligned);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"Error in AMR octet-aligned <-> bandwidth-efficient mode conversion\n");
"Error in AMR octet-aligned <-> bandwidth-efficient mode conversion (target=%s)\n",
conn_dst->end.codec->param.amr_octet_aligned ? "octet-aligned" : "bandwidth-efficient");
break;
}
} else if (rtp_end->rfc5993_hr_convert &&
@@ -1207,32 +1248,14 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
"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(rtp_end->rtp.fd, &conn_src->tap_out,
forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out,
msg);
/* FIXME: HACK HACK HACK. See OS#2459.
* The ip.access nano3G needs the first RTP payload's first two bytes to read hex
* 'e400', or it will reject the RAB assignment. It seems to not harm other femto
* cells (as long as we patch only the first RTP payload in each stream).
*/
if (!rtp_state->patched_first_rtp_payload
&& conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
uint8_t *data = msgb_data(msg) + 12;
if (data[0] == 0xe0) {
data[0] = 0xe4;
data[1] = 0x00;
rtp_state->patched_first_rtp_payload = true;
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"Patching over first two bytes"
" to fake an IuUP Initialization Ack\n");
}
}
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port,
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr,
(char *)msgb_data(msg), msgb_length(msg));
if (len <= 0)
@@ -1247,15 +1270,17 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
} while (buflen > 0);
return nbytes;
} else if (!trunk->omit_rtcp) {
struct osmo_sockaddr rtcp_addr = rtp_end->addr;
osmo_sockaddr_set_port(&rtcp_addr.u.sa, rtp_end->rtcp_port);
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"send 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)
dest_name, osmo_sockaddr_ntop(&rtcp_addr.u.sa, ipbuf),
osmo_sockaddr_port(&rtp_end->addr.u.sa),
osmo_sockaddr_port(&rtcp_addr.u.sa)
);
len = mgcp_udp_send(rtp_end->rtcp.fd,
&rtp_end->addr,
rtp_end->rtcp_port, (char *)msgb_data(msg), msgb_length(msg));
len = mgcp_udp_send(rtp_end->rtcp.fd, &rtcp_addr,
(char *)msgb_data(msg), msgb_length(msg));
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
@@ -1267,13 +1292,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
return 0;
}
/*! dispatch incoming RTP packet to opposite RTP connection.
* \param[in] proto protocol (MGCP_CONN_TYPE_RTP or MGCP_CONN_TYPE_RTCP).
* \param[in] addr socket address where the RTP packet has been received from.
* \param[in] buf buffer that hold the RTP payload.
* \param[in] buf_size size data length of buf.
* \param[in] conn originating connection.
* \returns 0 on success, -1 on ERROR. */
/*! Dispatch incoming RTP packet to opposite RTP connection.
* \param[in] msg Message buffer to bridge, coming from source connection.
* msg shall contain "struct osmo_rtp_msg_ctx *" attached in
* "OSMO_RTP_MSG_CTX(msg)".
* \returns 0 on success, -1 on ERROR.
*/
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
{
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
@@ -1291,6 +1315,9 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
* destination connection is known the RTP packet is sent via
* the destination connection. */
/* If source is IuUP, we need to handle state, forward it through specific bridge path: */
if (mgcp_conn_rtp_is_iuup(conn_src) && mc->proto == MGCP_PROTO_RTP)
return mgcp_conn_iuup_dispatch_rtp(msg);
/* Check if the connection is in loopback mode, if yes, just send the
* incoming data back to the origin */
@@ -1299,25 +1326,15 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
* packets back to their origin. We will use the originating
* address data from the UDP packet header to patch the
* outgoing address in connection on the fly */
if (conn->u.rtp.end.rtp_port == 0) {
if (osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa) == 0) {
memcpy(&conn->u.rtp.end.addr, from_addr,
sizeof(conn->u.rtp.end.addr));
switch (from_addr->u.sa.sa_family) {
case AF_INET:
conn->u.rtp.end.rtp_port = from_addr->u.sin.sin_port;
break;
case AF_INET6:
conn->u.rtp.end.rtp_port = from_addr->u.sin6.sin6_port;
break;
default:
OSMO_ASSERT(false);
}
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
"loopback mode: implicitly using source address (%s:%u) as destination address\n",
osmo_sockaddr_ntop(&from_addr->u.sa, ipbuf),
conn->u.rtp.end.rtp_port);
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa));
}
return mgcp_send_rtp(conn_src, msg);
return mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
}
/* Find a destination connection. */
@@ -1349,7 +1366,7 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
}
/* Dispatch RTP packet to destination RTP connection */
return mgcp_send_rtp(&conn_dst->u.rtp, msg);
return mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg);
}
/*! dispatch incoming RTP packet to E1 subslot, handle RTCP packets locally.
@@ -1374,25 +1391,15 @@ int mgcp_dispatch_e1_bridge_cb(struct msgb *msg)
* packets back to their origin. We will use the originating
* address data from the UDP packet header to patch the
* outgoing address in connection on the fly */
if (conn->u.rtp.end.rtp_port == 0) {
if (osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa) == 0) {
memcpy(&conn->u.rtp.end.addr, from_addr,
sizeof(conn->u.rtp.end.addr));
switch (from_addr->u.sa.sa_family) {
case AF_INET:
conn->u.rtp.end.rtp_port = from_addr->u.sin.sin_port;
break;
case AF_INET6:
conn->u.rtp.end.rtp_port = from_addr->u.sin6.sin6_port;
break;
default:
OSMO_ASSERT(false);
}
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
"loopback mode: implicitly using source address (%s:%u) as destination address\n",
osmo_sockaddr_ntop(&from_addr->u.sa, ipbuf),
conn->u.rtp.end.rtp_port);
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa));
}
return mgcp_send_rtp(conn_src, msg);
return mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
}
/* Forward to E1 */
@@ -1468,7 +1475,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
msgb_put(msg, ret);
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n",
proto == MGCP_PROTO_RTP ? "RTP" : "RTPC",
proto == MGCP_PROTO_RTP ? "RTP" : "RTCP",
msgb_length(msg), osmo_sockaddr_ntop(&addr.u.sa, ipbuf),
osmo_sockaddr_port(&addr.u.sa));
@@ -1501,12 +1508,12 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
sizeof(struct sockaddr_in)));
/* Increment RX statistics */
rate_ctr_inc(rate_ctr_group_get_ctr(conn_src->rate_ctr_group, RTP_PACKETS_RX_CTR));
rate_ctr_add(rate_ctr_group_get_ctr(conn_src->rate_ctr_group, RTP_OCTETS_RX_CTR), msgb_length(msg));
rate_ctr_inc(rate_ctr_group_get_ctr(conn_src->ctrg, RTP_PACKETS_RX_CTR));
rate_ctr_add(rate_ctr_group_get_ctr(conn_src->ctrg, RTP_OCTETS_RX_CTR), msgb_length(msg));
/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */
/* Forward a copy of the RTP data to a debug ip/port */
forward_data(fd->fd, &conn_src->tap_in, msg);
forward_data_tap(fd->fd, &conn_src->tap_in, msg);
rc = rx_rtp(msg);
@@ -1515,6 +1522,7 @@ out:
return rc;
}
/* Note: This function is able to handle RTP and RTCP */
static int rx_rtp(struct msgb *msg)
{
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
@@ -1525,24 +1533,30 @@ static int rx_rtp(struct msgb *msg)
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx_rtp(%u bytes)\n", msgb_length(msg));
mgcp_conn_watchdog_kick(conn_src->conn);
/* If AMR is configured for the ingress connection a conversion of the
* framing mode (octet-aligned vs. bandwith-efficient is explicitly
* define, then we check if the incoming payload matches that
* expectation. */
if (amr_oa_bwe_convert_indicated(conn_src->end.codec)) {
int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg));
if (oa < 0)
return -1;
if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned)
return -1;
}
/* Check if the origin of the RTP packet seems plausible */
if (!trunk->rtp_accept_all && check_rtp_origin(conn_src, from_addr))
return -1;
/* If AMR is configured for the ingress connection and conversion of the
* framing mode (octet-aligned vs. bandwith-efficient) is explicitly
* defined, then we check if the incoming payload matches that
* expectation. */
if (mc->proto == MGCP_PROTO_RTP &&
mgcp_codec_amr_align_mode_is_indicated(conn_src->end.codec)) {
int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg));
if (oa < 0)
return -1;
if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned) {
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
"rx_rtp(%u bytes): Expected RTP AMR octet-aligned=%u but got octet-aligned=%u."
" check the config of your call-agent!\n",
msgb_length(msg), conn_src->end.codec->param.amr_octet_aligned, oa);
return -1;
}
}
mgcp_conn_watchdog_kick(conn_src->conn);
/* Execute endpoint specific implementation that handles the
* dispatching of the RTP data */
return conn->endp->type->dispatch_rtp_cb(msg);

File diff suppressed because it is too large Load Diff

View File

@@ -46,6 +46,8 @@
#include <osmocom/mgcp/mgcp_sdp.h>
#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. */
@@ -97,10 +99,6 @@ struct mgcp_request {
/* function pointer to the request handler */
struct msgb *(*handle_request)(struct mgcp_request_data *data);
/* true if the request requires an endpoint, false if only a trunk
* is sufficient. (corner cases, e.g. wildcarded DLCX) */
bool require_endp;
/* a human readable name that describes the request */
char *debug_name;
};
@@ -112,32 +110,34 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *data);
static struct msgb *handle_rsip(struct mgcp_request_data *data);
static struct msgb *handle_noti_req(struct mgcp_request_data *data);
static const struct mgcp_request mgcp_requests[] = {
{ .name = "AUEP",
.handle_request = handle_audit_endpoint,
.debug_name = "AuditEndpoint",
.require_endp = true },
{ .name = "CRCX",
.handle_request = handle_create_con,
.debug_name = "CreateConnection",
.require_endp = true },
{ .name = "DLCX",
.handle_request = handle_delete_con,
.debug_name = "DeleteConnection",
.require_endp = false },
{ .name = "MDCX",
.handle_request = handle_modify_con,
.debug_name = "ModifiyConnection",
.require_endp = true },
{ .name = "RQNT",
.handle_request = handle_noti_req,
.debug_name = "NotificationRequest",
.require_endp = true },
{ .name = "AUEP", .handle_request = handle_audit_endpoint, .debug_name = "AuditEndpoint" },
{
.name = "CRCX",
.handle_request = handle_create_con,
.debug_name = "CreateConnection",
},
{
.name = "DLCX",
.handle_request = handle_delete_con,
.debug_name = "DeleteConnection",
},
{
.name = "MDCX",
.handle_request = handle_modify_con,
.debug_name = "ModifiyConnection",
},
{
.name = "RQNT",
.handle_request = handle_noti_req,
.debug_name = "NotificationRequest",
},
/* SPEC extension */
{ .name = "RSIP",
.handle_request = handle_rsip,
.debug_name = "ReSetInProgress",
.require_endp = true },
{
.name = "RSIP",
.handle_request = handle_rsip,
.debug_name = "ReSetInProgress",
},
};
/* Initalize transcoder */
@@ -149,7 +149,12 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn_dst = conn;
struct mgcp_conn *_conn;
if (conn->type != MGCP_RTP_DEFAULT && !mgcp_conn_rtp_is_osmux(conn)) {
switch (conn->type) {
case MGCP_RTP_DEFAULT:
case MGCP_RTP_OSMUX:
case MGCP_RTP_IUUP:
break;
default:
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
"RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
return 0;
@@ -173,12 +178,15 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
}
/* Helper function to allocate some memory for responses and retransmissions */
static struct msgb *mgcp_msgb_alloc(void)
static struct msgb *mgcp_msgb_alloc(void *ctx)
{
struct msgb *msg;
msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
if (!msg)
msg = msgb_alloc_headroom_c(ctx, 4096, 128, "MGCP msg");
if (!msg) {
LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
return NULL;
}
return msg;
}
@@ -186,7 +194,7 @@ static struct msgb *mgcp_msgb_alloc(void)
/* Helper function for do_retransmission() and create_resp() */
static struct msgb *create_retransmission_response(const struct mgcp_endpoint *endp)
{
struct msgb *msg = mgcp_msgb_alloc();
struct msgb *msg = mgcp_msgb_alloc(endp->trunk);
if (!msg)
return NULL;
@@ -196,15 +204,14 @@ static struct msgb *create_retransmission_response(const struct mgcp_endpoint *e
return msg;
}
static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
const char *txt, const char *msg,
const char *trans, const char *param,
const char *sdp)
static struct msgb *create_resp(void *msgctx, struct mgcp_endpoint *endp, int code, const char *txt, const char *msg,
const char *trans, const char *param, const char *sdp)
{
int len;
struct msgb *res;
res = mgcp_msgb_alloc();
OSMO_ASSERT(msgctx != 0);
res = mgcp_msgb_alloc(msgctx);
if (!res)
return NULL;
@@ -236,26 +243,22 @@ static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
return res;
}
static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp,
int code, const char *msg,
const char *trans,
const char *param)
static struct msgb *create_ok_resp_with_param(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
const char *trans, const char *param)
{
return create_resp(endp, code, " OK", msg, trans, param, NULL);
return create_resp(msgctx, endp, code, " OK", msg, trans, param, NULL);
}
static struct msgb *create_ok_response(struct mgcp_endpoint *endp,
int code, const char *msg,
static struct msgb *create_ok_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
const char *trans)
{
return create_ok_resp_with_param(endp, code, msg, trans, NULL);
return create_ok_resp_with_param(msgctx, endp, code, msg, trans, NULL);
}
static struct msgb *create_err_response(struct mgcp_endpoint *endp,
int code, const char *msg,
static struct msgb *create_err_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
const char *trans)
{
return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
return create_resp(msgctx, endp, code, " FAIL", msg, trans, NULL, NULL);
}
/* Format MGCP response string (with SDP attached) */
@@ -278,7 +281,7 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
int rc;
struct msgb *result;
sdp = msgb_alloc_headroom(4096, 128, "sdp record");
sdp = msgb_alloc_headroom_c(endp->trunk, 4096, 128, "sdp record");
if (!sdp)
return NULL;
@@ -298,7 +301,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;
}
@@ -309,7 +312,7 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
if (rc < 0)
goto error;
result = create_resp(endp, 200, " OK", msg, trans_id, NULL, (char*) sdp->data);
result = create_resp(endp->trunk, endp, 200, " OK", msg, trans_id, NULL, (char *)sdp->data);
msgb_free(sdp);
return result;
error:
@@ -321,8 +324,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);
}
@@ -376,7 +384,7 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
if (rc < 0) {
LOGP(DLMGCP, LOGL_ERROR, "%s: failed to parse MCGP message\n", rq.name);
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
return create_err_response(NULL, -rc, rq.name, "000000");
return create_err_response(cfg, NULL, -rc, rq.name, "000000");
}
/* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */
@@ -397,7 +405,7 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
if (!rq.trunk) {
LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n",
rq.name, pdata.epname);
return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
}
} else {
/* If the endpoint name suggests that the request refers to a specific endpoint, then the
@@ -405,7 +413,7 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
LOGP(DLMGCP, LOGL_NOTICE,
"%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
pdata.epname, -rq.mgcp_cause);
return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
}
} else {
osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name));
@@ -422,15 +430,6 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
/* Find an appropriate handler for the current request and execute it */
for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) {
if (strcmp(mgcp_requests[i].name, rq.name) == 0) {
/* Check if the request requires and endpoint, if yes, check if we have it, otherwise don't
* execute the request handler. */
if (mgcp_requests[i].require_endp && !rq.endp) {
LOGP(DLMGCP, LOGL_ERROR,
"%s: the request handler \"%s\" requires an endpoint resource for \"%s\", which is not available -- abort\n",
rq.name, mgcp_requests[i].debug_name, pdata.epname);
return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
}
/* Execute request handler */
if (rq.endp)
LOGP(DLMGCP, LOGL_INFO,
@@ -461,7 +460,12 @@ 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");
return create_ok_response(rq->endp, 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);
}
return create_ok_response(rq->trunk, rq->endp, 200, "AUEP", rq->pdata->trans);
}
/* Try to find a free port by attempting to bind on it. Also handle the
@@ -710,7 +714,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" : "");
}
@@ -741,12 +745,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);
@@ -847,7 +851,7 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
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];
@@ -855,11 +859,18 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
/* we must have a free ep */
if (!endp) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "CRCX: no free endpoints available!\n");
return create_err_response(rq->trunk, NULL, 403, "CRCX", pdata->trans);
}
if (!mgcp_endp_avail(endp)) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"CRCX: selected endpoint not available!\n");
return create_err_response(NULL, 501, "CRCX", pdata->trans);
return create_err_response(rq->trunk, NULL, 501, "CRCX", pdata->trans);
}
/* parse CallID C: and LocalParameters L: */
@@ -879,7 +890,7 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
* together with a CRCX, the MGW will assign the
* connection identifier by itself on CRCX */
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION));
return create_err_response(NULL, 523, "CRCX", pdata->trans);
return create_err_response(rq->trunk, NULL, 523, "CRCX", pdata->trans);
break;
case 'M':
mode = (const char *)line + 3;
@@ -887,9 +898,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;
}
@@ -905,7 +916,7 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
"CRCX: unhandled option: '%c'/%d\n", *line, *line);
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM));
return create_err_response(NULL, 539, "CRCX", pdata->trans);
return create_err_response(rq->trunk, NULL, 539, "CRCX", pdata->trans);
break;
}
}
@@ -916,14 +927,14 @@ mgcp_header_done:
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"CRCX: insufficient parameters, missing callid\n");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID));
return create_err_response(endp, 516, "CRCX", pdata->trans);
return create_err_response(endp, endp, 516, "CRCX", pdata->trans);
}
if (!mode) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"CRCX: insufficient parameters, missing mode\n");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
return create_err_response(endp, 517, "CRCX", pdata->trans);
return create_err_response(endp, endp, 517, "CRCX", pdata->trans);
}
/* Check if we are able to accept the creation of another connection */
@@ -940,7 +951,7 @@ mgcp_header_done:
/* There is no more room for a connection, leave
* everything as it is and return with an error */
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
return create_err_response(endp, 540, "CRCX", pdata->trans);
return create_err_response(endp, endp, 540, "CRCX", pdata->trans);
}
}
@@ -958,7 +969,7 @@ mgcp_header_done:
/* This is not our call, leave everything as it is and
* return with an error. */
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID));
return create_err_response(endp, 400, "CRCX", pdata->trans);
return create_err_response(endp, endp, 400, "CRCX", pdata->trans);
}
}
@@ -969,7 +980,7 @@ mgcp_header_done:
rc = mgcp_endp_claim(endp, callid);
if (rc != 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM));
return create_err_response(endp, 502, "CRCX", pdata->trans);
return create_err_response(endp, endp, 502, "CRCX", pdata->trans);
}
}
@@ -992,16 +1003,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));
@@ -1029,6 +1041,11 @@ mgcp_header_done:
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
goto error2;
}
/* 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);
}
conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
trunk->audio_fmtp_extra);
@@ -1043,7 +1060,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;
@@ -1053,7 +1070,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;
@@ -1066,13 +1086,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);
@@ -1088,7 +1115,7 @@ error2:
mgcp_endp_release(endp);
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
"CRCX: unable to create connection\n");
return create_err_response(endp, error_code, "CRCX", pdata->trans);
return create_err_response(endp, endp, error_code, "CRCX", pdata->trans);
}
/* MDCX command handler, processes the received command */
@@ -1107,31 +1134,29 @@ 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 (!mgcp_endp_avail(endp)) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"MDCX: selected endpoint not available!\n");
return create_err_response(NULL, 501, "MDCX", pdata->trans);
}
/* Prohibit wildcarded requests */
if (rq->wildcarded) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"MDCX: wildcarded endpoint names not supported.\n");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));
return create_err_response(endp, 507, "MDCX", pdata->trans);
return create_err_response(rq->trunk, endp, 507, "MDCX", pdata->trans);
}
if (!endp || !mgcp_endp_avail(endp)) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "MDCX: selected endpoint not available!\n");
return create_err_response(rq->trunk, NULL, 501, "MDCX", pdata->trans);
}
if (llist_count(&endp->conns) <= 0) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"MDCX: endpoint is not holding a connection.\n");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
return create_err_response(endp, 400, "MDCX", pdata->trans);
return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
}
for_each_line(line, pdata->save) {
@@ -1165,9 +1190,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 */
@@ -1181,7 +1206,7 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
"MDCX: Unhandled MGCP option: '%c'/%d\n",
line[0], line[0]);
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));
return create_err_response(NULL, 539, "MDCX", pdata->trans);
return create_err_response(rq->trunk, NULL, 539, "MDCX", pdata->trans);
break;
}
}
@@ -1191,13 +1216,13 @@ mgcp_header_done:
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));
return create_err_response(endp, 515, "MDCX", pdata->trans);
return create_err_response(endp, endp, 515, "MDCX", pdata->trans);
}
conn = mgcp_conn_get_rtp(endp, conn_id);
if (!conn) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
return create_err_response(endp, 400, "MDCX", pdata->trans);
return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
}
mgcp_conn_watchdog_kick(conn->conn);
@@ -1244,30 +1269,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);
@@ -1291,7 +1325,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);
@@ -1304,7 +1337,7 @@ mgcp_header_done:
mgcp_endp_update(endp);
return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
error3:
return create_err_response(endp, error_code, "MDCX", pdata->trans);
return create_err_response(endp, endp, error_code, "MDCX", pdata->trans);
out_silent:
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
@@ -1335,14 +1368,27 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"DLCX: selected endpoint not available!\n");
return create_err_response(NULL, 501, "DLCX", pdata->trans);
return create_err_response(rq->trunk, NULL, 501, "DLCX", pdata->trans);
}
if (endp && !rq->wildcarded && llist_empty(&endp->conns)) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"DLCX: endpoint is not holding a connection.\n");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
return create_err_response(endp, 515, "DLCX", pdata->trans);
return create_err_response(endp, endp, 515, "DLCX", pdata->trans);
}
/* Handle wildcarded DLCX that refers to the whole trunk. This means
* that we walk over all endpoints on the trunk in order to drop all
* connections on the trunk. (see also RFC3435 Annex F.7) */
if (rq->wildcarded) {
int num_conns = 0;
for (i = 0; i < trunk->number_endpoints; i++) {
num_conns += llist_count(&trunk->endpoints[i]->conns);
mgcp_endp_release(trunk->endpoints[i]);
}
rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
return create_ok_response(trunk, NULL, 200, "DLCX", pdata->trans);
}
for_each_line(line, pdata->save) {
@@ -1357,7 +1403,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
"cannot handle requests with call-id (C) without endpoint -- abort!");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
return create_err_response(NULL, 539, "DLCX", pdata->trans);
return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
}
if (mgcp_verify_call_id(endp, line + 3) != 0) {
@@ -1373,7 +1419,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
"cannot handle requests with conn-id (I) without endpoint -- abort!");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
return create_err_response(NULL, 539, "DLCX", pdata->trans);
return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
}
conn_id = (const char *)line + 3;
@@ -1389,24 +1435,11 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n",
line[0], line[0]);
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
return create_err_response(NULL, 539, "DLCX", pdata->trans);
return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
break;
}
}
/* Handle wildcarded DLCX that refers to the whole trunk. This means
* that we walk over all endpoints on the trunk in order to drop all
* connections on the trunk. (see also RFC3435 Annex F.7) */
if (rq->wildcarded) {
int num_conns = 0;
for (i = 0; i < trunk->number_endpoints; i++) {
num_conns += llist_count(&trunk->endpoints[i]->conns);
mgcp_endp_release(trunk->endpoints[i]);
}
rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
return create_ok_response(NULL, 200, "DLCX", pdata->trans);
}
/* The logic does not permit to go past this point without having the
* the endp pointer populated. */
OSMO_ASSERT(endp);
@@ -1429,7 +1462,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
/* Note: In this case we do not return any statistics,
* as we assume that the client is not interested in
* this case. */
return create_ok_response(endp, 200, "DLCX", pdata->trans);
return create_ok_response(endp, endp, 200, "DLCX", pdata->trans);
}
/* Find the connection */
@@ -1458,10 +1491,10 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
if (silent)
goto out_silent;
return create_ok_resp_with_param(endp, 250, "DLCX", pdata->trans, stats);
return create_ok_resp_with_param(endp, endp, 250, "DLCX", pdata->trans, stats);
error3:
return create_err_response(endp, error_code, "DLCX", pdata->trans);
return create_err_response(endp, endp, error_code, "DLCX", pdata->trans);
out_silent:
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
@@ -1516,14 +1549,13 @@ static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
/* we didn't see a signal request with a tone */
if (tone == CHAR_MAX)
return create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans);
return create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans);
if (rq->pdata->cfg->rqnt_cb)
res = rq->pdata->cfg->rqnt_cb(rq->endp, tone);
return res == 0 ?
create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans) :
create_err_response(rq->endp, res, "RQNT", rq->pdata->trans);
return res == 0 ? create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans) :
create_err_response(rq->endp, rq->endp, res, "RQNT", rq->pdata->trans);
}
/* Connection keepalive timer, will take care that dummy packets are send
@@ -1561,8 +1593,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);
}
}
@@ -1616,7 +1647,6 @@ 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;

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

@@ -98,7 +98,7 @@ static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
/* Helper function to update codec map information with additional data from
* SDP, called from: mgcp_parse_sdp_data() */
static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
int payload, const char *audio_name)
int payload_type, const char *audio_name)
{
int i;
@@ -110,7 +110,7 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
/* Note: We can only update payload codecs that already exist
* in our codec list. If we get an unexpected payload type,
* we just drop it */
if (codecs[i].payload_type != payload)
if (codecs[i].payload_type != payload_type)
continue;
if (sscanf(audio_name, "%63[^/]/%d/%d",
@@ -127,7 +127,7 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
return;
}
LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload,
LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload_type,
audio_name);
}
@@ -334,7 +334,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
void *tmp_ctx = talloc_new(NULL);
struct mgcp_rtp_end *rtp;
int payload;
int payload_type;
int ptime, ptime2 = 0;
char audio_name[64];
int port, rc;
@@ -355,8 +355,8 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
/* skip these SDP attributes */
break;
case 'a':
if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) {
codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
if (sscanf(line, "a=rtpmap:%d %63s", &payload_type, audio_name) == 2) {
codecs_update(tmp_ctx, codecs, codecs_used, payload_type, audio_name);
break;
}
@@ -384,7 +384,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 +432,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");
@@ -601,7 +601,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);

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

@@ -0,0 +1,207 @@
/*
* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <inttypes.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/eventfd.h>
#include <sys/types.h>
#include <unistd.h>
#include <talloc.h>
#include <osmocom/mgcp/mgcp_threads_queue.h>
/*
classic lamport circular lockfree spsc queue:
every "side" only writes its own ptr, but may read the other sides ptr
notify reader using eventfd as soon as element is added, reader then reads until
read fails
-> reader pops in a loop until FALSE and might get spurious events because it
read before it was notified, which is fine
-> writing pushes *the same data* in a loop until TRUE, blocks
shutting this down requires
1) to stop reading and pushing
2) ONE side to take care of the eventfds
*/
static struct spsc *spsc_init(void *talloc_ctx, unsigned int count, unsigned int size_per_buf, bool blockr, bool blockw)
{
struct spsc *q = talloc_zero_size(talloc_ctx, sizeof(struct spsc) + sizeof(uintptr_t) * count);
atomic_init(&q->readptr, 0);
atomic_init(&q->writeptr, 0);
q->efd_r = eventfd(0, blockr ? 0 : EFD_NONBLOCK);
q->efd_w = eventfd(1, blockw ? 0 : EFD_NONBLOCK);
q->count = count;
q->size_per_buf = size_per_buf;
q->buf = talloc_zero_size(q, size_per_buf * count);
for (int i = 0; i < count; i++)
q->data[i] = (uintptr_t)q->buf + i * size_per_buf;
return q;
}
static void spsc_deinit(struct spsc *q)
{
talloc_free(q->buf);
close(q->efd_r);
close(q->efd_w);
talloc_free(q);
}
static ssize_t spsc_check_r(struct spsc *q)
{
uint64_t efdr;
return read(q->efd_r, &efdr, sizeof(uint64_t));
}
static ssize_t spsc_check_w(struct spsc *q)
{
uint64_t efdr;
return read(q->efd_w, &efdr, sizeof(uint64_t));
}
static void spsc_notify_r(struct spsc *q)
{
uint64_t efdu = 1;
write(q->efd_r, &efdu, sizeof(uint64_t));
}
static void spsc_notify_w(struct spsc *q)
{
uint64_t efdu = 1;
write(q->efd_w, &efdu, sizeof(uint64_t));
}
/*! Adds element to the queue by copying the data.
* \param[in] q queue.
* \param[in] elem input buffer, must match the originally configured queue buffer size!.
* \returns true if queue was not full and element was successfully pushed */
bool spsc_push(struct spsc *q, void *elem)
{
size_t cur_wp, cur_rp;
cur_wp = atomic_load_explicit(&q->writeptr, memory_order_relaxed);
cur_rp = atomic_load_explicit(&q->readptr, memory_order_acquire);
if ((cur_wp + 1) % q->count == cur_rp) {
spsc_check_w(q); /* blocks, ensures next (!) call succeeds */
return false;
}
memcpy((void *)q->data[cur_wp], elem, q->size_per_buf);
atomic_store_explicit(&q->writeptr, (cur_wp + 1) % q->count, memory_order_release);
spsc_notify_r(q); /* fine after release */
return true;
}
/*! Reads the read-fd of the queue, which, depending on settings passed on queue creation, blocks.
* This function can be used to deliberately wait for a non-empty queue on the read side.
* \param[in] q queue.
* \returns result of reading the fd. */
ssize_t spsc_prep_pop(struct spsc *q)
{
return spsc_check_r(q);
}
/*! Removes element from the queue by copying the data.
* \param[in] q queue.
* \param[in] elem output buffer, must match the originally configured queue buffer size!.
* \returns true if queue was not empty and element was successfully removed */
bool spsc_pop(struct spsc *q, void *elem)
{
size_t cur_wp, cur_rp;
cur_wp = atomic_load_explicit(&q->writeptr, memory_order_acquire);
cur_rp = atomic_load_explicit(&q->readptr, memory_order_relaxed);
if (cur_wp == cur_rp) /* blocks via prep_pop */
return false;
memcpy(elem, (void *)q->data[cur_rp], q->size_per_buf);
atomic_store_explicit(&q->readptr, (cur_rp + 1) % q->count, memory_order_release);
spsc_notify_w(q);
return true;
}
/*! Creates a bidirectional queue channel that consists of two queues, one in each direction,
* commonly referred to as a and b side.
* \param[in] talloc_ctx allocation context.
* \param[in] count number of buffers per queue.
* \param[in] size_per_buf size of buffers per queue.
* \param[in] blockr_a should reading the a-side read fd block?.
* \param[in] blockw_a should reading the a-side write fd block?.
* \param[in] blockr_b should reading the b-side read fd block?.
* \param[in] blockw_b should reading the b-side write fd block?.
* \returns queue channel */
struct qchan spsc_chan_init_ex(void *talloc_ctx, unsigned int count, unsigned int size_per_buf, bool blockr_a,
bool blockw_a, bool blockr_b, bool blockw_b)
{
struct qchan q;
q.a = spsc_init(talloc_ctx, count, size_per_buf, blockr_a, blockw_a);
q.b = spsc_init(talloc_ctx, count, size_per_buf, blockr_b, blockw_b);
return q;
}
/*! Creates a bidirectional queue channel that consists of two queues, one in each direction,
* commonly referred to as a and b side.
* \param[in] talloc_ctx allocation context.
* \param[in] count number of buffers per queue.
* \param[in] size_per_buf size of buffers per queue.
* \returns queue channel */
struct qchan spsc_chan_init(void *talloc_ctx, unsigned int count, unsigned int size_per_buf)
{
return spsc_chan_init_ex(talloc_ctx, count, size_per_buf, false, true, false, true);
}
/*! Closes a bidirectional queue channel.
* \param[in] q queue */
void spsc_chan_close(struct qchan *q)
{
spsc_deinit(q->a);
spsc_deinit(q->b);
free(q);
}
/*! Gets queue channel read/write fd for a/b side according to function name.
* \param[in] q queue channel.
* \returns fd */
int spsc_get_a_rdfd(struct qchan *q)
{
return q->a->efd_r;
}
/*! Gets queue channel read/write fd for a/b side according to function name.
* \param[in] q queue channel.
* \returns fd */
int spsc_get_b_rdfd(struct qchan *q)
{
return q->b->efd_r;
}
/*! Gets queue channel read/write fd for a/b side according to function name.
* \param[in] q queue channel.
* \returns fd */
int spsc_get_a_wrfd(struct qchan *q)
{
return q->a->efd_w;
}
/*! Gets queue channel read/write fd for a/b side according to function name.
* \param[in] q queue channel.
* \returns fd */
int spsc_get_b_wrfd(struct qchan *q)
{
return q->b->efd_w;
}

View File

@@ -129,7 +129,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 +141,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 +175,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"
@@ -189,7 +195,7 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
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,
@@ -197,6 +203,30 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
VTY_NEWLINE, end->fmtp_extra, 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 +319,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 +380,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);
@@ -1539,12 +1577,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 +1590,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 +1621,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 +1630,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 +1641,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 +1748,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 +1798,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>
@@ -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");
@@ -295,13 +294,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,
},
};
@@ -357,7 +365,7 @@ int main(int argc, char **argv)
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

@@ -24,7 +24,7 @@ EXTRA_DIST = \
mgcp_test.ok \
$(NULL)
noinst_PROGRAMS = \
check_PROGRAMS = \
mgcp_test \
$(NULL)

View File

@@ -868,7 +868,7 @@ static void test_messages(void)
printf("Connection mode not set\n");
OSMO_ASSERT(conn->end.output_enabled
== (conn->conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0));
== !!(conn->conn->mode & MGCP_CONN_SEND_ONLY));
conn->conn->mode |= CONN_UNMODIFIED;
@@ -1071,7 +1071,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;
@@ -1492,7 +1492,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);
@@ -1632,7 +1632,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);
}
@@ -1896,16 +1897,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
.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} },
{ .payload_type_map = {111, 122}, },
{ .end = true },
},
},
@@ -1914,15 +1912,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
.codecs = {
{
{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
},
{
{ 122, "AMR/8000", &amr_param_octet_aligned_unset, },
},
},
.expect = {
{ .payload_type_map = {111, -EINVAL}, },
{ .payload_type_map = {112, 122} },
{ .payload_type_map = {111, 122}, },
{ .end = true },
},
},
@@ -1931,15 +1927,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
.codecs = {
{
{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
},
{
{ 122, "AMR/8000", NULL, },
},
},
.expect = {
{ .payload_type_map = {111, -EINVAL}, },
{ .payload_type_map = {112, 122} },
{ .payload_type_map = {111, 122}, },
{ .end = true },
},
},
@@ -2085,7 +2079,7 @@ static void test_mgcp_codec_pt_translate(void)
OSMO_ASSERT(ok);
}
void test_conn_id_matching()
void test_conn_id_matching(void)
{
struct mgcp_endpoint endp = {};
struct mgcp_conn *conn;
@@ -2125,7 +2119,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;
@@ -2175,7 +2169,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[] =

View File

@@ -1347,32 +1347,24 @@ Testing mgcp_codec_pt_translate()
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
#4: conn1 has no codecs
#4: conn1 has no codecs
- add codecs on conn0:
1: 0 PCMU/8000/1 -> rc=0
2: 111 GSM-HR-08/8000/1 -> rc=0
- add codecs on conn1:
(none)
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
0: 112 AMR/8000/1 octet-aligned=1 -> rc=0
1: 0 PCMU/8000/1 -> rc=0
2: 111 GSM-HR-08/8000/1 -> rc=0
- add codecs on conn1:
(none)
- add codecs on conn0:
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
- add codecs on conn1:
0: 122 AMR/8000 octet-aligned=0 -> rc=0
1: 121 AMR/8000 octet-aligned=1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
#5: test AMR with differing octet-aligned settings
- add codecs on conn0:
0: 111 AMR/8000 octet-aligned=1 -> rc=0
- add codecs on conn1:
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
0: 122 AMR/8000 octet-aligned=0 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 122
0: 111 AMR/8000 octet-aligned=1 -> rc=0
1: 112 AMR/8000 octet-aligned=0 -> rc=0
- add codecs on conn1:
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 111
#6: test AMR with missing octet-aligned settings (defaults to 0)
- add codecs on conn0:
0: 111 AMR/8000 octet-aligned=1 -> rc=0
- add codecs on conn1:

View File

@@ -22,7 +22,7 @@ EXTRA_DIST = \
mgcp_client_test.err \
$(NULL)
noinst_PROGRAMS = \
check_PROGRAMS = \
mgcp_client_test \
$(NULL)

View File

@@ -315,7 +315,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;
@@ -487,7 +487,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;