diff --git a/modules/rtpengine/rtpengine.c b/modules/rtpengine/rtpengine.c index c3c07dfa4c4..bb66b7bd0a7 100644 --- a/modules/rtpengine/rtpengine.c +++ b/modules/rtpengine/rtpengine.c @@ -43,6 +43,9 @@ #include #include #include +#include +#include +#include #include "../../flags.h" #include "../../sr_module.h" @@ -172,6 +175,10 @@ static int mod_init(void); static int child_init(int); static void mod_destroy(void); +static int get_ip_type(char *str_addr); +static int get_ip_scope(char *str_addr); // useful for link-local ipv6 +static int bind_force_send_ip(int sock_idx); + /* Pseudo-Variables */ static int pv_get_rtpstat_f(struct sip_msg *, pv_param_t *, pv_value_t *); @@ -219,7 +226,7 @@ static pv_spec_t* write_sdp_pvar = NULL; char* force_send_ip_str=""; - +int force_send_ip_af = AF_UNSPEC; typedef struct rtpp_set_link { struct rtpp_set *rset; @@ -318,6 +325,140 @@ struct module_exports exports = { }; +static int get_ip_type(char *str_addr) +{ + struct addrinfo hint, *info = NULL; + int ret; + + memset(&hint, '\0', sizeof hint); + hint.ai_family = PF_UNSPEC; + hint.ai_flags = AI_NUMERICHOST; + + ret = getaddrinfo(str_addr, NULL, &hint, &info); + if (ret) { + /* Invalid ip addinfos */ + return -1; + } + + if(info->ai_family == AF_INET) { + LM_DBG("%s is an ipv4 addinfos\n", str_addr); + } else if (info->ai_family == AF_INET6) { + LM_DBG("%s is an ipv6 addinfos\n", str_addr); + } else { + LM_DBG("%s is an unknown addinfos format AF=%d\n",str_addr, info->ai_family); + return -1; + } + + ret = info->ai_family; + + freeaddrinfo(info); + + return ret; +} + + +static int get_ip_scope(char *str_addr) +{ + struct ifaddrs *ifaddr, *ifa; + struct sockaddr_in6 *in6; + char str_if_ip[NI_MAXHOST]; + int ret = -1; + + if (getifaddrs(&ifaddr) == -1) { + LM_ERR("getifaddrs() failed: %s\n", gai_strerror(ret)); + return -1; + } + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + in6 = (struct sockaddr_in6 *)ifa->ifa_addr; + + if (ifa->ifa_addr == NULL) + continue; + + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + ret = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), + str_if_ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (ret != 0) { + LM_ERR("getnameinfo() failed: %s\n", gai_strerror(ret)); + return -1; + } + + if (strstr(str_if_ip, str_addr)) { + LM_INFO("dev: %-8s address: <%s> scope %d\n", + ifa->ifa_name, str_if_ip, in6->sin6_scope_id); + ret = in6->sin6_scope_id; + break; + } + } + + freeifaddrs(ifaddr); + + return ret; +} + + +static int bind_force_send_ip(int sock_idx) +{ + struct sockaddr_in tmp, ip4addr; + struct sockaddr_in6 tmp6, ip6addr; + char str_addr[INET_ADDRSTRLEN]; + char str_addr6[INET6_ADDRSTRLEN]; + socklen_t sock_len = sizeof(struct sockaddr); + int ret, scope; + + switch (force_send_ip_af) { + case AF_INET: + memset(&ip4addr, 0, sizeof(ip4addr)); + ip4addr.sin_family = AF_INET; + ip4addr.sin_port = htons(0); + inet_pton(AF_INET, force_send_ip_str, &ip4addr.sin_addr); + + if (bind(rtpp_socks[sock_idx], (struct sockaddr*)&ip4addr, sizeof(ip4addr)) < 0) { + LM_ERR("can't bind socket to required ipv4 interface\n"); + return -1; + } + + memset(&tmp, 0, sizeof(tmp)); + getsockname(rtpp_socks[sock_idx], (struct sockaddr *) &tmp, &sock_len); + inet_ntop(AF_INET, &tmp.sin_addr, str_addr, INET_ADDRSTRLEN); + LM_INFO("Binding on %s:%d\n", str_addr, ntohs(tmp.sin_port)); + + break; + + case AF_INET6: + if ((scope = get_ip_scope(force_send_ip_str)) < 0) { + LM_ERR("can't get the ipv6 interface scope\n"); + return -1; + } + memset(&ip6addr, 0, sizeof(ip6addr)); + ip6addr.sin6_family = AF_INET6; + ip6addr.sin6_port = htons(0); + ip6addr.sin6_scope_id = scope; + inet_pton(AF_INET6, force_send_ip_str, &ip6addr.sin6_addr); + + if ((ret = bind(rtpp_socks[sock_idx], (struct sockaddr*)&ip6addr, sizeof(ip6addr))) < 0) { + LM_ERR("can't bind socket to required ipv6 interface\n"); + LM_ERR("ret=%d errno=%d\n", ret, errno); + return -1; + } + + memset(&tmp6, 0, sizeof(tmp6)); + getsockname(rtpp_socks[sock_idx], (struct sockaddr *) &tmp6, &sock_len); + inet_ntop(AF_INET6, &tmp6.sin6_addr, str_addr6, INET6_ADDRSTRLEN); + LM_INFO("Binding on ipv6 %s:%d\n", str_addr6, ntohs(tmp6.sin6_port)); + + break; + + default: + LM_INFO("force_send_ip_str not specified in .cfg file!\n"); + break; + } + + return 0; +} + static inline int str_eq(const str *p, const char *q) { int l = strlen(q); @@ -799,7 +940,6 @@ mod_init(void) pv_spec_t *avp_spec; unsigned short avp_flags; str s; - unsigned char ip_buff[sizeof(struct in6_addr)]; if(register_mi_mod(exports.name, mi_cmds)!=0) { @@ -899,12 +1039,14 @@ mod_init(void) memset(&tmb, 0, sizeof(struct tm_binds)); } - if ( 0 != strlen(force_send_ip_str)) { - if ( inet_pton(AF_INET, force_send_ip_str, ip_buff) <= 0) { - LM_ERR("Invalid IP address for force_send_interface <%s>\n", force_send_ip_str); - return -1; - } + /* Determine IP addr type (IPv4 or IPv6 allowed) */ + force_send_ip_af = get_ip_type(force_send_ip_str); + if (force_send_ip_af != AF_INET && force_send_ip_af != AF_INET6 && + strlen(force_send_ip_str) > 0) { + LM_ERR("%s is an unknown address\n", force_send_ip_str); + return -1; } + return 0; } @@ -917,8 +1059,6 @@ child_init(int rank) struct addrinfo hints, *res; struct rtpp_set *rtpp_list; struct rtpp_node *pnode; - struct sockaddr_in tmp, ip4addr; - socklen_t sock_len = sizeof(struct sockaddr); if(rtpp_set_list==NULL ) return 0; @@ -975,38 +1115,28 @@ child_init(int rank) rtpp_socks[pnode->idx] = socket((pnode->rn_umode == 6) ? AF_INET6 : AF_INET, SOCK_DGRAM, 0); - if ( rtpp_socks[pnode->idx] == -1) { + if (rtpp_socks[pnode->idx] == -1) { LM_ERR("can't create socket\n"); freeaddrinfo(res); return -1; } - if (strlen(force_send_ip_str)!=0) { - memset(&ip4addr, 0, sizeof(ip4addr)); - ip4addr.sin_family = AF_INET; - ip4addr.sin_port = htons(0); - inet_pton(AF_INET, force_send_ip_str, &ip4addr.sin_addr); - - if (bind(rtpp_socks[pnode->idx], (struct sockaddr*)&ip4addr, sizeof(ip4addr)) <0) { - LM_ERR("can't bind socket to required interface \n"); - close( rtpp_socks[pnode->idx] ); - rtpp_socks[pnode->idx] = -1; - freeaddrinfo(res); - return -1; - } - - memset(&tmp, 0, sizeof(tmp)); sock_len = sizeof(struct sockaddr); - getsockname(rtpp_socks[pnode->idx], (struct sockaddr *) &tmp, &sock_len); - LM_INFO("Binding on %s:%d\n", inet_ntoa(tmp.sin_addr), ntohs(tmp.sin_port)); + if (bind_force_send_ip(pnode->idx) == -1) { + LM_ERR("can't bind socket\n"); + close(rtpp_socks[pnode->idx]); + rtpp_socks[pnode->idx] = -1; + freeaddrinfo(res); + return -1; } - if (connect( rtpp_socks[pnode->idx], res->ai_addr, res->ai_addrlen) == -1) { + if (connect(rtpp_socks[pnode->idx], res->ai_addr, res->ai_addrlen) == -1) { LM_ERR("can't connect to a RTP proxy\n"); - close( rtpp_socks[pnode->idx] ); + close(rtpp_socks[pnode->idx]); rtpp_socks[pnode->idx] = -1; freeaddrinfo(res); return -1; } + freeaddrinfo(res); rptest: pnode->rn_disabled = rtpp_test(pnode, 0, 1);