Compare commits

...

155 Commits
1.0.2 ... 1.3.0

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
Philipp Maier
5dbfc78aff network: use originating RTP packet address for loopback
When a connection is created in loopback mode all incoming
packets should be reflected back to their origin. If the
user did not supply a destination address with the CRCX
command all incoming packets will be tossed because no
destination address is officially known yet.

If there is no destination address set and the connection
is in loopback mode. Then use the originating address of
the incoming packet as destination address.

Change-Id: I3d1abe56d016e28c97f60635eb574679d36e2c52
2017-12-12 16:27:14 +01:00
Philipp Maier
7bc5552512 mcgp_client: mgcp_msg_gen(): add checks to verify params
mgcp_msg_gen() does not check the contents of the prameters that
are handed over with the struct. This may lead to invalid mgcp
messages sent to the MGW, which can be difficult to debug.

Add some additional checks to make a possible problem
noticeable in an early stage.

- verify that the endpoint is not a nullstring
- verify that the connection id is not a nullstring
- verify that the ip-address is not a nullstring
- verify that the port number is a value greater 0

Change-Id: I15c464c4bcdf6e524f68acc62f44186dd7ad19a7
2017-12-10 23:22:14 +01:00
Philipp Maier
a330b864e5 mgcp_test: fix possible double free
Change-Id: I2d044382c0bb66e190400e3397449c3f2387359a
2017-12-04 17:34:11 +01:00
Philipp Maier
7df419b434 mgcp_test: add returncode check
The returncode of get_conn_id_from_response() is unchecked, which
is intentional since some of the test messages will intentionally cause
this function to fail (the response does not inclde a connection
identifier). This means it makes no sense to assert this function
to a fixed value.

In order to spot regressions better print a log message depending
on the return code.

Change-Id: I9bce9ca39b7751b557374b7ab57c6c9005bcdb7d
Fixes: Coverity CID#180534
2017-12-04 17:34:11 +01:00
Philipp Maier
7cedfd753b mgcp_test: fix nullpointer dereference
Change-Id: Ic2ee79eaaca2fada673baf6ff4c226aa16c26269
Fixes: Coverity CID#180536
2017-12-04 17:33:59 +01:00
Philipp Maier
23b8e29835 mgcp_test: fix wrong strcmp() parameters
The CRCX string parameter lacks the quotes and the result of
the function call is not checked against zero. Also the
return code of get_conn_id_from_response() is not asserted.

Fixes: Coverity CID#180534

Change-Id: If4f3ed2c3572da196160569a9705b7a302b700a9
2017-12-04 16:51:40 +01:00
Philipp Maier
1c3287f1db conn: remove assertions
the assertions in the code that handles the connection
lists introduce a lot of unnecessary bloat.

Change-Id: I7badc729e97b76701abbce6a73a1ad1e46d3fee0
2017-12-04 16:04:10 +01:00
Philipp Maier
922ae9a5bf client: use string as connection identifier
The test that tests the cancelation of a pending mgcp message
uses an integer as connection identifier, which leads to a
segfault since connection identifiers are represented as strings.

Use a string as connection identifier.

Change-Id: I395a23c1828cf216031d69d481ad35dd458ee7d4
2017-12-04 16:04:10 +01:00
Neels Hofmeyr
189d6bf4d4 mgcp_client_test makefile: add update_exp target
Change-Id: I8cda082c46ab5734873cbb39b313037d86777589
2017-12-03 22:47:38 +00:00
Neels Hofmeyr
c8f37cb4d6 mgcp_client: add transaction cleanup
So far, if an MGCP message is sent, the transaction gets enqueued, but there is
no way to end the transaction other than receiving a valid reply. So, if the
caller decides that the transaction timed out and tears down the priv pointer
passed to mgcp_client_tx, and if then a late reply arrives, the callback will
dereference the invalid priv pointer and cause a segfault. Hence it is possible
to crash an mgcp_client program by sending a late response.

Furthermore, if no reply ever arrives, we would keep the pending response in
the list forever, amounting to a "memory leak".

Add mgcp_client_cancel() to discard a pending transaction. The caller can now
decide to discard a pending response when it sees fit (e.g. the caller's
timeout expired). This needs to be added to OsmoMSC and OsmoBSC.

Add mgcp_msg_trans_id() to provide an obvious way to obtain the transaction id
from a generated MGCP message.

No public API is broken; but refine the negative return code from
mgcp_client_rx(): return -ENOENT if no such transaction ID is found, and still
-1 if decoding failed. This is mainly for mgcp_client_test.

Implement a test for mgcp_client_cancel() in mgcp_client_test.c.

Tweak internal mgcp_client_response_pending_get() to take only the transaction
id as argument instead of the entire mgcp message struct.

Found-by: dexter
Related: OS#2695 OS#2696
Change-Id: I16811e168a46a82a05943252a737b3434143f4bd
2017-12-03 22:47:01 +00:00
Neels Hofmeyr
c0dcc3c60c Revert "mgcp_client: don't configure "bts base"" until osmo-msc is ready
OsmoMSC is in the odd situation that it is already using the new 
libosmo-mgcp-client, which is targeted at osmo-mgw, to configure talking to the
old osmo-bsc_mgcp. By removing the bts_base, we break current OsmoMSC.

Removing bts_base makes sense, but let's revert this until OsmoMSC is ready
after merging Ieea9630358b3963261fa1993cf1f3b563ff23538 (which moves the 
osmo-msc over to osmo-mgw).

This reverts commit 0be3ce66c0.

Change-Id: Ibce214c2bfc35623097abbb647619426ef3dcc94
2017-12-02 18:37:18 +00:00
Philipp Maier
230e4fc270 cosmetic: clearly mark endpoint numbers as hex
The log prints the endpoint numbers as hexadecimal numbers, but
it does not prefix them with "0x".

Add "0x" prefixes to all endpoint number outputs in the log

Change-Id: I284627de02cd140a894445375e9152ff007a71e6
2017-12-01 11:58:24 +00:00
Philipp Maier
ead2f60f73 cosmetic: fix sourcecode formatting
Change-Id: I1a4eda30986e07237bb7b496704f36f03d25a149
2017-12-01 11:58:24 +00:00
Philipp Maier
f8bfbe8b14 client: use osmo_strlcpy instead of strncpy
simplify \nul termination of the ip_addr string

Change-Id: I94e3815f45d08e0d40faf41e580547de937c4ce8
2017-12-01 11:58:24 +00:00
Philipp Maier
ffd75e420c libosmo-mgcp: Connection Identifiers are allocated by MGW, not CA
The MGCP connection identifier is allocated by the MGW while processing
the CRCX, see RFC3435 2.1.3.2:. Including/Accepting a connection
identifier in CRCX is "forbidden" as per RFC3435 Section 3.2.2.

So the MGW side must *reject* a CRCX message with 'I' parameter, and
allocate a connection identifier which is subsequently returned in the
response.

Closes: OS#2648
Change-Id: Iab6a6038e7610c62f34e642cd49c93d11151252c
2017-12-01 11:58:24 +00:00
Philipp Maier
01d24a3281 MGCP: Connection Identifiers are hex strings
The MGCP spec in RFC3435 is quite clear: Connection Identifiers are
hexadecimal strings of up to 32 characters. We should not print and
parse them as integers on either client or server.

Change the internal uint32_t representation of connection identifiers
to a string representation in the client and also in the server.

Closes: OS#2649
Change-Id: I0531a1b670d00cec50078423a2868207135b2436
2017-12-01 11:58:23 +00:00
Neels Hofmeyr
0be3ce66c0 mgcp_client: don't configure "bts base"
There should not be any BTS base port to be configured at an MGCP client.

Possibly this is related to the legacy behavior of libosmo-legacy-mgcp, and
certainly has no place in libosmo-mgcp-client.

Further changes may be needed to follow up on removal of the BTS base port
concept, at least drop it from the VTY for now.

Change-Id: I36e46208d7b75611e5ade3c74d8e1c25870de511
2017-11-30 12:18:57 +01:00
Philipp Maier
fcd0655176 vty: do not change number_endpoints at runtime
The variable number_endpoints is used as a length indicator
for the array that contains the trunk endpoints at all times.
When osmo-mgw is startet up, the variable is set and osmo-mgw
will allocate the memory for the endpoints. However, it is
still possible to manipulate the variable via the telnet
interface. When the value is increased osmo-mgw might start
using unallocated memory at some point.

Store subsequent changes of number_enspoints in a separate
variable in order to write them to the config file. The
changes will then take effect after a restart.

Closes: OS#2632
Change-Id: I3994af016fb96427263edbba05f560743f85fdd4
2017-11-27 14:38:29 +00:00
Philipp Maier
48454983c4 vty: simplify endpoint allocation
mgcp_parse_config() uses a helper function allocate_trunk() to
perform the trunk allocation. This helper function only calls
mgcp_endpoints_allocate() and checks the return code.

Call mgcp_endpoints_allocate() directly from mgcp_parse_config()

Change-Id: Iefdc5b905d76d2cd97f26584261fe5cbefb699cf
2017-11-27 11:01:43 +01:00
Philipp Maier
9a3543a8d4 cosmetic: use correct VTY port number constant
osmo-mgw currently uses VTY port number constant of osmo-bsc_mgwp,
however, libosmore now offers a constant specifically for osmo_mgw,
which has the same value, but is intended to be used by osmo-mgw

use the new port number constant for osmo-mgw

Closes: OS#2628
Change-Id: I63c3b300cc9287d1755a3f2c5b5ade7fc6398f6e
Depends: libosmocore I1770787e697906322ce5815fcaadba06c01ddee9
2017-11-24 12:40:51 +01:00
Neels Hofmeyr
465446b4b4 mgcp_test: sanitize: free msgb_ctx
Ensure that all msgb were cleaned up, then free the overall msgb_ctx, in order
to not leave any memory leaks the sanitizer build complains about.

Change-Id: I53373023a6c3f490d6d6cb1c283db5dfb915882c
2017-11-22 02:59:03 +01:00
Neels Hofmeyr
b597b4fc9d mgcp_test: test_no_cycle: sanitize: free endp
Release endpoint to avoid sanitizer errors.

Change-Id: I78d16ffc435c0f967fe99c6e38dde829b6fa0dc9
2017-11-22 02:58:27 +01:00
Neels Hofmeyr
d20910c655 mgcp_test: test_packet_error_detection: sanitize: free all conns
Don't leave any memory leaks a sanitizer build will complain about.

Change-Id: I6823653d5efcebaed40471123d21a9321cf633fd
2017-11-18 21:27:55 +01:00
Neels Hofmeyr
677f4ad968 legacy_mgcp: mgcp_test: sanitize: free msgb_ctx
Ensure that all msgb were cleaned up, then free the overall msgb_ctx, in order
to not leave any memory leaks the sanitizer build complains about.

Change-Id: I84e0ac7f0928f04ffddd7da18200466841589c25
2017-11-18 21:26:27 +01:00
Neels Hofmeyr
7c20c9de5a add --enable-sanitize config option
Change-Id: I2693238c5c8d914cf3ff7721511e7b4b56e413d2
2017-11-18 10:17:39 +00:00
Harald Welte
839fc954d0 osmo-mgw: Fix copyright notice
The copyright statement and contribution notices were copied 1:1
from OpenBSC, which is of course not correct for OsmoMGW.

Change-Id: I6143becdd0da589451efcbda530a78f655b7ce0b
2017-11-17 20:58:22 +01:00
Harald Welte
d164b05655 osmo-mgw: Config file is osmo-mgw.cfg, and not mgcp.cfg
Change-Id: I16016684ee5bd6d82a8867d4b2be108b6d718291
2017-11-17 15:41:24 +01:00
Harald Welte
a896f9cdeb osmo-mgw: Update copyright statement
sysmocom (specifically Philipp) was doing all the new osmo-mgw
development, but that is not yet reflected in the Copyright statement
for some reason.  Let's fix it.

