Compare commits

..

71 Commits

Author SHA1 Message Date
Pau Espin Pedrol
d761d355f9 Bump version: 1.2.0.109-8d064-dirty → 1.3.0
Change-Id: I524222f5a056111325087cfb44d83d02571b475f
2018-05-03 17:40:35 +02:00
Alexander Couzens
8d064dfd24 Revert "stats: use libosmocore rate counter for in/out_stream.err_ts_counter"
This reverts commit 7181cc1f02.
The tests are broken on i686, arm (non 64bit systems).

Change-Id: I15f3c78f8410d709733ed5692ba94ba17559d7e1
2018-04-21 20:26:17 +02:00
Philipp Maier
7181cc1f02 stats: use libosmocore rate counter for in/out_stream.err_ts_counter
The two counters: in_stream.err_ts_counter and out_stream.err_ts_counter
are still handcoded. To make them better accessible they should
be replaced with libosmocore rate counters.

- replace state.in_stream.err_ts_counter with libosmocore rate counter
- replace state.out_stream.err_ts_counter with libosmocore rate counter

Change-Id: I67aa7a8602f60366ef3ba2c5b1319b1b85719f64
Related: OS#2517
2018-04-17 16:43:59 +02:00
Neels Hofmeyr
60f8e31a2f use osmo_init_logging2() with proper talloc ctx
Change-Id: I3e2a9aef5242efdf11a64536f79099a6e9cec53f
2018-04-01 16:05:05 +02:00
Neels Hofmeyr
086c3f3c67 cosmetic: mgcp, legacy_mgcp: drop unused vty.h definitions
Change-Id: I38e279ed175259e6d36c0e69d6a352506face014
2018-03-22 14:56:20 +01:00
Philipp Maier
d0b470d1a9 mgcp_conn: add function mgcp_rtp_conn_cleanup()
The function mgcp_conn_free() holds a few lines to de-initalize
members which are struct mgcp_conn_rtp specific. Since we already
have an mgcp_rtp_conn_init() that does the intialization, we should
have an mgcp_rtp_conn_cleanup() too.

 - add function mgcp_rtp_conn_cleanup() and move rtp specific
   code to that function.

Change-Id: Ib9bf6d2a3af4f1df1a4ab5ec789b39a2cee2532f
2018-03-19 18:05:51 +01:00
Philipp Maier
77f76d0be5 cosmetic: rename .._codec_reset() to .._codec_init()
The function mgcp_rtp_codec_reset() is soley called from
mgcp_rtp_conn_init(), lets change the prefix here to _init too.

- rename mgcp_rtp_conn_reset() to mgcp_rtp_conn_init()

Change-Id: I246aabc896089c1f2b3d0409ec3422a85e43575c
2018-03-19 18:05:51 +01:00
Philipp Maier
892dec0be9 mgcp_conn: do not touch u.rtp in mgcp_conn_alloc()
The function mgcp_conn_alloc() calls mgcp_rtp_conn_init() to initalize
the RTP specific members (union u.rtp) but also touches u.rtp directly.
This should not be the case, only mgcp_rtp_conn_init() may touch the
union depending on which type of RTP connection is initialized
(currently there is only MGCP_CONN_TYPE_RTP).

- let mgcp_rtp_conn_init() set the backpointer to the generic
  conn part.

Change-Id: I6f806f9bfa71b446c15bdc34ae59d2bc1cd10d7e
Note: This is merely a cosmetic change.
2018-03-19 18:05:51 +01:00
Philipp Maier
c430d19112 cosmetic: rename function .._conn_reset() to .._conn_init()
We do allocate connections dynamically and we initialize them
once by calling mgcp_rtp_conn_reset(). Calling this a reset
function implies that the reset happens multiple times while
the struct lives. This is not tha case, so lets change the
suffix to _init()

- rename mgcp_rtp_conn_reset() to mgcp_rtp_conn_init()

Change-Id: Ie48b575ff81c8f48afcc25f485967e011e90027b
2018-03-19 18:05:46 +01:00
Neels Hofmeyr
7b8e419d92 jenkins.sh: add --enable-werror to configure flags
Change-Id: If3af8d0fe9247c22d57dbe70fc80a58ee7297f75
2018-03-13 15:39:32 +01:00
Neels Hofmeyr
4f7613eeb1 configure: add --enable-werror
Provide a sane means of adding the -Werror compiler flag.

Currently, some of our jenkins.sh add -Werror by passing 'CFLAGS="-Werror"',
but that actually *overwrites* all the other CFLAGS we might want to have set.

Maintain these exceptions from -Werror:
a) deprecation (allow upstream to mark deprecation without breaking builds);
b) "#warning" pragmas (allow to remind ourselves of errors without breaking
   builds)

As a last configure step before generating the output files, print the complete
CFLAGS and CPPFLAGS by means of AC_MSG_RESULT.

Change-Id: Ia66aa48e957f4dcbdf8a7df3010b84f472c33f76
2018-03-05 20:43:27 +01:00
Philipp Maier
01f039538e cosmetic: mgcp_client_fsm: rename enums
The enum defining the event and state identifiers is prefixed with
"bsc_".

- coose a more conceise prefix

Change-Id: I662d8e4328911610e7d1943f1b623e96c3a8b3c1
2018-02-26 16:09:57 +01:00
Philipp Maier
d2e3a52230 mgcp_client_fsm: Add FSM event names
The FSM lacks a proper definition of the FSM event names. This causes
problems when inspecting the FSM using the VTY.

- Add proper FSM Event names

Change-Id: Ic0990abea2e9fd92546e7b337b5ff3d6f0866321
Related: OS#2924
2018-02-26 16:09:57 +01:00
Stefan Sperling
2924825bc6 enable osmo_fsm vty commands in libosmo-mgcp-client vty
Call osmo_fsm_vty_add_cmds() to make osmo_fsm VTY commands available
in libosmo-mgcp-client's VTY interface.

Change-Id: If772edc304a9f342a57fb548f26908256cc9e6e5
Related: OS#2967
2018-02-22 18:10:45 +01:00
Neels Hofmeyr
bd86f94a09 compiler warning: ignore deprecated in mgcp_client_test.c
mgcp_msg_crcx() causes deprecation warning, but it's fine for a unit test
to use deprecated API.

Change-Id: Iba6d0c9c729367e00a9ab7fff7c89007d336e59d
2018-02-22 07:59:04 +00:00
Neels Hofmeyr
1083533db4 mgcp_client: detect SDP section-start parsing errors
After call to mgcp_find_section_end(), actually check the proper variable to
evaluate its return value.

Show in mgcp_client_test output that the parsing errors are fixed, and enable
the assertion that no tests should fail.

Change-Id: I62a2453cd9e2e7d5408423161fa65ec9c9989f98
2018-02-21 16:58:53 +01:00
Neels Hofmeyr
0793d2f5d5 mgcp_client: cosmetic: clean up SDP params parsing
The mgcp_response_parse_params() is in a jumble. Straighten out these cosmetic
issues:

- Move assertion of r->body close to its first use.
- Instead of a talloc_zero and osmo_strlcpy dance, simply use talloc_strdup().
- Drop the first unused invocation of mgcp_find_section_end().
- Drop unused assignment of data_ptr = data.
- In the log, mention "SDP" to clarify.
- Add a comment clarifying how we skip the section marker.

Change-Id: Icf1df761270777a142bc8ace75f2a10918314f73
2018-02-21 16:58:53 +01:00
Neels Hofmeyr
a8c6a9c37a mgcp_client: show failure by MGCP SDP section parsing test
To show how the current code fails, add test_sdp_section_start() to
mgcp_client_test.c, and temporarily accept failing output. This will be fixed
in change I62a2453cd9e2e7d5408423161fa65ec9c9989f98.

Change-Id: I5c6d3566b8f6dbf04c0cd8b127423f5295c19f8d
2018-02-21 16:58:30 +01:00
Pau Espin Pedrol
2da99a2946 mgcp_stat: Don't print osmux stats if it is off
Otherwise we get Osmux stats during a session using RTP, which is
confusing.

Change-Id: I6fcd680a073fbf8769488ffa2b2b32098c87edf4
2018-02-20 13:12:02 +01:00
Pau Espin Pedrol
c3eed40f00 legacy: mgcp_protocol: Don't print osmux stats if it is off
Otherwise we get Osmux stats during a session using RTP, which is
confusing.

Forward-ported from openbsc e39e18992a3b966581f06fa632d6342643996aaa.

Change-Id: I9031350242dd37ce255631c20eed33976887faa6
2018-02-20 13:11:43 +01:00
Neels Hofmeyr
515e341ff6 osmo-mgw: Add talloc context introspection via VTY
This requires libosmocore with Change-Id
I43fc42880b22294d83c565ae600ac65e4f38b30d or later.

Change-Id: I59feac155ba2342fcc2b27b029e803b8a10da2d3
2018-02-17 14:11:33 +01:00
Philipp Maier
8bda7a7766 client: add an optional FSM interface
the client API is not very intuitive and requires a lot of extra
care when it is used from an osmo-fsm.

- Add an FSM that permits comfortable handling of an MGCP
  connection.

Change-Id: I887ce0c15a831dffeb6251a975337b83942af566
2018-02-08 16:22:45 +01:00
Philipp Maier
df5d219f39 mgcp: fix use-after-free and add callback for endpoint cleanup
Since we will support multiple different types of endpoints in the
future, all these endpoints will handle connections slightly different
and there will be possibly state that needs to be kept consistant
when a connection is deleted.

In mgcp_network.c where we implement the callback that is used to
create an rtp-bride-endpoint. In that callback we cache the pointer
of the connection we where we want to bride to (opposite connection).
When one of the connections is deleted using a DLCX operation, the
pointer is still there and the next incoming packet causes a use-
after-free segfault.

- introduce an endpoint specific callback function that is executed
  before removing the connection.

- implement the endpoint specific callback for rtp bridge endpoints,
  so that the use-after-free is prevented.

Change-Id: I921d9bbe58be1c3298e164a37f3c974880b3759f
2018-02-06 08:21:24 +00:00
Philipp Maier
5656fbf49d protocol: prohibit wildcarded requests for MDCX and DLCX
When a wildcarded request is made with a DLCX or MDCX command
the MGW will search for a free endpoint and continues the command
execution with that endpoint.

- Catch the wildcarded request early on DLCX and MDCX and return
  with an error code.

See also TTCN3 testcases:
MGCP_Test.TC_mdcx_wildcarded
MGCP_Test.TC_dlcx_wildcarded

Change-Id: Ia77d44a6a86083e62338e5845b553e5cf13ebd10
2018-02-06 08:21:24 +00:00
Harald Welte
19d640e806 Turn libosmo-mgcp into local, non-installed library
This is an internal library simmilar to 'libmsc' in osmo-msc, which
we don't expect to be used by other programs except osmo-mgw.  Hence,
there's no need to install it as a shared library, which introduces
requirements about ABI/API stability and the like.

osmo-bsc_nat uses libosmo-legacy-mgcp, and once we should rewrite
osmo-bsc_nat, we might need some of the libosmo-mgcp related functions,
but at this point it's unclear what exactly would be needed and if
current libosmo-mgcp can provide that.  As needed, we can introduce
a related shared library at that point.

Change-Id: Iba0a2c9c694e360356ac2ca584e97795281c6198
2018-02-05 22:41:32 +00:00
Philipp Maier
207ab51270 protocol: fix tagging of wildcarded requests
When a wildcarded CRCX is done flag "wildcarded_crcx" is set in the
endpoint struct. The flag tells other part of the code whether the
request was wildcarded or not since in some cases the behaviour
might be different for wildcarded requests. The implementation of
this mechanism is not entirely correct. The flag is set on wildcarded
requests but on non wildcarded requests it is not reset. Also the
name is misleading.

- rename wildcarded_crcx to wildcarded_req

- ensure the flag is refreshed with every new request

Change-Id: Ia5063ec65f5bc3a8a0943d1fd823aaeee20b8637
2018-02-05 10:32:42 +01:00
Philipp Maier
c3cc654add protocol: check requested connection mode
The connection mode setting (e.g. recvonly) is not checked on CRCX
and MDCX. This allows requests that set the connection mode to
sendrecv or sendonly without ever configuring the remote end of
the connection (half-open connection).

- reject sendrecv or sendonly on half open connections

See also TTCN3 Test:
MGCP_Test.TC_crcx_early_bidir_mode

Change-Id: I6ab01855d3b1faa36aebac357e7b97c563990678
Related: OS#2652
2018-02-05 10:32:42 +01:00
Philipp Maier
b911b879d8 cosmetic: Add missing \n on log line
The final log lone in find_endpoint() lacks the \n causing a messed
up log output.

- Add missing \n

Change-Id: I97fca654b199dfb7aae2359322a56c6d0bae9599
2018-02-05 10:32:42 +01:00
Philipp Maier
af07f66ca3 protocol: exit cleanly when local cx options check fails
When set_local_cx_options() returns an error code the MGCP command
execution is aborted and and the error code is returned, but on
CRCX the already seized eindpoint is not released.

- Do not generate the error response on the spot, jump to the
  respective label and let the already existing error handling
  do its work.

This patch is a follow-up page to:
Change-Id I02aaa3042f2a0e32eb4ec6b8753deab7082947a0

Change-Id: Iaef4ea6c6a2f24ac8b276966bda72d0b30f25cd5
Related: OS#2654
2018-02-05 10:32:42 +01:00
Philipp Maier
dd0c522cd4 protocol: reject DLCX/CRCX/MDCX on unsupported parameters
When an unsupported MGCP parameter (e.g. N) is used, then this
parameter is ignored and the command execution continues. However,
an MGCP command that contains an unsupported parameter should
be rejected.

- Make sure that MGCP commands DLCX, CRCX and MDCX are rejected,
  when they contain unsupported parameters.

Change-Id: I8cd5987fc6befcd53a7c4916f77b1a24c904ba48
2018-02-05 10:32:42 +01:00
Philipp Maier
37d11c80da cosmetic: rename mgcp_ep.c/h to mgcp_endp.c/h
The short term of endpoint has always been "endp" througout the whole
project and not "ep".

- rename mcgp_ep.c to mgcp_endp.c

- rename mgcp_ep.h to mgcp_endp.h

Change-Id: Id52047bb2d0407655ac272c858ed3412b8ae9e6d
2018-02-05 10:32:42 +01:00
Philipp Maier
1355d7e4f7 cosmetic: rename mgcp_release_endp to mgcp_endp_release
In order to allow clean prefixes for future endpoint related
functions the "rlease" should be moved to the end of the
function name.

- rename mgcp_release_endp to mgcp_endp_release

Change-Id: I22e938e702f57ad76d38c9f4a1370b110ac1ba11
2018-02-05 10:32:42 +01:00
Philipp Maier
fdd603c4c8 ep: move endpoint struct and define to mgcp_ep.h
The endpoint and the define that computes the endpoint number is
defined in mgcp_internal.h. Since we have a dedicated module for
endpoint related code it makes sense to move the endpoint related
parts there.

- move struct mgcp_endpoint to mgcp_ep.h

- move #define ENDPOINT_NUMBER(endp) to mgcp_ep.h

Change-Id: Ibae55e1859bd41e2d1918eda433418b4bf8365fe
2018-02-05 10:32:42 +01:00
Philipp Maier
1fc6999a77 client: use heap to store mgcp_response
The struct that holds the parsing results of the MGCP response is
allocated on the stack. However, it would make sense to allocate
the struct dynamically on the heap. This also would provide a
talloc context that is in reach on most places of the code.

- Allocate struct mgcp_response dynamically in mgcp_client_rx()

- Use struct mgcp_response as talloc context for temporary
  allocated memory while parsing the response.

Change-Id: I5099abe68b580c75b47bc797bf93f01084f0c4db
2018-02-05 10:32:42 +01:00
Harald Welte
220f487238 libosmo-mgcp-client is GPLv2+, not AGPLv3+
The client library should be usable to all GPLv2/v3/AGPLv2/v3 programs,
so like our general project practises, let's put it under
GPLv2-or-later.  I believe this was unintentional from the beginning.

Our general policy has been:
* libraries under GPLv2-or-later
* applications under AGPLv3-or-later

Change-Id: I29ed7edc510dba67d28b9247aecb4d6d5d25cc0c
2018-02-04 16:06:38 +00:00
Philipp Maier
edc00f4ea7 cosmetic: move mgcp_release_endp() to mgcp_ep.c
- move mgcp_release_endp() to mgcp_ep.c since it is clearly
  an endpoint sicific function.

Change-Id: I0a65b6e906c52a9e7cd75c88c4cbe1bf473b866b
2018-02-02 15:59:16 +01:00
Philipp Maier
a49e32a1ab msg: fix response code on exhausted endp resources
When all endpoints are seized and a call agent tries to allocate
another one, then 500 is returned as response code, which means
that the endpoint was not found. This does not match.

- Return 403 which is defined as "Insufficient resources available
  at this time"

Change-Id: Idf241b47e711e35e9f9b5a43f3cea5c0298ea30b
2018-02-01 18:50:48 +01:00
Philipp Maier
dd4ede34ad protocol: on wildcarded CRCX return endpoint number as hex
When a wildcarded CRCX is done, then the endpoint number is
returned as unsigned integer (%u). This results into problems
with endpoint numbers higher than 9.

- Return endpoint identifier with the endpoint number in
  hexadecimal representation

Change-Id: I504f4658c193009347753b15256dbb46b32ad5a4
2018-02-01 17:55:50 +01:00
Philipp Maier
3aa815755d client: prohibit endpoint ids without @ character
The function mgcp_msg_gen() does only check if the user supplied
an endpoint name or not. The user may still supply an endpoint
name that does not contain the separator (@) character.

- Refuse to generate the message if the endpoint name does not
  contain any @ character.

Change-Id: I92dd1556e4a26b4bef8e1c8c57141552abf988ca
2018-01-31 17:39:06 +01:00
Philipp Maier
3261dc7540 client: do not accept endpoint ids without @ character in responses
At the moment the client does not check if the endpoint identifier
that is returned from the MGW actually contains an @ character.

- Check if the endpoint id in the response contains an @ character.

Change-Id: I6073419a4b6cdcd31880672564f0861cb4bd02f5
2018-01-31 17:39:06 +01:00
Philipp Maier
771b26a043 client: Do not accept endpoint ids with wildcards in responses
When the client gets a specific endpoint identifier (Z) in a
MGCP response it just accepts the identifier even when it is
not specific (contsins wildcard characters). In those cases,
the client should refuse to parse the response.

- Check for wildcards in endpoint identifiers and stop
  parsing when check is positive.

Change-Id: Ic94bd8c025b7b3eb006b639fecfd7282194e504a
2018-01-31 17:39:06 +01:00
Philipp Maier
36a81129ec cosmetic: remove spaces from pointer symbol
Change-Id: Id778181a40638bce15c6f085841c35c1895d2602
2018-01-31 17:39:06 +01:00
Philipp Maier
a390d0ba52 protocol: check the packetization in local cx options
When the local connection options in an MDCX or CRCX request
are parsed, then the packetization interval is not checked.

- Check if the packetization is a multiple of 20ms

