jolly has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/osmo-mgw/+/33548 )


Change subject: ASCI: Support conference briding with 1..n connections
......................................................................

ASCI: Support conference briding with 1..n connections

An RTP bridge may have 1 or many endpoints. Each endpoint may receive,
send or echo audio back. Because transcoding is not supported, it must
be assumed that only connection receives RTP in a conference with more
than 2 connections.

A connection that receives RTP into the endpont will have "send" pointer
set to (one of) the other connection that shall send out RTP.
All connection that send RTP have "send_next" pointer set to the next
or first connection that sends RTP. They are linked in a ring.

Regular bridge with two connections: Each connection forwards audio
towards the other connection and only towards the other connection.
The "send_next" pointers are set to NULL or pointing to the other
endpoint.

connection A                            connection B
("sendrecv")                            ("sendrecv")
conn->send --------------------------->
 <------------------------------------- conn->send
conn->send_next ---------------------->
 <------------------------------------- conn->send_next

Conference bridge with three connections: Each connection forwards
audio to itself and also towards all other connection.

connection A        connection B        connection C
("confecho")        ("confecho")        ("confecho");
conn->send --\      conn->send --\      conn->send --\
 <-----------/       <-----------/       <-----------/
conn->send_next -->
                    conn->send_next -->
 <------------------------------------- conn->send_next

The same conference bridge but without echo: Each connection forwards
auto to all other connection but not to itself.

connection A        connection B        connection C
("confecho")        ("confecho")        ("confecho");
conn->send -------> conn->send ------->
 <------------------------------------- conn->send
conn->send_next -->
                    conn->send_next -->
 <------------------------------------- conn->send_next

Change-Id: Ic99a55ab5a3a6170e940403fadd52697e99f2f3a
---
M include/osmocom/mgcp/mgcp_trunk.h
M src/libosmo-mgcp/mgcp_endp.c
M src/libosmo-mgcp/mgcp_network.c
M src/libosmo-mgcp/mgcp_protocol.c
M src/libosmo-mgcp/mgcp_trunk.c
5 files changed, 190 insertions(+), 60 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-mgw refs/changes/48/33548/1

diff --git a/include/osmocom/mgcp/mgcp_trunk.h 
b/include/osmocom/mgcp/mgcp_trunk.h
index e608161..7dff2b5 100644
--- a/include/osmocom/mgcp/mgcp_trunk.h
+++ b/include/osmocom/mgcp/mgcp_trunk.h
@@ -79,6 +79,8 @@
 struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const 
char *epname);
 int e1_trunk_nr_from_epname(unsigned int *trunk_nr, const char *epname);
 struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, 
unsigned int num);
+int mgcp_trunk_connect(struct mgcp_endpoint *endp, struct mgcp_conn 
*exclude_conn);
+void mgcp_trunk_endp_update(struct mgcp_endpoint *endp);

 /* The virtual trunk is always created on trunk id 0 for historical reasons,
  * use this define constant as ID when allocating a virtual trunk. Other
diff --git a/src/libosmo-mgcp/mgcp_endp.c b/src/libosmo-mgcp/mgcp_endp.c
index 20088b7..4114cf9 100644
--- a/src/libosmo-mgcp/mgcp_endp.c
+++ b/src/libosmo-mgcp/mgcp_endp.c
@@ -629,7 +629,7 @@
        /* Allocate resources */
        switch (endp->trunk->trunk_type) {
        case MGCP_TRUNK_VIRTUAL:
-               /* No updating initaliziation required for virtual endpoints. */
+               mgcp_trunk_endp_update(endp);
                break;
        case MGCP_TRUNK_E1:
                mgcp_e1_endp_update(endp);
diff --git a/src/libosmo-mgcp/mgcp_network.c b/src/libosmo-mgcp/mgcp_network.c
index 8850670..a03f697 100644
--- a/src/libosmo-mgcp/mgcp_network.c
+++ b/src/libosmo-mgcp/mgcp_network.c
@@ -1292,6 +1292,7 @@
        struct mgcp_conn *conn_dst;
        struct osmo_sockaddr *from_addr = mc->from_addr;
        char ipbuf[INET6_ADDRSTRLEN];
+       int rc = 0;

        /*! NOTE: This callback function implements the endpoint specific
         *  dispatch behaviour of an rtp bridge/proxy endpoint. It is assumed
@@ -1323,36 +1324,25 @@
                return mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
        }

-       /* Find a destination connection. */
-       /* NOTE: This code path runs every time an RTP packet is received. The
-        * function mgcp_find_dst_conn() we use to determine the detination
-        * connection will iterate the connection list inside the endpoint.
-        * Since list iterations are quite costly, we will figure out the
-        * destination only once and use the optional private data pointer of
-        * the connection to cache the destination connection pointer. */
-       if (!conn->send) {
-               conn_dst = mgcp_find_dst_conn(conn);
-               conn->send = conn_dst;
-       } else {
-               conn_dst = conn->send;
+       /* Dispatch RTP packet to destination RTP connection(s).
+        * Only if reception is enabled and there is at least one destination,
+        * conn->send is set to that destination. TRP is forwarded to that
+        * destination. If there are multiple destinations (conference),
+        * conn->send_next points to the next destination. The loop is executed
+        * until data is sent to all destinations. If conn->send points to the
+        * receiving conn (this conn), data is also echoed back to the sender.
+        *
+        * The loop terminates if there is no other destination (conn_dst == 
NULL)
+        * or if it reaches the originating connection (conn_dst == conn)
+        * or if it reaches the destination where it started (conn_dst == 
conn->send).
+        */
+       if ((conn_dst = conn->send)) {
+               do {
+                       rc = mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg);
+                       conn_dst = conn_dst->send_next;
+               } while (conn_dst && conn_dst != conn && conn_dst != 
conn->send);
        }
-
-       /* There is no destination conn, stop here */
-       if (!conn_dst) {
-               LOGPCONN(conn, DRTP, LOGL_DEBUG,
-                        "no connection to forward an incoming RTP packet 
to\n");
-               return -1;
-       }
-
-       /* The destination conn is not an RTP connection */
-       if (conn_dst->type != MGCP_CONN_TYPE_RTP) {
-               LOGPCONN(conn, DRTP, LOGL_ERROR,
-                        "unable to find suitable destination conn\n");
-               return -1;
-       }
-
-       /* Dispatch RTP packet to destination RTP connection */
-       return mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg);
+       return rc;
 }

 /*! dispatch incoming RTP packet to E1 subslot, handle RTCP packets locally.
@@ -1397,18 +1387,8 @@
  *  \param[in] conn Connection that is about to be removed (ignored). */
 void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn 
