Add Osmux IPv6 support

A new osmux bind-ip-v6 VTY command is added, similar to what's already
available for RTP. This way the user can decide whether to support IPv4,
IPv6 or both types of IP versions.

Both IP sockets are by default disabled, and must be explicitly enabled
by setting the bind-ip in the VTY configuration.

Change-Id: I446cd7da217e9f4a74995d7784ae55dcc60a29b7
diff --git a/include/osmocom/mgcp/mgcp.h b/include/osmocom/mgcp/mgcp.h
index 4c541cb..2005e4f 100644
--- a/include/osmocom/mgcp/mgcp.h
+++ b/include/osmocom/mgcp/mgcp.h
@@ -160,7 +160,8 @@
 	/* Osmux usage policy: */
 	enum osmux_usage osmux_use;
 	/* addr to bind the server to */
-	char osmux_addr[INET6_ADDRSTRLEN];
+	char *osmux_addr_v4;
+	char *osmux_addr_v6;
 	/* The BSC-NAT may ask for enabling osmux on demand. This tells us if
 	 * the osmux socket is already initialized.
 	 */
diff --git a/include/osmocom/mgcp/mgcp_network.h b/include/osmocom/mgcp/mgcp_network.h
index 349ff94..a3d57f0 100644
--- a/include/osmocom/mgcp/mgcp_network.h
+++ b/include/osmocom/mgcp/mgcp_network.h
@@ -157,7 +157,7 @@
 			  struct mgcp_rtp_state *state,
 			  struct mgcp_rtp_end *rtp_end,
 			  struct osmo_sockaddr *addr, struct msgb *msg);
-void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
+int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
 
 /* payload processing default functions */
 int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
diff --git a/src/libosmo-mgcp/mgcp_network.c b/src/libosmo-mgcp/mgcp_network.c
index 401bb09..41e6ffc 100644
--- a/src/libosmo-mgcp/mgcp_network.c
+++ b/src/libosmo-mgcp/mgcp_network.c
@@ -81,12 +81,13 @@
 /*! Determine the local rtp bind IP-address.
  *  \param[out] addr caller provided memory to store the resulting IP-Address.
  *  \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters.
+ * \ returns 0 on success, -1 if no local address could be provided.
  *
  *  The local bind IP-address is automatically selected by probing the
  *  IP-Address of the interface that is pointing towards the remote IP-Address,
  *  if no remote IP-Address is known yet, the statically configured
  *  IP-Addresses are used as fallback. */
-void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
+int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
 {
 
 	struct mgcp_endpoint *endp;
@@ -99,12 +100,30 @@
 	/* Osmux: No smart IP addresses allocation is supported yet. Simply
 	 * return the one set in VTY config: */
 	if (mgcp_conn_rtp_is_osmux(conn)) {
-		bind_addr = conn->conn->endp->trunk->cfg->osmux_addr;
-		LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
-			 "using configured osmux bind ip as local bind ip %s\n",
+		if (rem_addr_set) {
+			/* Match IP version with what was requested from remote: */
+			bind_addr = conn->end.addr.u.sa.sa_family == AF_INET6 ?
+				    conn->conn->endp->trunk->cfg->osmux_addr_v6 :
+				    conn->conn->endp->trunk->cfg->osmux_addr_v4;
+		} else {
+			/* Choose any of the bind addresses, preferring v6 over v4 if available: */
+			bind_addr = conn->conn->endp->trunk->cfg->osmux_addr_v6;
+			if (!bind_addr)
+				bind_addr = conn->conn->endp->trunk->cfg->osmux_addr_v4;
+		}
+		if (!bind_addr) {
+			LOGPCONN(conn->conn, DOSMUX, LOGL_ERROR,
+				"Unable to locate local Osmux address, check your configuration! v4=%u v6=%u remote_known=%s\n",
+				!!conn->conn->endp->trunk->cfg->osmux_addr_v4,
+				!!conn->conn->endp->trunk->cfg->osmux_addr_v6,
+				rem_addr_set ? osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf) : "no");
+			return -1;
+		}
+		LOGPCONN(conn->conn, DOSMUX, LOGL_DEBUG,
+			 "Using configured osmux bind ip as local bind ip %s\n",
 			 bind_addr);
 		osmo_strlcpy(addr, bind_addr, INET6_ADDRSTRLEN);
-		return;
+		return 0;
 	}
 
 	/* Try probing the local IP-Address */
@@ -117,7 +136,7 @@
 			LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
 				 "selected local rtp bind ip %s by probing using remote ip %s\n",
 				 addr, osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf));
-			return;
+			return 0;
 		}
 	}
 
@@ -147,6 +166,7 @@
 			"using mgcp bind ip as local rtp bind ip: %s\n", bind_addr);
 	}
 	osmo_strlcpy(addr, bind_addr, INET6_ADDRSTRLEN);
