Blame src/libnftables.c

Packit c5a612
/*
Packit c5a612
 * Copyright (c) 2017 Eric Leblond <eric@regit.org>
Packit c5a612
 *
Packit c5a612
 * This program is free software; you can redistribute it and/or modify
Packit c5a612
 * it under the terms of the GNU General Public License version 2 as
Packit c5a612
 * published by the Free Software Foundation.
Packit c5a612
 *
Packit c5a612
 */
Packit c5a612
#include <nftables/libnftables.h>
Packit c5a612
#include <erec.h>
Packit c5a612
#include <mnl.h>
Packit c5a612
#include <parser.h>
Packit c5a612
#include <utils.h>
Packit c5a612
#include <iface.h>
Packit c5a612
Packit c5a612
#include <errno.h>
Packit c5a612
#include <stdlib.h>
Packit c5a612
#include <string.h>
Packit c5a612
Packit c5a612
static int nft_netlink(struct nft_ctx *nft,
Packit c5a612
		       struct list_head *cmds, struct list_head *msgs,
Packit c5a612
		       struct mnl_socket *nf_sock)
Packit c5a612
{
Packit c5a612
	uint32_t batch_seqnum, seqnum = 0, num_cmds = 0;
Packit c5a612
	struct netlink_ctx ctx = {
Packit c5a612
		.nft  = nft,
Packit c5a612
		.msgs = msgs,
Packit c5a612
		.list = LIST_HEAD_INIT(ctx.list),
Packit c5a612
		.batch = mnl_batch_init(),
Packit c5a612
	};
Packit c5a612
	struct cmd *cmd;
Packit c5a612
	struct mnl_err *err, *tmp;
Packit c5a612
	LIST_HEAD(err_list);
Packit c5a612
	int ret = 0;
Packit c5a612
Packit c5a612
	if (list_empty(cmds))
Packit c5a612
		goto out;
Packit c5a612
Packit c5a612
	batch_seqnum = mnl_batch_begin(ctx.batch, mnl_seqnum_alloc(&seqnum));
Packit c5a612
	list_for_each_entry(cmd, cmds, list) {
Packit c5a612
		ctx.seqnum = cmd->seqnum = mnl_seqnum_alloc(&seqnum);
Packit c5a612
		ret = do_command(&ctx, cmd);
Packit c5a612
		if (ret < 0) {
Packit c5a612
			netlink_io_error(&ctx, &cmd->location,
Packit c5a612
					 "Could not process rule: %s",
Packit c5a612
					 strerror(errno));
Packit c5a612
			goto out;
Packit c5a612
		}
Packit c5a612
		num_cmds++;
Packit c5a612
	}
Packit c5a612
	if (!nft->check)
Packit c5a612
		mnl_batch_end(ctx.batch, mnl_seqnum_alloc(&seqnum));
Packit c5a612
Packit c5a612
	if (!mnl_batch_ready(ctx.batch))
Packit c5a612
		goto out;
Packit c5a612
Packit c5a612
	ret = mnl_batch_talk(&ctx, &err_list, num_cmds);
Packit c5a612
	if (ret < 0) {
Packit c5a612
		netlink_io_error(&ctx, NULL,
Packit c5a612
				 "Could not process rule: %s", strerror(errno));
Packit c5a612
		goto out;
Packit c5a612
	}
Packit c5a612
Packit c5a612
	if (!list_empty(&err_list))
Packit c5a612
		ret = -1;
Packit c5a612
Packit c5a612
	list_for_each_entry_safe(err, tmp, &err_list, head) {
Packit c5a612
		list_for_each_entry(cmd, cmds, list) {
Packit c5a612
			if (err->seqnum == cmd->seqnum ||
Packit c5a612
			    err->seqnum == batch_seqnum) {
Packit c5a612
				netlink_io_error(&ctx, &cmd->location,
Packit c5a612
						 "Could not process rule: %s",
Packit c5a612
						 strerror(err->err));
Packit c5a612
				errno = err->err;
Packit c5a612
				if (err->seqnum == cmd->seqnum) {
Packit c5a612
					mnl_err_list_free(err);
Packit c5a612
					break;
Packit c5a612
				}
Packit c5a612
			}
Packit c5a612
		}
Packit c5a612
	}
Packit c5a612
out:
Packit c5a612
	mnl_batch_reset(ctx.batch);
Packit c5a612
	return ret;
Packit c5a612
}
Packit c5a612
Packit c5a612
static void nft_init(struct nft_ctx *ctx)
Packit c5a612
{
Packit c5a612
	mark_table_init(ctx);
Packit c5a612
	realm_table_rt_init(ctx);
Packit c5a612
	devgroup_table_init(ctx);
Packit c5a612
	ct_label_table_init(ctx);
Packit c5a612
}
Packit c5a612
Packit c5a612
static void nft_exit(struct nft_ctx *ctx)
Packit c5a612
{
Packit c5a612
	ct_label_table_exit(ctx);
Packit c5a612
	realm_table_rt_exit(ctx);
Packit c5a612
	devgroup_table_exit(ctx);
Packit c5a612
	mark_table_exit(ctx);
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_add_include_path);
Packit c5a612
int nft_ctx_add_include_path(struct nft_ctx *ctx, const char *path)
Packit c5a612
{
Packit c5a612
	char **tmp;
Packit c5a612
	int pcount = ctx->num_include_paths;
Packit c5a612
Packit c5a612
	tmp = realloc(ctx->include_paths, (pcount + 1) * sizeof(char *));
Packit c5a612
	if (!tmp)
Packit c5a612
		return -1;
Packit c5a612
Packit c5a612
	ctx->include_paths = tmp;
Packit c5a612
Packit c5a612
	if (asprintf(&ctx->include_paths[pcount], "%s", path) < 0)
Packit c5a612
		return -1;
Packit c5a612
Packit c5a612
	ctx->num_include_paths++;
Packit c5a612
	return 0;
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_clear_include_paths);
Packit c5a612
void nft_ctx_clear_include_paths(struct nft_ctx *ctx)
Packit c5a612
{
Packit c5a612
	while (ctx->num_include_paths)
Packit c5a612
		xfree(ctx->include_paths[--ctx->num_include_paths]);
Packit c5a612
Packit c5a612
	xfree(ctx->include_paths);
Packit c5a612
	ctx->include_paths = NULL;
Packit c5a612
}
Packit c5a612
Packit c5a612
static void nft_ctx_netlink_init(struct nft_ctx *ctx)
Packit c5a612
{
Packit c5a612
	ctx->nf_sock = nft_mnl_socket_open();
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_new);
Packit c5a612
struct nft_ctx *nft_ctx_new(uint32_t flags)
Packit c5a612
{
Packit c5a612
	static bool init_once;
Packit c5a612
	struct nft_ctx *ctx;
Packit c5a612
Packit c5a612
	if (!init_once) {
Packit c5a612
		init_once = true;
Packit c5a612
		gmp_init();
Packit c5a612
#ifdef HAVE_LIBXTABLES
Packit c5a612
		xt_init();
Packit c5a612
#endif
Packit c5a612
	}
Packit c5a612
Packit c5a612
	ctx = xzalloc(sizeof(struct nft_ctx));
Packit c5a612
	nft_init(ctx);
Packit c5a612
Packit c5a612
	ctx->state = xzalloc(sizeof(struct parser_state));
Packit c5a612
	nft_ctx_add_include_path(ctx, DEFAULT_INCLUDE_PATH);
Packit c5a612
	ctx->parser_max_errors	= 10;
Packit c5a612
	init_list_head(&ctx->cache.list);
Packit c5a612
	ctx->top_scope = scope_alloc();
Packit c5a612
	ctx->flags = flags;
Packit c5a612
	ctx->output.output_fp = stdout;
Packit c5a612
	ctx->output.error_fp = stderr;
Packit c5a612
Packit c5a612
	if (flags == NFT_CTX_DEFAULT)
Packit c5a612
		nft_ctx_netlink_init(ctx);
Packit c5a612
Packit c5a612
	return ctx;
Packit c5a612
}
Packit c5a612
Packit c5a612
static ssize_t cookie_write(void *cptr, const char *buf, size_t buflen)
Packit c5a612
{
Packit c5a612
	struct cookie *cookie = cptr;
Packit c5a612
Packit c5a612
	if (!cookie->buflen) {
Packit c5a612
		cookie->buflen = buflen + 1;
Packit c5a612
		cookie->buf = xmalloc(cookie->buflen);
Packit c5a612
	} else if (cookie->pos + buflen >= cookie->buflen) {
Packit c5a612
		size_t newlen = cookie->buflen * 2;
Packit c5a612
Packit c5a612
		while (newlen <= cookie->pos + buflen)
Packit c5a612
			newlen *= 2;
Packit c5a612
Packit c5a612
		cookie->buf = xrealloc(cookie->buf, newlen);
Packit c5a612
		cookie->buflen = newlen;
Packit c5a612
	}
Packit c5a612
	memcpy(cookie->buf + cookie->pos, buf, buflen);
Packit c5a612
	cookie->pos += buflen;
Packit c5a612
	cookie->buf[cookie->pos] = '\0';
Packit c5a612
Packit c5a612
	return buflen;
Packit c5a612
}
Packit c5a612
Packit c5a612
static int init_cookie(struct cookie *cookie)
Packit c5a612
{
Packit c5a612
	cookie_io_functions_t cookie_fops = {
Packit c5a612
		.write = cookie_write,
Packit c5a612
	};
Packit c5a612
Packit c5a612
	if (cookie->orig_fp) { /* just rewind buffer */
Packit c5a612
		if (cookie->buflen) {
Packit c5a612
			cookie->pos = 0;
Packit c5a612
			cookie->buf[0] = '\0';
Packit c5a612
		}
Packit c5a612
		return 0;
Packit c5a612
	}
Packit c5a612
Packit c5a612
	cookie->orig_fp = cookie->fp;
Packit c5a612
Packit c5a612
	cookie->fp = fopencookie(cookie, "w", cookie_fops);
Packit c5a612
	if (!cookie->fp) {
Packit c5a612
		cookie->fp = cookie->orig_fp;
Packit c5a612
		cookie->orig_fp = NULL;
Packit c5a612
		return 1;
Packit c5a612
	}
Packit c5a612
Packit c5a612
	return 0;
Packit c5a612
}
Packit c5a612
Packit c5a612
static int exit_cookie(struct cookie *cookie)
Packit c5a612
{
Packit c5a612
	if (!cookie->orig_fp)
Packit c5a612
		return 1;
Packit c5a612
Packit c5a612
	fclose(cookie->fp);
Packit c5a612
	cookie->fp = cookie->orig_fp;
Packit c5a612
	cookie->orig_fp = NULL;
Packit c5a612
	free(cookie->buf);
Packit c5a612
	cookie->buf = NULL;
Packit c5a612
	cookie->buflen = 0;
Packit c5a612
	cookie->pos = 0;
Packit c5a612
	return 0;
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_buffer_output);
Packit c5a612
int nft_ctx_buffer_output(struct nft_ctx *ctx)
Packit c5a612
{
Packit c5a612
	return init_cookie(&ctx->output.output_cookie);
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_unbuffer_output);
Packit c5a612
int nft_ctx_unbuffer_output(struct nft_ctx *ctx)
Packit c5a612
{
Packit c5a612
	return exit_cookie(&ctx->output.output_cookie);
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_buffer_error);
Packit c5a612
int nft_ctx_buffer_error(struct nft_ctx *ctx)
Packit c5a612
{
Packit c5a612
	return init_cookie(&ctx->output.error_cookie);
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_unbuffer_error);
Packit c5a612
int nft_ctx_unbuffer_error(struct nft_ctx *ctx)
Packit c5a612
{
Packit c5a612
	return exit_cookie(&ctx->output.error_cookie);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const char *get_cookie_buffer(struct cookie *cookie)
Packit c5a612
{
Packit c5a612
	fflush(cookie->fp);
Packit c5a612
Packit c5a612
	/* This is a bit tricky: Rewind the buffer for future use and return
Packit c5a612
	 * the old content at the same time. Therefore return an empty string
Packit c5a612
	 * if buffer position is zero, otherwise just rewind buffer position
Packit c5a612
	 * and return the unmodified buffer. */
Packit c5a612
Packit c5a612
	if (!cookie->pos)
Packit c5a612
		return "";
Packit c5a612
Packit c5a612
	cookie->pos = 0;
Packit c5a612
	return cookie->buf;
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_get_output_buffer);
Packit c5a612
const char *nft_ctx_get_output_buffer(struct nft_ctx *ctx)
Packit c5a612
{
Packit c5a612
	return get_cookie_buffer(&ctx->output.output_cookie);
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_get_error_buffer);
Packit c5a612
const char *nft_ctx_get_error_buffer(struct nft_ctx *ctx)
Packit c5a612
{
Packit c5a612
	return get_cookie_buffer(&ctx->output.error_cookie);
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_free);
Packit c5a612
void nft_ctx_free(struct nft_ctx *ctx)
Packit c5a612
{
Packit c5a612
	if (ctx->nf_sock)
Packit c5a612
		mnl_socket_close(ctx->nf_sock);
Packit c5a612
Packit c5a612
	exit_cookie(&ctx->output.output_cookie);
Packit c5a612
	exit_cookie(&ctx->output.error_cookie);
Packit c5a612
	iface_cache_release();
Packit c5a612
	cache_release(&ctx->cache);
Packit c5a612
	nft_ctx_clear_include_paths(ctx);
Packit c5a612
	scope_free(ctx->top_scope);
Packit c5a612
	xfree(ctx->state);
Packit c5a612
	nft_exit(ctx);
Packit c5a612
	xfree(ctx);
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_set_output);
Packit c5a612
FILE *nft_ctx_set_output(struct nft_ctx *ctx, FILE *fp)
Packit c5a612
{
Packit c5a612
	FILE *old = ctx->output.output_fp;
Packit c5a612
Packit c5a612
	if (!fp || ferror(fp))
Packit c5a612
		return NULL;
Packit c5a612
Packit c5a612
	ctx->output.output_fp = fp;
Packit c5a612
Packit c5a612
	return old;
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_set_error);
Packit c5a612
FILE *nft_ctx_set_error(struct nft_ctx *ctx, FILE *fp)
Packit c5a612
{
Packit c5a612
	FILE *old = ctx->output.error_fp;
Packit c5a612
Packit c5a612
	if (!fp || ferror(fp))
Packit c5a612
		return NULL;
Packit c5a612
Packit c5a612
	ctx->output.error_fp = fp;
Packit c5a612
Packit c5a612
	return old;
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_get_dry_run);
Packit c5a612
bool nft_ctx_get_dry_run(struct nft_ctx *ctx)
Packit c5a612
{
Packit c5a612
	return ctx->check;
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_set_dry_run);
Packit c5a612
void nft_ctx_set_dry_run(struct nft_ctx *ctx, bool dry)
Packit c5a612
{
Packit c5a612
	ctx->check = dry;
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_output_get_flags);
Packit c5a612
unsigned int nft_ctx_output_get_flags(struct nft_ctx *ctx)
Packit c5a612
{
Packit c5a612
	return ctx->output.flags;
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_output_set_flags);
Packit c5a612
void nft_ctx_output_set_flags(struct nft_ctx *ctx, unsigned int flags)
Packit c5a612
{
Packit c5a612
	ctx->output.flags = flags;
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_ctx_output_get_debug);
Packit c5a612
unsigned int nft_ctx_output_get_debug(struct nft_ctx *ctx)
Packit c5a612
{
Packit c5a612
	return ctx->debug_mask;
Packit c5a612
}
Packit c5a612
EXPORT_SYMBOL(nft_ctx_output_set_debug);
Packit c5a612
void nft_ctx_output_set_debug(struct nft_ctx *ctx, unsigned int mask)
Packit c5a612
{
Packit c5a612
	ctx->debug_mask = mask;
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct input_descriptor indesc_cmdline = {
Packit c5a612
	.type	= INDESC_BUFFER,
Packit c5a612
	.name	= "<cmdline>",
Packit c5a612
};
Packit c5a612
Packit c5a612
static int nft_parse_bison_buffer(struct nft_ctx *nft, const char *buf,
Packit c5a612
				  struct list_head *msgs, struct list_head *cmds)
Packit c5a612
{
Packit c5a612
	int ret;
Packit c5a612
Packit c5a612
	parser_init(nft, nft->state, msgs, cmds, nft->top_scope);
Packit c5a612
	nft->scanner = scanner_init(nft->state);
Packit c5a612
	scanner_push_buffer(nft->scanner, &indesc_cmdline, buf);
Packit c5a612
Packit c5a612
	ret = nft_parse(nft, nft->scanner, nft->state);
Packit c5a612
	if (ret != 0 || nft->state->nerrs > 0)
Packit c5a612
		return -1;
Packit c5a612
Packit c5a612
	return 0;
Packit c5a612
}
Packit c5a612
Packit c5a612
static int nft_parse_bison_filename(struct nft_ctx *nft, const char *filename,
Packit c5a612
				    struct list_head *msgs, struct list_head *cmds)
Packit c5a612
{
Packit c5a612
	int ret;
Packit c5a612
Packit c5a612
	parser_init(nft, nft->state, msgs, cmds, nft->top_scope);
Packit c5a612
	nft->scanner = scanner_init(nft->state);
Packit c5a612
	if (scanner_read_file(nft, filename, &internal_location) < 0)
Packit c5a612
		return -1;
Packit c5a612
Packit c5a612
	ret = nft_parse(nft, nft->scanner, nft->state);
Packit c5a612
	if (ret != 0 || nft->state->nerrs > 0)
Packit c5a612
		return -1;
Packit c5a612
Packit c5a612
	return 0;
Packit c5a612
}
Packit c5a612
Packit c5a612
static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
Packit c5a612
			struct list_head *cmds)
Packit c5a612
{
Packit c5a612
	unsigned int flags;
Packit c5a612
	struct cmd *cmd;
Packit c5a612
Packit c5a612
	flags = cache_evaluate(nft, cmds);
Packit c5a612
	if (cache_update(nft, flags, msgs) < 0)
Packit c5a612
		return -1;
Packit c5a612
Packit c5a612
	list_for_each_entry(cmd, cmds, list) {
Packit c5a612
		struct eval_ctx ectx = {
Packit c5a612
			.nft	= nft,
Packit c5a612
			.msgs	= msgs,
Packit c5a612
		};
Packit c5a612
		if (cmd_evaluate(&ectx, cmd) < 0 &&
Packit c5a612
		    ++nft->state->nerrs == nft->parser_max_errors)
Packit c5a612
			return -1;
Packit c5a612
	}
Packit c5a612
Packit c5a612
	if (nft->state->nerrs)
Packit c5a612
		return -1;
Packit c5a612
Packit c5a612
	list_for_each_entry(cmd, cmds, list)
Packit c5a612
		nft_cmd_expand(cmd);
Packit c5a612
Packit c5a612
	return 0;
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_run_cmd_from_buffer);
Packit c5a612
int nft_run_cmd_from_buffer(struct nft_ctx *nft, const char *buf)
Packit c5a612
{
Packit c5a612
	int rc = -EINVAL, parser_rc;
Packit c5a612
	struct cmd *cmd, *next;
Packit c5a612
	LIST_HEAD(msgs);
Packit c5a612
	LIST_HEAD(cmds);
Packit c5a612
	char *nlbuf;
Packit c5a612
Packit c5a612
	nlbuf = xzalloc(strlen(buf) + 2);
Packit c5a612
	sprintf(nlbuf, "%s\n", buf);
Packit c5a612
Packit c5a612
	if (nft_output_json(&nft->output))
Packit c5a612
		rc = nft_parse_json_buffer(nft, nlbuf, &msgs, &cmds);
Packit c5a612
	if (rc == -EINVAL)
Packit c5a612
		rc = nft_parse_bison_buffer(nft, nlbuf, &msgs, &cmds);
Packit c5a612
Packit c5a612
	parser_rc = rc;
Packit c5a612
Packit c5a612
	rc = nft_evaluate(nft, &msgs, &cmds);
Packit c5a612
	if (rc < 0)
Packit c5a612
		goto err;
Packit c5a612
Packit c5a612
	if (parser_rc) {
Packit c5a612
		rc = parser_rc;
Packit c5a612
		goto err;
Packit c5a612
	}
Packit c5a612
Packit c5a612
	if (nft_netlink(nft, &cmds, &msgs, nft->nf_sock) != 0)
Packit c5a612
		rc = -1;
Packit c5a612
err:
Packit c5a612
	erec_print_list(&nft->output, &msgs, nft->debug_mask);
Packit c5a612
	list_for_each_entry_safe(cmd, next, &cmds, list) {
Packit c5a612
		list_del(&cmd->list);
Packit c5a612
		cmd_free(cmd);
Packit c5a612
	}
Packit c5a612
	iface_cache_release();
Packit c5a612
	if (nft->scanner) {
Packit c5a612
		scanner_destroy(nft);
Packit c5a612
		nft->scanner = NULL;
Packit c5a612
	}
Packit c5a612
	free(nlbuf);
Packit c5a612
Packit c5a612
	if (!rc &&
Packit c5a612
	    nft_output_json(&nft->output) &&
Packit c5a612
	    nft_output_echo(&nft->output))
Packit c5a612
		json_print_echo(nft);
Packit c5a612
	if (rc)
Packit c5a612
		cache_release(&nft->cache);
Packit c5a612
	return rc;
Packit c5a612
}
Packit c5a612
Packit c5a612
EXPORT_SYMBOL(nft_run_cmd_from_filename);
Packit c5a612
int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
Packit c5a612
{
Packit c5a612
	struct cmd *cmd, *next;
Packit c5a612
	int rc, parser_rc;
Packit c5a612
	LIST_HEAD(msgs);
Packit c5a612
	LIST_HEAD(cmds);
Packit c5a612
Packit c5a612
	if (!strcmp(filename, "-"))
Packit c5a612
		filename = "/dev/stdin";
Packit c5a612
Packit c5a612
	rc = -EINVAL;
Packit c5a612
	if (nft_output_json(&nft->output))
Packit c5a612
		rc = nft_parse_json_filename(nft, filename, &msgs, &cmds);
Packit c5a612
	if (rc == -EINVAL)
Packit c5a612
		rc = nft_parse_bison_filename(nft, filename, &msgs, &cmds);
Packit c5a612
Packit c5a612
	parser_rc = rc;
Packit c5a612
Packit c5a612
	rc = nft_evaluate(nft, &msgs, &cmds);
Packit c5a612
	if (rc < 0)
Packit c5a612
		goto err;
Packit c5a612
Packit c5a612
	if (parser_rc) {
Packit c5a612
		rc = parser_rc;
Packit c5a612
		goto err;
Packit c5a612
	}
Packit c5a612
Packit c5a612
	if (nft_netlink(nft, &cmds, &msgs, nft->nf_sock) != 0)
Packit c5a612
		rc = -1;
Packit c5a612
err:
Packit c5a612
	erec_print_list(&nft->output, &msgs, nft->debug_mask);
Packit c5a612
	list_for_each_entry_safe(cmd, next, &cmds, list) {
Packit c5a612
		list_del(&cmd->list);
Packit c5a612
		cmd_free(cmd);
Packit c5a612
	}
Packit c5a612
	iface_cache_release();
Packit c5a612
	if (nft->scanner) {
Packit c5a612
		scanner_destroy(nft);
Packit c5a612
		nft->scanner = NULL;
Packit c5a612
	}
Packit c5a612
Packit c5a612
	if (!rc &&
Packit c5a612
	    nft_output_json(&nft->output) &&
Packit c5a612
	    nft_output_echo(&nft->output))
Packit c5a612
		json_print_echo(nft);
Packit c5a612
	if (rc)
Packit c5a612
		cache_release(&nft->cache);
Packit c5a612
	return rc;
Packit c5a612
}