Compare commits

...

74 Commits

Author SHA1 Message Date
Philipp Maier
5a71251229 mgcp_trunk: drop "trunk 0" limitation
Due to the internal handling of the trunks it was not possible to allow
an E1 trunk that has the ID 0. However this limitation is no longer
present, so we now can allow an E1 trunk with ID 0.

Change-Id: I302c2007628f607033686e277c407232351e66ad
Related: OS#2659
2020-08-10 23:36:11 +02:00
Philipp Maier
113141d4f9 mgcp_ratectr: fix comments in header file
Change-Id: Idd9d7b108e81b44501b78264284dfa46e679d994
2020-08-10 22:56:59 +02:00
Philipp Maier
993ea6be7a get rid of mgcp_internal.h
The file mgcp_internal.h still contains mostly definitions and types
that are relevant for mgcp_network.c and mgcp_protocol.c. Lets give
the network and protocol module its own header files, also move stuff
that does not relate to protocol and network to the appropiate places.

Change-Id: I837eaad771ed7252304db4a81c37953b70766fff
2020-08-10 22:56:59 +02:00
Philipp Maier
889fe7f203 mgcp_e1: finish E1 support, add E1 support from libosmoabis
Currently only the endpoint handling for E1 exists, but there is no
actual code behind it that handles the E1 traffic.

Change-Id: I6b93809b5ac7d01af55888347dd787b0bc997ae1
Related: OS#2659
2020-08-10 22:56:59 +02:00
Philipp Maier
efd09d0b77 mgcp_protocol: remove unused variable
The function allocate_port() has pointer a variable end, it even does an
OSMO_ASSERT on it, but it never uses it. Lets remove it.

Change-Id: I369361389c6276e5511c683ebd630093713bdd37
2020-08-02 19:30:48 +02:00
Vadim Yanitskiy
ffbc618a13 libosmo-mgcp-client: mgcp_client_tx(): return rc on error
Change-Id: Ic2080241ceb9e00109a5222b78c35b7971320c21
2020-07-31 10:21:49 +00:00
Harald Welte
563ffc51b5 libosmo-mgcp-client: fix memleak in case if no response is received
This problem was noticed while running several LCLS test cases from
ttcn3-bsc-test.  Every test case makes osmo-bsc leak at least two
chunks named 'struct mgcp_response_pending'.

Here is the related osmo-bsc output with additional debug messages:

  DRLL ERROR mgcp_client_fsm.c:525 MGCP_CONN(to-MSC)[0x612000016120]{ST_READY}:
             MGW/DLCX: abrupt FSM termination with connections still present,
             sending unconditional DLCX...
  DLMGCP DEBUG mgcp_client.c:1010 mgcp_client_next_trans_id(id=35): new trans ID
  DLMGCP DEBUG mgcp_client.c:918 mgcp_client_pending_add(id=35): allocated and queued
  DLMGCP DEBUG mgcp_client.c:962 Queued 53 bytes for MGCP GW
  DLMGCP DEBUG mgcp_client.c:725 Tx MGCP: r=127.0.0.1:2427<->l=127.0.0.1:2727:
               len=53 'DLCX 35 rtpbridge/1@mgw MGCP 1.0\r\nC: 5\r\nI:'...
  DLMGCP ERROR mgcp_client.c:704 Failed to read: r=127.0.0.1:2427<->l=127.0.0.1:2727:
               111='Connection refused'

The MGCP client FSM enqueues a DLCX from its fsm_cleanup_cb(), and
terminates.  Thus if the remote MGCP peer becomes unavailable (e.g.
due to a network failure), we would never get a response, and since
the FSM is already terminated, nobody would pop and free() the
response handler from the queue (mgcp->responses_pending).

As a simple workaround, let's avoid allocating dummy entries of
'struct mgcp_response_pending' without a response handler.  The
only case where an MGCP message is sent without a handler is
exactly during the FSM termination.

Change-Id: I83938ff47fa8570b8d9dc810a184864a0c0b58aa
Related: OS#4619
2020-07-31 10:21:49 +00:00
Neels Hofmeyr
51b42ff2e1 refactor: use msgb to receive, pass and send RTP packets
Instead of numerous arguments (buf, len and context data), use a msgb, like
most other osmo programs do, with a msb->cb pointing at a context data struct.

This opens the future for adding/stripping IuUP header data from the msgb
easily.

(Checked to pass current ttcn3-mgw-tests.)

Change-Id: I3af40b63bc49f8636d4e7ea2f8f83bb67f6619ee
2020-07-21 16:13:23 +00:00
Philipp Maier
7a755be4c2 mgcp_trunk: use talloc_zero_array instead of _talloc_zero_array
_talloc_zero_array is not supposed to be called by the API user. Lets
use talloc_zero_array instead.

Related: OS#2659
Change-Id: I27549585016a7998e9233c52f6d86429fc75f509
2020-07-16 11:57:45 +00:00
Philipp Maier
06ea49159e mgcp_test: remove trunk2 from unit-test
Some of the unit-tests initalize a second trunk (trunk2) but the test
never do anything with this trunk. Lets remove it.

Change-Id: I228aa45160152091baac9d9c2e6486b774278b6a
Related: OS#2659
2020-07-16 11:57:45 +00:00
Philipp Maier
869b21c869 mgcp_vty: fix endpoint number configuration
At the moment the number of possible E1 endpoints (depends on the number
of E1 timeslots that should be used) is hardcoded and the configuration
of the number of virtual endpoints has an off-by-one problem.

For the E1 timeslots one might choose not to occupy all E1 timeslots of
once. A one TRX E1 BTS usually requires 3 E1 timeslots. One as D-Channel
timeslot and two to cover the voice channels. The voice channels
timeslots need to be set up in osmo-mgw, while the D-Channel timeslot
must not be touched. The VTY config needs to be able to reflect that.

Change-Id: I73b31e3c236a61ea0a6f76ef5ff98ce589f52c77
Related: OS#2547
2020-07-16 11:57:45 +00:00
Philipp Maier
37a808c5a5 mgcp_test: do not access endpoint array elements directly
The test assumes that the endpoint "rtpbridge/X@mgw" is at array
position X in many places. This does not necessarly have to match.
Accessing the array elements directly was the prefered way when the MGW
did use integer numbers and not strings to identify endpoints. Since the
endpoint name strings are used to access the endpoints the unit-test
should also reflect this.

Lets replace the integer variable last_endpoint with a string variable
and do related verifications based on strings.

Change-Id: Ic950c427f23be4a792af94972554637c2b0fbdf2
Related: OS#2659
2020-07-16 11:57:45 +00:00
Philipp Maier
24b8c0ce92 mgcp_trunk: remove double check
At the moment, the trunk prefix is checked twice. Lets re-arange the
code a bit so that the check only happens once.

Change-Id: I91fb8cf6e3b077ba8f18fdbcd071275c6fd7cacd
Related: OS#2547
2020-07-16 11:57:45 +00:00
Philipp Maier
fe67e094ad mgcp_endp.c: cosmetic: fix sourcecode formatting
Change-Id: Ib83442d9a033b96c304bfd5e81206405d98d2ed5
2020-07-16 11:56:39 +00:00
Philipp Maier
fbcf39976e mgcp_endp: use define constant to define max number of E1 subslots
There are 15 possible subslots (not all at the same time) in one E1
timeslot. Lets use a define constant for that.

Change-Id: If7cb74e486946aff09e22abf8a8885bf0693f34e
Related: OS#2547
2020-07-16 11:56:39 +00:00
Philipp Maier
4863591c23 mgcp_client: add function to generate e1-endpoint names
mgcp_client.h offers functions to generate endpoint names for wildcarded
request. This is used in osmo-bsc, lets now also add a function that can
generate e1-endpoint names.

Change-Id: Iec35b5bae8a7b07ddb3559f7114a24dcd10e8f14
Related: OS#2547
2020-07-16 11:56:39 +00:00
Harald Welte
827da2c867 mgcp_client_pending_add(): Consider "talloc returns NULL" case
Change-Id: I43a4715fa41fba5a5506fc08e3c21ef6f9ca2521
2020-07-15 10:57:08 +02:00
Harald Welte
efe1571b7b mgcp_client_init(): consider "talloc returns NULL" case
Change-Id: I131019b3c04a7860a118991c8f3bb31185fb11e4
2020-07-15 10:56:45 +02:00
Neels Hofmeyr
6521fc0695 manuals: generate vty reference xml at build time
Move 'doc' subdir further down to "make sure" the osmo-mgw binary is built
before the docs.

Remove mgw_vty_reference.xml from the source tree.

In manuals/Makefile.am use the new BUILT_REFERENCE_XML feature recently added
to osmo-gsm-manuals, and add a build target to generate the XML using the new
osmo-mgw --vty-ref-xml cmdline switch.

Depends: I613d692328050a036d05b49a436ab495fc2087ba (osmo-gsm-manuals)
Change-Id: I526af21134087e2b43b9ada59c93f636ae242e24
2020-07-11 02:02:50 +00:00
Neels Hofmeyr
f5531845bc add osmo-mgw --vty-ref-xml: dump VTY ref XML to stdout
Add only a long option to not clutter the cmdline namespace.

To add a long option without a short letter is slightly complex: use the 'flag'
and 'val' mechanism as in 'man 3 getopt' to write an option index to
long_option.

Depends: Ic74bbdb6dc5ea05f03c791cc70184861e39cd492 (libosmocore)
Change-Id: Ia988ea1c3f5169bdb4d21f2f05933665711cfcbf
2020-07-11 01:53:32 +00:00
Vadim Yanitskiy
13fae78b32 libosmo-mgcp: always check result of msgb_printf() in add_fmtp()
Change-Id: I218ac21612116366eabd0c75ce3648bad4e27abf
2020-07-08 07:54:16 +00:00
Vadim Yanitskiy
b7d395d83a libosmo-mgcp: fix unused extra argument to printf() in add_fmtp()
Change-Id: Ie48da20aea7bc1eedc3f8b5b4a708458f0860a25
Closes: CID#208171
2020-07-08 07:54:16 +00:00
Philipp Maier
6fbbeec064 mgcp_trunk: pick trunk by number and type
The function mgcp_trunk_by_num() is used to directly pick a specific
trunk that is known by its id number (sometimes called "index").
Traditionally the virtual trunk will reside under id number 0 and all
consecutively created E1 trunks will be created under number 1 to 64.
This works fine, but puts a limitation on us should we ever introduce an
aditional trunk type (e.g. T1). Since the numbers must be unique
regardless of the trunk type one could not have an E1 trunk number 1 and
e.g. a T1 trunk number 1 at the same time. So we should pick the trunk
not only by its number, but also by its type to allow different trunk
types to carry the same number. The trunks will still be distinguishable
by its type along with the respective endpoint prefix.

Change-Id: I7af1e9ce601babd4a51e88201a98319e03945f83
Related: OS#2659
2020-07-07 12:45:14 +02:00
Philipp Maier
0ffa3bdc45 endp: require domain name also for E1 endpoints
RFC3435 requires an MGW domain name appeneded to every endpoints. When
defining endpoint names in Appendix E, the domain name is is not
mentioned for digital trunks, however, this does not mean that digital
trunks do not have a domain appended. Osmo-mgw currently violates the
spec because it explicitly checks if the domain name is _NOT_ present
for E1 endpoints.

Change-Id: Ibb800b689e090b97b58d0206959b660890acd967
Related: OS#2547
2020-07-07 10:25:35 +00:00
Vadim Yanitskiy
ca8639dc05 libosmo-mgcp: fix unsigned compared against 0 in mgcp_trunk_by_name()
e1_trunk_nr_from_epname() returns a signed integer:

  int e1_trunk_nr_from_epname(const char *epname);

mgcp_trunk_by_num() accepts a signed integer:

  struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, int index);

Change-Id: Id333a6ddcefd37d82d19f9378ab87d1c02ffd7e3
Closes: CID#211333
2020-07-07 14:03:15 +07:00
Philipp Maier
8d6a193c1a endp: add E1 endpoint interlocking
E1 endpoint names also represent different rates, this may mean that
some rate / subslot combinations are not possible because they overlap
within nthe timeslot. When the equipment (BSC) is properly configured,
this will be no problem, however invalid configuration may cause the
selection of overlapping endpoints and this needs to be prevented, and
logged. Also rate counters need to be in place.

Change-Id: I18e90b10648a7e504371179ad144645fc82e1c27
Related: OS#2547
2020-07-06 19:19:27 +02:00
Philipp Maier
58a1ba85c7 mgcp_internal: remove forward declaration struct mgcp_endpoint_type
In is no longer needed to define struct mgcp_endpoint_type in
mgcp_internal.h

Change-Id: Iecea75e5620e8a2f1fd2066949c116bf72320aca
Related: OS#2659
2020-07-06 19:18:52 +02:00
Philipp Maier
b0c05aa3a9 mgcp_conn: move struct mgcp_conn mgcp_conn.h
The struct mgcp_conn is currently defined in mgcp_internal.h, however it
makes more sense to put the struct in mgcp_conn.h

Change-Id: Ibe9a356300ddb9567432fe48e37c956b7125c79c
Related: OS#2659
2020-07-06 19:17:17 +02:00
Philipp Maier
0996a1e4ae endp: add typeset for e1-endpoints
Add an endpoint typeset for E1 support, also lets add dummy callbacks
for the cleanup and rtp dispatch functionality.

Related: OS#2547
Change-Id: I68b719a906e8f7251f0ca8c74ceec73bc40376f7
2020-07-04 10:02:56 +02:00
Philipp Maier
7e9ddc9904 trunk: parse E1 trunk number
The E1 trunk number is currently not parsed, whenever a trunk prefix is
detected that indicates an E1 trunk, then the entire request is
rejected.

Parse the trunk number and select the trunk accordingly

Related: OS#2547
Change-Id: Ifdaab953544151e73b58cc3e95d21afdb40765f4
2020-07-04 10:02:56 +02:00
Philipp Maier
04bbb9de3e mgcp_trunk: use enum type for trunk type variable
The trunk_type variable in struct mgcp_trunk is specified as an int,
however there is an enum mgcp_trunk_type specified. Lets use the enum as
type for trunk_type instead of int.

Related: OS#2659
Change-Id: I8e8b0cf448cfe67ad3b7caab24f301708d2a515f
2020-07-02 21:25:19 +02:00
Philipp Maier
bea56788cb mgcp_trunk: move enum mgcp_trunk_type to mgcp_trunk.h
The enum mgcp_trunk_type, which is currently located in mgcp_internal.h
makes more sense in mgcp_trunk.h, so lets move it.

Related: OS#2659
Change-Id: I077121503c44fc112a33f1c946f368414e28f841
2020-07-01 23:21:51 +02:00
Philipp Maier
48bcc2ee12 mgcp_osmux: remove unused define constants
The define constants CONN_ID_BTS and CONN_ID_NET were used in mgcp_osmux
long time ago when osmux support was temporarly broken. Now those
defines are no longer used anywhere, so lets remove them.

Change-Id: I3d0b9d482ef0e2187bccace5779a7f8b9507c4e2
2020-07-01 23:13:25 +02:00
Philipp Maier
080935a8c7 mgcp_trunk: fix docstring for mgcp_trunk_alloc()
Change-Id: I845397d829476e15f7e3221c63ea35a00a965647
2020-07-01 23:09:48 +02:00
Philipp Maier
a466f57007 mgcp_client: add docstring for mgcp_client_rtpbridge_wildcard()
Change-Id: I2d811b6ddda5b330054145abff37c996c54c3e3a
2020-06-26 10:50:06 +02:00
Harald Welte
41f77d8018 osmo-mgw.spec.in: Use %config(noreplace) to retain current config file
Change-Id: I37c130d9715a6826cc338f77edcd3cbec762fffd
2020-06-22 14:20:53 +02:00
Vadim Yanitskiy
e674345e29 libosmo-mgcp-client: fix use-after-free in mgcp_msg_gen()
Change-Id: Ib8b6c25489a6a704912aa1763d7430c8055d54e3
2020-06-18 11:40:37 +00:00
Vadim Yanitskiy
3f8139c55f libosmo-mgcp-client: fix use-after-free in mgcp_client_tx()
This function calls mgcp_client_pending_add(), that in its turn
allocates a 'mgcp_response_pending' and appends it to the queue.

In case of an error, it calls mgcp_client_handle_response() that
would free the 'mgcp_response_pending', but this structure would
still remain in the linked list (the queue).

Change-Id: Id94bb93a6b0ea7b7241cf7868112e9bec3e60f0b
2020-06-18 11:40:37 +00:00
Philipp Maier
98c09b3f30 endp: add name generator function for E1 endpoints
Currently the endpoint name that is generated for an E1 endpoint is not
correct. Let's add an endpoint name generator function that derives a
full endpoint name for a given E1 index

Change-Id: I70e0c3f96aa3947165f9926666815ee5614c8f57
Related: OS#2547
2020-06-18 12:31:16 +02:00
Philipp Maier
7462b95829 endp: move endpoint name generation into mgcp_endp.c
When the trunk allocates its endpoints by using mgcp_endp_alloc()
ist passes the name for each endpoint as a parameter. In order to
generate the name endpoint specific knowlege is required.

This process can be simplified, since all what
mgcp_trunk_alloc_endpts() does is calling mgcp_endp_alloc() in a loop in
order to generate a consecuitve series of endpoints. The endpoint names
are generated from the index of the for loop.

When we just pass the index instead of the endpoint name to
mgcp_endp_alloc(), then we can greatly simplify the code since all the
knowledge about the name generation can go into mgcp_endp.c. The
endpoint will name itsself by the trunk properties and the index number
we pass with the allocator function.

Change-Id: I8dee07f1c63037d1f73113f69c612d1f2703cee5
Related: OS#2659
2020-06-18 12:03:19 +02:00
Philipp Maier
7a64182f9a cosmetic: remove excess space
Change-Id: I3620efea2f809fb822c5b8f0a68036066dc6aa95
2020-06-18 12:03:19 +02:00
Philipp Maier
d19de2ee80 trunk: get rid of virt_trunk pointer
The virtual trunk is a pre-configured trunk that always exists. It is
kept separate from the trunk list using a separate pointer. This makes
thinks unecessarly complicated. Lets organize the trunk in the trunk
list like any other trunk, except that we automatically create it on
startup and assign it always the trunk id number 0.

Change-Id: I81934fbd211b225ab7920e78510729c8e22607b3
Related: OS#2659
2020-06-18 12:03:19 +02:00
Philipp Maier
08eb9352ab cosmetic: fix doxygen for mgcp_cleanup_rtp_bridge_cb()
Change-Id: I231b9026402a0f0d3aa23c8de748fc7e88b8bb36
2020-06-18 12:03:19 +02:00
Philipp Maier
0b79d21c7f cosmetic: fix doxygen
Change-Id: I31f7ccf748d09062dbb82f3e921a90e77db02a3d
2020-06-18 12:03:19 +02:00
Philipp Maier
bce5f29265 cosmetic: fix doxygen
Change-Id: Icd757befce68fd12aa0832b7790ca118103a102c
2020-06-18 11:59:26 +02:00
Philipp Maier
3d5a2dd19f ratectr: move rate counter definitions into mgcp_ratectr.h
The rate counter definition (enums) are still in mgcp.h.
Lets move them to mgcp_ratectr.h since it makes more sense
to keep them there.

Change-Id: Id37f66673bc20f9c2cc47a6b44cdfe75f728b936
Related: OS#2659
2020-06-18 11:31:24 +02:00
Philipp Maier
7f90ddb519 mgcp_trunk: remove audio_name and audio_payload
get rid of deprecated trunk parameters which seem to be leftovers
from the old osmo-bsc_mgcp implementation. This is in particular
audio_name and audio_payload in struct mgcp_trunk_config which
allowed the user to "hardcode" an andio name and payload type
via VTY configuration

The removal of the struct members above also require a change to
mgcp_codec.c. The code that is is never actively used and even
causes wrong behavior when activated (set the no-transcoding
flag in VTY). Since the code is removed also the unit tests
also require to be changed to match the new behavior.

Change-Id: Ia050ec3cd34b410dfe089c41b977ae3d5aed7354
Related: OS#2659
2020-06-12 17:08:41 +02:00
Philipp Maier
c66ab2c4c3 osmo-mgw: refactor endpoint and trunk handling
The trunk and endpoint handling in osmo-mgw is still very complex and
implemented in various places (mostly mgcp_protocol.c). Also we use
still integers for endpoint identification, which is not flexible enough
to address timeslots/subslots on an E1 trunk. Some refactoring is needed.

  - get rid of integers as endpoint identifiers, use strings instead and
    find the endpoint based on its string name on the trunk.

  - identify the trunk based on the trunk prefix given in the endpoint
    name.

  - refactor trunk and endpoint allocation. Aggregate functionality in
    in mgcp_endp.c and mgcp_trunk.c. Also remove non-reusable code that
    relates to the still exisiting, but unfinished E1 trunk support.

  - refactor rate counters, put them into a separate module and do no
    longer allocate them per trunk. Allocate them globally instead.

Change-Id: Ia8cf4d6caf05a4e13f1f507dc68cbabb7e6239aa
Related: OS#2659
2020-06-12 17:08:41 +02:00
Philipp Maier
f53796c1fe mgcp_vty: fix indentation in VTY config write
The config under the node mgcp is written with an indentation that has
one space too much.

Change-Id: I2aefeaf3d7ad4a98b7bfcdc7cbc1ce6ebcbe0537
Related: OS#2659
2020-06-03 13:57:38 +02:00
Philipp Maier
14b27a8893 osmo-mgw: rename struct mgcp_trunk_config and symbol tcfg
rename struct mgcp_trunk_config to struct mgcp_trunk and the related
symbol name "tcfg" to "trunk" in order to better match the reality.

Change-Id: I02889dbf8149e139b1bd0326e13ce4c1aec867d1
Related: OS#2659
2020-06-02 20:30:58 +02:00
Philipp Maier
21be42abed mgcp_vty: fix indentation
Some DEFUN macros are not correctly indented

Change-Id: I613f2ebcb06a01744d957e87e8b1215a141b43c4
2020-05-29 21:41:46 +02:00
Philipp Maier
2d681fd84c vty: fix unreachable code (error msg on trunk alloc fail)
When a trunk is selected that does not exist, a new one is created. In
this case the VTY would print an error message but the function exits
early. The code that would print the error is unreachable.

Change-Id: Ie8c3b083174eb8209df2c06f65db6d7bbfaa87f7
fixes: CID#210637
2020-05-29 16:49:06 +02:00
Harald Welte
c39b1bffec mgcp_protocol: Avoid code duplication between virtual + other trunks
There were two code paths that were supposed to do exactly the same,
but then in Change-Id I3994af016fb96427263edbba05f560743f85fdd4 only
one of the two was modified, resulting in OS#4034

Let's
* dynamically allocate the virtual trunk
* rename mgcp_config.trunk to mgcp_config.virt_trunk to clarify
* as a result, abolish copy+pasted code for trunk initialization

Change-Id: I54762af6d417b849a24b6e71b6c5c996a5cb3fa6
Related: OS#4034
2020-05-28 09:23:25 +00:00
Philipp Maier
62612e8575 mgcp: find better locations for LOGPCONN and LOGPENDP
The logging defines LOGPCONN and LOGPENDP are currently located in
mgcp_internal.h. However, there are specific header files for conn
(mgcp_conn.h) and endpoint (mgcp_endp.h) related stuff. Lets put LOGPCON
into mgcp_conn.h and LOGPENDP in mgcp_endp.h

Change-Id: I25ff37ee8108c27d169d294fd16ddcdde9b00195
2020-05-28 09:23:25 +00:00
Harald Welte
af932ce3bc remove accidential TODO-RELEASE entry
I wanted to use gerrit to merge v1 of
 I8d58281e1ff898638293c9e8cb000329462c7a70, but gerrit merged v2
nevertheless :(

Change-Id: I7b16912e66e91f0c30716e4ea1181b39906bacc1
2020-05-28 11:22:29 +02:00
Philipp Maier
265b0a8045 mgcp: remove unused callback pointer
struct mgcp_config contains a function pointer realloc_cb, which is
never popoulated nor used anywhere in the code. Lets remove it

Change-Id: I8d58281e1ff898638293c9e8cb000329462c7a70
2020-05-27 13:45:26 +02:00
Philipp Maier
74390c521e cosmetic: remove excess newlines
Change-Id: Idefe3e86d5b659666bf0356991906c9a2f858aae
2020-05-26 22:26:04 +02:00
Oliver Smith
abfb858f0c Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
Change-Id: I1ab1e30cc0c8a7ece997ae776ab0945a989eb82a
2020-05-22 13:41:39 +02:00
Oliver Smith
6500d72aaf contrib: integrate RPM spec
Remove OpenSUSE bug report link, set version to @VERSION@, make it build
with CentOS 8 etc.

Related: OS#4550
Change-Id: I1d03ac87a7d0c3c600d187f3e485cb2dab8838bb
2020-05-19 15:33:19 +02:00
Oliver Smith
d6877eb8f9 contrib: import RPM spec
Copy the RPM spec file from:
https://build.opensuse.org/project/show/home:mnhauke:osmocom:nightly

Related: OS#4550
Change-Id: I6d6119ca5debf4adfec6c155f81027c8a3583537
2020-05-14 11:47:38 +02:00
Alexander Chemeris
ebb9bf3f12 rtp_bridge: Demote a chatty ERROR log message to DEBUG level.
Not having a second leg of an MGCP endpoint is a normal situtation
and can't be treated as an ERROR message, especially not as an ERROR
message logged on every RTP packet. This happens routinely at
the beginning of call setup and we get tens of ERROR messages in
the logs for every call.

Change-Id: If741a742208772bda4e59236345d7ae650368d5a
2020-05-11 18:14:31 +03:00
Alexander Chemeris
61cf9bb5f1 mgcp_network: Fix a typo in the comment bahviour -> behaviour
Change-Id: I59a06b95e9bbf90c038c5c9234f5c857315d5f07
2020-05-11 18:13:53 +03:00
Pau Espin Pedrol
a7152e055a Use OSMO_FD_* instead of deprecated BSC_FD_*
New define is available since libosmocore 1.1.0, and we already require
1.1.0, so no need to update dependenices.
Let's change it to avoid people re-using old BSC_FD_* symbols when
copy-pasting somewhere else.

Change-Id: I9b6463af713f76c06a144bdbf202c0d91eef4d21
2020-05-09 19:15:50 +02:00
Alexander Chemeris
63866009e2 counters: Implement more useful counters.
Right now a lot of errors with MGCP processing are invisible in rate
counters which makes them difficult to trace or even notice in
a production environment. E.g. reaching a limit of MGCP endpoints
is completely invisible even though it's a critical opertion alarm.

Change-Id: I6db68f044255c927dfd534fed880e405ec3ed4d6
2020-05-05 22:17:41 +03:00
Alexander Chemeris
dab89af070 vty: Prepend VTY output of counters for better visual separation.
Before this patch rate counters started right after trunk information
with no visual separation which was quite confusing. We're adding
a new line and a header to warn a user of the section change.

Change-Id: I3943def03ab821b05ac597f40bdfa4a3a71ddca3
2020-05-05 20:38:16 +03:00
Eric
eebbf2b1fc configure.ac: fix libtool issue with clang and sanitizer
As pointed out at https://github.com/libexpat/libexpat/issues/312
libtool does not play nice with clang sanitizer builds at all.
For those builds LD shoud be set to clang too (and LDFLAGS needs the
sanitizer flags as well), because the clang compiler driver knows how
linking to the sanitizer libs works, but then at a later stage libtool
fails to actually produce the shared libraries and the build fails. This
is fixed by this patch.

Addtionally LD_LIBRARY_PATH has no effect on conftest runs during
configure time, so the rpath needs to be set to the asan library path to
ensure the configure run does not fail due to a missing asan library,
i.e.:

SANS='-fsanitize=memory -fsanitize-recover=all -shared-libsan'
export CC=clang-10
ASANPATH=$(dirname `$CC -print-file-name=libclang_rt.asan-x86_64.so`)
export LDFLAGS="-Wl,-rpath,$ASANPATH $SANS $LDFLAGS"

Change-Id: I2314ef45e6f588e88d5aab8213cc7b5cdef11325
2020-04-11 18:33:04 +00:00
Eric
e885bc5c24 tests: dlopen does not imply availability of dlsym..
Check for both.

Change-Id: I1a1e82882ad28dd53e634f10f9cebb4bc74cac1e
2020-04-11 00:57:13 +02:00
Philipp Maier
173dc129fc doc: do not bind osmo-mgw to random ip-address
The example config bind the MGW to a random ip-address, lets use the
loopback address here, this will suit cases where osmo-bts runs on the
same machine as the MGW (nitb). For all other cases were an external BTS
is used the ip-address still needs to be changed.

Change-Id: Iae52c671c48953ea6b52b18c5d77347343cde0df
2020-03-24 20:37:44 +01:00
Neels Hofmeyr
3abced8d64 allow larger MGCP client wqueue: 10 -> 1024
Enlarge the MGCP client workqueue maximum limit by factor 100.

During Abis load testing, a BSC trying to DLCX 200 conns at the same time hit
the limit of 10 very very quickly, and everything broke down.

Change-Id: I8980cce37bae0757828b28455b25c77bcb6316d0
2020-03-10 03:55:35 +01:00
Harald Welte
a48ff4a738 Update per-trunk global packet/byte counters in real-time
We used to update only the per-connection rx/tx packet/byte counters
on-the-fly, but not the per-trunk global counters.  The latter would
only be updated at the end of a connection.  As MGCP connections
can last quite long (think of a long phone call) this is maybe
not the best of ideas.

Note: The all_rtp:err_tstmp_in and all_rt:err_tstmp_out are still
only updated at the end of a connection.

Change-Id: Ib3866cb8149d3257fcf39733846c97c33881c4ee
Related: OS#4437
2020-03-08 14:50:20 +01:00
Neels Hofmeyr
6c92f9d83e fix vty dump_trunk: start from zero, do not omit first CONN
Change-Id: Ibb97fbf5c0b46ab841c3f6126b3622e4a8054feb
2020-03-08 14:12:54 +01:00
Harald Welte
9852e22101 Add CTRL interface to osmo-mgw
OsmoMGW has a lot of nice built-in statistics (rate_ctr,...) but it
seems the only way to look at them is via the VTY. While libosmocore
contains automatic exposure of all rate counters via CTRL, the CTRL
interface simply is not used by osmo-mgw so far.

Closes: OS#4441
Change-Id: I7ed6bdb9f4749c24ca11a5905a620546cfe42952
2020-03-08 13:23:46 +01:00
Harald Welte
b141cccbfb Fix number of endpoints of default trunk
If a config file doesn't have a 'number endpoints' config line,
we would use -1 as unsigned integer and end up with
 number endpoints 4294967295
if the config file is re-written

Change-Id: I05a3814117b1d6e0cdc30740da31709ce333df4b
Closes: OS#4034
2020-03-08 10:51:41 +01:00
Pau Espin Pedrol
ec45068972 Bump version: 1.6.0.30-832bc-dirty → 1.7.0
libosmocore required version increased due to include used from
libosmo-netif including an include from libosmocore which in previous
versions misses including an include from a symbol used.

Change-Id: I1d5f14b1ad36b2ed94343fca71fdc622424403d3
2020-01-03 13:35:10 +01:00
50 changed files with 3860 additions and 3297 deletions

2
.gitignore vendored
View File

@@ -61,3 +61,5 @@ doc/manuals/generated/
doc/manuals/osmomsc-usermanual.xml
doc/manuals/common
doc/manuals/build
contrib/osmo-mgw.spec

View File

@@ -9,10 +9,10 @@ AM_CPPFLAGS = \
$(NULL)
SUBDIRS = \
doc \
include \
src \
tests \
doc \
contrib \
$(NULL)
@@ -22,7 +22,13 @@ pkgconfig_DATA = \
$(NULL)
BUILT_SOURCES = $(top_srcdir)/.version
EXTRA_DIST = git-version-gen osmoappdesc.py .version
EXTRA_DIST = \
.version \
contrib/osmo-mgw.spec.in \
debian \
git-version-gen \
osmoappdesc.py \
$(NULL)
AM_DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)