Change-Id: I4cad29daaabec1caec1bd09088414e59fa15a17e
2017-11-17 15:08:41 +01:00
Harald Welte
1b0cf6fa1c Fix possible buffer overflow in mgcp_conn_dump()
mgcp_conn.c: In function ‘mgcp_conn_dump’:
mgcp_conn.c:248:30: warning: ‘/rtp, id:’ directive output may be truncated writing 9 bytes into a region of size between 0 and 255 [-Wformat-truncation=]
   snprintf(str, sizeof(str), "(%s/rtp, id:%u, ip:%s, "
                              ^~~~~~~~~~~~~~~~~~~~~~~~~
mgcp_conn.c:248:30: note: directive argument in the range [0, 65535]
mgcp_conn.c:248:30: note: directive argument in the range [0, 65535]
mgcp_conn.c:248:3: note: ‘snprintf’ output 32 or more bytes (assuming 295) into a destination of size 256
   snprintf(str, sizeof(str), "(%s/rtp, id:%u, ip:%s, "

as mgcp_conn->name can already be up to 256 bytes, a total buffer size
of 256 is insufficient!

Change-Id: I5d48132b1358d19fe72e3901117737b09a42c69c
2017-11-17 14:28:46 +01:00
Harald Welte
9bf7c53779 cosmetic: fix whitespaces; we use tabs for indentation
Change-Id: I547f650bcda369ea399c5171a3541a96862d978b
2017-11-17 14:18:24 +01:00
Alexander Couzens
6be0fdb5e4 debian: include systemd service osmo-mgw.service
Change-Id: Ic298b836620b3497734aed89b92c4f22a9071f0d
2017-11-16 15:54:55 +00:00
Alexander Couzens
641c4d47b9 debian/control: correct library dependency of osmo-mgw against libosmo-mgcp1
When the library version was bump, the dependency of osmo-mgw
was forgotten.

Change-Id: I3eeafa3c294d9ec71a72fb2833fe3b2bdef05a50
Fixes: e7d27aeae1 ("Tag/Release Version 1.2.0")
2017-11-16 15:54:54 +00:00
Neels Hofmeyr
c1b9fa158e MGCP endpoints: parse as decimal, not hex
Parse the endpoint index from the MGCP messages as base-10, not 16.

If osmo-mgw parses the endpoint IDs as base-16 numbers while OsmoMSC and
OsmoBSC pass in decimal endpoint numbers, the consequence is, for example:

- I configure 32 endpoints in osmo-mgw,
- I tell OsmoBSC to use endpoint range 1-32,
- At some point OsmoBSC may pass in, say, "30@mgw",
- "30" is parsed base-16 and ends up being endpoint index 48, instead of 32,
- OsmoMGW sees that 48 > number_endpoints and barfs.

Related: OS#2633
Change-Id: Ic18608ff23303c1564548a86d5f6bfa539fe555e
2017-11-15 23:02:05 +00:00
Philipp Maier
6a421f1b6f doc: update sample config file
The current example configuration is out of date.

Add a recent configuration file

Change-Id: Iad2034ce4c68bb8b70cb72d3978d2a0f685bbe19
2017-11-12 14:22:23 +00:00
Philipp Maier
f1889d8640 cosmetic: remove prefix "net" from rtp related vty commands
There the prefix "net" is a leftover from the time when
there was a bts and a net side. Now we do not distinguish
anymore between the two.

remove prefix "net"

Change-Id: Id627e2ef6f725979ed52a585ca09686e1a049adf
2017-11-12 14:22:22 +00:00
Philipp Maier
2982e42d93 cosmetic: guard dead osmux vty code with ifdef
Since currently osmux is not available we decided to lock down the
respective VTY command with an early return CMD_WARNING, making
the code after this line unreachable.

Guard the dead code with an ifdef

Fixes: Coverity CID#178648
Change-Id: I2ad9579453f52fe129cf120801a2efa19a26304e
2017-11-10 21:32:35 +00:00
Philipp Maier
ddf1f9d7d5 osmux: fix nullpointer dereference
in point_lookup() the connection pointer is determined using
mgcp_conn_get_rtp() this function may return 0. At the moment
there are no nullpointer checks implemented

Add checks to test for nullpointer.

Fixes: Coverity CID#178662
Change-Id: If9a3c1ac002bc8adc90ca1c1c3dd1db4feea07ac
2017-11-10 21:30:19 +00:00
Neels Hofmeyr
653c4ff354 fix segfault: DLCX for unknown endpoint: dont try to log NULL endpoint
Change-Id: Ib127fd81a2d68f51c470ff77ef0822bdb4829de4
2017-11-10 16:23:36 +01:00
Pau Espin Pedrol
4efce88a62 contrib: Add osmo-mgw systemd service
Change-Id: Ic89815dc4ef8dff8a9d8541b61212ab8d4837712
2017-11-10 10:31:39 +00:00
Harald Welte
e7d27aeae1 Tag/Release Version 1.2.0
Change-Id: Ieff5a16285d2d2d46ad8977713fec622ad0596a7
2017-11-10 11:21:40 +09:00
Philipp Maier
e6f172dd57 network: remove unused return code
The function that forwards the tapped (voice ebug) traffic returns
its status (sendto) to the caller. However, none of the callers
seem need this info.

Remove the return code and print an error message on failure

Fixes: Coverity CID#178666
Change-Id: I16c12c4565bccbc0d75c412b43469bf70b6b7ea5
2017-11-08 19:14:06 +00:00
Philipp Maier
b969455941 network: fix rtp packet length
When sending rtp packets sizeof(buf) is used as length. This causes
all RTP packets to be the size of the buffer (4096) containing the
rtp payload and random excess data from previoes memory usage

Use the actual length of the received RTP data, rather then the
full buffer size.

Change-Id: I47a15701f9a7e7a492df183b67ec971f5be61069
2017-11-08 16:57:31 +01:00
Neels Hofmeyr
54dd4b3f72 mgcp client: vty: tweak doc strings
Fix errors like "remote bind address", mention 'MGW' instead of 'MGCP gateway',
minor typos and wording tweaks.

Change-Id: Ie1a408f9e651c5fb3424a84ceaaa603e20ad595c
2017-11-06 16:35:46 +00:00
Pau Espin Pedrol
dbd88e5167 mgcp_client_vty.c: Fix VTY compatibility with 'mgcpgw bts-base'
Commit 87203f2a37 renamed some cmds to use
mgw instead of mgcpgw, and added deprecated alias for the old commands,
but forgot to add one for 'mgcpgw bts-base'. This commit fixes
backawards compatibility with old config files that mentioned commit
introduced.

Change-Id: Ib1c58945f4203b05d79f367afb3082b9a6a2c4e3
2017-11-06 14:02:34 +01:00
Neels Hofmeyr
87203f2a37 mgcp-client vty: use name 'mgw' instead of 'mgcpgw'
'mgcpgw' was a working title for the osmo-mgw. Before this takes hold out
there, let's rename the VTY commands to 'mgw'. I'd rather have some local
fallout in our testing environments now than drag the stupid name along.

Keep deprecated 'mgcpgw' commands for backwards compat.

Change-Id: I1d43d42929dc9162e57640499526fb7cadbcfbe6
2017-11-05 19:32:48 +00:00
Philipp Maier
1cb1e38dbc network: autdetect rtp bind ip-address
Currently there are two ways to set the rtp bind ip-address (local
ip address where the rtp streams are bound to). It is possible to
set no set an rtp bind ip, if none no address is configured, then
the ip address where the mgcp service is bound to is used.

On a system with multiple network interfaces it is likely that
there are the remote end is not reachable through the interface
that has been configured. In this case rtp ip-probing can be
enabled via vty option in order to automatically detect the
ip address of the interface that points towards the remote end.

The autodetection can only work if the ip-address is known when
a CRCX is performed. For this the remote entity must include the
remote ip address in the sdp trailer.

Implement probing to determine te right local ip address
Add debug log to display which ip address is actually used
Add a VTY option for the probing functionality.

Change-Id: Ia57cf7dab8421fd3ab6e1515727db0080373485e
2017-11-02 17:16:04 +01:00
Philipp Maier
e726d4fad2 cosmetic: make dummy packet handling more explicit
The way how osmo-mgw decides when to send a dummy packet and
when not is not very obvious.

use more explicit if statements, and define constants. Also add
comments that explain how it works.

Change-Id: Ie7ee9409baec50a09fb357d655b5253434fae924
2017-11-02 11:47:44 +01:00
Philipp Maier
c341388b30 network: add separate log category
the network (mgcp_network.c) part and the protocol part
(mgcp_protoocl.c) share a single loglevel DLMGCP. This
makes debuging hard because when debugging the protocol
we also get the log output from the RTP packets.

assign the network part a private loglevel and keep DLMGCP
for the directly MGCP related code

Change-Id: I55a2711798d1d1c2c9ef2f3b7ebb8fdd78bd6ea2
2017-11-02 11:47:44 +01:00
Philipp Maier
7bf4ce3aed mgcp: remove port/timeslot calculator functions from mgcp.h
the functions rtp_calculate_port(), mgcp_timeslot_to_endpoint(),
mgcp_endpoint_to_timeslot() were a hack to map CIC addresses
to endpoints and ports. This is no longer needed.

Remove the affected functions.

Change-Id: I9ef14396dc9f97e570d9bcfb4d9b4a94e650ad46
2017-11-02 11:47:44 +01:00
Philipp Maier
f4c0e37352 protocol: allow wildcarded DLCX
In many cases it is simpler to instruct the mgcp-gw to drop all
connections at once instead of removing each connection
individually.

drop all connections and release the endpoint in when no connection
id is supplied with the DLCX command.

Change-Id: Ib5fcc72775bf72b489ff79ade36fb345d8d20736
2017-11-02 11:47:44 +01:00
Philipp Maier
06da85ed3a client: add ip address parsing to the client
Some MGCP messages (CRCX, MDCX) return IP-Addresses. Make use of
this information.

Change-Id: I44b338b09de45e1675cedf9737fa72dde72e979a
2017-11-02 11:47:44 +01:00
Philipp Maier
1dc6be6a82 client: add unified function to generate MGCP messages
currently the only way to generate MGCP messages is to use
mgcp_msg_crcx(), mgcp_msg_mdcx() and mgcp_msg_dlcx(). All
three function take a fixed set of parameters via their
parameter list. There is no way to add or leave away optional
parameters.

add function mgcp_msg_gen(), this function takes a unified
message struct. The struct features a presence bitmask which
allows to enable and disable parameters as needed. It is also
possible to add new parameters in the future without breaking
the API.

Depends: libosmocore I15e1af68616309555d0ed9ac5da027c9833d42e3

Change-Id: I29c5e2fb972896faeb771ba040f015592487fcbe
2017-11-02 11:47:44 +01:00
Philipp Maier
8348abb372 client: fix stderror logging in unit-test
When testing the file name and the line numbers are output to
stderr, this causes the test to fail when change moves the
lines.

Disable line numbers in the stderror log when testing, also
disable timestamps and colors. Make sure the log category
is print.

Change-Id: I7f1bd9454188f0ca869dada1fcc2877b789cc0ac
2017-11-02 11:47:44 +01:00
Philipp Maier
d8d7b4b188 cosmetic: correct whitespaces
Change-Id: I22302ae8a48a2c776025a60267f5ef5af706e576
2017-11-02 11:47:44 +01:00
Philipp Maier
31c4305225 cosmetic: fix commenting style
Change-Id: Ib717d9dd3b17250ef4d6c67be0463a407cb83fbe
2017-11-02 11:47:44 +01:00
Philipp Maier
c7a228aceb cosmetic: fix coding style for mgcp_parse_sdp_data()
move variable declaration to the top

remove brackets in case statement

correct whitespaces

Change-Id: I6dcf53ef8d3af5885b8b1f258d963949fa3ee93a
2017-11-02 11:47:44 +01:00
Philipp Maier
e472b4e39a cosmetic: rename bts_codec to codec_str
make variable name more meaningful

e enter the commit message for your changes. Lines starting

Change-Id: I4d4d5af8925d032155ca1ef8c7d7d2e496a60fb6
2017-11-02 11:47:44 +01:00
Philipp Maier
8970c497bc sdp: refactoring sdp parser/generator
move SDP generator function write_response_sdp() from mgcp_protocol.c
to mgcp_sdp.c and use msgb_printf() instead of snprintf()

move prototypes for mgcp_parse_sdp_data() and mgcp_set_audio_info()
to mgcp_sdp.h

change parameter list of mgcp_parse_sdp_data() so that it takes the
rtp conn directly, rather than struct mgcp_rtp_end.

add doxygen comments to all public functions

Change-Id: I9f88c93872ff913bc211f560b26901267f577324
2017-11-02 11:47:44 +01:00
Neels Hofmeyr
1e0b9f872c vty: skip installing cmds now always installed by default
vty_install_default() and install_default() will soon be deprecated.

Depends: I5021c64a787b63314e0f2f1cba0b8fc7bff4f09b
Change-Id: I246853156c4bd2a47690e580e647105eb838ca92
2017-11-01 00:50:52 +01:00
Neels Hofmeyr
8863f7ae64 jenkins: use osmo-clean-workspace.sh before and after build
See osmo-ci change I2409b2928b4d7ebbd6c005097d4ad7337307dd93 for rationale.

Depends: I2409b2928b4d7ebbd6c005097d4ad7337307dd93
Change-Id: I5a64b305dff5387cbe2462b564051f807061086d
2017-10-28 15:08:04 +00:00
Harald Welte
1ae2d5ebf4 Tag/Release 1.1.0
This marks the first release that includes the new libosmo-mgcp
as well as some updates to libosmo-mgcp-client.  Hence, all programs
using those libraries can now depend on a proper minimum version

Change-Id: I1748ed230041930b4e9f49deb03341772ab02144
2017-10-28 12:54:53 +02:00
Harald Welte
5852a78d2e libosmo-mgcp-client: Bump LIBVERSION as interfaces have been added
Change-Id: I3e00af70267563a5fbf7e2a3c1326c22481d2666
2017-10-28 12:54:35 +02:00
Harald Welte
59e2d518fd libosmo-legacy-mgcp: Link against libosmovty, don't link libosmogsm
This fixes the following dpkg-shlibdeps warnings:

Change-Id: I648bbda50520808afcf2a6ce64fe710df918936c
dpkg-shlibdeps: warning: symbol install_element_ve used by debian/libosmo-legacy-mgcp0/usr/lib/x86_64-linux-gnu/libosmo-legacy-mgcp.so.0.0.0 found in none of the libraries
dpkg-shlibdeps: warning: symbol argv_concat used by debian/libosmo-legacy-mgcp0/usr/lib/x86_64-linux-gnu/libosmo-legacy-mgcp.so.0.0.0 found in none of the libraries
dpkg-shlibdeps: warning: symbol vty_out used by debian/libosmo-legacy-mgcp0/usr/lib/x86_64-linux-gnu/libosmo-legacy-mgcp.so.0.0.0 found in none of the libraries
dpkg-shlibdeps: warning: symbol vty_install_default used by debian/libosmo-legacy-mgcp0/usr/lib/x86_64-linux-gnu/libosmo-legacy-mgcp.so.0.0.0 found in none of the libraries
dpkg-shlibdeps: warning: symbol vty_read_config_file used by debian/libosmo-legacy-mgcp0/usr/lib/x86_64-linux-gnu/libosmo-legacy-mgcp.so.0.0.0 found in none of the libraries
dpkg-shlibdeps: warning: symbol install_element used by debian/libosmo-legacy-mgcp0/usr/lib/x86_64-linux-gnu/libosmo-legacy-mgcp.so.0.0.0 found in none of the libraries
dpkg-shlibdeps: warning: symbol install_node used by debian/libosmo-legacy-mgcp0/usr/lib/x86_64-linux-gnu/libosmo-legacy-mgcp.so.0.0.0 found in none of the libraries
2017-10-28 12:11:29 +02:00
Harald Welte
062b0f0eee osmo-bsc_mgcp: Don't link against libosmo-netif
This fixes the following dpkg-shlibdeps warning:

Change-Id: I1eb079aa74a56b75e881b7abae0e5d9b4d7ae9c4
dpkg-shlibdeps: warning: package could avoid a useless dependency if debian/osmo-bsc-mgcp/usr/bin/osmo-bsc_mgcp was not linked against libosmonetif.so.4 (it uses none of the library's symbols)
2017-10-28 12:08:19 +02:00
Harald Welte
71c619ee05 libosmo-mgcp-client: don't link against libosmonetif
This fixes the following dpkg-shlibdeps warning:

Change-Id: If60583b2bec344fc674af6f129787206540bc9fc
dpkg-shlibdeps: warning: package could avoid a useless dependency if debian/libosmo-mgcp-client0/usr/lib/x86_64-linux-gnu/libosmo-mgcp-client.so.0.0.0 was not linked against libosmonetif.so.4 (it uses none of the library's symbols)
2017-10-28 12:06:38 +02:00
Harald Welte
e3b5de2285 libosmo-mgcp-client: Don't link libosmogsm, but link libosmovty
This addresses the following dpkg-shlibdeps warnings:

Change-Id: I737c36402b7b88634b56725f2caab4f5f971ac51
dpkg-shlibdeps: warning: symbol install_element used by debian/libosmo-mgcp-client0/usr/lib/x86_64-linux-gnu/libosmo-mgcp-client.so.0.0.0 found in none of the libraries
dpkg-shlibdeps: warning: symbol vty_out used by debian/libosmo-mgcp-client0/usr/lib/x86_64-linux-gnu/libosmo-mgcp-client.so.0.0.0 found in none of the libraries
2017-10-28 12:05:22 +02:00
Harald Welte
f96e46fcce libosmo-mgcp: Don't link against libosmogsm; link against libosmovty
This addresses the following dpkg-shlibdeps warnings:

Change-Id: I518eb5e19cef5f261711b034d28337265c69f443
dpkg-shlibdeps: warning: symbol install_element_ve used by debian/libosmo-mgcp0/usr/lib/x86_64-linux-gnu/libosmo-mgcp.so.0.0.0 found in none of the libraries
dpkg-shlibdeps: warning: symbol vty_read_config_file used by debian/libosmo-mgcp0/usr/lib/x86_64-linux-gnu/libosmo-mgcp.so.0.0.0 found in none of the libraries
dpkg-shlibdeps: warning: symbol vty_install_default used by debian/libosmo-mgcp0/usr/lib/x86_64-linux-gnu/libosmo-mgcp.so.0.0.0 found in none of the libraries
dpkg-shlibdeps: warning: symbol install_element used by debian/libosmo-mgcp0/usr/lib/x86_64-linux-gnu/libosmo-mgcp.so.0.0.0 found in none of the libraries
dpkg-shlibdeps: warning: symbol install_node used by debian/libosmo-mgcp0/usr/lib/x86_64-linux-gnu/libosmo-mgcp.so.0.0.0 found in none of the libraries
dpkg-shlibdeps: warning: symbol vty_out used by debian/libosmo-mgcp0/usr/lib/x86_64-linux-gnu/libosmo-mgcp.so.0.0.0 found in none of the libraries
dpkg-shlibdeps: warning: symbol argv_concat used by debian/libosmo-mgcp0/usr/lib/x86_64-linux-gnu/libosmo-mgcp.so.0.0.0 found in none of the libraries
2017-10-28 12:03:01 +02:00
Harald Welte
88d4dae645 osmo-mgw: Don't link against libosmo-netif
Change-Id: Ic62efeb3c139016aa8a1e68651442edf2044eeef
dpkg-shlibdeps: warning: package could avoid a useless dependency if debian/osmo-mgw/usr/bin/osmo-mgw was not linked against libosmonetif.so.4 (it uses none of the library's symbols)
2017-10-28 12:00:11 +02:00
Harald Welte
8042539444 Debian: Include the osmo-bsc-mgcp systemd service in Debian package
Change-Id: Ife3f034ddaaba993bf27c1f9a34a1d57d1bf3e05
2017-10-28 11:57:50 +02:00
Harald Welte
6ba4406c47 Debian: make sure osmo-bsc_mgcp ends up in its own package
... rather than being in the osmo-mgw package, which is clearly wrong.

Change-Id: Ib5d19f56b611722101040639db615184fa223f82
2017-10-28 11:56:38 +02:00
Harald Welte
1e940d615a configure.ac: Depend on latest tagged/released libosmo-* versions
This is the safe choice, as in absence of automatic testing we don't
know if we actually still build against the [sometimes] ancient
dependencies.

Change-Id: I8118defcd270bb487d9fd674fe30a74d379fda18
2017-10-28 11:51:07 +02:00
Philipp Maier
fde78ad412 mgcp: move misplaced include
mgcp.h includes mgcp_ep.h but does not use anything from it. This
does not hurt the mgcp code internally, but when mgcp.h is included
by an application compilation fails because mgcp_ep.h is not
installed.

remove mgcp_ep.h from include list in mgcp.h

add mgcp_ep.h to include list in mgcp_conn.c where it is needed.

Change-Id: Ib58a8ed64f729055282c2b9673f7684cdfdb5936
2017-10-16 14:47:21 +02:00
Philipp Maier
3d9b656a1c mgcp_test: do not send rtp packets
The sendto() override in mgcp_test sends rtp packets out. This
might be a problem for some test hosts. e.g. on OBS, sending packets
fails with an error message, which exits sendto() early and hence fails
to send the expected amount of "Dummy Packets". Interestingly enough
calling the real sendto is not necessary to run the test at all.

Remove the execution of the real_sendto and just return len.

Related: OS#2561
Change-Id: Ia8fa0770f9bc75725cc6b0cd445e753f7e029ca5
2017-10-13 18:41:52 +00:00
Neels Hofmeyr
2152f8523e build: make sure copied mgcp_common.h is removed on 'make clean'
I thought the BUILT_SOURCES also enters the files to CLEANFILES, but 'make
clean' leaves the copied header behind for me. Add to CLEANFILES to get it
removed.

Change-Id: I5a56f83289e32a09643047f0e779c9de3e4b39a6
2017-10-11 08:15:38 +00:00
Alexander Couzens
c5ecebb711 debian/rules: show testsuite.log files when tests are failing
Change-Id: I11aca905350b2e3464fb526ec4d3ae6ffcf55114
2017-10-11 06:05:33 +02:00
Neels Hofmeyr
020e89bce9 mgcp_test: tweak test failure output
Change-Id: Ibfc80f06b2811cc32fe38518b11efd6764b347ee
2017-10-11 02:02:22 +02:00
Neels Hofmeyr
6779354245 drop code dup between libosmo-mgcp-client and libosmo-mgcp
Remove mgcp_common.c and replace with mgcp_common.h.

Move mgcp_common.h from mgcp_client/ to mgcp/;
Place a compile-time copy of it back in mgcp_client/.

Add builddir/include to compiler -I paths to find generated header.

Rationale:
- Keep separate copies of the file for each of the library to not require
  debian dependencies between the two libraries.
- Avoid code dup by copying during 'make' (think: a generated header, BUILT_SOURCE).
- The copy does not have implications for linking (like mgcp_common.c did) nor
  is it a source for build confusion or fallout in other projects, because it
  does not reach across several git source trees (like gsm_data_shared.h did).

mgcp_connection_mode_strs are not actually used in libosmo-mgcp, so drop them.
(It would make semantic sense to have then in mgcp, but we can add it when it
is needed. A similar value string array remains in libosmo-mgcp-client.)

Change-Id: I7a5d3b9a2eb90be7e34b95efa529429f2e6c3ed8
2017-10-05 19:32:58 +02:00
Philipp Maier
87bd9be0b0 Initially implement the new osmo-mgw and libosmo-mgcp
Leave the old osmo-bsc_mgcp and libosmo-legacy-mgcp as it is; on a copy thereof
(added by a previous commit), apply changes to initially implement the new
osmo-mgw.

Adjust build system and debian packaging to accomodate the new libosmo-mgcp and
osmo-mgw.

The main differences:

*) use a list to manage rtp connections.

Aggregate all rtp related information inside a single struct.

Use a linked list to manage the both connections (net and bts).
The idea behind using a list is that we might support conference
calls at some later point.

Store the linked list in struct mgcp_endpoint, have a private linked
list for each endpoint. The list contains connection items which are
implemented in struct mgcp_conn. A connection is allocated and freed
using the functions in mgcp_conn.c. A connection is allocated on the
reception of a CRCX command and freed with the reception of a DLCX
command.

*) remove external transcoder feature

Fortunatelly the external transcoder feature is not needed
anymore. This patch removes the related code.

*) vty: get rid of CONN_BTS and CONN_NET

Since the new connection model does not make a difference
between BTS and NET connections the VTY should not use
the fixed CONN_BTS and CONN_NET constants.

- Handle the conns list inside the endpoint directly
- introduce function to dump basic rtp connection info
- introduce human readable names for connections

Parts of the code adjusted to use generalized connections instead of explicit
BTS/NET ones:

- teach mgcp_send_dummy() to send dummy packets to any RTP connection
- network: generalize mgcp_bind_net/bts_rtp_port()
- network: generalize mgcp_send()
- tap: generalize call tapping feature
- stat: generalize statistics
- Replace rtp_data_net() and rtp_data_bts() with generalized rtp_data_rx()

*) mgcp_protocol.c fixes:

- check ci string before it is converted:
  In case of missing ci, a nullpointer is delivered to strtoul().
  Add a function that takes ci, checks it and converts it to an
  uint32_t. Use the return code to react on missing ci.
- output error message on missing CI.
- when parsing the mode, print log message when mode is missing.
- use mode_orig when mode is missing.
- fix ptime formatstring to use %u rather than %d.
- cosmetic: log when connection is deleted on DLCX.
- change loglevels of CRCX, MDCX, DLCX events from DEBUG to NOTICE.

*) mgcp_test

- apply rename of strline_r() to mgcp_strline().
- MGCP command macros:
  - Add 'I: 1' parameters.
  - Use proper port numbers:
    from m=audio 0 RTP/AVP 126
    to   m=audio 16002 RTP/AVP 128
  - Change ptime to 'a=ptime:40' because this is what the MGW currently
    returns.  CRCX generally feed a ptime:40 and this is expected to be
    returned.
- struct mgcp_test: Use only one ptype, there are no explicit BTS and NET
  endpoints anymore.
  Hence remove one column from tests[].
- test_messages():
  - Enable: remove '#if 0'
  - Remove concept of BTS and NET endpoints: test only one conn, as they are
    now interchangeable anyway.
  - remove endpoint init, now done internally.
  - add false asserts in error cases.
- test_retransmission():
  - remove endpoint init, now done internally.
  - add false asserts in error cases.
- test_packet_error_detection():
  - Remove concept of BTS and NET endpoints: test only one conn, as they are
    now interchangeable anyway. Use arbitrary conn ids (e.g. 4711).
  - remove endpoint init, now done internally.
  - add false assert in error case.
  - Assert that a conn really vanishes on DLCX, previously the conn would
    remain and just be unused, now it is actually discarded.
- test_no_cycle()
  - Remove concept of BTS and NET endpoints: test only one conn, as they are
    now interchangeable anyway. Use arbitrary conn ids (e.g. 4711).
- test_no_name()
  - Enable: remove '#if 0'.
  - remove endpoint init, now done internally.
  - add false assert in error case.
- mgcp_test.ok: adjust expected results to status quo:
  - We now see two dummy packets instead of one, now sent to both sides because
    we don't know of BTS or NET side. (maybe drop dummy packets later...)
  - packet duration, conn mode: now sane defaults show instead of unset.
- various whitespace and formatting changes from lindent.

Change-Id: Ie008599136c7ed8a0dfbb0cf803188975a499fc5
2017-10-05 01:40:43 +00:00
Neels Hofmeyr
f83ec56212 create libosmo-mgcp and osmo-mgw by copying legacy code
This a cosmetic commit, copying libosmo-legacy-mgcp to libosmo-mgcp and
osmo-bsc_mgcp to osmo-mgw 1:1 at first, to provide a basis for next patches
that highlight the changes from legacy to new code.

Until osmo-msc and osmo-bsc are adjusted to operate with the new code, we will
keep the legacy code alongside the new code. The legacy code might be dropped
later.