see also TTCN3 test: MGCP_Test.TC_crcx_unsupp_packet_intv

Change-Id: I02aaa3042f2a0e32eb4ec6b8753deab7082947a0
Related: OS#2654
2018-01-31 17:39:06 +01:00
Philipp Maier
b759473d10 client: fix sdp parameter ordering
The parameter ordering of the client responses does not match the
ordering as proposed by by RFC2327, Chapter 6. SDP Specification

- reorder generated SDP parameters so that they match RFC2327

Change-Id: I63cac2ebc982ffead92703c22bf68c7aafa7936c
2018-01-31 16:58:37 +01:00
Philipp Maier
c3cfae2cf8 protocol: fix missing carriage return
Some of the line breaks lack the \r character, which leads to an
inconsistancy. While our software and even wireshark does ignore
the problem, other third party implementations might reject those
messages.

- Add the missing \r characters to make the message format
  consistant.

Change-Id: I0cd80afae65accd3b4ddc5d82e5d30385879141c
2018-01-26 00:32:22 +00:00
Philipp Maier
3cbfb8a53c protocol: fix problem with line break and OSMUX
The SDP parameter block must be detached from the regular parameters
using an additional line break (empty line). At the moment this works
because the empty OSMOX variable is added and by this also adds a
line break. It breaks as soon as OSMUX is used again.

- Make clear that no OSMUX variable is added when OSMUX is not in
  use.

- Add the extra line break independently

Change-Id: I6261971040db527b96fe79676520ccd7794bd327
2018-01-26 00:32:18 +00:00
Philipp Maier
275ac97036 cosmetic: client: add doxygen comments
The client lacks doxygen apidoc comments

- Add missing doxygen apidoc comments

Change-Id: I0b8a0652e60f2b3d72ee1cedfa6e2d5547d88455
2018-01-26 00:32:14 +00:00
Philipp Maier
abe8c897fd client: eliminate destructive head parsing
While parsing the head of an MGCP response the r->body buffer is
manipulated in order to NUL terminate the extracted comment filed.

- Use a static buffer to store and manipulate the comment field.

Change-Id: Ib273c13d6fe7ee042fb4e3b8ed46ac02602226f6
2018-01-26 00:32:09 +00:00
Philipp Maier
e9d645b3b3 client: eliminate destructive parameter parsing
The function mgcp_response_parse_params() that is used to parse the
SDP parameters edits the content of the r->body.

- Create a local copy of r->body and work on this copy to keep
  the original r-body in its original state.

Change-Id: Ia475036f7f3802b1638e0511a5e9162fea1592eb
2018-01-26 00:32:04 +00:00
Philipp Maier
7f0966c13d mgcp: add prefix to virtual trunk
the virtual trunk is addressed without a prefix (just *@domain).

- reorganize find_endpoint() so that it accepts a prefix when
  addressing the virtual trunk.

- do no longer accept wildcarded CRCX requests without prefix
  (will not break anything, the feature of wildcarded CRCX is
  not in use yet)

- keep the old prefix-less method but print a warning that it is
  depreacted.

Change-Id: I2aac3ba0f1f3122dfbb3bf36f74198ecb2b21de5
2018-01-26 00:31:51 +00:00
Philipp Maier
55295f7b07 mgcp: permit wildcarded endpoint assignment (CRCX)
The mgcp protocol in general allows wildcarded endpoints on CRCX.
osmo-mgw does not support this feature yet.

- when the endpoint name contains a wildcard character, search
  a free endpoint automatically

- return the resulting endpoint name in the parameter section of
  the mgcp response

- add parsing support for the returned endpoint names

- Be more concious about the parameters that are returned with
  each response. Do not unnecessarily attach known parameters.
  Return the connection ID only on CRCX commands. Only return
  the endpoint ID on CRCX commands that are wildcarded.

Change-Id: Iebc95043569191b6f5fbc8fe266b13fcfcab2e48
related: OS#2631
2018-01-26 00:31:22 +00:00
Philipp Maier
9d25d7a2e6 client: add missing mandatory SDP fields
The mcgp message generator function mgcp_msg_gen() lacks support
for the mandatory SDP fields (v)ersion, (o)rigin, (s)ession and
(t)ime.

- Automatically generate the missing fields when SDP is
  generated.

Change-Id: I5fbc31a17e8ac10c7cc5dbc31357b61e8920aaa5
Related: OS#2837
2018-01-26 00:27:34 +00:00
Philipp Maier
490cbaa89e client: make callid in MDCX mandatory
An MDCX without call-id does not make much sense. The call-id is
an integral element of the MDCX message to ensure that the correct
call is modified.

- update the presence check bitmasks to mark the call-id field
  mandatory for MDCX requests

Change-Id: Id2bcc3a68139e0d935790bcea2ef91eaf6291aa3
2018-01-22 17:35:50 +01:00
Philipp Maier
eb0bde09bb main: display mgcp ip/port
osmo-mgw does not display the IP/Port on which it is listening for
MGCP commands. However, this information can be very helpful when
working with multiple MGCP instances on one machine.

- print IP/Port on which we listen for MGCP commands on startup

Change-Id: Idf5e8b6a7344c4ebaf9b89940456a496b2c23334
2018-01-19 18:03:58 +00:00
Philipp Maier
3b12e1b011 client: do not insist on \n\n when parsing MGCP messages
The current implementation of mgcp_client.c requires MGCP
paragraphs to be separated wit a \n\n sequence. However,
when the client is used with servers other than osmo-mgcp,
the parapgraph may be formatted differently.

Also allow \n\r\n\r and \r\n\r\n as separator

Change-Id: Ie209fb71499e011e52f58575c6af118de2fdee88
2018-01-19 18:03:38 +00:00
Philipp Maier
2138779559 cosmetic: protocol: remove unnecessary nul termination
Adding a NUL manually is a common idiom after calling strncpy() because
strncpy() does not always NUL-terminate the string. But snprintf() is
fine.

- remove NUL termination after snprintf in mgcp_send_reset_ep()

Change-Id: I5a1187b13b21b11674f13d3449c730616b0a4ddf
2018-01-19 10:35:12 +01:00
Philipp Maier
12943ea0c1 mgcp: make domain name configurable
At the moment the MGW has a fixed domain name string that is not even
checked properly.

- Make domain name configurable, use the current "mgw" string as
  defualt to maintain compatibility

- Check the domain name with each request. If the endpoint contains
  an unexpected domain name, the request must be rejected.

Change-Id: Ia91ac428ba83ac1f9b52a0ec8dbf00ef7876da9e
2018-01-19 10:15:47 +01:00
Philipp Maier
6efb43a210 client/common: move constant MGCP_ENDPOINT_MAXLEN
MGCP_ENDPOINT_MAXLEN is currently only defined for the mgcp client,
since this is in general a common parameter it should be moved to
mgcp_common.h so that both sides can use it.

Change-Id: I9e1c52aa5ebd83b2d9e5178ea24cb27d96cb7ddd
2018-01-18 18:26:52 +01:00
Philipp Maier
03cc48474c mgcp: allow endpoints beginning from zero
there is a now obsolete constraint that endpoint numbers must
start at 1.

- remove the check to allow also endpoints starting at 0

Change-Id: Iec2f4e36e1ab01ff23875d99e4b0e04af7c1ad98
2018-01-18 18:26:31 +01:00
Neels Hofmeyr
5672563f7e cosmetic: mgcp_network: typo in log
Change-Id: Ia7675e52fe1082d21bd68dcf54fe34c0f0326f11
2018-01-17 10:51:09 +00:00
Harald Welte
106743c66b osmo-bsc_mgcp: Add LIBOSMONETIF_{CFLAGS,LIBS}
The osmo-mgw gerrit build is currently failing with the following error:

make[3]: Entering directory '/build/src/osmo-bsc_mgcp'
  CC       mgcp_main.o
In file included from ../../include/osmocom/legacy_mgcp/mgcp_internal.h:146:0,
                 from mgcp_main.c:36:
../../include/osmocom/legacy_mgcp/osmux.h:4:33: fatal error: osmocom/netif/osmux.h: No such file or directory
 #include <osmocom/netif/osmux.h>
                                 ^
compilation terminated.

Let's make sure we're adding the required flags/libs for libosmonetif dependency

Related: OS#2829
Change-Id: I402314532590498a6340dc14101a32b605cd5e09
2018-01-15 18:59:29 +01:00
Harald Welte
8890dfa634 osmo-mgw: Use libosmocore socket abstraction
There's no need for us to use the sockets API directly:  We have
pretty nice socket helper functions in libosmocore, let's make
use of them.

Change-Id: I39d47b8a27f683060a2facf2dbecff8d00c19ce9
2018-01-08 12:52:18 +00:00
Philipp Maier
10f32dbfbf client: mgcp_response_parse_params: check rtp port
Also check the port number for plausibility like we do it
already for the IP-Address

Change-Id: I594a06fc9dd1bf0522f6e72a8943df52448d2ce4
2018-01-04 10:57:04 +00:00
Harald Welte
e35eeae8e0 Return proper MGCP Error codes, as per spec
Change-Id: I55db8351422ff951516fefa6a29e87086b7ab74b
Closes: OS#2657, OS#2656
2017-12-28 14:00:42 +01:00
Harald Welte
abbb6b9088 centralize handling of common errors like "endpoint not found"
Previously we
* did not distinguish between the cause of errors in mgcp_header_parse
* common errors were not handled in mgcp_handle_message() but in
  individual per-verb functions

Let's centralize the handling of generating error responses and remove
that burden from the individual per-verb handler functions.

Change-Id: I463b27306e10ae3b021583ed102977e7299e5e66
2017-12-28 14:00:37 +01:00
Harald Welte
3f35a3708a mgcp_internal.h: document more struct members with comments
Change-Id: Idfba05de37d354f9485030f37dfc9c780eff7b35
2017-12-28 03:17:10 +01:00
Harald Welte
33eafe050b mgcp_msg: We must parse endpoint numbers as hex, not decimal!
The MGCP client uses hex numbers, while the server side parses it
as decimal. This results in the first 10 calls succeeding, but from
0x0a onwards it of course fails :/

Change-Id: I006f5f5325f0a5069d02fec8912a38d943cfc552
Closes: OS#2784
2017-12-28 03:17:10 +01:00
Harald Welte
1d1b98f4a4 libosmo-mgcp: Cosmetic spelling fixes in comments
Change-Id: Ic0469c2a4d69b55a6a90653ad7ea1ad89a34e4bc
2017-12-25 10:10:29 +01:00
Harald Welte
a0ac30faa5 mgcp_rtp_end: Group statistics members into 'stats' sub-struct
Change-Id: I4e0ecdcd9a75fe08abc55b730780c01617f3d4af
2017-12-25 10:10:29 +01:00
Harald Welte
49e3d5a9c9 mgcp_rtp_state: grup 'stats' members into sub-structure
Change-Id: I92a1bead01c6b85bf237b6edf64a1b76b9e97c78
2017-12-25 10:10:29 +01:00
Harald Welte
3338135ead strct mgcp_rtp_state: Group + document struct members related to patching
Change-Id: I403ddcafe0a11290103bb017568cfacaf5c04412
2017-12-25 10:10:29 +01:00
50 changed files with 2002 additions and 560 deletions

View File

@@ -20,7 +20,6 @@ pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = \
libosmo-legacy-mgcp.pc \
libosmo-mgcp-client.pc \
libosmo-mgcp.pc \
$(NULL)
BUILT_SOURCES = $(top_srcdir)/.version

View File

@@ -24,6 +24,3 @@
# If any interfaces have been removed or changed since the last public release, a=0.
#
#library what description / commit summary line
libosmo-mgcp API/ABI change parse and represent connection identifiers as hex strings
libosmo-mgcp API/ABI change connection identifiers are assigned by the server, not CA
libosmo-mgcp-client API/ABI change parse and store connection identifier in response

View File