View File

@@ -22,6 +22,11 @@ AC_PROG_CC
AC_PROG_INSTALL
LT_INIT
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
AS_CASE(["$LD"],[*clang*],
[AS_CASE(["${host_os}"],
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
@@ -38,11 +43,17 @@ dnl checks for libraries
AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DL)
AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DLSYM)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.1.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.1.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.1.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.1.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.6.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.6.0)
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 0.6.0)
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
@@ -200,4 +211,5 @@ AC_OUTPUT(
doc/manuals/Makefile
contrib/Makefile
contrib/systemd/Makefile
contrib/osmo-mgw.spec
Makefile)

134
contrib/osmo-mgw.spec.in Normal file
View File

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

42
debian/changelog vendored
View File

@@ -1,3 +1,45 @@
osmo-mgw (1.7.0) unstable; urgency=medium
[ Neels Hofmeyr ]
* rename codecs_cmp() to codecs_same()
* mgcp_codec: constify 'param' arg
* fix crashes: don't assert on incoming RTP packet size
* mgcp_send(): stop looping on conversion error
* mgcp_codec: split codec_free() off of codec_init()
* fix memleak: actually free strings in mgcp_codec_reset_all()
* mgcp_test: extend / rewrite test_mgcp_codec_pt_translate()
* test_mgcp_codec_pt_translate(): more tests
* differentiate AMR octet-aligned=0 vs =1
* ptmap: implicitly match '/8000' and '/8000/1'
* mgcp_codec: codec_set(): log about all possible errors
* mgcp_codec_add: fix audio_name size check
* explicitly free codecs in mgcp_rtp_conn_cleanup()
* tweak mgcp_parse_audio_ptime_rtpmap()
* SDP: store all ptmap entries
* mgcp_client_fsm cleanup: Do not assert on DLCX failure
* clear pending requests on MGCP failure
* client: endp fsm: add notify struct, prep for cancel-notify
* client: endp fsm: clear ci[] before dispatching DLCX success
* client: endp fsm: allow cancelling a notify event
* client: endp fsm: add osmo_mgcpc_ep_ci_ep()
* accept MGCP without SDP
* fix use-after-free: require new fsm deferred dealloc, check for term
[ Pau Espin Pedrol ]
* mgcp_test: Correctly release all endpoints allocated
* mgw: Allocate mgcp_conn instance under tcfg->endpoints
[ Harald Welte ]
* manual: Fix copy+paste error
* mgcp_client: Check for osmo_fsm_register() error return value
* Move fsm_mgcp_client regstration to __attribute__((contructor))
* exit(2) on unsupported positional arguments on command line
[ Oliver Smith ]
* osmoappdesc.py: switch to python 3
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 03 Jan 2020 13:35:09 +0100
osmo-mgw (1.6.0) unstable; urgency=medium
[ Oliver Smith ]

View File

@@ -4,7 +4,7 @@
mgcp
bind ip 127.0.0.1
rtp port-range 4002 16000
rtp bind-ip 10.9.1.122
rtp bind-ip 127.0.0.1
rtp ip-probing
rtp ip-tos 184
bind port 2427

View File

@@ -13,6 +13,11 @@ if BUILD_MANUALS
VTY_REFERENCE = osmomgw-vty-reference.xml
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
BUILT_REFERENCE_XML = $(builddir)/vty/mgw_vty_reference.xml
$(builddir)/vty/mgw_vty_reference.xml: $(top_builddir)/src/osmo-mgw/osmo-mgw
mkdir -p $(builddir)/vty
$(top_builddir)/src/osmo-mgw/osmo-mgw --vty-ref-xml > $@
OSMO_REPOSITORY = osmo-mgw
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
endif

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,6 @@ nobase_include_HEADERS = \
osmocom/mgcp_client/mgcp_client_fsm.h \
osmocom/mgcp/mgcp.h \
osmocom/mgcp/mgcp_common.h \
osmocom/mgcp/mgcp_internal.h \
osmocom/mgcp/osmux.h \
$(NULL)

View File

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

View File

@@ -29,6 +29,7 @@
/* Debug Areas of the code */
enum {
DRTP,
DE1,
Debug_LastEntry,
};

View File

@@ -34,6 +34,8 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include "mgcp_ratectr.h"
#define RTP_PORT_DEFAULT_RANGE_START 16002
#define RTP_PORT_DEFAULT_RANGE_END RTP_PORT_DEFAULT_RANGE_START + 64
@@ -42,7 +44,7 @@
*/
struct mgcp_endpoint;
struct mgcp_config;
struct mgcp_trunk_config;
struct mgcp_trunk;
struct mgcp_rtp_end;
#define MGCP_ENDP_CRCX 1
@@ -59,10 +61,9 @@ struct mgcp_rtp_end;
#define MGCP_POLICY_REJECT 5
#define MGCP_POLICY_DEFER 6
typedef int (*mgcp_realloc)(struct mgcp_trunk_config *cfg, int endpoint);
typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state);
typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id);
typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg);
typedef int (*mgcp_change)(struct mgcp_endpoint *endp, int state);
typedef int (*mgcp_policy)(struct mgcp_endpoint *endp, int state, const char *transaction_id);
typedef int (*mgcp_reset)(struct mgcp_trunk *cfg);
typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
/**
@@ -119,104 +120,6 @@ struct mgcp_port_range {
#define MGCP_KEEPALIVE_ONCE (-1)
#define MGCP_KEEPALIVE_NEVER 0
/* Global MCGP CRCX related rate counters */
enum {
MGCP_CRCX_SUCCESS,
MGCP_CRCX_FAIL_BAD_ACTION,
MGCP_CRCX_FAIL_UNHANDLED_PARAM,
MGCP_CRCX_FAIL_MISSING_CALLID,
MGCP_CRCX_FAIL_INVALID_MODE,
MGCP_CRCX_FAIL_LIMIT_EXCEEDED,
MGCP_CRCX_FAIL_UNKNOWN_CALLID,
MGCP_CRCX_FAIL_ALLOC_CONN,
MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC,
MGCP_CRCX_FAIL_START_RTP,
MGCP_CRCX_FAIL_REJECTED_BY_POLICY,
MGCP_CRCX_FAIL_NO_OSMUX,
MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS,
MGCP_CRCX_FAIL_CODEC_NEGOTIATION,
MGCP_CRCX_FAIL_BIND_PORT,
};
/* Global MCGP MDCX related rate counters */
enum {
MGCP_MDCX_SUCCESS,
MGCP_MDCX_FAIL_WILDCARD,
MGCP_MDCX_FAIL_NO_CONN,
MGCP_MDCX_FAIL_INVALID_CALLID,
MGCP_MDCX_FAIL_INVALID_CONNID,
MGCP_MDCX_FAIL_UNHANDLED_PARAM,
MGCP_MDCX_FAIL_NO_CONNID,
MGCP_MDCX_FAIL_CONN_NOT_FOUND,
MGCP_MDCX_FAIL_INVALID_MODE,
MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS,
MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC,
MGCP_MDCX_FAIL_START_RTP,
MGCP_MDCX_FAIL_REJECTED_BY_POLICY,
MGCP_MDCX_DEFERRED_BY_POLICY
};
/* Global MCGP DLCX related rate counters */
enum {
MGCP_DLCX_SUCCESS,
MGCP_DLCX_FAIL_WILDCARD,
MGCP_DLCX_FAIL_NO_CONN,
MGCP_DLCX_FAIL_INVALID_CALLID,
MGCP_DLCX_FAIL_INVALID_CONNID,
MGCP_DLCX_FAIL_UNHANDLED_PARAM,
MGCP_DLCX_FAIL_REJECTED_BY_POLICY,
MGCP_DLCX_DEFERRED_BY_POLICY,
};
struct mgcp_trunk_config {
struct llist_head entry;
struct mgcp_config *cfg;
int trunk_nr;
int trunk_type;
char *audio_fmtp_extra;
char *audio_name;
int audio_payload;
int audio_send_ptime;
int audio_send_name;
int audio_loop;
int no_audio_transcoding;
int omit_rtcp;
int keepalive_interval;
/* RTP patching */
int force_constant_ssrc; /* 0: don't, 1: once */
int force_aligned_timing;
bool rfc5993_hr_convert;
/* spec handling */
int force_realloc;
/* timer */
struct osmo_timer_list keepalive_timer;
/* When set, incoming RTP packets are not filtered
* when ports and ip-address do not match (debug) */
int rtp_accept_all;
unsigned int number_endpoints;
int vty_number_endpoints;
struct mgcp_endpoint *endpoints;
/* Rate counter group which contains stats for processed CRCX commands. */
struct rate_ctr_group *mgcp_crcx_ctr_group;
/* Rate counter group which contains stats for processed MDCX commands. */
struct rate_ctr_group *mgcp_mdcx_ctr_group;
/* Rate counter group which contains stats for processed DLCX commands. */
struct rate_ctr_group *mgcp_dlcx_ctr_group;
/* Rate counter group which aggregates stats of individual RTP connections. */
struct rate_ctr_group *all_rtp_conn_stats;
};
enum mgcp_role {
MGCP_BSC = 0,
MGCP_BSC_NAT,
@@ -244,14 +147,12 @@ struct mgcp_config {
mgcp_change change_cb;
mgcp_policy policy_cb;
mgcp_reset reset_cb;
mgcp_realloc realloc_cb;
mgcp_rqnt rqnt_cb;
void *data;
uint32_t last_call_id;
/* trunk handling */
struct mgcp_trunk_config trunk;
/* list holding the trunks */
struct llist_head trunks;
enum mgcp_role role;
@@ -279,6 +180,13 @@ struct mgcp_config {
/* time after which inactive connections (CIs) get closed */
int conn_timeout;
/* osmocom CTRL interface */
struct ctrl_handle *ctrl;
/* global rate counters to measure the MGWs overall performance and
* health */
struct mgcp_ratectr_global ratectr;
};
/* config management */
@@ -286,8 +194,7 @@ struct mgcp_config *mgcp_config_alloc(void);
int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
enum mgcp_role role);
int mgcp_vty_init(void);
int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg);
void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval);
/*
* format helper functions
@@ -295,7 +202,7 @@ void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);
int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint);
int mgcp_send_reset_ep(struct mgcp_endpoint *endp);
int mgcp_send_reset_all(struct mgcp_config *cfg);

View File

@@ -1,7 +1,19 @@
#pragma once
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
#define PTYPE_UNDEFINED (-1)
struct mgcp_conn_rtp;
void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param);
int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
const char *subtype_name, unsigned int match_nr);

View File

@@ -100,6 +100,10 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg)
/* A prefix to denote the virtual trunk (RTP on both ends) */
#define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/"
/* A prefix to denote the e1 trunk
* (see also RFC3435 section E.2) */
#define MGCP_ENDPOINT_PREFIX_E1_TRUNK "ds/e1-"
/* Maximal number of payload types / codecs that can be negotiated via SDP at
* at once. */
#define MGCP_MAX_CODECS 10

View File

@@ -23,11 +23,114 @@
#pragma once
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_network.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/rate_ctr.h>
#include <inttypes.h>
#define LOGPCONN(conn, cat, level, fmt, args...) \
LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
(conn)->id, \
## args)
#define LOG_CONN(conn, level, fmt, args...) \
LOGP(DRTP, level, "(%s I:%s) " fmt, \
(conn)->endp ? (conn)->endp->name : "none", (conn)->id, ## args)
#define LOG_CONN_RTP(conn_rtp, level, fmt, args...) \
LOG_CONN((conn_rtp)->conn, level, fmt, ## args)
/* 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,
};
/*! Connection type, specifies which member of the union "u" in mgcp_conn
* contains a useful connection description (currently only RTP) */
enum mgcp_conn_type {
MGCP_CONN_TYPE_RTP,
};
/* MGCP connection (RTP) */
struct mgcp_conn_rtp {
/* Backpointer to conn struct */
struct mgcp_conn *conn;
/* Specific connection type */
enum mgcp_conn_rtp_type type;
/* Port status */
struct mgcp_rtp_end end;
/* Sequence bits */
struct mgcp_rtp_state state;
/* taps for the rtp connection; one per direction */
struct mgcp_rtp_tap tap_in;
struct mgcp_rtp_tap tap_out;
/* Osmux states (optional) */
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 */
struct osmux_in_handle *in;
/* handle to unbatch messages */
struct osmux_out_handle out;
/* statistics */
struct {
uint32_t chunks;
uint32_t octets;
} stats;
} osmux;
struct rate_ctr_group *rate_ctr_group;
};
/*! MGCP connection (untyped) */
struct mgcp_conn {
/*! list head */
struct llist_head entry;
/*! Backpointer to the endpoint where the conn belongs to */
struct mgcp_endpoint *endp;
/*! type of the connection (union) */
enum mgcp_conn_type type;
/*! mode of the connection */
enum mgcp_connection_mode mode;
/*! copy of the mode to restore the original setting (VTY) */
enum mgcp_connection_mode mode_orig;
/*! connection id to identify the connection */
char id[MGCP_CONN_ID_MAXLEN];
/*! human readable name (vty, logging) */
char name[256];
/*! activity tracker (for cleaning up inactive connections) */
struct osmo_timer_list watchdog;
/*! union with connection description */
union {
struct mgcp_conn_rtp rtp;
} u;
/*! pointer to optional private data */
void *priv;
};
/* RTP connection related counters */
enum {
IN_STREAM_ERR_TSTMP_CTR,
@@ -68,6 +171,11 @@ 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."}
};
/* 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;
}
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
enum mgcp_conn_type type, char *name);
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
@@ -78,3 +186,5 @@ void mgcp_conn_free_oldest(struct mgcp_endpoint *endp);
void mgcp_conn_free_all(struct mgcp_endpoint *endp);
char *mgcp_conn_dump(struct mgcp_conn *conn);
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn);
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp);
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);

View File

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

View File

@@ -0,0 +1,28 @@
#pragma once
/* A 64k timeslot on an E1 line can be subdevied into the following
* subslot combinations:
*
* subslot: offset:
* [ ][ ][ 16k ][8k_subslot] 0
* [ ][ 32k ][_subslot__][8k_subslot] 1
* [ ][ subslot ][ 16k ][8k_subslot] 2
* [ 64k ][__________][_subslot__][8k_subslot] 3
* [ timeslot ][ ][ 16k ][8k_subslot] 4
* [ ][ 32K ][_subslot__][8k_subslot] 5
* [ ][ subslot ][ 16k ][8k_subslot] 6
* [ ][ ][ subslot ][8k_subslot] 7
*
* Since overlapping assignment of subslots is not possible there is a limited
* set of subslot assignments possible. The e1_rates array lists the possible
* assignments as depicted above. Also each subslot assignment comes along with
* a bit offset in the E1 bitstream. The e1_offsets arrays lists the bit
* offsets. */
static const uint8_t e1_rates[] = { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8 };
static const uint8_t e1_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
int mgcp_e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr);
int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs);
void mgcp_e1_endp_update(struct mgcp_endpoint *endp);
void mgcp_e1_endp_release(struct mgcp_endpoint *endp);
int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg);

View File

@@ -23,15 +23,35 @@
#pragma once
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/i460_mux.h>
struct sockaddr_in;
struct mgcp_conn;
struct mgcp_conn_rtp;
struct mgcp_endpoint;
/* Callback type for RTP dispatcher functions
(e.g mgcp_dispatch_rtp_bridge_cb, see below) */
typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in *addr,
char *buf, unsigned int buf_size,
struct mgcp_conn *conn);
/* Number of E1 subslots (different variants, not all useable at the same time) */
#define MGCP_ENDP_E1_SUBSLOTS 15
#define LOGPENDP(endp, cat, level, fmt, args...) \
LOGP(cat, level, "endpoint:%s " fmt, \
endp ? endp->name : "none", \
## args)
struct osmo_rtp_msg_ctx {
int proto;
struct mgcp_conn_rtp *conn_src;
struct sockaddr_in *from_addr;
};
#define OSMO_RTP_MSG_CTX(MSGB) ((struct osmo_rtp_msg_ctx*)(MSGB)->cb)
osmo_static_assert(sizeof(((struct msgb*)0)->cb) >= sizeof(struct osmo_rtp_msg_ctx), osmo_rtp_msg_ctx_fits_in_msgb_cb);
/* Callback type for RTP dispatcher functions (e.g mgcp_dispatch_rtp_bridge_cb, see below).
* The OSMO_RTP_MSG_CTX() should be set appropriately on the msg. */
typedef int (*mgcp_dispatch_rtp_cb) (struct msgb *msg);
/* Callback type for endpoint specific cleanup actions. This function
* is automatically executed when a connection is freed (see mgcp_conn_free()
@@ -55,6 +75,7 @@ struct mgcp_endpoint_type {
/*! MGCP endpoint typeset */
struct mgcp_endpoint_typeset {
struct mgcp_endpoint_type rtp;
struct mgcp_endpoint_type e1;
};
/*! static MGCP endpoint typeset (pre-initalized, read-only) */
@@ -63,6 +84,9 @@ extern const struct mgcp_endpoint_typeset ep_typeset;
/*! MGCP endpoint model */
struct mgcp_endpoint {
/*! Unique endpoint name, used for addressing via MGCP */
char *name;
/*! Call identifier string (as supplied by the call agant) */
char *callid;
@@ -75,8 +99,8 @@ struct mgcp_endpoint {
/*! Backpointer to the MGW configuration */
struct mgcp_config *cfg;
/*! Backpointer to the Trunk specific configuration */
struct mgcp_trunk_config *tcfg;
/*! Backpointer to the trunk this endpoint belongs to */
struct mgcp_trunk *trunk;
/*! Endpoint properties (see above) */
const struct mgcp_endpoint_type *type;
@@ -93,9 +117,25 @@ struct mgcp_endpoint {
/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */
uint32_t x_osmo_ign;
/* E1 specific */
struct {
struct osmo_i460_schan_desc scd;
struct osmo_i460_subchan *schan;
struct osmo_fsm_inst *trau_sync_fi;
struct osmo_trau2rtp_state *trau_rtp_st;
uint8_t last_amr_ft;
struct mgcp_rtp_codec *last_codec;
} e1;
};
/*! Extract endpoint number for a given endpoint */
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, unsigned int index);
void mgcp_endp_release(struct mgcp_endpoint *endp);
int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid);
void mgcp_endp_update(struct mgcp_endpoint *endp);
struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
const struct mgcp_trunk *trunk);
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
struct mgcp_config *cfg);
bool mgcp_endp_avail(struct mgcp_endpoint *endp);

View File

@@ -1,352 +0,0 @@
/* MGCP Private Data */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <string.h>
#include <inttypes.h>
#include <osmocom/core/select.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/counter.h>
#include <osmocom/core/rate_ctr.h>
#define CI_UNUSED 0
/* FIXME: This this is only needed to compile the currently
* broken OSMUX support. Remove when fixed */
#define CONN_ID_BTS "0"
#define CONN_ID_NET "1"
enum mgcp_trunk_type {
MGCP_TRUNK_VIRTUAL,
MGCP_TRUNK_E1,
};
struct mgcp_rtp_stream_state {
uint32_t ssrc;
uint16_t last_seq;
uint32_t last_timestamp;
struct rate_ctr *err_ts_ctr;
int32_t last_tsdelta;
uint32_t last_arrival_time;
};
struct mgcp_rtp_state {
/* has this state structure been initialized? */
int initialized;
struct {
/* are we patching the SSRC value? */
int patch_ssrc;
/* original SSRC (to which we shall patch any different SSRC) */
uint32_t orig_ssrc;
/* offset to apply on the sequence number */
int seq_offset;
/* offset to apply on the timestamp number */
int32_t timestamp_offset;
} patch;
/* duration of a packet (FIXME: in which unit?) */
uint32_t packet_duration;
struct mgcp_rtp_stream_state in_stream;
struct mgcp_rtp_stream_state out_stream;
/* jitter and packet loss calculation */
struct {
int initialized;
uint16_t base_seq;
uint16_t max_seq;
uint32_t ssrc;
uint32_t jitter;
int32_t transit;
int cycles;
} stats;
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
};
struct mgcp_rtp_codec {
uint32_t rate;
int channels;
uint32_t frame_duration_num;
uint32_t frame_duration_den;
int payload_type;
char *audio_name;
char *subtype_name;
bool param_present;
struct mgcp_codec_param param;
};
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
struct mgcp_rtp_end {
/* local IP address of the RTP socket */
struct in_addr addr;
/* in network byte order */
int rtp_port, rtcp_port;
/* currently selected audio codec */
struct mgcp_rtp_codec *codec;
/* array with assigned audio codecs to choose from (SDP) */
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
/* number of assigned audio codecs (SDP) */
unsigned int codecs_assigned;
/* per endpoint data */
int frames_per_packet;
uint32_t packet_duration_ms;
int maximum_packet_time; /* -1: not set */
char *fmtp_extra;
/* are we transmitting packets (1) or dropping (0) outbound packets */
int output_enabled;
/* FIXME: This parameter can be set + printed, but is nowhere used! */
int force_output_ptime;
/* RTP patching */
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
int force_aligned_timing;
bool rfc5993_hr_convert;
/* Each end has a separate socket for RTP and RTCP */
struct osmo_fd rtp;
struct osmo_fd rtcp;
/* local UDP port number of the RTP socket; RTCP is +1 */
int local_port;
};
struct mgcp_rtp_tap {
/* is this tap active (1) or not (0) */
int enabled;
/* IP/port to which we're forwarding the tapped data */
struct sockaddr_in forward;
};
struct mgcp_lco {
char *string;
char *codec;
int pkt_period_min; /* time in ms */
int pkt_period_max; /* time in ms */
};
/* 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,
};
#include <osmocom/mgcp/osmux.h>
struct mgcp_conn;
/* MGCP connection (RTP) */
struct mgcp_conn_rtp {
/* Backpointer to conn struct */
struct mgcp_conn *conn;
/* Specific connection type */
enum mgcp_conn_rtp_type type;
/* Port status */
struct mgcp_rtp_end end;
/* Sequence bits */
struct mgcp_rtp_state state;
/* taps for the rtp connection; one per direction */
struct mgcp_rtp_tap tap_in;
struct mgcp_rtp_tap tap_out;
/* Osmux states (optional) */
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 */
struct osmux_in_handle *in;
/* handle to unbatch messages */
struct osmux_out_handle out;
/* statistics */
struct {
uint32_t chunks;
uint32_t octets;
} stats;
} osmux;
struct rate_ctr_group *rate_ctr_group;
};
/*! Connection type, specifies which member of the union "u" in mgcp_conn
* contains a useful connection description (currently only RTP) */
enum mgcp_conn_type {
MGCP_CONN_TYPE_RTP,
};
/*! MGCP connection (untyped) */
struct mgcp_conn {
/*! list head */
struct llist_head entry;
/*! Backpointer to the endpoint where the conn belongs to */
struct mgcp_endpoint *endp;
/*! type of the connection (union) */
enum mgcp_conn_type type;
/*! mode of the connection */
enum mgcp_connection_mode mode;
/*! copy of the mode to restore the original setting (VTY) */
enum mgcp_connection_mode mode_orig;
/*! connection id to identify the connection */
char id[MGCP_CONN_ID_MAXLEN];
/*! human readable name (vty, logging) */
char name[256];
/*! activity tracker (for cleaning up inactive connections) */
struct osmo_timer_list watchdog;
/*! union with connection description */
union {
struct mgcp_conn_rtp rtp;
} u;
/*! pointer to optional private data */
void *priv;
};
#include <osmocom/mgcp/mgcp_conn.h>
struct mgcp_endpoint_type;
/**
* Internal structure while parsing a request
*/
struct mgcp_parse_data {
struct mgcp_config *cfg;
struct mgcp_endpoint *endp;
char *trans;
char *save;
};
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
char *buf, int rc, struct mgcp_conn_rtp *conn_src,
struct mgcp_conn_rtp *conn_dst);
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
unsigned int buf_size, struct mgcp_conn *conn);
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
struct mgcp_conn_rtp *conn);
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
/* For transcoding we need to manage an in and an output that are connected */
static inline int endp_back_channel(int endpoint)
{
return endpoint + 60;
}
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
char *get_lco_identifier(const char *options);
int check_local_cx_options(void *ctx, const char *options);
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
struct mgcp_rtp_end *rtp);
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *rtp);
/* payload processing default functions */
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size);
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn_dst,
struct mgcp_conn_rtp *conn_src);
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
const struct mgcp_rtp_codec **codec,
const char **fmtp_extra,
struct mgcp_conn_rtp *conn);
/* internal RTP Annex A counting */
void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
const uint16_t seq, const int32_t transit,
const uint32_t ssrc);
int mgcp_set_ip_tos(int fd, int tos);
/* Was conn configured to handle Osmux? */
static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) {
return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT;
}
enum {
MGCP_DEST_NET = 0,
MGCP_DEST_BTS,
};
#define MGCP_DUMMY_LOAD 0x23
/**
* SDP related information
*/
/* Assume audio frame length of 20ms */
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
#define PTYPE_UNDEFINED (-1)
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);
#define LOGPENDP(endp, cat, level, fmt, args...) \
LOGP(cat, level, "endpoint:0x%x " fmt, \
endp ? ENDPOINT_NUMBER(endp) : -1, \
## args)
#define LOGPCONN(conn, cat, level, fmt, args...) \
LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
(conn)->id, \
## args)

View File

@@ -0,0 +1,163 @@
#pragma once
#include <inttypes.h>
#include <stdbool.h>
#include <osmocom/mgcp/mgcp.h>
#define MGCP_DUMMY_LOAD 0x23
#define RTP_BUF_SIZE 4096
struct mgcp_rtp_stream_state {
uint32_t ssrc;
uint16_t last_seq;
uint32_t last_timestamp;
struct rate_ctr *err_ts_ctr;
int32_t last_tsdelta;
uint32_t last_arrival_time;
};
struct mgcp_rtp_state {
/* has this state structure been initialized? */
int initialized;
struct {
/* are we patching the SSRC value? */
int patch_ssrc;
/* original SSRC (to which we shall patch any different SSRC) */
uint32_t orig_ssrc;
/* offset to apply on the sequence number */
int seq_offset;
/* offset to apply on the timestamp number */
int32_t timestamp_offset;
} patch;
/* duration of a packet (FIXME: in which unit?) */
uint32_t packet_duration;
/* Note: These states are not continuously updated, they serve as an
* information source to patch certain values in the RTP header. Do
* not use this state if constantly updated data about the RTP stream
* is needed. (see also mgcp_patch_and_count() */
struct mgcp_rtp_stream_state in_stream;
struct mgcp_rtp_stream_state out_stream;
/* jitter and packet loss calculation */
struct {
int initialized;
uint16_t base_seq;
uint16_t max_seq;
uint32_t ssrc;
uint32_t jitter;
int32_t transit;
int cycles;
} stats;
/* Alternative values for RTP tx, in case no sufficient header
* information is available so the header needs to be generated
* locally (when just forwarding packets, the header of incoming
* 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 {
uint32_t rate;
int channels;
uint32_t frame_duration_num;
uint32_t frame_duration_den;
int payload_type;
char *audio_name;
char *subtype_name;
bool param_present;
struct mgcp_codec_param param;
};
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
struct mgcp_rtp_end {
/* local IP address of the RTP socket */
struct in_addr addr;
/* in network byte order */
int rtp_port, rtcp_port;
/* currently selected audio codec */
struct mgcp_rtp_codec *codec;
/* array with assigned audio codecs to choose from (SDP) */
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
/* number of assigned audio codecs (SDP) */
unsigned int codecs_assigned;
/* per endpoint data */
int frames_per_packet;
uint32_t packet_duration_ms;
int maximum_packet_time; /* -1: not set */
char *fmtp_extra;
/* are we transmitting packets (1) or dropping (0) outbound packets */
int output_enabled;
/* FIXME: This parameter can be set + printed, but is nowhere used! */
int force_output_ptime;
/* RTP patching */
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
int force_aligned_timing;
bool rfc5993_hr_convert;
/* Each end has a separate socket for RTP and RTCP */
struct osmo_fd rtp;
struct osmo_fd rtcp;
/* local UDP port number of the RTP socket; RTCP is +1 */
int local_port;
};
struct mgcp_rtp_tap {
/* is this tap active (1) or not (0) */
int enabled;
/* IP/port to which we're forwarding the tapped data */
struct sockaddr_in forward;
};
struct mgcp_conn;
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
struct msgb *msg, struct mgcp_conn_rtp *conn_src,
struct mgcp_conn_rtp *conn_dst);
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg);
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
int mgcp_dispatch_e1_bridge_cb(struct msgb *msg);
void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
struct mgcp_conn_rtp *conn);
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
struct mgcp_rtp_state *state,
struct mgcp_rtp_end *rtp_end,
struct sockaddr_in *addr, struct msgb *msg);
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
int mgcp_set_ip_tos(int fd, int tos);
/* payload processing default functions */
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size);
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn_dst,
struct mgcp_conn_rtp *conn_src);
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
const struct mgcp_rtp_codec **codec,
const char **fmtp_extra,
struct mgcp_conn_rtp *conn);
/* internal RTP Annex A counting */
void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
const uint16_t seq, const int32_t transit,
const uint32_t ssrc);