Change-Id: Idf54481754a1765bdb2d0d7033bc0d7dc2018024
2017-10-05 01:40:43 +00:00
Pau Espin Pedrol
1c8d67d7f5 libosmo-legacy-mgcp: fix link against libgsm
libosmo-legacy-mgcp was not linked against libgsm
when built with --enable-mgcp-transcoding.
When afterwards a binary such as osmo-bsc is built and
tries to link against it, it will fail with an error
like the one below:
/home/pespin/dev/sysmocom/bin/../build/new/out/lib/libosmo-legacy-mgcp.so: undefined reference to `gsm_create'

Tested that building with this patch fixes the issue.
Also tested that it still builds fine without --enable-mgcp-transcoding.

Change-Id: I5ed356ac0c8f476e263fc6dcc5613d594890dfcd
2017-09-26 13:50:32 +02:00
Neels Hofmeyr
9d4f1d57f3 debian: tweak .install: less wildcards
With the upcoming addition of libosmo-mgcp, when we try the same scheme
of wildcards there, it would also include all of the other libraries:

  *mgcp*.so.*

There we will have to clarify that we mean exactly:

  libosmo-mgcp.so.*

This is a cosmetic change to also be less ambiguous for the current libs:
I prefer to state exactly which parts of the file names may vary, so that
we have a firmer understanding of what is being installed.

Change-Id: I6a3f3b9efa08eb9c5cdca3e02f8fab4a96c28ad5
2017-09-24 23:06:06 +02:00
Neels Hofmeyr
d95ab1e9ad libosmo-mgcp-client: fix debian, make self-contained
Add mgcp_common.h to libosmo-mgcp-client, to not need to share a header file
with libosmo-legacy-mgcp (nor the upcoming libosmo-mgcp). Both libraries use
the enum mgcp_connection_mode (and a for-loop macro and a string mangling
function), so far declared in mgcp.h, and both mgcp-dev and mgcp-client-dev
debian packages would require this header file to be installed. So far the
mgcp-client-dev lacks this header file, which causes the osmo-msc debian
package to fail building. Ways to solve:
- If both -dev debian packages installed the same header file in the same
  place, they would conflict if ever installed at the same time.
- mgcp-client-dev can depend on mgcp-dev, but it seems bad to keep such a large
  dependency for just one enum and two helpers.
- Instead, this patch solves this by copying the few definitions to
  libosmo-mgcp-client.

Once libosmo-mgcp-client has its own copy of those definitions, it is
fully self-contained and depending builds (osmo-msc deb) will succeed.

Copy the few actually common definitions to new header
<osmocom/mgcp_client/mgcp_common.h>. The nature of this .h is that it may be
shared with future libosmo-mgcp without causing linking problems.

Remove libosmo-legacy-mgcp/mgcp_common.c file from the build of
libosmo-mgcp-client, no longer needed.

Add to new mgcp_common.h:
- enum mgcp_connection_mode;
- for_each_non_empty_line() macro.
- mgcp_msg_terminate_nul() as static function.  Its complexity is just above
  your average static inline, but being inline is a way to use it in both mgcp
  and mgcp_client without linking problems.

Replace for_each_line() use in mgcp_client with for_each_non_empty_line()
(for_each_non_empty_line() replaces for_each_line() and uses strtok_r() instead
of a local reinvention).

mgcp_connection_mode_strs are actually only used in libosmo-mgcp-client, so
rename to mgcp_client_ prefix and move to mgcp_client.c.

BTW, the future plan for upcoming libosmo-mgcp is to use the identical header
file, and keep only one copy in the git source tree. The second copy may be
generated during 'make', to avoid code dup while having two distinct headers.

Related: I8e3359bedf973077c0a038aa04f5371a00c48fa0 (fix osmo-msc after this),
         I7a5d3b9a2eb90be7e34b95efa529429f2e6c3ed8 (mgcp_common.h)
Change-Id: Ifb8f3fc2b399662a9dbba174e942352a1a21df3f
2017-09-24 19:48:37 +02:00
Neels Hofmeyr
a1fa2f4bc3 tweak API version comments
Clarify LIBVERSION comments in TODO-RELEASE and reference Makefile.am comments
to look there.

Change-Id: I4edf786fde085f612f8a04972c55175a080ac65f
2017-09-21 22:01:55 +00:00
Neels Hofmeyr
c221b7ad01 set API versions back to 0
Roll back the LIBVERSIONs to 0:0:0 -- the bumps so far have been confused and
wrong. Let's start over from scratch with API-current of 0 while we still can.

Rename the mgcp_client debian .install files back to 0 to match the API
version.

Change-Id: I3d81853f811f412b186621c3657bab6af397a980
2017-09-21 00:00:40 +02:00
72 changed files with 12384 additions and 280 deletions

View File

@@ -1,9 +1,26 @@
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
# according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
# In short:
# LIBVERSION=c:r:a
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:0.
# If any interfaces have been added since the last public release: c:r:a + 1.
# If any interfaces have been removed or changed since the last public release: c:r:0.
#library what description / commit summary line
# When cleaning up this file upon a release:
#
# - Note that the release version number is entirely unrelated to the API
# versions. A release version 5.2.3 may happily have an API version of 42:7:5.
#
# - Bump API version in src/lib*/Makefile.am files according to chapter
# "Library interface versions" of the libtool documentation.
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
#
# - Iff the 'current' API version has changed, rename debian/lib*.install
#
# API version bumping for the impatient:
# LIBVERSION=c:r:a (current:revision_of_current:backwards_compat_age)
# 5:2:4 means that
# - this implements version 5 of the API;
# - this is the 2nd (compatible) revision of API version 5;
# - this is backwards compatible to all APIs since 4 versions ago,
# i.e. callers that need API versions from 1 to 5 can use this.
#
# Bumping API versions recipe:
# If the library source code has changed at all since the last update, r++;
# If any interfaces have been added, removed, or changed since the last update, c++, r=0;
# If any interfaces have been added since the last public release, a++;
# If any interfaces have been removed or changed since the last public release, a=0.
#
#library what description / commit summary line

View File

@@ -39,9 +39,40 @@ AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DL)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.9.5)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.0.1)
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(
[--enable-sanitize],
[Compile with address sanitizer enabled],
)],
[sanitize=$enableval], [sanitize="no"])
if test x"$sanitize" = x"yes"
then
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
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.])],
@@ -113,6 +144,9 @@ 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)
@@ -123,14 +157,18 @@ AC_OUTPUT(
include/osmocom/Makefile
include/osmocom/legacy_mgcp/Makefile
include/osmocom/mgcp_client/Makefile
include/osmocom/mgcp/Makefile
src/Makefile
src/libosmo-legacy-mgcp/Makefile
src/libosmo-mgcp-client/Makefile
src/libosmo-mgcp/Makefile
src/osmo-bsc_mgcp/Makefile
src/osmo-mgw/Makefile
tests/Makefile
tests/atlocal
tests/legacy_mgcp/Makefile
tests/mgcp_client/Makefile
tests/mgcp/Makefile
doc/Makefile
doc/examples/Makefile
contrib/Makefile

View File

@@ -14,9 +14,9 @@ deps="$base/deps"
inst="$deps/install"
export deps inst
mkdir "$deps" || true
rm -rf "$inst"
osmo-clean-workspace.sh
mkdir "$deps" || true
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
@@ -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
@@ -45,3 +45,5 @@ LD_LIBRARY_PATH="$inst/lib" \
DISTCHECK_CONFIGURE_FLAGS="$MGCP --enable-vty-tests --enable-external-tests" \
$MAKE distcheck \
|| cat-testlogs.sh
osmo-clean-workspace.sh

View File

@@ -0,0 +1,11 @@
[Unit]
Description=Osmocom Media Gateway (MGW)
[Service]
Type=simple
Restart=always
ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg
RestartSec=2
[Install]
WantedBy=multi-user.target

161
debian/changelog vendored
View File

@@ -1,3 +1,164 @@
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 ]
* jenkins: use osmo-clean-workspace.sh before and after build
* vty: skip installing cmds now always installed by default
* mgcp-client vty: use name 'mgw' instead of 'mgcpgw'
* mgcp client: vty: tweak doc strings
[ Philipp Maier ]
* sdp: refactoring sdp parser/generator
* cosmetic: rename bts_codec to codec_str
* cosmetic: fix coding style for mgcp_parse_sdp_data()
* cosmetic: fix commenting style
* cosmetic: correct whitespaces
* client: fix stderror logging in unit-test
* client: add unified function to generate MGCP messages
* client: add ip address parsing to the client
* protocol: allow wildcarded DLCX
* mgcp: remove port/timeslot calculator functions from mgcp.h
* network: add separate log category
* cosmetic: make dummy packet handling more explicit
* network: autdetect rtp bind ip-address
* network: fix rtp packet length
* network: remove unused return code
[ Pau Espin Pedrol ]
* mgcp_client_vty.c: Fix VTY compatibility with 'mgcpgw bts-base'
-- Harald Welte <laforge@gnumonks.org> Fri, 10 Nov 2017 11:10:23 +0900
osmo-mgw (1.1.0) unstable; urgency=medium
* New upstream release
-- Harald Welte <lafore@gnumonks.org> Sat, 28 Oct 2017 12:48:41 +0200
osmo-mgw (1.0.2) unstable; urgency=low
* First release after major rename.

42
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-legacy-mgcp0, ${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-legacy-mgcp0
Section: libs
Architecture: any
Multi-Arch: same
Pre-Depends: ${misc:Pre-Depends}
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: libosmo-legacy-mgcp: Osmocom's Media Gateway server library
Package: libosmo-legacy-mgcp-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libosmo-legacy-mgcp0 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-legacy-mgcp: Osmocom's Media Gateway server library
Package: libosmo-mgcp-client1
Package: libosmo-mgcp-client3
Section: libs
Architecture: any
Multi-Arch: same
@@ -46,5 +31,26 @@ Package: libosmo-mgcp-client-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libosmo-mgcp-client1 (= ${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
Architecture: any
Multi-Arch: foreign
Depends: libosmo-legacy-mgcp0, ${misc:Depends}, ${shlibs:Depends}
Description: OsmoBSC-MGCP: Osmocom's Legacy Media Gateway; use osmo-mgw instead.
Package: libosmo-legacy-mgcp0
Section: libs
Architecture: any
Multi-Arch: same
Pre-Depends: ${misc:Pre-Depends}
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: libosmo-legacy-mgcp: Osmocom's Legacy Media Gateway server library; use libosmo-mgcp instead.
Package: libosmo-legacy-mgcp-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libosmo-legacy-mgcp0 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-legacy-mgcp: Osmocom's Legacy Media Gateway server library; use libosmo-mgcp instead.

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 +1,4 @@
usr/include/osmocom/legacy_mgcp
usr/lib/*/*legacy-mgcp*.so
usr/lib/*/*legacy-mgcp*.a
usr/lib/*/pkgconfig/*legacy-mgcp*.pc
usr/lib/*/libosmo-legacy-mgcp.so
usr/lib/*/libosmo-legacy-mgcp.a
usr/lib/*/pkgconfig/libosmo-legacy-mgcp.pc

View File

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

View File

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

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

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

1
debian/osmo-bsc-mgcp.install vendored Normal file
View File

@@ -0,0 +1 @@
usr/bin/osmo-bsc_mgcp

1
debian/osmo-bsc-mgcp.service vendored Symbolic link
View File

@@ -0,0 +1 @@
../contrib/systemd/osmo-bsc-mgcp.service

View File

@@ -1 +1 @@
usr/bin
usr/bin/osmo-mgw

1
debian/osmo-mgw.service vendored Symbolic link
View File

@@ -0,0 +1 @@
../contrib/systemd/osmo-mgw.service

3
debian/rules vendored
View File

@@ -26,6 +26,9 @@ CFLAGS += -g
#override_dh_install:
# dh_install --list-missing -X.la -X.pyc -X.pyo
override_dh_auto_test:
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
override_dh_autoreconf:
echo $(VERSION) > .tarball-version
dh_autoreconf

View File

@@ -0,0 +1,18 @@
!
! MGCP configuration example
!
mgcp
bind ip 127.0.0.1
rtp port-range 4002 16000
rtp bind-ip 10.9.1.122
rtp ip-probing
rtp ip-tos 184
bind port 2427
sdp audio payload number 98
sdp audio payload name GSM
number endpoints 31
loop 0
force-realloc 1
rtcp-omit
rtp-patch ssrc
rtp-patch timestamp

View File

@@ -7,4 +7,10 @@ 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 \
osmocom/mgcp/mgcp_internal.h \
osmocom/mgcp/osmux.h \
$(NULL)

View File

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

View File

@@ -177,13 +177,6 @@ enum mgcp_connection_mode {
MGCP_CONN_LOOPBACK = 4 | MGCP_CONN_RECV_SEND,
};
extern const struct value_string mgcp_connection_mode_strs[];
static inline const char *mgcp_cmode_name(enum mgcp_connection_mode mode)
{
return get_value_string(mgcp_connection_mode_strs, mode);
}
struct mgcp_config {
int source_port;
char *local_ip;

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

@@ -0,0 +1,9 @@
noinst_HEADERS = \
vty.h \
mgcp_msg.h \
mgcp_conn.h \
mgcp_stat.h \
mgcp_endp.h \
mgcp_sdp.h \
debug.h \
$(NULL)

View File

@@ -0,0 +1,35 @@
/* (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
#include <stdio.h>
#include <osmocom/core/linuxlist.h>
#define DEBUG
#include <osmocom/core/logging.h>
/* Debug Areas of the code */
enum {
DRTP,
Debug_LastEntry,
};
extern const struct log_info log_info;

238
include/osmocom/mgcp/mgcp.h Normal file
View File

@@ -0,0 +1,238 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/core/msgb.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/logging.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define RTP_PORT_DEFAULT_RANGE_START 16002
#define RTP_PORT_DEFAULT_RANGE_END RTP_PORT_DEFAULT_RANGE_START + 64
/*
* Handling of MGCP Endpoints and the MGCP Config
*/
struct mgcp_endpoint;
struct mgcp_config;
struct mgcp_trunk_config;
struct mgcp_rtp_end;
#define MGCP_ENDP_CRCX 1
#define MGCP_ENDP_DLCX 2
#define MGCP_ENDP_MDCX 3
/*
* what to do with the msg?
* - continue as usual?
* - reject and send a failure code?
* - defer? do not send anything
*/
#define MGCP_POLICY_CONT 4
#define MGCP_POLICY_REJECT 5
#define MGCP_POLICY_DEFER 6
typedef int (*mgcp_realloc)(struct mgcp_trunk_config *cfg, int endpoint);
typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state);
typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id);
typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg);
typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
/**
* Return:
* < 0 in case no audio was processed
* >= 0 in case audio was processed. The remaining payload
* length will be returned.
*/
typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size);
typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
struct mgcp_rtp_end *src_end);
struct mgcp_conn_rtp;
typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
int *payload_type,
const char**subtype_name,
const char**fmtp_extra,
struct mgcp_conn_rtp *conn);
/**
* This holds information on how to allocate ports
*/
struct mgcp_port_range {
/* addr or NULL to fall-back to default */
char *bind_addr;
/* dynamically allocated */
int range_start;
int range_end;
int last_port;
/* set to true to enable automatic probing
* of the local bind IP-Address, bind_addr
* (or its fall back) is used when automatic
* probing fails */
bool bind_addr_probe;
};
/* There are up to three modes in which the keep-alive dummy packet can be
* 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 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)
#define MGCP_KEEPALIVE_NEVER 0
struct mgcp_trunk_config {
struct llist_head entry;
struct mgcp_config *cfg;
int trunk_nr;
int trunk_type;
char *audio_fmtp_extra;
char *audio_name;
int audio_payload;
int audio_send_ptime;
int audio_send_name;
int audio_loop;
int no_audio_transcoding;
int omit_rtcp;
int keepalive_interval;
/* RTP patching */
int force_constant_ssrc; /* 0: don't, 1: once */
int force_aligned_timing;
/* spec handling */
int force_realloc;
/* timer */
struct osmo_timer_list keepalive_timer;
/* When set, incoming RTP packets are not filtered
* when ports and ip-address do not match (debug) */
int rtp_accept_all;
unsigned int number_endpoints;
int vty_number_endpoints;
struct mgcp_endpoint *endpoints;
};
enum mgcp_role {
MGCP_BSC = 0,
MGCP_BSC_NAT,
};
struct mgcp_config {
int source_port;
char *local_ip;
char *source_addr;
char *call_agent_addr;
/* RTP processing */
mgcp_processing rtp_processing_cb;
mgcp_processing_setup setup_rtp_processing_cb;
mgcp_get_format get_net_downlink_format_cb;
struct osmo_wqueue gw_fd;
struct mgcp_port_range net_ports;
int endp_dscp;
int force_ptime;
mgcp_change change_cb;
mgcp_policy policy_cb;
mgcp_reset reset_cb;
mgcp_realloc realloc_cb;
mgcp_rqnt rqnt_cb;
void *data;
uint32_t last_call_id;
/* trunk handling */
struct mgcp_trunk_config trunk;
struct llist_head trunks;
enum mgcp_role role;
/* osmux translator: 0 means disabled, 1 means enabled */
int osmux;
/* addr to bind the server to */
char *osmux_addr;
/* The BSC-NAT may ask for enabling osmux on demand. This tells us if
* the osmux socket is already initialized.
*/
int osmux_init;
/* osmux batch factor: from 1 to 4 maximum */
int osmux_batch;
/* osmux batch size (in bytes) */
int osmux_batch_size;
/* osmux port */
uint16_t osmux_port;
/* Pad circuit with dummy messages until we see the first voice
* message.
*/
uint16_t osmux_dummy;
/* domain name of the media gateway */
char domain[255+1];
};
/* config management */
struct mgcp_config *mgcp_config_alloc(void);
int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
enum mgcp_role role);
int mgcp_vty_init(void);
int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg);
void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
/*
* format helper functions
*/
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);
int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint);
int mgcp_send_reset_all(struct mgcp_config *cfg);
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port);
int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len);

View File

@@ -0,0 +1,85 @@
/* MGCP common implementations.
* These are used in libosmo-mgcp as well as libosmo-mgcp-client.
* To avoid interdependency, these are implemented in .h file only. */
/*
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* Two copies of this file are kept in osmocom/mgcp/ and osmocom/mgcp_client/.
* Since both are by definition identical, use the old header exclusion ifdefs
* instead of '#pragma once' to avoid including both of these files.
* Though at the time of writing there are no such users, this allows including
* both libosmo-mgcp and libosmo-mgcp-client headers in the same file. */
#ifndef OSMO_MGCP_COMMON_H
#define OSMO_MGCP_COMMON_H
#include <string.h>
#include <errno.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
#define for_each_non_empty_line(line, save) \
for (line = strtok_r(NULL, "\r\n", &save); line; \
line = strtok_r(NULL, "\r\n", &save))
enum mgcp_connection_mode {
MGCP_CONN_NONE = 0,
MGCP_CONN_RECV_ONLY = 1,
MGCP_CONN_SEND_ONLY = 2,
MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY,
MGCP_CONN_LOOPBACK = 4 | MGCP_CONN_RECV_SEND,
};
/* Ensure that the msg->l2h is NUL terminated. */
static inline int mgcp_msg_terminate_nul(struct msgb *msg)
{
unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */
if (tail[-1] == '\0')
/* nothing to do */;
else if (msgb_tailroom(msg) > 0)
tail[0] = '\0';
else if (tail[-1] == '\r' || tail[-1] == '\n')
tail[-1] = '\0';
else {
LOGP(DLMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: "
"Length: %d, Buffer size: %d\n",
msgb_l2len(msg), msg->data_len);
return -ENOTSUP;
}
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,39 @@
/* Message connection list handling */
/*
* (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
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/core/linuxlist.h>
#include <inttypes.h>
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
enum mgcp_conn_type type, char *name);
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
const char *id);
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id);
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp);
void mgcp_conn_free_all(struct mgcp_endpoint *endp);
char *mgcp_conn_dump(struct mgcp_conn *conn);
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn);

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

@@ -0,0 +1,330 @@
/* MGCP Private Data */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <string.h>
#include <inttypes.h>
#include <osmocom/core/select.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/counter.h>
#define CI_UNUSED 0
/* FIXME: This this is only needed to compile the currently
* broken OSMUX support. Remove when fixed */
#define CONN_ID_BTS "0"
#define CONN_ID_NET "1"
enum mgcp_trunk_type {
MGCP_TRUNK_VIRTUAL,
MGCP_TRUNK_E1,
};
struct mgcp_rtp_stream_state {
uint32_t ssrc;
uint16_t last_seq;
uint32_t last_timestamp;
uint32_t err_ts_counter;
int32_t last_tsdelta;
uint32_t last_arrival_time;
};
struct mgcp_rtp_state {
/* has this state structure been initialized? */
int initialized;
struct {
/* are we patching the SSRC value? */
int patch_ssrc;
/* original SSRC (to which we shall patch any different SSRC) */
uint32_t orig_ssrc;
/* offset to apply on the sequence number */
int seq_offset;
/* offset to apply on the timestamp number */
int32_t timestamp_offset;
} patch;
/* duration of a packet (FIXME: in which unit?) */
uint32_t packet_duration;
struct mgcp_rtp_stream_state in_stream;
struct mgcp_rtp_stream_state out_stream;
/* jitter and packet loss calculation */
struct {
int initialized;
uint16_t base_seq;
uint16_t max_seq;
uint32_t ssrc;
uint32_t jitter;
int32_t transit;
int cycles;
} stats;
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
};
struct mgcp_rtp_codec {
uint32_t rate;
int channels;
uint32_t frame_duration_num;
uint32_t frame_duration_den;
int payload_type;
char *audio_name;
char *subtype_name;
};
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
struct mgcp_rtp_end {
/* statistics */
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 */
int rtp_port, rtcp_port;
/* audio codec information */
struct mgcp_rtp_codec codec;
struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */
/* per endpoint data */
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;
};
struct mgcp_lco {
char *string;
char *codec;
int pkt_period_min; /* time in ms */
int pkt_period_max; /* time in ms */
};
/* Specific rtp connection type (see struct mgcp_conn_rtp) */
enum mgcp_conn_rtp_type {
MGCP_RTP_DEFAULT = 0,
MGCP_OSMUX_BSC,
MGCP_OSMUX_BSC_NAT,
};
#include <osmocom/mgcp/osmux.h>
struct mgcp_conn;
/* MGCP connection (RTP) */
struct mgcp_conn_rtp {
/* Backpointer to conn struct */
struct mgcp_conn *conn;
/* Specific connection type */
enum mgcp_conn_rtp_type type;
/* Port status */
struct mgcp_rtp_end end;
/* Sequence bits */
struct mgcp_rtp_state state;
/* taps for the rtp connection; one per direction */
struct mgcp_rtp_tap tap_in;
struct mgcp_rtp_tap tap_out;
/* Osmux states (optional) */
struct {
/* Osmux state: disabled, activating, active */
enum osmux_state state;
/* Allocated Osmux circuit ID for this endpoint */
int allocated_cid;
/* Used Osmux circuit ID for this endpoint */
uint8_t cid;
/* handle to batch messages */
struct osmux_in_handle *in;
/* handle to unbatch messages */
struct osmux_out_handle out;
/* statistics */
struct {
uint32_t chunks;
uint32_t octets;
} stats;
} osmux;
};
/*! Connection type, specifies which member of the union "u" in mgcp_conn
* contains a useful connection description (currently only RTP) */
enum mgcp_conn_type {
MGCP_CONN_TYPE_RTP,
};
/*! MGCP connection (untyped) */
struct mgcp_conn {
/*!< list head */
struct llist_head entry;
/*!< Backpointer to the endpoint where the conn belongs to */
struct mgcp_endpoint *endp;
/*!< type of the connection (union) */
enum mgcp_conn_type type;
/*!< mode of the connection */
enum mgcp_connection_mode mode;
/*!< copy of the mode to restore the original setting (VTY) */
enum mgcp_connection_mode mode_orig;
/*!< connection id to identify the connection */
char id[MGCP_CONN_ID_LENGTH];
/*!< human readable name (vty, logging) */
char name[256];
/*!< union with connection description */
union {
struct mgcp_conn_rtp rtp;
} u;
/*!< pointer to optional private data */
void *priv;
};
#include <osmocom/mgcp/mgcp_conn.h>
struct mgcp_endpoint_type;
/**
* Internal structure while parsing a request
*/
struct mgcp_parse_data {
struct mgcp_config *cfg;
struct mgcp_endpoint *endp;
char *trans;
char *save;
};
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
char *buf, int rc, struct mgcp_conn_rtp *conn_src,
struct mgcp_conn_rtp *conn_dst);
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
unsigned int buf_size, struct mgcp_conn *conn);
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
struct mgcp_conn_rtp *conn);
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
/* For transcoding we need to manage an in and an output that are connected */
static inline int endp_back_channel(int endpoint)
{
return endpoint + 60;
}
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
struct mgcp_rtp_end *rtp);
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *rtp);
/* payload processing default functions */
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size);
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
struct mgcp_rtp_end *src_end);
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
int *payload_type,
const char**audio_name,
const char**fmtp_extra,
struct mgcp_conn_rtp *conn);
/* internal RTP Annex A counting */
void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
const uint16_t seq, const int32_t transit,
const uint32_t ssrc);
int mgcp_set_ip_tos(int fd, int tos);
enum {
MGCP_DEST_NET = 0,
MGCP_DEST_BTS,
};
#define MGCP_DUMMY_LOAD 0x23
/**
* SDP related information
*/
/* Assume audio frame length of 20ms */
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
#define PTYPE_UNDEFINED (-1)
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);

View File

@@ -0,0 +1,56 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* Message parser/generator utilities */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <stdint.h>
struct mgcp_conn;
struct mgcp_parse_data;
struct mgcp_endpoint;
void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble);
int mgcp_parse_conn_mode(const char *msg, struct mgcp_endpoint *endp,
struct mgcp_conn *conn);
int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data);
int mgcp_parse_osmux_cid(const char *line);
int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line);
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid);
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id);
char *mgcp_strline(char *str, char **saveptr);
#define for_each_line(line, save)\
for (line = mgcp_strline(NULL, &save); line;\
line = mgcp_strline(NULL, &save))
#define for_each_non_empty_line(line, save)\
for (line = strtok_r(NULL, "\r\n", &save); line;\
line = strtok_r(NULL, "\r\n", &save))

View File

@@ -0,0 +1,35 @@
/*
* SDP generation and parsing
*
* (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2014 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/mgcp/mgcp_sdp.h>
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn,
struct mgcp_parse_data *p);
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
int payload_type, const char *audio_name);
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
const char *addr);

View File

@@ -0,0 +1,37 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* The statistics generator */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/mgcp/mgcp_internal.h>
#include <inttypes.h>
void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn);
/* Exposed for test purposes only, do not use actively */
void calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *,
uint32_t *expected, int *loss);
/* Exposed for test purposes only, do not use actively */
uint32_t calc_jitter(struct mgcp_rtp_state *);

View File

@@ -0,0 +1,38 @@
#pragma once
#include <osmocom/netif/osmux.h>
struct mgcp_conn_rtp;
#define OSMUX_PORT 1984
enum {
OSMUX_ROLE_BSC = 0,
OSMUX_ROLE_BSC_NAT,
};
int osmux_init(int role, struct mgcp_config *cfg);
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
struct in_addr *addr, uint16_t port);
void osmux_disable_conn(struct mgcp_conn_rtp *conn);
void osmux_allocate_cid(struct mgcp_conn_rtp *conn);
void osmux_release_cid(struct mgcp_conn_rtp *conn);
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn);
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
int osmux_get_cid(void);
void osmux_put_cid(uint8_t osmux_cid);
int osmux_used_cid(void);
enum osmux_state {
OSMUX_STATE_DISABLED = 0,
OSMUX_STATE_NEGOTIATING,
OSMUX_STATE_ACTIVATING,
OSMUX_STATE_ENABLED,
};
enum osmux_usage {
OSMUX_USAGE_OFF = 0,
OSMUX_USAGE_ON = 1,
OSMUX_USAGE_ONLY = 2,
};

View File

@@ -0,0 +1,8 @@
#pragma once
#include <osmocom/vty/command.h>
enum mgcp_vty_node {
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
TRUNK_NODE,
};

View File

@@ -1,3 +1,13 @@
BUILT_SOURCES = \
mgcp_common.h \
$(NULL)
noinst_HEADERS = \
mgcp_client_internal.h \
$(NULL)
mgcp_common.h: $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h
echo -e "/*\n\n DO NOT EDIT THIS FILE!\n THIS IS OVERWRITTEN DURING BUILD\n This is an automatic copy of <osmocom/mgcp/mgcp_common.h>\n\n */" > mgcp_common.h
cat $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h >> mgcp_common.h
CLEANFILES = mgcp_common.h

View File

@@ -1,6 +1,9 @@
#pragma once
#include <stdint.h>
#include <arpa/inet.h>
#include <osmocom/mgcp_client/mgcp_common.h>
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0"
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 0
@@ -24,15 +27,45 @@ struct mgcp_client_conf {
typedef unsigned int mgcp_trans_id_t;
struct mgcp_response_head {
int response_code;
mgcp_trans_id_t trans_id;
const char *comment;
int response_code;
mgcp_trans_id_t trans_id;
char comment[MGCP_COMMENT_MAXLEN];
char conn_id[MGCP_CONN_ID_LENGTH];
char endpoint[MGCP_ENDPOINT_MAXLEN];
};
struct mgcp_response {
char *body;
struct mgcp_response_head head;
uint16_t audio_port;
char audio_ip[INET_ADDRSTRLEN];
};
enum mgcp_verb {
MGCP_VERB_CRCX,
MGCP_VERB_MDCX,
MGCP_VERB_DLCX,
MGCP_VERB_AUEP,
MGCP_VERB_RSIP,
};
#define MGCP_MSG_PRESENCE_ENDPOINT 0x0001
#define MGCP_MSG_PRESENCE_CALL_ID 0x0002
#define MGCP_MSG_PRESENCE_CONN_ID 0x0004
#define MGCP_MSG_PRESENCE_AUDIO_IP 0x0008
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
struct mgcp_msg {
enum mgcp_verb verb;
/* See MGCP_MSG_PRESENCE_* constants */
uint32_t presence;
char endpoint[MGCP_ENDPOINT_MAXLEN];
unsigned int call_id;
char *conn_id;
uint16_t audio_port;
char *audio_ip;
enum mgcp_connection_mode conn_mode;
};
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
@@ -58,16 +91,29 @@ int mgcp_response_parse_params(struct mgcp_response *r);
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
mgcp_response_cb_t response_cb, void *priv);
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
enum mgcp_connection_mode;
struct msgb *mgcp_msg_crcx(struct mgcp_client *mgcp,
uint16_t rtp_endpoint, unsigned int call_id,
enum mgcp_connection_mode mode);
enum mgcp_connection_mode mode)
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp,
uint16_t rtp_endpoint, const char *rtp_conn_addr,
uint16_t rtp_port, enum mgcp_connection_mode mode);
uint16_t rtp_port, enum mgcp_connection_mode mode)
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
unsigned int call_id);
unsigned int call_id)
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);
extern const struct value_string mgcp_client_connection_mode_strs[];
static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
{
return get_value_string(mgcp_client_connection_mode_strs, mode);
}

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,5 +1,7 @@
#pragma once
#include <osmocom/core/write_queue.h>
#define MSGB_CB_MGCP_TRANS_ID 0
struct mgcp_client {

View File

@@ -16,13 +16,15 @@
app_configs = {
"mgcp": ["doc/examples/osmo-bsc_mgcp/mgcp.cfg"],
"osmo-mgw": ["doc/examples/osmo-mgw/osmo-mgw.cfg"],
"osmo-bsc_mgcp": ["doc/examples/osmo-bsc_mgcp/mgcp.cfg"],
}
apps = [(4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "mgcp"),
apps = [(4243, "src/osmo-mgw/osmo-mgw", "OsmoMGW", "osmo-mgw"),
(4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "osmo-bsc_mgcp"),
]
vty_command = ["./src/osmo-bsc_mgcp/osmo-bsc_mgcp", "-c",
"doc/examples/osmo-bsc_mgcp/mgcp.cfg"]
vty_command = ["./src/osmo-mgw/osmo-mgw", "-c",
"doc/examples/osmo-mgw/osmo-mgw.cfg"]
vty_app = apps[0]

View File

@@ -23,9 +23,11 @@ AM_LDFLAGS = \
SUBDIRS = \
libosmo-legacy-mgcp \
libosmo-mgcp-client \
libosmo-mgcp \
$(NULL)
# Programs
SUBDIRS += \
osmo-bsc_mgcp \
osmo-mgw \
$(NULL)

View File

@@ -15,16 +15,16 @@ AM_CFLAGS = \
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(COVERAGE_LDFLAGS) \
$(LIBBCG729_LIBS) \
$(LIBRARY_GSM) \
$(NULL)
# This is _NOT_ the library release version, it's an API version.
# Please read Chapter 6 "Library interface versions" of the libtool
# documentation before making any modification
LEGACY_MGCP_LIBVERSION=0:0:0
# This is not at all related to the release version, but a range of supported
# API versions. Read TODO_RELEASE in the source tree's root!
LEGACY_MGCP_LIBVERSION=0:1:0
lib_LTLIBRARIES = \
libosmo-legacy-mgcp.la \

View File

@@ -372,8 +372,8 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
display_mgcp_message(msg->l2h, msgb_l2len(msg), "Received message");
/* attempt to treat it as a response */
if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
/* attempt to treat it as a response */
if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
return NULL;
}
@@ -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

@@ -1356,7 +1356,6 @@ int mgcp_vty_init(void)
install_element(CONFIG_NODE, &cfg_mgcp_cmd);
install_node(&mgcp_node, config_write_mgcp);
vty_install_default(MGCP_NODE);
install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd);
install_element(MGCP_NODE, &cfg_mgcp_bts_ip_cmd);
install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd);
@@ -1416,7 +1415,6 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
install_node(&trunk_node, config_write_trunk);
vty_install_default(TRUNK_NODE);
install_element(TRUNK_NODE, &cfg_trunk_rtp_keepalive_cmd);
install_element(TRUNK_NODE, &cfg_trunk_rtp_keepalive_once_cmd);
install_element(TRUNK_NODE, &cfg_trunk_no_rtp_keepalive_cmd);

View File

@@ -1,6 +1,7 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir)/include \
-I$(top_builddir) \
$(NULL)
@@ -8,21 +9,18 @@ AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
# This is _NOT_ the library release version, it's an API version.
# Please read Chapter 6 "Library interface versions" of the libtool
# documentation before making any modification
MGCP_CLIENT_LIBVERSION=1:0:1
# 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=3:0:0
lib_LTLIBRARIES = \
libosmo-mgcp-client.la \
@@ -31,7 +29,7 @@ lib_LTLIBRARIES = \
libosmo_mgcp_client_la_SOURCES = \
mgcp_client.c \
mgcp_client_vty.c \
../libosmo-legacy-mgcp/mgcp_common.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/>.
*
*/
@@ -23,9 +23,9 @@
#include <osmocom/core/write_queue.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/byteswap.h>
#include <osmocom/core/socket.h>
#include <osmocom/legacy_mgcp/mgcp.h>
#include <osmocom/legacy_mgcp/mgcp_internal.h>
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_internal.h>
@@ -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)
{
@@ -134,20 +141,17 @@ static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg)
r->body = (char *)msg->data;
if (sscanf(r->body, "%3d %u %n",
if (sscanf(r->body, "%3d %u %n",
&r->head.response_code, &r->head.trans_id,
&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:
@@ -175,66 +179,223 @@ static bool mgcp_line_is_valid(const char *line)
}
/* Parse a line like "m=audio 16002 RTP/AVP 98" */
static int mgcp_parse_audio(struct mgcp_response *r, const char *line)
static int mgcp_parse_audio_port(struct mgcp_response *r, const char *line)
{
if (sscanf(line, "m=audio %hu",
if (sscanf(line, "m=audio %hu",
&r->audio_port) != 1)
goto response_parse_failure;
if (r->audio_port == 0)
goto response_parse_failure;
return 0;
response_parse_failure:
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse MGCP response header\n");
"Failed to parse MGCP response header (audio port)\n");
return -EINVAL;
}
/* Parse a line like "c=IN IP4 10.11.12.13" */
static int mgcp_parse_audio_ip(struct mgcp_response *r, const char *line)
{
struct in_addr ip_test;
if (strlen(line) < 16)
goto response_parse_failure;
/* The current implementation strictly supports IPV4 only ! */
if (memcmp("c=IN IP4 ", line, 9) != 0)
goto response_parse_failure;
/* Extract IP-Address */
osmo_strlcpy(r->audio_ip, line + 9, sizeof(r->audio_ip));
/* Check IP-Address */
if (inet_aton(r->audio_ip, &ip_test) == 0)
goto response_parse_failure;
return 0;
response_parse_failure:
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse MGCP response header (audio ip)\n");
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_line(line, data) {
for_each_non_empty_line(line, data_ptr) {
if (!mgcp_line_is_valid(line))
return -EINVAL;
switch (line[0]) {
case 'm':
rc = mgcp_parse_audio(r, line);
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)
goto exit;
break;
default:
/* skip unhandled parameters */
break;
}
}
rc = 0;
exit:
talloc_free(data);
return rc;
}
/* 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;
/* Check if the label matches */
snprintf(label_string, sizeof(label_string), "%c: ", label);
if (memcmp(label_string, line, 3) != 0)
goto response_parse_failure;
/* 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 (parameter label: %c)\n", label);
return -EINVAL;
}
/* Parse MGCP parameters of the response */
static int parse_head_params(struct mgcp_response *r)
{
char *line;
int rc = 0;
OSMO_ASSERT(r->body);
char *data;
char *data_ptr;
char *data_end;
/* 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_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_head_param(r->head.conn_id,
sizeof(r->head.conn_id),
'I', line);
if (rc)
goto exit;
break;
default:
/* skip unhandled parameters */
break;
}
}
exit:
talloc_free(data);
return rc;
}
static struct mgcp_response_pending *mgcp_client_response_pending_get(
struct mgcp_client *mgcp,
struct mgcp_response *r)
mgcp_trans_id_t trans_id)
{
struct mgcp_response_pending *pending;
if (!r)
return NULL;
llist_for_each_entry(pending, &mgcp->responses_pending, entry) {
if (pending->trans_id == r->head.trans_id) {
if (pending->trans_id == trans_id) {
llist_del(&pending->entry);
return pending;
}
@@ -250,26 +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\n");
return -1;
LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head)\n");
rc = 1;
goto error;
}
pending = mgcp_client_response_pending_get(mgcp, &r);
rc = parse_head_params(r);
if (rc) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head parameters)\n");
rc = 1;
goto error;
}
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 -1;
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)
@@ -293,7 +470,7 @@ static int mgcp_do_read(struct osmo_fd *fd)
LOGP(DLMGCP, LOGL_ERROR, "Too much data: %d\n", ret);
msgb_free(msg);
return -1;
}
}
msg->l2h = msgb_put(msg, ret);
ret = mgcp_client_rx(mgcp, msg);
@@ -308,7 +485,7 @@ static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
unsigned int l = msg->len < sizeof(strbuf) ? msg->len : sizeof(strbuf);
unsigned int i;
strncpy(strbuf, (const char*)msg->data, l);
osmo_strlcpy(strbuf, (const char*)msg->data, l);
for (i = 0; i < sizeof(strbuf); i++) {
if (strbuf[i] == '\n' || strbuf[i] == '\r') {
strbuf[i] = '\0';
@@ -356,9 +533,11 @@ 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)
{
int on;
struct sockaddr_in addr;
struct osmo_wqueue *wq;
int rc;
@@ -370,46 +549,19 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
wq = &mgcp->wq;
wq->bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
if (wq->bfd.fd < 0) {
LOGP(DLMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno);
return -errno;
}
on = 1;
if (setsockopt(wq->bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
rc = osmo_sock_init2_ofd(&wq->bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
mgcp->actual.local_addr, mgcp->actual.local_port,
mgcp->actual.remote_addr, mgcp->actual.remote_port,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
if (rc < 0) {
LOGP(DLMGCP, LOGL_FATAL,
"Failed to initialize socket for MGCP GW: %s\n",
strerror(errno));
rc = -errno;
"Failed to initialize socket %s:%u -> %s:%u for MGCP GW: %s\n",
mgcp->actual.local_addr, mgcp->actual.local_port,
mgcp->actual.remote_addr, mgcp->actual.remote_port, strerror(errno));
goto error_close_fd;
}
/* bind socket */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
inet_aton(mgcp->actual.local_addr, &addr.sin_addr);
addr.sin_port = htons(mgcp->actual.local_port);
if (bind(wq->bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
LOGP(DLMGCP, LOGL_FATAL,
"Failed to bind for MGCP GW to %s %u\n",
mgcp->actual.local_addr, mgcp->actual.local_port);
rc = -errno;
goto error_close_fd;
}
/* connect to the remote */
inet_aton(mgcp->actual.remote_addr, &addr.sin_addr);
addr.sin_port = htons(mgcp->actual.remote_port);
if (connect(wq->bfd.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
LOGP(DLMGCP, LOGL_FATAL,
"Failed to connect to MGCP GW at %s %u: %s\n",
mgcp->actual.remote_addr, mgcp->actual.remote_port,
strerror(errno));
rc = -errno;
goto error_close_fd;
}
mgcp->remote_addr = htonl(addr.sin_addr.s_addr);
osmo_wqueue_init(wq, 10);
@@ -418,11 +570,6 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
wq->read_cb = mgcp_do_read;
wq->write_cb = mgcp_do_write;
if (osmo_fd_register(&wq->bfd) != 0) {
LOGP(DLMGCP, LOGL_FATAL, "Failed to register BFD\n");
rc = -EIO;
goto error_close_fd;
}
LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s:%u -> %s:%u\n",
mgcp->actual.local_addr, mgcp->actual.local_port,
mgcp->actual.remote_addr, mgcp->actual.remote_port);
@@ -434,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;
@@ -471,7 +626,10 @@ struct mgcp_response_pending * mgcp_client_pending_add(
* response_cb. NOTE: the response_cb still needs to call
* mgcp_response_parse_params(response) to get the parsed parameters -- to
* potentially save some CPU cycles, only the head line has been parsed when
* the response_cb is invoked. */
* the response_cb is invoked.
* Before the priv pointer becomes invalid, e.g. due to transaction timeout,
* mgcp_client_cancel() needs to be called for this transaction.
*/
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
mgcp_response_cb_t response_cb, void *priv)
{
@@ -514,6 +672,35 @@ mgcp_tx_error:
return -1;
}
/*! 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(). */
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) {
/*! 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.
*/
}
static struct msgb *mgcp_msg_from_buf(mgcp_trans_id_t trans_id,
const char *buf, int len)
{
@@ -584,7 +771,7 @@ struct msgb *mgcp_msg_crcx(struct mgcp_client *mgcp,
trans_id,
rtp_endpoint,
call_id,
mgcp_cmode_name(mode));
mgcp_client_cmode_name(mode));
}
struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp,
@@ -602,7 +789,7 @@ struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp,
,
trans_id,
rtp_endpoint,
mgcp_cmode_name(mode),
mgcp_client_cmode_name(mode),
rtp_conn_addr,
rtp_port);
}
@@ -616,7 +803,194 @@ struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
"C: %x\r\n", trans_id, rtp_endpoint, call_id);
}
#define MGCP_CRCX_MANDATORY (MGCP_MSG_PRESENCE_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;
/* Add command verb */
switch (mgcp_msg->verb) {
case MGCP_VERB_CRCX:
mandatory_mask = MGCP_CRCX_MANDATORY;
rc += msgb_printf(msg, "CRCX %u", trans_id);
break;
case MGCP_VERB_MDCX:
mandatory_mask = MGCP_MDCX_MANDATORY;
rc += msgb_printf(msg, "MDCX %u", trans_id);
break;
case MGCP_VERB_DLCX:
mandatory_mask = MGCP_DLCX_MANDATORY;
rc += msgb_printf(msg, "DLCX %u", trans_id);
break;
case MGCP_VERB_AUEP:
mandatory_mask = MGCP_AUEP_MANDATORY;
rc += msgb_printf(msg, "AUEP %u", trans_id);
break;
case MGCP_VERB_RSIP:
mandatory_mask = MGCP_RSIP_MANDATORY;
rc += msgb_printf(msg, "RSIP %u", trans_id);
break;
default:
LOGP(DLMGCP, LOGL_ERROR,
"Invalid command verb, can not generate MGCP message\n");
msgb_free(msg);
return NULL;
}
/* Check if mandatory fields are missing */
if (!((mgcp_msg->presence & mandatory_mask) == mandatory_mask)) {
LOGP(DLMGCP, LOGL_ERROR,
"One or more missing mandatory fields, can not generate MGCP message\n");
msgb_free(msg);
return NULL;
}
/* Add endpoint name */
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_ENDPOINT) {
if (strlen(mgcp_msg->endpoint) <= 0) {
LOGP(DLMGCP, LOGL_ERROR,
"Empty endpoint name, can not generate MGCP message\n");
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);
}
/* Add protocol version */
rc += msgb_printf(msg, " MGCP 1.0\r\n");
/* Add call id */
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CALL_ID)
rc += msgb_printf(msg, "C: %x\r\n", mgcp_msg->call_id);
/* Add connection id */
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_ID) {
if (strlen(mgcp_msg->conn_id) <= 0) {
LOGP(DLMGCP, LOGL_ERROR,
"Empty connection id, can not generate MGCP message\n");
msgb_free(msg);
return NULL;
}
rc += msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id);
}
/* Add local connection options */
if (mgcp_msg->verb == MGCP_VERB_CRCX)
rc += msgb_printf(msg, "L: p:20, a:AMR, nt:IN\r\n");
/* Add mode */
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_MODE)
rc +=
msgb_printf(msg, "M: %s\r\n",
mgcp_client_cmode_name(mgcp_msg->conn_mode));
/* 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");
msgb_free(msg);
return NULL;
}
if (strlen(mgcp_msg->audio_ip) <= 0) {
LOGP(DLMGCP, LOGL_ERROR,
"Empty ip address, can not generate MGCP message\n");
msgb_free(msg);
return NULL;
}
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);
}
if (rc != 0) {
LOGP(DLMGCP, LOGL_ERROR,
"message buffer to small, can not generate MGCP message\n");
msgb_free(msg);
msg = NULL;
}
return msg;
}
/*! 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;
}
const struct value_string mgcp_client_connection_mode_strs[] = {
{ MGCP_CONN_NONE, "none" },
{ MGCP_CONN_RECV_SEND, "sendrecv" },
{ MGCP_CONN_SEND_ONLY, "sendonly" },
{ MGCP_CONN_RECV_ONLY, "recvonly" },
{ MGCP_CONN_LOOPBACK, "loopback" },
{ 0, NULL }
};

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

@@ -1,4 +1,4 @@
/* MGCPGW client interface to quagga VTY */
/* MGCP client interface to quagga VTY */
/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
* Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
* (C) 2009 by Harald Welte <laforge@gnumonks.org>
@@ -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,19 +25,19 @@
#include <talloc.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/misc.h>
#include <osmocom/core/utils.h>
#include <osmocom/legacy_mgcp/vty.h>
#include <osmocom/mgcp_client/mgcp_client.h>
#define MGCPGW_STR "MGCP gateway configuration for RTP streams\n"
#define MGW_STR "Configure MGCP connection to Media Gateway\n"
void *global_mgcp_client_ctx = NULL;
struct mgcp_client_conf *global_mgcp_client_conf = NULL;
DEFUN(cfg_mgcpgw_local_ip, cfg_mgcpgw_local_ip_cmd,
"mgcpgw local-ip A.B.C.D",
MGCPGW_STR "local bind to connect to MGCP gateway with\n"
DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
"mgw local-ip A.B.C.D",
MGW_STR "local bind to connect to MGW from\n"
"local bind IP address\n")
{
if (!global_mgcp_client_conf)
@@ -47,10 +47,14 @@ DEFUN(cfg_mgcpgw_local_ip, cfg_mgcpgw_local_ip_cmd,
talloc_strdup(global_mgcp_client_ctx, argv[0]);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd,
"mgcpgw local-ip A.B.C.D",
MGW_STR "local bind to connect to MGCP gateway with\n"
"local bind IP address\n")
DEFUN(cfg_mgcpgw_local_port, cfg_mgcpgw_local_port_cmd,
"mgcpgw local-port <0-65535>",
MGCPGW_STR "local bind to connect to MGCP gateway with\n"
DEFUN(cfg_mgw_local_port, cfg_mgw_local_port_cmd,
"mgw local-port <0-65535>",
MGW_STR "local port to connect to MGW from\n"
"local bind port\n")
{
if (!global_mgcp_client_conf)
@@ -58,11 +62,15 @@ DEFUN(cfg_mgcpgw_local_port, cfg_mgcpgw_local_port_cmd,
global_mgcp_client_conf->local_port = atoi(argv[0]);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_local_port, cfg_mgcpgw_local_port_cmd,
"mgcpgw local-port <0-65535>",
MGW_STR "local bind to connect to MGCP gateway with\n"
"local bind port\n")
DEFUN(cfg_mgcpgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
"mgcpgw remote-ip A.B.C.D",
MGCPGW_STR "remote bind to connect to MGCP gateway with\n"
"remote bind IP address\n")
DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
"mgw remote-ip A.B.C.D",
MGW_STR "remote IP address to reach the MGW at\n"
"remote IP address\n")
{
if (!global_mgcp_client_conf)
return CMD_ERR_NOTHING_TODO;
@@ -71,23 +79,31 @@ DEFUN(cfg_mgcpgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
talloc_strdup(global_mgcp_client_ctx, argv[0]);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
"mgcpgw remote-ip A.B.C.D",
MGW_STR "remote bind to connect to MGCP gateway with\n"
"remote bind IP address\n")
DEFUN(cfg_mgcpgw_remote_port, cfg_mgcpgw_remote_port_cmd,
"mgcpgw remote-port <0-65535>",
MGCPGW_STR "remote bind to connect to MGCP gateway with\n"
"remote bind port\n")
DEFUN(cfg_mgw_remote_port, cfg_mgw_remote_port_cmd,
"mgw remote-port <0-65535>",
MGW_STR "remote port to reach the MGW at\n"
"remote port\n")
{
if (!global_mgcp_client_conf)
return CMD_ERR_NOTHING_TODO;
global_mgcp_client_conf->remote_port = atoi(argv[0]);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
"mgcpgw remote-port <0-65535>",
MGW_STR "remote bind to connect to MGCP gateway with\n"
"remote bind port\n")
DEFUN(cfg_mgcpgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
"mgcpgw endpoint-range <1-65534> <1-65534>",
MGCPGW_STR "usable range of endpoint identifiers\n"
"set first useable endpoint identifier\n"
"set the last useable endpoint identifier\n")
DEFUN(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
"mgw endpoint-range <1-65534> <1-65534>",
MGW_STR "usable range of endpoint identifiers\n"
"set first usable endpoint identifier\n"
"set last usable endpoint identifier\n")
{
uint16_t first_endpoint = atoi(argv[0]);
uint16_t last_endpoint = atoi(argv[1]);
@@ -102,19 +118,30 @@ DEFUN(cfg_mgcpgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
global_mgcp_client_conf->last_endpoint = last_endpoint;
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
"mgcpgw endpoint-range <1-65534> <1-65534>",
MGW_STR "usable range of endpoint identifiers\n"
"set first useable endpoint identifier\n"
"set the last useable endpoint identifier\n")
#define BTS_START_STR "First UDP port allocated for the BTS side\n"
#define UDP_PORT_STR "UDP Port number\n"
DEFUN(cfg_mgcp_rtp_bts_base_port,
cfg_mgcp_rtp_bts_base_port_cmd,
"mgcpgw bts-base <0-65534>",
MGCPGW_STR
DEFUN(cfg_mgw_rtp_bts_base_port,
cfg_mgw_rtp_bts_base_port_cmd,
"mgw bts-base <0-65534>",
MGW_STR
BTS_START_STR
UDP_PORT_STR)
{
global_mgcp_client_conf->bts_base = atoi(argv[0]);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
cfg_mgcpgw_rtp_bts_base_port_cmd,
"mgcpgw bts-base <0-65534>",
MGW_STR
BTS_START_STR
UDP_PORT_STR)
int mgcp_client_config_write(struct vty *vty, const char *indent)
{
@@ -126,32 +153,32 @@ int mgcp_client_config_write(struct vty *vty, const char *indent)
addr = global_mgcp_client_conf->local_addr;
if (addr)
vty_out(vty, "%smgcpgw local-ip %s%s", indent, addr,
vty_out(vty, "%smgw local-ip %s%s", indent, addr,
VTY_NEWLINE);
port = global_mgcp_client_conf->local_port;
if (port >= 0)
vty_out(vty, "%smgcpgw local-port %u%s", indent,
vty_out(vty, "%smgw local-port %u%s", indent,
(uint16_t)port, VTY_NEWLINE);
addr = global_mgcp_client_conf->remote_addr;
if (addr)
vty_out(vty, "%smgcpgw remote-ip %s%s", indent, addr,
vty_out(vty, "%smgw remote-ip %s%s", indent, addr,
VTY_NEWLINE);
port = global_mgcp_client_conf->remote_port;
if (port >= 0)
vty_out(vty, "%smgcpgw remote-port %u%s", indent,
vty_out(vty, "%smgw remote-port %u%s", indent,
(uint16_t)port, VTY_NEWLINE);
first_endpoint = global_mgcp_client_conf->first_endpoint;
last_endpoint = global_mgcp_client_conf->last_endpoint;
if (last_endpoint != 0) {
vty_out(vty, "%smgcpgw endpoint-range %u %u%s", indent,
vty_out(vty, "%smgw endpoint-range %u %u%s", indent,
first_endpoint, last_endpoint, VTY_NEWLINE);
}
bts_base = global_mgcp_client_conf->bts_base;
if (bts_base) {
vty_out(vty, "%smgcpgw bts-base %u%s", indent,
vty_out(vty, "%smgw bts-base %u%s", indent,
bts_base, VTY_NEWLINE);
}
@@ -163,10 +190,20 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
global_mgcp_client_ctx = talloc_ctx;
global_mgcp_client_conf = conf;
install_element(node, &cfg_mgw_local_ip_cmd);
install_element(node, &cfg_mgw_local_port_cmd);
install_element(node, &cfg_mgw_remote_ip_cmd);
install_element(node, &cfg_mgw_remote_port_cmd);
install_element(node, &cfg_mgw_endpoint_range_cmd);
install_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
/* deprecated 'mgcpgw' commands */
install_element(node, &cfg_mgcpgw_local_ip_cmd);
install_element(node, &cfg_mgcpgw_local_port_cmd);
install_element(node, &cfg_mgcpgw_remote_ip_cmd);
install_element(node, &cfg_mgcpgw_remote_port_cmd);
install_element(node, &cfg_mgcpgw_endpoint_range_cmd);
install_element(node, &cfg_mgcp_rtp_bts_base_port_cmd);
install_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd);
osmo_fsm_vty_add_cmds();
}

View File

@@ -0,0 +1,42 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir) \
$(NULL)
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
noinst_LIBRARIES = \
libosmo-mgcp.a \
$(NULL)
noinst_HEADERS = \
g711common.h \
$(NULL)
libosmo_mgcp_a_SOURCES = \
mgcp_protocol.c \
mgcp_network.c \
mgcp_vty.c \
mgcp_osmux.c \
mgcp_sdp.c \
mgcp_msg.c \
mgcp_conn.c \
mgcp_stat.c \
mgcp_endp.c \
$(NULL)

View File

@@ -0,0 +1,187 @@
/*
* PCM - A-Law conversion
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
*
* Wrapper for linphone Codec class by Simon Morlat <simon.morlat@linphone.org>
*
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
static inline int val_seg(int val)
{
int r = 0;
val >>= 7; /*7 = 4 + 3*/
if (val & 0xf0) {
val >>= 4;
r += 4;
}
if (val & 0x0c) {
val >>= 2;
r += 2;
}
if (val & 0x02)
r += 1;
return r;
}
/*
* s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
*
* s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data.
*
* Linear Input Code Compressed Code
* ------------------------ ---------------
* 0000000wxyza 000wxyz
* 0000001wxyza 001wxyz
* 000001wxyzab 010wxyz
* 00001wxyzabc 011wxyz
* 0001wxyzabcd 100wxyz
* 001wxyzabcde 101wxyz
* 01wxyzabcdef 110wxyz
* 1wxyzabcdefg 111wxyz
*
* For further information see John C. Bellamy's Digital Telephony, 1982,
* John Wiley & Sons, pps 98-111 and 472-476.
* G711 is designed for 13 bits input signal, this function add extra shifting to take this into account.
*/
static inline unsigned char s16_to_alaw(int pcm_val)
{
int mask;
int seg;
unsigned char aval;
if (pcm_val >= 0) {
mask = 0xD5;
} else {
mask = 0x55;
pcm_val = -pcm_val;
if (pcm_val > 0x7fff)
pcm_val = 0x7fff;
}
if (pcm_val < 256) /*256 = 32 << 3*/
aval = pcm_val >> 4; /*4 = 1 + 3*/
else {
/* Convert the scaled magnitude to segment number. */
seg = val_seg(pcm_val);
aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
}
return aval ^ mask;
}
/*
* alaw_to_s16() - Convert an A-law value to 16-bit linear PCM
*
*/
static inline int alaw_to_s16(unsigned char a_val)
{
int t;
int seg;
a_val ^= 0x55;
t = a_val & 0x7f;
if (t < 16)
t = (t << 4) + 8;
else {
seg = (t >> 4) & 0x07;
t = ((t & 0x0f) << 4) + 0x108;
t <<= seg -1;
}
return ((a_val & 0x80) ? t : -t);
}
/*
* s16_to_ulaw() - Convert a linear PCM value to u-law
*
* In order to simplify the encoding process, the original linear magnitude
* is biased by adding 33 which shifts the encoding range from (0 - 8158) to
* (33 - 8191). The result can be seen in the following encoding table:
*
* Biased Linear Input Code Compressed Code
* ------------------------ ---------------
* 00000001wxyza 000wxyz
* 0000001wxyzab 001wxyz
* 000001wxyzabc 010wxyz
* 00001wxyzabcd 011wxyz
* 0001wxyzabcde 100wxyz
* 001wxyzabcdef 101wxyz
* 01wxyzabcdefg 110wxyz
* 1wxyzabcdefgh 111wxyz
*
* Each biased linear code has a leading 1 which identifies the segment
* number. The value of the segment number is equal to 7 minus the number
* of leading 0's. The quantization interval is directly available as the
* four bits wxyz. * The trailing bits (a - h) are ignored.
*
* Ordinarily the complement of the resulting code word is used for
* transmission, and so the code word is complemented before it is returned.
*
* For further information see John C. Bellamy's Digital Telephony, 1982,
* John Wiley & Sons, pps 98-111 and 472-476.
*/
static inline unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */
{
int mask;
int seg;
unsigned char uval;
if (pcm_val < 0) {
pcm_val = 0x84 - pcm_val;
mask = 0x7f;
} else {
pcm_val += 0x84;
mask = 0xff;
}
if (pcm_val > 0x7fff)
pcm_val = 0x7fff;
/* Convert the scaled magnitude to segment number. */
seg = val_seg(pcm_val);
/*
* Combine the sign, segment, quantization bits;
* and complement the code word.
*/
uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
return uval ^ mask;
}
/*
* ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM
*
* First, a biased linear code is derived from the code word. An unbiased
* output can then be obtained by subtracting 33 from the biased code.
*
* Note that this function expects to be passed the complement of the
* original code word. This is in keeping with ISDN conventions.
*/
static inline int ulaw_to_s16(unsigned char u_val)
{
int t;
/* Complement to obtain normal u-law value. */
u_val = ~u_val;
/*
* Extract and bias the quantization bits. Then
* shift up by the segment number and subtract out the bias.
*/
t = ((u_val & 0x0f) << 3) + 0x84;
t <<= (u_val & 0x70) >> 4;
return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84));
}

View File

@@ -0,0 +1,320 @@
/* Message connection list handling */
/*
* (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/>.
*
*/
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/gsm/gsm_utils.h>
#include <ctype.h>
/* Allocate a new connection identifier. According to RFC3435, they must
* be unique only within the scope of the endpoint. (Caller must provide
* memory for id) */
static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
{
int i;
int k;
int rc;
uint8_t id_bin[16];
char *id_hex;
/* Generate a connection id that is unique for the current endpoint.
* Technically a counter would be sufficient, but in order to
* be able to find a specific connection in large logfiles and to
* prevent unintentional connections we assign the connection
* identifiers randomly from a reasonable large number space */
for (i = 0; i < 32; i++) {
rc = osmo_get_rand_id(id_bin, sizeof(id_bin));
if (rc < 0)
return rc;
id_hex = osmo_hexdump_nospc(id_bin, sizeof(id_bin));
for (k = 0; k < strlen(id_hex); k++)
id_hex[k] = toupper(id_hex[k]);
/* ensure that the generated conn_id is unique
* for this endpoint */
if (!mgcp_conn_get_rtp(endp, id_hex)) {
osmo_strlcpy(id, id_hex, MGCP_CONN_ID_LENGTH);
return 0;
}
}
LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x, unable to generate a unique connectionIdentifier\n",
ENDPOINT_NUMBER(endp));
return -1;
}
/* Reset codec state and free memory */
static void mgcp_rtp_codec_init(struct mgcp_rtp_codec *codec)
{
codec->payload_type = -1;
codec->subtype_name = NULL;
codec->audio_name = NULL;
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
/* see also mgcp_sdp.c, mgcp_set_audio_info() */
talloc_free(codec->subtype_name);
talloc_free(codec->audio_name);
}
/* 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_rtp->end;
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;
memset(&end->stats, 0, sizeof(end->stats));
end->rtp_port = end->rtcp_port = 0;
talloc_free(end->fmtp_extra);
end->fmtp_extra = NULL;
/* Set default values */
end->frames_per_packet = 0; /* unknown */
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
end->output_enabled = 0;
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.
* \param[in] ctx talloc context
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection
* \param[in] type connection type (e.g. MGCP_CONN_TYPE_RTP)
* \returns pointer to allocated connection, NULL on error */
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
enum mgcp_conn_type type, char *name)
{
struct mgcp_conn *conn;
int rc;
/* Do not allow more then two connections */
if (llist_count(&endp->conns) >= endp->type->max_conns)
return NULL;
/* Create new connection and add it to the list */
conn = talloc_zero(ctx, struct mgcp_conn);
if (!conn)
return NULL;
conn->endp = endp;
conn->type = type;
conn->mode = MGCP_CONN_NONE;
conn->mode_orig = MGCP_CONN_NONE;
osmo_strlcpy(conn->name, name, sizeof(conn->name));
rc = mgcp_alloc_id(endp, conn->id);
if (rc < 0) {
talloc_free(conn);
return NULL;
}
switch (type) {
case MGCP_CONN_TYPE_RTP:
mgcp_rtp_conn_init(&conn->u.rtp, conn);
break;
default:
/* NOTE: This should never be called with an
* invalid type, its up to the programmer
* to ensure propery types */
OSMO_ASSERT(false);
}
llist_add(&conn->entry, &endp->conns);
return conn;
}
/*! find a connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection
* \returns pointer to allocated connection, NULL if not found */
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id)
{
struct mgcp_conn *conn;
llist_for_each_entry(conn, &endp->conns, entry) {
if (strncmp(conn->id, id, sizeof(conn->id)) == 0)
return conn;
}
return NULL;
}
/*! find an RTP connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection
* \returns pointer to allocated connection, NULL if not found */
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
const char *id)
{
struct mgcp_conn *conn;
conn = mgcp_conn_get(endp, id);
if (!conn)
return NULL;
if (conn->type == MGCP_CONN_TYPE_RTP)
return &conn->u.rtp;
return NULL;
}
/*! free a connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection */
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
{
struct mgcp_conn *conn;
conn = mgcp_conn_get(endp, 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:
mgcp_rtp_conn_cleanup(&conn->u.rtp);
break;
default:
/* NOTE: This should never be called with an
* invalid type, its up to the programmer
* to ensure propery types */
OSMO_ASSERT(false);
}
llist_del(&conn->entry);
talloc_free(conn);
}
/*! free oldest connection in the list.
* \param[in] endp associated endpoint */
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
{
struct mgcp_conn *conn;
if (llist_empty(&endp->conns))
return;
conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry);
if (!conn)
return;
mgcp_conn_free(endp, conn->id);
}
/*! free all connections at once.
* \param[in] endp associated endpoint */
void mgcp_conn_free_all(struct mgcp_endpoint *endp)
{
struct mgcp_conn *conn;
struct mgcp_conn *conn_tmp;
/* Drop all items in the list */
llist_for_each_entry_safe(conn, conn_tmp, &endp->conns, entry) {
mgcp_conn_free(endp, conn->id);
}
return;
}
/*! dump basic connection information to human readable string.
* \param[in] conn to dump
* \returns human readable string */
char *mgcp_conn_dump(struct mgcp_conn *conn)
{
static char str[sizeof(conn->name)+sizeof(conn->id)+256];
if (!conn) {
snprintf(str, sizeof(str), "(null connection)");
return str;
}
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
/* Dump RTP connection */
snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, "
"rtp:%u rtcp:%u)",
conn->name,
conn->id,
inet_ntoa(conn->u.rtp.end.addr),
ntohs(conn->u.rtp.end.rtp_port),
ntohs(conn->u.rtp.end.rtcp_port));
break;
default:
/* Should not happen, we should be able to dump
* every possible connection type. */
snprintf(str, sizeof(str), "(unknown connection type)");
break;
}
return str;
}
/*! find destination connection on a specific endpoint.
* \param[in] conn to search a destination for
* \returns destination connection, NULL on failure */
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn)
{
struct mgcp_endpoint *endp;
struct mgcp_conn *partner_conn;
endp = conn->endp;
/*! NOTE: This simply works by grabbing the first connection that is
* not the supplied connection, which is suitable for endpoints that
* do not serve more than two connections. */
llist_for_each_entry(partner_conn, &endp->conns, entry) {
if (conn != partner_conn) {
return partner_conn;
}
}
return NULL;
}

View File

@@ -0,0 +1,56 @@
/* 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/>.
*
*/
#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.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;
}

496
src/libosmo-mgcp/mgcp_msg.c Normal file
View File

@@ -0,0 +1,496 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* Message parser/generator utilities */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <limits.h>
#include <osmocom/mgcp/mgcp_internal.h>
#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
* \param[in] len message mgcp message string length
* \param[in] preamble string to display in logtext in front of each line */
void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble)
{
unsigned char line[80];
unsigned char *ptr;
unsigned int consumed = 0;
unsigned int consumed_line = 0;
unsigned int line_count = 0;
if (!log_check_level(DLMGCP, LOGL_DEBUG))
return;
while (1) {
memset(line, 0, sizeof(line));
ptr = line;
consumed_line = 0;
do {
if (*message != '\n' && *message != '\r') {
*ptr = *message;
ptr++;
}
message++;
consumed++;
consumed_line++;
} while (*message != '\n' && consumed < len
&& consumed_line < sizeof(line));
if (strlen((const char *)line)) {
LOGP(DLMGCP, LOGL_DEBUG, "%s: line #%02u: %s\n",
preamble, line_count, line);
line_count++;
}
if (consumed >= len)
return;
}
}
/*! Parse connection mode.
* \param[in] mode as string (recvonly, sendrecv, sendonly or loopback)
* \param[in] endp pointer to endpoint (only used for log output)
* \param[out] associated connection to be modified accordingly
* \returns 0 on success, -1 on error */
int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
struct mgcp_conn *conn)
{
int ret = 0;
if (!mode) {
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:0x%x missing connection mode\n",
ENDPOINT_NUMBER(endp));
return -1;
}
if (!conn)
return -1;
if (!endp)
return -1;
if (strcmp(mode, "recvonly") == 0)
conn->mode = MGCP_CONN_RECV_ONLY;
else if (strcmp(mode, "sendrecv") == 0)
conn->mode = MGCP_CONN_RECV_SEND;
else if (strcmp(mode, "sendonly") == 0)
conn->mode = MGCP_CONN_SEND_ONLY;
else if (strcmp(mode, "loopback") == 0)
conn->mode = MGCP_CONN_LOOPBACK;
else {
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:0x%x unknown connection mode: '%s'\n",
ENDPOINT_NUMBER(endp), mode);
ret = -1;
}
/* Special handling für RTP connections */
if (conn->type == MGCP_CONN_TYPE_RTP) {
conn->u.rtp.end.output_enabled =
conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
}
LOGP(DLMGCP, LOGL_DEBUG,
"endpoint:0x%x conn:%s\n",
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn));
LOGP(DLMGCP, LOGL_DEBUG,
"endpoint:0x%x connection mode '%s' %d\n",
ENDPOINT_NUMBER(endp), mode, conn->mode);
/* Special handling für RTP connections */
if (conn->type == MGCP_CONN_TYPE_RTP) {
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x output_enabled %d\n",
ENDPOINT_NUMBER(endp), conn->u.rtp.end.output_enabled);
}
/* The VTY might change the connection mode at any time, so we have
* to hold a copy of the original connection mode */
conn->mode_orig = conn->mode;
return ret;
}
/* We have a null terminated string with the endpoint name here. We only
* support two kinds. Simple ones as seen on the BSC level and the ones
* seen on the trunk side. (helper function for find_endpoint()) */
static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
const char *mgcp)
{
char *rest = NULL;
struct mgcp_trunk_config *tcfg;
int trunk, endp;
struct mgcp_endpoint *endp_ptr;
trunk = strtoul(mgcp + 6, &rest, 10);
if (rest == NULL || rest[0] != '/' || trunk < 1) {
LOGP(DLMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
return NULL;
}
endp = strtoul(rest + 1, &rest, 10);
if (rest == NULL || rest[0] != '@') {
LOGP(DLMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
return NULL;
}
/* signalling is on timeslot 1 */
if (endp == 1)
return NULL;
tcfg = mgcp_trunk_num(cfg, trunk);
if (!tcfg) {
LOGP(DLMGCP, LOGL_ERROR, "The trunk %d is not declared.\n",
trunk);
return NULL;
}
if (!tcfg->endpoints) {
LOGP(DLMGCP, LOGL_ERROR,
"Endpoints of trunk %d not allocated.\n", trunk);
return NULL;
}
if (endp < 1 || endp >= tcfg->number_endpoints) {
LOGP(DLMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n",
mgcp);
return NULL;
}
endp_ptr = &tcfg->endpoints[endp];
endp_ptr->wildcarded_req = false;
return endp_ptr;
}
/* Find an endpoint that is not in use. Do this by going through the endpoint
* array, check the callid. A callid nullpointer indicates that the endpoint
* is free */
static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints,
unsigned int number_endpoints)
{
struct mgcp_endpoint *endp;
unsigned int i;
for (i = 0; i < number_endpoints; i++) {
if (endpoints[i].callid == NULL) {
endp = &endpoints[i];
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,
int *cause)
{
char *endptr = NULL;
unsigned int gw = INT_MAX;
const char *endpoint_number_str;
struct mgcp_endpoint *endp;
*cause = 0;
/* Check if the domainname in the request is correct */
if (check_domain_name(cfg, mgcp)) {
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;
}
/*! Analyze and parse the the hader of an MGCP messeage string.
* \param[out] pdata caller provided memory to store the parsing results
* \param[in] data mgcp message string
* \returns when the status line was complete and transaction_id and
* endp out parameters are set, -1 on error */
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.
* The function will also automatically search the pool with
* available endpoints in order to find an endpoint that matches
* the endpoint string in in the header */
OSMO_ASSERT(data);
pdata->trans = "000000";
for (elem = strtok_r(data, " ", &save); elem;
elem = strtok_r(NULL, " ", &save)) {
switch (i) {
case 0:
pdata->trans = elem;
break;
case 1:
pdata->endp = find_endpoint(pdata->cfg, elem, &cause);
if (!pdata->endp) {
LOGP(DLMGCP, LOGL_ERROR,
"Unable to find Endpoint `%s'\n", elem);
return cause;
}
break;
case 2:
if (strcmp("MGCP", elem)) {
LOGP(DLMGCP, LOGL_ERROR,
"MGCP header parsing error\n");
return -510;
}
break;
case 3:
if (strcmp("1.0", elem)) {
LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' "
"not supported\n", elem);
return -528;
}
break;
}
i++;
}
if (i != 4) {
LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n");
pdata->trans = "000000";
pdata->endp = NULL;
return -510;
}
return 0;
}
/*! Extract OSMUX CID from an MGCP parameter line (string).
* \param[in] line single parameter line from the MGCP message
* \returns OSMUX CID, -1 on error */
int mgcp_parse_osmux_cid(const char *line)
{
int osmux_cid;
if (sscanf(line + 2, "Osmux: %u", &osmux_cid) != 1)
return -1;
if (osmux_cid > OSMUX_CID_MAX) {
LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
osmux_cid, OSMUX_CID_MAX);
return -1;
}
LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
return osmux_cid;
}
/*! Check MGCP parameter line (string) for plausibility.
* \param[in] endp pointer to endpoint (only used for log output)
* \param[in] line single parameter line from the MGCP message
* \returns 1 when line seems plausible, 0 on error */
int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
{
const size_t line_len = strlen(line);
if (line[0] != '\0' && line_len < 2) {
LOGP(DLMGCP, LOGL_ERROR,
"Wrong MGCP option format: '%s' on 0x%x\n",
line, ENDPOINT_NUMBER(endp));
return 0;
}
/* FIXME: A couple more checks wouldn't hurt... */
return 1;
}
/*! Check if the specified callid seems plausible.
* \param[in] endp pointer to endpoint
* \param{in] callid to verify
* \returns 1 when callid seems plausible, 0 on error */
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
{
/*! This function compares the supplied callid with the called that is
* stored in the endpoint structure. */
if (!endp)
return -1;
if (!callid)
return -1;
if (!endp->callid)
return -1;
if (strcmp(endp->callid, callid) != 0) {
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:0x%x CallIDs does not match '%s' != '%s'\n",
ENDPOINT_NUMBER(endp), endp->callid, callid);
return -1;
}
return 0;
}
/*! Check if the specified connection id seems plausible.
* \param[in] endp pointer to endpoint
* \param{in] connection id to verify
* \returns 1 when connection id seems plausible, 0 on error */
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
{
/* Check for null identifiers */
if (!conn_id) {
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:0x%x invalid ConnectionIdentifier (missing)\n",
ENDPOINT_NUMBER(endp));
return -1;
}
/* Check for empty connection identifiers */
if (strlen(conn_id) == 0) {
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:0x%x invalid ConnectionIdentifier (empty)\n",
ENDPOINT_NUMBER(endp));
return -1;
}
/* Check for over long connection identifiers */
if (strlen(conn_id) > MGCP_CONN_ID_LENGTH) {
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:0x%x invalid ConnectionIdentifier (too long) 0x%s\n",
ENDPOINT_NUMBER(endp), conn_id);
return -1;
}
/* Check if connection exists */
if (mgcp_conn_get(endp, conn_id))
return 0;
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:0x%x no connection found under ConnectionIdentifier 0x%s\n",
ENDPOINT_NUMBER(endp), conn_id);
return -1;
}
/*! Extract individual lines from MCGP message.
* \param[in] str MGCP message string, consisting of multiple lines
* \param{in] saveptr pointer to next line in str
* \returns line, NULL when done */
char *mgcp_strline(char *str, char **saveptr)
{
char *result;
/*! The function must be called with *str set to the input string
* for the first line. After that saveptr will be initalized.
* all consecutive lines are extracted by calling the function
* with str set to NULL. When done, the function will return NULL
* to indicate that all lines have been parsed. */
if (str)
*saveptr = str;
result = *saveptr;
if (*saveptr != NULL) {
*saveptr = strpbrk(*saveptr, "\r\n");
if (*saveptr != NULL) {
char *eos = *saveptr;
if ((*saveptr)[0] == '\r' && (*saveptr)[1] == '\n')
(*saveptr)++;
(*saveptr)++;
if ((*saveptr)[0] == '\0')
*saveptr = NULL;
*eos = '\0';
}
}
return result;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,700 @@
/*
* (C) 2012-2013 by Pablo Neira Ayuso <pablo@gnumonks.org>
* (C) 2012-2013 by On Waves ehf <http://www.on-waves.com>
* All rights not specifically granted under this license are reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*/
#include <stdio.h> /* for printf */
#include <string.h> /* for memcpy */
#include <stdlib.h> /* for abs */
#include <inttypes.h> /* for PRIu64 */
#include <netinet/in.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/netif/osmux.h>
#include <osmocom/netif/rtp.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_endp.h>
static struct osmo_fd osmux_fd;
static LLIST_HEAD(osmux_handle_list);
struct osmux_handle {
struct llist_head head;
struct osmux_in_handle *in;
struct in_addr rem_addr;
int rem_port;
int refcnt;
};
static void *osmux;
/* Deliver OSMUX batch to the remote end */
static void osmux_deliver_cb(struct msgb *batch_msg, void *data)
{
struct osmux_handle *handle = data;
struct sockaddr_in out = {
.sin_family = AF_INET,
.sin_port = handle->rem_port,
};
memcpy(&out.sin_addr, &handle->rem_addr, sizeof(handle->rem_addr));
sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0,
(struct sockaddr *)&out, sizeof(out));
msgb_free(batch_msg);
}
/* Lookup existing OSMUX handle for specified destination address. */
static struct osmux_handle *
osmux_handle_find_get(struct in_addr *addr, int rem_port)
{
struct osmux_handle *h;
llist_for_each_entry(h, &osmux_handle_list, head) {
if (memcmp(&h->rem_addr, addr, sizeof(struct in_addr)) == 0 &&
h->rem_port == rem_port) {
LOGP(DLMGCP, LOGL_DEBUG, "using existing OSMUX handle "
"for addr=%s:%d\n",
inet_ntoa(*addr), ntohs(rem_port));
h->refcnt++;
return h;
}
}
return NULL;
}
/* Put down no longer needed OSMUX handle */
static void osmux_handle_put(struct osmux_in_handle *in)
{
struct osmux_handle *h;
llist_for_each_entry(h, &osmux_handle_list, head) {
if (h->in == in) {
if (--h->refcnt == 0) {
LOGP(DLMGCP, LOGL_INFO,
"Releasing unused osmux handle for %s:%d\n",
inet_ntoa(h->rem_addr),
ntohs(h->rem_port));
LOGP(DLMGCP, LOGL_INFO, "Stats: "
"input RTP msgs: %u bytes: %"PRIu64" "
"output osmux msgs: %u bytes: %"PRIu64"\n",
in->stats.input_rtp_msgs,
in->stats.input_rtp_bytes,
in->stats.output_osmux_msgs,
in->stats.output_osmux_bytes);
llist_del(&h->head);
osmux_xfrm_input_fini(h->in);
talloc_free(h);
}
return;
}
}
LOGP(DLMGCP, LOGL_ERROR, "cannot find Osmux input handle %p\n", in);
}
/* Allocate free OSMUX handle */
static struct osmux_handle *
osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
{
struct osmux_handle *h;
h = talloc_zero(osmux, struct osmux_handle);
if (!h)
return NULL;
h->rem_addr = *addr;
h->rem_port = rem_port;
h->refcnt++;
h->in = talloc_zero(h, struct osmux_in_handle);
if (!h->in) {
talloc_free(h);
return NULL;
}
/* sequence number to start OSMUX message from */
h->in->osmux_seq = 0;
h->in->batch_factor = cfg->osmux_batch;
/* If batch size is zero, the library defaults to 1470 bytes. */
h->in->batch_size = cfg->osmux_batch_size;
h->in->deliver = osmux_deliver_cb;
osmux_xfrm_input_init(h->in);
h->in->data = h;
llist_add(&h->head, &osmux_handle_list);
LOGP(DLMGCP, LOGL_DEBUG, "created new OSMUX handle for addr=%s:%d\n",
inet_ntoa(*addr), ntohs(rem_port));
return h;
}
/* Lookup existing handle for a specified address, if the handle can not be
* 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)
{
struct osmux_handle *h;
h = osmux_handle_find_get(addr, rem_port);
if (h != NULL)
return h->in;
h = osmux_handle_alloc(cfg, addr, rem_port);
if (h == NULL)
return NULL;
return h->in;
}
/*! send RTP packet through OSMUX connection.
* \param[in] buf rtp data
* \param[in] buf_len length of rtp data
* \param[in] conn associated RTP connection
* \returns 0 on success, -1 on ERROR */
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn)
{
int ret;
struct msgb *msg;
msg = msgb_alloc(4096, "RTP");
if (!msg)
return -1;
memcpy(msg->data, buf, buf_len);
msgb_put(msg, buf_len);
while ((ret = osmux_xfrm_input(conn->osmux.in, msg, conn->osmux.cid)) > 0) {
/* batch full, build and deliver it */
osmux_xfrm_input_deliver(conn->osmux.in);
}
return 0;
}
/* Lookup the endpoint that corresponds to the specified address (port) */
static struct mgcp_endpoint *
endpoint_lookup(struct mgcp_config *cfg, int cid,
struct in_addr *from_addr, int type)
{
struct mgcp_endpoint *endp = NULL;
int i;
struct mgcp_conn_rtp *conn_net = NULL;
struct mgcp_conn_rtp *conn_bts = NULL;
for (i=0; i<cfg->trunk.number_endpoints; i++) {
struct in_addr *this;
endp = &cfg->trunk.endpoints[i];
#if 0
if (!tmp->allocated)
continue;
#endif
switch(type) {
case MGCP_DEST_NET:
/* FIXME: Get rid of CONN_ID_XXX! */
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (conn_net)
this = &conn_net->end.addr;
else
this = NULL;
break;
case MGCP_DEST_BTS:
/* FIXME: Get rid of CONN_ID_XXX! */
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
if (conn_bts)
this = &conn_bts->end.addr;
else
this = NULL;
break;
default:
/* Should not ever happen */
LOGP(DLMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type);
return NULL;
}
/* FIXME: Get rid of CONN_ID_XXX! */
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (conn_net && this && conn_net->osmux.cid == cid
&& this->s_addr == from_addr->s_addr)
return endp;
}
LOGP(DLMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n", cid);
return NULL;
}
static void scheduled_tx_net_cb(struct msgb *msg, void *data)
{
struct mgcp_endpoint *endp = data;
struct mgcp_conn_rtp *conn_net = NULL;
struct mgcp_conn_rtp *conn_bts = NULL;
/* FIXME: Get rid of CONN_ID_XXX! */
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_bts || !conn_net)
return;
struct sockaddr_in addr = {
.sin_addr = conn_net->end.addr,
.sin_port = conn_net->end.rtp_port,
};
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! */
mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
conn_bts, conn_net);
msgb_free(msg);
}
static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
{
struct mgcp_endpoint *endp = data;
struct mgcp_conn_rtp *conn_net = NULL;
struct mgcp_conn_rtp *conn_bts = NULL;
/* FIXME: Get rid of CONN_ID_XXX! */
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_bts || !conn_net)
return;
struct sockaddr_in addr = {
.sin_addr = conn_bts->end.addr,
.sin_port = conn_bts->end.rtp_port,
};
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! */
mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
conn_net, conn_bts);
msgb_free(msg);
}
static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr)
{
struct msgb *msg;
socklen_t slen = sizeof(*addr);
int ret;
msg = msgb_alloc(4096, "OSMUX");
if (!msg) {
LOGP(DLMGCP, LOGL_ERROR, "cannot allocate message\n");
return NULL;
}
ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0,
(struct sockaddr *)addr, &slen);
if (ret <= 0) {
msgb_free(msg);
LOGP(DLMGCP, LOGL_ERROR, "cannot receive message\n");
return NULL;
}
msgb_put(msg, ret);
return msg;
}
#define osmux_chunk_length(msg, rem) (rem - msg->len);
int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
{
struct msgb *msg;
struct osmux_hdr *osmuxh;
struct llist_head list;
struct sockaddr_in addr;
struct mgcp_config *cfg = ofd->data;
uint32_t rem;
struct mgcp_conn_rtp *conn_net = NULL;
msg = osmux_recv(ofd, &addr);
if (!msg)
return -1;
/* not any further processing dummy messages */
if (msg->data[0] == MGCP_DUMMY_LOAD)
goto out;
rem = msg->len;
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
struct mgcp_endpoint *endp;
/* Yes, we use MGCP_DEST_NET to locate the origin */
endp = endpoint_lookup(cfg, osmuxh->circuit_id,
&addr.sin_addr, MGCP_DEST_NET);
/* FIXME: Get rid of CONN_ID_XXX! */
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_net)
goto out;
if (!endp) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot find an endpoint for circuit_id=%d\n",
osmuxh->circuit_id);
goto out;
}
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
conn_net->osmux.stats.chunks++;
rem = msg->len;
osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
}
out:
msgb_free(msg);
return 0;
}
/* This is called from the bsc-nat */
static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
struct msgb *msg)
{
struct mgcp_endpoint *endp;
uint8_t osmux_cid;
struct mgcp_conn_rtp *conn_net = NULL;
if (msg->len < 1 + sizeof(osmux_cid)) {
LOGP(DLMGCP, LOGL_ERROR,
"Discarding truncated Osmux dummy load\n");
goto out;
}
LOGP(DLMGCP, LOGL_DEBUG, "Received Osmux dummy load from %s\n",
inet_ntoa(addr->sin_addr));
if (!cfg->osmux) {
LOGP(DLMGCP, LOGL_ERROR,
"bsc wants to use Osmux but bsc-nat did not request it\n");
goto out;
}
/* extract the osmux CID from the dummy message */
memcpy(&osmux_cid, &msg->data[1], sizeof(osmux_cid));
endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, MGCP_DEST_BTS);
if (!endp) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot find endpoint for Osmux CID %d\n", osmux_cid);
goto out;
}
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_net)
goto out;
if (conn_net->osmux.state == OSMUX_STATE_ENABLED)
goto out;
if (osmux_enable_conn(endp, conn_net, &addr->sin_addr, addr->sin_port) < 0 ) {
LOGP(DLMGCP, LOGL_ERROR,
"Could not enable osmux in endpoint %d\n",
ENDPOINT_NUMBER(endp));
goto out;
}
LOGP(DLMGCP, LOGL_INFO, "Enabling osmux in endpoint %d for %s:%u\n",
ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr),
ntohs(addr->sin_port));
out:
msgb_free(msg);
return 0;
}
int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
{
struct msgb *msg;
struct osmux_hdr *osmuxh;
struct llist_head list;
struct sockaddr_in addr;
struct mgcp_config *cfg = ofd->data;
uint32_t rem;
struct mgcp_conn_rtp *conn_net = NULL;
msg = osmux_recv(ofd, &addr);
if (!msg)
return -1;
/* not any further processing dummy messages */
if (msg->data[0] == MGCP_DUMMY_LOAD)
return osmux_handle_dummy(cfg, &addr, msg);
rem = msg->len;
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
struct mgcp_endpoint *endp;
/* Yes, we use MGCP_DEST_BTS to locate the origin */
endp = endpoint_lookup(cfg, osmuxh->circuit_id,
&addr.sin_addr, MGCP_DEST_BTS);
/* FIXME: Get rid of CONN_ID_XXX! */
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_net)
goto out;
if (!endp) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot find an endpoint for circuit_id=%d\n",
osmuxh->circuit_id);
goto out;
}
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
conn_net->osmux.stats.chunks++;
rem = msg->len;
osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
}
out:
msgb_free(msg);
return 0;
}
int osmux_init(int role, struct mgcp_config *cfg)
{
int ret;
switch(role) {
case OSMUX_ROLE_BSC:
osmux_fd.cb = osmux_read_from_bsc_nat_cb;
break;
case OSMUX_ROLE_BSC_NAT:
osmux_fd.cb = osmux_read_from_bsc_cb;
break;
default:
LOGP(DLMGCP, LOGL_ERROR, "wrong role for OSMUX\n");
return -1;
}
osmux_fd.data = cfg;
ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port);
if (ret < 0) {
LOGP(DLMGCP, LOGL_ERROR, "cannot bind OSMUX socket\n");
return ret;
}
mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp);
osmux_fd.when |= BSC_FD_READ;
ret = osmo_fd_register(&osmux_fd);
if (ret < 0) {
LOGP(DLMGCP, LOGL_ERROR, "cannot register OSMUX socket\n");
return ret;
}
cfg->osmux_init = 1;
return 0;
}
/*! enable OSXMUX circuit for a specified connection.
* \param[in] endp mgcp endpoint (configuration)
* \param[in] conn connection to disable
* \param[in] addr IP address of remote OSMUX endpoint
* \param[in] port portnumber of the remote OSMUX endpoint
* \returns 0 on success, -1 on ERROR */
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
struct in_addr *addr, uint16_t port)
{
/*! If osmux is enabled, initialize the output handler. This handler is
* used to reconstruct the RTP flow from osmux. The RTP SSRC is
* allocated based on the circuit ID (conn_net->osmux.cid), which is unique
* in the local scope to the BSC/BSC-NAT. We use it to divide the RTP
* SSRC space (2^32) by the 256 possible circuit IDs, then randomly
* select one value from that window. Thus, we have no chance to have
* overlapping RTP SSRC traveling to the BTSes behind the BSC,
* similarly, for flows traveling to the MSC.
*/
static const uint32_t rtp_ssrc_winlen = UINT32_MAX / 256;
uint16_t osmux_dummy = endp->cfg->osmux_dummy;
/* Check if osmux is enabled for the specified connection */
if (conn->osmux.state == OSMUX_STATE_DISABLED) {
LOGP(DLMGCP, LOGL_ERROR, "OSMUX not enabled for conn:%s\n",
mgcp_conn_dump(conn->conn));
return -1;
}
osmux_xfrm_output_init(&conn->osmux.out,
(conn->osmux.cid * rtp_ssrc_winlen) +
(random() % rtp_ssrc_winlen));
conn->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
if (!conn->osmux.in) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle for conn:%s\n",
mgcp_conn_dump(conn->conn));
return -1;
}
if (!osmux_xfrm_input_open_circuit(conn->osmux.in, conn->osmux.cid, osmux_dummy)) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot open osmux circuit %u for conn:%s\n",
conn->osmux.cid, mgcp_conn_dump(conn->conn));
return -1;
}
switch (endp->cfg->role) {
case MGCP_BSC_NAT:
conn->type = MGCP_OSMUX_BSC_NAT;
break;
case MGCP_BSC:
conn->type = MGCP_OSMUX_BSC;
break;
}
conn->osmux.state = OSMUX_STATE_ENABLED;
return 0;
}
/*! disable OSXMUX circuit for a specified connection.
* \param[in] conn connection to disable */
void osmux_disable_conn(struct mgcp_conn_rtp *conn)
{
if (!conn)
return;
if (conn->osmux.state != OSMUX_STATE_ENABLED)
return;
LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n",
conn->conn->id, conn->osmux.cid);
osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
conn->osmux.state = OSMUX_STATE_DISABLED;
conn->osmux.cid = -1;
osmux_handle_put(conn->osmux.in);
}
/*! relase OSXMUX cid, that had been allocated to this connection.
* \param[in] conn connection with OSMUX cid to release */
void osmux_release_cid(struct mgcp_conn_rtp *conn)
{
if (!conn)
return;
if (conn->osmux.state != OSMUX_STATE_ENABLED)
return;
if (conn->osmux.allocated_cid >= 0)
osmux_put_cid(conn->osmux.allocated_cid);
conn->osmux.allocated_cid = -1;
}
/*! allocate OSXMUX cid to connection.
* \param[in] conn connection for which we allocate the OSMUX cid*/
void osmux_allocate_cid(struct mgcp_conn_rtp *conn)
{
osmux_release_cid(conn);
conn->osmux.allocated_cid = osmux_get_cid();
}
/*! send RTP dummy packet to OSMUX connection port.
* \param[in] endp mcgp endpoint that holds the RTP connection
* \param[in] conn associated RTP connection
* \returns bytes sent, -1 on error */
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
char buf[1 + sizeof(uint8_t)];
struct in_addr addr_unset = {};
/*! The dummy packet will not be sent via the actual OSMUX connection,
* instead it is sent out of band to port where the remote OSMUX
* multplexer is listening. The goal is to ensure that the connection
* is kept open */
/*! We don't need to send the dummy load for osmux so often as another
* endpoint may have already punched the hole in the firewall. This
* approach is simple though. */
buf[0] = MGCP_DUMMY_LOAD;
memcpy(&buf[1], &conn->osmux.cid, sizeof(conn->osmux.cid));
/* Wait until we have the connection information from MDCX */
if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0)
return 0;
if (conn->osmux.state == OSMUX_STATE_ACTIVATING) {
if (osmux_enable_conn(endp, conn, &conn->end.addr,
htons(endp->cfg->osmux_port)) < 0) {
LOGP(DLMGCP, LOGL_ERROR,
"Could not activate osmux for conn:%s\n",
mgcp_conn_dump(conn->conn));
}
LOGP(DLMGCP, LOGL_ERROR,
"Osmux CID %u for %s:%u is now enabled\n",
conn->osmux.cid, inet_ntoa(conn->end.addr),
endp->cfg->osmux_port);
}
LOGP(DLMGCP, LOGL_DEBUG,
"sending OSMUX dummy load to %s CID %u\n",
inet_ntoa(conn->end.addr), conn->osmux.cid);
return mgcp_udp_send(osmux_fd.fd, &conn->end.addr,
htons(endp->cfg->osmux_port), buf, sizeof(buf));
}
/*! bsc-nat allocates/releases the OSMUX cids (Circuit IDs). */
static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1) / 8];
/*! count the number of taken OSMUX cids.
* \returns number of OSMUX cids in use */
int osmux_used_cid(void)
{
int i, j, used = 0;
for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
for (j = 0; j < 8; j++) {
if (osmux_cid_bitmap[i] & (1 << j))
used += 1;
}
}
return used;
}
/*! take a free OSMUX cid.
* \returns OSMUX cid */
int osmux_get_cid(void)
{
int i, j;
for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
for (j = 0; j < 8; j++) {
if (osmux_cid_bitmap[i] & (1 << j))
continue;
osmux_cid_bitmap[i] |= (1 << j);
LOGP(DLMGCP, LOGL_DEBUG,
"Allocating Osmux CID %u from pool\n", (i * 8) + j);
return (i * 8) + j;
}
}
LOGP(DLMGCP, LOGL_ERROR, "All Osmux circuits are in use!\n");
return -1;
}
/*! put back a no longer used OSMUX cid.
* \param[in] osmux_cid OSMUX cid */
void osmux_put_cid(uint8_t osmux_cid)
{
LOGP(DLMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid);
osmux_cid_bitmap[osmux_cid / 8] &= ~(1 << (osmux_cid % 8));
}