*conn)
 {
-       struct mgcp_conn *conn_cleanup;
-
-       /* In mgcp_dispatch_rtp_bridge_cb() we use conn->priv to cache the
-        * pointer to the destination connection, so that we do not have
-        * to go through the list every time an RTP packet arrives. To prevent
-        * a use-after-free situation we invalidate this information for all
-        * connections present when one connection is removed from the
-        * endpoint. */
-       llist_for_each_entry(conn_cleanup, &endp->conns, entry) {
-               if (conn_cleanup->send == conn)
-                       conn_cleanup->send = NULL;
-       }
+       /* Restructure connections with this connection excluded. */
+       mgcp_trunk_connect(endp, conn);
 }

 /*! cleanup an endpoint when a connection on an E1 endpoint is removed.
diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c
index 6223ffa..c4149ab 100644
--- a/src/libosmo-mgcp/mgcp_protocol.c
+++ b/src/libosmo-mgcp/mgcp_protocol.c
@@ -966,24 +966,6 @@
                return create_err_response(endp, endp, 517, "CRCX", 
pdata->trans);
        }

-       /* Check if we are able to accept the creation of another connection */
-       if (llist_count(&endp->conns) >= endp->type->max_conns) {
-               LOGPENDP(endp, DLMGCP, LOGL_ERROR,
-                       "CRCX: endpoint full, max. %i connections allowed!\n",
-                       endp->type->max_conns);
-               if (trunk->force_realloc) {
-                       /* There is no more room for a connection, make some
-                        * room by blindly tossing the oldest of the two two
-                        * connections */
-                       mgcp_conn_free_oldest(endp);
-               } else {
-                       /* There is no more room for a connection, leave
-                        * everything as it is and return with an error */
-                       rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, 
MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
-                       return create_err_response(endp, endp, 540, "CRCX", 
pdata->trans);
-               }
-       }
-
        /* Check if this endpoint already serves a call, if so, check if the
         * callids match up so that we are sure that this is our call */
        if (endp->callid && mgcp_verify_call_id(endp, callid)) {
diff --git a/src/libosmo-mgcp/mgcp_trunk.c b/src/libosmo-mgcp/mgcp_trunk.c
index 69750f8..da685e1 100644
--- a/src/libosmo-mgcp/mgcp_trunk.c
+++ b/src/libosmo-mgcp/mgcp_trunk.c
@@ -24,6 +24,7 @@
 #include <osmocom/mgcp/mgcp.h>
 #include <osmocom/mgcp/mgcp_protocol.h>
 #include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_conn.h>
 #include <osmocom/mgcp/mgcp_trunk.h>
 #include <osmocom/mgcp/mgcp_e1.h>
 #include <osmocom/abis/e1_input.h>
@@ -306,3 +307,115 @@

        return NULL;
 }
