From cda75709bb2dcad6daeba4ad139a9f5ffc05176e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 13 Feb 2013 18:01:43 -0500 Subject: [PATCH] address-pool: Add API to request a specific address from the pool Also add relevant unit tests. --- gst/rtsp-server/rtsp-address-pool.c | 143 +++++++++++++++++++++++++++++++++--- gst/rtsp-server/rtsp-address-pool.h | 8 ++ tests/check/gst/addresspool.c | 69 ++++++++++++++++- 3 files changed, 210 insertions(+), 10 deletions(-) diff --git a/gst/rtsp-server/rtsp-address-pool.c b/gst/rtsp-server/rtsp-address-pool.c index 002751e..54fe834 100644 --- a/gst/rtsp-server/rtsp-address-pool.c +++ b/gst/rtsp-server/rtsp-address-pool.c @@ -272,13 +272,15 @@ invalid: } } +/* increments the address by count */ + static void -inc_address (GstRTSPAddressPool * pool, Addr * addr) +inc_address (Addr * addr, guint8 count) { gint i; guint carry; - carry = 1; + carry = count; for (i = addr->size - 1; i >= 0 && carry > 0; i--) { carry += addr->bytes[i]; addr->bytes[i] = carry & 0xff; @@ -286,18 +288,48 @@ inc_address (GstRTSPAddressPool * pool, Addr * addr) } } +/* tells us the number of addresses between min_addr and max_addr */ + +static guint +diff_address (Addr * max_addr, Addr * min_addr) +{ + gint i; + guint result = 0; + + g_return_val_if_fail (min_addr->size == max_addr->size, 0); + + for (i = 0; i < min_addr->size; i++) { + g_return_val_if_fail (result < (1 << 24), result); + + result <<= 8; + result += max_addr->bytes[i] - min_addr->bytes[i]; + } + + return result; +} + + static AddrRange * -split_range (GstRTSPAddressPool * pool, AddrRange * range, gint skip, - gint n_ports) +split_range (GstRTSPAddressPool * pool, AddrRange * range, guint skip_addr, + guint skip_port, gint n_ports) { GstRTSPAddressPoolPrivate *priv = pool->priv; AddrRange *temp; + if (skip_addr) { + temp = g_slice_dup (AddrRange, range); + memcpy (temp->max.bytes, temp->min.bytes, temp->min.size); + inc_address (&temp->max, skip_addr - 1); + priv->addresses = g_list_prepend (priv->addresses, temp); + + inc_address (&range->min, skip_addr); + } + if (!RANGE_IS_SINGLE (range)) { /* min and max are not the same, we have more than one address. */ temp = g_slice_dup (AddrRange, range); /* increment the range min address */ - inc_address (pool, &temp->min); + inc_address (&temp->min, 1); /* and store back in pool */ priv->addresses = g_list_prepend (priv->addresses, temp); @@ -306,15 +338,15 @@ split_range (GstRTSPAddressPool * pool, AddrRange * range, gint skip, } /* range now contains only one single address */ - if (skip > 0) { + if (skip_port > 0) { /* make a range with the skipped ports */ temp = g_slice_dup (AddrRange, range); - temp->max.port = temp->min.port + skip - 1; + temp->max.port = temp->min.port + skip_port - 1; /* and store back in pool */ priv->addresses = g_list_prepend (priv->addresses, temp); /* increment range port */ - range->min.port += skip; + range->min.port += skip_port; } /* range now contains single address with desired port number */ if (range->max.port - range->min.port + 1 > n_ports) { @@ -387,7 +419,7 @@ gst_rtsp_address_pool_acquire_address (GstRTSPAddressPool * pool, /* we found a range, remove from the list */ priv->addresses = g_list_delete_link (priv->addresses, walk); /* now split and exit our loop */ - result = split_range (pool, range, skip, n_ports); + result = split_range (pool, range, 0, skip, n_ports); priv->allocated = g_list_prepend (priv->allocated, result); break; } @@ -495,3 +527,96 @@ gst_rtsp_address_pool_dump (GstRTSPAddressPool * pool) g_list_foreach (priv->allocated, (GFunc) dump_range, pool); g_mutex_unlock (&priv->lock); } + +/** + * gst_rtsp_address_pool_reserve_address: + * @pool: a #GstRTSPAddressPool + * @address: The IP address to reserve + * @port: The first port to reserve + * @n_ports: The number of ports + * @ttl: The requested ttl + * + * Take a specific address and ports from @pool. @n_ports consecutive + * ports will be allocated of which the first one can be found in + * @port. + * + * Returns: a #GstRTSPAddress that should be freed with gst_rtsp_address_free + * after use or %NULL when no address could be acquired. + */ + +GstRTSPAddress * +gst_rtsp_address_pool_reserve_address (GstRTSPAddressPool * pool, + const gchar * address, guint port, guint n_ports, guint ttl) +{ + GstRTSPAddressPoolPrivate *priv; + Addr input_addr; + GList *walk, *next; + AddrRange *result; + GstRTSPAddress *addr; + + g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), NULL); + g_return_val_if_fail (address != NULL, NULL); + g_return_val_if_fail (port > 0, NULL); + g_return_val_if_fail (n_ports > 0, NULL); + + priv = pool->priv; + result = NULL; + addr = NULL; + + if (!fill_address (address, port, &input_addr)) { + GST_ERROR_OBJECT (pool, "invalid address %s", address); + return NULL; + } + + g_mutex_lock (&priv->lock); + /* go over available ranges */ + for (walk = priv->addresses; walk; walk = next) { + AddrRange *range; + gint skip_port, skip_addr; + + range = walk->data; + next = walk->next; + + /* Not the right type of address */ + if (range->min.size != input_addr.size) + continue; + + /* Check that the address is in the interval */ + if (memcmp (range->min.bytes, input_addr.bytes, input_addr.size) > 0 || + memcmp (range->max.bytes, input_addr.bytes, input_addr.size) < 0) + continue; + + /* Make sure the requested ports are inside the range */ + if (port < range->min.port || port + n_ports - 1 > range->max.port) + continue; + + if (ttl != range->ttl) + continue; + + skip_addr = diff_address (&input_addr, &range->min); + skip_port = port - range->min.port; + + /* we found a range, remove from the list */ + priv->addresses = g_list_delete_link (priv->addresses, walk); + /* now split and exit our loop */ + result = split_range (pool, range, skip_addr, skip_port, n_ports); + priv->allocated = g_list_prepend (priv->allocated, result); + break; + } + g_mutex_unlock (&priv->lock); + + if (result) { + addr = g_slice_new0 (GstRTSPAddress); + addr->pool = g_object_ref (pool); + addr->address = get_address_string (&result->min); + addr->n_ports = n_ports; + addr->port = result->min.port; + addr->ttl = result->ttl; + addr->priv = result; + + GST_DEBUG_OBJECT (pool, "reserved address %s:%u ttl %u", addr->address, + addr->port, addr->ttl); + } + + return addr; +} diff --git a/gst/rtsp-server/rtsp-address-pool.h b/gst/rtsp-server/rtsp-address-pool.h index b893790..2f7a80a 100644 --- a/gst/rtsp-server/rtsp-address-pool.h +++ b/gst/rtsp-server/rtsp-address-pool.h @@ -96,6 +96,14 @@ gboolean gst_rtsp_address_pool_add_range (GstRTSPAddressPool GstRTSPAddress * gst_rtsp_address_pool_acquire_address (GstRTSPAddressPool * pool, GstRTSPAddressFlags flags, gint n_ports); + +GstRTSPAddress * gst_rtsp_address_pool_reserve_address (GstRTSPAddressPool * pool, + const gchar *address, + guint port, + guint n_ports, + guint ttl); + + G_END_DECLS #endif /* __GST_RTSP_ADDRESS_POOL_H__ */ diff --git a/tests/check/gst/addresspool.c b/tests/check/gst/addresspool.c index 9df35c7..4152a44 100644 --- a/tests/check/gst/addresspool.c +++ b/tests/check/gst/addresspool.c @@ -24,7 +24,7 @@ GST_START_TEST (test_pool) { GstRTSPAddressPool *pool; - GstRTSPAddress *addr, *addr2; + GstRTSPAddress *addr, *addr2, *addr3; pool = gst_rtsp_address_pool_new (); @@ -81,6 +81,7 @@ GST_START_TEST (test_pool) GST_RTSP_ADDRESS_FLAG_IPV6 | GST_RTSP_ADDRESS_FLAG_EVEN_PORT, 2); fail_unless (addr != NULL); fail_unless (addr->port == 5002); + fail_unless (!g_ascii_strcasecmp (addr->address, "2001:DB8::1")); /* Will fail becuse there is only one IPv6 address left */ addr2 = gst_rtsp_address_pool_acquire_address (pool, @@ -103,15 +104,81 @@ GST_START_TEST (test_pool) GST_RTSP_ADDRESS_FLAG_EVEN_PORT, 2); fail_unless (addr != NULL); fail_unless (addr->port == 5000); + fail_unless (!strcmp (addr->address, "233.252.0.0")); addr2 = gst_rtsp_address_pool_acquire_address (pool, GST_RTSP_ADDRESS_FLAG_EVEN_PORT, 2); fail_unless (addr2 != NULL); fail_unless (addr2->port == 5000); + fail_unless (!strcmp (addr2->address, "233.252.0.1")); gst_rtsp_address_free (addr); gst_rtsp_address_free (addr2); + gst_rtsp_address_pool_clear (pool); + + + fail_unless (gst_rtsp_address_pool_add_range (pool, + "233.252.1.1", "233.252.1.1", 5000, 5001, 1)); + + addr = gst_rtsp_address_pool_reserve_address (pool, "233.252.1.1", 5000, 3, + 1); + fail_unless (addr == NULL); + + addr = gst_rtsp_address_pool_reserve_address (pool, "233.252.1.2", 5000, 2, + 1); + fail_unless (addr == NULL); + + addr = gst_rtsp_address_pool_reserve_address (pool, "233.252.1.1", 500, 2, 1); + fail_unless (addr == NULL); + + addr = gst_rtsp_address_pool_reserve_address (pool, "233.252.1.1", 5000, 2, + 2); + fail_unless (addr == NULL); + + addr = gst_rtsp_address_pool_reserve_address (pool, "2000::1", 5000, 2, 2); + fail_unless (addr == NULL); + + addr = gst_rtsp_address_pool_reserve_address (pool, "1.1", 5000, 2, 2); + fail_unless (addr == NULL); + + addr = gst_rtsp_address_pool_reserve_address (pool, "233.252.1.1", 5000, 2, + 1); + fail_unless (addr != NULL); + fail_unless (addr->port == 5000); + fail_unless (!strcmp (addr->address, "233.252.1.1")); + + gst_rtsp_address_free (addr); + gst_rtsp_address_pool_clear (pool); + + fail_unless (gst_rtsp_address_pool_add_range (pool, + "233.252.1.1", "233.252.1.3", 5000, 5001, 1)); + + addr = gst_rtsp_address_pool_reserve_address (pool, "233.252.1.1", 5000, 2, + 1); + fail_unless (addr != NULL); + fail_unless (addr->port == 5000); + fail_unless (!strcmp (addr->address, "233.252.1.1")); + + addr2 = gst_rtsp_address_pool_reserve_address (pool, "233.252.1.3", 5000, 2, + 1); + fail_unless (addr2 != NULL); + fail_unless (addr2->port == 5000); + fail_unless (!strcmp (addr2->address, "233.252.1.3")); + + addr3 = gst_rtsp_address_pool_acquire_address (pool, + GST_RTSP_ADDRESS_FLAG_EVEN_PORT, 2); + fail_unless (addr3 != NULL); + fail_unless (addr3->port == 5000); + fail_unless (!strcmp (addr3->address, "233.252.1.2")); + + fail_unless (gst_rtsp_address_pool_acquire_address (pool, + GST_RTSP_ADDRESS_FLAG_EVEN_PORT, 2) == NULL); + + gst_rtsp_address_free (addr); + gst_rtsp_address_free (addr2); + gst_rtsp_address_free (addr3); + g_object_unref (pool); } -- 2.7.4