+	return 0;
 }
 
 /* This does not need to be a precision timestamp and
diff --git a/src/libosmo-mgcp/mgcp_osmux.c b/src/libosmo-mgcp/mgcp_osmux.c
index de0bc38..3be6d78 100644
--- a/src/libosmo-mgcp/mgcp_osmux.c
+++ b/src/libosmo-mgcp/mgcp_osmux.c
@@ -30,7 +30,8 @@
 #include <osmocom/mgcp/mgcp_endp.h>
 #include <osmocom/mgcp/mgcp_trunk.h>
 
-static struct osmo_fd osmux_fd;
+static struct osmo_fd osmux_fd_v4;
+static struct osmo_fd osmux_fd_v6;
 
 static LLIST_HEAD(osmux_handle_list);
 
@@ -76,20 +77,22 @@
 {
 	struct osmux_handle *handle = data;
 	socklen_t dest_len;
-	int rc;
-	struct mgcp_trunk *trunk = (struct mgcp_trunk *)osmux_fd.data;
+	int rc, fd;
+	struct mgcp_trunk *trunk = (struct mgcp_trunk *)osmux_fd_v4.data;
 	struct rate_ctr_group *all_osmux_stats = trunk->ratectr.all_osmux_conn_stats;
 
 	switch (handle->rem_addr.u.sa.sa_family) {
 	case AF_INET6:
 		dest_len = sizeof(handle->rem_addr.u.sin6);
+		fd = osmux_fd_v6.fd;
 		break;
 	case AF_INET:
 	default:
 		dest_len = sizeof(handle->rem_addr.u.sin);
+		fd = osmux_fd_v4.fd;
 		break;
 	}
-	rc = sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0,
+	rc = sendto(fd, batch_msg->data, batch_msg->len, 0,
 		    (struct sockaddr *)&handle->rem_addr.u.sa, dest_len);
 	if (rc < 0) {
 		char errbuf[129];
@@ -187,11 +190,6 @@
 {
 	struct osmux_handle *h;
 
-	if (rem_addr->u.sa.sa_family != AF_INET) {
-		LOGP(DOSMUX, LOGL_DEBUG, "IPv6 not supported in osmux yet!\n");
-		return NULL;
-	}
-
 	h = osmux_handle_find_get(rem_addr);
 	if (h != NULL)
 		return h->in;
@@ -450,27 +448,46 @@
 	/* So far we only support running on one trunk: */
 	OSMO_ASSERT(trunk == mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID));
 
-	osmo_fd_setup(&osmux_fd, -1, OSMO_FD_READ, osmux_read_fd_cb, trunk, 0);
+	osmo_fd_setup(&osmux_fd_v4, -1, OSMO_FD_READ, osmux_read_fd_cb, trunk, 0);
+	osmo_fd_setup(&osmux_fd_v6, -1, OSMO_FD_READ, osmux_read_fd_cb, trunk, 0);
 
-	ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port,
-				cfg->endp_dscp, cfg->endp_priority);
-	if (ret < 0) {
-		LOGP(DOSMUX, LOGL_ERROR, "cannot bind OSMUX socket to %s:%u\n",
-		     cfg->osmux_addr, cfg->osmux_port);
-		return ret;
+	if (cfg->osmux_addr_v4) {
+		ret = mgcp_create_bind(cfg->osmux_addr_v4, &osmux_fd_v4, cfg->osmux_port,
+					cfg->endp_dscp, cfg->endp_priority);
+		if (ret < 0) {
+			LOGP(DOSMUX, LOGL_ERROR, "Cannot bind OSMUX IPv4 socket to %s:%u\n",
+			     cfg->osmux_addr_v4, cfg->osmux_port);
+			return ret;
+		}
+
+		ret = osmo_fd_register(&osmux_fd_v4);
+		if (ret < 0) {
+			LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv4 socket %s\n",
+			     osmo_sock_get_name2(osmux_fd_v4.fd));
+			return ret;
+		}
+		LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv4 socket listening on %s\n",
+		     osmo_sock_get_name2(osmux_fd_v4.fd));
 	}
+	if (cfg->osmux_addr_v6) {
+		ret = mgcp_create_bind(cfg->osmux_addr_v6, &osmux_fd_v6, cfg->osmux_port,
+					cfg->endp_dscp, cfg->endp_priority);
+		if (ret < 0) {
+			LOGP(DOSMUX, LOGL_ERROR, "Cannot bind OSMUX IPv6 socket to %s:%u\n",
+			     cfg->osmux_addr_v6, cfg->osmux_port);
+			return ret;
+		}
 
-	ret = osmo_fd_register(&osmux_fd);
-	if (ret < 0) {
-		LOGP(DOSMUX, LOGL_ERROR, "cannot register OSMUX socket %s\n",
-		     osmo_sock_get_name2(osmux_fd.fd));
-		return ret;
+		ret = osmo_fd_register(&osmux_fd_v6);
+		if (ret < 0) {
+			LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv6 socket %s\n",
+			     osmo_sock_get_name2(osmux_fd_v6.fd));
+			return ret;
+		}
+		LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv6 socket listening on %s\n",
+		     osmo_sock_get_name2(osmux_fd_v6.fd));
 	}
 	cfg->osmux_initialized = true;