File diff suppressed because it is too large Load Diff

410
src/libosmo-mgcp/mgcp_sdp.c Normal file
View File

@@ -0,0 +1,410 @@
/*
* Some SDP file parsing...
*
* (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2014 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/core/msgb.h>
#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>
struct sdp_rtp_map {
/* the type */
int payload_type;
/* null, static or later dynamic codec name */
char *codec_name;
/* A pointer to the original line for later parsing */
char *map_line;
int rate;
int channels;
};
/*! Set codec configuration depending on payload type and codec name.
* \param[in] ctx talloc context.
* \param[out] codec configuration (caller provided memory).
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
* \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
* \returns 0 on success, -1 on failure. */
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
int payload_type, const char *audio_name)
{
int rate = codec->rate;
int channels = codec->channels;
char audio_codec[64];
talloc_free(codec->subtype_name);
codec->subtype_name = NULL;
talloc_free(codec->audio_name);
codec->audio_name = NULL;
if (payload_type != PTYPE_UNDEFINED)
codec->payload_type = payload_type;
if (!audio_name) {
switch (payload_type) {
case 0:
audio_name = "PCMU/8000/1";
break;
case 3:
audio_name = "GSM/8000/1";
break;
case 8:
audio_name = "PCMA/8000/1";
break;
case 18:
audio_name = "G729/8000/1";
break;
default:
/* Payload type is unknown, don't change rate and
* channels. */
/* TODO: return value? */
return 0;
}
}
if (sscanf(audio_name, "%63[^/]/%d/%d",
audio_codec, &rate, &channels) < 1)
return -EINVAL;
codec->rate = rate;
codec->channels = channels;
codec->subtype_name = talloc_strdup(ctx, audio_codec);
codec->audio_name = talloc_strdup(ctx, audio_name);
if (!strcmp(audio_codec, "G729")) {
codec->frame_duration_num = 10;
codec->frame_duration_den = 1000;
} else {
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
}
if (payload_type < 0) {
payload_type = 96;
if (rate == 8000 && channels == 1) {
if (!strcmp(audio_codec, "GSM"))
payload_type = 3;
else if (!strcmp(audio_codec, "PCMA"))
payload_type = 8;
else if (!strcmp(audio_codec, "PCMU"))
payload_type = 0;
else if (!strcmp(audio_codec, "G729"))
payload_type = 18;
}
codec->payload_type = payload_type;
}
if (channels != 1)
LOGP(DLMGCP, LOGL_NOTICE,
"Channels != 1 in SDP: '%s'\n", audio_name);
return 0;
}
static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
{
int i;
for (i = 0; i < used; ++i) {
switch (codecs[i].payload_type) {
case 0:
codecs[i].codec_name = "PCMU";
codecs[i].rate = 8000;
codecs[i].channels = 1;
break;
case 3:
codecs[i].codec_name = "GSM";
codecs[i].rate = 8000;
codecs[i].channels = 1;
break;
case 8:
codecs[i].codec_name = "PCMA";
codecs[i].rate = 8000;
codecs[i].channels = 1;
break;
case 18:
codecs[i].codec_name = "G729";
codecs[i].rate = 8000;
codecs[i].channels = 1;
break;
}
}
}
static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
int payload, const char *audio_name)
{
int i;
for (i = 0; i < used; ++i) {
char audio_codec[64];
int rate = -1;
int channels = -1;
if (codecs[i].payload_type != payload)
continue;
if (sscanf(audio_name, "%63[^/]/%d/%d",
audio_codec, &rate, &channels) < 1) {
LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
audio_name);
continue;
}
codecs[i].map_line = talloc_strdup(ctx, audio_name);
codecs[i].codec_name = talloc_strdup(ctx, audio_codec);
codecs[i].rate = rate;
codecs[i].channels = channels;
return;
}
LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload,
audio_name);
}
/* Check if the codec matches what is set up in the trunk config */
static int is_codec_compatible(const struct mgcp_endpoint *endp,
const struct sdp_rtp_map *codec)
{
char *codec_str;
char audio_codec[64];
if (!codec->codec_name)
return 0;
/* GSM, GSM/8000 and GSM/8000/1 should all be compatible...
* let's go by name first. */
codec_str = endp->tcfg->audio_name;
if (sscanf(codec_str, "%63[^/]/%*d/%*d", audio_codec) < 1)
return 0;
return strcasecmp(audio_codec, codec->codec_name) == 0;
}
/*! Analyze SDP input string.
* \param[in] endp trunk endpoint.
* \param[out] conn associated rtp connection.
* \param[out] caller provided memory to store the parsing results.
* \returns 0 on success, -1 on failure.
*
* Note: In conn (conn->end) the function returns the packet duration,
* the rtp port and the rtcp port */
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn,
struct mgcp_parse_data *p)
{
struct sdp_rtp_map codecs[10];
int codecs_used = 0;
char *line;
int maxptime = -1;
int i;
int codecs_assigned = 0;
void *tmp_ctx = talloc_new(NULL);
struct mgcp_rtp_end *rtp;
int payload;
int ptime, ptime2 = 0;
char audio_name[64];
int port, rc;
char ipv4[16];
OSMO_ASSERT(endp);
OSMO_ASSERT(conn);
OSMO_ASSERT(p);
rtp = &conn->end;
memset(&codecs, 0, sizeof(codecs));
for_each_line(line, p->save) {
switch (line[0]) {
case 'o':
case 's':
case 't':
case 'v':
/* skip these SDP attributes */
break;
case 'a':
if (sscanf(line, "a=rtpmap:%d %63s",
&payload, audio_name) == 2) {
codecs_update(tmp_ctx, codecs,
codecs_used, payload, audio_name);
} else
if (sscanf
(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
if (ptime2 > 0 && ptime2 != ptime)
rtp->packet_duration_ms = 0;
else
rtp->packet_duration_ms = ptime;
} else if (sscanf(line, "a=maxptime:%d", &ptime2)
== 1) {
maxptime = ptime2;
}
break;
case 'm':
rc = sscanf(line,
"m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d",
&port, &codecs[0].payload_type,
&codecs[1].payload_type,
&codecs[2].payload_type,
&codecs[3].payload_type,
&codecs[4].payload_type,
&codecs[5].payload_type,
&codecs[6].payload_type,
&codecs[7].payload_type,
&codecs[8].payload_type,
&codecs[9].payload_type);
if (rc >= 2) {
rtp->rtp_port = htons(port);
rtp->rtcp_port = htons(port + 1);
codecs_used = rc - 1;
codecs_initialize(tmp_ctx, codecs, codecs_used);
}
break;
case 'c':
if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
inet_aton(ipv4, &rtp->addr);
}
break;
default:
if (p->endp)
LOGP(DLMGCP, LOGL_NOTICE,
"Unhandled SDP option: '%c'/%d on 0x%x\n",
line[0], line[0],
ENDPOINT_NUMBER(p->endp));
else
LOGP(DLMGCP, LOGL_NOTICE,
"Unhandled SDP option: '%c'/%d\n",
line[0], line[0]);
break;
}
}
/* Now select the primary and alt_codec */
for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) {
struct mgcp_rtp_codec *codec = codecs_assigned == 0 ?
&rtp->codec : &rtp->alt_codec;
if (endp->tcfg->no_audio_transcoding &&
!is_codec_compatible(endp, &codecs[i])) {
LOGP(DLMGCP, LOGL_NOTICE, "Skipping codec %s\n",
codecs[i].codec_name);
continue;
}
mgcp_set_audio_info(p->cfg, codec,
codecs[i].payload_type, codecs[i].map_line);
codecs_assigned += 1;
}
if (codecs_assigned > 0) {
/* TODO/XXX: Store this per codec and derive it on use */
if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den >
rtp->codec.frame_duration_num * 1500) {
/* more than 1 frame */
rtp->packet_duration_ms = 0;
}
LOGP(DLMGCP, LOGL_NOTICE,
"Got media info via SDP: port %d, payload %d (%s), "
"duration %d, addr %s\n",
ntohs(rtp->rtp_port), rtp->codec.payload_type,
rtp->codec.subtype_name ? rtp->
codec.subtype_name : "unknown", rtp->packet_duration_ms,
inet_ntoa(rtp->addr));
}
talloc_free(tmp_ctx);
return codecs_assigned > 0;
}
/*! Generate SDP response string.
* \param[in] endp trunk endpoint.
* \param[in] conn associated rtp connection.
* \param[out] sdp msg buffer to append resulting SDP string data.
* \param[in] addr IPV4 address string (e.g. 192.168.100.1).
* \returns 0 on success, -1 on failure. */
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
const char *addr)
{
const char *fmtp_extra;
const char *audio_name;
int payload_type;
int rc;
OSMO_ASSERT(endp);
OSMO_ASSERT(conn);
OSMO_ASSERT(sdp);
OSMO_ASSERT(addr);
/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
&payload_type, &audio_name,
&fmtp_extra,
(struct mgcp_conn_rtp *)conn);
rc = msgb_printf(sdp,
"v=0\r\n"
"o=- %s 23 IN IP4 %s\r\n"
"s=-\r\n"
"c=IN IP4 %s\r\n"
"t=0 0\r\n", conn->conn->id, addr, addr);
if (rc < 0)
goto buffer_too_small;
if (payload_type >= 0) {
rc = msgb_printf(sdp, "m=audio %d RTP/AVP %d\r\n",
conn->end.local_port, payload_type);
if (rc < 0)
goto buffer_too_small;
if (audio_name && endp->tcfg->audio_send_name) {
rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n",
payload_type, audio_name);
if (rc < 0)
goto buffer_too_small;
}
if (fmtp_extra) {
rc = msgb_printf(sdp, "%s\r\n", fmtp_extra);
if (rc < 0)
goto buffer_too_small;
}
}
if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
rc = msgb_printf(sdp, "a=ptime:%u\r\n",
conn->end.packet_duration_ms);
if (rc < 0)
goto buffer_too_small;
}
return 0;
buffer_too_small:
LOGP(DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
return -1;
}