View File

@@ -0,0 +1,27 @@
#pragma once
/* Internal structure while parsing a request */
struct mgcp_parse_data {
struct mgcp_config *cfg;
struct mgcp_endpoint *endp;
char *trans;
char *save;
};
/* Local connection options */
struct mgcp_lco {
char *string;
char *codec;
int pkt_period_min; /* time in ms */
int pkt_period_max; /* time in ms */
};
char *get_lco_identifier(const char *options);
int check_local_cx_options(void *ctx, const char *options);
struct mgcp_rtp_end;
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
struct mgcp_rtp_end *rtp);
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *rtp);

View File

@@ -0,0 +1,95 @@
#pragma once
/* Global MCGP general rate counters */
enum {
MGCP_GENERAL_RX_MSGS_TOTAL,
MGCP_GENERAL_RX_MSGS_RETRANSMITTED,
MGCP_GENERAL_RX_MSGS_HANDLED,
MGCP_GENERAL_RX_MSGS_UNHANDLED,
MGCP_GENERAL_RX_FAIL_MSG_PARSE,
MGCP_GENERAL_RX_FAIL_NO_ENDPOINT,
};
/* Trunk-global MCGP CRCX related rate counters */
enum {
MGCP_CRCX_SUCCESS,
MGCP_CRCX_FAIL_BAD_ACTION,
MGCP_CRCX_FAIL_UNHANDLED_PARAM,
MGCP_CRCX_FAIL_MISSING_CALLID,
MGCP_CRCX_FAIL_INVALID_MODE,
MGCP_CRCX_FAIL_LIMIT_EXCEEDED,
MGCP_CRCX_FAIL_UNKNOWN_CALLID,
MGCP_CRCX_FAIL_ALLOC_CONN,
MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC,
MGCP_CRCX_FAIL_START_RTP,
MGCP_CRCX_FAIL_REJECTED_BY_POLICY,
MGCP_CRCX_FAIL_NO_OSMUX,
MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS,
MGCP_CRCX_FAIL_CODEC_NEGOTIATION,
MGCP_CRCX_FAIL_BIND_PORT,
MGCP_CRCX_FAIL_AVAIL,
MGCP_CRCX_FAIL_CLAIM,
};
/* Trunk-global MCGP MDCX related rate counters */
enum {
MGCP_MDCX_SUCCESS,
MGCP_MDCX_FAIL_WILDCARD,
MGCP_MDCX_FAIL_NO_CONN,
MGCP_MDCX_FAIL_INVALID_CALLID,
MGCP_MDCX_FAIL_INVALID_CONNID,
MGCP_MDCX_FAIL_UNHANDLED_PARAM,
MGCP_MDCX_FAIL_NO_CONNID,
MGCP_MDCX_FAIL_CONN_NOT_FOUND,
MGCP_MDCX_FAIL_INVALID_MODE,
MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS,
MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC,
MGCP_MDCX_FAIL_START_RTP,
MGCP_MDCX_FAIL_REJECTED_BY_POLICY,
MGCP_MDCX_DEFERRED_BY_POLICY,
MGCP_MDCX_FAIL_AVAIL,
};
/* Trunk-global MCGP DLCX related rate counters */
enum {
MGCP_DLCX_SUCCESS,
MGCP_DLCX_FAIL_WILDCARD,
MGCP_DLCX_FAIL_NO_CONN,
MGCP_DLCX_FAIL_INVALID_CALLID,
MGCP_DLCX_FAIL_INVALID_CONNID,
MGCP_DLCX_FAIL_UNHANDLED_PARAM,
MGCP_DLCX_FAIL_REJECTED_BY_POLICY,
MGCP_DLCX_DEFERRED_BY_POLICY,
MGCP_DLCX_FAIL_AVAIL,
};
/* Trunk-global E1 related counters */
enum {
E1_I460_TRAU_RX_FAIL_CTR,
E1_I460_TRAU_TX_FAIL_CTR,
E1_I460_TRAU_MUX_EMPTY_CTR,
};
/* NOTE: When adding counters, also the dump_ratectr_* routines in vty.c must be updated. */
struct mgcp_ratectr_global {
/* Rate counter group which contains stats for generic MGCP events. */
struct rate_ctr_group *mgcp_general_ctr_group;
};
struct mgcp_ratectr_trunk {
/* Rate counter group which contains stats for processed CRCX commands. */
struct rate_ctr_group *mgcp_crcx_ctr_group;
/* Rate counter group which contains stats for processed MDCX commands. */
struct rate_ctr_group *mgcp_mdcx_ctr_group;
/* Rate counter group which contains stats for processed DLCX commands. */
struct rate_ctr_group *mgcp_dlcx_ctr_group;
/* Rate counter group which aggregates stats of individual RTP connections. */
struct rate_ctr_group *all_rtp_conn_stats;
/* Rate counter group which contains stats for E1 events (only valid for E1 trunks) */
struct rate_ctr_group *e1_stats;
};
int mgcp_ratectr_global_alloc(void *ctx, struct mgcp_ratectr_global *ratectr);
int mgcp_ratectr_trunk_alloc(void *ctx, struct mgcp_ratectr_trunk *ratectr);

View File

@@ -24,7 +24,6 @@
#pragma once
#include <osmocom/mgcp/mgcp_internal.h>
#include <inttypes.h>
void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn);

View File

@@ -0,0 +1,80 @@
#pragma once
#include <osmocom/gsm/i460_mux.h>
#define LOGPTRUNK(trunk, cat, level, fmt, args...) \
LOGP(cat, level, "trunk:%u " fmt, \
trunk ? trunk->trunk_nr : 0, \
## args)
enum mgcp_trunk_type {
MGCP_TRUNK_VIRTUAL,
MGCP_TRUNK_E1,
};
struct mgcp_trunk {
struct llist_head entry;
struct mgcp_config *cfg;
int trunk_nr;
enum mgcp_trunk_type trunk_type;
char *audio_fmtp_extra;
int audio_send_ptime;
int audio_send_name;
int audio_loop;
int no_audio_transcoding;
int omit_rtcp;
int keepalive_interval;
/* RTP patching */
int force_constant_ssrc; /* 0: don't, 1: once */
int force_aligned_timing;
bool rfc5993_hr_convert;
/* spec handling */
int force_realloc;
/* timer */
struct osmo_timer_list keepalive_timer;
/* When set, incoming RTP packets are not filtered
* when ports and ip-address do not match (debug) */
int rtp_accept_all;
unsigned int number_endpoints;
struct mgcp_endpoint **endpoints;
/* global rate counters to measure the trunks overall performance and health */
struct mgcp_ratectr_trunk ratectr;
union {
/* Virtual trunk specific */
struct {
unsigned int vty_number_endpoints;
} v;
/* E1 specific */
struct {
unsigned int vty_line_nr;
struct e1inp_line *line;
bool ts_in_use[31];
struct osmo_i460_timeslot i460_ts[31];
} e1;
};
};
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr);
int mgcp_trunk_equip(struct mgcp_trunk *trunk);
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr);
struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname);
int e1_trunk_nr_from_epname(const char *epname);
struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num);
/* The virtual trunk is always created on trunk id 0 for historical reasons,
* use this define constant as ID when allocating a virtual trunk. Other
* trunks may be assigned with arbritrary id numbers */
#define MGCP_VIRT_TRUNK_ID 0

View File

@@ -137,6 +137,8 @@ uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp);
const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp);
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp);
const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uint8_t trunk_id, uint8_t ts,
uint8_t rate, uint8_t offset);
/* Invoked when an MGCP response is received or sending failed. When the
* response is passed as NULL, this indicates failure during transmission. */

View File

@@ -20,7 +20,7 @@ AM_LDFLAGS = \
# This is not at all related to the release version, but a range of supported
# API versions. Read TODO_RELEASE in the source tree's root!
MGCP_CLIENT_LIBVERSION=6:0:0
MGCP_CLIENT_LIBVERSION=7:0:1
lib_LTLIBRARIES = \
libosmo-mgcp-client.la \

View File

@@ -735,6 +735,8 @@ struct mgcp_client *mgcp_client_init(void *ctx,
struct mgcp_client *mgcp;
mgcp = talloc_zero(ctx, struct mgcp_client);
if (!mgcp)
return NULL;
INIT_LLIST_HEAD(&mgcp->responses_pending);
INIT_LLIST_HEAD(&mgcp->inuse_endpoints);
@@ -828,8 +830,8 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
inet_aton(mgcp->actual.remote_addr, &addr.sin_addr);
mgcp->remote_addr = htonl(addr.sin_addr.s_addr);
osmo_wqueue_init(wq, 10);
wq->bfd.when = BSC_FD_READ;
osmo_wqueue_init(wq, 1024);
wq->bfd.when = OSMO_FD_READ;
wq->bfd.data = mgcp;
wq->read_cb = mgcp_do_read;
wq->write_cb = mgcp_do_write;
@@ -892,11 +894,66 @@ static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgc
return endpoint;
}
/*! Compose endpoint name for a wildcarded request to the virtual trunk
* \param[in] mgcp MGCP client descriptor.
* \returns string containing the endpoint name (e.g. rtpbridge\*@mgw) */
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp)
{
return _mgcp_client_name_append_domain(mgcp, "rtpbridge/*");
}
/*! Compose endpoint name for an E1 endpoint.
* \param[in] ctx talloc context.
* \param[in] mgcp MGCP client descriptor.
* \param[in] trunk_id id number of the E1 trunk (1-64).
* \param[in] ts timeslot on the E1 trunk (1-31).
* \param[in] rate bitrate used on the E1 trunk (e.g 16 for 16kbit).
* \param[in] offset bit offset of the E1 subslot (e.g. 4 for the third 16k subslot).
* \returns string containing the endpoint name (e.g. ds/e1-1/s-1/su16-4). */
const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uint8_t trunk_id, uint8_t ts,
uint8_t rate, uint8_t offset)
{
/* See also comment in libosmo-mgcp, mgcp_client.c, gen_e1_epname() */
const uint8_t valid_rates[] = { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8 };
const uint8_t valid_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
uint8_t i;
bool rate_offs_valid = false;
char *epname;
epname =
talloc_asprintf(ctx, "ds/e1-%u/s-%u/su%u-%u@%s", trunk_id, ts, rate, offset,
mgcp_client_endpoint_domain(mgcp));
if (!epname) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot compose MGCP e1-endpoint name!\n");
return NULL;
}
/* Check if the supplied rate/offset pair resembles a valid combination */
for (i = 0; i < sizeof(valid_rates); i++) {
if (valid_rates[i] == rate && valid_offsets[i] == offset)
rate_offs_valid = true;
}
if (!rate_offs_valid) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot compose MGCP e1-endpoint name (%s), rate(%u)/offset(%u) combination is invalid!\n", epname,
rate, offset);
talloc_free(epname);
return NULL;
}
/* An E1 line has a maximum of 32 timeslots, while the first (ts=0) is
* reserverd for framing and alignment, so we can not use it here. */
if (ts == 0 || ts > 31) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot compose MGCP e1-endpoint name (%s), E1-timeslot number (%u) is invalid!\n", epname, ts);
talloc_free(epname);
return NULL;
}
return epname;
}
struct mgcp_response_pending * mgcp_client_pending_add(
struct mgcp_client *mgcp,
mgcp_trans_id_t trans_id,
@@ -906,6 +963,9 @@ struct mgcp_response_pending * mgcp_client_pending_add(
struct mgcp_response_pending *pending;
pending = talloc_zero(mgcp, struct mgcp_response_pending);
if (!pending)
return NULL;
pending->trans_id = trans_id;
pending->response_cb = response_cb;
pending->priv = priv;
@@ -925,7 +985,7 @@ struct mgcp_response_pending * mgcp_client_pending_add(
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
mgcp_response_cb_t response_cb, void *priv)
{
struct mgcp_response_pending *pending;
struct mgcp_response_pending *pending = NULL;
mgcp_trans_id_t trans_id;
int rc;
@@ -937,7 +997,14 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
return -EINVAL;
}
pending = mgcp_client_pending_add(mgcp, trans_id, response_cb, priv);
/* Do not allocate a dummy 'mgcp_response_pending' entry */
if (response_cb != NULL) {
pending = mgcp_client_pending_add(mgcp, trans_id, response_cb, priv);
if (!pending) {
talloc_free(msg);
return -ENOMEM;
}
}
if (msgb_l2len(msg) > 4096) {
LOGP(DLMGCP, LOGL_ERROR,
@@ -959,9 +1026,13 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
return 0;
mgcp_tx_error:
if (!pending)
return rc;
/* Dequeue pending response, it's going to be free()d */
llist_del(&pending->entry);
/* Pass NULL to response cb to indicate an error */
mgcp_client_handle_response(mgcp, pending, NULL);
return -1;
return rc;
}
/*! Cancel a pending transaction.
@@ -1206,6 +1277,7 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
"Endpoint name (%s) lacks separator (@), can not generate MGCP message\n",
mgcp_msg->endpoint);
msgb_free(msg);
return NULL;
}
rc += msgb_printf(msg, " %s", mgcp_msg->endpoint);

View File

@@ -10,6 +10,8 @@ AM_CFLAGS = \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(LIBOSMOTRAU_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -18,6 +20,8 @@ AM_LDFLAGS = \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOTRAU_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
@@ -40,4 +44,8 @@ libosmo_mgcp_a_SOURCES = \
mgcp_conn.c \
mgcp_stat.c \
mgcp_endp.c \
mgcp_trunk.c \
mgcp_ctrl.c \
mgcp_ratectr.c \
mgcp_e1.c \
$(NULL)

View File

@@ -17,8 +17,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <errno.h>
/* Helper function to dump codec information of a specified codec to a printable
@@ -279,36 +284,16 @@ error:
* Helper function for mgcp_codec_decide() */
static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec)
{
char codec_name[64];
/* A codec name must be set, if not, this might mean that the codec
* (payload type) that was assigned is unknown to us so we must stop
* here. */
if (!codec->subtype_name)
return false;
/* We now extract the codec_name (letters before the /, e.g. "GSM"
* from the audio name that is stored in the trunk configuration.
* We do not compare to the full audio_name because we expect that
* "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the
* audio name of the codec is set to "GSM" */
if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1)
return false;
/* FIXME: implement meaningful checks to make sure that the given codec
* is compatible with the given endpoint */
/* Finally we check if the subtype_name we have generated from the
* audio_name in the trunc struct patches the codec_name of the
* given codec */
if (strcasecmp(codec_name, codec->subtype_name) == 0)
return true;
/* FIXME: It is questinable that the method to pick a compatible
* codec can work properly. Since this useses tcfg->audio_name, as
* a reference, which is set to "AMR/8000" permanently.
* tcfg->audio_name must be updated by the first connection that
* has been made on an endpoint, so that the second connection
* can make a meaningful decision here */
return false;
return true;
}
/*! Decide for one suitable codec
@@ -335,7 +320,7 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
for (i = 0; i < rtp->codecs_assigned; i++) {
/* When no transcoding is available, avoid codecs that would
* require transcoding. */
if (endp->tcfg->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
if (endp->trunk->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n",
rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name);
continue;
@@ -451,3 +436,28 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp
return codec_dst->payload_type;
}
/* Find the payload type number configured for a specific codec by SDP.
* For example, IuUP gets assigned a payload type number, and the endpoint needs to translate that to the number
* assigned to "AMR" on the other conn (by a=rtpmap:N).
* \param conn The side of an endpoint to get the payload type number for (to translate the payload type number to).
* \param subtype_name SDP codec name without parameters (e.g. "AMR").
* \param match_nr Index for the match found, first being match_nr == 0. Iterate all matches by calling multiple times
* with incrementing match_nr.
* \return codec definition for that conn matching the subtype_name, or NULL if no such match_nr is found.
*/
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)
{
int i;
for (i = 0; i < conn->end.codecs_assigned; i++) {
if (!strcmp(conn->end.codecs[i].subtype_name, subtype_name)) {
if (match_nr) {
match_nr--;
continue;
}
return &conn->end.codecs[i];
}
}
return NULL;
}

View File

@@ -22,9 +22,11 @@
*/
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_network.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/gsm/gsm_utils.h>
@@ -254,12 +256,10 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
return NULL;
}
static void
aggregate_rtp_conn_stats(struct mgcp_trunk_config *trunk, struct mgcp_conn_rtp *conn_rtp)
static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp)
{
struct rate_ctr_group *all_stats = trunk->all_rtp_conn_stats;
struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats;
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
int i;
if (all_stats == NULL || conn_stats == NULL)
return;
@@ -269,8 +269,11 @@ aggregate_rtp_conn_stats(struct mgcp_trunk_config *trunk, struct mgcp_conn_rtp *
* All other counters in both counter groups correspond to each other. */
OSMO_ASSERT(conn_stats->desc->num_ctr + 1 == all_stats->desc->num_ctr);
for (i = 0; i < conn_stats->desc->num_ctr; i++)
rate_ctr_add(&all_stats->ctr[i], conn_stats->ctr[i].current);
/* all other counters are [now] updated in real-time */
rate_ctr_add(&all_stats->ctr[IN_STREAM_ERR_TSTMP_CTR],
conn_stats->ctr[IN_STREAM_ERR_TSTMP_CTR].current);
rate_ctr_add(&all_stats->ctr[OUT_STREAM_ERR_TSTMP_CTR],
conn_stats->ctr[OUT_STREAM_ERR_TSTMP_CTR].current);
rate_ctr_inc(&all_stats->ctr[RTP_NUM_CONNECTIONS]);
}
@@ -294,7 +297,7 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
aggregate_rtp_conn_stats(endp->tcfg, &conn->u.rtp);
aggregate_rtp_conn_stats(endp, &conn->u.rtp);
mgcp_rtp_conn_cleanup(&conn->u.rtp);
break;
default:
@@ -395,3 +398,13 @@ struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn)
return NULL;
}
/*! get oldest connection in the list.
* \param[in] endp associated endpoint */
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp)
{
if (llist_empty(&endp->conns))
return NULL;
return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
}

View File

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

690
src/libosmo-mgcp/mgcp_e1.c Normal file
View File

