|
|
|
@ -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); |
|
|
|
|
|
|
|
|