From 416ca38d253d09f7b31a09b33f09b54c3cc65dea Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Thu, 6 Feb 2025 15:34:09 -0400 Subject: [PATCH] MT#55283 allow overlapping port ranges Change-Id: I6df35b8cc0687567d7c3c0fd6d44979345dca364 --- daemon/call.c | 2 +- daemon/media_socket.c | 254 +++++++++++++++++++---------- daemon/redis.c | 6 +- docs/rtpengine.md | 8 +- include/media_socket.h | 9 +- t/auto-daemon-tests-config-file.pl | 118 ++++++++++++++ t/test1.conf | 11 ++ 7 files changed, 311 insertions(+), 97 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index ecd36721b..049412904 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -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); } diff --git a/daemon/media_socket.c b/daemon/media_socket.c index 2828204d6..75032aa3d 100644 --- a/daemon/media_socket.c +++ b/daemon/media_socket.c @@ -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); diff --git a/daemon/redis.c b/daemon/redis.c index 6bc365ad1..2ab264680 100644 --- a/daemon/redis.c +++ b/daemon/redis.c @@ -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); diff --git a/docs/rtpengine.md b/docs/rtpengine.md index d91f27333..90db41f31 100644 --- a/docs/rtpengine.md +++ b/docs/rtpengine.md @@ -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 diff --git a/include/media_socket.h b/include/media_socket.h index 0bbb1b5ad..8b38a9f57 100644 --- a/include/media_socket.h +++ b/include/media_socket.h @@ -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); diff --git a/t/auto-daemon-tests-config-file.pl b/t/auto-daemon-tests-config-file.pl index 640482749..8e61d6da5 100755 --- a/t/auto-daemon-tests-config-file.pl +++ b/t/auto-daemon-tests-config-file.pl @@ -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' }, <=', 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' }, <=', 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' }, <=', 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' }, <=', 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(); diff --git a/t/test1.conf b/t/test1.conf index e29f5782f..6b192880b 100644 --- a/t/test1.conf +++ b/t/test1.conf @@ -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