@@ -0,0 +1,690 @@
/* E1 traffic handling */
/*
* (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* 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 <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/core/msgb.h>
#include <osmocom/abis/e1_input.h>
#include <osmocom/abis/abis.h>
#include <osmocom/trau/trau_sync.h>
#include <osmocom/trau/trau_frame.h>
#include <osmocom/trau/trau_rtp.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/netif/rtp.h>
#include <osmocom/mgcp/debug.h>
#include <osmocom/mgcp/mgcp_e1.h>
#include <osmocom/codec/codec.h>
#define DEBUG_BITS_MAX 80
#define DEBUG_BYTES_MAX 40
#define DEBUG_E1_TS 0
#define E1_TS_BYTES 160
#define E1_TRAU_BITS 320
#define E1_TRAU_BITS_MSGB 2048
static struct mgcp_config *cfg;
static const struct e1inp_line_ops dummy_e1_line_ops = {
.sign_link_up = NULL,
.sign_link_down = NULL,
.sign_link = NULL,
};
/* EFR idle frame */
static const ubit_t idle_tf_efr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1,
};
/* FR idle frame */
static const ubit_t idle_tf_fr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1,
};
/* Idle speech frame, see also GSM 08.60, chapter 3.4 */
static const ubit_t idle_tf_spch[] = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1,
};
/* If the RTP transmission has dropouts for some reason the I.460 TX-Queue may
* run empty. In order to make sure that the TRAU frame transmission continues
* we generate idle TRAU frames here. */
static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_data)
{
struct mgcp_endpoint *endp = user_data;
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
struct msgb *msg = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-IDLE-TX-TRAU-frame");
uint8_t *ptr;
const uint8_t *ptr_ft;
enum osmo_trau_frame_type ft;
rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_MUX_EMPTY_CTR]);
/* Choose an appropiate idle frame type */
ft = endp->e1.trau_rtp_st->type;
switch (ft) {
case OSMO_TRAU16_FT_FR:
ptr_ft = idle_tf_fr;
break;
case OSMO_TRAU16_FT_EFR:
ptr_ft = idle_tf_efr;
break;
default:
/* FIXME: What about 8k subslots and AMR frames? */
ptr_ft = idle_tf_spch;
}
/* Put the replacement into a message buffer and enqueue it into the
* I.460 multiplexer */
ptr = msgb_put(msg, E1_TRAU_BITS);
memcpy(ptr, ptr_ft, E1_TRAU_BITS);
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-IDLE-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg),
osmo_ubit_dump(msgb_data(msg), msgb_length(msg) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg)));
osmo_i460_mux_enqueue(endp->e1.schan, msg);
}
/* called by I.460 de-multeiplexer; feed output of I.460 demux into TRAU frame sync */
static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data, const ubit_t *bits,
unsigned int num_bits)
{
struct mgcp_endpoint *endp = user_data;
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u bits from subslot: %s...\n", num_bits,
osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
OSMO_ASSERT(endp->e1.trau_sync_fi);
osmo_trau_sync_rx_ubits(endp->e1.trau_sync_fi, bits, num_bits);
}
/* called for each synchronized TRAU frame received; decode frame + convert to RTP
* (the resulting frame will be prepended with an all-zero (12-byte) rtp header) */
static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int num_bits)
{
struct msgb *msg = msgb_alloc(RTP_BUF_SIZE, "RTP-rx-from-E1");
unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
struct mgcp_endpoint *endp = user_data;
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
struct mgcp_conn *conn_dst;
struct osmo_trau_frame fr;
int rc;
if (!bits || num_bits == 0)
goto skip;
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u TRAU frame bits from E1 subslot: %s...\n",
num_bits, osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
/* Decode TRAU frame */
switch (endp->e1.scd.rate) {
case OSMO_I460_RATE_8k:
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 8k trau frame...\n");
rc = osmo_trau_frame_decode_8k(&fr, bits, OSMO_TRAU_DIR_UL);
break;
case OSMO_I460_RATE_16k:
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 16k trau frame...\n");
rc = osmo_trau_frame_decode_16k(&fr, bits, OSMO_TRAU_DIR_UL);
break;
default:
/* TRAU frames only exist in 8K or 16K subslots. */
OSMO_ASSERT(false);
break;
}
if (rc != 0) {
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to decode trau frame\n");
goto skip;
}
/* Check if the payload type is supported and what the expected lenth
* of the RTP payload will be. */
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoded trau frame type: %s\n",
osmo_trau_frame_type_name(fr.type));
/* Convert decoded trau frame to RTP frame */
struct osmo_trau2rtp_state t2rs = {
.type = fr.type,
};
rc = osmo_trau2rtp(msgb_data(msg) + rtp_hdr_len, msg->data_len - rtp_hdr_len, &fr, &t2rs);
if (rc <= 0) {
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to convert trau frame to RTP audio\n");
goto skip;
}
msgb_put(msg, rtp_hdr_len + rc);
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: encoded %u bytes of RTP audio: %s\n", rc,
osmo_hexdump(msgb_data(msg) + rtp_hdr_len, msgb_length(msg) - rtp_hdr_len));
/* Forward RTP data to IP */
conn_dst = llist_first_entry(&endp->conns, struct mgcp_conn, entry);
if (!conn_dst) {
LOGPENDP(endp, DE1, LOGL_DEBUG,
"E1-I.460-RX: unable to forward RTP audio data from E1: no connection to forward an incoming RTP packet to\n");
goto skip;
}
OSMO_ASSERT(conn_dst->type == MGCP_CONN_TYPE_RTP);
mgcp_send(endp, 1, NULL, msg, &conn_dst->u.rtp, &conn_dst->u.rtp);
msgb_free(msg);
return;
skip:
rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_RX_FAIL_CTR]);
msgb_free(msg);
return;
}
/* Function to handle outgoing E1 traffic */
static void e1_send(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
{
struct msgb *msg = msgb_alloc(E1_TS_BYTES, "E1-TX-timeslot-bytes");
uint8_t *ptr;
/* Get E1 frame from I.460 multiplexer */
ptr = msgb_put(msg, E1_TS_BYTES);
osmo_i460_mux_out(&trunk->e1.i460_ts[ts->num - 1], ptr, E1_TS_BYTES);
#if DEBUG_E1_TS == 1
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-TX: (ts:%u) sending %u bytes: %s...\n", ts->num, msgb_length(msg),
osmo_hexdump_nospc(msgb_data(msg),
msgb_length(msg) > DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
#endif
/* Hand data over to the E1 stack */
msgb_enqueue(&ts->raw.tx_queue, msg);
return;
}
/* Callback function to handle incoming E1 traffic */
static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
{
struct mgcp_trunk *trunk;
/* Find associated trunk */
trunk = mgcp_trunk_by_line_num(cfg, ts->line->num);
if (!trunk) {
LOGP(DE1, LOGL_DEBUG, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num);
return;
}
/* Check if the incoming data looks sane */
if (msgb_length(msg) != E1_TS_BYTES) {
LOGPTRUNK(trunk, DE1, LOGL_NOTICE,
"E1-RX: (ts:%u) expected length is %u, actual length is %u!\n", ts->num, E1_TS_BYTES,
msgb_length(msg));
}
#if DEBUG_E1_TS == 1
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-RX: (ts:%u) receiving %u bytes: %s...\n", ts->num,
msgb_length(msg), osmo_hexdump_nospc(msgb_data(msg),
msgb_length(msg) >
DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
#endif
/* Hand data over to the I.460 demultiplexer. */
osmo_i460_demux_in(&trunk->e1.i460_ts[ts->num - 1], msgb_data(msg), msgb_length(msg));
/* Trigger sending of pending E1 traffic */
e1_send(ts, trunk);
}
/*! Find an endpoint by its name on a specified trunk.
* \param[in] trunk trunk configuration.
* \param[in] ts_nr E1 timeslot number.
* \returns -EINVAL on failure, 0 on success. */
int mgcp_e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
{
/*! Each timeslot needs only to be configured once. The Timeslot then
* stays open and permanently receives data. It is then up to the
* I.460 demultiplexer to add/remove subchannels as needed. It is
* allowed to call this function multiple times since we check if the
* timeslot is already configured. */
struct e1inp_line *e1_line;
int rc;
OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
cfg = trunk->cfg;
if (trunk->e1.ts_in_use[ts_nr - 1]) {
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u already set up, skipping...\n", ts_nr);
return 0;
}
/* Get E1 line */
if (!trunk->e1.line) {
e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
if (!e1_line) {
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "no such E1 line %u - check VTY config!\n",
trunk->e1.vty_line_nr);
return -EINVAL;
}
e1inp_line_bind_ops(e1_line, &dummy_e1_line_ops);
} else
e1_line = trunk->e1.line;
if (!e1_line)
return -EINVAL;
/* Configure E1 timeslot */
rc = e1inp_ts_config_raw(&e1_line->ts[ts_nr - 1], e1_line, e1_recv_cb);
if (rc < 0)
return -EINVAL;
e1inp_line_update(e1_line);
if (rc < 0)
return -EINVAL;
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u set up successfully.\n", ts_nr);
trunk->e1.ts_in_use[ts_nr - 1] = true;
return 0;
}
/* Determine a suitable TRAU frame type for a given codec */
static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
uint8_t amr_ft, struct mgcp_endpoint *endp)
{
if (strcmp(sdp_subtype_name, "GSM") == 0)
return OSMO_TRAU16_FT_FR;
else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
return OSMO_TRAU16_FT_EFR;
else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
return OSMO_TRAU16_FT_HR;
else if (strcmp(sdp_subtype_name, "AMR") == 0) {
if (i460_rate == OSMO_I460_RATE_8k) {
switch (amr_ft) {
case AMR_4_75:
case AMR_5_15:
case AMR_5_90:
return OSMO_TRAU8_AMR_LOW;
case AMR_6_70:
return OSMO_TRAU8_AMR_6k7;
case AMR_7_40:
return OSMO_TRAU8_AMR_7k4;
default:
LOGPENDP(endp, DE1, LOGL_ERROR,
"E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
return OSMO_TRAU_FT_NONE;
}
}
return OSMO_TRAU16_FT_AMR;
} else {
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
sdp_subtype_name);
return OSMO_TRAU_FT_NONE;
}
}
/* Determine a suitable TRAU frame type for a given codec */
static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
uint8_t amr_ft, struct mgcp_endpoint *endp)
{
if (strcmp(sdp_subtype_name, "GSM") == 0)
return OSMO_TRAU_SYNCP_16_FR_EFR;
else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
return OSMO_TRAU_SYNCP_16_FR_EFR;
else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
return OSMO_TRAU_SYNCP_8_HR;
else if (strcmp(sdp_subtype_name, "AMR") == 0) {
if (i460_rate == OSMO_I460_RATE_8k) {
switch (amr_ft) {
case AMR_4_75:
case AMR_5_15:
case AMR_5_90:
return OSMO_TRAU_SYNCP_8_AMR_LOW;
case AMR_6_70:
return OSMO_TRAU_SYNCP_8_AMR_6K7;
case AMR_7_40:
return OSMO_TRAU_SYNCP_8_AMR_7K4;
default:
LOGPENDP(endp, DE1, LOGL_ERROR,
"E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
return OSMO_TRAU_SYNCP_16_FR_EFR;
}
}
return OSMO_TRAU_SYNCP_16_FR_EFR;
} else {
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
sdp_subtype_name);
return OSMO_TRAU_SYNCP_16_FR_EFR;
}
}
/* Find out if a given TRAU frame type is AMR */
static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
{
switch (ft) {
case OSMO_TRAU16_FT_AMR:
case OSMO_TRAU8_AMR_LOW:
case OSMO_TRAU8_AMR_6k7:
case OSMO_TRAU8_AMR_7k4:
return true;
default:
return false;
}
}
/* !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.
* \param[in] offset E1 bit offset.
* \returns 0 on success, -EINVAL on error. */
int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs)
{
int rc;
enum osmo_tray_sync_pat_id sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
OSMO_ASSERT(ts != 0);
OSMO_ASSERT(ts != 0xFF);
OSMO_ASSERT(ss != 0xFF);
OSMO_ASSERT(offs != 0xFF);
memset(&endp->e1, 0, sizeof(endp->e1));
endp->e1.last_amr_ft = AMR_4_75;
/* Set up E1 line / timeslot */
rc = mgcp_e1_init(endp->trunk, ts);
if (rc != 0)
return -EINVAL;
/* Set up I.460 mux */
switch (e1_rates[ss]) {
case 64:
endp->e1.scd.rate = OSMO_I460_RATE_64k;
endp->e1.scd.demux.num_bits = 160 * 8;
break;
case 32:
endp->e1.scd.rate = OSMO_I460_RATE_32k;
endp->e1.scd.demux.num_bits = 80 * 8;
break;
case 16:
endp->e1.scd.rate = OSMO_I460_RATE_16k;
endp->e1.scd.demux.num_bits = 40 * 8;
sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
break;
case 8:
endp->e1.scd.rate = OSMO_I460_RATE_8k;
endp->e1.scd.demux.num_bits = 20 * 8;
sync_pat_id = OSMO_TRAU_SYNCP_8_HR;
break;
}
endp->e1.scd.bit_offset = offs;
endp->e1.scd.demux.out_cb_bits = e1_i460_demux_bits_cb;
endp->e1.scd.demux.out_cb_bytes = NULL;
endp->e1.scd.demux.user_data = endp;
endp->e1.scd.mux.in_cb_queue_empty = e1_i460_mux_empty_cb;
endp->e1.scd.mux.user_data = endp;
LOGPENDP(endp, DE1, LOGL_DEBUG, "adding I.460 subchannel: ts=%u, bit_offset=%u, rate=%uk, num_bits=%lu\n", ts,
offs, e1_rates[ss], endp->e1.scd.demux.num_bits);
endp->e1.schan = osmo_i460_subchan_add(endp, &endp->trunk->e1.i460_ts[ts - 1], &endp->e1.scd);
if (!endp->e1.schan) {
LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 subchannel: failed!\n");
return -EINVAL;
}
if (endp->e1.scd.rate == OSMO_I460_RATE_16k || endp->e1.scd.rate == OSMO_I460_RATE_8k) {
/* TRAU frames are only specified for 16k and 8k subslots. For all other subslot
* types the concept of TRAU frames does not apply. However, at the moment this
* is the only format we currently support in osmo-mgw */
endp->e1.trau_sync_fi = osmo_trau_sync_alloc(endp, "trau-sync", sync_frame_out_cb, sync_pat_id, endp);
if (!endp->e1.trau_sync_fi) {
LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 TRAU frame sync: failed!\n");
return -EINVAL;
}
endp->e1.trau_rtp_st = talloc_zero(endp->e1.trau_sync_fi, struct osmo_trau2rtp_state);
endp->e1.trau_rtp_st->type = OSMO_TRAU_FT_NONE;
} else {
LOGPENDP(endp, DE1, LOGL_ERROR,
"osmo-mgw currently only supports 16K and 8K subslots (TRAU frames)!\n");
return -EINVAL;
}
return 0;
}
/*! Update E1 related parameters (codec and sync pattern).
* \param[in] endp endpoint to update. */
void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
{
struct mgcp_conn *conn;
struct mgcp_rtp_codec *codec;
enum osmo_tray_sync_pat_id sync_pat_id;
/* In order to determine the codec, find the oldest connection on
* the endpoint and use its codec information. Normally on an E1
* endpoint no more than one connection should exist. */
conn = mgcp_conn_get_oldest(endp);
OSMO_ASSERT(conn);
codec = conn->u.rtp.end.codec;
OSMO_ASSERT(codec);
/* Update codec information */
endp->e1.trau_rtp_st->type =
determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
endp->e1.last_codec = codec;
/* Update sync pattern */
sync_pat_id = determine_trau_sync_pat(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
osmo_trau_sync_set_pat(endp->e1.trau_sync_fi, sync_pat_id);
}
/*! Remove E1 resources from endpoint
* \param[in] endp endpoint to release. */
void mgcp_e1_endp_release(struct mgcp_endpoint *endp)
{
LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n");
if (endp->e1.schan)
osmo_i460_subchan_del(endp->e1.schan);
if (endp->e1.trau_rtp_st)
talloc_free(endp->e1.trau_rtp_st);
if (endp->e1.trau_sync_fi)
osmo_fsm_inst_term(endp->e1.trau_sync_fi, OSMO_FSM_TERM_REGULAR, NULL);
memset(&endp->e1, 0, sizeof(endp->e1));
}
/*! Accept RTP message buffer with RTP data and enqueue voice data for E1 transmit.
* \param[in] endp related endpoint (does not take ownership).
* \param[in] codec configuration.
* \param[in] msg RTP message buffer (including RTP header).
* \returns 0 on success, -1 on ERROR. */
int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg)
{
struct msgb *msg_tf = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-TX-TRAU-frame");
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
struct osmo_trau_frame tf;
uint8_t amr_ft;
int rc;
/* Extract AMR frame type from AMR head (if AMR is used) */
if (tf_type_is_amr(endp->e1.trau_rtp_st->type))
amr_ft = (msgb_data(msg)[rtp_hdr_len + 1] >> 3) & 0xf;
else
amr_ft = 0xff;
/* Adapt TRAU frame type on codec changes */
OSMO_ASSERT(endp->e1.last_codec);
if (codec != endp->e1.last_codec || (amr_ft != 0xff && amr_ft != endp->e1.last_amr_ft)) {
endp->e1.trau_rtp_st->type =
determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, amr_ft, endp);
endp->e1.last_codec = codec;
endp->e1.last_amr_ft = amr_ft;
}
if (endp->e1.trau_rtp_st->type == OSMO_TRAU_FT_NONE)
goto skip;
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: using trau frame type for encoding: %s\n",
osmo_trau_frame_type_name(endp->e1.trau_rtp_st->type));
/* Convert from RTP to TRAU format */
msg->l2h = msgb_data(msg) + rtp_hdr_len;
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: decoding %u bytes of RTP audio to TRAU format: %s\n",
msgb_length(msg), osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
memset(&tf, 0, sizeof(tf));
tf.dir = OSMO_TRAU_DIR_DL;
rc = osmo_rtp2trau(&tf, msgb_l2(msg), msgb_l2len(msg), endp->e1.trau_rtp_st);
if (rc < 0) {
LOGPENDP(endp, DE1, LOGL_DEBUG,
"E1-I.460-TX: failed to decode from RTP payload format to TRAU format\n");
goto skip;
}
rc = osmo_trau_frame_encode(msgb_data(msg_tf), msg_tf->data_len, &tf);
if (rc < 0) {
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: failed to encode TRAU frame\n");
goto skip;
}
msgb_put(msg_tf, rc);
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg_tf),
osmo_ubit_dump(msgb_data(msg_tf),
msgb_length(msg_tf) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg_tf)));
/* Enqueue data to I.460 multiplexer */
OSMO_ASSERT(endp->e1.schan);
OSMO_ASSERT(endp->e1.trau_sync_fi);
osmo_i460_mux_enqueue(endp->e1.schan, msg_tf);
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: %u bits of audio enqued for E1 tx\n", msgb_length(msg_tf));
return 0;
skip:
rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_TX_FAIL_CTR]);
msgb_free(msg_tf);
return -1;
}

View File

@@ -1,7 +1,7 @@
/* Endpoint types */
/*
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
@@ -21,17 +21,95 @@
*
*/
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_e1.h>
#define E1_TIMESLOTS 32
#define E1_RATE_MAX 64
#define E1_OFFS_MAX 8
/* Endpoint typeset definition */
const struct mgcp_endpoint_typeset ep_typeset = {
/* Specify endpoint properties for RTP endpoint */
.rtp.max_conns = 2,
.rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
.rtp.cleanup_cb = mgcp_cleanup_rtp_bridge_cb
.rtp = {
.max_conns = 2,
.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
.cleanup_cb = mgcp_cleanup_rtp_bridge_cb,
},
/* Specify endpoint properties for E1 endpoint */
.e1 = {
.max_conns = 1,
.dispatch_rtp_cb = mgcp_dispatch_e1_bridge_cb,
.cleanup_cb = mgcp_cleanup_e1_bridge_cb,
},
};
/* Generate virtual endpoint name from given parameters */
static char *gen_virtual_epname(void *ctx, const char *domain,
unsigned int index)
{
return talloc_asprintf(ctx, "%s%x@%s",
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, index, domain);
}
/* Generate E1 endpoint name from given numeric parameters */
static char *gen_e1_epname(void *ctx, const char *domain, uint8_t trunk_nr,
uint8_t ts_nr, uint8_t ss_nr)
{
unsigned int rate;
unsigned int offset;
OSMO_ASSERT(ss_nr < sizeof(e1_rates));
rate = e1_rates[ss_nr];
offset = e1_offsets[ss_nr];
return talloc_asprintf(ctx, "%s%u/s-%u/su%u-%u@%s",
MGCP_ENDPOINT_PREFIX_E1_TRUNK, trunk_nr, ts_nr,
rate, offset, domain);
}
/*! allocate an endpoint and set default values.
* \param[in] trunk configuration.
* \param[in] name endpoint index.
* \returns endpoint on success, NULL on failure. */
struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk,
unsigned int index)
{
struct mgcp_endpoint *endp;
endp = talloc_zero(trunk->endpoints, struct mgcp_endpoint);
if (!endp)
return NULL;
INIT_LLIST_HEAD(&endp->conns);
endp->cfg = trunk->cfg;
endp->trunk = trunk;
switch (trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
endp->type = &ep_typeset.rtp;
endp->name = gen_virtual_epname(endp, trunk->cfg->domain, index);
break;
case MGCP_TRUNK_E1:
endp->type = &ep_typeset.e1;
endp->name = gen_e1_epname(endp, trunk->cfg->domain,
trunk->trunk_nr,
index / MGCP_ENDP_E1_SUBSLOTS, index % MGCP_ENDP_E1_SUBSLOTS);
break;
default:
osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n",
trunk->trunk_type, __FILE__, __LINE__);
}
return endp;
}
/*! release endpoint, all open connections are closed.
* \param[in] endp endpoint to release */
void mgcp_endp_release(struct mgcp_endpoint *endp)
@@ -52,4 +130,539 @@ void mgcp_endp_release(struct mgcp_endpoint *endp)
talloc_free(endp->local_options.codec);
endp->local_options.codec = NULL;
endp->wildcarded_req = false;
if (endp->trunk->trunk_type == MGCP_TRUNK_E1)
mgcp_e1_endp_release(endp);
}
/* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or
* "ds/e1-") and write the epname without the prefix back to the memory
* pointed at by epname. (per trunk the prefix is the same for all endpoints,
* so no ambiguity is introduced) */
static void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk)
{
size_t prefix_len;
switch (trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
if (strncmp
(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
prefix_len) == 0)
memmove(epname, epname + prefix_len,
strlen(epname) - prefix_len + 1);
return;
case MGCP_TRUNK_E1:
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
if (strncmp
(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
prefix_len) == 0)
memmove(epname, epname + prefix_len,
strlen(epname) - prefix_len + 1);
return;
default:
OSMO_ASSERT(false);
}
}
/* Check if the endpoint name contains a suffix (e.g. "@mgw") and truncate
* epname by writing a '\0' char where the suffix starts. */
static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
{
char *suffix_begin;
/* Endpoints on the virtual trunk may have a domain name that is
* followed after an @ character, this can be chopped off. All
* other supported trunk types do not have any suffixes that may
* be chopped off */
if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
suffix_begin = strchr(epname, '@');
if (!suffix_begin)
return;
*suffix_begin = '\0';
}
}
/* Convert all characters in epname to lowercase and strip trunk prefix and
* endpoint name suffix (domain name) from epname. The result is written to
* to the memory pointed at by epname_stripped. The expected size of the
* result is either equal or lower then the length of the input string
* (epname) */
static void strip_epname(char *epname_stripped, const char *epname,
const struct mgcp_trunk *trunk)
{
osmo_str_tolower_buf(epname_stripped, MGCP_ENDPOINT_MAXLEN, epname);
chop_epname_prefix(epname_stripped, trunk);
chop_epname_suffix(epname_stripped, trunk);
}
/* Go through the trunk and find a random free (no active calls) endpoint,
* this function is called when a wildcarded request is carried out, which
* means that it is up to the MGW to choose a random free endpoint. */
static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk)
{
struct mgcp_endpoint *endp;
unsigned int i;
for (i = 0; i < trunk->number_endpoints; i++) {
endp = trunk->endpoints[i];
/* A free endpoint must not serve a call already and it must
* be available. */
if (endp->callid == NULL && mgcp_endp_avail(endp))
return endp;
}
return NULL;
}
/* Find an endpoint specified by its name. If the endpoint can not be found,
* return NULL */
static struct mgcp_endpoint *find_specific_endpoint(const char *epname,
const struct mgcp_trunk *trunk)
{
char epname_stripped[MGCP_ENDPOINT_MAXLEN];
char epname_stripped_endp[MGCP_ENDPOINT_MAXLEN];
struct mgcp_endpoint *endp;
unsigned int i;
/* Strip irrelevant information from the endpoint name */
strip_epname(epname_stripped, epname, trunk);
for (i = 0; i < trunk->number_endpoints; i++) {
endp = trunk->endpoints[i];
strip_epname(epname_stripped_endp, endp->name, trunk);
if (strcmp(epname_stripped_endp, epname_stripped) == 0)
return endp;
}
return NULL;
}
/*! Find an endpoint by its name on a specified trunk.
* \param[out] cause pointer to store cause code, can be NULL.
* \param[in] epname endpoint name to lookup.
* \param[in] trunk where the endpoint is located.
* \returns endpoint or NULL if endpoint was not found. */
struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
const struct mgcp_trunk *trunk)
{
struct mgcp_endpoint *endp;
if (cause)
*cause = 0;
/* At the moment we only support a primitive ('*'-only) method of
* wildcarded endpoint searches that picks the next free endpoint on
* a trunk. */
if (strstr(epname, "*")) {
endp = find_free_endpoint(trunk);
if (endp) {
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
"(trunk:%d) found free endpoint: %s\n",
trunk->trunk_nr, endp->name);
endp->wildcarded_req = true;
return endp;
}
LOGP(DLMGCP, LOGL_ERROR,
"(trunk:%d) Not able to find a free endpoint\n",
trunk->trunk_nr);
if (cause)
*cause = -403;
return NULL;
}
/* Find an endpoint by its name (if wildcarded request is not
* applicable) */
endp = find_specific_endpoint(epname, trunk);
if (endp) {
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
"(trunk:%d) found endpoint: %s\n",
trunk->trunk_nr, endp->name);
endp->wildcarded_req = false;
return endp;
}
LOGP(DLMGCP, LOGL_ERROR,
"(trunk:%d) Not able to find specified endpoint: %s\n",
trunk->trunk_nr, epname);
if (cause)
*cause = -500;
return NULL;
}
/* Check if the domain name, which is supplied with the endpoint name
* matches the configuration. */
static int check_domain_name(const char *epname, struct mgcp_config *cfg)
{
char *domain_to_check;
domain_to_check = strstr(epname, "@");
if (!domain_to_check) {
LOGP(DLMGCP, LOGL_ERROR, "missing domain name in endpoint name \"%s\", expecting \"%s\"\n",
epname, cfg->domain);
return -EINVAL;
}
/* Accept any domain if configured as "*" */
if (!strcmp(cfg->domain, "*"))
return 0;
if (strcmp(domain_to_check+1, cfg->domain) != 0) {
LOGP(DLMGCP, LOGL_ERROR, "wrong domain name in endpoint name \"%s\", expecting \"%s\"\n",
epname, cfg->domain);
return -EINVAL;
}
return 0;
}
/*! Find an endpoint by its name, search at all trunks.
* \param[out] cause, pointer to store cause code, can be NULL.
* \param[in] epname, must contain trunk prefix.
* \param[in] cfg, mgcp configuration (trunks).
* \returns endpoint or NULL if endpoint was not found. */
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
struct mgcp_config *cfg)
{
struct mgcp_trunk *trunk;
struct mgcp_endpoint *endp;
char epname_lc[MGCP_ENDPOINT_MAXLEN];
osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
epname = epname_lc;
if (cause)
*cause = -500;
/* Identify the trunk where the endpoint is located */
trunk = mgcp_trunk_by_name(cfg, epname);
if (!trunk)
return NULL;
/* All endpoint names require a domain as suffix */
if (check_domain_name(epname, cfg))
return NULL;
/* Identify the endpoint on the trunk */
endp = mgcp_endp_by_name_trunk(cause, epname, trunk);
if (!endp) {
return NULL;
}
if (cause)
*cause = 0;
return endp;
}
/* Get the E1 timeslot number from a given E1 endpoint name
* (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
static uint8_t e1_ts_nr_from_epname(const char *epname)
{
char buf[MGCP_ENDPOINT_MAXLEN + 1];
char *save_ptr = NULL;
char *buf_ptr = buf;
char *token;
unsigned long int res = 0;
strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
while (1) {
token = strtok_r(buf_ptr, "/", &save_ptr);
buf_ptr = NULL;
if (!token)
break;
if (strncmp(token, "s-", 2) == 0) {
errno = 0;
res = strtoul(token + 2, NULL, 10);
if (errno == ERANGE || res > E1_TIMESLOTS)
return 0xff;
return (uint8_t) res;
}
}
return 0xff;
}
/* Get the E1 timeslot number from a given E1 endpoint name
* (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
static uint8_t e1_rate_from_epname(const char *epname)
{
char buf[MGCP_ENDPOINT_MAXLEN + 1];
char *save_ptr = NULL;
char *buf_ptr = buf;
char *token;
unsigned long int res = 0;
unsigned int i;
strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
while (1) {
token = strtok_r(buf_ptr, "/", &save_ptr);
buf_ptr = NULL;
if (!token)
break;
if (strncmp(token, "su", 2) == 0) {
errno = 0;
res = strtoul(token + 2, NULL, 10);
if (errno == ERANGE || res > E1_RATE_MAX)
return 0xff;
/* Make sure the rate is a valid rate */
for (i = 0; i < sizeof(e1_rates); i++) {
if (res == e1_rates[i])
return (uint8_t) res;
}
return 0xff;
}
}
return 0xff;
}
/* Get the E1 bitstream offset from a given E1 endpoint name
* (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
static uint8_t e1_offs_from_epname(const char *epname)
{
char buf[MGCP_ENDPOINT_MAXLEN + 1];
char *save_ptr = NULL;
char *buf_ptr = buf;
char *token;
unsigned long int res = 0;
strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
while (1) {
token = strtok_r(buf_ptr, "/", &save_ptr);
buf_ptr = NULL;
if (!token)
break;
if (strncmp(token, "su", 2) == 0) {
token = strstr(token, "-");
if (!token)
return 0xff;
token += 1;
errno = 0;
res = strtoul(token, NULL, 10);
if (errno == ERANGE || res > E1_OFFS_MAX)
return 0xff;
return (uint8_t) res;
}
}
return 0xff;
}
/* Get the E1 subslot number (internal) from a given E1 endpoint name
* (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
static uint8_t e1_ss_nr_from_epname(const char *epname)
{
uint8_t rate;
uint8_t offs;
unsigned int i;
rate = e1_rate_from_epname(epname);
offs = e1_offs_from_epname(epname);
osmo_static_assert(sizeof(e1_rates) == sizeof(e1_offsets), e1_rates_e1_offsets_size);
for (i = 0; i < sizeof(e1_rates); i++) {
if ((e1_rates[i] == rate) && (e1_offsets[i] == offs))
return i;
}
return 0xff;
}
/* Check if the selected E1 endpoint is avalable, which means that none of
* the overlapping endpoints are currently serving a call. (if the system
* is properly configured such a situation should never ocurr!) */
static bool endp_avail_e1(struct mgcp_endpoint *endp)
{
/* The following map shows the overlapping of the subslots and their
* respective rates. The numbers on the right running from top to bottom
* are the bit offsets in the whole 64k timeslot. The numbers inside the
* boxes symbolize the internal subslot number (array index) and the
* rate in the form: i:r where i is the subslot number and r the
* respective rate.
*
* +--------+--------+--------+--------+ 0
* | | | | 7:8k |
* | | + 3:16k +--------+ 1
* | | | | 8:8k |
* | | 1:32k +--------+--------+ 2
* | | | | 9:8k |
* | | + 4:16k +--------+ 3
* | | | | 10:8k |
* | 0:64k +--------+--------+--------+ 4
* | | | | 11:8k |
* | | + 5:16k +--------+ 5
* | | | | 12:8k |
* | | 2:32k +--------+--------+ 6
* | | | | 13:8k |
* | | + 6:16k +--------+ 7
* | | | | 14:8k |
* +--------+--------+--------+--------+ 8
*
* The following array contains tables with the subslot numbers that must be
* unused for each subslot. During this test we do not have to check the
* endpoint we need to verify, only the overlaps need to be checked. This is
* also the reason why the related subslot number is missing from each each
* line. */
const int8_t interlock_tab[MGCP_ENDP_E1_SUBSLOTS][15] = {
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1 },
{ 0, 3, 4, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 5, 6, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 1, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 1, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 2, 11, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 2, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } };
const int8_t *interlock;
unsigned int i;
uint8_t ts_nr = 0;
uint8_t ss_nr = 0;
char *epname_check;
struct mgcp_endpoint *endp_check;
bool available = true;
/* This function must only be used with E1 type endpoints! */
OSMO_ASSERT(endp->trunk->trunk_type == MGCP_TRUNK_E1);
ts_nr = e1_ts_nr_from_epname(endp->name);
ss_nr = e1_ss_nr_from_epname(endp->name);
if (ts_nr == 0xff || ss_nr == 0xff) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"cannot check endpoint availability, endpoint name not parseable!\n");
return false;
}
interlock = interlock_tab[ss_nr];
for (i = 0; i < sizeof(interlock_tab[0]); i++) {
/* Detect row end */
if (interlock[i] == -1)
break;
/* Pick overlapping endpoint to check */
epname_check = gen_e1_epname(endp, endp->trunk->cfg->domain,
endp->trunk->trunk_nr, ts_nr,
interlock[i]);
endp_check = find_specific_endpoint(epname_check, endp->trunk);
if (!endp_check) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"cannot check endpoint availability, overlapping endpoint:%s not found!\n",
epname_check);
talloc_free(epname_check);
continue;
}
talloc_free(epname_check);
/* Check if overlapping endpoint currently serves another call
* (This is an exceptional situation, that should not occur
* in a properly configured environment!) */
if (endp_check->callid) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"endpoint unavailable - overlapping endpoint:%s already serves a call!\n",
endp_check->name);
available = false;
}
}
return available;
}
/*! check if an endpoint is available for any kind of operation.
* \param[in] endp endpoint to check.
* \returns true if endpoint is avalable, false it is blocked for any reason. */
bool mgcp_endp_avail(struct mgcp_endpoint *endp)
{
switch (endp->trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
/* There are no obstacles that may render a virtual trunk
* endpoint unusable, so virtual trunk endpoints are always
* available */
return true;
case MGCP_TRUNK_E1:
return endp_avail_e1(endp);
default:
OSMO_ASSERT(false);
}
return false;
}
/*! claim endpoint, sets callid and activates endpoint, should be called at the
* beginning of the CRCX procedure when it is clear that a new call should be
* created.
* \param[in] endp endpoint to claim.
* \param[in] callid that is assingned to this endpoint. */
int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid)
{
int rc = 0;
uint8_t ts;
uint8_t ss;
uint8_t offs;
/* TODO: Make this function more intelligent, it should run the
* call id checks we currently have in protocol.c directly here. */
/* Set the callid, creation of another connection will only be possible
* when the callid matches up. (Connections are distinguished by their
* connection ids) */
endp->callid = talloc_strdup(endp, callid);
OSMO_ASSERT(endp->callid);
/* Allocate resources */
switch (endp->trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
/* No additional initaliziation required here, virtual
* endpoints will open/close network sockets themselves
* on demand. */
break;
case MGCP_TRUNK_E1:
ts = e1_ts_nr_from_epname(endp->name);
ss = e1_ss_nr_from_epname(endp->name);
offs = e1_offs_from_epname(endp->name);
OSMO_ASSERT(ts != 0xFF);
OSMO_ASSERT(ts != 0);
OSMO_ASSERT(ss != 0xFF);
OSMO_ASSERT(offs != 0xFF);
rc = mgcp_e1_endp_equip(endp, ts, ss, offs);
break;
default:
OSMO_ASSERT(false);
}
/* Make sure the endpoint is released when claiming the endpoint
* failes. */
if (rc < 0)
mgcp_endp_release(endp);
return rc;
}
/*! update endpoint, updates internal endpoint specific data, should be
* after when MDCX or CRCX has been executed successuflly.
* \param[in] endp endpoint to update. */
void mgcp_endp_update(struct mgcp_endpoint *endp)
{
/* Allocate resources */
switch (endp->trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
/* No updating initaliziation required for virtual endpoints. */
break;
case MGCP_TRUNK_E1:
mgcp_e1_endp_update(endp);
break;
default:
OSMO_ASSERT(false);
}
}

