diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 57d0b7ebd..4fc0a4f3a 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -1619,6 +1619,17 @@ static void call_ng_flags_freqs(const ng_parser_t *parser, parser_arg value, sdp } } +static void call_ng_flags_peer_address(const str *peer_ip, str *direction, const char *direction_text) { + const str *resolved = resolve_interface_from_peer_ip(peer_ip); + if (resolved) { + *direction = *resolved; + ilog(LOG_DEBUG, "%s peer " STR_FORMAT " resolved to interface " STR_FORMAT, + direction_text, STR_FMT(peer_ip), STR_FMT(resolved)); + } + else + ilog(LOG_WARN, "Failed to resolve %s peer address " STR_FORMAT, direction_text, STR_FMT(peer_ip)); +} + static void call_ng_received_from_string(sdp_ng_flags *flags, str *s) { flags->received_from_family = STR_NULL; flags->received_from_address = *s; @@ -1964,6 +1975,9 @@ void call_ng_main_flags(const ng_parser_t *parser, str *key, parser_arg value, h case CSH_LOOKUP("from-interface"): out->direction[0] = s; break; + case CSH_LOOKUP("inbound-peer"): + call_ng_flags_peer_address(&s, &out->direction[0], "Inbound"); + break; case CSH_LOOKUP("from-label"): case CSH_LOOKUP("label"): out->label = s; @@ -2047,6 +2061,9 @@ void call_ng_main_flags(const ng_parser_t *parser, str *key, parser_arg value, h case CSH_LOOKUP("interface"): out->interface = s; break; + case CSH_LOOKUP("peer"): + call_ng_flags_peer_address(&s, &out->interface, "Interface"); + break; case CSH_LOOKUP("instance"): out->instance = s; break; @@ -2312,6 +2329,9 @@ void call_ng_main_flags(const ng_parser_t *parser, str *key, parser_arg value, h case CSH_LOOKUP("to-interface"): out->direction[1] = s; break; + case CSH_LOOKUP("outbound-peer"): + call_ng_flags_peer_address(&s, &out->direction[1], "Outbound"); + break; case CSH_LOOKUP("to-label"): out->to_label = s; break; diff --git a/daemon/media_socket.c b/daemon/media_socket.c index 2284797c6..9c749762e 100644 --- a/daemon/media_socket.c +++ b/daemon/media_socket.c @@ -45,7 +45,6 @@ #define MAX_RECV_LOOP_STRIKES 5 #endif - TYPED_GQUEUE(logical_intf, struct logical_intf) struct intf_key { @@ -4115,3 +4114,51 @@ struct interface_stats_block *interface_sampled_rate_stats_get(struct interface_ ret->last_run = rtpe_now; return &ret->stats; } + +/* Resolve peer IP address to interface name via routing lookup. + * Returns pointer to interface name (valid for lifetime of interface config) or NULL on error. + */ +const str *resolve_interface_from_peer_ip(const str *peer_ip) { + if (!peer_ip || !peer_ip->s || !peer_ip->len) { + ilog(LOG_ERR, "Empty peer IP address"); + return NULL; + } + + sockaddr_t target; + if (!sockaddr_parse_any_str(&target, peer_ip)) { + ilog(LOG_ERR, "Invalid peer IP address: " STR_FORMAT ": %s", + STR_FMT(peer_ip), strerror(errno)); + return NULL; + } + + /* create probe socket and connect to target. + * port 9 (discard) is used as dummy - actual port doesn't affect routing */ + socket_t sock; + ZERO(sock); + endpoint_t ep = { .address = target, .port = 9 }; + if (!connect_socket(&sock, SOCK_DGRAM, &ep)) { + ilog(LOG_ERR, "Failed to create probe socket for peer IP: %s", + strerror(errno)); + return NULL; + } + + /* get local address assigned by kernel routing */ + if (!socket_getsockname(&sock)) { + close_socket(&sock); + ilog(LOG_ERR, "getsockname failed for peer IP: %s", strerror(errno)); + return NULL; + } + sockaddr_t local_addr = sock.local.address; + close_socket(&sock); + + /* find first matching interface (loop preserves config ordering) */ + for (__auto_type l = all_local_interfaces.head; l; l = l->next) { + struct local_intf *lif = l->data; + if (sockaddr_eq(&local_addr, &lif->spec->local_address.addr)) + return &lif->logical->name; + } + + ilog(LOG_ERR, "No configured interface found for peer-resolved address %s", + sockaddr_print_buf(&local_addr)); + return NULL; +} diff --git a/docs/ng_control_protocol.md b/docs/ng_control_protocol.md index 4da14ca4e..d49466816 100644 --- a/docs/ng_control_protocol.md +++ b/docs/ng_control_protocol.md @@ -381,6 +381,14 @@ Optionally included keys are: "received from" direction of this message. Identical to setting the first `direction=` value. +* `inbound-peer` + + Contains an IP address (IPv4 or IPv6) of the peer from which media will be + received. RTPengine performs a routing table lookup to determine which local + interface would be used to reach this peer, and uses that interface for the + inbound direction. Equivalent to setting `from-interface` with the resolved + interface name. + * `frequency` or `frequencies` Sets the tone frequency or frequencies for `DTMF-security=tone` in Hertz. @@ -457,6 +465,13 @@ Optionally included keys are: `interface` option is used instead of `direction` where only one interface is required (e.g. outside of an offer/answer scenario), for example in the `publish` or `subscribe request` commands. +* `peer` + + Contains an IP address (IPv4 or IPv6) of a peer. RTPengine performs a routing + table lookup to determine which local interface would be used to reach this peer, + and uses that interface for the call. Equivalent to setting `interface` with the + resolved interface name. + * `label` or `from-label` A custom free-form string which *rtpengine* remembers for this participating endpoint and reports @@ -797,6 +812,14 @@ Optionally included keys are: "going to" direction of this message. Identical to setting the second `direction=` value. +* `outbound-peer` + + Contains an IP address (IPv4 or IPv6) of the peer to which media will be + sent. RTPengine performs a routing table lookup to determine which local + interface would be used to reach this peer, and uses that interface for the + outbound direction. Equivalent to setting `to-interface` with the resolved + interface name. + * `to-label` Commands that allow selection of two call participants (e.g. `block diff --git a/include/media_socket.h b/include/media_socket.h index cadb4b823..862424a95 100644 --- a/include/media_socket.h +++ b/include/media_socket.h @@ -389,6 +389,7 @@ struct local_intf *get_interface_address(const struct logical_intf *lif, sockfam struct local_intf *get_any_interface_address(const struct logical_intf *lif, sockfamily_t *fam); void interfaces_exclude_port(endpoint_t *); bool is_local_endpoint(const struct intf_address *addr, unsigned int port); +const str *resolve_interface_from_peer_ip(const str *peer_ip); struct socket_port_link get_specific_port(unsigned int port, struct intf_spec *spec, const str *label);