pbx_dundi: added IPv4/IPv6 dual bind support to DUNDi
authorKirsty Tyerman <kirsty.tyerman@boeing.com>
Tue, 8 Jan 2019 06:14:07 +0000 (16:14 +1000)
committerGeorge Joseph <gjoseph@digium.com>
Wed, 5 Jun 2019 17:56:36 +0000 (11:56 -0600)
ASTERISK-28234
Reported-by: Kirsty Tyerman

Change-Id: I5d6e6b52dbe51415046bb3953fd16f5b421bc2e1

configs/samples/dundi.conf.sample
doc/CHANGES-staging/pbx_dundi_ipv6.txt [new file with mode: 0644]
pbx/pbx_dundi.c

index b97d3b2..3c67164 100644 (file)
 ;phone=+12565551212
 ;
 ;
-; Specify bind address and port number.  Default is
-; 4520
+; Specify bind address. IPv6 addresses are accepted. Default is 0.0.0.0
+; You can specify 'bindaddr2' to bind to another address however
+; 'bindaddr and 'bindaddr2' need to be different IP protocols.
+; Specify port number. Default is 4520.
 ;
 ;bindaddr=0.0.0.0
 ;port=4520
diff --git a/doc/CHANGES-staging/pbx_dundi_ipv6.txt b/doc/CHANGES-staging/pbx_dundi_ipv6.txt
new file mode 100644 (file)
index 0000000..c15ae44
--- /dev/null
@@ -0,0 +1,3 @@
+Subject: pbx_dundi
+
+The DUNDi PBX module now supports IPv4/IPv6 dual binding.
index ec150f3..6cc7711 100644 (file)
@@ -183,7 +183,8 @@ enum {
 
 static struct io_context *io;
 static struct ast_sched_context *sched;
-static int netsocket = -1;
+static int netsocket = -1; /* Socket for bindaddr if only one bindaddr. Otherwise the IPv4 socket when bindaddr2 given. */
+static int netsocket2 = -1; /* IPv6 socket when bindaddr2 given. */
 static pthread_t netthreadid = AST_PTHREADT_NULL;
 static pthread_t precachethreadid = AST_PTHREADT_NULL;
 static pthread_t clearcachethreadid = AST_PTHREADT_NULL;
@@ -2079,14 +2080,14 @@ static int handle_frame(struct dundi_hdr *h, struct ast_sockaddr *sin, int datal
        return 0;
 }
 
-static int socket_read(int *id, int fd, short events, void *cbdata)
+static int socket_read(int *id, int fd, short events, void *sock)
 {
        struct ast_sockaddr sin;
        int res;
        struct dundi_hdr *h;
        char buf[MAX_PACKET_SIZE];
 
-       res = ast_recvfrom(netsocket, buf, sizeof(buf), 0, &sin);
+       res = ast_recvfrom(*((int *)sock), buf, sizeof(buf), 0, &sin);
        if (res < 0) {
                if (errno != ECONNREFUSED)
                        ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
@@ -2196,7 +2197,11 @@ static void *network_thread(void *ignore)
           from the network, and queue them for delivery to the channels */
        int res;
        /* Establish I/O callback for socket read */
-       int *socket_read_id = ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
+       int *socket_read_id = ast_io_add(io, netsocket, socket_read, AST_IO_IN, &netsocket);
+       int *socket_read_id2 = NULL;
+       if (netsocket2 >= 0) {
+               socket_read_id2 = ast_io_add(io, netsocket2, socket_read, AST_IO_IN, &netsocket2);
+       }
 
        while (!dundi_shutdown) {
                res = ast_sched_wait(sched);
@@ -2213,6 +2218,10 @@ static void *network_thread(void *ignore)
 
        ast_io_remove(io, socket_read_id);
 
+       if (socket_read_id2) {
+               ast_io_remove(io, socket_read_id2);
+       }
+
        return NULL;
 }
 
@@ -3162,7 +3171,17 @@ static int dundi_xmit(struct dundi_packet *pack)
        int res;
        if (dundidebug)
                dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
-       res = ast_sendto(netsocket, pack->data, pack->datalen, 0, &pack->parent->addr);
+
+       if (netsocket2 < 0) {
+               res = ast_sendto(netsocket, pack->data, pack->datalen, 0, &pack->parent->addr);
+       } else {
+               if (ast_sockaddr_is_ipv4(&pack->parent->addr)) {
+                       res = ast_sendto(netsocket, pack->data, pack->datalen, 0, &pack->parent->addr);
+               } else {
+                       res = ast_sendto(netsocket2, pack->data, pack->datalen, 0, &pack->parent->addr);
+               }
+       }
+
        if (res < 0) {
                ast_log(LOG_WARNING, "Failed to transmit to '%s': %s\n",
                        ast_sockaddr_stringify(&pack->parent->addr), strerror(errno));
@@ -4916,7 +4935,7 @@ static void set_host_ipaddr(struct ast_sockaddr *sin)
        get_ipaddress(ipaddr, sizeof(ipaddr), hn, family);
 }
 
-static int set_config(char *config_file, struct ast_sockaddr *sin, int reload)
+static int set_config(char *config_file, struct ast_sockaddr *sin, int reload, struct ast_sockaddr *sin2)
 {
        struct ast_config *cfg;
        struct ast_variable *v;
@@ -4927,7 +4946,8 @@ static int set_config(char *config_file, struct ast_sockaddr *sin, int reload)
        int port = 0;
        int globalpcmodel = 0;
        dundi_eid testeid;
-       char bind_addr[80];
+       char bind_addr[80]={0,};
+       char bind_addr2[80]={0,};
 
        if (!(cfg = ast_config_load(config_file, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
                ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
@@ -4957,6 +4977,12 @@ static int set_config(char *config_file, struct ast_sockaddr *sin, int reload)
                                        ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
                                }
                        }
+               } else if (!strcasecmp(v->name, "bindaddr2")) {
+                       if (get_ipaddress(bind_addr2, sizeof(bind_addr2), v->value, AF_UNSPEC) == 0) {
+                               if (!ast_sockaddr_parse(sin2, bind_addr2, 0)) {
+                                       ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
+                               }
+                       }
                } else if (!strcasecmp(v->name, "authdebug")) {
                        authdebug = ast_true(v->value);
                } else if (!strcasecmp(v->name, "ttl")) {
@@ -5031,6 +5057,10 @@ static int set_config(char *config_file, struct ast_sockaddr *sin, int reload)
 
        set_host_ipaddr(sin);
 
+       if (!ast_sockaddr_isnull(sin2)) {
+               ast_sockaddr_set_port(sin2, port);
+       }
+
        AST_LIST_UNLOCK(&peers);
 
        mark_mappings();
@@ -5093,6 +5123,14 @@ static int unload_module(void)
                clearcachethreadid = AST_PTHREADT_NULL;
        }
 
+       if (netsocket >= 0) {
+               close(netsocket);
+       }
+
+       if (netsocket2 >= 0) {
+               close(netsocket2);
+       }
+
        mark_mappings();
        prune_mappings();
        mark_peers();
@@ -5118,10 +5156,12 @@ static int unload_module(void)
 static int reload(void)
 {
        struct ast_sockaddr sin;
+       struct ast_sockaddr sin2;
 
        ast_sockaddr_setnull(&sin);
+       ast_sockaddr_setnull(&sin2);
 
-       if (set_config("dundi.conf", &sin, 1))
+       if (set_config("dundi.conf", &sin, 1, &sin2))
                return AST_MODULE_LOAD_FAILURE;
 
        return AST_MODULE_LOAD_SUCCESS;
@@ -5130,6 +5170,7 @@ static int reload(void)
 static int load_module(void)
 {
        struct ast_sockaddr sin;
+       struct ast_sockaddr sin2;
 
        dundi_set_output(dundi_debug_output);
        dundi_set_error(dundi_error_output);
@@ -5143,28 +5184,68 @@ static int load_module(void)
        }
 
        ast_sockaddr_setnull(&sin);
+       ast_sockaddr_setnull(&sin2);
 
-       if (set_config("dundi.conf", &sin, 0)) {
+       if (set_config("dundi.conf", &sin, 0, &sin2)) {
                goto declined;
        }
 
-       if (ast_sockaddr_is_ipv6(&sin)) {
-               netsocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
-       } else {
-               netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
-       }
+       if (!ast_sockaddr_isnull(&sin2)) {
+               if ((ast_sockaddr_is_ipv4(&sin) == ast_sockaddr_is_ipv4(&sin2)) || (ast_sockaddr_is_ipv6(&sin) == ast_sockaddr_is_ipv6(&sin2))) {
+                       ast_log(LOG_ERROR, "bindaddr & bindaddr2 should be different IP protocols.\n");
+                       goto declined;
+               }
 
-       if (netsocket < 0) {
-               ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
-               goto declined;
-       }
-       if (ast_bind(netsocket, &sin))  {
-               ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
-                       ast_sockaddr_stringify(&sin), strerror(errno));
-               goto declined;
-       }
+               /*bind netsocket to ipv4, netsocket2 to ipv6 */
 
-       ast_set_qos(netsocket, tos, 0, "DUNDi");
+               netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+               netsocket2 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
+               if (netsocket < 0 || netsocket2 < 0) {
+                       ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
+                       goto declined;
+               }
+               if (ast_sockaddr_is_ipv4(&sin)) {
+                       if (ast_bind(netsocket, &sin)) {
+                               ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
+                               ast_sockaddr_stringify(&sin), strerror(errno));
+                               goto declined;
+                       }
+                       if (ast_bind(netsocket2, &sin2)) {
+                               ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
+                               ast_sockaddr_stringify(&sin2), strerror(errno));
+                               goto declined;
+                       }
+               } else {
+                       if (ast_bind(netsocket, &sin2)) {
+                               ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
+                               ast_sockaddr_stringify(&sin2), strerror(errno));
+                               goto declined;
+                       }
+                       if (ast_bind(netsocket2, &sin)) {
+                               ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
+                                       ast_sockaddr_stringify(&sin), strerror(errno));
+                               goto declined;
+                       }
+               }
+               ast_set_qos(netsocket, tos, 0, "DUNDi");
+               ast_set_qos(netsocket2, tos, 0, "DUNDi");
+       } else {
+               if (ast_sockaddr_is_ipv6(&sin)) {
+                       netsocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
+               } else {
+                       netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+               }
+               if (netsocket < 0) {
+                       ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
+                       goto declined;
+               }
+               if (ast_bind(netsocket, &sin))  {
+                       ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
+                               ast_sockaddr_stringify(&sin), strerror(errno));
+                       goto declined;
+               }
+               ast_set_qos(netsocket, tos, 0, "DUNDi");
+       }
 
        if (start_network_thread()) {
                ast_log(LOG_ERROR, "Unable to start network thread\n");
@@ -5179,6 +5260,8 @@ static int load_module(void)
        ast_custom_function_register(&dundi_result_function);
 
        ast_verb(2, "DUNDi Ready and Listening on %s\n", ast_sockaddr_stringify(&sin));
+       if (!ast_sockaddr_isnull(&sin2))
+               ast_verb(2, "DUNDi Ready and Listening on %s\n", ast_sockaddr_stringify(&sin2));
 
        return AST_MODULE_LOAD_SUCCESS;