View File

@@ -24,7 +24,9 @@
#include <limits.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_conn.h>
@@ -129,166 +131,6 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
return ret;
}
/* We have a null terminated string with the endpoint name here. We only
* support two kinds. Simple ones as seen on the BSC level and the ones
* seen on the trunk side. (helper function for find_endpoint()) */
static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
const char *mgcp)
{
char *rest = NULL;
struct mgcp_trunk_config *tcfg;
int trunk, endp;
struct mgcp_endpoint *endp_ptr;
trunk = strtoul(mgcp + 6, &rest, 10);
if (rest == NULL || rest[0] != '/' || trunk < 1) {
LOGP(DLMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
return NULL;
}
endp = strtoul(rest + 1, &rest, 10);
if (rest == NULL || rest[0] != '@') {
LOGP(DLMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
return NULL;
}
/* signalling is on timeslot 1 */
if (endp == 1)
return NULL;
tcfg = mgcp_trunk_num(cfg, trunk);
if (!tcfg) {
LOGP(DLMGCP, LOGL_ERROR, "The trunk %d is not declared.\n",
trunk);
return NULL;
}
if (!tcfg->endpoints) {
LOGP(DLMGCP, LOGL_ERROR,
"Endpoints of trunk %d not allocated.\n", trunk);
return NULL;
}
if (endp < 1 || endp >= tcfg->number_endpoints) {
LOGP(DLMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n",
mgcp);
return NULL;
}
endp_ptr = &tcfg->endpoints[endp];
endp_ptr->wildcarded_req = false;
return endp_ptr;
}
/* Find an endpoint that is not in use. Do this by going through the endpoint
* array, check the callid. A callid nullpointer indicates that the endpoint
* is free */
static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints,
unsigned int number_endpoints)
{
struct mgcp_endpoint *endp;
unsigned int i;
for (i = 0; i < number_endpoints; i++) {
if (endpoints[i].callid == NULL) {
endp = &endpoints[i];
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
"found free endpoint\n");
endp->wildcarded_req = true;
return endp;
}
}
LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint\n");
return NULL;
}
/* Check if the domain name, which is supplied with the endpoint name
* matches the configuration. */
static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
{
char *domain_to_check;
domain_to_check = strstr(mgcp, "@");
if (!domain_to_check)
return -EINVAL;
/* Accept any domain if configured as "*" */
if (!strcmp(cfg->domain, "*"))
return 0;
if (strcmp(domain_to_check+1, cfg->domain) != 0) {
LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s', expecting '%s'\n", mgcp, cfg->domain);
return -EINVAL;
}
return 0;
}
/* Search the endpoint pool for the endpoint that had been selected via the
* MGCP message (helper function for mgcp_analyze_header()) */
static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
const char *mgcp,
int *cause)
{
char *endptr = NULL;
unsigned int gw = INT_MAX;
const char *endpoint_number_str;
struct mgcp_endpoint *endp;
*cause = 0;
/* Check if the domainname in the request is correct */
if (check_domain_name(cfg, mgcp)) {
*cause = -500;
return NULL;
}
/* Check if the E1 trunk is requested */
if (strncmp(mgcp, "ds/e1", 5) == 0) {
endp = find_e1_endpoint(cfg, mgcp);
if (!endp)
*cause = -500;
return endp;
}
/* Check if the virtual trunk is addressed (new, correct way with prefix) */
if (strncmp
(mgcp, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK)) == 0) {
endpoint_number_str =
mgcp + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
if (endpoint_number_str[0] == '*') {
endp = find_free_endpoint(cfg->trunk.endpoints,
cfg->trunk.number_endpoints);
if (!endp)
*cause = -403;
return endp;
}
gw = strtoul(endpoint_number_str, &endptr, 16);
if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
endp = &cfg->trunk.endpoints[gw];
endp->wildcarded_req = false;
return endp;
}
}
/* Deprecated method without prefix */
LOGP(DLMGCP, LOGL_NOTICE,
"Addressing virtual trunk without prefix (deprecated), please use %s: '%s'\n",
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, mgcp);
gw = strtoul(mgcp, &endptr, 16);
if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
endp = &cfg->trunk.endpoints[gw];
endp->wildcarded_req = false;
return endp;
}
LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
*cause = -500;
return NULL;
}
/*! Analyze and parse the the hader of an MGCP messeage string.
* \param[out] pdata caller provided memory to store the parsing results
* \param[in] data mgcp message string
@@ -316,7 +158,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
pdata->trans = elem;
break;
case 1:
pdata->endp = find_endpoint(pdata->cfg, elem, &cause);
pdata->endp = mgcp_endp_by_name(&cause, elem, pdata->cfg);
if (!pdata->endp) {
LOGP(DLMGCP, LOGL_ERROR,
"Unable to find Endpoint `%s'\n", elem);
@@ -390,8 +232,8 @@ int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
const size_t line_len = strlen(line);
if (line[0] != '\0' && line_len < 2) {
LOGP(DLMGCP, LOGL_ERROR,
"Wrong MGCP option format: '%s' on 0x%x\n",
line, ENDPOINT_NUMBER(endp));
"Wrong MGCP option format: '%s' on %s\n",
line, endp->name);
return 0;
}

View File

@@ -37,29 +37,49 @@
#include <osmocom/netif/amr.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_network.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_stat.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/debug.h>
#include <osmocom/codec/codec.h>
#include <osmocom/mgcp/mgcp_e1.h>
#define RTP_SEQ_MOD (1 << 16)
#define RTP_MAX_DROPOUT 3000
#define RTP_MAX_MISORDER 100
#define RTP_BUF_SIZE 4096
enum {
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,
int id, int inc)
{
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
struct rate_ctr_group *mgw_stats = endp->trunk->ratectr.all_rtp_conn_stats;
/* add to both the per-connection and the global stats */
rate_ctr_add(&conn_stats->ctr[id], inc);
rate_ctr_add(&mgw_stats->ctr[id], inc);
}
static 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);
/*! 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
* \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.
*
* The local bind IP-address is automatically selected by probing the
* IP-Address of the interface that is pointing towards the remote IP-Address,
@@ -128,12 +148,12 @@ static uint32_t get_current_ts(unsigned codec_rate)
}
/*! 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 */
* \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 in_addr *addr, int port, char *buf, int len)
{
struct sockaddr_in out;
@@ -150,9 +170,9 @@ int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len)
}
/*! send RTP dummy packet (to keep NAT connection open).
* \param[in] endp mcgp endpoint that holds the RTP connection
* \param[in] conn associated RTP connection
* \returns bytes sent, -1 on error */
* \param[in] endp mcgp endpoint that holds the RTP connection.
* \param[in] conn associated RTP connection.
* \returns bytes sent, -1 on error. */
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
static char buf[] = { MGCP_DUMMY_LOAD };
@@ -171,7 +191,7 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
if (rc == -1)
goto failed;
if (endp->tcfg->omit_rtcp)
if (endp->trunk->omit_rtcp)
return rc;
was_rtcp = 1;
@@ -378,12 +398,12 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp,
}
/*! dummy callback to disable transcoding (see also cfg->rtp_processing_cb).
* \param[in] associated endpoint
* \param[in] destination RTP end
* \param[in,out] pointer to buffer with voice data
* \param[in] voice data length
* \param[in] maximum size of caller provided voice data buffer
* \returns ignores input parameters, return always 0 */
* \param[in] associated endpoint.
* \param[in] destination RTP end.
* \param[in,out] pointer to buffer with voice data.
* \param[in] voice data length.
* \param[in] maximum size of caller provided voice data buffer.
* \returns ignores input parameters, return always 0. */
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size)
@@ -393,10 +413,10 @@ int mgcp_rtp_processing_default(struct mgcp_endpoint *endp,
}
/*! dummy callback to disable transcoding (see also cfg->setup_rtp_processing_cb).
* \param[in] associated endpoint
* \param[in] destination RTP connnection
* \param[in] source RTP connection
* \returns ignores input parameters, return always 0 */
* \param[in] associated endpoint.
* \param[in] destination RTP connnection.
* \param[in] source RTP connection.
* \returns ignores input parameters, return always 0. */
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn_dst,
struct mgcp_conn_rtp *conn_src)
@@ -469,16 +489,19 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp,
* Patch the payload type of an RTP packet so that it uses the payload type
* that is valid for the destination connection (conn_dst) */
static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
struct mgcp_conn_rtp *conn_dst, char *data, int len)
struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
{
struct rtp_hdr *rtp_hdr;
uint8_t pt_in;
int pt_out;
if (len < sizeof(struct rtp_hdr))
if (msgb_length(msg) < sizeof(struct rtp_hdr)) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTP packet too short (%u < %zu)\n",
msgb_length(msg), sizeof(struct rtp_hdr));
return -EINVAL;
}
rtp_hdr = (struct rtp_hdr *)data;
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
pt_in = rtp_hdr->payload_type;
pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in);
@@ -498,7 +521,7 @@ static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
struct mgcp_rtp_state *state,
struct mgcp_rtp_end *rtp_end,
struct sockaddr_in *addr, char *data, int len)
struct sockaddr_in *addr, struct msgb *msg)
{
uint32_t arrival_time;
int32_t transit;
@@ -506,11 +529,12 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
uint32_t timestamp, ssrc;
struct rtp_hdr *rtp_hdr;
int payload = rtp_end->codec->payload_type;
unsigned int len = msgb_length(msg);
if (len < sizeof(*rtp_hdr))
return;
rtp_hdr = (struct rtp_hdr *)data;
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);
@@ -632,9 +656,8 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
return;
#if 0
DEBUGP(DRTP,
"endpoint:0x%x payload hdr payload %u -> endp payload %u\n",
ENDPOINT_NUMBER(endp), rtp_hdr->payload_type, payload);
LOGPENDP(endp, DRTP, LOGL_DEBUG, "payload hdr payload %u -> endp payload %u\n",
rtp_hdr->payload_type, payload);
rtp_hdr->payload_type = payload;
#endif
}
@@ -644,32 +667,26 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
* function is used to convert between RFC 5993 and TS 101318, which we normally
* use.
* Return 0 on sucess, negative on errors like invalid data length. */
static int rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
static int rfc5993_hr_convert(struct mgcp_endpoint *endp, struct msgb *msg)
{
/* NOTE: *data has an overall length of RTP_BUF_SIZE, so there is
* plenty of space available to store the slightly larger, converted
* data */
struct rtp_hdr *rtp_hdr;
if (*len < sizeof(struct rtp_hdr)) {
if (msgb_length(msg) < sizeof(struct rtp_hdr)) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n",
*len, sizeof(struct rtp_hdr));
msgb_length(msg), sizeof(struct rtp_hdr));
return -EINVAL;
}
rtp_hdr = (struct rtp_hdr *)data;
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
if (*len == GSM_HR_BYTES + sizeof(struct rtp_hdr)) {
if (msgb_length(msg) == GSM_HR_BYTES + sizeof(struct rtp_hdr)) {
/* TS 101318 encoding => RFC 5993 encoding */
msgb_put(msg, 1);
memmove(rtp_hdr->data + 1, rtp_hdr->data, GSM_HR_BYTES);
rtp_hdr->data[0] = 0x00;
(*len) += 1;
} else if (*len == GSM_HR_BYTES + sizeof(struct rtp_hdr) + 1) {
} else if (msgb_length(msg) == GSM_HR_BYTES + sizeof(struct rtp_hdr) + 1) {
/* RFC 5993 encoding => TS 101318 encoding */
memmove(rtp_hdr->data, rtp_hdr->data + 1, GSM_HR_BYTES);
(*len) -= 1;
msgb_trim(msg, msgb_length(msg) - 1);
} else {
/* It is possible that multiple payloads occur in one RTP
* packet. This is not supported yet. */
@@ -684,25 +701,24 @@ static int rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
* 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, char *data, int *len,
static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
bool target_is_oa)
{
/* NOTE: *data has an overall length of RTP_BUF_SIZE, so there is
/* NOTE: the msgb has an allocated length of RTP_BUF_SIZE, so there is
* plenty of space available to store the slightly larger, converted
* data */
struct rtp_hdr *rtp_hdr;
unsigned int payload_len;
int rc;
if (*len < sizeof(struct rtp_hdr)) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n", *len, sizeof(struct rtp_hdr));
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));
return -EINVAL;
}
rtp_hdr = (struct rtp_hdr *)data;
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
payload_len = *len - sizeof(struct rtp_hdr);
payload_len = msgb_length(msg) - sizeof(struct rtp_hdr);
if (osmo_amr_is_oa(rtp_hdr->data, payload_len)) {
if (!target_is_oa)
@@ -730,9 +746,7 @@ static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, char *data, int *len,
return -EINVAL;
}
*len = rc + sizeof(struct rtp_hdr);
return 0;
return msgb_trim(msg, rc + sizeof(struct rtp_hdr));
}
/* Check if a conversion between octet-aligned and bandwith-efficient mode is
@@ -770,15 +784,14 @@ 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, const char *buf,
int len)
static void forward_data(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
{
int rc;
if (!tap->enabled)
return;
rc = sendto(fd, buf, len, 0, (struct sockaddr *)&tap->forward,
rc = sendto(fd, msgb_data(msg), msgb_length(msg), 0, (struct sockaddr *)&tap->forward,
sizeof(tap->forward));
if (rc < 0)
@@ -786,28 +799,46 @@ static void forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf,
"Forwarding tapped (debug) voice data failed.\n");
}
/* Generate an RTP header if it is missing */
static void gen_rtp_header(struct msgb *msg, struct mgcp_rtp_end *rtp_end,
struct mgcp_rtp_state *state)
{
struct rtp_hdr *hdr = (struct rtp_hdr *)msgb_data(msg);
if (hdr->version > 0)
return;
hdr->version = 2;
hdr->payload_type = rtp_end->codec->payload_type;
hdr->timestamp = osmo_htonl(get_current_ts(rtp_end->codec->rate));
hdr->sequence = osmo_htons(state->alt_rtp_tx_sequence);
hdr->ssrc = state->alt_rtp_tx_ssrc;
}
/*! Send RTP/RTCP data to a specified destination connection.
* \param[in] endp associated endpoint (for configuration, logging)
* \param[in] is_rtp flag to specify if the packet is of type RTP or RTCP
* \param[in] spoofed source address (set to NULL to disable)
* \param[in] buf buffer that contains the RTP/RTCP data
* \param[in] len length of the buffer that contains the RTP/RTCP data
* \param[in] conn_src associated source connection
* \param[in] conn_dst associated destination connection
* \returns 0 on success, -1 on ERROR */
* \param[in] endp associated endpoint (for configuration, logging).
* \param[in] is_rtp flag to specify if the packet is of type RTP or RTCP.
* \param[in] spoofed source address (set to NULL to disable).
* \param[in] buf buffer that contains the RTP/RTCP data.
* \param[in] len length of the buffer that contains the RTP/RTCP data.
* \param[in] conn_src associated source connection.
* \param[in] conn_dst associated destination connection.
* \returns 0 on success, -1 on ERROR. */
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
char *buf, int len, struct mgcp_conn_rtp *conn_src,
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_trunk_config *tcfg = endp->tcfg;
struct mgcp_trunk *trunk = endp->trunk;
struct mgcp_rtp_end *rtp_end;
struct mgcp_rtp_state *rtp_state;
char *dest_name;
int rc;
int len;
OSMO_ASSERT(conn_src);
OSMO_ASSERT(conn_dst);
@@ -819,7 +850,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
}
LOGPENDP(endp, DRTP, LOGL_DEBUG, "loop:%d, mode:%d%s\n",
tcfg->audio_loop, conn_src->conn->mode,
trunk->audio_loop, conn_src->conn->mode,
conn_src->conn->mode == MGCP_CONN_LOOPBACK ? " (loopback)" : "");
/* FIXME: It is legal that the payload type on the egress connection is
@@ -831,7 +862,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
* should not occur if transcoding is consequently avoided. Until
* we have transcoding support in osmo-mgw we can not resolve this. */
if (is_rtp) {
rc = mgcp_patch_pt(conn_src, conn_dst, buf, len);
rc = mgcp_patch_pt(conn_src, conn_dst, msg);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"can not patch PT because no suitable egress codec was found.\n");
@@ -844,8 +875,13 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
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) {
rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_DROPPED_PACKETS_CTR]);
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_DROPPED_PACKETS_CTR);
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"output disabled, drop to %s %s "
"rtp_port:%u rtcp_port:%u\n",
@@ -856,21 +892,26 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
} else if (is_rtp) {
int cont;
int nbytes = 0;
int buflen = len;
int buflen = msgb_length(msg);
/* Make sure we have a valid RTP header, in cases where no RTP
* header is present, we will generate one. */
gen_rtp_header(msg, rtp_end, rtp_state);
do {
/* Run transcoder */
cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
buf, &buflen,
(char*)msgb_data(msg), &buflen,
RTP_BUF_SIZE);
if (cont < 0)
break;
if (addr)
mgcp_patch_and_count(endp, rtp_state, rtp_end,
addr, buf, buflen);
addr, msg);
if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
rc = amr_oa_bwe_convert(endp, buf, &buflen,
rc = amr_oa_bwe_convert(endp, msg,
conn_dst->end.codec->param.amr_octet_aligned);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
@@ -881,7 +922,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
else if (rtp_end->rfc5993_hr_convert
&& strcmp(conn_src->end.codec->subtype_name,
"GSM-HR-08") == 0) {
rc = rfc5993_hr_convert(endp, buf, &buflen);
rc = rfc5993_hr_convert(endp, msg);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
break;
@@ -897,7 +938,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
/* Forward a copy of the RTP data to a debug ip/port */
forward_data(rtp_end->rtp.fd, &conn_src->tap_out,
buf, buflen);
msg);
/* FIXME: HACK HACK HACK. See OS#2459.
* The ip.access nano3G needs the first RTP payload's first two bytes to read hex
@@ -906,7 +947,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
*/
if (!rtp_state->patched_first_rtp_payload
&& conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
uint8_t *data = (uint8_t *) & buf[12];
uint8_t *data = msgb_data(msg) + 12;
if (data[0] == 0xe0) {
data[0] = 0xe4;
data[1] = 0x00;
@@ -917,21 +958,21 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
}
}
len = mgcp_udp_send(rtp_end->rtp.fd,
&rtp_end->addr,
rtp_end->rtp_port, buf, buflen);
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port,
(char*)msgb_data(msg), msgb_length(msg));
if (len <= 0)
return len;
rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len);
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
rtp_state->alt_rtp_tx_sequence++;
nbytes += len;
buflen = cont;
} while (buflen > 0);
return nbytes;
} else if (!tcfg->omit_rtcp) {
} else if (!trunk->omit_rtcp) {
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"send to %s %s rtp_port:%u rtcp_port:%u\n",
dest_name, inet_ntoa(rtp_end->addr),
@@ -940,10 +981,11 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
len = mgcp_udp_send(rtp_end->rtcp.fd,
&rtp_end->addr,
rtp_end->rtcp_port, buf, len);
rtp_end->rtcp_port, (char*)msgb_data(msg), msgb_length(msg));
rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len);
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
rtp_state->alt_rtp_tx_sequence++;
return len;
}
@@ -951,45 +993,6 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
return 0;
}
/* Helper function for mgcp_recv(),
Receive one RTP Packet + Originating address from file descriptor */
static int receive_from(struct mgcp_endpoint *endp, int fd,
struct sockaddr_in *addr, char *buf, int bufsize)
{
int rc;
socklen_t slen = sizeof(*addr);
struct sockaddr_in addr_sink;
char buf_sink[RTP_BUF_SIZE];
bool tossed = false;
if (!addr)
addr = &addr_sink;
if (!buf) {
tossed = true;
buf = buf_sink;
bufsize = sizeof(buf_sink);
}
rc = recvfrom(fd, buf, bufsize, 0, (struct sockaddr *)addr, &slen);
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"receiving %u bytes length packet from %s:%u ...\n",
rc, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"failed to receive packet, errno: %d/%s\n",
errno, strerror(errno));
return -1;
}
if (tossed) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "packet tossed\n");
}
return rc;
}
/* Check if the origin (addr) matches the address/port data of the RTP
* connections. */
static int check_rtp_origin(struct mgcp_conn_rtp *conn,
@@ -1082,7 +1085,7 @@ static int check_rtp_destin(struct mgcp_conn_rtp *conn)
/* Do some basic checks to make sure that the RTCP packets we are going to
* process are not complete garbage */
static int check_rtcp(char *buf, unsigned int buf_size)
static int check_rtcp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
{
struct rtcp_hdr *hdr;
unsigned int len;
@@ -1090,33 +1093,43 @@ static int check_rtcp(char *buf, unsigned int buf_size)
/* RTPC packets that are just a header without data do not make
* any sense. */
if (buf_size < sizeof(struct rtcp_hdr))
if (msgb_length(msg) < sizeof(struct rtcp_hdr)) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP packet too short (%u < %zu)\n",
msgb_length(msg), sizeof(struct rtcp_hdr));
return -EINVAL;
}
/* Make sure that the length of the received packet does not exceed
* the available buffer size */
hdr = (struct rtcp_hdr *)buf;
hdr = (struct rtcp_hdr *)msgb_data(msg);
len = (osmo_ntohs(hdr->length) + 1) * 4;
if (len > buf_size)
if (len > msgb_length(msg)) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP header length exceeds packet size (%u > %u)\n",
len, msgb_length(msg));
return -EINVAL;
}
/* Make sure we accept only packets that have a proper packet type set
* See also: http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
type = hdr->type;
if ((type < 192 || type > 195) && (type < 200 || type > 213))
if ((type < 192 || type > 195) && (type < 200 || type > 213)) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP header: invalid type: %u\n", type);
return -EINVAL;
}
return 0;
}
/* Do some basic checks to make sure that the RTP packets we are going to
* process are not complete garbage */
static int check_rtp(char *buf, unsigned int buf_size)
static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
{
/* RTP packets that are just a header without data do not make
* any sense. */
if (buf_size < sizeof(struct rtp_hdr))
return -EINVAL;
size_t min_size = sizeof(struct rtp_hdr);
if (msgb_length(msg) < min_size) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTP packet too short (%u < %zu)\n",
msgb_length(msg), min_size);
return -1;
}
/* FIXME: Add more checks, the reason why we do not check more than
* the length is because we currently handle IUUP packets as RTP
@@ -1127,86 +1140,15 @@ static int check_rtp(char *buf, unsigned int buf_size)
return 0;
}
/* Receive RTP data from a specified source connection and dispatch it to a
* destination connection. */
static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
unsigned int buf_size, struct osmo_fd *fd)
{
struct mgcp_endpoint *endp;
struct mgcp_conn_rtp *conn;
struct mgcp_trunk_config *tcfg;
int rc;
conn = (struct mgcp_conn_rtp*) fd->data;
endp = conn->conn->endp;
tcfg = endp->tcfg;
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "receiving RTP/RTCP packet...\n");
rc = receive_from(endp, fd->fd, addr, buf, buf_size);
if (rc <= 0)
return -1;
/* FIXME: The way how we detect the protocol looks odd. We should look
* into the packet header. Also we should introduce a packet type
* MGCP_PROTO_IUUP because currently we handle IUUP packets like RTP
* packets which is problematic. */
*proto = fd == &conn->end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
if (*proto == MGCP_PROTO_RTP) {
if (check_rtp(buf, rc) < 0) {
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
"invalid RTP packet received -- packet tossed\n");
return -1;
}
} else if (*proto == MGCP_PROTO_RTCP) {
if (check_rtcp(buf, rc) < 0) {
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
"invalid RTCP packet received -- packet tossed\n");
return -1;
}
}
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "");
LOGPC(DRTP, LOGL_DEBUG, "receiving from %s %s %d\n",
conn->conn->name, inet_ntoa(addr->sin_addr),
ntohs(addr->sin_port));
LOGPENDP(endp, DRTP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn->conn));
/* Check if the origin of the RTP packet seems plausible */
if (tcfg->rtp_accept_all == 0) {
if (check_rtp_origin(conn, addr) != 0)
return -1;
}
/* Filter out dummy message */
if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) {
LOGPCONN(conn->conn, DRTP, LOGL_NOTICE,
"dummy message received\n");
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
"packet tossed\n");
return 0;
}
/* Increment RX statistics */
rate_ctr_inc(&conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]);
rate_ctr_add(&conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], rc);
/* Forward a copy of the RTP data to a debug ip/port */
forward_data(fd->fd, &conn->tap_in, buf, rc);
return rc;
}
/* Send RTP data. Possible options are standard RTP packet
* transmission or trsmission via an osmux connection */
static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
unsigned int buf_size,
struct mgcp_conn_rtp *conn_src,
struct mgcp_conn_rtp *conn_dst)
static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
{
struct mgcp_endpoint *endp;
endp = conn_src->conn->endp;
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
enum rtp_proto proto = mc->proto;
struct mgcp_conn_rtp *conn_src = mc->conn_src;
struct sockaddr_in *from_addr = mc->from_addr;
struct mgcp_endpoint *endp = conn_src->conn->endp;
LOGPENDP(endp, DRTP, LOGL_DEBUG, "destin conn:%s\n",
mgcp_conn_dump(conn_dst->conn));
@@ -1225,13 +1167,13 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
"endpoint type is MGCP_RTP_DEFAULT, "
"using mgcp_send() to forward data directly\n");
return mgcp_send(endp, proto == MGCP_PROTO_RTP,
addr, buf, buf_size, conn_src, conn_dst);
from_addr, msg, conn_src, conn_dst);
case MGCP_OSMUX_BSC_NAT:
case MGCP_OSMUX_BSC:
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"endpoint type is MGCP_OSMUX_BSC_NAT, "
"using osmux_xfrm_to_osmux() to forward data through OSMUX\n");
return osmux_xfrm_to_osmux(buf, buf_size, conn_dst);
return osmux_xfrm_to_osmux((char*)msgb_data(msg), msgb_length(msg), conn_dst);
}
/* If the data has not been handled/forwarded until here, it will
@@ -1243,19 +1185,22 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
}
/*! 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 */
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
unsigned int buf_size, struct mgcp_conn *conn)
* \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. */
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
{
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
struct mgcp_conn_rtp *conn_src = mc->conn_src;
struct mgcp_conn *conn = conn_src->conn;
struct mgcp_conn *conn_dst;
struct sockaddr_in *from_addr = mc->from_addr;
/*! NOTE: This callback function implements the endpoint specific
* dispatch bahviour of an rtp bridge/proxy endpoint. It is assumed
* dispatch behaviour of an rtp bridge/proxy endpoint. It is assumed
* that the endpoint will hold only two connections. This premise
* is used to determine the opposite connection (it is always the
* connection that is not the originating connection). Once the
@@ -1271,11 +1216,10 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
* 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) {
conn->u.rtp.end.addr = addr->sin_addr;
conn->u.rtp.end.rtp_port = addr->sin_port;
conn->u.rtp.end.addr = from_addr->sin_addr;
conn->u.rtp.end.rtp_port = from_addr->sin_port;
}
return mgcp_send_rtp(proto, addr, buf,
buf_size, &conn->u.rtp, &conn->u.rtp);
return mgcp_send_rtp(conn_src, msg);
}
/* Find a destination connection. */
@@ -1294,8 +1238,8 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
/* There is no destination conn, stop here */
if (!conn_dst) {
LOGPCONN(conn, DRTP, LOGL_ERROR,
"unable to find destination conn\n");
LOGPCONN(conn, DRTP, LOGL_DEBUG,
"no connection to forward an incoming RTP packet to\n");
return -1;
}
@@ -1307,15 +1251,44 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
}
/* Dispatch RTP packet to destination RTP connection */
return mgcp_send_rtp(proto, addr, buf,
buf_size, &conn->u.rtp, &conn_dst->u.rtp);
return mgcp_send_rtp(&conn_dst->u.rtp, msg);
}
/*! dispatch incoming RTP packet to E1 subslot, handle RTCP packets locally.
* \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. */
int mgcp_dispatch_e1_bridge_cb(struct msgb *msg)
{
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
struct mgcp_conn_rtp *conn_src = mc->conn_src;
struct mgcp_conn *conn = conn_src->conn;
struct sockaddr_in *from_addr = mc->from_addr;
/* Check if the connection is in loopback mode, if yes, just send the
* incoming data back to the origin */
if (conn->mode == MGCP_CONN_LOOPBACK) {
/* When we are in loopback mode, we loop back all incoming
* 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) {
conn->u.rtp.end.addr = from_addr->sin_addr;
conn->u.rtp.end.rtp_port = from_addr->sin_port;
}
return mgcp_send_rtp(conn_src, msg);
}
/* Forward to E1 */
return mgcp_e1_send_rtp(conn->endp, conn->u.rtp.end.codec, msg);
}
/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
* \param[in] endp Endpoint on which the connection resides.
* \param[in] conn Connection that is about to be removed (ignored).
* \returns 0 on success, -1 on ERROR. */
* \param[in] conn Connection that is about to be removed (ignored). */
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
{
struct mgcp_conn *conn_cleanup;
@@ -1331,6 +1304,21 @@ void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *co
}
}
/*! cleanup an endpoint when a connection on an E1 endpoint is removed.
* \param[in] endp Endpoint on which the connection resides.
* \param[in] conn Connection that is about to be removed (ignored). */
void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
{
/* Cleanup tasks for E1 are the same as for regular endpoint. The
* shut down of the E1 part is handled separately. */
mgcp_cleanup_rtp_bridge_cb(endp, conn);
}
static bool is_dummy_msg(enum rtp_proto proto, struct msgb *msg)
{
return msgb_length(msg) == 1 && msgb_data(msg)[0] == MGCP_DUMMY_LOAD;
}
/* Handle incoming RTP data from NET */
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
{
@@ -1344,23 +1332,83 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
struct mgcp_conn_rtp *conn_src;
struct mgcp_endpoint *endp;
struct sockaddr_in addr;
char buf[RTP_BUF_SIZE];
int proto;
int len;
socklen_t slen = sizeof(addr);
int ret;
enum rtp_proto proto;
struct osmo_rtp_msg_ctx *mc;
struct msgb *msg = msgb_alloc(RTP_BUF_SIZE, "RTP-rx");
int rc;
conn_src = (struct mgcp_conn_rtp *)fd->data;
OSMO_ASSERT(conn_src);
endp = conn_src->conn->endp;
OSMO_ASSERT(endp);
LOGPENDP(endp, DRTP, LOGL_DEBUG, "source conn:%s\n",
mgcp_conn_dump(conn_src->conn));
proto = (fd == &conn_src->end.rtp)? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
/* Receive packet */
len = mgcp_recv(&proto, &addr, buf, sizeof(buf), fd);
if (len < 0)
return -1;
ret = recvfrom(fd->fd, msgb_data(msg), msg->data_len, 0, (struct sockaddr *)&addr, &slen);
if (ret <= 0) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "recvfrom error: %s\n", strerror(errno));
rc = -1;
goto out;
}
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",
msgb_length(msg), inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
if ((proto == MGCP_PROTO_RTP && check_rtp(conn_src, msg))
|| (proto == MGCP_PROTO_RTCP && check_rtcp(conn_src, msg))) {
/* Logging happened in the two check_ functions */
rc = -1;
goto out;
}
if (is_dummy_msg(proto, msg)) {
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx dummy packet (dropped)\n");
rc = 0;
goto out;
}
/* Since the msgb remains owned and freed by this function, the msg ctx data struct can just be on the stack and
* needs not be allocated with the msgb. */
mc = OSMO_RTP_MSG_CTX(msg);
*mc = (struct osmo_rtp_msg_ctx){
.proto = proto,
.conn_src = conn_src,
.from_addr = &addr,
};
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "msg ctx: %d %p %s\n",
mc->proto, mc->conn_src,
osmo_hexdump((void*)mc->from_addr, sizeof(struct sockaddr_in)));
/* Increment RX statistics */
rate_ctr_inc(&conn_src->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]);
rate_ctr_add(&conn_src->rate_ctr_group->ctr[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);
rc = rx_rtp(msg);
out:
msgb_free(msg);
return rc;
}
static int rx_rtp(struct msgb *msg)
{
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
struct mgcp_conn_rtp *conn_src = mc->conn_src;
struct sockaddr_in *from_addr = mc->from_addr;
struct mgcp_conn *conn = conn_src->conn;
struct mgcp_trunk *trunk = conn->endp->trunk;
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx_rtp(%u bytes)\n", msgb_length(msg));
mgcp_conn_watchdog_kick(conn_src->conn);
@@ -1369,23 +1417,26 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
* 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(buf, len);
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;
/* Execute endpoint specific implementation that handles the
* dispatching of the RTP data */
return endp->type->dispatch_rtp_cb(proto, &addr, buf, len,
conn_src->conn);
return conn->endp->type->dispatch_rtp_cb(msg);
}
/*! set IP Type of Service parameter.
* \param[in] fd associated file descriptor
* \param[in] tos dscp value
* \returns 0 on success, -1 on ERROR */
* \param[in] fd associated file descriptor.
* \param[in] tos dscp value.
* \returns 0 on success, -1 on ERROR. */
int mgcp_set_ip_tos(int fd, int tos)
{
int ret;
@@ -1397,10 +1448,10 @@ int mgcp_set_ip_tos(int fd, int tos)
}
/*! bind RTP port to osmo_fd.
* \param[in] source_addr source (local) address to bind on
* \param[in] fd associated file descriptor
* \param[in] port to bind on
* \returns 0 on success, -1 on ERROR */
* \param[in] source_addr source (local) address to bind on.
* \param[in] fd associated file descriptor.
* \param[in] port to bind on.
* \returns 0 on success, -1 on ERROR. */
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port)
{
int rc;
@@ -1420,11 +1471,10 @@ int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port)
/* Bind RTP and RTCP port (helper function for mgcp_bind_net_rtp_port()) */
static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
struct mgcp_rtp_end *rtp_end, int endpno)
struct mgcp_rtp_end *rtp_end, struct mgcp_endpoint *endp)
{
/* NOTE: The port that is used for RTCP is the RTP port incremented by one
* (e.g. RTP-Port = 16000 ==> RTCP-Port = 16001) */
struct mgcp_endpoint *endp = &cfg->trunk.endpoints[endpno];
if (mgcp_create_bind(source_addr, &rtp_end->rtp,
rtp_end->local_port) != 0) {
@@ -1446,7 +1496,7 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
mgcp_set_ip_tos(rtp_end->rtp.fd, cfg->endp_dscp);
mgcp_set_ip_tos(rtp_end->rtcp.fd, cfg->endp_dscp);
rtp_end->rtp.when = BSC_FD_READ;
rtp_end->rtp.when = OSMO_FD_READ;
if (osmo_fd_register(&rtp_end->rtp) != 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"failed to register RTP port %d\n",
@@ -1454,7 +1504,7 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
goto cleanup2;
}
rtp_end->rtcp.when = BSC_FD_READ;
rtp_end->rtcp.when = OSMO_FD_READ;
if (osmo_fd_register(&rtp_end->rtcp) != 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"failed to register RTCP port %d\n",
@@ -1477,10 +1527,10 @@ cleanup0:
}
/*! bind RTP port to endpoint/connection.
* \param[in] endp endpoint that holds the RTP connection
* \param[in] rtp_port port number to bind on
* \param[in] conn associated RTP connection
* \returns 0 on success, -1 on ERROR */
* \param[in] endp endpoint that holds the RTP connection.
* \param[in] rtp_port port number to bind on.
* \param[in] conn associated RTP connection.
* \returns 0 on success, -1 on ERROR. */
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
struct mgcp_conn_rtp *conn)
{
@@ -1511,8 +1561,7 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
mgcp_get_local_addr(local_ip_addr, conn);
return bind_rtp(endp->cfg, local_ip_addr, end,
ENDPOINT_NUMBER(endp));
return bind_rtp(endp->cfg, local_ip_addr, end, endp);
}
/*! free allocated RTP and RTCP ports.

View File

@@ -23,10 +23,11 @@
#include <osmocom/netif/amr.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
static struct osmo_fd osmux_fd;
@@ -197,14 +198,15 @@ static struct mgcp_conn_rtp*
osmux_conn_lookup(struct mgcp_config *cfg, uint8_t cid,
struct in_addr *from_addr)
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
struct mgcp_endpoint *endp;
struct mgcp_conn *conn = NULL;
struct mgcp_conn_rtp * conn_rtp;
int i;
for (i=0; i<cfg->trunk.number_endpoints; i++) {
for (i = 0; i < trunk->number_endpoints; i++) {
endp = &cfg->trunk.endpoints[i];
endp = trunk->endpoints[i];
llist_for_each_entry(conn, &endp->conns, entry) {
if (conn->type != MGCP_CONN_TYPE_RTP)
@@ -234,13 +236,15 @@ static void scheduled_from_osmux_tx_rtp_cb(struct msgb *msg, void *data)
{
struct mgcp_conn_rtp *conn = data;
struct mgcp_endpoint *endp = conn->conn->endp;
struct sockaddr_in addr = {
.sin_addr = conn->end.addr,
.sin_port = conn->end.rtp_port,
}; /* FIXME: not set/used in cb */
struct sockaddr_in addr = { /* FIXME: do we know the source address?? */ };
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
*mc = (struct osmo_rtp_msg_ctx){
.proto = MGCP_PROTO_RTP,
.conn_src = conn,
.from_addr = &addr,
};
endp->type->dispatch_rtp_cb(MGCP_PROTO_RTP, &addr, (char *)msg->data, msg->len, conn->conn);
endp->type->dispatch_rtp_cb(msg);
msgb_free(msg);
}
@@ -406,7 +410,7 @@ int osmux_init(int role, struct mgcp_config *cfg)
return ret;
}
mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp);
osmux_fd.when |= BSC_FD_READ;
osmux_fd.when |= OSMO_FD_READ;
ret = osmo_fd_register(&osmux_fd);
if (ret < 0) {

View File

@@ -32,14 +32,16 @@
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/stats.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/mgcp/mgcp_network.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_stat.h>
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_conn.h>
@@ -53,83 +55,6 @@ struct mgcp_request {
#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
{ .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
[MGCP_CRCX_SUCCESS] = {"crcx:success", "CRCX command processed successfully."},
[MGCP_CRCX_FAIL_BAD_ACTION] = {"crcx:bad_action", "bad action in CRCX command."},
[MGCP_CRCX_FAIL_UNHANDLED_PARAM] = {"crcx:unhandled_param", "unhandled parameter in CRCX command."},
[MGCP_CRCX_FAIL_MISSING_CALLID] = {"crcx:missing_callid", "missing CallId in CRCX command."},
[MGCP_CRCX_FAIL_INVALID_MODE] = {"crcx:invalid_mode", "invalid connection mode in CRCX command."},
[MGCP_CRCX_FAIL_LIMIT_EXCEEDED] = {"crcx:limit_exceeded", "limit of concurrent connections was reached."},
[MGCP_CRCX_FAIL_UNKNOWN_CALLID] = {"crcx:unkown_callid", "unknown CallId in CRCX command."},
[MGCP_CRCX_FAIL_ALLOC_CONN] = {"crcx:alloc_conn_fail", "connection allocation failure."},
[MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC] = {"crcx:no_remote_conn_desc", "no opposite end specified for connection."},
[MGCP_CRCX_FAIL_START_RTP] = {"crcx:start_rtp_failure", "failure to start RTP processing."},
[MGCP_CRCX_FAIL_REJECTED_BY_POLICY] = {"crcx:conn_rejected", "connection rejected by policy."},
[MGCP_CRCX_FAIL_NO_OSMUX] = {"crcx:no_osmux", "no osmux offered by peer."},
[MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS] = {"crcx:conn_opt", "connection options invalid."},
[MGCP_CRCX_FAIL_CODEC_NEGOTIATION] = {"crcx:codec_nego", "codec negotiation failure."},
[MGCP_CRCX_FAIL_BIND_PORT] = {"crcx:bind_port", "port bind failure."},
};
const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
.group_name_prefix = "crcx",
.group_description = "crxc statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
.num_ctr = ARRAY_SIZE(mgcp_crcx_ctr_desc),
.ctr_desc = mgcp_crcx_ctr_desc
};
static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
[MGCP_MDCX_SUCCESS] = {"mdcx:success", "MDCX command processed successfully."},
[MGCP_MDCX_FAIL_WILDCARD] = {"mdcx:wildcard", "wildcard endpoint names in MDCX commands are unsupported."},
[MGCP_MDCX_FAIL_NO_CONN] = {"mdcx:no_conn", "endpoint specified in MDCX command has no active connections."},
[MGCP_MDCX_FAIL_INVALID_CALLID] = {"mdcx:callid", "invalid CallId specified in MDCX command."},
[MGCP_MDCX_FAIL_INVALID_CONNID] = {"mdcx:connid", "invalid connection ID specified in MDCX command."},
[MGCP_MDCX_FAIL_UNHANDLED_PARAM] = {"crcx:unhandled_param", "unhandled parameter in MDCX command."},
[MGCP_MDCX_FAIL_NO_CONNID] = {"mdcx:no_connid", "no connection ID specified in MDCX command."},
[MGCP_MDCX_FAIL_CONN_NOT_FOUND] = {"mdcx:conn_not_found", "connection specified in MDCX command does not exist."},
[MGCP_MDCX_FAIL_INVALID_MODE] = {"mdcx:invalid_mode", "invalid connection mode in MDCX command."},
[MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS] = {"mdcx:conn_opt", "connection options invalid."},
[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC] = {"mdcx:no_remote_conn_desc", "no opposite end specified for connection."},
[MGCP_MDCX_FAIL_START_RTP] = {"mdcx:start_rtp_failure", "failure to start RTP processing."},
[MGCP_MDCX_FAIL_REJECTED_BY_POLICY] = {"mdcx:conn_rejected", "connection rejected by policy."},
[MGCP_MDCX_DEFERRED_BY_POLICY] = {"mdcx:conn_deferred", "connection deferred by policy."},
};
const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
.group_name_prefix = "mdcx",
.group_description = "mdcx statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
.num_ctr = ARRAY_SIZE(mgcp_mdcx_ctr_desc),
.ctr_desc = mgcp_mdcx_ctr_desc
};
static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = {
[MGCP_DLCX_SUCCESS] = {"dlcx:success", "DLCX command processed successfully."},
[MGCP_DLCX_FAIL_WILDCARD] = {"dlcx:wildcard", "wildcard names in DLCX commands are unsupported."},
[MGCP_DLCX_FAIL_NO_CONN] = {"dlcx:no_conn", "endpoint specified in DLCX command has no active connections."},
[MGCP_DLCX_FAIL_INVALID_CALLID] = {"dlcx:callid", "CallId specified in DLCX command mismatches endpoint's CallId ."},
[MGCP_DLCX_FAIL_INVALID_CONNID] = {"dlcx:connid", "connection ID specified in DLCX command does not exist on endpoint."},
[MGCP_DLCX_FAIL_UNHANDLED_PARAM] = {"dlcx:unhandled_param", "unhandled parameter in DLCX command."},
[MGCP_DLCX_FAIL_REJECTED_BY_POLICY] = {"dlcx:rejected", "connection deletion rejected by policy."},
[MGCP_DLCX_DEFERRED_BY_POLICY] = {"dlcx:deferred", "connection deletion deferred by policy."},
};
const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
.group_name_prefix = "dlcx",
.group_description = "dlcx statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
.num_ctr = ARRAY_SIZE(mgcp_dlcx_ctr_desc),
.ctr_desc = mgcp_dlcx_ctr_desc
};
const static 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,
.num_ctr = ARRAY_SIZE(all_rtp_conn_rate_ctr_desc),
.ctr_desc = all_rtp_conn_rate_ctr_desc
};
static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *data);
static struct msgb *handle_create_con(struct mgcp_parse_data *data);
@@ -236,11 +161,11 @@ static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
* Remember the last transmission per endpoint.
*/
if (endp) {
struct mgcp_trunk_config *tcfg = endp->tcfg;
struct mgcp_trunk *trunk = endp->trunk;
talloc_free(endp->last_response);
talloc_free(endp->last_trans);
endp->last_trans = talloc_strdup(tcfg->endpoints, trans);
endp->last_response = talloc_strndup(tcfg->endpoints,
endp->last_trans = talloc_strdup(trunk->endpoints, trans);
endp->last_response = talloc_strndup(trunk->endpoints,
(const char *)res->l2h,
msgb_l2len(res));
}
@@ -278,10 +203,8 @@ static int add_params(struct msgb *msg, const struct mgcp_endpoint *endp,
/* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
if (endp->wildcarded_req
&& endp->tcfg->trunk_type == MGCP_TRUNK_VIRTUAL) {
rc = msgb_printf(msg, "Z: %s%x@%s\r\n",
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
ENDPOINT_NUMBER(endp), endp->cfg->domain);
&& endp->trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
rc = msgb_printf(msg, "Z: %s\r\n", endp->name);
if (rc < 0)
return -EINVAL;
}
@@ -361,24 +284,32 @@ static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
* - or a response (three numbers, space, transaction id) */
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
{
struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;
struct mgcp_parse_data pdata;
int rc, i, code, handled = 0;
struct msgb *resp = NULL;
char *data;
/* Count all messages, even incorect ones */
rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_MSGS_TOTAL]);
if (msgb_l2len(msg) < 4) {
LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_FAIL_MSG_PARSE]);
return NULL;
}
if (mgcp_msg_terminate_nul(msg))
if (mgcp_msg_terminate_nul(msg)) {
rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_FAIL_MSG_PARSE]);
return NULL;
}
mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");
/* attempt to treat it as a response */
if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_FAIL_MSG_PARSE]);
return NULL;
}
@@ -394,12 +325,14 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
if (pdata.endp && pdata.trans
&& pdata.endp->last_trans
&& strcmp(pdata.endp->last_trans, pdata.trans) == 0) {
rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_MSGS_RETRANSMITTED]);
return do_retransmission(pdata.endp);
}
/* check for general parser failure */
if (rc < 0) {
LOGP(DLMGCP, LOGL_NOTICE, "%s: failed to find the endpoint\n", msg->l2h);
rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_FAIL_NO_ENDPOINT]);
return create_err_response(NULL, -rc, (const char *) msg->l2h, pdata.trans);
}
@@ -413,9 +346,13 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
}
}
if (!handled)
if (handled) {
rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_MSGS_HANDLED]);
} else {
rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_MSGS_UNHANDLED]);
LOGP(DLMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n",
&msg->l2h[0]);
}
return resp;
}
@@ -435,13 +372,10 @@ static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
int i;
struct mgcp_rtp_end *end;
struct mgcp_port_range *range;
unsigned int tries;
OSMO_ASSERT(conn);
end = &conn->end;
OSMO_ASSERT(end);
range = &endp->cfg->net_ports;
@@ -659,13 +593,13 @@ static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
struct mgcp_rtp_end *rtp)
{
struct mgcp_trunk_config *tcfg = endp->tcfg;
struct mgcp_trunk *trunk = endp->trunk;
int patch_ssrc = expect_ssrc_change && tcfg->force_constant_ssrc;
int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
rtp->force_aligned_timing = tcfg->force_aligned_timing;
rtp->force_aligned_timing = trunk->force_aligned_timing;
rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
rtp->rfc5993_hr_convert = tcfg->rfc5993_hr_convert;
rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
"Configuring RTP endpoint: local port %d%s%s\n",
@@ -797,9 +731,9 @@ static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
/* CRCX command handler, processes the received command */
static struct msgb *handle_create_con(struct mgcp_parse_data *p)
{
struct mgcp_trunk_config *tcfg = p->endp->tcfg;
struct mgcp_trunk *trunk = p->endp->trunk;
struct mgcp_endpoint *endp = p->endp;
struct rate_ctr_group *rate_ctrs = tcfg->mgcp_crcx_ctr_group;
struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
int error_code = 400;
const char *local_options = NULL;
const char *callid = NULL;
@@ -812,6 +746,10 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p)
int rc;
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
if (!mgcp_endp_avail(endp)) {
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_AVAIL]);
return create_err_response(NULL, 501, "CRCX", p->trans);
}
/* parse CallID C: and LocalParameters L: */
for_each_line(line, p->save) {
@@ -882,7 +820,7 @@ mgcp_header_done:
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"CRCX: endpoint full, max. %i connections allowed!\n",
endp->type->max_conns);
if (tcfg->force_realloc) {
if (trunk->force_realloc) {
/* There is no more room for a connection, make some
* room by blindly tossing the oldest of the two two
* connections */
@@ -901,7 +839,7 @@ mgcp_header_done:
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"CRCX: already seized by other call (%s)\n",
endp->callid);
if (tcfg->force_realloc)
if (trunk->force_realloc)
/* This is not our call, toss everything by releasing
* the entire endpoint. (rude!) */
mgcp_endp_release(endp);
@@ -913,13 +851,19 @@ mgcp_header_done:
}
}
/* Set the callid, creation of another connection will only be possible
* when the callid matches up. (Connections are distinguished by their
* connection ids) */
endp->callid = talloc_strdup(tcfg->endpoints, callid);
if (!endp->callid) {
/* Claim endpoint resources. This will also set the callid,
* creating additional connections will only be possible if
* the callid matches up (see above). */
rc = mgcp_endp_claim(endp, callid);
if (rc != 0) {
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_CLAIM]);
return create_err_response(endp, 502, "CRCX", p->trans);
}
}
snprintf(conn_name, sizeof(conn_name), "%s", callid);
_conn = mgcp_conn_alloc(tcfg->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
_conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
if (!_conn) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"CRCX: unable to allocate RTP connection\n");
@@ -927,6 +871,7 @@ mgcp_header_done:
goto error2;
}
conn = mgcp_conn_get_rtp(endp, _conn->id);
OSMO_ASSERT(conn);
@@ -954,7 +899,7 @@ mgcp_header_done:
/* Set local connection options, if present */
if (local_options) {
rc = set_local_cx_options(endp->tcfg->endpoints,
rc = set_local_cx_options(endp->trunk->endpoints,
&endp->local_options, local_options);
if (rc != 0) {
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
@@ -974,8 +919,8 @@ mgcp_header_done:
goto error2;
}
conn->end.fmtp_extra = talloc_strdup(tcfg->endpoints,
tcfg->audio_fmtp_extra);
conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
trunk->audio_fmtp_extra);
if (p->cfg->force_ptime) {
conn->end.packet_duration_ms = p->cfg->force_ptime;
@@ -1010,8 +955,7 @@ mgcp_header_done:
/* policy CB */
if (p->cfg->policy_cb) {
int rc;
rc = p->cfg->policy_cb(tcfg, ENDPOINT_NUMBER(endp),
MGCP_ENDP_CRCX, p->trans);
rc = p->cfg->policy_cb(endp, MGCP_ENDP_CRCX, p->trans);
switch (rc) {
case MGCP_POLICY_REJECT:
LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
@@ -1033,17 +977,18 @@ mgcp_header_done:
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
"CRCX: Creating connection: port: %u\n", conn->end.local_port);
if (p->cfg->change_cb)
p->cfg->change_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX);
p->cfg->change_cb(endp, MGCP_ENDP_CRCX);
/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
OSMO_ASSERT(tcfg->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
if (conn->conn->mode & MGCP_CONN_RECV_ONLY
&& tcfg->keepalive_interval != MGCP_KEEPALIVE_NEVER)
&& trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
send_dummy(endp, conn);
LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
"CRCX: connection successfully created\n");
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_SUCCESS]);
mgcp_endp_update(endp);
return create_response_with_sdp(endp, conn, "CRCX", p->trans, true);
error2:
mgcp_endp_release(endp);
@@ -1052,16 +997,11 @@ error2:
return create_err_response(endp, error_code, "CRCX", p->trans);
}
/* MDCX command handler, processes the received command */
static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
{
struct mgcp_trunk_config *tcfg = p->endp->tcfg;
struct mgcp_endpoint *endp = p->endp;
struct rate_ctr_group *rate_ctrs = tcfg->mgcp_mdcx_ctr_group;
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.mgcp_mdcx_ctr_group;
int error_code = 500;
int silent = 0;
int have_sdp = 0;
@@ -1075,6 +1015,11 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
if (!mgcp_endp_avail(endp)) {
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_AVAIL]);
return create_err_response(NULL, 501, "MDCX", p->trans);
}
/* Prohibit wildcarded requests */
if (endp->wildcarded_req) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
@@ -1169,7 +1114,7 @@ mgcp_header_done:
/* Set local connection options, if present */
if (local_options) {
rc = set_local_cx_options(endp->tcfg->endpoints,
rc = set_local_cx_options(endp->trunk->endpoints,
&endp->local_options, local_options);
if (rc != 0) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
@@ -1228,8 +1173,7 @@ mgcp_header_done:
/* policy CB */
if (p->cfg->policy_cb) {
int rc;
rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
MGCP_ENDP_MDCX, p->trans);
rc = p->cfg->policy_cb(endp, MGCP_ENDP_MDCX, p->trans);
switch (rc) {
case MGCP_POLICY_REJECT:
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
@@ -1258,13 +1202,12 @@ mgcp_header_done:
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
"MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
if (p->cfg->change_cb)
p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
MGCP_ENDP_MDCX);
p->cfg->change_cb(endp, MGCP_ENDP_MDCX);
/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
OSMO_ASSERT(endp->tcfg->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
OSMO_ASSERT(endp->trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
if (conn->conn->mode & MGCP_CONN_RECV_ONLY
&& endp->tcfg->keepalive_interval != MGCP_KEEPALIVE_NEVER)
&& endp->trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
send_dummy(endp, conn);
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_SUCCESS]);
@@ -1273,6 +1216,7 @@ mgcp_header_done:
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
"MDCX: connection successfully modified\n");
mgcp_endp_update(endp);
return create_response_with_sdp(endp, conn, "MDCX", p->trans, false);
error3:
return create_err_response(endp, error_code, "MDCX", p->trans);
@@ -1285,9 +1229,8 @@ out_silent:
/* DLCX command handler, processes the received command */
static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
{
struct mgcp_trunk_config *tcfg = p->endp->tcfg;
struct mgcp_endpoint *endp = p->endp;
struct rate_ctr_group *rate_ctrs = tcfg->mgcp_dlcx_ctr_group;
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.mgcp_dlcx_ctr_group;
int error_code = 400;
int silent = 0;
char *line;
@@ -1298,6 +1241,11 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
"DLCX: deleting connection ...\n");
if (!mgcp_endp_avail(endp)) {
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_AVAIL]);
return create_err_response(NULL, 501, "DLCX", p->trans);
}
/* Prohibit wildcarded requests */
if (endp->wildcarded_req) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
@@ -1348,8 +1296,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
/* policy CB */
if (p->cfg->policy_cb) {
int rc;
rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
MGCP_ENDP_DLCX, p->trans);
rc = p->cfg->policy_cb(endp, MGCP_ENDP_DLCX, p->trans);
switch (rc) {
case MGCP_POLICY_REJECT:
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "DLCX: rejected by policy\n");
@@ -1413,8 +1360,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
}
if (p->cfg->change_cb)
p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
MGCP_ENDP_DLCX);
p->cfg->change_cb(endp, MGCP_ENDP_DLCX);
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_SUCCESS]);
if (silent)
@@ -1443,7 +1389,7 @@ static struct msgb *handle_rsip(struct mgcp_parse_data *p)
LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
if (p->cfg->reset_cb)
p->cfg->reset_cb(p->endp->tcfg);
p->cfg->reset_cb(p->endp->trunk);
return NULL;
}
@@ -1489,37 +1435,37 @@ static struct msgb *handle_noti_req(struct mgcp_parse_data *p)
/* Connection keepalive timer, will take care that dummy packets are send
* regularly, so that NAT connections stay open */
static void mgcp_keepalive_timer_cb(void *_tcfg)
static void mgcp_keepalive_timer_cb(void *_trunk)
{
struct mgcp_trunk_config *tcfg = _tcfg;
struct mgcp_trunk *trunk = _trunk;
struct mgcp_conn *conn;
int i;
LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
tcfg->trunk_nr);
trunk->trunk_nr);
/* Do not accept invalid configuration values
* valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
* values greater 0 */
OSMO_ASSERT(tcfg->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
/* The dummy packet functionality has been disabled, we will exit
* immediately, no further timer is scheduled, which means we will no
* longer send dummy packets even when we did before */
if (tcfg->keepalive_interval == MGCP_KEEPALIVE_NEVER)
if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
return;
/* In cases where only one dummy packet is sent, we do not need
* the timer since the functions that handle the CRCX and MDCX are
* triggering the sending of the dummy packet. So we behave like in
* the MGCP_KEEPALIVE_NEVER case */
if (tcfg->keepalive_interval == MGCP_KEEPALIVE_ONCE)
if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
return;
/* Send walk over all endpoints and send out dummy packets through
* every connection present on each endpoint */
for (i = 1; i < tcfg->number_endpoints; ++i) {
struct mgcp_endpoint *endp = &tcfg->endpoints[i];
for (i = 1; i < trunk->number_endpoints; ++i) {
struct mgcp_endpoint *endp = trunk->endpoints[i];
llist_for_each_entry(conn, &endp->conns, entry) {
if (conn->mode == MGCP_CONN_RECV_ONLY)
send_dummy(endp, &conn->u.rtp);
@@ -1528,75 +1474,29 @@ static void mgcp_keepalive_timer_cb(void *_tcfg)
/* Schedule the keepalive timer for the next round */
LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
tcfg->trunk_nr);
osmo_timer_schedule(&tcfg->keepalive_timer, tcfg->keepalive_interval,
trunk->trunk_nr);
osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
0);
}
void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval)
void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
{
tcfg->keepalive_interval = interval;
osmo_timer_setup(&tcfg->keepalive_timer, mgcp_keepalive_timer_cb, tcfg);
trunk->keepalive_interval = interval;
osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
if (interval <= 0)
osmo_timer_del(&tcfg->keepalive_timer);
osmo_timer_del(&trunk->keepalive_timer);
else
osmo_timer_schedule(&tcfg->keepalive_timer,
tcfg->keepalive_interval, 0);
}
static int free_rate_counter_group(struct rate_ctr_group *rate_ctr_group)
{
rate_ctr_group_free(rate_ctr_group);
return 0;
}
static int alloc_mgcp_rate_counters(struct mgcp_trunk_config *trunk, void *ctx)
{
/* FIXME: Each new rate counter group requires a unique index. At the
* moment we generate an index using a counter, but perhaps there is
* a better way of assigning indices? */
static unsigned int crcx_rate_ctr_index = 0;
static unsigned int mdcx_rate_ctr_index = 0;
static unsigned int dlcx_rate_ctr_index = 0;
static unsigned int all_rtp_conn_rate_ctr_index = 0;
if (trunk->mgcp_crcx_ctr_group == NULL) {
trunk->mgcp_crcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index);
if (!trunk->mgcp_crcx_ctr_group)
return -1;
talloc_set_destructor(trunk->mgcp_crcx_ctr_group, free_rate_counter_group);
crcx_rate_ctr_index++;
}
if (trunk->mgcp_mdcx_ctr_group == NULL) {
trunk->mgcp_mdcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index);
if (!trunk->mgcp_mdcx_ctr_group)
return -1;
talloc_set_destructor(trunk->mgcp_mdcx_ctr_group, free_rate_counter_group);
mdcx_rate_ctr_index++;
}
if (trunk->mgcp_dlcx_ctr_group == NULL) {
trunk->mgcp_dlcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index);
if (!trunk->mgcp_dlcx_ctr_group)
return -1;
talloc_set_destructor(trunk->mgcp_dlcx_ctr_group, free_rate_counter_group);
dlcx_rate_ctr_index++;
}
if (trunk->all_rtp_conn_stats == NULL) {
trunk->all_rtp_conn_stats = rate_ctr_group_alloc(ctx, &all_rtp_conn_rate_ctr_group_desc,
all_rtp_conn_rate_ctr_index);
if (!trunk->all_rtp_conn_stats)
return -1;
talloc_set_destructor(trunk->all_rtp_conn_stats, free_rate_counter_group);
all_rtp_conn_rate_ctr_index++;
}
return 0;
osmo_timer_schedule(&trunk->keepalive_timer,
trunk->keepalive_interval, 0);
}
/*! allocate configuration with default values.
* (called once at startup by main function) */
struct mgcp_config *mgcp_config_alloc(void)
{
/* FIXME: This is unrelated to the protocol, put this in some
* appropiate place! */
struct mgcp_config *cfg;
cfg = talloc_zero(NULL, struct mgcp_config);
@@ -1620,112 +1520,19 @@ struct mgcp_config *mgcp_config_alloc(void)
cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
/* default trunk handling */
cfg->trunk.cfg = cfg;
cfg->trunk.trunk_nr = 0;
cfg->trunk.trunk_type = MGCP_TRUNK_VIRTUAL;
cfg->trunk.audio_name = talloc_strdup(cfg, "AMR/8000");
cfg->trunk.audio_payload = 126;
cfg->trunk.audio_send_ptime = 1;
cfg->trunk.audio_send_name = 1;
cfg->trunk.omit_rtcp = 0;
mgcp_trunk_set_keepalive(&cfg->trunk, MGCP_KEEPALIVE_ONCE);
if (alloc_mgcp_rate_counters(&cfg->trunk, cfg) < 0) {
INIT_LLIST_HEAD(&cfg->trunks);
/* Allocate virtual trunk */
if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
talloc_free(cfg);
return NULL;
}
INIT_LLIST_HEAD(&cfg->trunks);
mgcp_ratectr_global_alloc(cfg, &cfg->ratectr);
return cfg;
}
/*! allocate configuration with default values.
* (called once at startup by VTY)
* \param[in] cfg mgcp configuration
* \param[in] nr trunk number
* \returns pointer to allocated trunk configuration */
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr)
{
struct mgcp_trunk_config *trunk;
trunk = talloc_zero(cfg, struct mgcp_trunk_config);
if (!trunk) {
LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate.\n");
return NULL;
}
trunk->cfg = cfg;
trunk->trunk_type = MGCP_TRUNK_E1;
trunk->trunk_nr = nr;
trunk->audio_name = talloc_strdup(cfg, "AMR/8000");
trunk->audio_payload = 126;
trunk->audio_send_ptime = 1;
trunk->audio_send_name = 1;
trunk->vty_number_endpoints = 33;
trunk->omit_rtcp = 0;
mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
alloc_mgcp_rate_counters(trunk, trunk);
llist_add_tail(&trunk->entry, &cfg->trunks);
return trunk;
}
/*! get trunk configuration by trunk number (index).
* \param[in] cfg mgcp configuration
* \param[in] index trunk number
* \returns pointer to trunk configuration, NULL on error */
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index)
{
struct mgcp_trunk_config *trunk;
llist_for_each_entry(trunk, &cfg->trunks, entry)
if (trunk->trunk_nr == index)
return trunk;
return NULL;
}
/*! allocate endpoints and set default values.
* (called once at startup by VTY)
* \param[in] tcfg trunk configuration
* \returns 0 on success, -1 on failure */
int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
{
int i;
tcfg->endpoints = _talloc_zero_array(tcfg->cfg,
sizeof(struct mgcp_endpoint),
tcfg->vty_number_endpoints,
"endpoints");
if (!tcfg->endpoints)
return -1;
for (i = 0; i < tcfg->vty_number_endpoints; ++i) {
INIT_LLIST_HEAD(&tcfg->endpoints[i].conns);
tcfg->endpoints[i].cfg = tcfg->cfg;
tcfg->endpoints[i].tcfg = tcfg;
switch (tcfg->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
tcfg->endpoints[i].type = &ep_typeset.rtp;
break;
case MGCP_TRUNK_E1:
/* FIXME: Implement E1 allocation */
LOGP(DLMGCP, LOGL_FATAL, "E1 trunks not implemented!\n");
break;
default:
osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n",
tcfg->trunk_type, __FILE__, __LINE__);
}
}
tcfg->number_endpoints = tcfg->vty_number_endpoints;
alloc_mgcp_rate_counters(tcfg, tcfg->cfg);
return 0;
}
static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
{
return write(cfg->gw_fd.bfd.fd, buf, len);
@@ -1756,17 +1563,16 @@ int mgcp_send_reset_all(struct mgcp_config *cfg)
/*! Reset a single endpoint by sending RSIP message to self.
* (called by VTY)
* \param[in] endp trunk endpoint
* \param[in] endpoint number
* \param[in] endp to reset
* \returns 0 on success, -1 on error */
int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint)
int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
{
char buf[MGCP_ENDPOINT_MAXLEN + 128];
int len;
int rc;
len = snprintf(buf, sizeof(buf),
"RSIP 39 %x@%s MGCP 1.0\r\n", endpoint, endp->cfg->domain);
"RSIP 39 %s MGCP 1.0\r\n", endp->name);
if (len < 0)
return -1;

