Commit 467acc5f authored by Daniel Salzman's avatar Daniel Salzman

processing: remove UCW list from the API, refactor usage

parent 5c9cda00
......@@ -65,20 +65,28 @@ static int dnsproxy_fwd(int state, knot_pkt_t *pkt, struct query_data *qdata, vo
/* Create a forwarding request. */
struct knot_requestor re;
knot_requestor_init(&re, qdata->mm);
struct capture_param param;
param.sink = pkt;
int ret = knot_requestor_overlay(&re, LAYER_CAPTURE, &param);
int ret = knot_requestor_init(&re, qdata->mm);
if (ret != KNOT_EOK) {
return KNOT_STATE_FAIL;
return state; /* Ignore, not enough memory. */
}
struct capture_param param = {
.sink = pkt
};
ret = knot_requestor_overlay(&re, LAYER_CAPTURE, &param);
if (ret != KNOT_EOK) {
knot_requestor_clear(&re);
return state; /* Ignore, not enough memory. */
}
bool is_tcp = net_is_stream(qdata->param->socket);
struct knot_request *req;
const struct sockaddr *dst = (const struct sockaddr *)&proxy->remote.addr;
const struct sockaddr *src = (const struct sockaddr *)&proxy->remote.via;
req = knot_request_make(re.mm, dst, src, qdata->query, is_tcp ? 0 : KNOT_RQ_UDP);
struct knot_request *req = knot_request_make(re.mm, dst, src, qdata->query,
is_tcp ? 0 : KNOT_RQ_UDP);
if (req == NULL) {
knot_requestor_clear(&re);
return state; /* Ignore, not enough memory. */
}
......@@ -89,7 +97,7 @@ static int dnsproxy_fwd(int state, knot_pkt_t *pkt, struct query_data *qdata, vo
struct timeval tv = { conf_int(&val), 0 };
ret = knot_requestor_exec(&re, &tv);
} else {
knot_request_free(re.mm, req);
knot_request_free(req, re.mm);
}
knot_requestor_clear(&re);
......
......@@ -92,8 +92,9 @@ static int process_single_update(struct knot_request *request,
static void set_rcodes(list_t *requests, const uint16_t rcode)
{
struct knot_request *req;
WALK_LIST(req, *requests) {
ptrnode_t *node = NULL;
WALK_LIST(node, *requests) {
struct knot_request *req = node->d;
if (knot_wire_get_rcode(req->resp->wire) == KNOT_RCODE_NOERROR) {
knot_wire_set_rcode(req->resp->wire, rcode);
}
......@@ -108,11 +109,13 @@ static void store_original_qname(struct query_data *qdata, const knot_pkt_t *pkt
static int process_bulk(zone_t *zone, list_t *requests, zone_update_t *up)
{
// Walk all the requests and process.
struct knot_request *req;
WALK_LIST(req, *requests) {
ptrnode_t *node = NULL;
WALK_LIST(node, *requests) {
struct knot_request *req = node->d;
// Init qdata structure for logging (unique per-request).
struct process_query_param param = { 0 };
param.remote = &req->remote;
struct process_query_param param = {
.remote = &req->remote
};
struct query_data qdata;
init_qdata_from_request(&qdata, zone, req, &param);
......@@ -195,11 +198,10 @@ static int process_normal(zone_t *zone, list_t *requests)
return KNOT_EOK;
}
static int process_requests(zone_t *zone, list_t *requests)
static void process_requests(zone_t *zone, list_t *requests)
{
if (zone == NULL || requests == NULL) {
return KNOT_EINVAL;
}
assert(zone);
assert(requests);
/* Keep original state. */
struct timeval t_start, t_end;
......@@ -211,14 +213,14 @@ static int process_requests(zone_t *zone, list_t *requests)
if (ret != KNOT_EOK) {
log_zone_error(zone->name, "DDNS, processing failed (%s)",
knot_strerror(ret));
return ret;
return;
}
/* Evaluate response. */
const uint32_t new_serial = zone_contents_serial(zone->contents);
if (new_serial == old_serial) {
log_zone_info(zone->name, "DDNS, finished, no changes to the zone were made");
return KNOT_EOK;
return;
}
gettimeofday(&t_end, NULL);
......@@ -227,23 +229,68 @@ static int process_requests(zone_t *zone, list_t *requests)
time_diff(&t_start, &t_end) / 1000.0);
zone_events_schedule(zone, ZONE_EVENT_NOTIFY, ZONE_EVENT_NOW);
return KNOT_EOK;
}
static int forward_request(zone_t *zone, struct knot_request *request)
static int remote_forward(struct knot_request *request, conf_remote_t *remote)
{
/* Copy request and assign new ID. */
knot_pkt_t *query = knot_pkt_new(NULL, request->query->max_size, NULL);
int ret = knot_pkt_copy(query, request->query);
if (ret != KNOT_EOK) {
knot_pkt_free(&query);
knot_wire_set_rcode(request->resp->wire, KNOT_RCODE_SERVFAIL);
return ret;
}
knot_wire_set_id(query->wire, dnssec_random_uint16_t());
knot_tsig_append(query->wire, &query->size, query->max_size, query->tsig_rr);
/* Create requestor instance. */
struct knot_requestor re;
ret = knot_requestor_init(&re, NULL);
if (ret != KNOT_EOK) {
knot_pkt_free(&query);
return ret;
}
/* Prepare packet capture layer. */
struct capture_param param = {
.sink = request->resp
};
ret = knot_requestor_overlay(&re, LAYER_CAPTURE, &param);
if (ret != KNOT_EOK) {
knot_requestor_clear(&re);
knot_pkt_free(&query);
return ret;
}
/* Create a request. */
const struct sockaddr *dst = (const struct sockaddr *)&remote->addr;
const struct sockaddr *src = (const struct sockaddr *)&remote->via;
struct knot_request *req = knot_request_make(re.mm, dst, src, query, 0);
if (req == NULL) {
knot_requestor_clear(&re);
knot_pkt_free(&query);
return KNOT_ENOMEM;
}
/* Enqueue the request. */
ret = knot_requestor_enqueue(&re, req);
if (ret == KNOT_EOK) {
conf_val_t val = conf_get(conf(), C_SRV, C_TCP_REPLY_TIMEOUT);
struct timeval tv = { conf_int(&val), 0 };
ret = knot_requestor_exec(&re, &tv);
} else {
knot_request_free(req, re.mm);
}
knot_requestor_clear(&re);
return ret;
}
static void forward_request(zone_t *zone, struct knot_request *request)
{
/* Read the ddns master or the first master. */
conf_val_t remote = conf_zone_get(conf(), C_DDNS_MASTER, zone->name);
if (remote.code != KNOT_EOK) {
......@@ -253,41 +300,14 @@ static int forward_request(zone_t *zone, struct knot_request *request)
/* Get the number of remote addresses. */
conf_val_t addr = conf_id_get(conf(), C_RMT, C_ADDR, &remote);
size_t addr_count = conf_val_count(&addr);
assert(addr_count > 0);
/* Try all remote addresses to forward the request to. */
int ret = KNOT_EOK;
for (size_t i = 0; i < addr_count; i++) {
conf_remote_t master = conf_remote(conf(), &remote, i);
/* Create requestor instance. */
struct knot_requestor re;
knot_requestor_init(&re, NULL);
/* Prepare packet capture layer. */
struct capture_param param;
param.sink = request->resp;
knot_requestor_overlay(&re, LAYER_CAPTURE, &param);
/* Create a request. */
const struct sockaddr *dst = (const struct sockaddr *)&master.addr;
const struct sockaddr *src = (const struct sockaddr *)&master.via;
struct knot_request *req = knot_request_make(re.mm, dst, src, query, 0);
if (req == NULL) {
knot_pkt_free(&query);
return KNOT_ENOMEM;
}
/* Enqueue the request. */
ret = knot_requestor_enqueue(&re, req);
if (ret != KNOT_EOK) {
knot_requestor_clear(&re);
continue;
}
/* Execute the request. */
conf_val_t val = conf_get(conf(), C_SRV, C_TCP_REPLY_TIMEOUT);
struct timeval tv = { conf_int(&val), 0 };
ret = knot_requestor_exec(&re, &tv);
knot_requestor_clear(&re);
ret = remote_forward(request, &master);
if (ret == KNOT_EOK) {
break;
}
......@@ -306,14 +326,16 @@ static int forward_request(zone_t *zone, struct knot_request *request)
} else {
log_zone_info(zone->name, "DDNS, updates forwarded to the master");
}
return ret;
}
static void forward_requests(zone_t *zone, list_t *requests)
{
struct knot_request *req;
WALK_LIST(req, *requests) {
assert(zone);
assert(requests);
ptrnode_t *node = NULL;
WALK_LIST(node, *requests) {
struct knot_request *req = node->d;
forward_request(zone, req);
}
}
......@@ -377,21 +399,21 @@ static void free_request(struct knot_request *req)
static void send_update_responses(const zone_t *zone, list_t *updates)
{
struct knot_request *req;
node_t *nxt = NULL;
WALK_LIST_DELSAFE(req, nxt, *updates) {
ptrnode_t *node = NULL, *nxt = NULL;
WALK_LIST_DELSAFE(node, nxt, *updates) {
struct knot_request *req = node->d;
send_update_response(zone, req);
free_request(req);
}
init_list(updates);
ptrlist_free(updates, NULL);
}
static int init_update_responses(const zone_t *zone, list_t *updates,
size_t *update_count)
{
struct knot_request *req = NULL;
node_t *nxt = NULL;
WALK_LIST_DELSAFE(req, nxt, *updates) {
ptrnode_t *node = NULL, *nxt = NULL;
WALK_LIST_DELSAFE(node, nxt, *updates) {
struct knot_request *req = node->d;
req->resp = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
if (req->resp == NULL) {
return KNOT_ENOMEM;
......@@ -404,8 +426,10 @@ static int init_update_responses(const zone_t *zone, list_t *updates,
continue;
}
struct process_query_param param = { 0 };
param.remote = &req->remote;
struct process_query_param param = {
.remote = &req->remote
};
struct query_data qdata;
init_qdata_from_request(&qdata, zone, req, &param);
......@@ -414,6 +438,7 @@ static int init_update_responses(const zone_t *zone, list_t *updates,
send_update_response(zone, req);
// Remove this request from processing list.
free_request(req);
ptrlist_rem(node, NULL);
*update_count -= 1;
}
}
......@@ -448,13 +473,13 @@ int update_query_process(knot_pkt_t *pkt, struct query_data *qdata)
return KNOT_STATE_DONE;
}
int updates_execute(zone_t *zone)
void updates_execute(zone_t *zone)
{
/* Get list of pending updates. */
list_t updates;
size_t update_count = zone_update_dequeue(zone, &updates);
if (update_count == 0) {
return KNOT_EOK;
return;
}
/* Block config changes. */
......@@ -467,16 +492,17 @@ int updates_execute(zone_t *zone)
set_rcodes(&updates, KNOT_RCODE_SERVFAIL);
send_update_responses(zone, &updates);
rcu_read_unlock();
return ret;
return;
}
if (update_count == 0) {
/* All updates failed their ACL checks. */
rcu_read_unlock();
return KNOT_EOK;
return;
}
/* Process update list - forward if zone has master, or execute. */
/* Process update list - forward if zone has master, or execute.
RCODEs are set. */
if (zone_is_slave(zone)) {
log_zone_info(zone->name,
"DDNS, forwarding %zu updates", update_count);
......@@ -484,13 +510,11 @@ int updates_execute(zone_t *zone)
} else {
log_zone_info(zone->name,
"DDNS, processing %zu updates", update_count);
ret = process_requests(zone, &updates);
process_requests(zone, &updates);
}
UNUSED(ret); /* Don't care about the Knot code, RCODEs are set. */
/* Send responses. */
send_update_responses(zone, &updates);
rcu_read_unlock();
return KNOT_EOK;
}
......@@ -44,11 +44,11 @@ int update_query_process(knot_pkt_t *pkt, struct query_data *qdata);
* \brief Processes serialized packet with DDNS. Function expects that the
* query is already authenticated and TSIG signature is verified.
*
* \note RCODEs are set.
*
* \param pkt Prepared response packet.
* \param qdata Minimal query data context.
*
* \return KNOT_E*
*/
int updates_execute(zone_t *zone);
void updates_execute(zone_t *zone);
/*! @} */
......@@ -132,14 +132,21 @@ static int tcp_handle(tcp_context_t *tcp, int fd,
rx->iov_len = ret;
}
/* Create packets. */
mm_ctx_t *mm = tcp->overlay.mm;
knot_pkt_t *ans = knot_pkt_new(tx->iov_base, tx->iov_len, mm);
knot_pkt_t *query = knot_pkt_new(rx->iov_base, rx->iov_len, mm);
/* Initialize processing overlay. */
knot_overlay_init(&tcp->overlay, mm);
knot_overlay_add(&tcp->overlay, NS_PROC_QUERY, &param);
ret = knot_overlay_init(&tcp->overlay, mm);
if (ret != KNOT_EOK) {
return ret;
}
ret = knot_overlay_add(&tcp->overlay, NS_PROC_QUERY, &param);
if (ret != KNOT_EOK) {
return ret;
}
/* Create packets. */
knot_pkt_t *ans = knot_pkt_new(tx->iov_base, tx->iov_len, mm);
knot_pkt_t *query = knot_pkt_new(rx->iov_base, rx->iov_len, mm);
/* Input packet. */
(void) knot_pkt_parse(query, 0);
......
......@@ -122,15 +122,16 @@ void udp_handle(udp_context_t *udp, int fd, struct sockaddr_storage *ss,
param.proc_flags |= NS_QUERY_LIMIT_RATE;
}
/* Create packets. */
mm_ctx_t *mm = udp->overlay.mm;
knot_pkt_t *query = knot_pkt_new(rx->iov_base, rx->iov_len, mm);
knot_pkt_t *ans = knot_pkt_new(tx->iov_base, tx->iov_len, mm);
/* Create query processing context. */
knot_overlay_init(&udp->overlay, mm);
knot_overlay_add(&udp->overlay, NS_PROC_QUERY, &param);
/* Create packets. */
knot_pkt_t *query = knot_pkt_new(rx->iov_base, rx->iov_len, mm);
knot_pkt_t *ans = knot_pkt_new(tx->iov_base, tx->iov_len, mm);
/* Input packet. */
(void) knot_pkt_parse(query, 0);
int state = knot_overlay_consume(&udp->overlay, query);
......
......@@ -18,7 +18,6 @@
#include "dnssec/random.h"
#include "libknot/libknot.h"
#include "libknot/internal/macros.h"
#include "libknot/processing/requestor.h"
#include "libknot/yparser/yptrafo.h"
#include "contrib/ucw/mempool.h"
......@@ -130,6 +129,47 @@ static int prepare_edns(zone_t *zone, knot_pkt_t *pkt)
return KNOT_EOK;
}
/*! \brief Process query using requestor. */
static int zone_query_request(knot_pkt_t *query, const conf_remote_t *remote,
struct process_answer_param *param, mm_ctx_t *mm)
{
/* Create requestor instance. */
struct knot_requestor re;
int ret = knot_requestor_init(&re, mm);
if (ret != KNOT_EOK) {
return ret;
}
ret = knot_requestor_overlay(&re, KNOT_STATE_ANSWER, param);
if (ret != KNOT_EOK) {
knot_requestor_clear(&re);
return ret;
}
/* Create a request. */
const struct sockaddr *dst = (const struct sockaddr *)&remote->addr;
const struct sockaddr *src = (const struct sockaddr *)&remote->via;
struct knot_request *req = knot_request_make(re.mm, dst, src, query, 0);
if (req == NULL) {
knot_requestor_clear(&re);
return KNOT_ENOMEM;
}
/* Send the queries and process responses. */
ret = knot_requestor_enqueue(&re, req);
if (ret == KNOT_EOK) {
conf_val_t val = conf_get(conf(), C_SRV, C_TCP_REPLY_TIMEOUT);
struct timeval tv = { conf_int(&val), 0 };
ret = knot_requestor_exec(&re, &tv);
} else {
knot_request_free(req, re.mm);
}
/* Cleanup. */
knot_requestor_clear(&re);
return ret;
}
/*!
* \brief Create a zone event query, send it, wait for the response and process it.
*
......@@ -139,63 +179,49 @@ static int prepare_edns(zone_t *zone, knot_pkt_t *pkt)
static int zone_query_execute(zone_t *zone, uint16_t pkt_type, const conf_remote_t *remote)
{
/* Create a memory pool for this task. */
int ret = KNOT_EOK;
mm_ctx_t mm;
mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE);
/* Create a query message. */
knot_pkt_t *query = zone_query(zone, pkt_type, &mm);
if (query == NULL) {
mp_delete(mm.ctx);
return KNOT_ENOMEM;
}
/* Answer processing parameters. */
struct process_answer_param param = { 0 };
param.zone = zone;
param.query = query;
param.remote = &remote->addr;
/* Create requestor instance. */
struct knot_requestor re;
knot_requestor_init(&re, &mm);
knot_requestor_overlay(&re, KNOT_STATE_ANSWER, &param);
/* Set EDNS section. */
ret = prepare_edns(zone, query);
int ret = prepare_edns(zone, query);
if (ret != KNOT_EOK) {
goto fail;
knot_pkt_free(&query);
mp_delete(mm.ctx);
return ret;
}
/* Answer processing parameters. */
struct process_answer_param param = {
.zone = zone,
.query = query,
.remote = &remote->addr
};
const knot_tsig_key_t *key = remote->key.name != NULL ?
&remote->key : NULL;
tsig_init(&param.tsig_ctx, key);
ret = tsig_sign_packet(&param.tsig_ctx, query);
if (ret != KNOT_EOK) {
goto fail;
}
/* Create a request. */
const struct sockaddr *dst = (const struct sockaddr *)&remote->addr;
const struct sockaddr *src = (const struct sockaddr *)&remote->via;
struct knot_request *req = knot_request_make(re.mm, dst, src, query, 0);
if (req == NULL) {
ret = KNOT_ENOMEM;
goto fail;
tsig_cleanup(&param.tsig_ctx);
knot_pkt_free(&query);
mp_delete(mm.ctx);
return ret;
}
/* Send the queries and process responses. */
ret = knot_requestor_enqueue(&re, req);
if (ret == KNOT_EOK) {
conf_val_t val = conf_get(conf(), C_SRV, C_TCP_REPLY_TIMEOUT);
struct timeval tv = { conf_int(&val), 0 };
ret = knot_requestor_exec(&re, &tv);
}
/* Process the query. */
ret = zone_query_request(query, remote, &param, &mm);
fail:
/* Cleanup. */
tsig_cleanup(&param.tsig_ctx);
knot_requestor_clear(&re);
knot_pkt_free(&query);
mp_delete(mm.ctx);
return ret;
......@@ -502,8 +528,7 @@ int event_update(zone_t *zone)
assert(zone);
/* Process update list - forward if zone has master, or execute. */
int ret = updates_execute(zone);
UNUSED(ret); /* Don't care about the Knot code, RCODEs are set. */
updates_execute(zone);
/* Trim extra heap. */
mem_trim();
......
......@@ -99,14 +99,14 @@ static void replan_flush(zone_t *zone, const zone_t *old_zone)
/*!< \brief Creates new DDNS q in the new zone - q contains references from the old zone. */
static void duplicate_ddns_q(zone_t *zone, zone_t *old_zone)
{
struct knot_request *d, *nxt;
WALK_LIST_DELSAFE(d, nxt, old_zone->ddns_queue) {
add_tail(&zone->ddns_queue, (node_t *)d);
ptrnode_t *node = NULL;
WALK_LIST(node, old_zone->ddns_queue) {
ptrlist_add(&zone->ddns_queue, node->d, NULL);
}
zone->ddns_queue_size = old_zone->ddns_queue_size;
// Reset the list, new zone will free the data.
init_list(&old_zone->ddns_queue);
ptrlist_free(&old_zone->ddns_queue, NULL);
}
/*!< Replans DNSSEC event. Not whole resign needed, \todo #247 */
......
......@@ -44,14 +44,11 @@
static void free_ddns_queue(zone_t *z)
{
struct knot_request *n = NULL;
node_t *nxt = NULL;
WALK_LIST_DELSAFE(n, nxt, z->ddns_queue) {
close(n->fd);
knot_pkt_free(&n->query);
rem_node((node_t *)n);
free(n);
ptrnode_t *node = NULL, *nxt = NULL;
WALK_LIST_DELSAFE(node, nxt, z->ddns_queue) {
knot_request_free(node->d, NULL);
}
ptrlist_free(&z->ddns_queue, NULL);
}
zone_t* zone_new(const knot_dname_t *name)
......@@ -393,7 +390,7 @@ int zone_update_enqueue(zone_t *zone, knot_pkt_t *pkt, struct process_query_para
pthread_mutex_lock(&zone->ddns_lock);
/* Enqueue created request. */
add_tail(&zone->ddns_queue, (node_t *)req);
ptrlist_add(&zone->ddns_queue, req, NULL);
++zone->ddns_queue_size;
pthread_mutex_unlock(&zone->ddns_lock);
......
......@@ -184,7 +184,7 @@ size_t list_size(const list_t *l)
* @val: added pointer
* @mm: memory context
*/
ptrnode_t *ptrlist_add(list_t *to, const void *val, mm_ctx_t *mm)
ptrnode_t *ptrlist_add(list_t *to, void *val, mm_ctx_t *mm)
{
ptrnode_t *node = mm_alloc(mm , sizeof(ptrnode_t));
if (node == NULL) {
......@@ -211,17 +211,12 @@ void ptrlist_free(list_t *list, mm_ctx_t *mm)
}
/**
* ptrlist_contains - check if list contains pointer
* @list: target list
* @val: searched pointer
* ptrlist_rem - remove pointer node
* @val: pointer to remove
* @mm: memory context
*/
bool ptrlist_contains(list_t *list, const void *val)
void ptrlist_rem(ptrnode_t *node, mm_ctx_t *mm)
{
ptrnode_t *n = NULL;
WALK_LIST(n, *list) {
if (n->d == val) {
return true;
}
}
return false;
rem_node(&node->n);
mm_free(mm, node);
}
......@@ -89,12 +89,12 @@ size_t list_size(const list_t *);
*/
typedef struct ptrnode {
node_t n;
const void *d;
void *d;
} ptrnode_t;
ptrnode_t *ptrlist_add(list_t *, const void *, mm_ctx_t *);
ptrnode_t *ptrlist_add(list_t *, void *, mm_ctx_t *);
void ptrlist_free(list_t *, mm_ctx_t *);
bool ptrlist_contains(list_t *, const void *);
void ptrlist_rem(ptrnode_t *node, mm_ctx_t *mm);
/*! \todo This is broken atm.
#ifndef _BIRD_LISTS_C_
......
......@@ -27,7 +27,6 @@
#include <stdint.h>
#include "libknot/packet/pkt.h"
#include "libknot/internal/lists.h"
#include "libknot/internal/mempattern.h"
/*! Layer processing states.
......@@ -47,9 +46,8 @@ struct knot_layer_api;
/*! \brief Packet processing context. */
typedef struct knot_layer {
node_t node;
uint16_t state; /* Bitmap of enum knot_layer_state. */
mm_ctx_t *mm; /* Processing memory context. */
uint16_t state; /* Bitmap of enum knot_layer_state. */
void *data; /* Module specific. */
const struct knot_layer_api *api;
} knot_layer_t;
......
......@@ -16,33 +16,48 @@
#include "libknot/processing/overlay.h"
#include "libknot/errcode.h"
#include "libknot/internal/lists.h"
#include "libknot/internal/macros.h"
#define LAYERS ((list_t *)overlay->layers)
/*! \note Macro for state-chaining layers. */
#define ITERATE_LAYERS(overlay, func, ...) \
int state = overlay->state; \
struct knot_layer *layer = NULL; \
WALK_LIST(layer, (overlay)->layers) { \
ptrnode_t *node = NULL; \
WALK_LIST(node, *LAYERS) { \
knot_layer_t *layer = node->d; \
layer->state = state; /* Pass-through state. */ \
state = (func)(layer, ##__VA_ARGS__); \
} \
return overlay->state = state;
_public_
void knot_overlay_init(struct knot_overlay *overlay, mm_ctx_t *mm)
int knot_overlay_init(struct knot_overlay *overlay, mm_ctx_t *mm)
{
init_list(&overlay->layers);
list_t *layers = mm_alloc(mm, sizeof(list_t));
if (layers == NULL) {
return KNOT_ENOMEM;
}
init_list(layers);
overlay->mm = mm;
overlay->state = KNOT_STATE_NOOP;
overlay->layers = layers;
return KNOT_EOK;
}
_public_
void knot_overlay_deinit(struct knot_overlay *overlay)
{
struct knot_layer *layer = NULL, *next = NULL;
WALK_LIST_DELSAFE(layer, next, overlay->layers) {
mm_free(overlay->mm, layer);
ptrnode_t *node = NULL;
WALK_LIST(node, *LAYERS) {
mm_free(overlay->mm, node->d);