From 672bf5305ce43901d591095b4d77199fafb45205 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 31 Jan 2025 11:51:14 -0400 Subject: [PATCH] MT#62053 add interfaces-config= option Change-Id: I32c67bd6834f51c835c08af67a7d84f3216486db --- daemon/main.c | 76 +++++++++++++++++++++++++++++++++----- docs/rtpengine.md | 91 ++++++++++++++++++++++++++++++++++++++++++++++ etc/rtpengine.conf | 11 +++++- lib/auxlib.h | 1 + t/test3.conf | 23 +++++++++++- 5 files changed, 190 insertions(+), 12 deletions(-) diff --git a/daemon/main.c b/daemon/main.c index 53567b674..80480d877 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -123,6 +123,11 @@ struct rtpengine_config rtpe_config = { .kernel_player_media = 128, }; +struct interface_config_callback_arg { + struct ifaddrs *ifas; + intf_config_q *icq; +}; + static void sighandler(gpointer x) { sigset_t ss; int ret; @@ -346,6 +351,33 @@ static bool if_add(intf_config_q *q, struct ifaddrs *ifas, const str *name, return true; } +static void add_if_from_config(const char *name, charp_ht ht, struct interface_config_callback_arg *icca) { + char *alias = t_hash_table_lookup(ht, "alias"); + if (alias) { + if_add_alias(&rtpe_config.interfaces, STR_PTR(name), alias); + return; + } + + char *address = t_hash_table_lookup(ht, "address"); + if (!address) + die("No 'address' given in interface config section '%s'", name); + char *adv_addr = t_hash_table_lookup(ht, "advertised"); + if (!adv_addr) + adv_addr = t_hash_table_lookup(ht, "advertised address"); + if (!adv_addr) + adv_addr = t_hash_table_lookup(ht, "advertised-address"); + if (!adv_addr) + adv_addr = t_hash_table_lookup(ht, "advertised_address"); + + const char *orig_name = name; + char *n2 = t_hash_table_lookup(ht, "name"); + if (n2) + name = n2; + + if (!if_add(icca->icq, icca->ifas, STR_PTR(name), address, adv_addr, 0, 0)) + die("Failed to parse interface information '%s' from config file", orig_name); +} + static bool if_addr_parse(intf_config_q *q, char *s, struct ifaddrs *ifas) { str name; char *c; @@ -552,6 +584,7 @@ static void options(int *argc, char ***argv, charp_ht templates) { #endif g_autoptr(char) redis_format = NULL; g_autoptr(char) templates_section = NULL; + g_autoptr(char) interfaces_config = NULL; GOptionEntry e[] = { { "table", 't', 0, G_OPTION_ARG_INT, &rtpe_config.kernel_table, "Kernel table to use", "INT" }, @@ -566,6 +599,7 @@ static void options(int *argc, char ***argv, charp_ht templates) { { "nftables-status",0, 0, G_OPTION_ARG_NONE, &nftables_status, "Check nftables rules, print result and exit", NULL }, #endif { "interface", 'i', 0, G_OPTION_ARG_STRING_ARRAY,&if_a, "Local interface for RTP", "[NAME/]IP[!IP]"}, + { "interfaces-config",0,0, G_OPTION_ARG_STRING, &interfaces_config, "Config section prefix for interfaces", "STR"}, { "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 }, { "subscribe-keyspace", 'k', 0, G_OPTION_ARG_STRING_ARRAY,&ks_a, "Subscription keyspace list", "INT INT ..."}, @@ -738,6 +772,17 @@ static void options(int *argc, char ***argv, charp_ht templates) { { NULL, } }; + struct ifaddrs *ifas; + if (getifaddrs(&ifas)) { + ifas = NULL; + ilog(LOG_WARN, "Failed to retrieve list of network interfaces: %s", strerror(errno)); + } + + // Store interfaces in separate queue first, instead of directly populating + // rtpe_config.interface. This is to ensure predictable ordering, and also because + // global port-min/max may not be set yet. + intf_config_q icq = TYPED_GQUEUE_INIT; + config_load_ext(argc, argv, e, " - next-generation media proxy", "/etc/rtpengine/rtpengine.conf", "rtpengine", &rtpe_config.common, (struct rtpenging_config_callback []) { @@ -749,6 +794,17 @@ static void options(int *argc, char ***argv, charp_ht templates) { .callback = add_c_str_to_ht, }, }, + { + .type = RCC_FILE_GROUPS, + .arg.icca = &(struct interface_config_callback_arg) { + .ifas = ifas, + .icq = &icq, + }, + .file_groups = { + .prefix = &interfaces_config, + .callback = add_if_from_config, + }, + }, { 0 }, }); @@ -816,18 +872,20 @@ static void options(int *argc, char ***argv, charp_ht templates) { } #endif - if (!if_a) - die("Missing option --interface"); - - struct ifaddrs *ifas; - if (getifaddrs(&ifas)) { - ifas = NULL; - ilog(LOG_WARN, "Failed to retrieve list of network interfaces: %s", strerror(errno)); - } - for (iter = if_a; *iter; iter++) { + for (iter = if_a; iter && *iter; iter++) { if (!if_addr_parse(&rtpe_config.interfaces, *iter, ifas)) die("Invalid interface specification: '%s'", *iter); } + while (icq.length) { + __auto_type ic = t_queue_pop_head(&icq); + // fill in port ranges from global if needed + if (!ic->port_min) + ic->port_min = rtpe_config.port_min; + if (!ic->port_max) + ic->port_max = rtpe_config.port_max; + t_queue_push_tail(&rtpe_config.interfaces, ic); + } + if (ifas) freeifaddrs(ifas); diff --git a/docs/rtpengine.md b/docs/rtpengine.md index 092492e86..4f596e991 100644 --- a/docs/rtpengine.md +++ b/docs/rtpengine.md @@ -1485,6 +1485,13 @@ call to inject-DTMF won't be sent to __\-\-dtmf-log-dest=__ or __\-\-listen-tcp- ## INTERFACES +This section describes the legacy syntax for configuring interfaces, which can +equally be used from the configuration file as from the command line. The new +syntax to configure interfaces, which can only be used from the config file, is +described at the end of this section, which now is the preferred method. + +### Legacy Syntax + The command-line options __-i__ or __\-\-interface__, or equivalently the __interface__ config file option, specify local network interfaces for RTP. At least one must be given, but multiple can be specified. @@ -1653,6 +1660,90 @@ used by the __rtpproxy__ module, the interfaces must be named __internal__ and __external__ corresponding to the __i__ and __e__ flags if you wish to use network bridging in this mode. +### New Configuration-File Based Syntax + +When a configuration file is in use, instead of having to list multiple +interfaces in one long line in the config file, it's now possible to use config +file sections (or "groups") for a more convenient way to configure and manage +multiple interfaces. To use it, instead of setting the __interface__ option to +any value, the option __interfaces-config__ must be set to a non-empty string. +This string is a prefix, which *rtpengine* uses to look in the config file for +config sections (groups) that contain interface configurations. + +For example, if the setting `interfaces-config = interface` is present in the +config file, *rtpengine* would consider config file sections starting with the +string `interface` and followed by a dash to be interface configurations. The +remainder of the name of the config section (the part after the dash) becomes +the default name of the interface. The name for the interface can then be +overridden within the config section (see below). + +_NOTE: The names of config sections must be unique within the config file, and +each interface config can list only a single address. To add multiple addresses +to the same logical interface, the name of the logical interfaces must +necessarily be explicitly set in each config section, instead of relying on the +name extracted from the name of the config section._ + +Each config section must at least define an interface address by setting the +__address__ option, or define an alias interface (as described above) by +setting the __alias__ option. The option __name__ can be set to override the +default name extracted from the name of the config section. + +Non-alias interfaces support the additional option __advertised__ to set the +advertised address. Round-robin interface usage is supported in the same way as +described above, i.e. by using a colon and a suffix as part of the interface +name. + +Interface sections are processed in order, and as such the first one listed +becomes the default interface. If both legacy syntax and new configuration-file +based syntax are in use, then interfaces from the legacy syntax are processed +first. + +A complete example: + + [rtpengine] + interfaces-config = interface + + # Create an interface "default" and pick up any non-local addresses + # that are bound to the system at startup. + [interface-default] + address = any + + # Create an interface "external" and pick up any addresses bound + # to the physical interface "enp63s0". + [interface-external] + address = enp63s0 + + # Create an interface "internal" with an explicitly set address, + # and also set the advertised address. + [interface-internal] + address = 192.168.67.43 + advertised = 203.0.113.7 + + # Create an interface "ICE" and add all addresses bound to the + # physical interface "enp63s0". Use a mismatched but unique name for + # the interface section so more addresses can be added to this + # interface. + [interface-ICE-1] + name = ICE + address = enp63s0 + + # Add addresses from interface "enp35s0" to the interface "ICE". + [interface-ICE-2] + name = ICE + address = enp35s0 + + # Create an alias interface "virt" pointing to "external". + [interface-virt] + alias = external + + # Create two interfaces that will be used in a round-robin way + # by referring to the interface "rr". + [interface-rr:0] + address = enp5p0 + + [interface-rr:1] + address = enp15p0 + ## SIGNALLING TEMPLATES Since much of the behaviour of *rtpengine* is controlled by flags and diff --git a/etc/rtpengine.conf b/etc/rtpengine.conf index 210fac925..6f11364ec 100644 --- a/etc/rtpengine.conf +++ b/etc/rtpengine.conf @@ -5,6 +5,10 @@ table = 0 ### for userspace forwarding only: # table = -1 +interfaces-config = interface + +### legacy interface config syntax: +# interface = any ### a single interface: # interface = 123.234.345.456 ### separate multiple interfaces with semicolons: @@ -12,8 +16,6 @@ table = 0 ### for different advertised address: # interface = 12.23.34.45!23.34.45.56 -interface = any - # name of config section in this file to contain signalling templates templates = templates @@ -177,6 +179,11 @@ recording-method = proc [templates] WebRTC = transport-protocol=UDP/TLS/RTP/SAVPF ICE=force trickle-ICE rtcp-mux=[offer require] no-rtcp-attribute SDES=off generate-mid +# one single default interface +[interface-default] +address = any +# name = default + [rtpengine-testing] table = -1 interface = 10.15.20.121 diff --git a/lib/auxlib.h b/lib/auxlib.h index 3d8f40713..369922ff5 100644 --- a/lib/auxlib.h +++ b/lib/auxlib.h @@ -56,6 +56,7 @@ TYPED_GHASHTABLE(charp_ht, char, char, c_str_hash, c_str_equal, g_free, g_free) union rtpenging_config_callback_arg { charp_ht ht; + struct interface_config_callback_arg *icca; } __attribute__((__transparent_union__)); struct rtpenging_config_callback { diff --git a/t/test3.conf b/t/test3.conf index 93cf9cdcc..e44ad6fda 100644 --- a/t/test3.conf +++ b/t/test3.conf @@ -1,6 +1,6 @@ [rtpengine] table = -1 -interface = 203.0.113.1 ; 2001:db8:4321::1 ; 203.0.113.2 ; 2001:db8:4321::2 ; foobar/203.0.113.3 ; quux/203.0.113.4 +interfaces-config = interface listen-ng = 2223 foreground = true log-level = 7 @@ -9,3 +9,24 @@ templates = templates [templates] offer = transport-protocol=UDP/TLS/RTP/SAVPF ICE=force trickle-ICE rtcp-mux=[offer require] no-rtcp-attribute SDES=off generate-mid + +[interface-default] +address = 203.0.113.1 + +[interface-default-2] +name = default +address = 2001:db8:4321::1 + +[interface-default-3] +name = default +address = 203.0.113.2 + +[interface-default-4] +name = default +address = 2001:db8:4321::2 + +[interface-foobar] +address = 203.0.113.3 + +[interface-quux] +address = 203.0.113.4