View File

@@ -0,0 +1,229 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* rate-counter implementation */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <errno.h>
#include <osmocom/core/stats.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_ratectr.h>
static const struct rate_ctr_desc mgcp_general_ctr_desc[] = {
/* rx_msgs = rx_msgs_retransmitted + rx_msgs_handled + rx_msgs_unhandled + err_rx_msg_parse + err_rx_no_endpoint */
[MGCP_GENERAL_RX_MSGS_TOTAL] = { "mgcp:rx_msgs", "total number of MGCP messages received." },
[MGCP_GENERAL_RX_MSGS_RETRANSMITTED] = { "mgcp:rx_msgs_retransmitted", "number of received retransmissions." },
[MGCP_GENERAL_RX_MSGS_HANDLED] = { "mgcp:rx_msgs_handled", "number of handled MGCP messages." },
[MGCP_GENERAL_RX_MSGS_UNHANDLED] = { "mgcp:rx_msgs_unhandled", "number of unhandled MGCP messages." },
[MGCP_GENERAL_RX_FAIL_MSG_PARSE] = { "mgcp:err_rx_msg_parse", "error parsing MGCP message." },
[MGCP_GENERAL_RX_FAIL_NO_ENDPOINT] =
{ "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 = {
.group_name_prefix = "mgcp",
.group_description = "mgcp general statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
.num_ctr = ARRAY_SIZE(mgcp_general_ctr_desc),
.ctr_desc = mgcp_general_ctr_desc
};
static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
[MGCP_CRCX_SUCCESS] = { "crcx:success", "CRCX command processed successfully." },
[MGCP_CRCX_FAIL_BAD_ACTION] = { "crcx:bad_action", "bad action in CRCX command." },
[MGCP_CRCX_FAIL_UNHANDLED_PARAM] = { "crcx:unhandled_param", "unhandled parameter in CRCX command." },
[MGCP_CRCX_FAIL_MISSING_CALLID] = { "crcx:missing_callid", "missing CallId in CRCX command." },
[MGCP_CRCX_FAIL_INVALID_MODE] = { "crcx:invalid_mode", "invalid connection mode in CRCX command." },
[MGCP_CRCX_FAIL_LIMIT_EXCEEDED] = { "crcx:limit_exceeded", "limit of concurrent connections was reached." },
[MGCP_CRCX_FAIL_UNKNOWN_CALLID] = { "crcx:unkown_callid", "unknown CallId in CRCX command." },
[MGCP_CRCX_FAIL_ALLOC_CONN] = { "crcx:alloc_conn_fail", "connection allocation failure." },
[MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC] =
{ "crcx:no_remote_conn_desc", "no opposite end specified for connection." },
[MGCP_CRCX_FAIL_START_RTP] = { "crcx:start_rtp_failure", "failure to start RTP processing." },
[MGCP_CRCX_FAIL_REJECTED_BY_POLICY] = { "crcx:conn_rejected", "connection rejected by policy." },
[MGCP_CRCX_FAIL_NO_OSMUX] = { "crcx:no_osmux", "no osmux offered by peer." },
[MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS] = { "crcx:conn_opt", "connection options invalid." },
[MGCP_CRCX_FAIL_CODEC_NEGOTIATION] = { "crcx:codec_nego", "codec negotiation failure." },
[MGCP_CRCX_FAIL_BIND_PORT] = { "crcx:bind_port", "port bind failure." },
[MGCP_CRCX_FAIL_AVAIL] = { "crcx:unavailable", "endpoint unavailable." },
[MGCP_CRCX_FAIL_CLAIM] = { "crcx:claim", "endpoint can not be claimed." },
};
const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
.group_name_prefix = "crcx",
.group_description = "crxc statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
.num_ctr = ARRAY_SIZE(mgcp_crcx_ctr_desc),
.ctr_desc = mgcp_crcx_ctr_desc
};
static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
[MGCP_MDCX_SUCCESS] = { "mdcx:success", "MDCX command processed successfully." },
[MGCP_MDCX_FAIL_WILDCARD] = { "mdcx:wildcard", "wildcard endpoint names in MDCX commands are unsupported." },
[MGCP_MDCX_FAIL_NO_CONN] = { "mdcx:no_conn", "endpoint specified in MDCX command has no active connections." },
[MGCP_MDCX_FAIL_INVALID_CALLID] = { "mdcx:callid", "invalid CallId specified in MDCX command." },
[MGCP_MDCX_FAIL_INVALID_CONNID] = { "mdcx:connid", "invalid connection ID specified in MDCX command." },
[MGCP_MDCX_FAIL_UNHANDLED_PARAM] = { "crcx:unhandled_param", "unhandled parameter in MDCX command." },
[MGCP_MDCX_FAIL_NO_CONNID] = { "mdcx:no_connid", "no connection ID specified in MDCX command." },
[MGCP_MDCX_FAIL_CONN_NOT_FOUND] =
{ "mdcx:conn_not_found", "connection specified in MDCX command does not exist." },
[MGCP_MDCX_FAIL_INVALID_MODE] = { "mdcx:invalid_mode", "invalid connection mode in MDCX command." },
[MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS] = { "mdcx:conn_opt", "connection options invalid." },
[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC] =
{ "mdcx:no_remote_conn_desc", "no opposite end specified for connection." },
[MGCP_MDCX_FAIL_START_RTP] = { "mdcx:start_rtp_failure", "failure to start RTP processing." },
[MGCP_MDCX_FAIL_REJECTED_BY_POLICY] = { "mdcx:conn_rejected", "connection rejected by policy." },
[MGCP_MDCX_DEFERRED_BY_POLICY] = { "mdcx:conn_deferred", "connection deferred by policy." },
[MGCP_MDCX_FAIL_AVAIL] = { "mdcx:unavailable", "endpoint unavailable." },
};
const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
.group_name_prefix = "mdcx",
.group_description = "mdcx statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
.num_ctr = ARRAY_SIZE(mgcp_mdcx_ctr_desc),
.ctr_desc = mgcp_mdcx_ctr_desc
};
static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = {
[MGCP_DLCX_SUCCESS] = { "dlcx:success", "DLCX command processed successfully." },
[MGCP_DLCX_FAIL_WILDCARD] = { "dlcx:wildcard", "wildcard names in DLCX commands are unsupported." },
[MGCP_DLCX_FAIL_NO_CONN] = { "dlcx:no_conn", "endpoint specified in DLCX command has no active connections." },
[MGCP_DLCX_FAIL_INVALID_CALLID] =
{ "dlcx:callid", "CallId specified in DLCX command mismatches endpoint's CallId ." },
[MGCP_DLCX_FAIL_INVALID_CONNID] =
{ "dlcx:connid", "connection ID specified in DLCX command does not exist on endpoint." },
[MGCP_DLCX_FAIL_UNHANDLED_PARAM] = { "dlcx:unhandled_param", "unhandled parameter in DLCX command." },
[MGCP_DLCX_FAIL_REJECTED_BY_POLICY] = { "dlcx:rejected", "connection deletion rejected by policy." },
[MGCP_DLCX_DEFERRED_BY_POLICY] = { "dlcx:deferred", "connection deletion deferred by policy." },
[MGCP_DLCX_FAIL_AVAIL] = { "dlcx:unavailable", "endpoint unavailable." },
};
const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
.group_name_prefix = "dlcx",
.group_description = "dlcx statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
.num_ctr = ARRAY_SIZE(mgcp_dlcx_ctr_desc),
.ctr_desc = mgcp_dlcx_ctr_desc
};
static const struct rate_ctr_desc e1_rate_ctr_desc[] = {
[E1_I460_TRAU_RX_FAIL_CTR] = {"e1:rx_fail", "Inbound I.460 TRAU failures."},
[E1_I460_TRAU_TX_FAIL_CTR] = {"e1:tx_fail", "Outbound I.460 TRAU failures."},
[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 = {
.group_name_prefix = "e1",
.group_description = "e1 statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
.num_ctr = ARRAY_SIZE(e1_rate_ctr_desc),
.ctr_desc = e1_rate_ctr_desc
};
const static 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,
.num_ctr = ARRAY_SIZE(all_rtp_conn_rate_ctr_desc),
.ctr_desc = all_rtp_conn_rate_ctr_desc
};
static int free_rate_counter_group(struct rate_ctr_group *rate_ctr_group)
{
rate_ctr_group_free(rate_ctr_group);
return 0;
}
/*! allocate global rate counters into a given rate counter struct
* (called once at startup)
* \param[in] ctx talloc context.
* \param[out] ratectr struct that holds the counters
* \returns 0 on success, -EINVAL on failure */
int mgcp_ratectr_global_alloc(void *ctx, struct mgcp_ratectr_global *ratectr)
{
/* FIXME: Each new rate counter group requires a unique index. At the
* moment we generate an index using a counter, but perhaps there is
* a better way of assigning indices? */
static unsigned int general_rate_ctr_index = 0;
if (ratectr->mgcp_general_ctr_group == NULL) {
ratectr->mgcp_general_ctr_group =
rate_ctr_group_alloc(ctx, &mgcp_general_ctr_group_desc, general_rate_ctr_index);
if (!ratectr->mgcp_general_ctr_group)
return -EINVAL;
talloc_set_destructor(ratectr->mgcp_general_ctr_group, free_rate_counter_group);
general_rate_ctr_index++;
}
return 0;
}
/*! allocate trunk specific rate counters into a given rate counter struct
* (called once on trunk initialization)
* \param[in] ctx talloc context.
* \param[out] ratectr struct that holds the counters
* \returns 0 on success, -EINVAL on failure */
int mgcp_ratectr_trunk_alloc(void *ctx, struct mgcp_ratectr_trunk *ratectr)
{
/* FIXME: see comment in mgcp_ratectr_global_alloc() */
static unsigned int crcx_rate_ctr_index = 0;
static unsigned int mdcx_rate_ctr_index = 0;
static unsigned int dlcx_rate_ctr_index = 0;
static unsigned int all_rtp_conn_rate_ctr_index = 0;
if (ratectr->mgcp_crcx_ctr_group == NULL) {
ratectr->mgcp_crcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index);
if (!ratectr->mgcp_crcx_ctr_group)
return -EINVAL;
talloc_set_destructor(ratectr->mgcp_crcx_ctr_group, free_rate_counter_group);
crcx_rate_ctr_index++;
}
if (ratectr->mgcp_mdcx_ctr_group == NULL) {
ratectr->mgcp_mdcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index);
if (!ratectr->mgcp_mdcx_ctr_group)
return -EINVAL;
talloc_set_destructor(ratectr->mgcp_mdcx_ctr_group, free_rate_counter_group);
mdcx_rate_ctr_index++;
}
if (ratectr->mgcp_dlcx_ctr_group == NULL) {
ratectr->mgcp_dlcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index);
if (!ratectr->mgcp_dlcx_ctr_group)
return -EINVAL;
talloc_set_destructor(ratectr->mgcp_dlcx_ctr_group, free_rate_counter_group);
dlcx_rate_ctr_index++;
}
if (ratectr->all_rtp_conn_stats == NULL) {
ratectr->all_rtp_conn_stats = rate_ctr_group_alloc(ctx, &all_rtp_conn_rate_ctr_group_desc,
all_rtp_conn_rate_ctr_index);
if (!ratectr->all_rtp_conn_stats)
return -EINVAL;
talloc_set_destructor(ratectr->all_rtp_conn_stats, free_rate_counter_group);
all_rtp_conn_rate_ctr_index++;
}
if (ratectr->e1_stats == NULL) {
ratectr->e1_stats = rate_ctr_group_alloc(ctx, &e1_rate_ctr_group_desc, mdcx_rate_ctr_index);
if (!ratectr->e1_stats)
return -EINVAL;
talloc_set_destructor(ratectr->e1_stats, free_rate_counter_group);
mdcx_rate_ctr_index++;
}
return 0;
}