View File

@@ -0,0 +1,131 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* The statistics generator */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/mgcp/mgcp_stat.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <limits.h>
/* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
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;
if (!state->stats.initialized) {
*expected = 0;
*loss = 0;
return;
}
/*
* Make sure the sign is correct and use the biggest
* positive/negative number that fits.
*/
*loss = *expected - end->stats.packets_rx;
if (*expected < end->stats.packets_rx) {
if (*loss > 0)
*loss = INT_MIN;
} else {
if (*loss < 0)
*loss = INT_MAX;
}
}
/* Helper function for mgcp_format_stats_rtp() to calculate jitter */
uint32_t calc_jitter(struct mgcp_rtp_state *state)
{
if (!state->stats.initialized)
return 0;
return state->stats.jitter >> 4;
}
/* Generate statistics for an RTP connection */
static void mgcp_format_stats_rtp(char *str, size_t str_len,
struct mgcp_conn_rtp *conn)
{
uint32_t expected, jitter;
int ploss;
int nchars;
calc_loss(&conn->state, &conn->end, &expected, &ploss);
jitter = calc_jitter(&conn->state);
nchars = snprintf(str, str_len,
"\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
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;
str += nchars;
str_len -= nchars;
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;
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:
str[str_len - 1] = '\0';
}
/*! format statistics into an mgcp parameter string.
* \param[out] str resulting string
* \param[in] str_len length of the string buffer
* \param[in] conn connection to evaluate */
void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn)
{
memset(str, 0, str_len);
if (!conn)
return;
/* NOTE: At the moment we only support generating statistics for
* RTP connections. However, in the future we may also want to
* generate statistics for other connection types as well. Lets
* keep this option open: */
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
mgcp_format_stats_rtp(str, str_len, &conn->u.rtp);
break;
default:
break;
}
}