+
+/* Interconnect payload of connections depending on their mode.
+ * Optionally exclude a connection that shall be removed from the endpoint.
+ *
+ * A connection that receives RTP into the endpont will have "send" pointer
+ * set to (one of) the other connection that shall send out RTP.
+ *
+ * All connection that send RTP have "send_next" pointer set to the next
+ * or first connection that sends RTP. They are linked in a ring.
+ *
+ * Regular bridge with two connections: Each connection forwards audio
+ * towards the other connection and only towards the other connection.
+ * The "send_next" pointers are pointing to the other endpoint.
+
+ * connection A                            connection B
+ * ("sendrecv")                            ("sendrecv")
+ * conn->send --------------------------->
+ *  <------------------------------------- conn->send
+ * conn->send_next ---------------------->
+ *  <------------------------------------- conn->send_next
+ *
+ * Conference bridge with three connections: Each connection forwards
+ * audio to itself and also towards all other connection.
+ *
+ * connection A        connection B        connection C
+ * ("confecho")        ("confecho")        ("confecho");
+ * conn->send --\      conn->send --\      conn->send --\
+ *  <-----------/       <-----------/       <-----------/
+ * conn->send_next -->
+ *                     conn->send_next -->
+ *  <------------------------------------- conn->send_next
+ *
+ * The same conference bridge but without echo: Each connection forwards
+ * auto to all other connection but not to itself.
+ *
+ * connection A        connection B        connection C
+ * ("confecho")        ("confecho")        ("confecho");
+ * conn->send -------> conn->send ------->
+ *  <------------------------------------- conn->send
+ * conn->send_next -->
+ *                     conn->send_next -->
+ *  <------------------------------------- conn->send_next
+ *
+ */
+int mgcp_trunk_connect(struct mgcp_endpoint *endp, struct mgcp_conn 
*exclude_conn)
+{
+       struct mgcp_conn *conn = NULL, *first_conn = NULL, *previous_conn, 
*send_conn;
+
+       /* Link all _sending_ connection in a circular linked list, if there is
+        * more than one sending endpoint. */
+       llist_for_each_entry(conn, &endp->conns, entry) {
+               if (conn == exclude_conn)
+                       continue;
+               if (conn->type != MGCP_CONN_TYPE_RTP)
+                       continue;
+               if (!(conn->mode & MGCP_CONN_SEND_ONLY))
+                       continue;
+               if (!first_conn)
+                       first_conn = conn;
+               else
+                       previous_conn->send_next = conn;
+               previous_conn = conn;
+       }
+       if (first_conn && previous_conn != first_conn)
+               previous_conn->send_next = first_conn;
+
+       /* Link all _receiving_ connections to a sending connection, if any.
+        * If the receiving connection also gets looped back what it receives, 
it is linked to itsef, so that the
+        * payload is also forwarded to itself. */
+       llist_for_each_entry(conn, &endp->conns, entry) {
+               if (conn == exclude_conn)
+                       continue;
+               if (conn->type != MGCP_CONN_TYPE_RTP)
+                       continue;
+               if (!(conn->mode & MGCP_CONN_RECV_ONLY))
+                       continue;
+               /* If we are also sending to ourself, our connection is the 
first sending endpoint. */
+               if ((conn->mode & MGCP_CONN_LOOPBACK) && (conn->mode & 
MGCP_CONN_SEND_ONLY))
+                       conn->send = conn;
+               /* If we are not sending to ourself, select the next sending 
connection in the circular list. */
+               else if (conn->send_next)
+                       conn->send = conn->send_next;
+               /* I case we are not in the ciruclar list, search for the first 
node in that list. */
+               else {
+                       llist_for_each_entry(send_conn, &endp->conns, entry) {
+                               if ((send_conn->mode & MGCP_CONN_SEND_ONLY))
+                                       conn->send = send_conn;
+                       }
+               }
+       }
+
+       /* Debug the result. */
+       LOGP(DLMGCP, LOGL_DEBUG, "Endpoint %s has the following 
interconnections:\n", endp->name);
+       llist_for_each_entry(conn, &endp->conns, entry) {
+               LOGP(DLMGCP, LOGL_DEBUG, " -> Connection ID %s %s incoming 
payload.\n", conn->id,
+                    (conn->mode & MGCP_CONN_RECV_ONLY) ? "receives" : 
"ignores");
+               send_conn = conn->send;
+               while (send_conn) {
+                       LOGP(DLMGCP, LOGL_DEBUG, "    Payload is forwarded to 
ID %s\n", send_conn->id);
+                       send_conn = send_conn->send_next;
+                       if (send_conn == conn || send_conn == conn->send)
+                               break;
+               }
+       }
+
+       return 0;
+}
+
+void mgcp_trunk_endp_update(struct mgcp_endpoint *endp)
+{
+       mgcp_trunk_connect(endp, NULL);
+}

--
To view, visit https://gerrit.osmocom.org/c/osmo-mgw/+/33548
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-mgw
Gerrit-Branch: master
Gerrit-Change-Id: Ic99a55ab5a3a6170e940403fadd52697e99f2f3a
Gerrit-Change-Number: 33548
Gerrit-PatchSet: 1
Gerrit-Owner: jolly <[email protected]>
Gerrit-MessageType: newchange

Reply via email to