Merge pull request #142 from smititelu/master
authorDaniel-Constantin Mierla <miconda@gmail.com>
Tue, 5 May 2015 19:23:07 +0000 (21:23 +0200)
committerDaniel-Constantin Mierla <miconda@gmail.com>
Tue, 5 May 2015 19:23:07 +0000 (21:23 +0200)
Added implementation for force_send_interface to also support IPv6.

modules/rtpengine/doc/rtpengine_admin.xml
modules/rtpengine/rtpengine.c

index 2c8bab5..6be5386 100644 (file)
@@ -265,9 +265,13 @@ modparam("rtpengine", "setid_avp", "$avp(setid)")
                <para>
                        Forces all control messages between the &sip; proxy and
                        the &rtp; proxy to be sent from the specified local
-                       interface. Only IPv4 addresses are supported so far. If
+                       interface. Both IPv4 and IPv6 addresses are supported. If
                        not specified, the default interface selected by the
                        operating system will be used.
+                       Note: when rtpengine_sock is a IPv6 link-local address,
+                       one _must_ set this parameter in order to successfully connect to RTP engine.
+                       This is necessarely because OS needs additional scope_id hint to communicate
+                       over IPv6 link locals. The scope_id is resolved based on the given IPv6.
                </para>
                <para>
                        There is no default value.
@@ -277,6 +281,7 @@ modparam("rtpengine", "setid_avp", "$avp(setid)")
 <programlisting format="linespecific">
 ...
 modparam("rtpengine", "force_send_interface", "10.3.7.123")
+modparam("rtpengine", "force_send_interface", "2001:8d8:1ff:10c0:9a90:96ff:fea8:fd99")
 ...
 </programlisting>
                </example>
index c3c07df..bb66b7b 100644 (file)
@@ -43,6 +43,9 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <ifaddrs.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 
 #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);