@@ -39,10 +39,10 @@ AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DL)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.10.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.10.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.10.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.1.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.11.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.2.0)
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
@@ -56,6 +56,24 @@ then
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
fi
AC_ARG_ENABLE(werror,
[AS_HELP_STRING(
[--enable-werror],
[Turn all compiler warnings into errors, with exceptions:
a) deprecation (allow upstream to mark deprecation without breaking builds);
b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
]
)],
[werror=$enableval], [werror="no"])
if test x"$werror" = x"yes"
then
WERROR_FLAGS="-Werror"
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
CFLAGS="$CFLAGS $WERROR_FLAGS"
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
fi
# Enable/disable transcoding within osmo-bsc_mgcp?
AC_ARG_ENABLE([mgcp-transcoding], [AS_HELP_STRING([--enable-mgcp-transcoding], [Build the MGCP gateway with internal transcoding enabled.])],
[osmo_ac_mgcp_transcoding="$enableval"],[osmo_ac_mgcp_transcoding="no"])
@@ -126,13 +144,15 @@ AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
AC_MSG_RESULT([$enable_ext_tests])
AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes")
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
dnl Generate the output
AM_CONFIG_HEADER(bscconfig.h)
AC_OUTPUT(
libosmo-legacy-mgcp.pc
libosmo-mgcp-client.pc
libosmo-mgcp.pc
include/Makefile
include/osmocom/Makefile
include/osmocom/legacy_mgcp/Makefile

View File

@@ -37,7 +37,7 @@ set -x
cd "$base"
autoreconf --install --force
./configure $MGCP --enable-vty-tests --enable-external-tests
./configure $MGCP --enable-vty-tests --enable-external-tests --enable-werror
$MAKE $PARALLEL_MAKE
LD_LIBRARY_PATH="$inst/lib" $MAKE check \
|| cat-testlogs.sh

125
debian/changelog vendored
View File

@@ -1,3 +1,128 @@
osmo-mgw (1.3.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* contrib: Add osmo-mgw systemd service
* legacy: mgcp_protocol: Don't print osmux stats if it is off
* mgcp_stat: Don't print osmux stats if it is off
[ Neels Hofmeyr ]
* fix segfault: DLCX for unknown endpoint: dont try to log NULL endpoint
* MGCP endpoints: parse as decimal, not hex
* add --enable-sanitize config option
* legacy_mgcp: mgcp_test: sanitize: free msgb_ctx
* mgcp_test: test_packet_error_detection: sanitize: free all conns
* mgcp_test: test_no_cycle: sanitize: free endp
* mgcp_test: sanitize: free msgb_ctx
* mgcp_client: don't configure "bts base"
* Revert "mgcp_client: don't configure "bts base"" until osmo-msc is ready
* mgcp_client: add transaction cleanup
* mgcp_client_test makefile: add update_exp target
* cosmetic: mgcp_network: typo in log
* osmo-mgw: Add talloc context introspection via VTY
* mgcp_client: show failure by MGCP SDP section parsing test
* mgcp_client: cosmetic: clean up SDP params parsing
* mgcp_client: detect SDP section-start parsing errors
* compiler warning: ignore deprecated in mgcp_client_test.c
* configure: add --enable-werror
* jenkins.sh: add --enable-werror to configure flags
* cosmetic: mgcp, legacy_mgcp: drop unused vty.h definitions
* use osmo_init_logging2() with proper talloc ctx
[ Philipp Maier ]
* osmux: fix nullpointer dereference
* cosmetic: guard dead osmux vty code with ifdef
* cosmetic: remove prefix "net" from rtp related vty commands
* doc: update sample config file
* cosmetic: use correct VTY port number constant
* vty: simplify endpoint allocation
* vty: do not change number_endpoints at runtime
* MGCP: Connection Identifiers are hex strings
* libosmo-mgcp: Connection Identifiers are allocated by MGW, not CA
* client: use osmo_strlcpy instead of strncpy
* cosmetic: fix sourcecode formatting
* cosmetic: clearly mark endpoint numbers as hex
* client: use string as connection identifier
* conn: remove assertions
* mgcp_test: fix wrong strcmp() parameters
* mgcp_test: fix nullpointer dereference
* mgcp_test: add returncode check
* mgcp_test: fix possible double free
* mcgp_client: mgcp_msg_gen(): add checks to verify params
* network: use originating RTP packet address for loopback
* client: mgcp_response_parse_params: check rtp port
* mgcp: allow endpoints beginning from zero
* client/common: move constant MGCP_ENDPOINT_MAXLEN
* mgcp: make domain name configurable
* cosmetic: protocol: remove unnecessary nul termination
* client: do not insist on \n\n when parsing MGCP messages
* main: display mgcp ip/port
* client: make callid in MDCX mandatory
* client: add missing mandatory SDP fields
* mgcp: permit wildcarded endpoint assignment (CRCX)
* mgcp: add prefix to virtual trunk
* client: eliminate destructive parameter parsing
* client: eliminate destructive head parsing
* cosmetic: client: add doxygen comments
* protocol: fix problem with line break and OSMUX
* protocol: fix missing carriage return
* client: fix sdp parameter ordering
* protocol: check the packetization in local cx options
* cosmetic: remove spaces from pointer symbol
* client: Do not accept endpoint ids with wildcards in responses
* client: do not accept endpoint ids without @ character in responses
* client: prohibit endpoint ids without @ character
* protocol: on wildcarded CRCX return endpoint number as hex
* msg: fix response code on exhausted endp resources
* cosmetic: move mgcp_release_endp() to mgcp_ep.c
* client: use heap to store mgcp_response
* ep: move endpoint struct and define to mgcp_ep.h
* cosmetic: rename mgcp_release_endp to mgcp_endp_release
* cosmetic: rename mgcp_ep.c/h to mgcp_endp.c/h
* protocol: reject DLCX/CRCX/MDCX on unsupported parameters
* protocol: exit cleanly when local cx options check fails
* cosmetic: Add missing \n on log line
* protocol: check requested connection mode
* protocol: fix tagging of wildcarded requests
* protocol: prohibit wildcarded requests for MDCX and DLCX
* mgcp: fix use-after-free and add callback for endpoint cleanup
* client: add an optional FSM interface
* mgcp_client_fsm: Add FSM event names
* cosmetic: mgcp_client_fsm: rename enums
* cosmetic: rename function .._conn_reset() to .._conn_init()
* mgcp_conn: do not touch u.rtp in mgcp_conn_alloc()
* cosmetic: rename .._codec_reset() to .._codec_init()
* mgcp_conn: add function mgcp_rtp_conn_cleanup()
* stats: use libosmocore rate counter for in/out_stream.err_ts_counter
[ Alexander Couzens ]
* debian/control: correct library dependency of osmo-mgw against libosmo-mgcp1
* debian: include systemd service osmo-mgw.service
* Revert "stats: use libosmocore rate counter for in/out_stream.err_ts_counter"
[ Harald Welte ]
* cosmetic: fix whitespaces; we use tabs for indentation
* Fix possible buffer overflow in mgcp_conn_dump()
* osmo-mgw: Update copyright statement
* osmo-mgw: Config file is osmo-mgw.cfg, and not mgcp.cfg
* osmo-mgw: Fix copyright notice
* strct mgcp_rtp_state: Group + document struct members related to patching
* mgcp_rtp_state: grup 'stats' members into sub-structure
* mgcp_rtp_end: Group statistics members into 'stats' sub-struct
* libosmo-mgcp: Cosmetic spelling fixes in comments
* mgcp_msg: We must parse endpoint numbers as hex, not decimal!
* mgcp_internal.h: document more struct members with comments
* centralize handling of common errors like "endpoint not found"
* Return proper MGCP Error codes, as per spec
* osmo-mgw: Use libosmocore socket abstraction
* osmo-bsc_mgcp: Add LIBOSMONETIF_{CFLAGS,LIBS}
* libosmo-mgcp-client is GPLv2+, not AGPLv3+
* Turn libosmo-mgcp into local, non-installed library
[ Stefan Sperling ]
* enable osmo_fsm vty commands in libosmo-mgcp-client vty
-- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 03 May 2018 17:40:35 +0200
osmo-mgw (1.2.0) unstable; urgency=medium
[ Neels Hofmeyr ]

21
debian/control vendored
View File

@@ -16,25 +16,10 @@ Homepage: https://osmocom.org/projects/osmo-mgw
Package: osmo-mgw
Architecture: any
Multi-Arch: foreign
Depends: libosmo-mgcp1, ${misc:Depends}, ${shlibs:Depends}
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
Package: libosmo-mgcp1
Section: libs
Architecture: any
Multi-Arch: same
Pre-Depends: ${misc:Pre-Depends}
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: libosmo-mgcp: Osmocom's Media Gateway server library
Package: libosmo-mgcp-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libosmo-mgcp1 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-mgcp: Osmocom's Media Gateway server library
Package: libosmo-mgcp-client2
Package: libosmo-mgcp-client3
Section: libs
Architecture: any
Multi-Arch: same
@@ -46,7 +31,7 @@ Package: libosmo-mgcp-client-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libosmo-mgcp-client2 (= ${binary:Version}), ${misc:Depends}
Depends: libosmo-mgcp-client3 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
Package: osmo-bsc-mgcp

19
debian/copyright vendored
View File

@@ -21,6 +21,25 @@ License: AGPL-3.0+
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/>.
Files: src/libosmo-mgcp-client/* include/osmocom/mgcp_client/*
Copyright: 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
2009 by Harald Welte <laforge@gnumonks.org>
2009-2011 by Holger Hans Peter Freyther
License: GPL-2.0+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Files: src/libosmo-legacy-mgcp/g711common.h
Copyright: 2000 Abramo Bagnara <abramo@alsa-project.org>
License: GPL-2.0+

View File

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

View File

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

View File

@@ -7,6 +7,7 @@ nobase_include_HEADERS = \
osmocom/legacy_mgcp/mgcp_internal.h \
osmocom/legacy_mgcp/osmux.h \
osmocom/mgcp_client/mgcp_client.h \
osmocom/mgcp_client/mgcp_client_fsm.h \
osmocom/mgcp_client/mgcp_common.h \
osmocom/mgcp/mgcp.h \
osmocom/mgcp/mgcp_common.h \

View File

@@ -1,31 +1,8 @@
#ifndef OPENBSC_VTY_H
#define OPENBSC_VTY_H
#pragma once
#include <osmocom/vty/vty.h>
#include <osmocom/vty/buffer.h>
#include <osmocom/vty/command.h>
struct gsm_network;
struct vty;
void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *);
struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base);
extern struct cmd_element cfg_description_cmd;
extern struct cmd_element cfg_no_description_cmd;
enum mgcp_vty_node {
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
TRUNK_NODE,
};
struct log_info;
int bsc_vty_init(struct gsm_network *network);
int bsc_vty_init_extra(void);
void msc_vty_init(struct gsm_network *msc_network);
struct gsm_network *gsmnet_from_vty(struct vty *vty);
#endif

View File

@@ -3,7 +3,7 @@ noinst_HEADERS = \
mgcp_msg.h \
mgcp_conn.h \
mgcp_stat.h \
mgcp_ep.h \
mgcp_endp.h \
mgcp_sdp.h \
debug.h \
$(NULL)

View File

@@ -105,12 +105,12 @@ struct mgcp_port_range {
};
/* There are up to three modes in which the keep-alive dummy packet can be
* sent. The beviour is controlled viw the keepalive_interval member of the
* sent. The behaviour is controlled via the keepalive_interval member of the
* trunk config. If that member is set to 0 (MGCP_KEEPALIVE_NEVER) no dummy-
* packet is sent at all and the timer that sends regular dummy packets
* is no longer scheduled. If the keepalive_interval is set to -1, only
* one dummy packet is sent when an CRCX or an MDCX is performed. No timer
* is scheduled. For all vales greater 0, the a timer is scheduled and the
* is scheduled. For all vales greater 0, the timer is scheduled and the
* value is used as interval. See also mgcp_keepalive_timer_cb(),
* handle_modify_con(), and handle_create_con() */
#define MGCP_KEEPALIVE_ONCE (-1)
@@ -212,6 +212,8 @@ struct mgcp_config {
* message.
*/
uint16_t osmux_dummy;
/* domain name of the media gateway */
char domain[255+1];
};
/* config management */
@@ -220,7 +222,6 @@ 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_release_endp(struct mgcp_endpoint *endp);
void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
/*

View File

@@ -68,8 +68,18 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg)
return 0;
}
/* Maximum length of the comment field */
#define MGCP_COMMENT_MAXLEN 256
/* String length of Connection Identifiers
* (see also RFC3435 2.1.3.2 Names of Connections) */
#define MGCP_CONN_ID_LENGTH 32+1
/* String length of Endpoint Identifiers.
/ (see also RFC3435 section 3.2.1.3) */
#define MGCP_ENDPOINT_MAXLEN (255*2+1+1)
/* A prefix to denote the virtual trunk (RTP on both ends) */
#define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/"
#endif

View File

@@ -0,0 +1,99 @@
/* Endpoint types */
/*
* (C) 2017 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/>.
*
*/
#pragma once
struct sockaddr_in;
struct mgcp_conn;
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);
/* Callback type for endpoint specific cleanup actions. This function
* is automatically executed when a connection is freed (see mgcp_conn_free()
* in mgcp_conn.c). Depending on the type of the endpoint there may be endpoint
* specific things to take care of once a connection has been removed. */
typedef void (*mgcp_cleanup_cp) (struct mgcp_endpoint *endp,
struct mgcp_conn *conn);
/*! MGCP endpoint properties */
struct mgcp_endpoint_type {
/*!< maximum number of connections */
int max_conns;
/*!< callback that defines how to dispatch incoming RTP data */
mgcp_dispatch_rtp_cb dispatch_rtp_cb;
/*!< callback that implements endpoint specific cleanup actions */
mgcp_cleanup_cp cleanup_cb;
};
/*! MGCP endpoint typeset */
struct mgcp_endpoint_typeset {
struct mgcp_endpoint_type rtp;
};
/*! static MGCP endpoint typeset (pre-initalized, read-only) */
extern const struct mgcp_endpoint_typeset ep_typeset;
/*! MGCP endpoint model */
struct mgcp_endpoint {
/*!< Call identifier string (as supplied by the call agant) */
char *callid;
/*!< Local connection options (see mgcp_intermal.h) */
struct mgcp_lco local_options;
/*!< List with connections active on this endpoint */
struct llist_head conns;
/*!< Backpointer to the MGW configuration */
struct mgcp_config *cfg;
/*!< Backpointer to the Trunk specific configuration */
struct mgcp_trunk_config *tcfg;
/*!< Endpoint properties (see above) */
const struct mgcp_endpoint_type *type;
/*!< Last MGCP transmission (in case re-transmission is required) */
char *last_trans;
/*!< Last MGCP response (in case re-transmission is required) */
char *last_response;
/*!< Memorize if this endpoint was choosen by the MGW (wildcarded, true)
* or if the user has choosen the particular endpoint explicitly. */
bool wildcarded_req;
};
/*! Extract endpoint number for a given endpoint */
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
void mgcp_endp_release(struct mgcp_endpoint *endp);

View File

@@ -1,50 +0,0 @@
/* Endpoint types */
/*
* (C) 2017 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/>.
*
*/
#pragma once
struct sockaddr_in;
struct mgcp_conn;
/* 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);
/*! MGCP endpoint properties */
struct mgcp_endpoint_type {
/*!< maximum number of connections */
int max_conns;
/*!< callback that defines how to dispatch incoming RTP data */
mgcp_dispatch_rtp_cb dispatch_rtp_cb;
};
/*! MGCP endpoint typeset */
struct mgcp_endpoint_typeset {
struct mgcp_endpoint_type rtp;
};
/*! static MGCP endpoint typeset (pre-initalized, read-only) */
extern const struct mgcp_endpoint_typeset ep_typeset;

View File

@@ -27,6 +27,7 @@
#include <osmocom/core/select.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/counter.h>
#define CI_UNUSED 0
@@ -50,27 +51,37 @@ struct mgcp_rtp_stream_state {
};
struct mgcp_rtp_state {
/* has this state structure been initialized? */
int initialized;
int patch_ssrc;
uint32_t orig_ssrc;
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;
int seq_offset;
int32_t timestamp_offset;
/* 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 */
int stats_initialized;
uint16_t stats_base_seq;
uint16_t stats_max_seq;
uint32_t stats_ssrc;
uint32_t stats_jitter;
int32_t stats_transit;
int stats_cycles;
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 */
};
@@ -85,13 +96,18 @@ struct mgcp_rtp_codec {
char *subtype_name;
};
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
struct mgcp_rtp_end {
/* statistics */
unsigned int packets_rx;
unsigned int octets_rx;
unsigned int packets_tx;
unsigned int octets_tx;
unsigned int dropped_packets;
struct {
unsigned int packets_rx;
unsigned int octets_rx;
unsigned int packets_tx;
unsigned int octets_tx;
unsigned int dropped_packets;
} stats;
/* local IP address of the RTP socket */
struct in_addr addr;
/* in network byte order */
@@ -105,23 +121,30 @@ struct mgcp_rtp_end {
int frames_per_packet;
uint32_t packet_duration_ms;
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;
/* FIXME: not used anymore, used to be [external] transcoding related */
void *rtp_process_data;
/* 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;
};
@@ -157,7 +180,7 @@ struct mgcp_conn_rtp {
/* Sequence bits */
struct mgcp_rtp_state state;
/* taps for the rtp connection */
/* taps for the rtp connection; one per direction */
struct mgcp_rtp_tap tap_in;
struct mgcp_rtp_tap tap_out;
@@ -204,7 +227,7 @@ struct mgcp_conn {
/*!< copy of the mode to restore the original setting (VTY) */
enum mgcp_connection_mode mode_orig;
/*!< connection id to identify the conntion */
/*!< connection id to identify the connection */
char id[MGCP_CONN_ID_LENGTH];
/*!< human readable name (vty, logging) */
@@ -223,25 +246,9 @@ struct mgcp_conn {
struct mgcp_endpoint_type;
struct mgcp_endpoint {
char *callid;
struct mgcp_lco local_options;
struct llist_head conns;
/* backpointer */
struct mgcp_config *cfg;
struct mgcp_trunk_config *tcfg;
const struct mgcp_endpoint_type *type;
/* fields for re-transmission */
char *last_trans;
char *last_response;
};
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
/**
* Internal structure while parsing a request
@@ -251,7 +258,6 @@ struct mgcp_parse_data {
struct mgcp_endpoint *endp;
char *trans;
char *save;
int found;
};
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
@@ -260,6 +266,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
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);

View File

@@ -1,31 +1,8 @@
#ifndef OPENBSC_VTY_H
#define OPENBSC_VTY_H
#pragma once
#include <osmocom/vty/vty.h>
#include <osmocom/vty/buffer.h>
#include <osmocom/vty/command.h>
struct gsm_network;
struct vty;
void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *);
struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base);
extern struct cmd_element cfg_description_cmd;
extern struct cmd_element cfg_no_description_cmd;
enum mgcp_vty_node {
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
TRUNK_NODE,
};
struct log_info;
int bsc_vty_init(struct gsm_network *network);
int bsc_vty_init_extra(void);
void msc_vty_init(struct gsm_network *msc_network);
struct gsm_network *gsmnet_from_vty(struct vty *vty);
#endif

View File

@@ -29,8 +29,9 @@ typedef unsigned int mgcp_trans_id_t;
struct mgcp_response_head {
int response_code;
mgcp_trans_id_t trans_id;
const char *comment;
char comment[MGCP_COMMENT_MAXLEN];
char conn_id[MGCP_CONN_ID_LENGTH];
char endpoint[MGCP_ENDPOINT_MAXLEN];
};
struct mgcp_response {
@@ -55,9 +56,6 @@ enum mgcp_verb {
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
/* See also RFC3435 section 3.2.1.3 */
#define MGCP_ENDPOINT_MAXLEN (255*2+1+1)
struct mgcp_msg {
enum mgcp_verb verb;
/* See MGCP_MSG_PRESENCE_* constants */

View File

@@ -0,0 +1,33 @@
#pragma once
#include <osmocom/mgcp_client/mgcp_common.h>
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
/*! This struct organizes the connection infromation one connection side
* (either remote or local). It is used to pass parameters (local) to the FSM
* and get responses (remote) from the FSM as pointer attached to the FSM
* event.
*
* When modifiying a connection, the endpoint and call_id members may be left
* unpopulated. The call_id field is ignored in this case. If an endpoint
* identifier is supplied it is checked against the internal state to make
* sure it is correct. */
struct mgcp_conn_peer {
/*!< RTP connection IP-Address (optional, string e.g. "127.0.0.1") */
char addr[INET_ADDRSTRLEN];
/*!< RTP connection IP-Port (optional) */
uint16_t port;
/*!< RTP endpoint */
char endpoint[MGCP_ENDPOINT_MAXLEN];
/*!< CALL ID (unique per connection) */
unsigned int call_id;
};
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
void mgcp_conn_delete(struct osmo_fsm_inst *fi);

View File

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

View File

@@ -24,7 +24,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!
LEGACY_MGCP_LIBVERSION=0:0:0
LEGACY_MGCP_LIBVERSION=0:1:0
lib_LTLIBRARIES = \
libosmo-legacy-mgcp.la \

View File

@@ -1588,24 +1588,26 @@ void mgcp_format_stats(struct mgcp_endpoint *endp, char *msg, size_t size)
msg += nchars;
size -= nchars;
/* Error Counter */
nchars = snprintf(msg, size,
"\r\nX-Osmo-CP: EC TIS=%u, TOS=%u, TIR=%u, TOR=%u",
endp->net_state.in_stream.err_ts_counter,
endp->net_state.out_stream.err_ts_counter,
endp->bts_state.in_stream.err_ts_counter,
endp->bts_state.out_stream.err_ts_counter);
if (nchars < 0 || nchars >= size)
goto truncate;
if (endp->cfg->osmux != OSMUX_USAGE_OFF) {
/* Error Counter */
nchars = snprintf(msg, size,
"\r\nX-Osmo-CP: EC TIS=%u, TOS=%u, TIR=%u, TOR=%u",
endp->net_state.in_stream.err_ts_counter,
endp->net_state.out_stream.err_ts_counter,
endp->bts_state.in_stream.err_ts_counter,
endp->bts_state.out_stream.err_ts_counter);
if (nchars < 0 || nchars >= size)
goto truncate;
msg += nchars;
size -= nchars;
msg += nchars;
size -= nchars;
if (endp->osmux.state == OSMUX_STATE_ENABLED) {
snprintf(msg, size,
"\r\nX-Osmux-ST: CR=%u, BR=%u",
endp->osmux.stats.chunks,
endp->osmux.stats.octets);
if (endp->osmux.state == OSMUX_STATE_ENABLED) {
snprintf(msg, size,
"\r\nX-Osmux-ST: CR=%u, BR=%u",
endp->osmux.stats.chunks,
endp->osmux.stats.octets);
}
}
truncate:
msg[size - 1] = '\0';

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=2:0:0
MGCP_CLIENT_LIBVERSION=3:0:0
lib_LTLIBRARIES = \
libosmo-mgcp-client.la \
@@ -29,6 +29,7 @@ lib_LTLIBRARIES = \
libosmo_mgcp_client_la_SOURCES = \
mgcp_client.c \
mgcp_client_vty.c \
mgcp_client_fsm.c \
$(NULL)
libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION)

View File

@@ -4,16 +4,16 @@
* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -36,6 +36,8 @@
#include <unistd.h>
#include <string.h>
/*! Initalize MGCP client configuration struct with default values.
* \param[out] conf Client configuration.*/
void mgcp_client_conf_init(struct mgcp_client_conf *conf)
{
/* NULL and -1 default to MGCP_CLIENT_*_DEFAULT values */
@@ -62,7 +64,9 @@ static bool endpoint_in_use(uint16_t id, struct mgcp_client *client)
return false;
}
/* Find and seize an unsused endpoint id */
/*! Pick next free endpoint ID.
* \param[in,out] client MGCP client descriptor.
* \returns 0 on success, -EINVAL on error. */
int mgcp_client_next_endpoint(struct mgcp_client *client)
{
int i;
@@ -95,6 +99,9 @@ int mgcp_client_next_endpoint(struct mgcp_client *client)
return -EINVAL;
}
/*! Release a seized endpoint ID to make it available again for other calls.
* \param[in] id Endpoint ID
* \param[in,out] client MGCP client descriptor. */
/* Release a seized endpoint id to make it available again for other calls */
void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client)
{
@@ -139,15 +146,12 @@ static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg)
&comment_pos) != 2)
goto response_parse_failure;
r->head.comment = r->body + comment_pos;
osmo_strlcpy(r->head.comment, r->body + comment_pos, sizeof(r->head.comment));
end = strchr(r->head.comment, '\r');
if (!end)
goto response_parse_failure;
/* Mark the end of the comment */
*end = '\0';
r->body = end + 1;
if (r->body[0] == '\n')
r->body ++;
return 0;
response_parse_failure:
@@ -181,6 +185,9 @@ static int mgcp_parse_audio_port(struct mgcp_response *r, const char *line)
&r->audio_port) != 1)
goto response_parse_failure;
if (r->audio_port == 0)
goto response_parse_failure;
return 0;
response_parse_failure:
@@ -216,26 +223,56 @@ response_parse_failure:
return -EINVAL;
}
/* A new section is marked by a double line break, check a few more
* patterns as there may be variants */
static char *mgcp_find_section_end(char *string)
{
char *rc;
rc = strstr(string, "\n\n");
if (rc)
return rc;
rc = strstr(string, "\n\r\n\r");
if (rc)
return rc;
rc = strstr(string, "\r\n\r\n");
if (rc)
return rc;
return NULL;
}
/*! Parse body (SDP) parameters of the MGCP response
* \param[in,out] r Response data
* \returns 0 on success, -EINVAL on error. */
int mgcp_response_parse_params(struct mgcp_response *r)
{
char *line;
int rc;
OSMO_ASSERT(r->body);
char *data = strstr(r->body, "\n\n");
char *data;
char *data_ptr;
if (!data) {
/* Since this functions performs a destructive parsing, we create a
* local copy of the body data */
OSMO_ASSERT(r->body);
data = talloc_strdup(r, r->body);
OSMO_ASSERT(data);
/* Find beginning of the parameter (SDP) section */
data_ptr = mgcp_find_section_end(data);
if (!data_ptr) {
LOGP(DLMGCP, LOGL_ERROR,
"MGCP response: cannot find start of parameters\n");
return -EINVAL;
"MGCP response: cannot find start of SDP parameters\n");
rc = -EINVAL;
goto exit;
}
/* Advance to after the \n\n, replace the second \n with \0. That's
* where the parameters start. */
data ++;
*data = '\0';
data ++;
/* data_ptr now points to the beginning of the section-end-marker; for_each_non_empty_line()
* skips any \r and \n characters for free, so we don't need to skip the marker. */
for_each_non_empty_line(line, data) {
for_each_non_empty_line(line, data_ptr) {
if (!mgcp_line_is_valid(line))
return -EINVAL;
@@ -243,36 +280,48 @@ int mgcp_response_parse_params(struct mgcp_response *r)
case 'm':
rc = mgcp_parse_audio_port(r, line);
if (rc)
return rc;
goto exit;
break;
case 'c':
rc = mgcp_parse_audio_ip(r, line);
if (rc)
return rc;
goto exit;
break;
default:
/* skip unhandled parameters */
break;
}
}
return 0;
rc = 0;
exit:
talloc_free(data);
return rc;
}
/* Parse a line like "I: 0cedfd5a19542d197af9afe5231f1d61" */
static int mgcp_parse_conn_id(struct mgcp_response *r, const char *line)
/* Parse a line like "X: something" */
static int mgcp_parse_head_param(char *result, unsigned int result_len,
char label, const char *line)
{
char label_string[4];
/* Detect empty parameters */
if (strlen(line) < 4)
goto response_parse_failure;
if (memcmp("I: ", line, 3) != 0)
/* Check if the label matches */
snprintf(label_string, sizeof(label_string), "%c: ", label);
if (memcmp(label_string, line, 3) != 0)
goto response_parse_failure;
osmo_strlcpy(r->head.conn_id, line + 3, sizeof(r->head.conn_id));
/* Copy payload part of the string to destinations (the label string
* is always 3 chars long) */
osmo_strlcpy(result, line + 3, result_len);
return 0;
response_parse_failure:
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse MGCP response (connectionIdentifier)\n");
"Failed to parse MGCP response (parameter label: %c)\n", label);
return -EINVAL;
}
@@ -282,18 +331,51 @@ static int parse_head_params(struct mgcp_response *r)
char *line;
int rc = 0;
OSMO_ASSERT(r->body);
char *data = r->body;
char *data_end = strstr(r->body, "\n\n");
char *data;
char *data_ptr;
char *data_end;
/* Protect SDP body, for_each_non_empty_line() will
* only parse until it hits \0 mark. */
/* Since this functions performs a destructive parsing, we create a
* local copy of the body data */
data = talloc_zero_size(r, strlen(r->body)+1);
OSMO_ASSERT(data);
data_ptr = data;
osmo_strlcpy(data, r->body, strlen(r->body));
/* If there is an SDP body attached, prevent for_each_non_empty_line()
* into running in there, we are not yet interested in the parameters
* stored there. */
data_end = mgcp_find_section_end(data);
if (data_end)
*data_end = '\0';
for_each_non_empty_line(line, data) {
for_each_non_empty_line(line, data_ptr) {
switch (line[0]) {
case 'Z':
rc = mgcp_parse_head_param(r->head.endpoint,
sizeof(r->head.endpoint),
'Z', line);
if (rc)
goto exit;
/* A specific endpoint identifier returned by the MGW
* must not contain any wildcard characters */
if (strstr(r->head.endpoint, "*") != NULL) {
rc = -EINVAL;
goto exit;
}
/* A specific endpoint identifier returned by the MGW
* must contain an @ character */
if (strstr(r->head.endpoint, "@") == NULL) {
rc = -EINVAL;
goto exit;
}
break;
case 'I':
rc = mgcp_parse_conn_id(r, line);
rc = mgcp_parse_head_param(r->head.conn_id,
sizeof(r->head.conn_id),
'I', line);
if (rc)
goto exit;
break;
@@ -303,10 +385,7 @@ static int parse_head_params(struct mgcp_response *r)
}
}
exit:
/* Restore original state */
if (data_end)
*data_end = '\n';
talloc_free(data);
return rc;
}
@@ -332,32 +411,42 @@ static struct mgcp_response_pending *mgcp_client_response_pending_get(
*/
int mgcp_client_rx(struct mgcp_client *mgcp, struct msgb *msg)
{
struct mgcp_response r = { 0 };
struct mgcp_response *r;
struct mgcp_response_pending *pending;
int rc;
rc = mgcp_response_parse_head(&r, msg);
r = talloc_zero(mgcp, struct mgcp_response);
OSMO_ASSERT(r);
rc = mgcp_response_parse_head(r, msg);
if (rc) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head)\n");
return -1;
rc = 1;
goto error;
}
rc = parse_head_params(&r);
rc = parse_head_params(r);
if (rc) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head parameters)\n");
return -1;
rc = 1;
goto error;
}
pending = mgcp_client_response_pending_get(mgcp, r.head.trans_id);
pending = mgcp_client_response_pending_get(mgcp, r->head.trans_id);
if (!pending) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot find matching MGCP transaction for trans_id %d\n",
r.head.trans_id);
return -ENOENT;
r->head.trans_id);
rc = -ENOENT;
goto error;
}
mgcp_client_handle_response(mgcp, pending, &r);
return 0;
mgcp_client_handle_response(mgcp, pending, r);
rc = 0;
error:
talloc_free(r);
return rc;
}
static int mgcp_do_read(struct osmo_fd *fd)
@@ -444,6 +533,9 @@ struct mgcp_client *mgcp_client_init(void *ctx,
return mgcp;
}
/*! Initalize client connection (opens socket only, no request is sent yet)
* \param[in,out] mgcp MGCP client descriptor.
* \returns 0 on success, -EINVAL on error. */
int mgcp_client_connect(struct mgcp_client *mgcp)
{
struct sockaddr_in addr;
@@ -489,17 +581,25 @@ error_close_fd:
return rc;
}
/*! Get the IP-Aaddress of the associated MGW as string.
* \param[in] mgcp MGCP client descriptor.
* \returns a pointer to the address string. */
const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp)
{
return mgcp->actual.remote_addr;
}
/*! Get the IP-Port of the associated MGW.
* \param[in] mgcp MGCP client descriptor.
* \returns port number. */
uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp)
{
return mgcp->actual.remote_port;
}
/* Return the MGCP GW binary IPv4 address in network byte order. */
/*! Get the IP-Aaddress of the associated MGW as its numeric representation.
* \param[in] mgcp MGCP client descriptor.
* \returns IP-Address as 32 bit integer (network byte order) */
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp)
{
return mgcp->remote_addr;
@@ -572,29 +672,32 @@ mgcp_tx_error:
return -1;
}
/* Cancel a pending transaction.
/*! Cancel a pending transaction.
* \param[in] mgcp MGCP client descriptor.
* \param[in,out] trans_id Transaction id.
* \returns 0 on success, -ENOENT on error.
*
* Should a priv pointer passed to mgcp_client_tx() become invalid, this function must be called. In
* practical terms, if the caller of mgcp_client_tx() wishes to tear down a transaction without having
* received a response this function must be called. The trans_id can be obtained by calling
* mgcp_msg_trans_id() on the msgb produced by mgcp_msg_gen().
*/
* mgcp_msg_trans_id() on the msgb produced by mgcp_msg_gen(). */
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id)
{
struct mgcp_response_pending *pending = mgcp_client_response_pending_get(mgcp, trans_id);
if (!pending) {
/* INFO is sufficient, it is not harmful to cancel a transaction twice. */
/*! Note: it is not harmful to cancel a transaction twice. */
LOGP(DLMGCP, LOGL_INFO, "Cannot cancel, no such transaction: %u\n", trans_id);
return -ENOENT;
}
LOGP(DLMGCP, LOGL_INFO, "Canceled transaction %u\n", trans_id);
talloc_free(pending);
return 0;
/* We don't really need to clean up the wqueue: In all sane cases, the msgb has already been sent
* out and is no longer in the wqueue. If it still is in the wqueue, then sending MGCP messages
* per se is broken and the program should notice so by a full wqueue. Even if this was called
* before we had a chance to send out the message and it is still going to be sent, we will just
* ignore the reply to it later. Removing a msgb from the wqueue here would just introduce more
* bug surface in terms of failing to update wqueue API's counters or some such.
/*! We don't really need to clean up the wqueue: In all sane cases, the msgb has already been sent
* out and is no longer in the wqueue. If it still is in the wqueue, then sending MGCP messages
* per se is broken and the program should notice so by a full wqueue. Even if this was called
* before we had a chance to send out the message and it is still going to be sent, we will just
* ignore the reply to it later. Removing a msgb from the wqueue here would just introduce more
* bug surface in terms of failing to update wqueue API's counters or some such.
*/
}
@@ -704,17 +807,23 @@ struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
MGCP_MSG_PRESENCE_CALL_ID | \
MGCP_MSG_PRESENCE_CONN_MODE)
#define MGCP_MDCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
MGCP_MSG_PRESENCE_CALL_ID | \
MGCP_MSG_PRESENCE_CONN_ID)
#define MGCP_DLCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
#define MGCP_AUEP_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
#define MGCP_RSIP_MANDATORY 0 /* none */
/*! Generate an MGCP message
* \param[in] mgcp MGCP client descriptor.
* \param[in] mgcp_msg Message description
* \returns message buffer on success, NULL on error. */
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
{
mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
uint32_t mandatory_mask;
struct msgb *msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
int rc = 0;
char local_ip[INET_ADDRSTRLEN];
msg->l2h = msg->data;
msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
@@ -764,6 +873,14 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
msgb_free(msg);
return NULL;
}
if (strstr(mgcp_msg->endpoint, "@") == NULL) {
LOGP(DLMGCP, LOGL_ERROR,
"Endpoint name (%s) lacks separator (@), can not generate MGCP message\n",
mgcp_msg->endpoint);
msgb_free(msg);
}
rc += msgb_printf(msg, " %s", mgcp_msg->endpoint);
}
@@ -795,9 +912,32 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
msgb_printf(msg, "M: %s\r\n",
mgcp_client_cmode_name(mgcp_msg->conn_mode));
/* Add RTP address and port (SDP) */
/* Add SDP body */
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
&& mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT) {
/* Add separator to mark the beginning of the SDP block */
rc += msgb_printf(msg, "\r\n");
/* Add SDP protocol version */
rc += msgb_printf(msg, "v=0\r\n");
/* Determine local IP-Address */
if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
LOGP(DLMGCP, LOGL_ERROR,
"Could not determine local IP-Address!\n");
msgb_free(msg);
return NULL;
}
/* Add owner/creator (SDP) */
rc += msgb_printf(msg, "o=- %x 23 IN IP4 %s\r\n",
mgcp_msg->call_id, local_ip);
/* Add session name (none) */
rc += msgb_printf(msg, "s=-\r\n");
/* Add RTP address and port */
if (mgcp_msg->audio_port == 0) {
LOGP(DLMGCP, LOGL_ERROR,
"Invalid port number, can not generate MGCP message\n");
@@ -810,8 +950,11 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
msgb_free(msg);
return NULL;
}
rc += msgb_printf(msg, "\r\n");
rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
/* Add time description, active time (SDP) */
rc += msgb_printf(msg, "t=0 0\r\n");
rc +=
msgb_printf(msg, "m=audio %u RTP/AVP 255\r\n",
mgcp_msg->audio_port);
@@ -827,12 +970,17 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
return msg;
}
/* Retrieve the MGCP transaction ID from a msgb generated by mgcp_msg_gen() */
/*! Retrieve the MGCP transaction ID from a msgb generated by mgcp_msg_gen()
* \param[in] msg message buffer
* \returns Transaction id. */
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg)
{
return (mgcp_trans_id_t)msg->cb[MSGB_CB_MGCP_TRANS_ID];
}
/*! Get the configuration parameters a given MGCP client instance
* \param[in] mgcp MGCP client descriptor.
* \returns configuration */
struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp)
{
return &mgcp->actual;

View File

@@ -0,0 +1,669 @@
/* (C) 2018 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 General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_fsm.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/byteswap.h>
#include <arpa/inet.h>
#include <osmocom/core/logging.h>
/* Context information, this is attached to the priv pointer of the FSM and
* is also handed back when dispatcheing events to the parent FSM. This is
* purly intened and not meant to be accessible for the API user */
struct mgcp_ctx {
/* MGCP client instance that is used to interact with the MGW */
struct mgcp_client *mgcp;
/* The ID of the last pending transaction. This is used internally
* to cancel the transaction in case of an error */
mgcp_trans_id_t mgw_pending_trans;
/* Flag to mark that there is a pending transaction */
bool mgw_trans_pending;
/* Connection ID which has been assigned by he MGW */
char conn_id[MGCP_CONN_ID_LENGTH];
/* Local RTP connection info, the MGW will send outgoing traffic to the
* ip/port specified here. The Address does not have to be choosen right
* on the creation of a connection. It can always be modified later by
* the user. */
struct mgcp_conn_peer conn_peer_local;
/* Remote RTP connection info, the ip/port specified here is the address
* where the MGW expects the RTP data to be sent. This address is
* defined by soly by the MGW and can not be influenced by the user. */
struct mgcp_conn_peer conn_peer_remote;
/* The terminate flag is a way to handle cornercase sitations that
* might occur when the user runs into an error situation and sends
* a DLCX command while the FSM is waiting for a response. In this
* case the DLCX command is not executed immediately. Instead the
* terminate flag is set. When the response to from the previous
* operation is received, we know that there is a DLCX event is
* pending. The FSM then generates the EV_DLCX by itsself before
* it enters ST_READY to cause the immediate execution of the
* DLCX procedure. (If normal operations are executed too fast,
* the API functions will return an error. In general, the user
* should synchronize using the callback events) */
bool terminate;
/* Event that is sent when the current operation is completed (except
* for DLCX, there the specified parent_term_evt is sent instead) */
uint32_t parent_evt;
};
#define S(x) (1 << (x))
#define MGCP_MGW_TIMEOUT 4 /* in seconds */
#define MGCP_MGW_TIMEOUT_TIMER_NR 1
enum fsm_mgcp_client_states {
ST_CRCX,
ST_CRCX_RESP,
ST_READY,
ST_MDCX_RESP,
ST_DLCX_RESP,
};
enum fsm_mgcp_client_evt {
EV_CRCX,
EV_CRCX_RESP,
EV_MDCX,
EV_MDCX_RESP,
EV_DLCX,
EV_DLCX_RESP,
};
static const struct value_string fsm_mgcp_client_evt_names[] = {
OSMO_VALUE_STRING(EV_CRCX),
OSMO_VALUE_STRING(EV_CRCX_RESP),
OSMO_VALUE_STRING(EV_MDCX),
OSMO_VALUE_STRING(EV_MDCX_RESP),
OSMO_VALUE_STRING(EV_DLCX),
OSMO_VALUE_STRING(EV_DLCX_RESP),
{0, NULL}
};
static struct msgb *make_crcx_msg_bind(struct mgcp_ctx *mgcp_ctx)
{
struct mgcp_msg mgcp_msg;
mgcp_msg = (struct mgcp_msg) {
.verb = MGCP_VERB_CRCX,
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
.call_id = mgcp_ctx->conn_peer_local.call_id,
.conn_mode = MGCP_CONN_LOOPBACK,
};
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
}
static struct msgb *make_crcx_msg_bind_connect(struct mgcp_ctx *mgcp_ctx)
{
struct mgcp_msg mgcp_msg;
mgcp_msg = (struct mgcp_msg) {
.verb = MGCP_VERB_CRCX,.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
MGCP_MSG_PRESENCE_AUDIO_PORT),
.call_id = mgcp_ctx->conn_peer_local.call_id,
.conn_mode = MGCP_CONN_RECV_SEND,
.audio_ip = mgcp_ctx->conn_peer_local.addr,
.audio_port = mgcp_ctx->conn_peer_local.port,
};
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
}
static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
{
struct mgcp_msg mgcp_msg;
mgcp_msg = (struct mgcp_msg) {
.verb = MGCP_VERB_MDCX,
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT),
.call_id = mgcp_ctx->conn_peer_remote.call_id,
.conn_id = mgcp_ctx->conn_id,
.conn_mode = MGCP_CONN_RECV_SEND,
.audio_ip = mgcp_ctx->conn_peer_local.addr,
.audio_port = mgcp_ctx->conn_peer_local.port,
};
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
/* Note: We take the endpoint and the call_id from the remote
* connection info, because we can be confident that the
* information there is valid. For the local info, we explicitly
* allow endpoint and call_id to be optional */
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
}
struct msgb *make_dlcx_msg(struct mgcp_ctx *mgcp_ctx)
{
struct mgcp_msg mgcp_msg;
mgcp_msg = (struct mgcp_msg) {
.verb = MGCP_VERB_DLCX,
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID),
.call_id = mgcp_ctx->conn_peer_remote.call_id,
.conn_id = mgcp_ctx->conn_id,
};
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
}
static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv);
static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct mgcp_ctx *mgcp_ctx = data;
struct mgcp_client *mgcp;
struct msgb *msg;
int rc;
OSMO_ASSERT(mgcp_ctx);
mgcp = mgcp_ctx->mgcp;
OSMO_ASSERT(mgcp);
switch (event) {
case EV_CRCX:
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: creating connection on MGW endpoint:%s...\n",
mgcp_ctx->conn_peer_local.endpoint);
if (mgcp_ctx->conn_peer_local.port)
msg = make_crcx_msg_bind_connect(mgcp_ctx);
else
msg = make_crcx_msg_bind(mgcp_ctx);
OSMO_ASSERT(msg);
mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
mgcp_ctx->mgw_trans_pending = true;
rc = mgcp_client_tx(mgcp, msg, mgw_crcx_resp_cb, fi);
if (rc < 0) {
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
osmo_fsm_inst_state_chg(fi, ST_CRCX_RESP, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
break;
default:
OSMO_ASSERT(false);
break;
}
}
static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv)
{
struct osmo_fsm_inst *fi = priv;
struct mgcp_ctx *mgcp_ctx;
int rc;
OSMO_ASSERT(fi);
mgcp_ctx = fi->priv;
OSMO_ASSERT(mgcp_ctx);
mgcp_ctx->mgw_trans_pending = false;
if (r->head.response_code != 200) {
LOGPFSML(fi, LOGL_ERROR,
"MGW/CRCX: response yields error: %d %s\n", r->head.response_code, r->head.comment);
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
osmo_strlcpy(mgcp_ctx->conn_id, r->head.conn_id, sizeof(mgcp_ctx->conn_id));
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with CI: %s\n", mgcp_ctx->conn_id);
rc = mgcp_response_parse_params(r);
if (rc) {
LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: Cannot parse CRCX response\n");
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr));
mgcp_ctx->conn_peer_remote.port = r->audio_port;
if (strlen(r->head.endpoint) > 0) {
/* If we get an endpoint identifier back from the MGW, take it */
osmo_strlcpy(mgcp_ctx->conn_peer_remote.endpoint, r->head.endpoint,
sizeof(mgcp_ctx->conn_peer_remote.endpoint));
} else if (strstr(mgcp_ctx->conn_peer_local.endpoint, "*") == NULL) {
/* If we do not get an endpoint identifier back and the
* identifier we used to create the connection is not a
* wildcarded one, we take the local endpoint identifier
* instead */
osmo_strlcpy(mgcp_ctx->conn_peer_remote.endpoint, mgcp_ctx->conn_peer_local.endpoint,
sizeof(mgcp_ctx->conn_peer_local.endpoint));
} else {
LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: CRCX yielded not suitable endpoint identifier\n");
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
mgcp_ctx->conn_peer_remote.call_id = mgcp_ctx->conn_peer_local.call_id;
osmo_fsm_inst_dispatch(fi, EV_CRCX_RESP, mgcp_ctx);
}
static void fsm_crcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct mgcp_ctx *mgcp_ctx = data;
OSMO_ASSERT(mgcp_ctx);
switch (event) {
case EV_CRCX_RESP:
osmo_fsm_inst_state_chg(fi, ST_READY, 0, 0);
if (mgcp_ctx->terminate) {
/* Trigger immediate DLCX if DLCX was requested while the FSM was
* busy with the previous operation */
LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: FSM was busy while DLCX was requested, executing now...\n");
osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx);
} else
osmo_fsm_inst_dispatch(fi->proc.parent, mgcp_ctx->parent_evt, &mgcp_ctx->conn_peer_remote);
break;
default:
OSMO_ASSERT(false);
break;
}
}
static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv);
static void mgw_dlcx_resp_cb(struct mgcp_response *r, void *priv);
static void fsm_ready_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct mgcp_ctx *mgcp_ctx = data;
struct msgb *msg;
struct mgcp_client *mgcp;
uint32_t new_state;
int rc;
OSMO_ASSERT(mgcp_ctx);
mgcp = mgcp_ctx->mgcp;
OSMO_ASSERT(mgcp);
switch (event) {
case EV_MDCX:
msg = make_mdcx_msg(mgcp_ctx);
OSMO_ASSERT(msg);
rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_resp_cb, fi);
new_state = ST_MDCX_RESP;
break;
case EV_DLCX:
msg = make_dlcx_msg(mgcp_ctx);
OSMO_ASSERT(msg);
rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_resp_cb, fi);
new_state = ST_DLCX_RESP;
break;
default:
OSMO_ASSERT(false);
break;
}
mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
mgcp_ctx->mgw_trans_pending = true;
if (rc < 0) {
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
osmo_fsm_inst_state_chg(fi, new_state, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
}
static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv)
{
struct osmo_fsm_inst *fi = priv;
struct mgcp_ctx *mgcp_ctx;
int rc;
OSMO_ASSERT(fi);
mgcp_ctx = fi->priv;
OSMO_ASSERT(mgcp_ctx);
mgcp_ctx->mgw_trans_pending = false;
if (r->head.response_code != 200) {
LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: response yields error: %d %s\n", r->head.response_code,
r->head.comment);
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
rc = mgcp_response_parse_params(r);
if (rc) {
LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: Cannot parse MDCX response\n");
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
LOGPFSML(fi, LOGL_DEBUG, "MGW/MDCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr));
mgcp_ctx->conn_peer_remote.port = r->audio_port;
osmo_fsm_inst_dispatch(fi, EV_MDCX_RESP, mgcp_ctx);
}
static void fsm_mdcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct mgcp_ctx *mgcp_ctx = data;
OSMO_ASSERT(mgcp_ctx);
switch (event) {
case EV_MDCX_RESP:
osmo_fsm_inst_state_chg(fi, ST_READY, 0, 0);
if (mgcp_ctx->terminate) {
/* Trigger immediate DLCX if DLCX was requested while the FSM was
* busy with the previous operation */
LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: FSM was busy while DLCX was requested, executing now...\n");
osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx);
} else
osmo_fsm_inst_dispatch(fi->proc.parent, mgcp_ctx->parent_evt, &mgcp_ctx->conn_peer_remote);
break;
default:
OSMO_ASSERT(false);
break;
}
}
static void mgw_dlcx_resp_cb(struct mgcp_response *r, void *priv)
{
struct osmo_fsm_inst *fi = priv;
struct mgcp_ctx *mgcp_ctx;
OSMO_ASSERT(fi);
mgcp_ctx = fi->priv;
OSMO_ASSERT(mgcp_ctx);
mgcp_ctx->mgw_trans_pending = false;
if (r->head.response_code != 250) {
LOGPFSML(fi, LOGL_ERROR,
"MGW/DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment);
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
osmo_fsm_inst_dispatch(fi, EV_DLCX_RESP, mgcp_ctx);
}
static void fsm_dlcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct mgcp_ctx *mgcp_ctx = data;
OSMO_ASSERT(mgcp_ctx);
switch (event) {
case EV_DLCX_RESP:
/* Rub out the connection identifier, since the connection
* is no longer present and we will use the connection id
* to know in error cases if the connection is still present
* or not */
memset(mgcp_ctx->conn_id, 0, sizeof(mgcp_ctx->conn_id));
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
break;
default:
OSMO_ASSERT(false);
break;
}
}
static int fsm_timeout_cb(struct osmo_fsm_inst *fi)
{
struct mgcp_ctx *mgcp_ctx = fi->priv;
struct mgcp_client *mgcp;
OSMO_ASSERT(mgcp_ctx);
mgcp = mgcp_ctx->mgcp;
OSMO_ASSERT(mgcp);
if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) {
/* Note: We were unable to communicate with the MGW,
* unfortunately there is no meaningful action we can take
* now other than giving up. */
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
} else {
/* Note: Ther must not be any unsolicited timers
* in this FSM. If so, we have serious problem. */
OSMO_ASSERT(false);
}
return 0;
}
static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct mgcp_ctx *mgcp_ctx = fi->priv;
struct mgcp_client *mgcp;
struct msgb *msg;
OSMO_ASSERT(mgcp_ctx);
mgcp = mgcp_ctx->mgcp;
OSMO_ASSERT(mgcp);
/* If there is still a transaction pending, cancel it now. */
if (mgcp_ctx->mgw_trans_pending)
mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
/* Should the FSM be terminated while there are still open connections
* on the MGW, we send an unconditional DLCX to terminate the
* connection. This is not the normal case. The user should always use
* mgcp_conn_delete() to instruct the FSM to perform a graceful exit */
if (strlen(mgcp_ctx->conn_id)) {
LOGPFSML(fi, LOGL_ERROR,
"MGW/DLCX: aprupt FSM termination with connections still present, sending unconditional DLCX...\n");
msg = make_dlcx_msg(mgcp_ctx);
OSMO_ASSERT(msg);
mgcp_client_tx(mgcp, msg, NULL, NULL);
}
talloc_free(mgcp_ctx);
}
static struct osmo_fsm_state fsm_mgcp_client_states[] = {
/* Initial CRCX state. This state is immediately entered and executed
* when the FSM is started. The rationale is that we first have to
* create a connectin before we can execute other operations on that
* connection. */
[ST_CRCX] = {
.in_event_mask = S(EV_CRCX),
.out_state_mask = S(ST_CRCX_RESP),
.name = OSMO_STRINGIFY(ST_CRCX),
.action = fsm_crcx_cb,
},
/* Wait for the response to a CRCX operation, check and process the
* results, change to ST_READY afterwards. */
[ST_CRCX_RESP] = {
.in_event_mask = S(EV_CRCX_RESP),
.out_state_mask = S(ST_READY),
.name = OSMO_STRINGIFY(ST_CRCX_RESP),
.action = fsm_crcx_resp_cb,
},
/* In this idle state we wait for further operations (e.g. MDCX) that
* can be executed by the user using the API. There is no timeout in
* this state. The connection lives on until the user decides to
* terminate it (DLCX). */
[ST_READY] = {
.in_event_mask = S(EV_MDCX) | S(EV_DLCX),
.out_state_mask = S(ST_MDCX_RESP) | S(ST_DLCX_RESP),
.name = OSMO_STRINGIFY(ST_READY),
.action = fsm_ready_cb,
},
/* Wait for the response of a MDCX operation, check and process the
* results, change to ST_READY afterwards. */
[ST_MDCX_RESP] = {
.in_event_mask = S(EV_MDCX_RESP),
.out_state_mask = S(ST_READY),
.name = OSMO_STRINGIFY(ST_MDCX_RESP),
.action = fsm_mdcx_resp_cb,
},
/* Wait for the response of a DLCX operation and terminate the FSM
* normally. */
[ST_DLCX_RESP] = {
.in_event_mask = S(EV_DLCX_RESP),
.out_state_mask = 0,
.name = OSMO_STRINGIFY(ST_DLCX_RESP),
.action = fsm_dlcx_resp_cb,
},
};
static struct osmo_fsm fsm_mgcp_client = {
.name = "MGCP_CONN",
.states = fsm_mgcp_client_states,
.num_states = ARRAY_SIZE(fsm_mgcp_client_states),
.timer_cb = fsm_timeout_cb,
.cleanup = fsm_cleanup_cb,
.event_names = fsm_mgcp_client_evt_names,
};
/*! allocate FSM, and create a new connection on the MGW.
* \param[in] mgcp MGCP client descriptor.
* \param[in] mgcpparent_fi Parent FSM instance.
* \param[in] parent_term_evt Event to be sent to parent when terminating.
* \param[in] parent_evt Event to be sent to parent when operation is done.
* \param[in] conn_peer Connection parameters (ip, port...).
* \returns newly-allocated, initialized and registered FSM instance, NULL on error. */
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi,
uint32_t parent_term_evt, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
{
struct mgcp_ctx *mgcp_ctx;
static bool fsm_registered = false;
struct osmo_fsm_inst *fi;
struct in_addr ip_test;
OSMO_ASSERT(parent_fi);
OSMO_ASSERT(mgcp);
OSMO_ASSERT(conn_peer);
/* Check if IP/Port informstaion in conn info makes sense */
if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0)
return NULL;
/* Register the fsm description (if not already done) */
if (fsm_registered == false) {
osmo_fsm_register(&fsm_mgcp_client);
fsm_registered = true;
}
/* Allocate and configure a new fsm instance */
fi = osmo_fsm_inst_alloc_child(&fsm_mgcp_client, parent_fi, parent_term_evt);
OSMO_ASSERT(fi);
mgcp_ctx = talloc_zero(fi, struct mgcp_ctx);
OSMO_ASSERT(mgcp_ctx);
mgcp_ctx->mgcp = mgcp;
mgcp_ctx->parent_evt = parent_evt;
memcpy(&mgcp_ctx->conn_peer_local, conn_peer, sizeof(mgcp_ctx->conn_peer_local));
fi->priv = mgcp_ctx;
/* start state machine */
OSMO_ASSERT(fi->state == ST_CRCX);
osmo_fsm_inst_dispatch(fi, EV_CRCX, mgcp_ctx);
return fi;
}
/*! modify an existing connection on the MGW.
* \param[in] fi FSM instance.
* \param[in] parent_evt Event to be sent to parent when operation is done.
* \param[in] conn_peer New connection information (ip, port...).
* \returns 0 on success, -EINVAL on error. */
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
{
OSMO_ASSERT(fi);
struct mgcp_ctx *mgcp_ctx = fi->priv;
struct in_addr ip_test;
OSMO_ASSERT(mgcp_ctx);
OSMO_ASSERT(conn_peer);
/* The user must not issue an MDCX before the CRCX has completed,
* if this happens, it means that the parent FSM has overhead the
* parent_evt (mandatory!) and executed the MDCX without even
* waiting for the results. Another reason could be that the
* parent FSM got messed up */
OSMO_ASSERT(fi->state != ST_CRCX_RESP);
/* If the user tries to issue an MDCX while an DLCX operation is
* pending, there must be a serious problem with the paren FSM.
* Eeither the parent_term_evt (mandatory!) has been overheard,
* or the parant FSM got messed so badly that it still assumes
* a live connection although it as killed it. */
OSMO_ASSERT(fi->state != ST_DLCX_RESP);
/* Check if IP/Port parameters make sense */
if (conn_peer->port == 0)
return -EINVAL;
if (inet_aton(conn_peer->addr, &ip_test) == 0)
return -EINVAL;
/*! The user may supply an endpoint identifier in conn_peer. The
* identifier is then checked. This check is optional. Later steps do
* not depend on the endpoint identifier supplied here because it is
* already implicitly known from the CRCX phase. */
if (strlen(conn_peer->endpoint) && strcmp(conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint))
return -EINVAL;
/*! Note: The call-id is implicitly known from the previous CRCX and
* will not be checked even when it is set in conn_peer. */
mgcp_ctx->parent_evt = parent_evt;
memcpy(&mgcp_ctx->conn_peer_local, conn_peer, sizeof(mgcp_ctx->conn_peer_local));
osmo_fsm_inst_dispatch(fi, EV_MDCX, mgcp_ctx);
return 0;
}
/*! delete existing connection on the MGW, destroy FSM afterwards.
* \param[in] fi FSM instance. */
void mgcp_conn_delete(struct osmo_fsm_inst *fi)
{
OSMO_ASSERT(fi);
struct mgcp_ctx *mgcp_ctx = fi->priv;
OSMO_ASSERT(mgcp_ctx);
/* Unlink FSM from parent */
osmo_fsm_inst_unlink_parent(fi, NULL);
/* An error situation where the parent FSM must be killed immediately
* may lead into a situation where the DLCX can not be executed right
* at that moment because the FSM is still busy with another operation.
* In those cases we postpone the DLCX so that the FSM and the
* connections on the MGW get cleaned up gracefully. */
if (fi->state != ST_READY) {
LOGPFSML(fi, LOGL_ERROR, "MGW: operation still pending, DLCX will be postponed.\n");
mgcp_ctx->terminate = true;
return;
}
osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx);
}