-
-	LOGP(DOSMUX, LOGL_INFO, "OSMUX socket listening on %s\n",
-		 osmo_sock_get_name2(osmux_fd.fd));
-
 	return 0;
 }
 
@@ -647,7 +664,7 @@
 		 osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf),
 		 osmo_sockaddr_port(&conn->end.addr.u.sa), conn->osmux.remote_cid);
 
-	return mgcp_udp_send(osmux_fd.fd, &conn->end.addr, (char *)osmuxh, buf_len);
+	return mgcp_udp_send(osmux_fd_v4.fd, &conn->end.addr, (char *)osmuxh, buf_len);
 }
 
 /* bsc-nat allocates/releases the Osmux circuit ID. +7 to round up to 8 bit boundary. */
diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c
index a257ffe..46e1d6b 100644
--- a/src/libosmo-mgcp/mgcp_protocol.c
+++ b/src/libosmo-mgcp/mgcp_protocol.c
@@ -1067,7 +1067,10 @@
 
 	/* Find a local address for conn based on policy and initial SDP remote
 	   information, then find a free port for it */
-	mgcp_get_local_addr(conn->end.local_addr, conn);
+	if (mgcp_get_local_addr(conn->end.local_addr, conn) < 0) {
+		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
+		goto error2;
+	}
 	if (allocate_port(endp, conn) != 0) {
 		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
 		goto error2;
@@ -1279,7 +1282,10 @@
 	   to update our announced IP addr and re-bind our local end. This can
 	   happen for instance if MGW initially provided an IPv4 during CRCX
 	   ACK, and now MDCX tells us the remote has an IPv6 address. */
-	mgcp_get_local_addr(new_local_addr, conn);
+	if (mgcp_get_local_addr(new_local_addr, conn) < 0) {
+		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
+		goto error3;
+	}
 	if (strcmp(new_local_addr, conn->end.local_addr)) {
 		osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
 		mgcp_free_rtp_port(&conn->end);
@@ -1627,7 +1633,6 @@
 
 	cfg->source_port = 2427;
 	osmo_strlcpy(cfg->source_addr, "0.0.0.0", sizeof(cfg->source_addr));
-	osmo_strlcpy(cfg->osmux_addr, "0.0.0.0", sizeof(cfg->osmux_addr));
 
 	cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
 	cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
diff --git a/src/libosmo-mgcp/mgcp_vty.c b/src/libosmo-mgcp/mgcp_vty.c
index 1bf8789..bc2673d 100644
--- a/src/libosmo-mgcp/mgcp_vty.c
+++ b/src/libosmo-mgcp/mgcp_vty.c
@@ -142,8 +142,12 @@
 		break;
 	}
 	if (g_cfg->osmux_use != OSMUX_USAGE_OFF) {
-		vty_out(vty, " osmux bind-ip %s%s",
-			g_cfg->osmux_addr, VTY_NEWLINE);
+		if (g_cfg->osmux_addr_v4)
+			vty_out(vty, " osmux bind-ip %s%s",
+				g_cfg->osmux_addr_v4, VTY_NEWLINE);
+		if (g_cfg->osmux_addr_v6)
+			vty_out(vty, " osmux bind-ip-v6 %s%s",
+				g_cfg->osmux_addr_v6, VTY_NEWLINE);
 		vty_out(vty, " osmux batch-factor %d%s",
 			g_cfg->osmux_batch, VTY_NEWLINE);
 		vty_out(vty, " osmux batch-size %u%s",
@@ -1584,12 +1588,21 @@
 
 DEFUN(cfg_mgcp_osmux_ip,
       cfg_mgcp_osmux_ip_cmd,
-      "osmux bind-ip " VTY_IPV46_CMD,
+      "osmux bind-ip " VTY_IPV4_CMD,
       OSMUX_STR IP_STR
-      "IPv4 Address to bind to\n"
+      "IPv4 Address to bind to\n")
+{
+	osmo_talloc_replace_string(g_cfg, &g_cfg->osmux_addr_v4, argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_osmux_ip_v6,
+      cfg_mgcp_osmux_ip_v6_cmd,
+      "osmux bind-ip-v6 " VTY_IPV6_CMD,
+      OSMUX_STR IP_STR
       "IPv6 Address to bind to\n")
 {
-	osmo_strlcpy(g_cfg->osmux_addr, argv[0], sizeof(g_cfg->osmux_addr));
+	osmo_talloc_replace_string(g_cfg, &g_cfg->osmux_addr_v6, argv[0]);
 	return CMD_SUCCESS;
 }
 
@@ -1718,6 +1731,7 @@
 	install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_name_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_osmux_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_v6_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_factor_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_size_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd);