diff --git a/README.md b/README.md index 68a6d2ad9..ba369999c 100644 --- a/README.md +++ b/README.md @@ -685,12 +685,28 @@ Interfaces configuration 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. -The format of the value is `[NAME/]IP[!IP]` with `IP` being either an IPv4 address or an IPv6 address. +The format of the value is `[NAME/]IP[!IP]` with `IP` being either an IPv4 address, an IPv6 address, +or the name of a system network interface (such as `eth0`). + +The possibility of configuring a network interface by name rather than by address should not be confused +with the logical interface name used internally by *rtpengine* (as described below). The `NAME` token +in the syntax above refers to the internal logical interface name, while the name of a system network +interface is used in place of the first `IP` token in the syntax above. For example, to configure a +logical network interface called `int` using all the addresses from the existing system network +interface `eth0`, you would use the syntax `int/eth0`. (Unless omitted, the second `IP` token used +for the advertised address must be an actual network address and cannot be an interface name.) To configure multiple interfaces using the command-line options, simply present multiple `-i` or `--interface=` options. When using the config file, only use a single `interface=` line, but specify multiple values separated by semicolons (e.g. `interface = internal/12.23.34.45;external/23.34.45.54`). +If an interface option is given using a system interface name in place of a network address, and if +multiple network address are found configured on that network interface, then *rtpengine* behaves as +if multiple `--interface` options had been specified. For example, if interface `eth0` exists with +both addresses `192.168.1.120` and `2001:db8:85a3::7334` configured on it, and if the option +`--interface=ext/eth0` is given, then *rtpengine* would behave as if both options +`--interface=ext/192.168.1.120` and `--interface=ext/2001:db8:85a3::7334` had been specified. + The second IP address after the exclamation point is optional and can be used if the address to advertise in outgoing SDP bodies should be different from the actual local address. This can be useful in certain cases, such as your SIP proxy being behind NAT. For example, `--interface=10.65.76.2!192.0.2.4` means diff --git a/daemon/main.c b/daemon/main.c index 2cc06e0f3..565df4cb4 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include "poller.h" #include "control_tcp.h" @@ -141,10 +143,11 @@ static void resources(void) { -static struct intf_config *if_addr_parse(char *s) { +static int if_addr_parse(GQueue *q, char *s, struct ifaddrs *ifas) { str name; char *c; - sockaddr_t addr, adv; + sockaddr_t *addr, adv; + GQueue addrs = G_QUEUE_INIT; struct intf_config *ifa; /* name */ @@ -163,33 +166,82 @@ static struct intf_config *if_addr_parse(char *s) { *c++ = 0; /* address */ - if (sockaddr_parse_any(&addr, s)) - return NULL; - if (is_addr_unspecified(&addr)) - return NULL; + addr = g_slice_alloc(sizeof(*addr)); + if (!sockaddr_parse_any(addr, s)) { + if (is_addr_unspecified(addr)) + return -1; + g_queue_push_tail(&addrs, addr); + } + else { + // could be an interface name? + ilog(LOG_DEBUG, "Could not parse '%s' as network address, checking to see if " + "it's an interface", s); + for (struct ifaddrs *ifa = ifas; ifa; ifa = ifa->ifa_next) { + if (strcmp(ifa->ifa_name, s)) + continue; + if (!(ifa->ifa_flags & IFF_UP)) + continue; + if (!ifa->ifa_addr) + continue; + if (ifa->ifa_addr->sa_family == AF_INET) { + struct sockaddr_in *sin = (void *) ifa->ifa_addr; + addr->family = __get_socket_family_enum(SF_IP4); + addr->u.ipv4 = sin->sin_addr; + } + else if (ifa->ifa_addr->sa_family == AF_INET6) { + struct sockaddr_in6 *sin = (void *) ifa->ifa_addr; + if (sin->sin6_scope_id) + continue; // link-local + addr->family = __get_socket_family_enum(SF_IP6); + addr->u.ipv6 = sin->sin6_addr; + } + else + continue; + + // got one + ilog(LOG_DEBUG, "Determined address %s for interface '%s'", + sockaddr_print_buf(addr), s); + g_queue_push_tail(&addrs, addr); + addr = g_slice_alloc(sizeof(*addr)); + } + + // free last unused entry + g_slice_free1(sizeof(*addr), addr); + } - adv = addr; + if (!addrs.length) // nothing found + return -1; + + ZERO(adv); if (c) { if (sockaddr_parse_any(&adv, c)) - return NULL; + return -1; if (is_addr_unspecified(&adv)) - return NULL; + return -1; } - ifa = g_slice_alloc0(sizeof(*ifa)); - ifa->name = name; - ifa->local_address.addr = addr; - ifa->local_address.type = socktype_udp; - ifa->advertised_address.addr = adv; - ifa->advertised_address.type = ifa->local_address.type; - ifa->port_min = rtpe_config.port_min; - ifa->port_max = rtpe_config.port_max; + while ((addr = g_queue_pop_head(&addrs))) { + ifa = g_slice_alloc0(sizeof(*ifa)); + ifa->name = name; + ifa->local_address.addr = *addr; + ifa->local_address.type = socktype_udp; + ifa->advertised_address.addr = adv; + if (is_addr_unspecified(&ifa->advertised_address.addr)) + ifa->advertised_address.addr = *addr; + ifa->advertised_address.type = ifa->local_address.type; + ifa->port_min = rtpe_config.port_min; + ifa->port_max = rtpe_config.port_max; + + // handle "base:suffix" separation for round-robin selection + ifa->name_rr_spec = ifa->name; + str_token(&ifa->name_base, &ifa->name_rr_spec, ':'); // sets name_rr_spec to null string if no ':' found - // handle "base:suffix" separation for round-robin selection - ifa->name_rr_spec = ifa->name; - str_token(&ifa->name_base, &ifa->name_rr_spec, ':'); // sets name_rr_spec to null string if no ':' found + g_queue_push_tail(q, ifa); + + g_slice_free1(sizeof(*addr), addr); + } - return ifa; + return 0; } @@ -233,7 +285,6 @@ static void options(int *argc, char ***argv) { unsigned long uint_keyspace_db; str str_keyspace_db; char **iter; - struct intf_config *ifa; char *listenps = NULL; char *listenudps = NULL; char *listenngs = NULL; @@ -325,12 +376,19 @@ static void options(int *argc, char ***argv) { if (!listenps && !listenudps && !listenngs) die("Missing option --listen-tcp, --listen-udp or --listen-ng"); + 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++) { - ifa = if_addr_parse(*iter); - if (!ifa) + int ret = if_addr_parse(&rtpe_config.interfaces, *iter, ifas); + if (ret) die("Invalid interface specification: %s", *iter); - g_queue_push_tail(&rtpe_config.interfaces, ifa); } + if (ifas) + freeifaddrs(ifas); + if (!rtpe_config.interfaces.length) die("Cannot start without any configured interfaces");