View File

@@ -6,16 +6,16 @@
* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -25,6 +25,7 @@
#include <talloc.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/misc.h>
#include <osmocom/core/utils.h>
#include <osmocom/mgcp_client/mgcp_client.h>
@@ -203,4 +204,6 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
install_element(node, &cfg_mgcpgw_remote_port_cmd);
install_element(node, &cfg_mgcpgw_endpoint_range_cmd);
install_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd);
osmo_fsm_vty_add_cmds();
}

View File

@@ -21,19 +21,15 @@ AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
$(NULL)
# This is not at all related to the release version, but a range of supported
# API versions. Read TODO_RELEASE in the source tree's root!
MGCP_LIBVERSION=1:0:0
lib_LTLIBRARIES = \
libosmo-mgcp.la \
noinst_LIBRARIES = \
libosmo-mgcp.a \
$(NULL)
noinst_HEADERS = \
g711common.h \
$(NULL)
libosmo_mgcp_la_SOURCES = \
libosmo_mgcp_a_SOURCES = \
mgcp_protocol.c \
mgcp_network.c \
mgcp_vty.c \
@@ -42,7 +38,5 @@ libosmo_mgcp_la_SOURCES = \
mgcp_msg.c \
mgcp_conn.c \
mgcp_stat.c \
mgcp_ep.c \
mgcp_endp.c \
$(NULL)
libosmo_mgcp_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_LIBVERSION)