View File

@@ -22,11 +22,15 @@
#include <osmocom/core/msgb.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <errno.h>
#include <stdlib.h>
@@ -358,10 +362,11 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
break;
default:
if (p->endp)
/* TODO: Check spec: We used the bare endpoint number before,
* now we use the endpoint name as a whole? Is this allowed? */
LOGP(DLMGCP, LOGL_NOTICE,
"Unhandled SDP option: '%c'/%d on 0x%x\n",
line[0], line[0],
ENDPOINT_NUMBER(p->endp));
"Unhandled SDP option: '%c'/%d on %s\n",
line[0], line[0], endp->name);
else
LOGP(DLMGCP, LOGL_NOTICE,
"Unhandled SDP option: '%c'/%d\n",
@@ -381,7 +386,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
if (rc < 0)
LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "failed to add codec\n");
}
talloc_free(tmp_ctx);
@@ -476,6 +481,8 @@ static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsign
for (i = 0; i < fmtp_params_len; i++) {
rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
if (rc < 0)
return -EINVAL;
/* Add amr octet align parameter */
if (fmtp_params[i].param.amr_octet_aligned_present) {
@@ -494,7 +501,7 @@ static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsign
return -EINVAL;
}
rc = msgb_printf(sdp, "\r\n", fmtp_params[i].payload_type);
rc = msgb_printf(sdp, "\r\n");
if (rc < 0)
return -EINVAL;
}
@@ -557,7 +564,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
if (rc < 0)
goto buffer_too_small;
if (endp->tcfg->audio_send_name) {
if (endp->trunk->audio_send_name) {
rc = add_rtpmap(sdp, payload_type, audio_name);
if (rc < 0)
goto buffer_too_small;
@@ -573,7 +580,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
if (rc < 0)
goto buffer_too_small;
}
if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
if (conn->end.packet_duration_ms > 0 && endp->trunk->audio_send_ptime) {
rc = msgb_printf(sdp, "a=ptime:%u\r\n",
conn->end.packet_duration_ms);
if (rc < 0)

View File

@@ -22,10 +22,12 @@
*
*/
#include <osmocom/mgcp/mgcp_stat.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <limits.h>
#include <inttypes.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_stat.h>
#include <osmocom/mgcp/mgcp_endp.h>
/* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss)

View File

@@ -0,0 +1,255 @@
/* Trunk handling */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_e1.h>
#include <osmocom/abis/e1_input.h>
/*! allocate trunk and add it (if required) to the trunk list.
* (called once at startup by VTY).
* \param[in] cfg mgcp configuration.
* \param[in] ttype trunk type.
* \param[in] nr trunk number.
* \returns pointer to allocated trunk, NULL on failure. */
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr)
{
struct mgcp_trunk *trunk;
trunk = talloc_zero(cfg, struct mgcp_trunk);
if (!trunk) {
LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate.\n");
return NULL;
}
trunk->cfg = cfg;
trunk->trunk_type = ttype;
trunk->trunk_nr = nr;
trunk->audio_send_ptime = 1;
trunk->audio_send_name = 1;
trunk->v.vty_number_endpoints = 32;
trunk->omit_rtcp = 0;
mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
llist_add_tail(&trunk->entry, &cfg->trunks);
mgcp_ratectr_trunk_alloc(cfg, &trunk->ratectr);
return trunk;
}
/*! allocate endpoints and set default values
* (called once at startup by VTY).
* \param[in] trunk trunk configuration.
* \returns 0 on success, -1 on failure. */
int mgcp_trunk_alloc_endpts(struct mgcp_trunk *trunk)
{
int i;
struct mgcp_endpoint *endp;
unsigned int number_endpoints;
unsigned int first_endpoint_nr;
/* This function is called once on startup by the VTY to allocate the
* endpoints. The number of endpoints must not change througout the
* runtime of the MGW */
OSMO_ASSERT(trunk->number_endpoints == 0);
OSMO_ASSERT(trunk->endpoints == NULL);
switch (trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
/* Due to historical reasons the endpoints on the virtual
* trunk start counting at 1. */
first_endpoint_nr = 1;
number_endpoints = trunk->v.vty_number_endpoints;
break;
case MGCP_TRUNK_E1:
/* The first timeslot on an E1 line is reserved for framing
* and alignment and can not be used for audio transport */
first_endpoint_nr = 1 * MGCP_ENDP_E1_SUBSLOTS;
number_endpoints = (NUM_E1_TS-1) * MGCP_ENDP_E1_SUBSLOTS;
break;
default:
OSMO_ASSERT(false);
}
/* Make sure the amount of requested endpoints does not execeed
* sane limits. The VTY already limits the possible amount,
* however miss-initialization of the struct or memory corruption
* could still lead to an excessive allocation of endpoints, so
* better stop early if that is the case. */
OSMO_ASSERT(number_endpoints < 65534);
/* allocate pointer array for the endpoints */
trunk->endpoints = talloc_zero_array(trunk->cfg, struct mgcp_endpoint*,
number_endpoints);
if (!trunk->endpoints)
return -1;
/* create endpoints */
for (i = 0; i < number_endpoints; i++) {
endp = mgcp_endp_alloc(trunk, i + first_endpoint_nr);
if (!endp) {
talloc_free(trunk->endpoints);
return -1;
}
trunk->endpoints[i] = endp;
}
/* make the endpoints we just created available to the MGW code */
trunk->number_endpoints = number_endpoints;
return 0;
}
/*! Equip trunk with endpoints and resources
* (called once at startup by VTY).
* \param[in] trunk trunk configuration.
* \returns 0 on success, -1 on failure. */
int mgcp_trunk_equip(struct mgcp_trunk *trunk)
{
unsigned int i;
/* Allocate endpoints */
if(mgcp_trunk_alloc_endpts(trunk) != 0)
return -1;
/* Allocate resources */
switch (trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
/* No additional initaliziation required here, virtual
* endpoints will open/close network sockets themselves
* on demand. */
break;
case MGCP_TRUNK_E1:
/* The TS initalization happens once on startup for all
* timeslots. This only affects the i460 multiplexer. Until
* now no E1 resources are claimed yet. This happens on demand
* when the related endpoint is actually used */
memset(trunk->e1.i460_ts, 0, sizeof(trunk->e1.i460_ts));
for (i = 0; i < (NUM_E1_TS-1); i++)
osmo_i460_ts_init(&trunk->e1.i460_ts[i]);
break;
default:
OSMO_ASSERT(false);
}
return 0;
}
/*! get trunk configuration by trunk number (index).
* \param[in] cfg mgcp configuration.
* \param[in] ttype trunk type.
* \param[in] nr trunk number.
* \returns pointer to trunk configuration, NULL on error. */
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr)
{
struct mgcp_trunk *trunk;
llist_for_each_entry(trunk, &cfg->trunks, entry) {
if (trunk->trunk_nr == nr && trunk->trunk_type == ttype)
return trunk;
}
return NULL;
}
/* Made public for unit-testing, do not use from outside this file */
int e1_trunk_nr_from_epname(const char *epname)
{
unsigned long int trunk_nr;
size_t prefix_len;
char *str_trunk_nr_end;
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
if (strncmp(epname, MGCP_ENDPOINT_PREFIX_E1_TRUNK, prefix_len) != 0)
return -EINVAL;
errno = 0;
trunk_nr = strtoul(epname + prefix_len, &str_trunk_nr_end, 10);
if (errno == ERANGE || trunk_nr > 64
|| epname + prefix_len == str_trunk_nr_end
|| str_trunk_nr_end[0] != '/')
return -EINVAL;
else
return trunk_nr;
}
/*! Find a trunk by the trunk prefix in the endpoint name.
* \param[in] epname endpoint name with trunk prefix to look up.
* \param[in] cfg that contains the trunks where the endpoint is located.
* \returns trunk or NULL if trunk was not found. */
struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname)
{
size_t prefix_len;
char epname_lc[MGCP_ENDPOINT_MAXLEN];
int trunk_nr;
osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
epname = epname_lc;
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
if (strncmp(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, prefix_len) == 0) {
return mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
}
trunk_nr = e1_trunk_nr_from_epname(epname);
if (trunk_nr >= 0)
return mgcp_trunk_by_num(cfg, MGCP_TRUNK_E1, trunk_nr);
/* Earlier versions of osmo-mgw were accepting endpoint names
* without trunk prefix. This is normally not allowed, each MGCP
* request should supply an endpoint name with trunk prefix.
* However in order to stay compatible with old versions of
* osmo-bsc and osmo-msc we still accept endpoint names without
* trunk prefix and just assume that the virtual trunk should
* be selected. There is even a TTCN3 test for this, see also:
* MGCP_Test.TC_crcx_noprefix */
if ((epname[0] >= '0' && epname[0] <= '9') || (epname[0] >= 'a' && epname[0] <= 'f')) {
LOGP(DLMGCP, LOGL_ERROR, "missing trunk prefix in endpoint name \"%s\", assuming trunk \"%s\"!\n", epname,
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
return mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
}
LOGP(DLMGCP, LOGL_ERROR, "unable to find trunk for endpoint name \"%s\"!\n", epname);
return NULL;
}
/*! Find a trunk (E1) by its associated E1 line number.
* \param[in] num e1 line number.
* \returns trunk or NULL if trunk was not found. */
struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num)
{
/*! When used on trunks other than E1, the result will always be NULL. */
struct mgcp_trunk *trunk;
llist_for_each_entry(trunk, &cfg->trunks, entry) {
if (trunk->trunk_type == MGCP_TRUNK_E1 && trunk->e1.vty_line_nr == num)
return trunk;
}
return NULL;
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,10 @@ AM_CFLAGS = \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOCTRL_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(LIBOSMOTRAU_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -26,5 +29,8 @@ osmo_mgw_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOTRAU_LIBS) \
$(NULL)

View File

@@ -30,14 +30,18 @@
#include <limits.h>
#include <unistd.h>
#include <errno.h>
#include <osmocom/core/msgb.h>
#include <osmocom/abis/e1_input.h>
#include <sys/socket.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/vty.h>
#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>
@@ -48,22 +52,30 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/socket.h>
#include <osmocom/ctrl/control_vty.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/ports.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/misc.h>
#include <osmocom/abis/abis.h>
#include "../../bscconfig.h"
#define _GNU_SOURCE
#include <getopt.h>
/* can be changed once libosmocore 1.4.0 is released */
#ifndef OSMO_CTRL_PORT_MGW
#define OSMO_CTRL_PORT_MGW 4267
#endif
/* FIXME: Make use of the rtp proxy code */
static struct mgcp_config *cfg;
static struct mgcp_trunk_config *reset_trunk;
static struct mgcp_trunk *reset_trunk;
static int reset_endpoints = 0;
static int daemonize = 0;
@@ -89,18 +101,21 @@ static void print_help()
printf(" -s --disable-color\n");
printf(" -D --daemonize Fork the process into a background daemon\n");
printf(" -V --version Print the version number\n");
printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n");
}
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static int long_option = 0;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"config-file", 1, 0, 'c'},
{"daemonize", 0, 0, 'D'},
{"version", 0, 0, 'V'},
{"disable-color", 0, 0, 's'},
{"vty-ref-xml", 0, &long_option, 1},
{0, 0, 0, 0},
};
@@ -114,6 +129,15 @@ static void handle_options(int argc, char **argv)
print_help();
exit(0);
break;
case 0:
switch (long_option) {
case 1:
vty_dump_xml_ref(stdout);
exit(0);
default:
fprintf(stderr, "error parsing cmdline options\n");
exit(2);
}
case 'c':
config_file = talloc_strdup(tall_bsc_ctx, optarg);
break;
@@ -140,13 +164,13 @@ static void handle_options(int argc, char **argv)
/* Callback function to be called when the RSIP ("Reset in Progress") mgcp
* command is received */
static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
static int mgcp_rsip_cb(struct mgcp_trunk *trunk)
{
/* Set flag so that, when read_call_agent() is called next time
* the reset can progress */
reset_endpoints = 1;
reset_trunk = tcfg;
reset_trunk = trunk;
return 0;
}
@@ -195,7 +219,7 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
/* Walk over all endpoints and trigger a release, this will release all
* endpoints, possible open connections are forcefully dropped */
for (i = 1; i < reset_trunk->number_endpoints; ++i)
mgcp_endp_release(&reset_trunk->endpoints[i]);
mgcp_endp_release(reset_trunk->endpoints[i]);
}
return 0;
@@ -247,7 +271,13 @@ static const struct log_info_cat log_categories[] = {
.description = "RTP stream handling",
.color = "\033[1;30m",
.enabled = 1,.loglevel = LOGL_NOTICE,
},
},
[DE1] = {
.name = "DE1",
.description = "E1 line handling",
.color = "\033[1;31m",
.enabled = 1,.loglevel = LOGL_NOTICE,
},
};
const struct log_info log_info = {
@@ -267,6 +297,7 @@ int main(int argc, char **argv)
osmo_init_ignore_signals();
osmo_init_logging2(tall_bsc_ctx, &log_info);
libosmo_abis_init(tall_bsc_ctx);
cfg = mgcp_config_alloc();
if (!cfg)
@@ -278,6 +309,8 @@ int main(int argc, char **argv)
osmo_talloc_vty_add_cmds();
osmo_stats_vty_add_cmds();
mgcp_vty_init();
ctrl_vty_init(cfg);
e1inp_vty_init();
handle_options(argc, argv);
@@ -294,6 +327,12 @@ 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);
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);
}
/* Set the reset callback function. This functions is called when the
* mgcp-command "RSIP" (Reset in Progress) is received */
cfg->reset_cb = mgcp_rsip_cb;

View File

@@ -11,6 +11,8 @@ AM_CFLAGS = \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(LIBOSMOTRAU_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -35,7 +37,10 @@ mgcp_test_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOTRAU_LIBS) \
$(LIBRARY_DL) \
$(LIBRARY_DLSYM) \
$(LIBOSMONETIF_LIBS) \
-lm \
$(NULL)

View File

@@ -22,10 +22,12 @@
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/vty.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_stat.h>
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_codec.h>
@@ -70,7 +72,7 @@ static void test_strline(void)
}
#define AUEP1 "AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0\r\n"
#define AUEP1_RET "200 158663169 OK\r\n"
#define AUEP1_RET "500 158663169 FAIL\r\n"
#define AUEP2 "AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0\r\n"
#define AUEP2_RET "500 18983213 FAIL\r\n"
#define EMPTY "\r\n"
@@ -81,7 +83,7 @@ static void test_strline(void)
#define MDCX_WRONG_EP "MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0\r\n"
#define MDCX_ERR_RET "500 18983213 FAIL\r\n"
#define MDCX_UNALLOCATED "MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0\r\n"
#define MDCX_RET "400 18983214 FAIL\r\n"
#define MDCX_RET "500 18983214 FAIL\r\n"
#define MDCX3 \
"MDCX 18983215 1@mgw MGCP 1.0\r\n" \
@@ -591,14 +593,25 @@ static struct msgb *create_msg(const char *str, const char *conn_id)
return msg;
}
static int last_endpoint = -1;
static char last_endpoint[MGCP_ENDPOINT_MAXLEN];
static int mgcp_test_policy_cb(struct mgcp_trunk_config *cfg, int endpoint,
int state, const char *transactio_id)
static int mgcp_test_policy_cb(struct mgcp_endpoint *endp,
int state, const char *transaction_id)
{
fprintf(stderr, "Policy CB got state %d on endpoint 0x%x\n",
state, endpoint);
last_endpoint = endpoint;
unsigned int i;
struct mgcp_trunk *trunk;
fprintf(stderr, "Policy CB got state %d on endpoint %s\n",
state, endp->name);
trunk = endp->trunk;
last_endpoint[0] = '\0';
for (i = 0; i < trunk->number_endpoints; i++) {
if (strcmp(endp->name, trunk->endpoints[i]->name) == 0)
osmo_strlcpy(last_endpoint, trunk->endpoints[i]->name,
sizeof(last_endpoint));
}
return MGCP_POLICY_CONT;
}
@@ -641,11 +654,11 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp)
return real_clock_gettime(clk_id, tp);
}
static void mgcp_endpoints_release(struct mgcp_trunk_config *trunk)
static void mgcp_endpoints_release(struct mgcp_trunk *trunk)
{
int i;
for (i = 1; i < trunk->number_endpoints; i++)
mgcp_endp_release(&trunk->endpoints[i]);
for (i = 0; i < trunk->number_endpoints; i++)
mgcp_endp_release(trunk->endpoints[i]);
}
#define CONN_UNMODIFIED (0x1000)
@@ -749,23 +762,21 @@ static void test_messages(void)
{
struct mgcp_config *cfg;
struct mgcp_endpoint *endp;
struct mgcp_trunk_config *trunk2;
struct mgcp_trunk *trunk;
int i;
struct mgcp_conn_rtp *conn = NULL;
char last_conn_id[256];
int rc;
cfg = mgcp_config_alloc();
trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
cfg->trunk.vty_number_endpoints = 64;
mgcp_endpoints_allocate(&cfg->trunk);
trunk->v.vty_number_endpoints = 64;
mgcp_trunk_equip(trunk);
cfg->policy_cb = mgcp_test_policy_cb;
memset(last_conn_id, 0, sizeof(last_conn_id));
trunk2 = mgcp_trunk_alloc(cfg, 1);
mgcp_endpoints_allocate(trunk2);
for (i = 0; i < ARRAY_SIZE(tests); i++) {
const struct mgcp_test *t = &tests[i];
struct msgb *inp;
@@ -774,10 +785,10 @@ static void test_messages(void)
printf("\n================================================\n");
printf("Testing %s\n", t->name);
last_endpoint = -1;
last_endpoint[0] = '\0';
dummy_packets = 0;
osmo_talloc_replace_string(cfg, &cfg->trunk.audio_fmtp_extra,
osmo_talloc_replace_string(cfg, &trunk->audio_fmtp_extra,
t->extra_fmtp);
inp = create_msg(t->req, last_conn_id);
@@ -809,8 +820,9 @@ static void test_messages(void)
if (dummy_packets)
printf("Dummy packets: %d\n", dummy_packets);
if (last_endpoint != -1) {
endp = &cfg->trunk.endpoints[last_endpoint];
if (last_endpoint[0] != '\0') {
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
OSMO_ASSERT(endp);
conn = mgcp_conn_get_rtp(endp, "1");
if (conn) {
@@ -865,10 +877,9 @@ static void test_messages(void)
/* Check detected payload type */
if (conn && t->ptype != PTYPE_IGNORE) {
OSMO_ASSERT(last_endpoint != -1);
endp = &cfg->trunk.endpoints[last_endpoint];
OSMO_ASSERT(last_endpoint[0] != '\0');
fprintf(stderr, "endpoint 0x%x: "
fprintf(stderr, "endpoint:%s: "
"payload type %d (expected %d)\n",
last_endpoint,
conn->end.codec->payload_type, t->ptype);
@@ -882,29 +893,26 @@ static void test_messages(void)
}
}
mgcp_endpoints_release(trunk2);
mgcp_endpoints_release(&cfg->trunk);
mgcp_endpoints_release(trunk);
talloc_free(cfg);
}
static void test_retransmission(void)
{
struct mgcp_config *cfg;
struct mgcp_trunk_config *trunk2;
struct mgcp_trunk *trunk;
int i;
char last_conn_id[256];
int rc;
cfg = mgcp_config_alloc();
trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
cfg->trunk.vty_number_endpoints = 64;
mgcp_endpoints_allocate(&cfg->trunk);
trunk->v.vty_number_endpoints = 64;
mgcp_trunk_equip(trunk);
memset(last_conn_id, 0, sizeof(last_conn_id));
trunk2 = mgcp_trunk_alloc(cfg, 1);
mgcp_endpoints_allocate(trunk2);
for (i = 0; i < ARRAY_SIZE(retransmit); i++) {
const struct mgcp_test *t = &retransmit[i];
struct msgb *inp;
@@ -943,8 +951,7 @@ static void test_retransmission(void)
msgb_free(msg);
}
mgcp_endpoints_release(trunk2);
mgcp_endpoints_release(&cfg->trunk);
mgcp_endpoints_release(trunk);
talloc_free(cfg);
}
@@ -958,18 +965,16 @@ static int rqnt_cb(struct mgcp_endpoint *endp, char _tone)
static void test_rqnt_cb(void)
{
struct mgcp_config *cfg;
struct mgcp_trunk_config *trunk2;
struct mgcp_trunk *trunk;
struct msgb *inp, *msg;
char conn_id[256];
cfg = mgcp_config_alloc();
trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
cfg->rqnt_cb = rqnt_cb;
cfg->trunk.vty_number_endpoints = 64;
mgcp_endpoints_allocate(&cfg->trunk);
trunk2 = mgcp_trunk_alloc(cfg, 1);
mgcp_endpoints_allocate(trunk2);
trunk->v.vty_number_endpoints = 64;
mgcp_trunk_equip(trunk);
inp = create_msg(CRCX, NULL);
msg = mgcp_handle_message(cfg, inp);
@@ -998,8 +1003,7 @@ static void test_rqnt_cb(void)
inp = create_msg(DLCX, conn_id);
msgb_free(mgcp_handle_message(cfg, inp));
msgb_free(inp);
mgcp_endpoints_release(trunk2);
mgcp_endpoints_release(&cfg->trunk);
mgcp_endpoints_release(trunk);
talloc_free(cfg);
}
@@ -1035,8 +1039,9 @@ static void test_packet_loss_calc(void)
{
int i;
struct mgcp_endpoint endp;
struct mgcp_endpoint *endpoints[1];
struct mgcp_config cfg = {0};
struct mgcp_trunk_config trunk;
struct mgcp_trunk trunk;
printf("Testing packet loss calculation.\n");
@@ -1045,9 +1050,10 @@ static void test_packet_loss_calc(void)
endp.cfg = &cfg;
endp.type = &ep_typeset.rtp;
trunk.vty_number_endpoints = 1;
trunk.endpoints = &endp;
endp.tcfg = &trunk;
trunk.v.vty_number_endpoints = 1;
trunk.endpoints = endpoints;
trunk.endpoints[0] = &endp;
endp.trunk = &trunk;
INIT_LLIST_HEAD(&endp.conns);
for (i = 0; i < ARRAY_SIZE(pl_test_dat); ++i) {
@@ -1256,19 +1262,19 @@ struct rtp_packet_info test_rtp_packets1[] = {
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
struct mgcp_rtp_state *state,
struct mgcp_rtp_end *rtp_end,
struct sockaddr_in *addr, char *data, int len);
struct sockaddr_in *addr, struct msgb *msg);
static void test_packet_error_detection(int patch_ssrc, int patch_ts)
{
int i;
struct mgcp_trunk_config trunk;
struct mgcp_trunk trunk;
struct mgcp_endpoint endp;
struct mgcp_endpoint *endpoints[1];
struct mgcp_config cfg = {0};
struct mgcp_rtp_state state;
struct mgcp_rtp_end *rtp;
struct sockaddr_in addr = { 0 };
char buffer[4096];
uint32_t last_ssrc = 0;
uint32_t last_timestamp = 0;
uint32_t last_seqno = 0;
@@ -1295,12 +1301,13 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
endp.cfg = &cfg;
endp.type = &ep_typeset.rtp;
trunk.vty_number_endpoints = 1;
trunk.endpoints = &endp;
trunk.v.vty_number_endpoints = 1;
trunk.endpoints = endpoints;
trunk.endpoints[0] = &endp;
trunk.force_constant_ssrc = patch_ssrc;
trunk.force_aligned_timing = patch_ts;
endp.tcfg = &trunk;
endp.trunk = &trunk;
INIT_LLIST_HEAD(&endp.conns);
_conn = mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
@@ -1316,16 +1323,17 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
struct rtp_packet_info *info = test_rtp_packets1 + i;
struct msgb *msg = msgb_alloc(4096, __func__);
force_monotonic_time_us = round(1000000.0 * info->txtime);
OSMO_ASSERT(info->len <= sizeof(buffer));
OSMO_ASSERT(info->len <= msgb_tailroom(msg));
OSMO_ASSERT(info->len >= 0);
memmove(buffer, info->data, info->len);
msg->l3h = msgb_put(msg, info->len);
memcpy((char*)msgb_l3(msg), info->data, info->len);
mgcp_rtp_end_config(&endp, 1, rtp);
mgcp_patch_and_count(&endp, &state, rtp, &addr,
buffer, info->len);
mgcp_patch_and_count(&endp, &state, rtp, &addr, msg);
if (state.out_stream.ssrc != last_ssrc) {
printf("Output SSRC changed to %08x\n",
@@ -1352,6 +1360,8 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
last_out_ts_err_cnt = state.out_stream.err_ts_ctr->current;
last_timestamp = state.out_stream.last_timestamp;
last_seqno = state.out_stream.last_seq;
msgb_free(msg);
}
force_monotonic_time_us = -1;
@@ -1361,7 +1371,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
static void test_multilple_codec(void)
{
struct mgcp_config *cfg;
struct mgcp_trunk_config *trunk2;
struct mgcp_trunk *trunk;
struct mgcp_endpoint *endp;
struct msgb *inp, *resp;
struct in_addr addr;
@@ -1371,15 +1381,13 @@ static void test_multilple_codec(void)
printf("Testing multiple payload types\n");
cfg = mgcp_config_alloc();
cfg->trunk.vty_number_endpoints = 64;
mgcp_endpoints_allocate(&cfg->trunk);
trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
trunk->v.vty_number_endpoints = 64;
mgcp_trunk_equip(trunk);
cfg->policy_cb = mgcp_test_policy_cb;
trunk2 = mgcp_trunk_alloc(cfg, 1);
mgcp_endpoints_allocate(trunk2);
/* Allocate endpoint 1@mgw with two codecs */
last_endpoint = -1;
last_endpoint[0] = '\0';
inp = create_msg(CRCX_MULT_1, NULL);
resp = mgcp_handle_message(cfg, inp);
OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
@@ -1387,14 +1395,15 @@ static void test_multilple_codec(void)
msgb_free(inp);
msgb_free(resp);
OSMO_ASSERT(last_endpoint == 1);
endp = &cfg->trunk.endpoints[last_endpoint];
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/1@mgw") == 0);
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
OSMO_ASSERT(endp);
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 18);
/* Allocate 2@mgw with three codecs, last one ignored */
last_endpoint = -1;
last_endpoint[0] = '\0';
inp = create_msg(CRCX_MULT_2, NULL);
resp = mgcp_handle_message(cfg, inp);
OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
@@ -1402,8 +1411,9 @@ static void test_multilple_codec(void)
msgb_free(inp);
msgb_free(resp);
OSMO_ASSERT(last_endpoint == 2);
endp = &cfg->trunk.endpoints[last_endpoint];
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/2@mgw") == 0);
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
OSMO_ASSERT(endp);
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 18);
@@ -1414,7 +1424,7 @@ static void test_multilple_codec(void)
* it makes and since we already decided in OS#2658 that a missing
* LCO should pick a sane default codec, it makes sense to expect
* the same behaviour if SDP lacks proper payload type information */
last_endpoint = -1;
last_endpoint[0] = '\0';
inp = create_msg(CRCX_MULT_3, NULL);
resp = mgcp_handle_message(cfg, inp);
OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
@@ -1422,14 +1432,15 @@ static void test_multilple_codec(void)
msgb_free(inp);
msgb_free(resp);
OSMO_ASSERT(last_endpoint == 3);
endp = &cfg->trunk.endpoints[last_endpoint];
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/3@mgw") == 0);
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
OSMO_ASSERT(endp);
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 0);
/* Allocate 4@mgw with a single codec */
last_endpoint = -1;
last_endpoint[0] = '\0';
inp = create_msg(CRCX_MULT_4, NULL);
resp = mgcp_handle_message(cfg, inp);
OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
@@ -1437,37 +1448,38 @@ static void test_multilple_codec(void)
msgb_free(inp);
msgb_free(resp);
OSMO_ASSERT(last_endpoint == 4);
endp = &cfg->trunk.endpoints[last_endpoint];
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/4@mgw") == 0);
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
OSMO_ASSERT(endp);
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 18);
/* Allocate 5@mgw at select GSM.. */
last_endpoint = -1;
/* Allocate 5@mgw and let osmo-mgw pick a codec from the list */
last_endpoint[0] = '\0';
inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);
talloc_free(cfg->trunk.audio_name);
cfg->trunk.audio_name = "GSM/8000";
cfg->trunk.no_audio_transcoding = 1;
trunk->no_audio_transcoding = 1;
resp = mgcp_handle_message(cfg, inp);
OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
sizeof(conn_id)) == 0);
msgb_free(inp);
msgb_free(resp);
OSMO_ASSERT(last_endpoint == 5);
endp = &cfg->trunk.endpoints[last_endpoint];
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
OSMO_ASSERT(endp);
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 3);
OSMO_ASSERT(conn->end.codec->payload_type == 0);
inp = create_msg(MDCX_NAT_DUMMY, conn_id);
last_endpoint = -1;
last_endpoint[0] = '\0';
resp = mgcp_handle_message(cfg, inp);
msgb_free(inp);
msgb_free(resp);
OSMO_ASSERT(last_endpoint == 5);
endp = &cfg->trunk.endpoints[last_endpoint];
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
OSMO_ASSERT(endp);
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 3);
@@ -1487,23 +1499,23 @@ static void test_multilple_codec(void)
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(!conn);
last_endpoint = -1;
last_endpoint[0] = '\0';
inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);
cfg->trunk.no_audio_transcoding = 0;
trunk->no_audio_transcoding = 0;
resp = mgcp_handle_message(cfg, inp);
OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
sizeof(conn_id)) == 0);
msgb_free(inp);
msgb_free(resp);
OSMO_ASSERT(last_endpoint == 5);
endp = &cfg->trunk.endpoints[last_endpoint];
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
OSMO_ASSERT(endp);
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 0);
mgcp_endpoints_release(trunk2);
mgcp_endpoints_release(&cfg->trunk);
mgcp_endpoints_release(trunk);
talloc_free(cfg);
}
@@ -1513,14 +1525,17 @@ static void test_no_cycle(void)
struct mgcp_endpoint *endp;
struct mgcp_conn_rtp *conn = NULL;
struct mgcp_conn *_conn = NULL;
struct mgcp_trunk *trunk;
printf("Testing no sequence flow on initial packet\n");
cfg = mgcp_config_alloc();
cfg->trunk.vty_number_endpoints = 64;
mgcp_endpoints_allocate(&cfg->trunk);
trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
trunk->v.vty_number_endpoints = 64;
mgcp_trunk_equip(trunk);
endp = &cfg->trunk.endpoints[1];
endp = mgcp_endp_by_name(NULL, "rtpbridge/1@mgw", cfg);
OSMO_ASSERT(endp);
_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP,
"test-connection");
@@ -1552,28 +1567,26 @@ static void test_no_cycle(void)
OSMO_ASSERT(conn->state.stats.cycles == UINT16_MAX + 1);
OSMO_ASSERT(conn->state.stats.max_seq == 0);
mgcp_endpoints_release(&cfg->trunk);
mgcp_endpoints_release(trunk);
talloc_free(cfg);
}
static void test_no_name(void)
{
struct mgcp_trunk_config *trunk2;
struct mgcp_trunk *trunk;
struct mgcp_config *cfg;
struct msgb *inp, *msg;
printf("Testing no rtpmap name\n");
cfg = mgcp_config_alloc();
trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
cfg->trunk.vty_number_endpoints = 64;
cfg->trunk.audio_send_name = 0;
mgcp_endpoints_allocate(&cfg->trunk);
trunk->v.vty_number_endpoints = 64;
trunk->audio_send_name = 0;
mgcp_trunk_equip(trunk);
cfg->policy_cb = mgcp_test_policy_cb;
trunk2 = mgcp_trunk_alloc(cfg, 1);
mgcp_endpoints_allocate(trunk2);
inp = create_msg(CRCX, NULL);
msg = mgcp_handle_message(cfg, inp);
@@ -1585,8 +1598,7 @@ static void test_no_name(void)
msgb_free(inp);
msgb_free(msg);
mgcp_endpoints_release(trunk2);
mgcp_endpoints_release(&cfg->trunk);
mgcp_endpoints_release(trunk);
talloc_free(cfg);
}
@@ -2105,6 +2117,48 @@ void test_conn_id_matching()
talloc_free(conn);
}
void test_e1_trunk_nr_from_epname()
{
int trunk_nr;
/* Note: e1_trunk_nr_from_epname does not check the text
* after the E1 trunk number, after the delimiter
* character "/" arbitrary text may follow. */
trunk_nr = e1_trunk_nr_from_epname("ds/e1-1/s-1/su16-0");
OSMO_ASSERT(trunk_nr == 1);
trunk_nr = e1_trunk_nr_from_epname("ds/e1-2/s-2/su16-0");
OSMO_ASSERT(trunk_nr == 2);
trunk_nr = e1_trunk_nr_from_epname("ds/e1-3/s-23/su32-0");
OSMO_ASSERT(trunk_nr == 3);
trunk_nr = e1_trunk_nr_from_epname("ds/e1-3/xxxxxxx");
OSMO_ASSERT(trunk_nr == 3);
trunk_nr = e1_trunk_nr_from_epname("ds/e1-24/s-1/su16-0");
OSMO_ASSERT(trunk_nr == 24);
trunk_nr = e1_trunk_nr_from_epname("ds/e1-64/s-1/su16-0");
OSMO_ASSERT(trunk_nr == 64);
/* The following endpoint strings should fail, either the
* trunk number exceeds the valid range or the trunk prefix
* is wrong. Also when the delimiter character "/" at the
* end of the trunk is wrong the parsing should fail. */
trunk_nr = e1_trunk_nr_from_epname("ds/e1-0/s-1/su16-0");
OSMO_ASSERT(trunk_nr == -EINVAL);
trunk_nr = e1_trunk_nr_from_epname("ds/e1-65/s-1/su16-0");
OSMO_ASSERT(trunk_nr == -EINVAL);
trunk_nr = e1_trunk_nr_from_epname("ds/e1--1/s-1/su16-0");
OSMO_ASSERT(trunk_nr == -EINVAL);
trunk_nr = e1_trunk_nr_from_epname("xxxxxx4zyz");
OSMO_ASSERT(trunk_nr == -EINVAL);
trunk_nr = e1_trunk_nr_from_epname("ds/e1+2/s-1/su16-0");
OSMO_ASSERT(trunk_nr == -EINVAL);
trunk_nr = e1_trunk_nr_from_epname("ds/e2-24/s-1/su16-0");
OSMO_ASSERT(trunk_nr == -EINVAL);
trunk_nr = e1_trunk_nr_from_epname("ds/e1-24s-1/su16-0");
OSMO_ASSERT(trunk_nr == -EINVAL);
return;
}
int main(int argc, char **argv)
{
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
@@ -2130,6 +2184,7 @@ int main(int argc, char **argv)
test_check_local_cx_options(ctx);
test_mgcp_codec_pt_translate();
test_conn_id_matching();
test_e1_trunk_nr_from_epname();
OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);

View File

@@ -35,6 +35,7 @@ mgcp_client_test_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBRARY_DL) \
$(LIBRARY_DLSYM) \
$(LIBOSMONETIF_LIBS) \
$(NULL)

View File

@@ -547,6 +547,57 @@ static void test_map_codec_to_pt_and_map_pt_to_codec(void)
printf("\n");
}
void test_mgcp_client_e1_epname(void)
{
char *epname;
if (mgcp)
talloc_free(mgcp);
mgcp = mgcp_client_init(ctx, &conf);
/* Valid endpoint names */
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 1, 15, 64, 0);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 2, 14, 32, 0);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 3, 13, 32, 4);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 4, 12, 16, 0);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 5, 11, 16, 2);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 6, 10, 16, 4);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 7, 9, 16, 6);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 8, 8, 8, 0);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 9, 7, 8, 1);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 10, 6, 8, 2);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 11, 5, 8, 3);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 12, 4, 8, 4);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 13, 3, 8, 5);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 14, 2, 8, 6);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 1, 8, 7);
printf("%s\n", epname);
/* A few invalid enpoint names */
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 1, 128, 0);
OSMO_ASSERT(epname == NULL);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 1, 8, 16);
OSMO_ASSERT(epname == NULL);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 0, 8, 2);
OSMO_ASSERT(epname == NULL);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 64, 8, 2);
OSMO_ASSERT(epname == NULL);
}
static const struct log_info_cat log_categories[] = {
};
@@ -575,6 +626,7 @@ int main(int argc, char **argv)
test_sdp_section_start();
test_map_codec_to_pt_and_map_pt_to_codec();
test_map_pt_to_codec();
test_mgcp_client_e1_epname();
printf("Done\n");
fprintf(stderr, "Done\n");

View File

@@ -72,4 +72,9 @@ DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
DLMGCP MGCP client: using endpoint domain '@mgw'
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su128-0@mgw), rate(128)/offset(0) combination is invalid!
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su8-16@mgw), rate(8)/offset(16) combination is invalid!
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-0/su8-2@mgw), E1-timeslot number (0) is invalid!
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-64/su8-2@mgw), E1-timeslot number (64) is invalid!
Done

View File

@@ -178,4 +178,19 @@ test_sdp_section_start() test [9]:
2 <= 2
100 <= 100
ds/e1-1/s-15/su64-0@mgw
ds/e1-2/s-14/su32-0@mgw
ds/e1-3/s-13/su32-4@mgw
ds/e1-4/s-12/su16-0@mgw
ds/e1-5/s-11/su16-2@mgw
ds/e1-6/s-10/su16-4@mgw
ds/e1-7/s-9/su16-6@mgw
ds/e1-8/s-8/su8-0@mgw
ds/e1-9/s-7/su8-1@mgw
ds/e1-10/s-6/su8-2@mgw
ds/e1-11/s-5/su8-3@mgw
ds/e1-12/s-4/su8-4@mgw
ds/e1-13/s-3/su8-5@mgw
ds/e1-14/s-2/su8-6@mgw
ds/e1-15/s-1/su8-7@mgw
Done