MT#61352 support signalling templates

Change-Id: If5624e3294bff7f677bdb08f98ea920c3b5f929a
pull/1884/head
Richard Fuchs 1 year ago
parent d48e028675
commit b8a915246f

@ -36,6 +36,7 @@ static pcre2_code *streams_re;
bool trust_address_def; bool trust_address_def;
bool dtls_passive_def; bool dtls_passive_def;
str_case_value_ht rtpe_signalling_templates;
enum basic_errors { enum basic_errors {
NG_ERROR_NO_SDP_BODY = 1, NG_ERROR_NO_SDP_BODY = 1,
@ -1453,6 +1454,7 @@ static void call_ng_received_from_iter(str *key, unsigned int i, helper_arg arg)
break; break;
} }
} }
void call_ng_main_flags(const ng_parser_t *parser, str *key, parser_arg value, helper_arg arg) { void call_ng_main_flags(const ng_parser_t *parser, str *key, parser_arg value, helper_arg arg) {
str s = STR_NULL; str s = STR_NULL;
sdp_ng_flags *out = arg.flags; sdp_ng_flags *out = arg.flags;
@ -1977,6 +1979,17 @@ void call_ng_main_flags(const ng_parser_t *parser, str *key, parser_arg value, h
call_ng_flags_str_list(parser, value, ng_t38_option, out); call_ng_flags_str_list(parser, value, ng_t38_option, out);
break; break;
#endif #endif
case CSH_LOOKUP("template"):;
str *tplate = t_hash_table_lookup(rtpe_signalling_templates, &s);
if (!tplate) {
ilog(LOG_WARN, "Templates for signalling flags '" STR_FORMAT "' not found",
STR_FMT(&s));
break;
}
// naive approach: just parse them out every time
// TODO: improve this by pre-parsing the flags at startup
parse_rtpp_flags(tplate, out);
break;
case CSH_LOOKUP("to-interface"): case CSH_LOOKUP("to-interface"):
out->direction[1] = s; out->direction[1] = s;
break; break;
@ -3999,9 +4012,25 @@ void call_interfaces_free(void) {
pcre2_code_free(streams_re); pcre2_code_free(streams_re);
streams_re= NULL; streams_re= NULL;
} }
t_hash_table_destroy(rtpe_signalling_templates);
}
static void parse_templates(GHashTable *templates) {
if (!templates)
return;
GHashTableIter iter;
g_hash_table_iter_init(&iter, templates);
void *keyp, *valuep;
while (g_hash_table_iter_next(&iter, &keyp, &valuep)) {
char *key = keyp;
char *value = valuep;
t_hash_table_insert(rtpe_signalling_templates, str_dup(STR_PTR(key)), str_dup(STR_PTR(value)));
}
} }
int call_interfaces_init(void) { int call_interfaces_init(GHashTable *templates) {
int errcode; int errcode;
PCRE2_SIZE erroff; PCRE2_SIZE erroff;
@ -4015,5 +4044,8 @@ int call_interfaces_init(void) {
if (!streams_re) if (!streams_re)
return -1; return -1;
rtpe_signalling_templates = str_case_value_ht_new();
parse_templates(templates);
return 0; return 0;
} }

@ -485,7 +485,7 @@ static void release_listeners(GQueue *q) {
} }
static void options(int *argc, char ***argv) { static void options(int *argc, char ***argv, GHashTable *templates) {
g_autoptr(char_p) if_a = NULL; g_autoptr(char_p) if_a = NULL;
g_autoptr(char_p) ks_a = NULL; g_autoptr(char_p) ks_a = NULL;
unsigned long uint_keyspace_db; unsigned long uint_keyspace_db;
@ -533,6 +533,7 @@ static void options(int *argc, char ***argv) {
g_autoptr(char) nftables_family = NULL; g_autoptr(char) nftables_family = NULL;
#endif #endif
g_autoptr(char) redis_format = NULL; g_autoptr(char) redis_format = NULL;
g_autoptr(char) templates_section = NULL;
GOptionEntry e[] = { GOptionEntry e[] = {
{ "table", 't', 0, G_OPTION_ARG_INT, &rtpe_config.kernel_table, "Kernel table to use", "INT" }, { "table", 't', 0, G_OPTION_ARG_INT, &rtpe_config.kernel_table, "Kernel table to use", "INT" },
@ -547,6 +548,7 @@ static void options(int *argc, char ***argv) {
{ "nftables-status",0, 0, G_OPTION_ARG_NONE, &nftables_status, "Check nftables rules, print result and exit", NULL }, { "nftables-status",0, 0, G_OPTION_ARG_NONE, &nftables_status, "Check nftables rules, print result and exit", NULL },
#endif #endif
{ "interface", 'i', 0, G_OPTION_ARG_STRING_ARRAY,&if_a, "Local interface for RTP", "[NAME/]IP[!IP]"}, { "interface", 'i', 0, G_OPTION_ARG_STRING_ARRAY,&if_a, "Local interface for RTP", "[NAME/]IP[!IP]"},
{ "templates", 0, 0, G_OPTION_ARG_STRING, &templates_section, "Config section to read signalling templates from ", "STR"},
{ "save-interface-ports",'S', 0, G_OPTION_ARG_NONE, &rtpe_config.save_interface_ports, "Bind ports only on first available interface of desired family", NULL }, { "save-interface-ports",'S', 0, G_OPTION_ARG_NONE, &rtpe_config.save_interface_ports, "Bind ports only on first available interface of desired family", NULL },
{ "subscribe-keyspace", 'k', 0, G_OPTION_ARG_STRING_ARRAY,&ks_a, "Subscription keyspace list", "INT INT ..."}, { "subscribe-keyspace", 'k', 0, G_OPTION_ARG_STRING_ARRAY,&ks_a, "Subscription keyspace list", "INT INT ..."},
{ "listen-ng", 'n', 0, G_OPTION_ARG_STRING_ARRAY, &listenngs, "UDP ports to listen on, NG protocol","[IP46|HOSTNAME:]PORT ..." }, { "listen-ng", 'n', 0, G_OPTION_ARG_STRING_ARRAY, &listenngs, "UDP ports to listen on, NG protocol","[IP46|HOSTNAME:]PORT ..." },
@ -703,8 +705,9 @@ static void options(int *argc, char ***argv) {
{ NULL, } { NULL, }
}; };
config_load(argc, argv, e, " - next-generation media proxy", config_load_ext(argc, argv, e, " - next-generation media proxy",
"/etc/rtpengine/rtpengine.conf", "rtpengine", &rtpe_config.common); "/etc/rtpengine/rtpengine.conf", "rtpengine", &rtpe_config.common,
&templates_section, templates);
// default values, if not configured // default values, if not configured
if (rtpe_config.rec_method == NULL) if (rtpe_config.rec_method == NULL)
@ -1120,6 +1123,9 @@ static void options(int *argc, char ***argv) {
if (rtpe_config.cpu_affinity <= 0) if (rtpe_config.cpu_affinity <= 0)
die("Number of CPU cores is unknown, cannot auto-set socket CPU affinity"); die("Number of CPU cores is unknown, cannot auto-set socket CPU affinity");
} }
// everything OK, do post-processing
} }
static void fill_initial_rtpe_cfg(struct rtpengine_config* ini_rtpe_cfg) { static void fill_initial_rtpe_cfg(struct rtpengine_config* ini_rtpe_cfg) {
@ -1276,7 +1282,7 @@ fallback:
} }
static void init_everything(void) { static void init_everything(GHashTable *templates) {
bufferpool_init(); bufferpool_init();
gettimeofday(&rtpe_now, NULL); gettimeofday(&rtpe_now, NULL);
log_init(rtpe_common_config_ptr->log_name); log_init(rtpe_common_config_ptr->log_name);
@ -1299,7 +1305,7 @@ static void init_everything(void) {
interfaces_init(&rtpe_config.interfaces); interfaces_init(&rtpe_config.interfaces);
iptables_init(); iptables_init();
control_ng_init(); control_ng_init();
if (call_interfaces_init()) if (call_interfaces_init(templates))
abort(); abort();
statistics_init(); statistics_init();
#ifdef WITH_TRANSCODING #ifdef WITH_TRANSCODING
@ -1506,8 +1512,11 @@ static void uring_poller_loop(void *ptr) {
int main(int argc, char **argv) { int main(int argc, char **argv) {
early_init(); early_init();
options(&argc, &argv); {
init_everything(); g_autoptr(GHashTable) templates = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
options(&argc, &argv, templates);
init_everything(templates);
}
create_everything(); create_everything();
fill_initial_rtpe_cfg(&initial_rtpe_config); fill_initial_rtpe_cfg(&initial_rtpe_config);

@ -582,6 +582,12 @@ Optionally included keys are:
Contains an integer corresponding to the SIP response code (e.g. 180 or Contains an integer corresponding to the SIP response code (e.g. 180 or
200) if this signalling message was triggered by a SIP response. 200) if this signalling message was triggered by a SIP response.
* `template`
Contains the name of a signalling template to be used for this particular
control message. See documentation for *SIGNALLING TEMPLATES* in the man
page.
* `via-branch` * `via-branch`
The SIP `Via` branch as string. Used to additionally refine the matching logic between media streams The SIP `Via` branch as string. Used to additionally refine the matching logic between media streams

@ -151,6 +151,18 @@ at the command line. See the __\-\-config-file__ option below for details.
In this case, startup of the daemon will fail with an error if this option In this case, startup of the daemon will fail with an error if this option
is given. is given.
- __\-\-templates=__*STR*
Name of the config file section to contain signalling templates. Requires a
configuration file to be in use (i.e. not __\-\-config-file=none__).
Default value is unset (i.e. no templates supported).
If set, then each entry within the given config section corresponds to a
named signalling template, which can then be used by referencing it via the
__template=...__ key in a signalling message to *rtpengine*.
See section *SIGNALLING TEMPLATES* below.
- __-S__, __\-\-save-interface-ports__ - __-S__, __\-\-save-interface-ports__
Will bind ports only on the first available local interface, of desired Will bind ports only on the first available local interface, of desired
@ -1522,6 +1534,48 @@ used by the __rtpproxy__ module, the interfaces must be named __internal__
and __external__ corresponding to the __i__ and __e__ flags if you wish to and __external__ corresponding to the __i__ and __e__ flags if you wish to
use network bridging in this mode. use network bridging in this mode.
## SIGNALLING TEMPLATES
Since much of the behaviour of *rtpengine* is controlled by flags and
keys/values given to it during runtime as part of the signalling control
protocol that is used for communication between the controlling agent (e.g. a
SIP proxy) and the *rtpengine* process, there often is a need to repeatedly
give the same set of default flags and values to *rtpengine* for each message
sent to it. This can lead to controlling scripts that are hard to maintain or
hard to read. To alleviate this problem, *rtpengine* supports signalling
templates that can be configured in its main configuration file and can then be
referred to by short names.
To use this feature, a configuration file must be in use (by default
`/etc/rtpengine/rtpengine.conf`) and the configuration key __templates=...__
must be set to a non-empty string. The value gives the name of the section in
the configuration file to contain signalling templates. For example, if the
value is set to __templates=templates__, then the section __[templates]__ will
be used to read signalling templates.
Each key/value in this file section then corresponds to one signalling
template, and can be referred to via __template=...__ in any control message.
For example, in order to make an offer to a WebRTC-compliant client, a Kamailio
or OpenSIPS script may have used:
rtpengine_offer("transport-protocol=UDP/TLS/RTP/SAVPF ICE=force trickle-ICE rtcp-mux=[offer require] no-rtcp-attribute SDES=off generate-mid");
This entire string of flags can now be converted into a signalling template in
the config file as such:
[rtpengine]
...
templates = templates
...
[templates]
WebRTC = transport-protocol=UDP/TLS/RTP/SAVPF ICE=force trickle-ICE rtcp-mux=[offer require] no-rtcp-attribute SDES=off generate-mid
The __offer__ command in Kamailio or OpenSIPS can then simply be turned into:
rtpengine_offer("template=WebRTC");
## EXIT STATUS ## EXIT STATUS
- __0__ - __0__

@ -14,6 +14,8 @@ table = 0
interface = any interface = any
# name of config section in this file to contain signalling templates
templates = templates
listen-ng = localhost:2223 listen-ng = localhost:2223
@ -149,6 +151,10 @@ recording-method = proc
# socket-cpu-affinity = -1 # socket-cpu-affinity = -1
# rtcp-interval = 5000 # rtcp-interval = 5000
# signalling templates (see key `templates` above)
[templates]
WebRTC = transport-protocol=UDP/TLS/RTP/SAVPF ICE=force trickle-ICE rtcp-mux=[offer require] no-rtcp-attribute SDES=off generate-mid
[rtpengine-testing] [rtpengine-testing]
table = -1 table = -1
interface = 10.15.20.121 interface = 10.15.20.121

@ -263,6 +263,7 @@ RTPE_NG_FLAGS_STR_CASE_HT_PARAMS
extern bool trust_address_def; extern bool trust_address_def;
extern bool dtls_passive_def; extern bool dtls_passive_def;
extern str_case_value_ht rtpe_signalling_templates;
str *call_request_tcp(char **); str *call_request_tcp(char **);
str *call_lookup_tcp(char **); str *call_lookup_tcp(char **);
@ -312,7 +313,7 @@ void call_unlock_release(call_t *c);
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(sdp_ng_flags, call_ng_free_flags) G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(sdp_ng_flags, call_ng_free_flags)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(call_t, call_unlock_release) G_DEFINE_AUTOPTR_CLEANUP_FUNC(call_t, call_unlock_release)
int call_interfaces_init(void); int call_interfaces_init(GHashTable *);
void call_interfaces_free(void); void call_interfaces_free(void);
void call_interfaces_timer(void); void call_interfaces_timer(void);

@ -155,13 +155,31 @@ void config_load_free(struct rtpengine_common_config *cconfig) {
g_free(cconfig->pidfile); g_free(cconfig->pidfile);
} }
static void load_templates(GKeyFile *kf, const char *template_section, GHashTable *templates) {
size_t length;
g_autoptr(GError) err = NULL;
g_autoptr(char_p) keys = g_key_file_get_keys(kf, template_section, &length, &err);
if (err)
die("Failed to load templates from given config file section '%s': %s", template_section, err->message);
if (!keys)
return; // empty config section
for (char **key = keys; *key; key++) {
char *val = g_key_file_get_string(kf, template_section, *key, &err);
if (err)
die("Failed to read template value '%s' from config file: %s", *key, err->message);
g_hash_table_insert(templates, g_strdup(*key), val); // hash table takes ownership of both
}
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOptionEntry, free) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOptionEntry, free)
typedef char *char_p_shallow; typedef char *char_p_shallow;
G_DEFINE_AUTOPTR_CLEANUP_FUNC(char_p_shallow, g_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC(char_p_shallow, g_free)
void config_load(int *argc, char ***argv, GOptionEntry *app_entries, const char *description, void config_load_ext(int *argc, char ***argv, GOptionEntry *app_entries, const char *description,
char *default_config, char *default_section, char *default_config, char *default_section,
struct rtpengine_common_config *cconfig) struct rtpengine_common_config *cconfig,
char * const *template_section, GHashTable *templates)
{ {
g_autoptr(GOptionContext) c = NULL; g_autoptr(GOptionContext) c = NULL;
g_autoptr(GError) er = NULL; g_autoptr(GError) er = NULL;
@ -360,6 +378,9 @@ void config_load(int *argc, char ***argv, GOptionEntry *app_entries, const char
} }
} }
if (template_section && *template_section && templates)
load_templates(kf, *template_section, templates);
out: out:
// default common values, if not configured // default common values, if not configured
if (rtpe_common_config_ptr->log_name == NULL) if (rtpe_common_config_ptr->log_name == NULL)

@ -66,9 +66,16 @@ void resources(void);
void wpidfile(void); void wpidfile(void);
void service_notify(const char *message); void service_notify(const char *message);
void config_load_free(struct rtpengine_common_config *); void config_load_free(struct rtpengine_common_config *);
void config_load(int *argc, char ***argv, GOptionEntry *entries, const char *description, void config_load_ext(int *argc, char ***argv, GOptionEntry *entries, const char *description,
char *default_config, char *default_section, char *default_config, char *default_section,
struct rtpengine_common_config *); struct rtpengine_common_config *,
char * const *template_section, GHashTable *templates);
INLINE void config_load(int *argc, char ***argv, GOptionEntry *entries, const char *description,
char *default_config, char *default_section,
struct rtpengine_common_config *cc)
{
config_load_ext(argc, argv, entries, description, default_config, default_section, cc, NULL, NULL);
}
char *get_thread_buf(void); char *get_thread_buf(void);
int thread_create(void *(*func)(void *), void *arg, bool joinable, pthread_t *handle, const char *name); int thread_create(void *(*func)(void *), void *arg, bool joinable, pthread_t *handle, const char *name);

@ -11,7 +11,7 @@ use NGCP::Rtpclient::ICE;
use POSIX; use POSIX;
autotest_start(qw(--config-file=test.conf)) or die; autotest_start(qw(--config-file=test1.conf)) or die;
@ -69,6 +69,42 @@ a=rtcp:PORT
SDP SDP
new_call;
offer('template', { template => 'WebRTC' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 172.17.0.2
s=tester
c=IN IP4 198.51.100.19
t=0 0
m=audio 6000 RTP/AVP 0 8
----------------------------------
v=0
o=- 1545997027 1 IN IP4 172.17.0.2
s=tester
t=0 0
m=audio PORT UDP/TLS/RTP/SAVPF 0 8
c=IN IP4 203.0.113.1
a=mid:1
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=sendrecv
a=rtcp-mux
a=setup:actpass
a=fingerprint:sha-256 FINGERPRINT256
a=tls-id:TLS_ID
a=ice-ufrag:ICEUFRAG
a=ice-pwd:ICEPWD
a=ice-options:trickle
a=candidate:ICEBASE 1 UDP 2130706431 203.0.113.1 PORT typ host
a=candidate:ICEBASE 1 UDP 2130706175 203.0.113.2 PORT typ host
a=candidate:ICEBASE 1 UDP 2130705919 2001:db8:4321::1 PORT typ host
a=candidate:ICEBASE 1 UDP 2130705663 2001:db8:4321::2 PORT typ host
a=end-of-candidates
SDP
#done_testing;NGCP::Rtpengine::AutoTest::terminate('f00');exit; #done_testing;NGCP::Rtpengine::AutoTest::terminate('f00');exit;
done_testing(); done_testing();

@ -74,7 +74,7 @@ int main(void) {
rtpe_ssl_init(); rtpe_ssl_init();
call_init(); call_init();
statistics_init(); statistics_init();
call_interfaces_init(); call_interfaces_init(NULL);
ice_init(); ice_init();
control_ng_init(); control_ng_init();
dtls_init(); dtls_init();

@ -5,3 +5,7 @@ listen-ng = 2223
foreground = true foreground = true
log-level = 7 log-level = 7
log-stderr = true log-stderr = true
templates = templates
[templates]
WebRTC = transport-protocol=UDP/TLS/RTP/SAVPF ICE=force trickle-ICE rtcp-mux=[offer require] no-rtcp-attribute SDES=off generate-mid
Loading…
Cancel
Save