1324
src/libosmo-mgcp/mgcp_vty.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -24,8 +24,8 @@ osmo_bsc_mgcp_SOURCES = \
osmo_bsc_mgcp_LDADD = \
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBBCG729_LIBS) \
$(LIBRARY_GSM) \
$(NULL)

View File

@@ -189,33 +189,33 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
int mgcp_vty_is_config_node(struct vty *vty, int node)
{
switch (node) {
case CONFIG_NODE:
return 0;
switch (node) {
case CONFIG_NODE:
return 0;
default:
return 1;
}
default:
return 1;
}
}
int mgcp_vty_go_parent(struct vty *vty)
{
switch (vty->node) {
case TRUNK_NODE:
vty->node = MGCP_NODE;
vty->index = NULL;
break;
case MGCP_NODE:
default:
if (mgcp_vty_is_config_node(vty, vty->node))
vty->node = CONFIG_NODE;
else
vty->node = ENABLE_NODE;
switch (vty->node) {
case TRUNK_NODE:
vty->node = MGCP_NODE;
vty->index = NULL;
break;
case MGCP_NODE:
default:
if (mgcp_vty_is_config_node(vty, vty->node))
vty->node = CONFIG_NODE;
else
vty->node = ENABLE_NODE;
vty->index = NULL;
}
vty->index = NULL;
}
return vty->node;
return vty->node;
}
@@ -231,8 +231,8 @@ static const struct log_info_cat log_categories[] = {
};
const struct log_info log_info = {
.cat = log_categories,
.num_cat = ARRAY_SIZE(log_categories),
.cat = log_categories,
.num_cat = ARRAY_SIZE(log_categories),
};
int main(int argc, char **argv)
@@ -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)
@@ -282,8 +282,8 @@ int main(int argc, char **argv)
/* set some callbacks */
cfg->reset_cb = mgcp_rsip_cb;
/* we need to bind a socket */
if (rc == 0) {
/* we need to bind a socket */
if (rc == 0) {
cfg->gw_fd.bfd.when = BSC_FD_READ;
cfg->gw_fd.bfd.cb = read_call_agent;
cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);

30
src/osmo-mgw/Makefile.am Normal file
View File

@@ -0,0 +1,30 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir) \
$(NULL)
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
bin_PROGRAMS = \
osmo-mgw \
$(NULL)
osmo_mgw_SOURCES = \
mgw_main.c \
$(NULL)
osmo_mgw_LDADD = \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(NULL)

338
src/osmo-mgw/mgw_main.c Normal file
View File

@@ -0,0 +1,338 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* The main method to drive it as a standalone process */
/*
* (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2011 by On-Waves
* (C) 2017 by sysmocom - s.f.m.c. GmbH, Author: Philipp Maier
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <osmocom/mgcp/mgcp.h>
#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>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/socket.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/ports.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/misc.h>
#include "../../bscconfig.h"
#define _GNU_SOURCE
#include <getopt.h>
/* FIXME: Make use of the rtp proxy code */
static struct mgcp_config *cfg;
static struct mgcp_trunk_config *reset_trunk;
static int reset_endpoints = 0;
static int daemonize = 0;
const char *osmomgw_copyright =
"Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
"Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
"Contributions by Pablo Neira Ayuso, Jacob Erlbeck, Neels Hofmeyr\r\n"
"Philipp Maier\r\n\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n";
static char *config_file = "osmo-mgw.cfg";
/* used by msgb and mgcp */
void *tall_bsc_ctx = NULL;
static void print_help()
{
printf("Some useful help...\n");
printf(" -h --help is printing this text.\n");
printf(" -c --config-file filename The config file to use.\n");
printf(" -s --disable-color\n");
printf(" -D --daemonize Fork the process into a background daemon\n");
printf(" -V --version Print the version number\n");
}
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"config-file", 1, 0, 'c'},
{"daemonize", 0, 0, 'D'},
{"version", 0, 0, 'V'},
{"disable-color", 0, 0, 's'},
{0, 0, 0, 0},
};
c = getopt_long(argc, argv, "hc:VD", long_options, &option_index);
if (c == -1)
break;
switch(c) {
case 'h':
print_help();
exit(0);
break;
case 'c':
config_file = talloc_strdup(tall_bsc_ctx, optarg);
break;
case 's':
log_set_use_color(osmo_stderr_target, 0);
break;
case 'V':
print_version(1);
exit(0);
break;
case 'D':
daemonize = 1;
break;
default:
/* ignore */
break;
};
}
}
/* Callback function to be called when the RSIP ("Reset in Progress") mgcp
* command is received */
static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
{
/* Set flag so that, when read_call_agent() is called next time
* the reset can progress */
reset_endpoints = 1;
reset_trunk = tcfg;
return 0;
}
static int read_call_agent(struct osmo_fd *fd, unsigned int what)
{
struct sockaddr_in addr;
socklen_t slen = sizeof(addr);
struct msgb *msg;
struct msgb *resp;
int i;
msg = (struct msgb *) fd->data;
/* read one less so we can use it as a \0 */
int rc = recvfrom(cfg->gw_fd.bfd.fd, msg->data, msg->data_len - 1, 0,
(struct sockaddr *) &addr, &slen);
if (rc < 0) {
perror("Gateway failed to read");
return -1;
} else if (slen > sizeof(addr)) {
fprintf(stderr, "Gateway received message from outerspace: %zu %zu\n",
(size_t) slen, sizeof(addr));
return -1;
}
/* handle message now */
msg->l2h = msgb_put(msg, rc);
resp = mgcp_handle_message(cfg, msg);
msgb_reset(msg);
if (resp) {
sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
msgb_free(resp);
}
/* reset endpoints */
if (reset_endpoints) {
LOGP(DLMGCP, LOGL_NOTICE,
"Asked to reset endpoints: %d/%d\n",
reset_trunk->trunk_nr, reset_trunk->trunk_type);
/* reset flag */
reset_endpoints = 0;
/* Walk over all endpoints and trigger a release, this will release all
* endpoints, possible open connections are forcefully dropped */
for (i = 1; i < reset_trunk->number_endpoints; ++i)
mgcp_endp_release(&reset_trunk->endpoints[i]);
}
return 0;
}
int mgcp_vty_is_config_node(struct vty *vty, int node)
{
switch (node) {
case CONFIG_NODE:
return 0;
default:
return 1;
}
}
int mgcp_vty_go_parent(struct vty *vty)
{
switch (vty->node) {
case TRUNK_NODE:
vty->node = MGCP_NODE;
vty->index = NULL;
break;
case MGCP_NODE:
default:
if (mgcp_vty_is_config_node(vty, vty->node))
vty->node = CONFIG_NODE;
else
vty->node = ENABLE_NODE;
vty->index = NULL;
}
return vty->node;
}
static struct vty_app_info vty_info = {
.name = "OsmoMGW",
.version = PACKAGE_VERSION,
.go_parent_cb = mgcp_vty_go_parent,
.is_config_node = mgcp_vty_is_config_node,
};
static const struct log_info_cat log_categories[] = {
/* DLMGCP is provided by the MGCP library */
[DRTP] = {
.name = "DRTP",
.description = "RTP stream handling",
.color = "\033[1;30m",
.enabled = 1,.loglevel = LOGL_NOTICE,
},
};
const struct log_info log_info = {
.cat = log_categories,
.num_cat = ARRAY_SIZE(log_categories),
};
int main(int argc, char **argv)
{
unsigned int flags;
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_logging2(tall_bsc_ctx, &log_info);
cfg = mgcp_config_alloc();
if (!cfg)
return -1;
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();
handle_options(argc, argv);
rate_ctr_init(tall_bsc_ctx);
osmo_stats_init(tall_bsc_ctx);
rc = mgcp_parse_config(config_file, cfg, MGCP_BSC);
if (rc < 0)
return rc;
/* start telnet after reading config for vty_get_bind_addr() */
rc = telnet_init_dynif(tall_bsc_ctx, NULL,
vty_get_bind_addr(), OSMO_VTY_PORT_MGW);
if (rc < 0)
return rc;
/* Set the reset callback function. This functions is called when the
* mgcp-command "RSIP" (Reset in Progress) is received */
cfg->reset_cb = mgcp_rsip_cb;
/* we need to bind a socket */
flags = OSMO_SOCK_F_BIND;
if (cfg->call_agent_addr)
flags |= OSMO_SOCK_F_CONNECT;
rc = osmo_sock_init2_ofd(&cfg->gw_fd.bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
cfg->source_addr, cfg->source_port,
cfg->call_agent_addr, cfg->call_agent_addr ? 2727 : 0, flags);
if (rc < 0) {
perror("Gateway failed to bind");
return -1;
}
cfg->gw_fd.bfd.cb = read_call_agent;
cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");
if (!cfg->gw_fd.bfd.data) {
fprintf(stderr, "Gateway memory error.\n");
return -1;
}
LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP, listen on %s:%u\n",
cfg->source_addr, cfg->source_port);
/* initialisation */
srand(time(NULL));
if (daemonize) {
rc = osmo_daemonize();
if (rc < 0) {
perror("Error during daemonize");
exit(1);
}
}
/* main loop */
while (1) {
osmo_select_main(0);
}
return 0;
}

