MT#55283 allow overlapping port ranges

Change-Id: I6df35b8cc0687567d7c3c0fd6d44979345dca364
pull/1910/head
Richard Fuchs 3 months ago
parent 1b7077152f
commit 416ca38d25

@ -879,7 +879,7 @@ static struct endpoint_map *__get_endpoint_map(struct call_media *media, unsigne
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Failed to set socket CPU "
"affinity: %s", strerror(errno));
}
sfd = stream_fd_new(&spl->socket, spl->link, media->call, il->local_intf);
sfd = stream_fd_new(&spl->socket, &spl->links, media->call, il->local_intf);
t_queue_push_tail(&em_il->list, sfd); // not referenced
g_free(spl);
}

@ -85,7 +85,7 @@ struct packet_handler_ctx {
struct late_port_release {
socket_t socket;
struct port_pool *pp;
ports_list *pp_link;
ports_q pp_links;
};
struct interface_stats_interval {
struct interface_stats_block stats;
@ -426,11 +426,12 @@ static int __name_family_eq(const struct intf_key *a, const struct intf_key *b);
static unsigned int __addr_type_hash(const struct intf_address *p);
static int __addr_type_eq(const struct intf_address *a, const struct intf_address *b);
TYPED_GQUEUE(intf_spec, struct intf_spec)
TYPED_GHASHTABLE(intf_lookup, struct intf_key, struct logical_intf, __name_family_hash, __name_family_eq,
g_free, NULL)
TYPED_GHASHTABLE(intf_rr_lookup, struct intf_key, struct intf_rr, __name_family_hash, __name_family_eq,
NULL, NULL)
TYPED_GHASHTABLE(intf_spec_ht, struct intf_address, struct intf_spec, __addr_type_hash, __addr_type_eq,
TYPED_GHASHTABLE(intf_spec_ht, struct intf_address, intf_spec_q, __addr_type_hash, __addr_type_eq,
NULL, NULL)
TYPED_GHASHTABLE(local_intf_ht, struct intf_address, local_intf_list, __addr_type_hash, __addr_type_eq,
NULL, NULL)
@ -680,29 +681,95 @@ int is_local_endpoint(const struct intf_address *addr, unsigned int port) {
return 0;
}
static void release_reserved_port(struct port_pool *pp, ports_q *);
/**
* This function just (globally) reserves a port number, it doesn't provide any binding/unbinding.
* Returns list link if successful, or NULL if failed.
* Returns linked list if successful, or NULL if failed.
*/
static ports_list *reserve_port(struct port_pool *pp, unsigned int port) {
static ports_q reserve_port(struct port_pool *pp, unsigned int port) {
ports_q ret = TYPED_GQUEUE_INIT;
if (port < pp->min || port > pp->max)
return NULL;
LOCK(&pp->free_list_lock);
__auto_type list = free_ports_link(pp, port);
if (!list)
return NULL;
t_queue_unlink(&pp->free_ports_q, list);
free_ports_link(pp, port) = NULL;
return list;
return ret;
{
LOCK(&pp->free_list_lock);
__auto_type link = free_ports_link(pp, port);
if (!link)
return ret;
// move link from free list to output
t_queue_unlink(&pp->free_ports_q, link);
free_ports_link(pp, port) = NULL;
t_queue_push_tail_link(&ret, link);
}
for (__auto_type l = pp->overlaps.head; l; l = l->next) {
__auto_type opp = l->data;
if (port < opp->min || port > opp->max)
continue;
LOCK(&opp->free_list_lock);
__auto_type link = free_ports_link(opp, port);
if (!link)
goto bail;
// move link from free list to output
t_queue_unlink(&opp->free_ports_q, link);
free_ports_link(opp, port) = NULL;
t_queue_push_tail_link(&ret, link);
}
return ret;
bail:
// Oops. Some spec didn't have the port available. Probably a race condition.
// Return everything to its place and report failure.
release_reserved_port(pp, &ret);
return ret;
}
/**
* This function just releases reserved port number, it doesn't provide any binding/unbinding.
*/
static void release_reserved_port(struct port_pool *pp, ports_list *link) {
LOCK(&pp->free_list_lock);
t_queue_push_tail_link(&pp->free_ports_q, link);
unsigned int port = GPOINTER_TO_UINT(link->data);
free_ports_link(pp, port) = link;
static void release_reserved_port(struct port_pool *pp, ports_q *list) {
// the list contains links in order:
// first port for port pool
// first port for first overlap pool
// first port for second overlap pool
// first port ...
// second port for port pool
// second port for first overlap pool
// ...
while (list->length) {
// remove top link from list, which belongs to our port pool
__auto_type link = t_queue_pop_head_link(list);
unsigned int port = GPOINTER_TO_UINT(link->data);
{
LOCK(&pp->free_list_lock);
t_queue_push_tail_link(&pp->free_ports_q, link);
free_ports_link(pp, port) = link;
}
for (__auto_type l = pp->overlaps.head; l; l = l->next) {
if (!list->length)
return; // ran out of items to return
assert(port == GPOINTER_TO_UINT(t_queue_peek_head(list)));
pp = l->data;
if (port < pp->min || port > pp->max)
continue;
// remove top link from list
link = t_queue_pop_head_link(list);
LOCK(&pp->free_list_lock);
t_queue_push_tail_link(&pp->free_ports_q, link);
free_ports_link(pp, port) = link;
}
}
}
/* Append a list of free ports within the min-max range */
static void __append_free_ports_to_int(struct intf_spec *spec) {
@ -825,9 +892,24 @@ static void __interface_append(struct intf_config *ifa, sockfamily_t *fam, bool
}
}
spec = t_hash_table_lookup(__intf_spec_addr_type_hash, &ifa->local_address);
// make sure hash table entry exists
__auto_type spec_q = t_hash_table_lookup(__intf_spec_addr_type_hash, &ifa->local_address);
if (!spec_q) {
spec_q = intf_spec_q_new();
t_hash_table_insert(__intf_spec_addr_type_hash, &ifa->local_address, spec_q);
}
// look for existing spec with matching port range
spec = NULL;
for (__auto_type l = spec_q->head; l; l = l->next) {
spec = l->data;
if (spec->port_pool.min == ifa->port_min && spec->port_pool.max == ifa->port_max)
break;
spec = NULL;
}
if (!spec) {
// create one if not found
if (ifa->port_min == 0 || ifa->port_max == 0 || ifa->port_min > 65535
|| ifa->port_max > 65535 || ifa->port_min > ifa->port_max)
die("Invalid RTP port range (%d > %d)", ifa->port_min, ifa->port_max);
@ -847,23 +929,23 @@ static void __interface_append(struct intf_config *ifa, sockfamily_t *fam, bool
unsigned int port = GPOINTER_TO_UINT(l->data);
if (port > 65535)
continue;
__auto_type ll = reserve_port(&spec->port_pool, port);
if (ll)
t_list_free(ll);
__auto_type pq = reserve_port(&spec->port_pool, port);
t_queue_clear(&pq);
}
t_hash_table_insert(__intf_spec_addr_type_hash, &spec->local_address, spec);
}
else {
if (spec->port_pool.min != ifa->port_min
|| spec->port_pool.max != ifa->port_max)
{
ilog(LOG_ERR, "Ignoring mismatched port range (%d > %d) on new "
"interface '" STR_FORMAT "', keeping existing "
"port range %d > %d", ifa->port_min, ifa->port_max,
STR_FMT(&ifa->name), spec->port_pool.min,
spec->port_pool.max);
// look for other specs with overlapping port ranges
for (__auto_type l = spec_q->head; l; l = l->next) {
__auto_type os = l->data;
if (os->port_pool.min > ifa->port_max)
continue;
if (os->port_pool.max < ifa->port_min)
continue;
// track overlap
t_queue_push_tail(&spec->port_pool.overlaps, &os->port_pool);
t_queue_push_tail(&os->port_pool.overlaps, &spec->port_pool);
}
t_queue_push_tail(spec_q, spec);
}
ifc = uid_alloc(&lif->list);
@ -915,13 +997,9 @@ void interfaces_init(intf_config_q *interfaces) {
}
void interfaces_exclude_port(endpoint_t *e) {
struct intf_spec *spec;
struct port_pool *pp;
intf_spec_ht_iter iter;
t_hash_table_iter_init(&iter, __intf_spec_addr_type_hash);
while (t_hash_table_iter_next(&iter, NULL, &spec)) {
for (__auto_type l = all_local_interfaces.head; l; l = l->next) {
__auto_type ifa = l->data;
__auto_type spec = ifa->spec;
if (e->address.family != spec->local_address.addr.family)
continue;
if (!is_addr_unspecified(&e->address)) {
@ -929,13 +1007,12 @@ void interfaces_exclude_port(endpoint_t *e) {
continue;
}
pp = &spec->port_pool;
__auto_type pp = &ifa->spec->port_pool;
if (e->port < pp->min || e->port > pp->max)
continue;
__auto_type ll = reserve_port(pp, e->port);
if (ll)
t_list_free(ll);
__auto_type pq = reserve_port(pp, e->port);
t_queue_clear(&pq);
}
}
@ -987,13 +1064,13 @@ static void release_port_push(void *p) {
__C_DBG("Adding the port '%u' to late-release list", lpr->socket.local.port);
t_queue_push_tail(&ports_to_release, lpr);
}
static void release_port_poller(socket_t *r, ports_list *link, struct port_pool *pp, struct poller *poller) {
static void release_port_poller(socket_t *r, ports_q *links, struct port_pool *pp, struct poller *poller) {
if (!r->local.port || r->fd == -1)
return;
struct late_port_release *lpr = g_slice_alloc(sizeof(*lpr));
move_socket(&lpr->socket, r);
lpr->pp = pp;
lpr->pp_link = link;
lpr->pp_links = *links;
if (!poller)
release_port_push(lpr);
else {
@ -1001,18 +1078,18 @@ static void release_port_poller(socket_t *r, ports_list *link, struct port_pool
rtpe_poller_del_item_callback(poller, lpr->socket.fd, release_port_push, lpr);
}
}
static void release_port(socket_t *r, ports_list *link, struct port_pool *pp) {
release_port_poller(r, link, pp, NULL);
static void release_port(socket_t *r, ports_q *links, struct port_pool *pp) {
release_port_poller(r, links, pp, NULL);
}
static void free_port(struct socket_port_link *spl, struct port_pool *pp) {
release_port(&spl->socket, spl->link, pp);
release_port(&spl->socket, &spl->links, pp);
g_free(spl);
}
/**
* Logic responsible for devastating the `ports_to_release` queue.
* It's being called by main poller.
*/
static void release_port_now(socket_t *r, ports_list *link, struct port_pool *pp) {
static void release_port_now(socket_t *r, ports_q *list, struct port_pool *pp) {
unsigned int port = r->local.port;
__C_DBG("Trying to release the port '%u'", port);
@ -1023,7 +1100,7 @@ static void release_port_now(socket_t *r, ports_list *link, struct port_pool *pp
iptables_del_rule(r);
/* first return the engaged port back */
release_reserved_port(pp, link);
release_reserved_port(pp, list);
} else {
ilog(LOG_WARNING, "Unable to close the socket for port '%u'", port);
}
@ -1047,7 +1124,7 @@ enum thread_looper_action release_closed_sockets(void) {
mutex_unlock(&ports_to_release_glob_lock);
while ((lpr = t_queue_pop_head(&ports_left))) {
release_port_now(&lpr->socket, lpr->pp_link, lpr->pp);
release_port_now(&lpr->socket, &lpr->pp_links, lpr->pp);
g_slice_free1(sizeof(*lpr), lpr);
}
}
@ -1075,7 +1152,7 @@ int __get_consecutive_ports(socket_port_q *out, unsigned int num_ports, unsigned
struct intf_spec *spec, const str *label)
{
unsigned int allocation_attempts = 0, available_ports = 0, additional_port = 0, port = 0;
ports_list *port_link = NULL;
ports_q all_ports = TYPED_GQUEUE_INIT;
ports_q ports_to_engage = TYPED_GQUEUE_INIT; /* usually it's only one RTCP port, theoretically can be more */
struct port_pool * pp = &spec->port_pool; /* port pool for a given local interface */
@ -1104,8 +1181,8 @@ int __get_consecutive_ports(socket_port_q *out, unsigned int num_ports, unsigned
/* specifically requested port */
if (wanted_start_port > 0) {
ilog(LOG_DEBUG, "A specific port value is requested, wanted_start_port: '%d'", wanted_start_port);
port_link = reserve_port(pp, wanted_start_port);
if (!port_link) {
all_ports = reserve_port(pp, wanted_start_port);
if (!all_ports.length) {
/* if engaged already, just select any other (so default logic) */
ilog(LOG_WARN, "This requested port has been already engaged, can't take it.");
wanted_start_port = 0; /* take what is proposed by FIFO instead */
@ -1157,7 +1234,7 @@ new_cycle:
* Then additionally make sure that the RTCP port can also be engaged, if needed.
*/
mutex_lock(&pp->free_list_lock);
port_link = t_queue_pop_head_link(free_ports_q);
__auto_type port_link = t_queue_pop_head_link(free_ports_q);
if (!port_link) {
mutex_unlock(&pp->free_list_lock);
@ -1169,10 +1246,12 @@ new_cycle:
free_ports_link(pp, port) = NULL;
mutex_unlock(&pp->free_list_lock);
t_queue_push_tail_link(&all_ports, port_link);
/* ports for RTP must be even, if there is an additional port for RTCP */
if (num_ports > 1 && (port & 1)) {
/* return port for RTP back and try again */
release_reserved_port(pp, port_link);
release_reserved_port(pp, &all_ports);
goto new_cycle;
}
@ -1182,24 +1261,19 @@ new_cycle:
{
additional_port++;
__auto_type add_link = reserve_port(pp, additional_port);
__auto_type add_port = reserve_port(pp, additional_port);
if (!add_link) {
if (!add_port.length) {
/* return port for RTP back and try again */
release_reserved_port(pp, port_link);
/* check if we managed to enagage anything in previous for-cycles */
while ((add_link = t_queue_pop_head_link(&ports_to_engage)))
{
/* return additional ports back */
release_reserved_port(pp, add_link);
}
release_reserved_port(pp, &all_ports);
/* return additional ports back */
release_reserved_port(pp, &ports_to_engage);
goto new_cycle;
}
/* engage this port right away */
/* track for which additional ports, we have to open sockets */
t_queue_push_tail_link(&ports_to_engage, add_link);
t_queue_move(&ports_to_engage, &add_port);
}
}
@ -1207,25 +1281,27 @@ new_cycle:
allocation_attempts);
/* at this point we consider all things before as successfull. Now just add the RTP port */
t_queue_push_head_link(&ports_to_engage, port_link);
t_queue_move(&all_ports, &ports_to_engage);
struct socket_port_link *spl;
while ((port_link = t_queue_pop_head_link(&ports_to_engage)))
{
while (all_ports.length) {
__auto_type port_link = t_queue_pop_head_link(&all_ports);
port = GPOINTER_TO_UINT(port_link->data);
ilog(LOG_DEBUG, "Trying to bind the socket for port = '%d'", port);
spl = g_new0(struct socket_port_link, 1);
spl->socket.fd = -1;
spl->link = port_link;
t_queue_push_tail_link(&spl->links, port_link);
t_queue_push_tail(out, spl);
// append other links belonging to the same port
while (all_ports.length && GPOINTER_TO_UINT(t_queue_peek_head(&all_ports)) == port) {
port_link = t_queue_pop_head_link(&all_ports);
t_queue_push_tail_link(&spl->links, port_link);
}
/* if not possible to engage this socket, try to reallocate it again */
if (!add_socket(&spl->socket, port, spec, label)) {
/* if something has been left in the `ports_to_engage` queue, release it right away */
while ((port_link = t_queue_pop_head(&ports_to_engage)))
{
release_reserved_port(pp, port_link);
}
release_reserved_port(pp, &all_ports);
/* ports which are already bound to a socket, will be freed by `free_port()` */
goto release_restart;
}
@ -3125,14 +3201,14 @@ out:
static void stream_fd_free(stream_fd *f) {
release_port(&f->socket, f->port_pool_link, &f->local_intf->spec->port_pool);
release_port(&f->socket, &f->port_pool_links, &f->local_intf->spec->port_pool);
crypto_cleanup(&f->crypto);
dtls_connection_cleanup(&f->dtls);
obj_put(f->call);
}
stream_fd *stream_fd_new(socket_t *fd, ports_list *link, call_t *call, struct local_intf *lif) {
stream_fd *stream_fd_new(socket_t *fd, ports_q *links, call_t *call, struct local_intf *lif) {
stream_fd *sfd;
struct poller_item pi;
@ -3141,7 +3217,8 @@ stream_fd *stream_fd_new(socket_t *fd, ports_list *link, call_t *call, struct lo
sfd->socket = *fd;
sfd->call = obj_get(call);
sfd->local_intf = lif;
sfd->port_pool_link = link;
if (links)
sfd->port_pool_links = *links;
t_queue_push_tail(&call->stream_fds, sfd); /* hand over ref */
__C_DBG("stream_fd_new localport=%d", sfd->socket.local.port);
@ -3190,7 +3267,7 @@ void stream_fd_release(stream_fd *sfd) {
&sfd->socket.local); // releases reference
}
release_port_poller(&sfd->socket, sfd->port_pool_link, &sfd->local_intf->spec->port_pool, sfd->poller);
release_port_poller(&sfd->socket, &sfd->port_pool_links, &sfd->local_intf->spec->port_pool, sfd->poller);
}
@ -3243,13 +3320,18 @@ void interfaces_free(void) {
intf_spec_ht_iter s_iter;
t_hash_table_iter_init(&s_iter, __intf_spec_addr_type_hash);
struct intf_spec *spec;
while (t_hash_table_iter_next(&s_iter, NULL, &spec)) {
struct port_pool *pp = &spec->port_pool;
t_queue_clear(&pp->free_ports_q);
mutex_destroy(&pp->free_list_lock);
g_free(pp->free_ports);
g_slice_free1(sizeof(*spec), spec);
intf_spec_q *spec_q;
while (t_hash_table_iter_next(&s_iter, NULL, &spec_q)) {
while (spec_q->length) {
__auto_type spec = t_queue_pop_head(spec_q);
struct port_pool *pp = &spec->port_pool;
t_queue_clear(&pp->free_ports_q);
mutex_destroy(&pp->free_list_lock);
t_queue_clear(&pp->overlaps);
g_free(pp->free_ports);
g_slice_free1(sizeof(*spec), spec);
}
t_queue_free(spec_q);
}
t_hash_table_destroy(__intf_spec_addr_type_hash);

@ -1421,7 +1421,7 @@ static int redis_sfds(call_t *c, struct redis_list *sfds) {
goto err;
struct socket_port_link *spl = NULL;
ports_list *link = NULL;
ports_q *links = NULL;
if (fd != -1) {
err = "failed to open ports";
@ -1432,14 +1432,14 @@ static int redis_sfds(call_t *c, struct redis_list *sfds) {
if (!spl)
goto err;
sock = &spl->socket;
link = spl->link;
links = &spl->links;
set_tos(sock, c->tos);
}
else {
sock = &local_sock;
dummy_socket(sock, &loc->spec->local_address.addr);
}
sfd = stream_fd_new(sock, link, c, loc);
sfd = stream_fd_new(sock, links, c, loc);
if (spl)
g_free(spl);

@ -1697,10 +1697,10 @@ Non-alias interfaces support the additional option __advertised__ to set the
advertised address, as well as __port-min__ and __port-max__ to set a port
range different from the global setting, and __exclude-ports__ taking a list of
semicolon-separated port numbers to exclude individual ports from the port
range. Note that all interfaces sharing a single IP address must use the same
port range. Round-robin interface usage is supported in the same way as
described above, i.e. by using a colon and a suffix as part of the interface
name.
range. Multiple interfaces sharing the same IP address are allowed to use
different, possibly overlapping, port ranges. Round-robin interface usage is
supported in the same way as described above, i.e. by using a colon and a
suffix as part of the interface name.
Interface sections are processed in order, and as such the first one listed
becomes the default interface. If both legacy syntax and new configuration-file

@ -90,9 +90,10 @@ TYPED_GQUEUE(ports, port_t)
struct socket_port_link {
socket_t socket;
ports_list *link;
ports_q links;
};
TYPED_GQUEUE(port_pool, struct port_pool)
struct port_pool {
unsigned int min, max;
@ -100,6 +101,8 @@ struct port_pool {
ports_q free_ports_q; /* for getting the next free port */
ports_list **free_ports; /* for a lookup if the port is used */
port_pool_q overlaps;
};
#define free_ports_link(pp, port) ((pp)->free_ports[port - (pp)->min])
@ -213,7 +216,7 @@ struct stream_fd {
unsigned int unique_id; /* RO */
socket_t socket; /* RO */
struct local_intf *local_intf; /* RO */
ports_list *port_pool_link; /* RO */
ports_q port_pool_links; /* RO */
/* stream_fd object holds a reference to the call it belongs to.
* Which in turn holds references to all stream_fd objects it contains,
@ -292,7 +295,7 @@ int is_local_endpoint(const struct intf_address *addr, unsigned int port);
int __get_consecutive_ports(socket_port_q *out, unsigned int num_ports, unsigned int wanted_start_port,
struct intf_spec *spec, const str *);
int get_consecutive_ports(socket_intf_list_q *out, unsigned int num_ports, unsigned int num_intfs, struct call_media *media);
stream_fd *stream_fd_new(socket_t *fd, ports_list *link, call_t *call, struct local_intf *lif);
stream_fd *stream_fd_new(socket_t *fd, ports_q *links, call_t *call, struct local_intf *lif);
stream_fd *stream_fd_lookup(const endpoint_t *);
void stream_fd_release(stream_fd *);
enum thread_looper_action release_closed_sockets(void);

@ -105,6 +105,124 @@ SDP
new_call;
# A: 4 port pairs, 2 unique
# B: 4 port pairs, 2 unique
my @ports_a1 = publish('overlap intf A1', { interface => 'overlap-A' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 172.17.0.2
s=tester
c=IN IP4 198.51.100.19
t=0 0
m=audio 6000 RTP/AVP 0 8
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.6
s=RTPE_VERSION
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.6
a=rtpmap:0 PCMU/8000
a=recvonly
a=rtcp:PORT
SDP
is($ports_a1[0], $ports_a1[1] - 1, 'RTP/RTCP');
cmp_ok($ports_a1[0], '>=', 3000, 'range OK');
cmp_ok($ports_a1[1], '<=', 3007, 'range OK');
# A: 3 port pairs, 1 or 2 unique
# B: 3 or 4 port pairs, 2 unique
new_call;
my @ports_a2 = publish('overlap intf A2', { interface => 'overlap-A' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 172.17.0.2
s=tester
c=IN IP4 198.51.100.19
t=0 0
m=audio 6000 RTP/AVP 0 8
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.6
s=RTPE_VERSION
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.6
a=rtpmap:0 PCMU/8000
a=recvonly
a=rtcp:PORT
SDP
is($ports_a2[0], $ports_a2[1] - 1, 'RTP/RTCP');
cmp_ok($ports_a2[0], '>=', 3000, 'range OK');
cmp_ok($ports_a2[1], '<=', 3007, 'range OK');
isnt($ports_a2[0], $ports_a1[0], 'unique port');
# A: 2 port pairs, 0-2 unique
# B: 2-4 port pairs, 2 unique
new_call;
my @ports_b1 = publish('overlap intf B1', { interface => 'overlap-B' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 172.17.0.2
s=tester
c=IN IP4 198.51.100.19
t=0 0
m=audio 6000 RTP/AVP 0 8
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.6
s=RTPE_VERSION
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.6
a=rtpmap:0 PCMU/8000
a=recvonly
a=rtcp:PORT
SDP
is($ports_b1[0], $ports_b1[1] - 1, 'RTP/RTCP');
cmp_ok($ports_b1[0], '>=', 3004, 'range OK');
cmp_ok($ports_b1[1], '<=', 3011, 'range OK');
isnt($ports_b1[0], $ports_a1[0], 'unique port');
isnt($ports_b1[0], $ports_a2[0], 'unique port');
# A: 2 port pairs, 0-2 unique
# B: 1-3 port pairs, 1 or 2 unique
new_call;
my @ports_b2 = publish('overlap intf B2', { interface => 'overlap-B' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 172.17.0.2
s=tester
c=IN IP4 198.51.100.19
t=0 0
m=audio 6000 RTP/AVP 0 8
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.6
s=RTPE_VERSION
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.6
a=rtpmap:0 PCMU/8000
a=recvonly
a=rtcp:PORT
SDP
is($ports_b2[0], $ports_b2[1] - 1, 'RTP/RTCP');
cmp_ok($ports_b2[0], '>=', 3004, 'range OK');
cmp_ok($ports_b2[1], '<=', 3011, 'range OK');
isnt($ports_b2[0], $ports_a1[0], 'unique port');
isnt($ports_b2[0], $ports_a2[0], 'unique port');
isnt($ports_b2[0], $ports_b1[0], 'unique port');
#done_testing;NGCP::Rtpengine::AutoTest::terminate('f00');exit;
done_testing();

@ -6,6 +6,17 @@ foreground = true
log-level = 7
log-stderr = true
templates = templates
interfaces-config = interface
[templates]
WebRTC = transport-protocol=UDP/TLS/RTP/SAVPF ICE=force trickle-ICE rtcp-mux=[offer require] no-rtcp-attribute SDES=off generate-mid
[interface-overlap-A]
address = 203.0.113.6
port-min = 3000
port-max = 3007
[interface-overlap-B]
address = 203.0.113.6
port-min = 3004
port-max = 3011

Loading…
Cancel
Save