mgcp: Add a call tap feature to forward audio to another forward port

For debugging it is useful to forward (tee) UDP packets to another
system and use gstreamer to inspect the rtp stream. This is untested
code and might contain bugs.... and of course only tap your own calls.
diff --git a/openbsc/src/mgcp/mgcp_network.c b/openbsc/src/mgcp/mgcp_network.c
index 3877112..bbc46f4 100644
--- a/openbsc/src/mgcp/mgcp_network.c
+++ b/openbsc/src/mgcp/mgcp_network.c
@@ -151,6 +151,14 @@
  * The below code is for dispatching. We have a dedicated port for
  * the data coming from the net and one to discover the BTS.
  */
+static int forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf, int len)
+{
+	if (!tap->enabled)
+		return 0;
+
+	return sendto(fd, buf, len, 0,
+		      (struct sockaddr *)&tap->forward, sizeof(tap->forward));
+}
 static int send_to(struct mgcp_endpoint *endp, int dest, int is_rtp,
 		   struct sockaddr_in *addr, char *buf, int rc)
 {
@@ -168,6 +176,8 @@
 			patch_and_count(endp, &endp->bts_state,
 					endp->net_end.payload_type,
 					addr, buf, rc);
+			forward_data(endp->net_end.rtp.fd,
+				     &endp->taps[MGCP_TAP_NET_OUT], buf, rc);
 			return udp_send(endp->net_end.rtp.fd, &endp->net_end.addr,
 					endp->net_end.rtp_port, buf, rc);
 		} else {
@@ -179,6 +189,8 @@
 			patch_and_count(endp, &endp->net_state,
 					endp->bts_end.payload_type,
 					addr, buf, rc);
+			forward_data(endp->bts_end.rtp.fd,
+				     &endp->taps[MGCP_TAP_BTS_OUT], buf, rc);
 			return udp_send(endp->bts_end.rtp.fd, &endp->bts_end.addr,
 					endp->bts_end.rtp_port, buf, rc);
 		} else {
@@ -248,6 +260,8 @@
 
 	proto = fd == &endp->net_end.rtp ? PROTO_RTP : PROTO_RTCP;
 	endp->net_end.packets += 1;
+
+	forward_data(fd->fd, &endp->taps[MGCP_TAP_NET_IN], buf, rc);
 	return send_to(endp, DEST_BTS, proto == PROTO_RTP, &addr, &buf[0], rc);
 }
 
@@ -310,6 +324,7 @@
 	/* do this before the loop handling */
 	endp->bts_end.packets += 1;
 
+	forward_data(fd->fd, &endp->taps[MGCP_TAP_BTS_IN], buf, rc);
 	return send_to(endp, DEST_NETWORK, proto == PROTO_RTP, &addr, &buf[0], rc);
 }
 
diff --git a/openbsc/src/mgcp/mgcp_protocol.c b/openbsc/src/mgcp/mgcp_protocol.c
index 036ddf0..9916b40 100644
--- a/openbsc/src/mgcp/mgcp_protocol.c
+++ b/openbsc/src/mgcp/mgcp_protocol.c
@@ -826,4 +826,6 @@
 
 	endp->conn_mode = endp->orig_mode = MGCP_CONN_NONE;
 	endp->allow_patch = 0;
+
+	memset(&endp->taps, 0, sizeof(endp->taps));
 }
diff --git a/openbsc/src/mgcp/mgcp_vty.c b/openbsc/src/mgcp/mgcp_vty.c
index 040e8ff..0faa674 100644
--- a/openbsc/src/mgcp/mgcp_vty.c
+++ b/openbsc/src/mgcp/mgcp_vty.c
@@ -310,10 +310,55 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(tap_call,
+      tap_call_cmd,
+      "tap-call ENDPOINT (bts-in|bts-out|net-in|net-out) A.B.C.D <0-65534>",
+      "Forward data on endpoint to a different system\n"
+      "The endpoint in hex\n"
+      "Forward the data coming from the bts\n"
+      "Forward the data coming from the bts leaving to the network\n"
+      "Forward the data coming from the net\n"
+      "Forward the data coming from the net leaving to the bts\n"
+      "destination IP of the data\n" "destination port\n")
+{
+	struct mgcp_rtp_tap *tap;
+	struct mgcp_endpoint *endp;
+	int port = 0;
+
+	int endp_no = strtoul(argv[0], NULL, 16);
+	if (endp_no < 1 || endp_no >= g_cfg->number_endpoints) {
+		vty_out(vty, "Endpoint number %s/%d is invalid.%s",
+		argv[0], endp_no, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	endp = &g_cfg->endpoints[endp_no];
+
+	if (strcmp(argv[1], "bts-in") == 0) {
+		port = MGCP_TAP_BTS_IN;
+	} else if (strcmp(argv[1], "bts-out") == 0) {
+		port = MGCP_TAP_BTS_OUT;
+	} else if (strcmp(argv[1], "net-in") == 0) {
+		port = MGCP_TAP_NET_IN;
+	} else if (strcmp(argv[1], "net-out") == 0) {
+		port = MGCP_TAP_NET_OUT;
+	} else {
+		vty_out(vty, "Unknown mode... tricked vty?%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	tap = &endp->taps[port];
+	memset(&tap->forward, 0, sizeof(tap->forward));
+	inet_aton(argv[2], &tap->forward.sin_addr);
+	tap->forward.sin_port = htons(atoi(argv[3]));
+	return CMD_SUCCESS;
+}
+
 int mgcp_vty_init(void)
 {
 	install_element_ve(&show_mgcp_cmd);
 	install_element(ENABLE_NODE, &loop_endp_cmd);
+	install_element(ENABLE_NODE, &tap_call_cmd);
 
 	install_element(CONFIG_NODE, &cfg_mgcp_cmd);
 	install_node(&mgcp_node, config_write_mgcp);