View File

@@ -24,7 +24,7 @@
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_ep.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/gsm/gsm_utils.h>
#include <ctype.h>
@@ -68,7 +68,7 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
}
/* Reset codec state and free memory */
static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec)
static void mgcp_rtp_codec_init(struct mgcp_rtp_codec *codec)
{
codec->payload_type = -1;
codec->subtype_name = NULL;
@@ -83,22 +83,20 @@ static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec)
talloc_free(codec->audio_name);
}
/* Reset states, free memory, set defaults and reset codec state */
static void mgcp_rtp_conn_reset(struct mgcp_conn_rtp *conn)
/* Initialize rtp connection struct with default values */
static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
{
struct mgcp_rtp_end *end = &conn->end;
struct mgcp_rtp_end *end = &conn_rtp->end;
conn->type = MGCP_RTP_DEFAULT;
conn->osmux.allocated_cid = -1;
conn_rtp->type = MGCP_RTP_DEFAULT;
conn_rtp->osmux.allocated_cid = -1;
/* backpointer to the generic part of the connection */
conn->u.rtp.conn = conn;
end->rtp.fd = -1;
end->rtcp.fd = -1;
end->local_port = 0;
end->packets_rx = 0;
end->octets_rx = 0;
end->packets_tx = 0;
end->octets_tx = 0;
end->dropped_packets = 0;
memset(&end->stats, 0, sizeof(end->stats));
end->rtp_port = end->rtcp_port = 0;
talloc_free(end->fmtp_extra);
end->fmtp_extra = NULL;
@@ -108,8 +106,16 @@ static void mgcp_rtp_conn_reset(struct mgcp_conn_rtp *conn)
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
end->output_enabled = 0;
mgcp_rtp_codec_reset(&end->codec);
mgcp_rtp_codec_reset(&end->alt_codec);
mgcp_rtp_codec_init(&end->codec);
mgcp_rtp_codec_init(&end->alt_codec);
}
/* Cleanup rtp connection struct */
static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
{
osmux_disable_conn(conn_rtp);
osmux_release_cid(conn_rtp);
mgcp_free_rtp_port(&conn_rtp->end);
}
/*! allocate a new connection list entry.
@@ -136,7 +142,6 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
conn->type = type;
conn->mode = MGCP_CONN_NONE;
conn->mode_orig = MGCP_CONN_NONE;
conn->u.rtp.conn = conn;
osmo_strlcpy(conn->name, name, sizeof(conn->name));
rc = mgcp_alloc_id(endp, conn->id);
if (rc < 0) {
@@ -146,7 +151,7 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
switch (type) {
case MGCP_CONN_TYPE_RTP:
mgcp_rtp_conn_reset(&conn->u.rtp);
mgcp_rtp_conn_init(&conn->u.rtp, conn);
break;
default:
/* NOTE: This should never be called with an
@@ -206,11 +211,15 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
if (!conn)
return;
/* Run endpoint cleanup action. By this we inform the endpoint about
* the removal of the connection and allow it to clean up its inner
* state accordingly */
if (endp->type->cleanup_cb)
endp->type->cleanup_cb(endp, conn);
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
osmux_disable_conn(&conn->u.rtp);
osmux_release_cid(&conn->u.rtp);
mgcp_free_rtp_port(&conn->u.rtp.end);
mgcp_rtp_conn_cleanup(&conn->u.rtp);
break;
default:
/* NOTE: This should never be called with an
@@ -254,9 +263,9 @@ void mgcp_conn_free_all(struct mgcp_endpoint *endp)
return;
}
/*! dump basic connection information to human readble string.
/*! dump basic connection information to human readable string.
* \param[in] conn to dump
* \returns human readble string */
* \returns human readable string */
char *mgcp_conn_dump(struct mgcp_conn *conn)
{
static char str[sizeof(conn->name)+sizeof(conn->id)+256];

View File

@@ -21,12 +21,36 @@
*
*/
#include <osmocom/mgcp/mgcp_ep.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_endp.h>
/* 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.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
.rtp.cleanup_cb = mgcp_cleanup_rtp_bridge_cb
};
/*! release endpoint, all open connections are closed.
* \param[in] endp endpoint to release */
void mgcp_endp_release(struct mgcp_endpoint *endp)
{
LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint:0x%x\n",
ENDPOINT_NUMBER(endp));
/* Normally this function should only be called when
* all connections have been removed already. In case
* that there are still connections open (e.g. when
* RSIP is executed), free them all at once. */
mgcp_conn_free_all(endp);
/* Reset endpoint parameters and states */
talloc_free(endp->callid);
endp->callid = NULL;
talloc_free(endp->local_options.string);
endp->local_options.string = NULL;
talloc_free(endp->local_options.codec);
endp->local_options.codec = NULL;
endp->wildcarded_req = false;
}

View File

@@ -28,6 +28,7 @@
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_endp.h>
/*! Display an mgcp message on the log output.
* \param[in] message mgcp message string
@@ -142,6 +143,7 @@ static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
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) {
@@ -178,25 +180,112 @@ static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
return NULL;
}
return &tcfg->endpoints[endp];
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];
LOGP(DLMGCP, LOGL_DEBUG,
"endpoint:0x%x found free endpoint\n",
ENDPOINT_NUMBER(endp));
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;
if (strcmp(domain_to_check+1, cfg->domain) != 0)
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)
const char *mgcp,
int *cause)
{
char *endptr = NULL;
unsigned int gw = INT_MAX;
const char *endpoint_number_str;
struct mgcp_endpoint *endp;
if (strncmp(mgcp, "ds/e1", 5) == 0)
return find_e1_endpoint(cfg, mgcp);
*cause = 0;
gw = strtoul(mgcp, &endptr, 10);
if (gw > 0 && gw < cfg->trunk.number_endpoints && endptr[0] == '@')
return &cfg->trunk.endpoints[gw];
/* Check if the domainname in the request is correct */
if (check_domain_name(cfg, mgcp)) {
LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s'\n", mgcp);
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;
}
@@ -209,6 +298,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
{
int i = 0;
char *elem, *save = NULL;
int cause;
/*! This function will parse the header part of the received
* MGCP message. The parsing results are stored in pdata.
@@ -226,25 +316,25 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
pdata->trans = elem;
break;
case 1:
pdata->endp = find_endpoint(pdata->cfg, elem);
pdata->endp = find_endpoint(pdata->cfg, elem, &cause);
if (!pdata->endp) {
LOGP(DLMGCP, LOGL_ERROR,
"Unable to find Endpoint `%s'\n", elem);
return -1;
return cause;
}
break;
case 2:
if (strcmp("MGCP", elem)) {
LOGP(DLMGCP, LOGL_ERROR,
"MGCP header parsing error\n");
return -1;
return -510;
}
break;
case 3:
if (strcmp("1.0", elem)) {
LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' "
"not supported\n", elem);
return -1;
return -528;
}
break;
}
@@ -255,7 +345,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n");
pdata->trans = "000000";
pdata->endp = NULL;
return -1;
return -510;
}
return 0;

View File

@@ -39,7 +39,7 @@
#include <osmocom/mgcp/mgcp_stat.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_ep.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/debug.h>
#define RTP_SEQ_MOD (1 << 16)
@@ -230,7 +230,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
"on 0x%x SSRC: %u timestamp: %u "
"from %s:%d\n",
text, seq,
state->timestamp_offset, state->seq_offset,
state->patch.timestamp_offset, state->patch.seq_offset,
ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp,
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
}
@@ -324,15 +324,15 @@ static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp,
out_timestamp = state->out_stream.last_timestamp + delta_seq * tsdelta;
timestamp_offset = out_timestamp - in_timestamp;
if (state->timestamp_offset != timestamp_offset) {
state->timestamp_offset = timestamp_offset;
if (state->patch.timestamp_offset != timestamp_offset) {
state->patch.timestamp_offset = timestamp_offset;
LOGP(DRTP, LOGL_NOTICE,
"Timestamp offset change on 0x%x SSRC: %u "
"SeqNo delta: %d, TS offset: %d, "
"from %s:%d\n",
ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
delta_seq, state->timestamp_offset,
delta_seq, state->patch.timestamp_offset,
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
}
@@ -353,11 +353,11 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp,
/* Align according to: T + Toffs - Tlast = k * Tptime */
ts_error = ts_alignment_error(&state->out_stream, ptime,
timestamp + state->timestamp_offset);
timestamp + state->patch.timestamp_offset);
/* If there is an alignment error, we have to compensate it */
if (ts_error) {
state->timestamp_offset += ptime - ts_error;
state->patch.timestamp_offset += ptime - ts_error;
LOGP(DRTP, LOGL_NOTICE,
"Corrected timestamp alignment error of %d on 0x%x SSRC: %u "
@@ -365,16 +365,16 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp,
"from %s:%d\n",
ts_error,
ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
state->timestamp_offset, inet_ntoa(addr->sin_addr),
state->patch.timestamp_offset, inet_ntoa(addr->sin_addr),
ntohs(addr->sin_port));
}
/* Check we really managed to compensate the timestamp
* offset. There should not be any remaining error, failing
* here would point to a serous problem with the alingnment
* error computation fuction */
* here would point to a serous problem with the alignment
* error computation function */
ts_check = ts_alignment_error(&state->out_stream, ptime,
timestamp + state->timestamp_offset);
timestamp + state->patch.timestamp_offset);
OSMO_ASSERT(ts_check == 0);
/* Return alignment error before compensation */
@@ -386,7 +386,7 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp,
* \param[in] destination RTP end
* \param[in,out] pointer to buffer with voice data
* \param[in] voice data length
* \param[in] maxmimum size of caller provided voice data buffer
* \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,
@@ -433,14 +433,14 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp,
int32_t d;
/* initialize or re-initialize */
if (!state->stats_initialized || state->stats_ssrc != ssrc) {
state->stats_initialized = 1;
state->stats_base_seq = seq;
state->stats_max_seq = seq - 1;
state->stats_ssrc = ssrc;
state->stats_jitter = 0;
state->stats_transit = transit;
state->stats_cycles = 0;
if (!state->stats.initialized || state->stats.ssrc != ssrc) {
state->stats.initialized = 1;
state->stats.base_seq = seq;
state->stats.max_seq = seq - 1;
state->stats.ssrc = ssrc;
state->stats.jitter = 0;
state->stats.transit = transit;
state->stats.cycles = 0;
} else {
uint16_t udelta;
@@ -451,10 +451,10 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp,
* It can't wrap during the initialization so let's
* skip it here. The Appendix A probably doesn't have
* this issue because of the probation. */
udelta = seq - state->stats_max_seq;
udelta = seq - state->stats.max_seq;
if (udelta < RTP_MAX_DROPOUT) {
if (seq < state->stats_max_seq)
state->stats_cycles += RTP_SEQ_MOD;
if (seq < state->stats.max_seq)
state->stats.cycles += RTP_SEQ_MOD;
} else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) {
LOGP(DRTP, LOGL_NOTICE,
"RTP seqno made a very large jump on 0x%x delta: %u\n",
@@ -466,12 +466,12 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp,
* taken closer to the read function. This was taken from the
* Appendix A of RFC 3550. Timestamp and arrival_time have a 1/rate
* resolution. */
d = transit - state->stats_transit;
state->stats_transit = transit;
d = transit - state->stats.transit;
state->stats.transit = transit;
if (d < 0)
d = -d;
state->stats_jitter += d - ((state->stats_jitter + 8) >> 4);
state->stats_max_seq = seq;
state->stats.jitter += d - ((state->stats.jitter + 8) >> 4);
state->stats.max_seq = seq;
}
/* The RFC 3550 Appendix A assumes there are multiple sources but
@@ -507,7 +507,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
if (!state->initialized) {
state->initialized = 1;
state->in_stream.last_seq = seq - 1;
state->in_stream.ssrc = state->orig_ssrc = ssrc;
state->in_stream.ssrc = state->patch.orig_ssrc = ssrc;
state->in_stream.last_tsdelta = 0;
state->packet_duration =
mgcp_rtp_packet_duration(endp, rtp_end);
@@ -518,7 +518,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
"endpoint:0x%x initializing stream, SSRC: %u timestamp: %u "
"pkt-duration: %d, from %s:%d\n",
ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
state->seq_offset, state->packet_duration,
state->patch.seq_offset, state->packet_duration,
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
if (state->packet_duration == 0) {
state->packet_duration =
@@ -542,7 +542,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
int16_t delta_seq;
/* Always increment seqno by 1 */
state->seq_offset =
state->patch.seq_offset =
(state->out_stream.last_seq + 1) - seq;
/* Estimate number of packets that would have been sent */
@@ -554,8 +554,8 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
adjust_rtp_timestamp_offset(endp, state, rtp_end, addr,
delta_seq, timestamp);
state->patch_ssrc = 1;
ssrc = state->orig_ssrc;
state->patch.patch_ssrc = 1;
ssrc = state->patch.orig_ssrc;
if (rtp_end->force_constant_ssrc != -1)
rtp_end->force_constant_ssrc -= 1;
@@ -564,7 +564,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
"SeqNo offset: %d, TS offset: %d "
"from %s:%d\n",
ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
state->seq_offset, state->timestamp_offset,
state->patch.seq_offset, state->patch.timestamp_offset,
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
}
@@ -575,8 +575,8 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
addr, seq, timestamp, "input",
&state->in_stream.last_tsdelta);
if (state->patch_ssrc)
ssrc = state->orig_ssrc;
if (state->patch.patch_ssrc)
ssrc = state->patch.orig_ssrc;
}
/* Save before patching */
@@ -591,15 +591,15 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
timestamp);
/* Store the updated SSRC back to the packet */
if (state->patch_ssrc)
if (state->patch.patch_ssrc)
rtp_hdr->ssrc = htonl(ssrc);
/* Apply the offset and store it back to the packet.
* This won't change anything if the offset is 0, so the conditional is
* omitted. */
seq += state->seq_offset;
seq += state->patch.seq_offset;
rtp_hdr->sequence = htons(seq);
timestamp += state->timestamp_offset;
timestamp += state->patch.timestamp_offset;
rtp_hdr->timestamp = htonl(timestamp);
/* Check again, whether the timestamps are still valid */
@@ -692,7 +692,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
dest_name = conn_dst->conn->name;
if (!rtp_end->output_enabled) {
rtp_end->dropped_packets += 1;
rtp_end->stats.dropped_packets += 1;
LOGP(DRTP, LOGL_DEBUG,
"endpoint:0x%x output disabled, drop to %s %s "
"rtp_port:%u rtcp_port:%u\n",
@@ -747,8 +747,8 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
if (len <= 0)
return len;
conn_dst->end.packets_tx += 1;
conn_dst->end.octets_tx += len;
conn_dst->end.stats.packets_tx += 1;
conn_dst->end.stats.octets_tx += len;
nbytes += len;
buflen = cont;
@@ -767,8 +767,8 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
&rtp_end->addr,
rtp_end->rtcp_port, buf, len);
conn_dst->end.packets_tx += 1;
conn_dst->end.octets_tx += len;
conn_dst->end.stats.packets_tx += 1;
conn_dst->end.stats.octets_tx += len;
return len;
}
@@ -905,7 +905,7 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
*proto = fd == &conn->end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x ", ENDPOINT_NUMBER(endp));
LOGPC(DRTP, LOGL_DEBUG, "receiveing from %s %s %d\n",
LOGPC(DRTP, LOGL_DEBUG, "receiving from %s %s %d\n",
conn->conn->name, inet_ntoa(addr->sin_addr),
ntohs(addr->sin_port));
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x conn:%s\n", ENDPOINT_NUMBER(endp),
@@ -928,8 +928,8 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
}
/* Increment RX statistics */
conn->end.packets_rx += 1;
conn->end.octets_rx += rc;
conn->end.stats.packets_rx += 1;
conn->end.stats.octets_rx += rc;
/* Forward a copy of the RTP data to a debug ip/port */
forward_data(fd->fd, &conn->tap_in, buf, rc);
@@ -1043,6 +1043,25 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
}
/*! 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. */
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
{
struct mgcp_conn *conn_cleanup;
/* In mgcp_dispatch_rtp_bridge_cb() we use conn->priv to cache the
* pointer to the destination connection, so that we do not have
* to go through the list every time an RTP packet arrives. To prevent
* a use-after-free situation we invalidate this information for all
* connections present when one connection is removed from the
* endpoint. */
llist_for_each_entry(conn_cleanup, &endp->conns, entry) {
conn_cleanup->priv = NULL;
}
}
/* Handle incoming RTP data from NET */
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
{

View File

@@ -24,6 +24,7 @@
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_endp.h>
static struct osmo_fd osmux_fd;
@@ -142,7 +143,7 @@ osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
}
/* Lookup existing handle for a specified address, if the handle can not be
* foud a the function will automatically allocate one */
* found, the function will automatically allocate one */
static struct osmux_in_handle *
osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
{
@@ -255,8 +256,8 @@ static void scheduled_tx_net_cb(struct msgb *msg, void *data)
.sin_port = conn_net->end.rtp_port,
};
conn_bts->end.octets_tx += msg->len;
conn_bts->end.packets_tx++;
conn_bts->end.stats.octets_tx += msg->len;
conn_bts->end.stats.packets_tx++;
/* Send RTP data to NET */
/* FIXME: Get rid of conn_bts and conn_net! */
@@ -282,8 +283,8 @@ static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
.sin_port = conn_bts->end.rtp_port,
};
conn_net->end.octets_tx += msg->len;
conn_net->end.packets_tx++;
conn_net->end.stats.octets_tx += msg->len;
conn_net->end.stats.packets_tx++;
/* Send RTP data to BTS */
/* FIXME: Get rid of conn_bts and conn_net! */

View File

@@ -38,7 +38,7 @@
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_stat.h>
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_ep.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_sdp.h>
struct mgcp_request {
@@ -192,17 +192,41 @@ static struct msgb *create_err_response(struct mgcp_endpoint *endp,
return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
}
/* Add MGCP parameters to a message buffer */
static int add_params(struct msgb *msg, const struct mgcp_endpoint *endp,
const struct mgcp_conn_rtp *conn)
{
int rc;
/* 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);
if (rc < 0)
return -EINVAL;
}
rc = msgb_printf(msg, "I: %s\r\n", conn->conn->id);
if (rc < 0)
return -EINVAL;
return 0;
}
/* Format MGCP response string (with SDP attached) */
static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn,
const char *msg,
const char *trans_id)
const char *trans_id,
bool add_conn_params)
{
const char *addr = endp->cfg->local_ip;
struct msgb *sdp;
int rc;
struct msgb *result;
char osmux_extension[strlen("\nX-Osmux: 255") + 1];
char osmux_extension[strlen("X-Osmux: 255") + 1];
char local_ip_addr[INET_ADDRSTRLEN];
sdp = msgb_alloc_headroom(4096, 128, "sdp record");
@@ -215,15 +239,28 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
}
if (conn->osmux.state == OSMUX_STATE_NEGOTIATING) {
sprintf(osmux_extension, "\nX-Osmux: %u", conn->osmux.cid);
sprintf(osmux_extension, "X-Osmux: %u", conn->osmux.cid);
conn->osmux.state = OSMUX_STATE_ACTIVATING;
} else {
osmux_extension[0] = '\0';
}
rc = msgb_printf(sdp, "I: %s%s\n\n", conn->conn->id, osmux_extension);
if (rc < 0)
goto error;
/* Attach optional connection parameters */
if (add_conn_params) {
rc = add_params(sdp, endp, conn);
if (rc < 0)
goto error;
}
/* Attach optional OSMUX parameters */
if (conn->osmux.state == OSMUX_STATE_NEGOTIATING) {
rc = msgb_printf(sdp, "%s\r\n", osmux_extension);
if (rc < 0)
goto error;
}
/* Attach line break to separate the parameters from the SDP block */
rc = msgb_printf(sdp, "\r\n");
rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
if (rc < 0)
@@ -252,7 +289,7 @@ static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
{
struct mgcp_parse_data pdata;
int i, code, handled = 0;
int rc, i, code, handled = 0;
struct msgb *resp = NULL;
char *data;
@@ -280,13 +317,19 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
memset(&pdata, 0, sizeof(pdata));
pdata.cfg = cfg;
data = mgcp_strline((char *)msg->l3h, &pdata.save);
pdata.found = mgcp_parse_header(&pdata, data);
rc = mgcp_parse_header(&pdata, data);
if (pdata.endp && pdata.trans
&& pdata.endp->last_trans
&& strcmp(pdata.endp->last_trans, pdata.trans) == 0) {
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);
return create_err_response(NULL, -rc, (const char *) msg->l2h, pdata.trans);
}
for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i) {
if (strncmp
(mgcp_requests[i].name, (const char *)&msg->l2h[0],
@@ -308,19 +351,13 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
{
LOGP(DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
if (p->found != 0) {
LOGP(DLMGCP, LOGL_ERROR,
"AUEP: failed to find the endpoint.\n");
return create_err_response(NULL, 500, "AUEP", p->trans);
} else
return create_ok_response(p->endp, 200, "AUEP", p->trans);
return create_ok_response(p->endp, 200, "AUEP", p->trans);
}
/* Try to find a free port by attemting to bind on it. Also handle the
/* Try to find a free port by attempting to bind on it. Also handle the
* counter that points on the next free port. Since we have a pointer
* to the next free port, binding should work on the first attemt,
* neverless, try at least the next 200 ports before giving up */
* to the next free port, binding should work on the first attempt,
* nevertheless, try at least the next 200 ports before giving up */
static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
int i;
@@ -356,10 +393,10 @@ static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
}
/* Set the LCO from a string (see RFC 3435).
* The string is stored in the 'string' field. A NULL string is handled excatlyy
* The string is stored in the 'string' field. A NULL string is handled exactly
* like an empty string, the 'string' field is never NULL after this function
* has been called. */
static void set_local_cx_options(void *ctx, struct mgcp_lco *lco,
static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
const char *options)
{
char *p_opt, *a_opt;
@@ -383,6 +420,15 @@ static void set_local_cx_options(void *ctx, struct mgcp_lco *lco,
LOGP(DLMGCP, LOGL_DEBUG,
"local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
lco->pkt_period_max, lco->codec);
/* Check if the packetization fits the 20ms raster */
if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
LOGP(DLMGCP, LOGL_ERROR,
"local CX options: packetization interval is not a multiple of 20ms!\n");
return 535;
}
return 0;
}
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
@@ -449,12 +495,10 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p)
struct mgcp_conn_rtp *conn = NULL;
struct mgcp_conn *_conn = NULL;
char conn_name[512];
int rc;
LOGP(DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
if (p->found != 0)
return create_err_response(NULL, 510, "CRCX", p->trans);
/* parse CallID C: and LocalParameters L: */
for_each_line(line, p->save) {
if (!mgcp_check_param(endp, line))
@@ -491,6 +535,7 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p)
LOGP(DLMGCP, LOGL_NOTICE,
"CRCX: endpoint:%x unhandled option: '%c'/%d\n",
ENDPOINT_NUMBER(endp), *line, *line);
return create_err_response(NULL, 539, "CRCX", p->trans);
break;
}
}
@@ -503,14 +548,14 @@ mgcp_header_done:
LOGP(DLMGCP, LOGL_ERROR,
"CRCX: endpoint:%x insufficient parameters, missing callid\n",
ENDPOINT_NUMBER(endp));
return create_err_response(endp, 400, "CRCX", p->trans);
return create_err_response(endp, 516, "CRCX", p->trans);
}
if (!mode) {
LOGP(DLMGCP, LOGL_ERROR,
"CRCX: endpoint:%x insufficient parameters, missing mode\n",
ENDPOINT_NUMBER(endp));
return create_err_response(endp, 400, "CRCX", p->trans);
return create_err_response(endp, 517, "CRCX", p->trans);
}
/* Check if we are able to accept the creation of another connection */
@@ -526,7 +571,7 @@ mgcp_header_done:
} else {
/* There is no more room for a connection, leave
* everything as it is and return with an error */
return create_err_response(endp, 400, "CRCX", p->trans);
return create_err_response(endp, 540, "CRCX", p->trans);
}
}
@@ -539,7 +584,7 @@ mgcp_header_done:
if (tcfg->force_realloc)
/* This is not our call, toss everything by releasing
* the entire endpoint. (rude!) */
mgcp_release_endp(endp);
mgcp_endp_release(endp);
else {
/* This is not our call, leave everything as it is and
* return with an error. */
@@ -548,13 +593,20 @@ mgcp_header_done:
}
/* Set the callid, creation of another connection will only be possible
* when the callid matches up. (Connections are distinuished by their
* when the callid matches up. (Connections are distinguished by their
* connection ids) */
endp->callid = talloc_strdup(tcfg->endpoints, callid);
/* Extract audio codec information */
set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
local_options);
rc = set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
local_options);
if (rc != 0) {
LOGP(DLMGCP, LOGL_ERROR,
"CRCX: endpoint:%x inavlid local connection options!\n",
ENDPOINT_NUMBER(endp));
error_code = rc;
goto error2;
}
snprintf(conn_name, sizeof(conn_name), "%s", callid);
_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP, conn_name);
@@ -602,6 +654,17 @@ mgcp_header_done:
mgcp_rtp_end_config(endp, 0, &conn->end);
/* check connection mode setting */
if (conn->conn->mode != MGCP_CONN_LOOPBACK
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
&& conn->end.rtp_port == 0) {
LOGP(DLMGCP, LOGL_ERROR,
"CRCX: endpoint:%x selected connection mode type requires an opposite end!\n",
ENDPOINT_NUMBER(endp));
error_code = 527;
goto error2;
}
if (allocate_port(endp, conn) != 0) {
goto error2;
}
@@ -623,7 +686,7 @@ mgcp_header_done:
LOGP(DLMGCP, LOGL_NOTICE,
"CRCX: endpoint:0x%x CRCX rejected by policy\n",
ENDPOINT_NUMBER(endp));
mgcp_release_endp(endp);
mgcp_endp_release(endp);
return create_err_response(endp, 400, "CRCX", p->trans);
break;
case MGCP_POLICY_DEFER:
@@ -651,9 +714,9 @@ mgcp_header_done:
LOGP(DLMGCP, LOGL_NOTICE,
"CRCX: endpoint:0x%x connection successfully created\n",
ENDPOINT_NUMBER(endp));
return create_response_with_sdp(endp, conn, "CRCX", p->trans);
return create_response_with_sdp(endp, conn, "CRCX", p->trans, true);
error2:
mgcp_release_endp(endp);
mgcp_endp_release(endp);
LOGP(DLMGCP, LOGL_NOTICE,
"CRCX: endpoint:0x%x unable to create connection resource error\n",
ENDPOINT_NUMBER(endp));
@@ -672,11 +735,17 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
const char *mode = NULL;
struct mgcp_conn_rtp *conn = NULL;
const char *conn_id = NULL;
int rc;
LOGP(DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
if (p->found != 0)
return create_err_response(NULL, 510, "MDCX", p->trans);
/* Prohibit wildcarded requests */
if (endp->wildcarded_req) {
LOGP(DLMGCP, LOGL_ERROR,
"MDCX: endpoint:0x%x wildcarded endpoint names not supported.\n",
ENDPOINT_NUMBER(endp));
return create_err_response(endp, 507, "MDCX", p->trans);
}
if (llist_count(&endp->conns) <= 0) {
LOGP(DLMGCP, LOGL_ERROR,
@@ -691,13 +760,17 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
switch (line[0]) {
case 'C':
if (mgcp_verify_call_id(endp, line + 3) != 0)
if (mgcp_verify_call_id(endp, line + 3) != 0) {
error_code = 516;
goto error3;
}
break;
case 'I':
conn_id = (const char *)line + 3;
if (mgcp_verify_ci(endp, conn_id) != 0)
if (mgcp_verify_ci(endp, conn_id) != 0) {
error_code = 515;
goto error3;
}
break;
case 'L':
local_options = (const char *)line + 3;
@@ -716,6 +789,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
LOGP(DLMGCP, LOGL_NOTICE,
"MDCX: endpoint:0x%x Unhandled MGCP option: '%c'/%d\n",
ENDPOINT_NUMBER(endp), line[0], line[0]);
return create_err_response(NULL, 539, "MDCX", p->trans);
break;
}
}
@@ -725,7 +799,7 @@ mgcp_header_done:
LOGP(DLMGCP, LOGL_ERROR,
"MDCX: endpoint:0x%x insufficient parameters, missing ci (connectionIdentifier)\n",
ENDPOINT_NUMBER(endp));
return create_err_response(endp, 400, "MDCX", p->trans);
return create_err_response(endp, 515, "MDCX", p->trans);
}
conn = mgcp_conn_get_rtp(endp, conn_id);
@@ -743,13 +817,31 @@ mgcp_header_done:
if (have_sdp)
mgcp_parse_sdp_data(endp, conn, p);
set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
local_options);
rc = set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
local_options);
if (rc != 0) {
LOGP(DLMGCP, LOGL_ERROR,
"MDCX: endpoint:%x inavlid local connection options!\n",
ENDPOINT_NUMBER(endp));
error_code = rc;
goto error3;
}
if (!have_sdp && endp->local_options.codec)
mgcp_set_audio_info(p->cfg, &conn->end.codec,
PTYPE_UNDEFINED, endp->local_options.codec);
/* check connection mode setting */
if (conn->conn->mode != MGCP_CONN_LOOPBACK
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
&& conn->end.rtp_port == 0) {
LOGP(DLMGCP, LOGL_ERROR,
"MDCX: endpoint:%x selected connection mode type requires an opposite end!\n",
ENDPOINT_NUMBER(endp));
error_code = 527;
goto error3;
}
if (setup_rtp_processing(endp, conn) != 0)
goto error3;
@@ -803,7 +895,7 @@ mgcp_header_done:
LOGP(DLMGCP, LOGL_NOTICE,
"MDCX: endpoint:0x%x connection successfully modified\n",
ENDPOINT_NUMBER(endp));
return create_response_with_sdp(endp, conn, "MDCX", p->trans);
return create_response_with_sdp(endp, conn, "MDCX", p->trans, false);
error3:
return create_err_response(endp, error_code, "MDCX", p->trans);
@@ -824,18 +916,23 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
const char *conn_id = NULL;
struct mgcp_conn_rtp *conn = NULL;
if (p->found != 0)
return create_err_response(NULL, error_code, "DLCX", p->trans);
LOGP(DLMGCP, LOGL_NOTICE,
"DLCX: endpoint:0x%x deleting connection ...\n",
ENDPOINT_NUMBER(endp));
/* Prohibit wildcarded requests */
if (endp->wildcarded_req) {
LOGP(DLMGCP, LOGL_ERROR,
"DLCX: endpoint:0x%x wildcarded endpoint names not supported.\n",
ENDPOINT_NUMBER(endp));
return create_err_response(endp, 507, "DLCX", p->trans);
}
if (llist_count(&endp->conns) <= 0) {
LOGP(DLMGCP, LOGL_ERROR,
"DLCX: endpoint:0x%x endpoint is not holding a connection.\n",
ENDPOINT_NUMBER(endp));
return create_err_response(endp, 400, "DLCX", p->trans);
return create_err_response(endp, 515, "DLCX", p->trans);
}
for_each_line(line, p->save) {
@@ -844,13 +941,17 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
switch (line[0]) {
case 'C':
if (mgcp_verify_call_id(endp, line + 3) != 0)
if (mgcp_verify_call_id(endp, line + 3) != 0) {
error_code = 516;
goto error3;
}
break;
case 'I':
conn_id = (const char *)line + 3;
if (mgcp_verify_ci(endp, conn_id) != 0)
if (mgcp_verify_ci(endp, conn_id) != 0) {
error_code = 515;
goto error3;
}
break;
case 'Z':
silent = strcmp("noanswer", line + 3) == 0;
@@ -859,6 +960,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
LOGP(DLMGCP, LOGL_NOTICE,
"DLCX: endpoint:0x%x Unhandled MGCP option: '%c'/%d\n",
ENDPOINT_NUMBER(endp), line[0], line[0]);
return create_err_response(NULL, 539, "DLCX", p->trans);
break;
}
}
@@ -895,7 +997,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
"DLCX: endpoint:0x%x missing ci (connectionIdentifier), will remove all connections at once\n",
ENDPOINT_NUMBER(endp));
mgcp_release_endp(endp);
mgcp_endp_release(endp);
/* Note: In this case we do not return any statistics,
* as we assume that the client is not interested in
@@ -922,7 +1024,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
/* When all connections are closed, the endpoint will be released
* in order to be ready to be used by another call. */
if (llist_count(&endp->conns) <= 0) {
mgcp_release_endp(endp);
mgcp_endp_release(endp);
LOGP(DLMGCP, LOGL_DEBUG,
"DLCX: endpoint:0x%x endpoint released\n",
ENDPOINT_NUMBER(endp));
@@ -958,12 +1060,6 @@ static struct msgb *handle_rsip(struct mgcp_parse_data *p)
LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
if (p->found != 0) {
LOGP(DLMGCP, LOGL_ERROR,
"RSIP: failed to find the endpoint.\n");
return NULL;
}
if (p->cfg->reset_cb)
p->cfg->reset_cb(p->endp->tcfg);
return NULL;
@@ -989,9 +1085,6 @@ static struct msgb *handle_noti_req(struct mgcp_parse_data *p)
LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
if (p->found != 0)
return create_err_response(NULL, 400, "RQNT", p->trans);
for_each_line(line, p->save) {
switch (line[0]) {
case 'S':
@@ -1013,7 +1106,7 @@ static struct msgb *handle_noti_req(struct mgcp_parse_data *p)
}
/* Connection keepalive timer, will take care that dummy packets are send
* regulary, so that NAT connections stay open */
* regularly, so that NAT connections stay open */
static void mgcp_keepalive_timer_cb(void *_tcfg)
{
struct mgcp_trunk_config *tcfg = _tcfg;
@@ -1082,6 +1175,8 @@ struct mgcp_config *mgcp_config_alloc(void)
return NULL;
}
osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
cfg->net_ports.last_port = cfg->net_ports.range_start;
@@ -1184,28 +1279,6 @@ int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
return 0;
}
/*! relase endpoint, all open connections are closed.
* \param[in] endp endpoint to release */
void mgcp_release_endp(struct mgcp_endpoint *endp)
{
LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint:0x%x\n",
ENDPOINT_NUMBER(endp));
/* Normally this function should only be called wehen
* all connections have been removed already. In case
* that there are still connections open (e.g. when
* RSIP is executed), free them all at once. */
mgcp_conn_free_all(endp);
/* Reset endpoint parameters and states */
talloc_free(endp->callid);
endp->callid = NULL;
talloc_free(endp->local_options.string);
endp->local_options.string = NULL;
talloc_free(endp->local_options.codec);
endp->local_options.codec = NULL;
}
static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
{
return write(cfg->gw_fd.bfd.fd, buf, len);
@@ -1218,13 +1291,16 @@ static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
* \returns 0 on success, -1 on error */
int mgcp_send_reset_all(struct mgcp_config *cfg)
{
char buf[MGCP_ENDPOINT_MAXLEN + 128];
int len;
int rc;
static const char mgcp_reset[] = {
"RSIP 1 *@mgw MGCP 1.0\r\n"
};
len = snprintf(buf, sizeof(buf),
"RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
if (len < 0)
return -1;
rc = send_agent(cfg, mgcp_reset, sizeof mgcp_reset - 1);
rc = send_agent(cfg, buf, len);
if (rc <= 0)
return -1;
@@ -1238,17 +1314,15 @@ int mgcp_send_reset_all(struct mgcp_config *cfg)
* \returns 0 on success, -1 on error */
int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint)
{
char buf[128];
char buf[MGCP_ENDPOINT_MAXLEN + 128];
int len;
int rc;
len = snprintf(buf, sizeof(buf),
"RSIP 39 %x@mgw MGCP 1.0\r\n", endpoint);
"RSIP 39 %x@%s MGCP 1.0\r\n", endpoint, endp->cfg->domain);
if (len < 0)
return -1;
buf[sizeof(buf) - 1] = '\0';
rc = send_agent(endp->cfg, buf, len);
if (rc <= 0)
return -1;

View File

@@ -24,6 +24,7 @@
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <errno.h>

View File

@@ -23,6 +23,7 @@
*/
#include <osmocom/mgcp/mgcp_stat.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <limits.h>
/* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
@@ -30,10 +31,10 @@ void calc_loss(struct mgcp_rtp_state *state,
struct mgcp_rtp_end *end, uint32_t *expected,
int *loss)
{
*expected = state->stats_cycles + state->stats_max_seq;
*expected = *expected - state->stats_base_seq + 1;
*expected = state->stats.cycles + state->stats.max_seq;
*expected = *expected - state->stats.base_seq + 1;
if (!state->stats_initialized) {
if (!state->stats.initialized) {
*expected = 0;
*loss = 0;
return;
@@ -43,8 +44,8 @@ void calc_loss(struct mgcp_rtp_state *state,
* Make sure the sign is correct and use the biggest
* positive/negative number that fits.
*/
*loss = *expected - end->packets_rx;
if (*expected < end->packets_rx) {
*loss = *expected - end->stats.packets_rx;
if (*expected < end->stats.packets_rx) {
if (*loss > 0)
*loss = INT_MIN;
} else {
@@ -56,9 +57,9 @@ void calc_loss(struct mgcp_rtp_state *state,
/* Helper function for mgcp_format_stats_rtp() to calculate jitter */
uint32_t calc_jitter(struct mgcp_rtp_state *state)
{
if (!state->stats_initialized)
if (!state->stats.initialized)
return 0;
return state->stats_jitter >> 4;
return state->stats.jitter >> 4;
}
/* Generate statistics for an RTP connection */
@@ -74,8 +75,8 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
nchars = snprintf(str, str_len,
"\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
conn->end.packets_tx, conn->end.octets_tx,
conn->end.packets_rx, conn->end.octets_rx,
conn->end.stats.packets_tx, conn->end.stats.octets_tx,
conn->end.stats.packets_rx, conn->end.stats.octets_rx,
ploss, jitter);
if (nchars < 0 || nchars >= str_len)
goto truncate;
@@ -83,21 +84,23 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
str += nchars;
str_len -= nchars;
/* Error Counter */
nchars = snprintf(str, str_len,
"\r\nX-Osmo-CP: EC TI=%u, TO=%u",
conn->state.in_stream.err_ts_counter,
conn->state.out_stream.err_ts_counter);
if (nchars < 0 || nchars >= str_len)
goto truncate;
if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) {
/* Error Counter */
nchars = snprintf(str, str_len,
"\r\nX-Osmo-CP: EC TI=%u, TO=%u",
conn->state.in_stream.err_ts_counter,
conn->state.out_stream.err_ts_counter);
if (nchars < 0 || nchars >= str_len)
goto truncate;
str += nchars;
str_len -= nchars;
str += nchars;
str_len -= nchars;
if (conn->osmux.state == OSMUX_STATE_ENABLED) {
snprintf(str, str_len,
"\r\nX-Osmux-ST: CR=%u, BR=%u",
conn->osmux.stats.chunks, conn->osmux.stats.octets);
if (conn->osmux.state == OSMUX_STATE_ENABLED) {
snprintf(str, str_len,
"\r\nX-Osmux-ST: CR=%u, BR=%u",
conn->osmux.stats.chunks, conn->osmux.stats.octets);
}
}
truncate:

View File

@@ -27,6 +27,7 @@
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/vty.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <string.h>
@@ -63,6 +64,7 @@ struct cmd_node trunk_node = {
static int config_write_mgcp(struct vty *vty)
{
vty_out(vty, "mgcp%s", VTY_NEWLINE);
vty_out(vty, " domain %s%s", g_cfg->domain, VTY_NEWLINE);
if (g_cfg->local_ip)
vty_out(vty, " local ip %s%s", g_cfg->local_ip, VTY_NEWLINE);
vty_out(vty, " bind ip %s%s", g_cfg->source_addr, VTY_NEWLINE);
@@ -167,7 +169,7 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_rtp_state *state,
" Output-Enabled: %d Force-PTIME: %d%s",
state->in_stream.err_ts_counter,
state->out_stream.err_ts_counter, VTY_NEWLINE,
end->dropped_packets, VTY_NEWLINE,
end->stats.dropped_packets, VTY_NEWLINE,
codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
codec->frame_duration_num, codec->frame_duration_den,
VTY_NEWLINE, end->frames_per_packet, end->packet_duration_ms,
@@ -1046,7 +1048,7 @@ DEFUN(free_endp, free_endp_cmd,
}
endp = &trunk->endpoints[endp_no];
mgcp_release_endp(endp);
mgcp_endp_release(endp);
return CMD_SUCCESS;
}
@@ -1179,6 +1181,14 @@ DEFUN(cfg_mgcp_osmux_dummy,
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_domain,
cfg_mgcp_domain_cmd,
"domain NAME", "domain\n" "qualified domain name\n")
{
osmo_strlcpy(g_cfg->domain, argv[0], sizeof(g_cfg->domain));
return CMD_SUCCESS;
}
int mgcp_vty_init(void)
{
install_element_ve(&show_mgcp_cmd);
@@ -1240,6 +1250,7 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd);
install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
install_element(MGCP_NODE, &cfg_mgcp_domain_cmd);
install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
install_node(&trunk_node, config_write_trunk);

View File

@@ -8,6 +8,7 @@ AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(LIBBCG729_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -24,6 +25,7 @@ osmo_bsc_mgcp_LDADD = \
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBBCG729_LIBS) \
$(LIBRARY_GSM) \
$(NULL)

View File

@@ -244,7 +244,7 @@ int main(int argc, char **argv)
msgb_talloc_ctx_init(tall_bsc_ctx, 0);
osmo_init_ignore_signals();
osmo_init_logging(&log_info);
osmo_init_logging2(tall_bsc_ctx, &log_info);
cfg = mgcp_config_alloc();
if (!cfg)

View File

@@ -8,6 +8,8 @@ AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -20,7 +22,9 @@ osmo_mgw_SOURCES = \
$(NULL)
osmo_mgw_LDADD = \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(NULL)

View File

@@ -37,6 +37,7 @@
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/vty.h>
#include <osmocom/mgcp/debug.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
@@ -52,6 +53,7 @@
#include <osmocom/vty/ports.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/misc.h>
#include "../../bscconfig.h"
@@ -189,7 +191,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_release_endp(&reset_trunk->endpoints[i]);
mgcp_endp_release(&reset_trunk->endpoints[i]);
}
return 0;
@@ -255,10 +257,12 @@ int main(int argc, char **argv)
int rc;
tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
vty_info.tall_ctx = tall_bsc_ctx;
msgb_talloc_ctx_init(tall_bsc_ctx, 0);
osmo_init_ignore_signals();
osmo_init_logging(&log_info);
osmo_init_logging2(tall_bsc_ctx, &log_info);
cfg = mgcp_config_alloc();
if (!cfg)
@@ -267,6 +271,7 @@ int main(int argc, char **argv)
vty_info.copyright = osmomgw_copyright;
vty_init(&vty_info);
logging_vty_add_cmds(NULL);
osmo_talloc_vty_add_cmds();
osmo_stats_vty_add_cmds(&log_info);
mgcp_vty_init();
@@ -309,7 +314,8 @@ int main(int argc, char **argv)
return -1;
}
LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP.\n");
LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP, listen on %s:%u\n",
cfg->source_addr, cfg->source_port);
/* initialisation */
srand(time(NULL));

View File

@@ -268,7 +268,9 @@ static void test_strline(void)
"C: 2\r\n"
#define DLCX_RET "250 7 OK\r\n" \
"P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n" \
"P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n"
#define DLCX_RET_OSMUX DLCX_RET \
"X-Osmo-CP: EC TIS=0, TOS=0, TIR=0, TOR=0\r\n"
#define RQNT "RQNT 186908780 1@mgw MGCP 1.0\r\n" \
@@ -1212,8 +1214,9 @@ const struct log_info log_info = {
int main(int argc, char **argv)
{
void *msgb_ctx = msgb_talloc_ctx_init(NULL, 0);
osmo_init_logging(&log_info);
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
osmo_init_logging2(ctx, &log_info);
test_strline();
test_values();

View File

@@ -588,7 +588,8 @@ const struct log_info log_info = {
int main(int argc, char **argv)
{
int rc;
osmo_init_logging(&log_info);
void *ctx = talloc_named_const(NULL, 0, "mgcp_transcoding_test");
osmo_init_logging2(ctx, &log_info);
printf("=== Transcoding Good Cases ===\n");

View File

@@ -8,6 +8,8 @@ AM_CFLAGS = \
-Wall \
-ggdb3 \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -29,9 +31,10 @@ mgcp_test_SOURCES = \
$(NULL)
mgcp_test_LDADD = \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBRARY_DL) \
$(LIBOSMONETIF_LIBS) \
-lm \

View File

@@ -25,7 +25,7 @@
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_stat.h>
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_ep.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
@@ -66,18 +66,18 @@ static void test_strline(void)
OSMO_ASSERT(counter == EXPECTED_NUMBER_OF_LINES);
}
#define AUEP1 "AUEP 158663169 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n"
#define AUEP1 "AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0\r\n"
#define AUEP1_RET "200 158663169 OK\r\n"
#define AUEP2 "AUEP 18983213 ds/e1-2/1@172.16.6.66 MGCP 1.0\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"
#define EMPTY_RET NULL
#define SHORT "CRCX \r\n"
#define SHORT_RET "510 000000 FAIL\r\n"
#define MDCX_WRONG_EP "MDCX 18983213 ds/e1-3/1@172.16.6.66 MGCP 1.0\r\n"
#define MDCX_ERR_RET "510 18983213 FAIL\r\n"
#define MDCX_UNALLOCATED "MDCX 18983214 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n"
#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 MDCX3 \
@@ -86,8 +86,7 @@ static void test_strline(void)
#define MDCX3_RET \
"200 18983215 OK\r\n" \
"I: %s\n" \
"\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
@@ -99,8 +98,7 @@ static void test_strline(void)
#define MDCX3A_RET \
"200 18983215 OK\r\n" \
"I: %s\n" \
"\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
@@ -112,8 +110,7 @@ static void test_strline(void)
#define MDCX3_FMTP_RET \
"200 18983215 OK\r\n" \
"I: %s\n" \
"\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
@@ -141,8 +138,7 @@ static void test_strline(void)
#define MDCX4_RET(Ident) \
"200 " Ident " OK\r\n" \
"I: %s\n" \
"\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
@@ -154,8 +150,7 @@ static void test_strline(void)
#define MDCX4_RO_RET(Ident) \
"200 " Ident " OK\r\n" \
"I: %s\n" \
"\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
@@ -252,8 +247,8 @@ static void test_strline(void)
#define CRCX_RET \
"200 2 OK\r\n" \
"I: %s\n" \
"\n" \
"I: %s\r\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
@@ -265,8 +260,8 @@ static void test_strline(void)
#define CRCX_RET_NO_RTPMAP \
"200 2 OK\r\n" \
"I: %s\n" \
"\n" \
"I: %s\r\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
@@ -277,8 +272,8 @@ static void test_strline(void)
#define CRCX_FMTP_RET \
"200 2 OK\r\n" \
"I: %s\n" \
"\n" \
"I: %s\r\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
@@ -301,8 +296,8 @@ static void test_strline(void)
#define CRCX_ZYN_RET \
"200 2 OK\r\n" \
"I: %s\n" \
"\n" \
"I: %s\r\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
@@ -319,7 +314,9 @@ static void test_strline(void)
#define DLCX_RET \
"250 7 OK\r\n" \
"P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n" \
"P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n"
#define DLCX_RET_OSMUX DLCX_RET \
"X-Osmo-CP: EC TI=0, TO=0\r\n"
#define RQNT \
@@ -561,25 +558,39 @@ static int get_conn_id_from_response(uint8_t *resp, char *conn_id,
{
char *conn_id_ptr;
int i;
bool got_conn_id = false;
/* First try to get the conn_id from the I: parameter */
conn_id_ptr = strstr((char *)resp, "I: ");
if (!conn_id_ptr)
return -EINVAL;
memset(conn_id, 0, conn_id_len);
memcpy(conn_id, conn_id_ptr + 3, 32);
for (i = 0; i < conn_id_len; i++) {
if (conn_id[i] == '\n' || conn_id[i] == '\r')
conn_id[i] = '\0';
if (conn_id_ptr) {
memset(conn_id, 0, conn_id_len);
memcpy(conn_id, conn_id_ptr + 3, 32);
got_conn_id = true;
} else {
/* Alternatively try to extract the conn_id from the o=- SDP
* parameter */
conn_id_ptr = strstr((char *)resp, "o=- ");
if(conn_id_ptr) {
memset(conn_id, 0, conn_id_len);
memcpy(conn_id, conn_id_ptr + 4, 32);
got_conn_id = true;
}
}
/* A valid conn_id must at least contain one digit, and must
* not exceed a length of 32 digits */
OSMO_ASSERT(strlen(conn_id) <= 32);
OSMO_ASSERT(strlen(conn_id) > 0);
if (got_conn_id) {
for (i = 0; i < conn_id_len; i++) {
if (conn_id[i] == '\n' || conn_id[i] == '\r')
conn_id[i] = '\0';
}
return 0;
/* A valid conn_id must at least contain one digit, and must
* not exceed a length of 32 digits */
OSMO_ASSERT(strlen(conn_id) <= 32);
OSMO_ASSERT(strlen(conn_id) > 0);
return 0;
}
return -EINVAL;
}
/* Check response, automatically patch connection ID if needed */
@@ -916,12 +927,12 @@ static void test_packet_loss_calc(void)
memset(&state, 0, sizeof(state));
memset(&rtp, 0, sizeof(rtp));
state.stats_initialized = 1;
state.stats_base_seq = pl_test_dat[i].base_seq;
state.stats_max_seq = pl_test_dat[i].max_seq;
state.stats_cycles = pl_test_dat[i].cycles;
state.stats.initialized = 1;
state.stats.base_seq = pl_test_dat[i].base_seq;
state.stats.max_seq = pl_test_dat[i].max_seq;
state.stats.cycles = pl_test_dat[i].cycles;
rtp.packets_rx = pl_test_dat[i].packets;
rtp.stats.packets_rx = pl_test_dat[i].packets;
calc_loss(&state, &rtp, &expected, &loss);
if (loss != pl_test_dat[i].loss
@@ -1183,7 +1194,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
state.out_stream.err_ts_counter - last_out_ts_err_cnt);
printf("Stats: Jitter = %u, Transit = %d\n",
calc_jitter(&state), state.stats_transit);
calc_jitter(&state), state.stats.transit);
last_in_ts_err_cnt = state.in_stream.err_ts_counter;
last_out_ts_err_cnt = state.out_stream.err_ts_counter;
@@ -1315,7 +1326,7 @@ static void test_multilple_codec(void)
/* Free the previous endpoint and the data and
* check if the connection really vanished... */
mgcp_release_endp(endp);
mgcp_endp_release(endp);
talloc_free(endp->last_response);
talloc_free(endp->last_trans);
endp->last_response = endp->last_trans = NULL;
@@ -1362,31 +1373,31 @@ static void test_no_cycle(void)
conn = mgcp_conn_get_rtp(endp, _conn->id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->state.stats_initialized == 0);
OSMO_ASSERT(conn->state.stats.initialized == 0);
mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342);
OSMO_ASSERT(conn->state.stats_initialized == 1);
OSMO_ASSERT(conn->state.stats_cycles == 0);
OSMO_ASSERT(conn->state.stats_max_seq == 0);
OSMO_ASSERT(conn->state.stats.initialized == 1);
OSMO_ASSERT(conn->state.stats.cycles == 0);
OSMO_ASSERT(conn->state.stats.max_seq == 0);
mgcp_rtp_annex_count(endp, &conn->state, 1, 0, 2342);
OSMO_ASSERT(conn->state.stats_initialized == 1);
OSMO_ASSERT(conn->state.stats_cycles == 0);
OSMO_ASSERT(conn->state.stats_max_seq == 1);
OSMO_ASSERT(conn->state.stats.initialized == 1);
OSMO_ASSERT(conn->state.stats.cycles == 0);
OSMO_ASSERT(conn->state.stats.max_seq == 1);
/* now jump.. */
mgcp_rtp_annex_count(endp, &conn->state, UINT16_MAX, 0, 2342);
OSMO_ASSERT(conn->state.stats_initialized == 1);
OSMO_ASSERT(conn->state.stats_cycles == 0);
OSMO_ASSERT(conn->state.stats_max_seq == UINT16_MAX);
OSMO_ASSERT(conn->state.stats.initialized == 1);
OSMO_ASSERT(conn->state.stats.cycles == 0);
OSMO_ASSERT(conn->state.stats.max_seq == UINT16_MAX);
/* and wrap */
mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342);
OSMO_ASSERT(conn->state.stats_initialized == 1);
OSMO_ASSERT(conn->state.stats_cycles == UINT16_MAX + 1);
OSMO_ASSERT(conn->state.stats_max_seq == 0);
OSMO_ASSERT(conn->state.stats.initialized == 1);
OSMO_ASSERT(conn->state.stats.cycles == UINT16_MAX + 1);
OSMO_ASSERT(conn->state.stats.max_seq == 0);
mgcp_release_endp(endp);
mgcp_endp_release(endp);
talloc_free(cfg);
}
@@ -1417,7 +1428,7 @@ static void test_no_name(void)
msgb_free(inp);
msgb_free(msg);
mgcp_release_endp(&cfg->trunk.endpoints[1]);
mgcp_endp_release(&cfg->trunk.endpoints[1]);
talloc_free(cfg);
}
@@ -1456,8 +1467,9 @@ const struct log_info log_info = {
int main(int argc, char **argv)
{
void *msgb_ctx = msgb_talloc_ctx_init(NULL, 0);
osmo_init_logging(&log_info);
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
osmo_init_logging2(ctx, &log_info);
test_strline();
test_values();

View File

@@ -16,7 +16,7 @@ line: ''
Testing AUEP1
creating message from statically defined input:
---------8<---------
AUEP 158663169 ds/e1-1/2@172.16.6.66 MGCP 1.0
AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0
---------8<---------
checking response:
@@ -28,7 +28,7 @@ Response matches our expectations.
Testing AUEP2
creating message from statically defined input:
---------8<---------
AUEP 18983213 ds/e1-2/1@172.16.6.66 MGCP 1.0
AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0
---------8<---------
checking response:
@@ -40,7 +40,7 @@ Response matches our expectations.
Testing MDCX1
creating message from statically defined input:
---------8<---------
MDCX 18983213 ds/e1-3/1@172.16.6.66 MGCP 1.0
MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0
---------8<---------
checking response:
@@ -52,7 +52,7 @@ Response matches our expectations.
Testing MDCX2
creating message from statically defined input:
---------8<---------
MDCX 18983214 ds/e1-1/2@172.16.6.66 MGCP 1.0
MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0
---------8<---------
checking response:
@@ -569,7 +569,6 @@ creating message from statically defined input:
---------8<---------
creating message from statically defined input:
---------8<---------
---------8<---------
DLCX 7 1@mgw MGCP 1.0
I: %s

View File

@@ -18,6 +18,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#include <string.h>
#include <osmocom/core/msgb.h>
@@ -271,6 +273,109 @@ void test_mgcp_client_cancel()
fprintf(stderr, "%s() done\n", __func__);
}
struct sdp_section_start_test {
const char *body;
int expect_rc;
struct mgcp_response expect_params;
};
static struct sdp_section_start_test sdp_section_start_tests[] = {
{
.body = "",
.expect_rc = -EINVAL,
},
{
.body = "\n\n",
},
{
.body = "\r\n\r\n",
},
{
.body = "\n\r\n\r",
},
{
.body = "some mgcp header data\r\nand header params"
"\n\n"
"m=audio 23\r\n",
.expect_params = {
.audio_port = 23,
},
},
{
.body = "some mgcp header data\r\nand header params"
"\r\n\r\n"
"m=audio 23\r\n",
.expect_params = {
.audio_port = 23,
},
},
{
.body = "some mgcp header data\r\nand header params"
"\n\r\n\r"
"m=audio 23\r\n",
.expect_params = {
.audio_port = 23,
},
},
{
.body = "some mgcp header data\r\nand header params"
"\n\r\n"
"m=audio 23\r\n",
.expect_rc = -EINVAL,
},
{
.body = "some mgcp header data\r\nand header params"
"\r\n\r"
"m=audio 23\r\n",
.expect_rc = -EINVAL,
},
{
.body = "some mgcp header data\r\nand header params"
"\n\r\r"
"m=audio 23\r\n",
.expect_rc = -EINVAL,
},
};
void test_sdp_section_start()
{
int i;
int failures = 0;
for (i = 0; i < ARRAY_SIZE(sdp_section_start_tests); i++) {
int rc;
struct sdp_section_start_test *t = &sdp_section_start_tests[i];
struct mgcp_response *r = talloc_zero(ctx, struct mgcp_response);
r->body = talloc_strdup(r, t->body);
printf("\n%s() test [%d]:\n", __func__, i);
fprintf(stderr, "\n%s() test [%d]:\n", __func__, i);
fprintf(stderr, "body: \"%s\"\n", osmo_escape_str(r->body, -1));
rc = mgcp_response_parse_params(r);
fprintf(stderr, "got rc=%d\n", rc);
if (rc != t->expect_rc) {
fprintf(stderr, "FAIL: Expected rc=%d\n", t->expect_rc);
failures++;
}
if (rc) {
talloc_free(r);
continue;
}
fprintf(stderr, "got audio_port=%u\n", t->expect_params.audio_port);
if (r->audio_port != t->expect_params.audio_port) {
fprintf(stderr, "FAIL: Expected audio_port=%u\n", t->expect_params.audio_port);
failures++;
}
talloc_free(r);
}
OSMO_ASSERT(!failures);
}
static const struct log_info_cat log_categories[] = {
};
@@ -284,7 +389,7 @@ int main(int argc, char **argv)
{
ctx = talloc_named_const(NULL, 1, "mgcp_client_test");
msgb_talloc_ctx_init(ctx, 0);
osmo_init_logging(&log_info);
osmo_init_logging2(ctx, &log_info);
log_set_print_filename(osmo_stderr_target, 0);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
@@ -297,6 +402,7 @@ int main(int argc, char **argv)
test_crcx();
test_mgcp_msg();
test_mgcp_client_cancel();
test_sdp_section_start();
printf("Done\n");
fprintf(stderr, "Done\n");

View File

@@ -12,4 +12,54 @@ DLMGCP Cannot find matching MGCP transaction for trans_id 1
- canceling again does nothing
DLMGCP Cannot cancel, no such transaction: 1
test_mgcp_client_cancel() done
test_sdp_section_start() test [0]:
body: ""
DLMGCP MGCP response: cannot find start of SDP parameters
got rc=-22
test_sdp_section_start() test [1]:
body: "\n\n"
got rc=0
got audio_port=0
test_sdp_section_start() test [2]:
body: "\r\n\r\n"
got rc=0
got audio_port=0
test_sdp_section_start() test [3]:
body: "\n\r\n\r"
got rc=0
got audio_port=0
test_sdp_section_start() test [4]:
body: "some mgcp header data\r\nand header params\n\nm=audio 23\r\n"
got rc=0
got audio_port=23
test_sdp_section_start() test [5]:
body: "some mgcp header data\r\nand header params\r\n\r\nm=audio 23\r\n"
got rc=0
got audio_port=23
test_sdp_section_start() test [6]:
body: "some mgcp header data\r\nand header params\n\r\n\rm=audio 23\r\n"
got rc=0
got audio_port=23
test_sdp_section_start() test [7]:
body: "some mgcp header data\r\nand header params\n\r\nm=audio 23\r\n"
DLMGCP MGCP response: cannot find start of SDP parameters
got rc=-22
test_sdp_section_start() test [8]:
body: "some mgcp header data\r\nand header params\r\n\rm=audio 23\r\n"
DLMGCP MGCP response: cannot find start of SDP parameters
got rc=-22
test_sdp_section_start() test [9]:
body: "some mgcp header data\r\nand header params\n\r\rm=audio 23\r\n"
DLMGCP MGCP response: cannot find start of SDP parameters
got rc=-22
Done

View File

@@ -43,7 +43,11 @@ C: 2f
I: 11
M: sendrecv
v=0
o=- 2f 23 IN IP4 127.0.0.1
s=-
c=IN IP4 192.168.100.23
t=0 0
m=audio 1234 RTP/AVP 255
Generated DLCX message:
@@ -78,4 +82,24 @@ I: 1
v=0
-----
test_sdp_section_start() test [0]:
test_sdp_section_start() test [1]:
test_sdp_section_start() test [2]:
test_sdp_section_start() test [3]:
test_sdp_section_start() test [4]:
test_sdp_section_start() test [5]:
test_sdp_section_start() test [6]:
test_sdp_section_start() test [7]:
test_sdp_section_start() test [8]:
test_sdp_section_start() test [9]:
Done