View File

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

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" \
@@ -489,11 +491,11 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp)
static void test_values(void)
{
/* Check that NONE disables all output */
OSMO_ASSERT((MGCP_CONN_NONE & MGCP_CONN_RECV_SEND) == 0)
OSMO_ASSERT((MGCP_CONN_NONE & MGCP_CONN_RECV_SEND) == 0);
/* Check that LOOPBACK enables all output */
OSMO_ASSERT((MGCP_CONN_LOOPBACK & MGCP_CONN_RECV_SEND) ==
MGCP_CONN_RECV_SEND)
MGCP_CONN_RECV_SEND);
}
@@ -541,7 +543,8 @@ static void test_messages(void)
if (msg)
printf("%s failed '%s'\n", t->name, (char *) msg->data);
} else if (strcmp((char *) msg->data, t->exp_resp) != 0)
printf("%s failed '%s'\n", t->name, (char *) msg->data);
printf("%s failed.\nExpected:\n%s\nGot:\n%s\n",
t->name, t->exp_resp, (char *) msg->data);
msgb_free(msg);
if (dummy_packets)
@@ -1211,8 +1214,9 @@ const struct log_info log_info = {
int main(int argc, char **argv)
{
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();
@@ -1230,6 +1234,9 @@ int main(int argc, char **argv)
test_no_name();
test_osmux_cid();
OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
talloc_free(msgb_ctx);
printf("Done\n");
return EXIT_SUCCESS;
}

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");

41
tests/mgcp/Makefile.am Normal file
View File

@@ -0,0 +1,41 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_srcdir) \
$(NULL)
AM_CFLAGS = \
-Wall \
-ggdb3 \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
$(NULL)
EXTRA_DIST = \
mgcp_test.ok \
$(NULL)
noinst_PROGRAMS = \
mgcp_test \
$(NULL)
mgcp_test_SOURCES = \
mgcp_test.c \
$(NULL)
mgcp_test_LDADD = \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBRARY_DL) \
$(LIBOSMONETIF_LIBS) \
-lm \
$(NULL)

1495
tests/mgcp/mgcp_test.c Normal file

File diff suppressed because it is too large Load Diff

1107
tests/mgcp/mgcp_test.ok Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir)/include \
-I$(top_srcdir) \
$(NULL)
@@ -36,3 +37,6 @@ mgcp_client_test_LDADD = \
$(LIBRARY_DL) \
$(LIBOSMONETIF_LIBS) \
$(NULL)
update_exp:
$(builddir)/mgcp_client_test >$(srcdir)/mgcp_client_test.ok 2>$(srcdir)/mgcp_client_test.err

View File

@@ -18,13 +18,15 @@
* 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>
#include <osmocom/core/application.h>
#include <osmocom/legacy_mgcp/mgcp.h>
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_internal.h>
#include <errno.h>
void *ctx;
@@ -47,14 +49,14 @@ static struct msgb *mgcp_from_str(const char *head, const char *params)
l = strlen(head);
msg->l2h = msgb_put(msg, l);
data = (char*)msgb_l2(msg);
strncpy(data, head, l);
osmo_strlcpy(data, head, l);
data = (char*)msgb_put(msg, 1);
*data = '\n';
l = strlen(params);
data = (char*)msgb_put(msg, l);
strncpy(data, params, l);
osmo_strlcpy(data, params, l);
return msg;
}
@@ -67,14 +69,14 @@ static struct msgb *from_str(const char *str)
char *data;
msg->l2h = msgb_put(msg, l);
data = (char*)msgb_l2(msg);
strncpy(data, str, l);
osmo_strlcpy(data, str, l);
return msg;
}
static struct mgcp_client_conf conf;
struct mgcp_client *mgcp = NULL;
static void reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
int conn_id, const char *params)
{
static char compose[4096 - 128];
@@ -88,7 +90,7 @@ static void reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
printf("composed response:\n-----\n%s\n-----\n",
compose);
mgcp_client_rx(mgcp, from_str(compose));
return mgcp_client_rx(mgcp, from_str(compose));
}
void test_response_cb(struct mgcp_response *response, void *priv)
@@ -100,11 +102,13 @@ void test_response_cb(struct mgcp_response *response, void *priv)
" head.response_code = %d\n"
" head.trans_id = %u\n"
" head.comment = %s\n"
" audio_port = %u\n",
" audio_port = %u\n"
" audio_ip = %s\n",
response->head.response_code,
response->head.trans_id,
response->head.comment,
response->audio_port
response->audio_port,
response->audio_ip
);
}
@@ -150,6 +154,228 @@ void test_crcx(void)
"a=ptime:20\r\n");
}
void test_mgcp_msg(void)
{
struct msgb *msg;
char audio_ip_overflow[5000];
/* A message struct prefilled with some arbitary values */
struct mgcp_msg mgcp_msg = {
.audio_ip = "192.168.100.23",
.endpoint = "23@mgw",
.audio_port = 1234,
.call_id = 47,
.conn_id = "11",
.conn_mode = MGCP_CONN_RECV_SEND
};
if (mgcp)
talloc_free(mgcp);
mgcp = mgcp_client_init(ctx, &conf);
printf("\n");
printf("Generated CRCX message:\n");
mgcp_msg.verb = MGCP_VERB_CRCX;
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
printf("%s\n", (char *)msg->data);
printf("Generated MDCX message:\n");
mgcp_msg.verb = MGCP_VERB_MDCX;
mgcp_msg.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);
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
printf("%s\n", (char *)msg->data);
printf("Generated DLCX message:\n");
mgcp_msg.verb = MGCP_VERB_DLCX;
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID);
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
printf("%s\n", (char *)msg->data);
printf("Generated AUEP message:\n");
mgcp_msg.verb = MGCP_VERB_AUEP;
mgcp_msg.presence = (MGCP_MSG_PRESENCE_ENDPOINT);
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
printf("%s\n", msg->data);
printf("Generated RSIP message:\n");
mgcp_msg.verb = MGCP_VERB_RSIP;
mgcp_msg.presence = (MGCP_MSG_PRESENCE_ENDPOINT);
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
printf("%s\n", (char *)msg->data);
printf("Overfolow test:\n");
mgcp_msg.verb = MGCP_VERB_MDCX;
mgcp_msg.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);
memset(audio_ip_overflow, 'X', sizeof(audio_ip_overflow));
audio_ip_overflow[sizeof(audio_ip_overflow) - 1] = '\0';
mgcp_msg.audio_ip = audio_ip_overflow;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
OSMO_ASSERT(msg == NULL);
printf("\n");
msgb_free(msg);
}
void test_mgcp_client_cancel()
{
mgcp_trans_id_t trans_id;
struct msgb *msg;
struct mgcp_msg mgcp_msg = {
.verb = MGCP_VERB_CRCX,
.audio_ip = "192.168.100.23",
.endpoint = "23@mgw",
.audio_port = 1234,
.call_id = 47,
.conn_id = "11",
.conn_mode = MGCP_CONN_RECV_SEND,
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID
| MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE),
};
printf("\n%s():\n", __func__);
fprintf(stderr, "\n%s():\n", __func__);
if (mgcp)
talloc_free(mgcp);
mgcp = mgcp_client_init(ctx, &conf);
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
trans_id = mgcp_msg_trans_id(msg);
fprintf(stderr, "- composed msg with trans_id=%u\n", trans_id);
fprintf(stderr, "- not in queue yet, cannot cancel yet\n");
OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == -ENOENT);
fprintf(stderr, "- enqueue\n");
dummy_mgcp_send(msg);
fprintf(stderr, "- cancel succeeds\n");
OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == 0);
fprintf(stderr, "- late response gets discarded\n");
OSMO_ASSERT(reply_to(trans_id, 200, "OK", 1, "v=0\r\n") == -ENOENT);
fprintf(stderr, "- canceling again does nothing\n");
OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == -ENOENT);
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[] = {
};
@@ -163,11 +389,20 @@ 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);
log_set_print_category(osmo_stderr_target, 1);
log_set_category_filter(osmo_stderr_target, DLMGCP, 1, LOGL_DEBUG);
mgcp_client_conf_init(&conf);
test_crcx();
test_mgcp_msg();
test_mgcp_client_cancel();
test_sdp_section_start();
printf("Done\n");
fprintf(stderr, "Done\n");

View File

@@ -1 +1,65 @@
DLMGCP message buffer to small, can not generate MGCP message
test_mgcp_client_cancel():
- composed msg with trans_id=1
- not in queue yet, cannot cancel yet
DLMGCP Cannot cancel, no such transaction: 1
- enqueue
- cancel succeeds
DLMGCP Canceled transaction 1
- late response gets discarded
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

@@ -28,4 +28,78 @@ response cb received:
head.trans_id = 1
head.comment = OK
audio_port = 16002
audio_ip = 10.9.1.120
Generated CRCX message:
CRCX 1 23@mgw MGCP 1.0
C: 2f
I: 11
L: p:20, a:AMR, nt:IN
M: sendrecv
Generated MDCX message:
MDCX 2 23@mgw MGCP 1.0
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:
DLCX 3 23@mgw MGCP 1.0
C: 2f
I: 11
Generated AUEP message:
AUEP 4 23@mgw MGCP 1.0
Generated RSIP message:
RSIP 5 23@mgw MGCP 1.0
Overfolow test:
test_mgcp_client_cancel():
composed:
-----
CRCX 1 23@mgw MGCP 1.0
C: 2f
I: 11
L: p:20, a:AMR, nt:IN
M: sendrecv
-----
composed response:
-----
200 1 OK
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

View File

@@ -20,3 +20,9 @@ cat $abs_srcdir/mgcp_client/mgcp_client_test.ok > expout
cat $abs_srcdir/mgcp_client/mgcp_client_test.err > experr
AT_CHECK([$abs_top_builddir/tests/mgcp_client/mgcp_client_test], [], [expout], [experr])
AT_CLEANUP
AT_SETUP([mgcp])
AT_KEYWORDS([mgcp])
cat $abs_srcdir/mgcp/mgcp_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
AT_CLEANUP