Blame src/statement.c

Packit c5a612
/*
Packit c5a612
 * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
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
 * Development of this code funded by Astaro AG (http://www.astaro.com/)
Packit c5a612
 */
Packit c5a612
Packit c5a612
#include <stddef.h>
Packit c5a612
#include <stdlib.h>
Packit c5a612
#include <stdio.h>
Packit c5a612
#include <stdint.h>
Packit c5a612
#include <inttypes.h>
Packit c5a612
#include <string.h>
Packit c5a612
#include <syslog.h>
Packit c5a612
Packit c5a612
#include <arpa/inet.h>
Packit c5a612
#include <linux/netfilter.h>
Packit c5a612
#include <netinet/ip_icmp.h>
Packit c5a612
#include <netinet/icmp6.h>
Packit c5a612
#include <statement.h>
Packit c5a612
#include <utils.h>
Packit c5a612
#include <list.h>
Packit c5a612
#include <xt.h>
Packit c5a612
#include <json.h>
Packit c5a612
Packit c5a612
#include <netinet/in.h>
Packit c5a612
#include <linux/netfilter/nf_nat.h>
Packit c5a612
#include <linux/netfilter/nf_log.h>
Packit c5a612
#include <linux/netfilter/nf_synproxy.h>
Packit c5a612
Packit c5a612
struct stmt *stmt_alloc(const struct location *loc,
Packit c5a612
			const struct stmt_ops *ops)
Packit c5a612
{
Packit c5a612
	struct stmt *stmt;
Packit c5a612
Packit c5a612
	stmt = xzalloc(sizeof(*stmt));
Packit c5a612
	init_list_head(&stmt->list);
Packit c5a612
	stmt->location = *loc;
Packit c5a612
	stmt->ops      = ops;
Packit c5a612
	return stmt;
Packit c5a612
}
Packit c5a612
Packit c5a612
void stmt_free(struct stmt *stmt)
Packit c5a612
{
Packit c5a612
	if (stmt == NULL)
Packit c5a612
		return;
Packit c5a612
	if (stmt->ops->destroy)
Packit c5a612
		stmt->ops->destroy(stmt);
Packit c5a612
	xfree(stmt);
Packit c5a612
}
Packit c5a612
Packit c5a612
void stmt_list_free(struct list_head *list)
Packit c5a612
{
Packit c5a612
	struct stmt *i, *next;
Packit c5a612
Packit c5a612
	list_for_each_entry_safe(i, next, list, list) {
Packit c5a612
		list_del(&i->list);
Packit c5a612
		stmt_free(i);
Packit c5a612
	}
Packit c5a612
}
Packit c5a612
Packit c5a612
void stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	stmt->ops->print(stmt, octx);
Packit c5a612
}
Packit c5a612
Packit c5a612
static void expr_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	expr_print(stmt->expr, octx);
Packit c5a612
}
Packit c5a612
Packit c5a612
static void expr_stmt_destroy(struct stmt *stmt)
Packit c5a612
{
Packit c5a612
	expr_free(stmt->expr);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops expr_stmt_ops = {
Packit c5a612
	.type		= STMT_EXPRESSION,
Packit c5a612
	.name		= "expression",
Packit c5a612
	.print		= expr_stmt_print,
Packit c5a612
	.json		= expr_stmt_json,
Packit c5a612
	.destroy	= expr_stmt_destroy,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *expr_stmt_alloc(const struct location *loc, struct expr *expr)
Packit c5a612
{
Packit c5a612
	struct stmt *stmt;
Packit c5a612
Packit c5a612
	stmt = stmt_alloc(loc, &expr_stmt_ops);
Packit c5a612
	stmt->expr = expr;
Packit c5a612
	return stmt;
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops verdict_stmt_ops = {
Packit c5a612
	.type		= STMT_VERDICT,
Packit c5a612
	.name		= "verdict",
Packit c5a612
	.print		= expr_stmt_print,
Packit c5a612
	.json		= verdict_stmt_json,
Packit c5a612
	.destroy	= expr_stmt_destroy,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *verdict_stmt_alloc(const struct location *loc, struct expr *expr)
Packit c5a612
{
Packit c5a612
	struct stmt *stmt;
Packit c5a612
Packit c5a612
	stmt = stmt_alloc(loc, &verdict_stmt_ops);
Packit c5a612
	stmt->expr = expr;
Packit c5a612
	return stmt;
Packit c5a612
}
Packit c5a612
Packit c5a612
static void meter_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	unsigned int flags = octx->flags;
Packit c5a612
Packit c5a612
	nft_print(octx, "meter ");
Packit c5a612
	if (stmt->meter.set) {
Packit c5a612
		expr_print(stmt->meter.set, octx);
Packit c5a612
		nft_print(octx, " ");
Packit c5a612
	}
Packit c5a612
	nft_print(octx, "size %u { ", stmt->meter.size);
Packit c5a612
	expr_print(stmt->meter.key, octx);
Packit c5a612
	nft_print(octx, " ");
Packit c5a612
Packit c5a612
	octx->flags |= NFT_CTX_OUTPUT_STATELESS;
Packit c5a612
	stmt_print(stmt->meter.stmt, octx);
Packit c5a612
	octx->flags = flags;
Packit c5a612
Packit c5a612
	nft_print(octx, " }");
Packit c5a612
Packit c5a612
}
Packit c5a612
Packit c5a612
static void meter_stmt_destroy(struct stmt *stmt)
Packit c5a612
{
Packit c5a612
	expr_free(stmt->meter.key);
Packit c5a612
	expr_free(stmt->meter.set);
Packit c5a612
	stmt_free(stmt->meter.stmt);
Packit c5a612
	xfree(stmt->meter.name);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops meter_stmt_ops = {
Packit c5a612
	.type		= STMT_METER,
Packit c5a612
	.name		= "meter",
Packit c5a612
	.print		= meter_stmt_print,
Packit c5a612
	.json		= meter_stmt_json,
Packit c5a612
	.destroy	= meter_stmt_destroy,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *meter_stmt_alloc(const struct location *loc)
Packit c5a612
{
Packit c5a612
	return stmt_alloc(loc, &meter_stmt_ops);
Packit c5a612
}
Packit c5a612
Packit c5a612
static void connlimit_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	nft_print(octx, "ct count %s%u ",
Packit c5a612
		  stmt->connlimit.flags ? "over " : "", stmt->connlimit.count);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops connlimit_stmt_ops = {
Packit c5a612
	.type		= STMT_CONNLIMIT,
Packit c5a612
	.name		= "connlimit",
Packit c5a612
	.print		= connlimit_stmt_print,
Packit c5a612
	.json		= connlimit_stmt_json,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *connlimit_stmt_alloc(const struct location *loc)
Packit c5a612
{
Packit c5a612
	struct stmt *stmt;
Packit c5a612
Packit c5a612
	stmt = stmt_alloc(loc, &connlimit_stmt_ops);
Packit c5a612
	stmt->flags |= STMT_F_STATEFUL;
Packit c5a612
	return stmt;
Packit c5a612
}
Packit c5a612
Packit c5a612
static void counter_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	nft_print(octx, "counter");
Packit c5a612
Packit c5a612
	if (nft_output_stateless(octx))
Packit c5a612
		return;
Packit c5a612
Packit c5a612
	nft_print(octx, " packets %" PRIu64 " bytes %" PRIu64,
Packit c5a612
		  stmt->counter.packets, stmt->counter.bytes);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops counter_stmt_ops = {
Packit c5a612
	.type		= STMT_COUNTER,
Packit c5a612
	.name		= "counter",
Packit c5a612
	.print		= counter_stmt_print,
Packit c5a612
	.json		= counter_stmt_json,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *counter_stmt_alloc(const struct location *loc)
Packit c5a612
{
Packit c5a612
	struct stmt *stmt;
Packit c5a612
Packit c5a612
	stmt = stmt_alloc(loc, &counter_stmt_ops);
Packit c5a612
	stmt->flags |= STMT_F_STATEFUL;
Packit c5a612
	return stmt;
Packit c5a612
}
Packit c5a612
Packit c5a612
static const char *objref_type[NFT_OBJECT_MAX + 1] = {
Packit c5a612
	[NFT_OBJECT_COUNTER]	= "counter",
Packit c5a612
	[NFT_OBJECT_QUOTA]	= "quota",
Packit c5a612
	[NFT_OBJECT_CT_HELPER]	= "ct helper",
Packit c5a612
	[NFT_OBJECT_LIMIT]	= "limit",
Packit c5a612
	[NFT_OBJECT_CT_TIMEOUT] = "ct timeout",
Packit c5a612
	[NFT_OBJECT_SECMARK]	= "secmark",
Packit c5a612
	[NFT_OBJECT_SYNPROXY]	= "synproxy",
Packit c5a612
	[NFT_OBJECT_CT_EXPECT]	= "ct expectation",
Packit c5a612
};
Packit c5a612
Packit c5a612
const char *objref_type_name(uint32_t type)
Packit c5a612
{
Packit c5a612
	if (type > NFT_OBJECT_MAX)
Packit c5a612
		return "unknown";
Packit c5a612
Packit c5a612
	return objref_type[type];
Packit c5a612
}
Packit c5a612
Packit c5a612
static void objref_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	switch (stmt->objref.type) {
Packit c5a612
	case NFT_OBJECT_CT_HELPER:
Packit c5a612
		nft_print(octx, "ct helper set ");
Packit c5a612
		break;
Packit c5a612
	case NFT_OBJECT_CT_TIMEOUT:
Packit c5a612
		nft_print(octx, "ct timeout set ");
Packit c5a612
		break;
Packit c5a612
	case NFT_OBJECT_CT_EXPECT:
Packit c5a612
		nft_print(octx, "ct expectation set ");
Packit c5a612
		break;
Packit c5a612
	case NFT_OBJECT_SECMARK:
Packit c5a612
		nft_print(octx, "meta secmark set ");
Packit c5a612
		break;
Packit c5a612
	default:
Packit c5a612
		nft_print(octx, "%s name ",
Packit c5a612
			  objref_type_name(stmt->objref.type));
Packit c5a612
		break;
Packit c5a612
	}
Packit c5a612
	expr_print(stmt->objref.expr, octx);
Packit c5a612
}
Packit c5a612
Packit c5a612
static void objref_stmt_destroy(struct stmt *stmt)
Packit c5a612
{
Packit c5a612
	expr_free(stmt->objref.expr);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops objref_stmt_ops = {
Packit c5a612
	.type		= STMT_OBJREF,
Packit c5a612
	.name		= "objref",
Packit c5a612
	.print		= objref_stmt_print,
Packit c5a612
	.json		= objref_stmt_json,
Packit c5a612
	.destroy	= objref_stmt_destroy,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *objref_stmt_alloc(const struct location *loc)
Packit c5a612
{
Packit c5a612
	struct stmt *stmt;
Packit c5a612
Packit c5a612
	stmt = stmt_alloc(loc, &objref_stmt_ops);
Packit c5a612
	return stmt;
Packit c5a612
}
Packit c5a612
Packit c5a612
static const char *syslog_level[NFT_LOGLEVEL_MAX + 1] = {
Packit c5a612
	[NFT_LOGLEVEL_EMERG]	= "emerg",
Packit c5a612
	[NFT_LOGLEVEL_ALERT]	= "alert",
Packit c5a612
	[NFT_LOGLEVEL_CRIT]	= "crit",
Packit c5a612
	[NFT_LOGLEVEL_ERR]	= "err",
Packit c5a612
	[NFT_LOGLEVEL_WARNING]	= "warn",
Packit c5a612
	[NFT_LOGLEVEL_NOTICE]	= "notice",
Packit c5a612
	[NFT_LOGLEVEL_INFO]	= "info",
Packit c5a612
	[NFT_LOGLEVEL_DEBUG]	= "debug",
Packit c5a612
	[NFT_LOGLEVEL_AUDIT] 	= "audit"
Packit c5a612
};
Packit c5a612
Packit c5a612
const char *log_level(uint32_t level)
Packit c5a612
{
Packit c5a612
	if (level > NFT_LOGLEVEL_MAX)
Packit c5a612
		return "unknown";
Packit c5a612
Packit c5a612
	return syslog_level[level];
Packit c5a612
}
Packit c5a612
Packit c5a612
int log_level_parse(const char *level)
Packit c5a612
{
Packit c5a612
	int i;
Packit c5a612
Packit c5a612
	for (i = 0; i <= NFT_LOGLEVEL_MAX; i++) {
Packit c5a612
		if (syslog_level[i] &&
Packit c5a612
		    !strcmp(level, syslog_level[i]))
Packit c5a612
			return i;
Packit c5a612
	}
Packit c5a612
	return -1;
Packit c5a612
}
Packit c5a612
Packit c5a612
static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	nft_print(octx, "log");
Packit c5a612
	if (stmt->log.flags & STMT_LOG_PREFIX)
Packit c5a612
		nft_print(octx, " prefix \"%s\"", stmt->log.prefix);
Packit c5a612
	if (stmt->log.flags & STMT_LOG_GROUP)
Packit c5a612
		nft_print(octx, " group %u", stmt->log.group);
Packit c5a612
	if (stmt->log.flags & STMT_LOG_SNAPLEN)
Packit c5a612
		nft_print(octx, " snaplen %u", stmt->log.snaplen);
Packit c5a612
	if (stmt->log.flags & STMT_LOG_QTHRESHOLD)
Packit c5a612
		nft_print(octx, " queue-threshold %u", stmt->log.qthreshold);
Packit c5a612
	if ((stmt->log.flags & STMT_LOG_LEVEL) &&
Packit c5a612
	    stmt->log.level != LOG_WARNING)
Packit c5a612
		nft_print(octx, " level %s", log_level(stmt->log.level));
Packit c5a612
Packit c5a612
	if ((stmt->log.logflags & NF_LOG_MASK) == NF_LOG_MASK) {
Packit c5a612
		nft_print(octx, " flags all");
Packit c5a612
	} else {
Packit c5a612
		if (stmt->log.logflags & (NF_LOG_TCPSEQ | NF_LOG_TCPOPT)) {
Packit c5a612
			const char *delim = " ";
Packit c5a612
Packit c5a612
			nft_print(octx, " flags tcp");
Packit c5a612
			if (stmt->log.logflags & NF_LOG_TCPSEQ) {
Packit c5a612
				nft_print(octx, " sequence");
Packit c5a612
				delim = ",";
Packit c5a612
			}
Packit c5a612
			if (stmt->log.logflags & NF_LOG_TCPOPT)
Packit c5a612
				nft_print(octx, "%soptions",
Packit c5a612
							delim);
Packit c5a612
		}
Packit c5a612
		if (stmt->log.logflags & NF_LOG_IPOPT)
Packit c5a612
			nft_print(octx, " flags ip options");
Packit c5a612
		if (stmt->log.logflags & NF_LOG_UID)
Packit c5a612
			nft_print(octx, " flags skuid");
Packit c5a612
		if (stmt->log.logflags & NF_LOG_MACDECODE)
Packit c5a612
			nft_print(octx, " flags ether");
Packit c5a612
	}
Packit c5a612
}
Packit c5a612
Packit c5a612
static void log_stmt_destroy(struct stmt *stmt)
Packit c5a612
{
Packit c5a612
	xfree(stmt->log.prefix);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops log_stmt_ops = {
Packit c5a612
	.type		= STMT_LOG,
Packit c5a612
	.name		= "log",
Packit c5a612
	.print		= log_stmt_print,
Packit c5a612
	.json		= log_stmt_json,
Packit c5a612
	.destroy	= log_stmt_destroy,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *log_stmt_alloc(const struct location *loc)
Packit c5a612
{
Packit c5a612
	return stmt_alloc(loc, &log_stmt_ops);
Packit c5a612
}
Packit c5a612
Packit c5a612
const char *get_unit(uint64_t u)
Packit c5a612
{
Packit c5a612
	switch (u) {
Packit c5a612
	case 1: return "second";
Packit c5a612
	case 60: return "minute";
Packit c5a612
	case 60 * 60: return "hour";
Packit c5a612
	case 60 * 60 * 24: return "day";
Packit c5a612
	case 60 * 60 * 24 * 7: return "week";
Packit c5a612
	}
Packit c5a612
Packit c5a612
	return "error";
Packit c5a612
}
Packit c5a612
Packit c5a612
static const char * const data_unit[] = {
Packit c5a612
	"bytes",
Packit c5a612
	"kbytes",
Packit c5a612
	"mbytes",
Packit c5a612
	NULL
Packit c5a612
};
Packit c5a612
Packit c5a612
const char *get_rate(uint64_t byte_rate, uint64_t *rate)
Packit c5a612
{
Packit c5a612
	int i;
Packit c5a612
Packit c5a612
	if (!byte_rate) {
Packit c5a612
		*rate = 0;
Packit c5a612
		return data_unit[0];
Packit c5a612
	}
Packit c5a612
Packit c5a612
	for (i = 0; data_unit[i + 1] != NULL; i++) {
Packit c5a612
		if (byte_rate % 1024)
Packit c5a612
			break;
Packit c5a612
		byte_rate /= 1024;
Packit c5a612
	}
Packit c5a612
Packit c5a612
	*rate = byte_rate;
Packit c5a612
	return data_unit[i];
Packit c5a612
}
Packit c5a612
Packit c5a612
static void limit_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	bool inv = stmt->limit.flags & NFT_LIMIT_F_INV;
Packit c5a612
	const char *data_unit;
Packit c5a612
	uint64_t rate;
Packit c5a612
Packit c5a612
	switch (stmt->limit.type) {
Packit c5a612
	case NFT_LIMIT_PKTS:
Packit c5a612
		nft_print(octx, "limit rate %s%" PRIu64 "/%s",
Packit c5a612
			  inv ? "over " : "", stmt->limit.rate,
Packit c5a612
			  get_unit(stmt->limit.unit));
Packit c5a612
		if (stmt->limit.burst && stmt->limit.burst != 5)
Packit c5a612
			nft_print(octx, " burst %u packets",
Packit c5a612
				  stmt->limit.burst);
Packit c5a612
		break;
Packit c5a612
	case NFT_LIMIT_PKT_BYTES:
Packit c5a612
		data_unit = get_rate(stmt->limit.rate, &rate;;
Packit c5a612
Packit c5a612
		nft_print(octx,	"limit rate %s%" PRIu64 " %s/%s",
Packit c5a612
			  inv ? "over " : "", rate, data_unit,
Packit c5a612
			  get_unit(stmt->limit.unit));
Packit c5a612
		if (stmt->limit.burst > 0) {
Packit c5a612
			uint64_t burst;
Packit c5a612
Packit c5a612
			data_unit = get_rate(stmt->limit.burst, &burst);
Packit c5a612
			nft_print(octx, " burst %" PRIu64 " %s", burst,
Packit c5a612
				  data_unit);
Packit c5a612
		}
Packit c5a612
		break;
Packit c5a612
	}
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops limit_stmt_ops = {
Packit c5a612
	.type		= STMT_LIMIT,
Packit c5a612
	.name		= "limit",
Packit c5a612
	.print		= limit_stmt_print,
Packit c5a612
	.json		= limit_stmt_json,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *limit_stmt_alloc(const struct location *loc)
Packit c5a612
{
Packit c5a612
	struct stmt *stmt;
Packit c5a612
Packit c5a612
	stmt = stmt_alloc(loc, &limit_stmt_ops);
Packit c5a612
	stmt->flags |= STMT_F_STATEFUL;
Packit c5a612
	return stmt;
Packit c5a612
}
Packit c5a612
Packit c5a612
static void queue_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	const char *delim = " ";
Packit c5a612
Packit c5a612
	nft_print(octx, "queue");
Packit c5a612
	if (stmt->queue.queue != NULL) {
Packit c5a612
		nft_print(octx, " num ");
Packit c5a612
		expr_print(stmt->queue.queue, octx);
Packit c5a612
	}
Packit c5a612
	if (stmt->queue.flags & NFT_QUEUE_FLAG_BYPASS) {
Packit c5a612
		nft_print(octx, "%sbypass", delim);
Packit c5a612
		delim = ",";
Packit c5a612
	}
Packit c5a612
	if (stmt->queue.flags & NFT_QUEUE_FLAG_CPU_FANOUT)
Packit c5a612
		nft_print(octx, "%sfanout", delim);
Packit c5a612
Packit c5a612
}
Packit c5a612
Packit c5a612
static void queue_stmt_destroy(struct stmt *stmt)
Packit c5a612
{
Packit c5a612
	expr_free(stmt->queue.queue);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops queue_stmt_ops = {
Packit c5a612
	.type		= STMT_QUEUE,
Packit c5a612
	.name		= "queue",
Packit c5a612
	.print		= queue_stmt_print,
Packit c5a612
	.json		= queue_stmt_json,
Packit c5a612
	.destroy	= queue_stmt_destroy,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *queue_stmt_alloc(const struct location *loc)
Packit c5a612
{
Packit c5a612
	return stmt_alloc(loc, &queue_stmt_ops);
Packit c5a612
}
Packit c5a612
Packit c5a612
static void quota_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	bool inv = stmt->quota.flags & NFT_QUOTA_F_INV;
Packit c5a612
	const char *data_unit;
Packit c5a612
	uint64_t bytes, used;
Packit c5a612
Packit c5a612
	data_unit = get_rate(stmt->quota.bytes, &bytes);
Packit c5a612
	nft_print(octx, "quota %s%" PRIu64 " %s",
Packit c5a612
		  inv ? "over " : "", bytes, data_unit);
Packit c5a612
Packit c5a612
	if (!nft_output_stateless(octx) && stmt->quota.used) {
Packit c5a612
		data_unit = get_rate(stmt->quota.used, &used);
Packit c5a612
		nft_print(octx, " used %" PRIu64 " %s", used, data_unit);
Packit c5a612
	}
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops quota_stmt_ops = {
Packit c5a612
	.type		= STMT_QUOTA,
Packit c5a612
	.name		= "quota",
Packit c5a612
	.print		= quota_stmt_print,
Packit c5a612
	.json		= quota_stmt_json,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *quota_stmt_alloc(const struct location *loc)
Packit c5a612
{
Packit c5a612
	struct stmt *stmt;
Packit c5a612
Packit c5a612
	stmt = stmt_alloc(loc, &quota_stmt_ops);
Packit c5a612
	stmt->flags |= STMT_F_STATEFUL;
Packit c5a612
	return stmt;
Packit c5a612
}
Packit c5a612
Packit c5a612
static void reject_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	nft_print(octx, "reject");
Packit c5a612
	switch (stmt->reject.type) {
Packit c5a612
	case NFT_REJECT_TCP_RST:
Packit c5a612
		nft_print(octx, " with tcp reset");
Packit c5a612
		break;
Packit c5a612
	case NFT_REJECT_ICMPX_UNREACH:
Packit c5a612
		if (stmt->reject.icmp_code == NFT_REJECT_ICMPX_PORT_UNREACH)
Packit c5a612
			break;
Packit c5a612
		nft_print(octx, " with icmpx type ");
Packit c5a612
		expr_print(stmt->reject.expr, octx);
Packit c5a612
		break;
Packit c5a612
	case NFT_REJECT_ICMP_UNREACH:
Packit c5a612
		switch (stmt->reject.family) {
Packit c5a612
		case NFPROTO_IPV4:
Packit c5a612
			if (!stmt->reject.verbose_print &&
Packit c5a612
			     stmt->reject.icmp_code == ICMP_PORT_UNREACH)
Packit c5a612
				break;
Packit c5a612
			nft_print(octx, " with icmp type ");
Packit c5a612
			expr_print(stmt->reject.expr, octx);
Packit c5a612
			break;
Packit c5a612
		case NFPROTO_IPV6:
Packit c5a612
			if (!stmt->reject.verbose_print &&
Packit c5a612
			    stmt->reject.icmp_code == ICMP6_DST_UNREACH_NOPORT)
Packit c5a612
				break;
Packit c5a612
			nft_print(octx, " with icmpv6 type ");
Packit c5a612
			expr_print(stmt->reject.expr, octx);
Packit c5a612
			break;
Packit c5a612
		}
Packit c5a612
		break;
Packit c5a612
	}
Packit c5a612
}
Packit c5a612
Packit c5a612
static void reject_stmt_destroy(struct stmt *stmt)
Packit c5a612
{
Packit c5a612
	expr_free(stmt->reject.expr);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops reject_stmt_ops = {
Packit c5a612
	.type		= STMT_REJECT,
Packit c5a612
	.name		= "reject",
Packit c5a612
	.print		= reject_stmt_print,
Packit c5a612
	.json		= reject_stmt_json,
Packit c5a612
	.destroy	= reject_stmt_destroy,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *reject_stmt_alloc(const struct location *loc)
Packit c5a612
{
Packit c5a612
	return stmt_alloc(loc, &reject_stmt_ops);
Packit c5a612
}
Packit c5a612
Packit c5a612
static void print_nf_nat_flags(uint32_t flags, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	const char *delim = " ";
Packit c5a612
Packit c5a612
	if (flags == 0)
Packit c5a612
		return;
Packit c5a612
Packit c5a612
	if (flags & NF_NAT_RANGE_PROTO_RANDOM) {
Packit c5a612
		nft_print(octx, "%srandom", delim);
Packit c5a612
		delim = ",";
Packit c5a612
	}
Packit c5a612
Packit c5a612
	if (flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) {
Packit c5a612
		nft_print(octx, "%sfully-random", delim);
Packit c5a612
		delim = ",";
Packit c5a612
	}
Packit c5a612
Packit c5a612
	if (flags & NF_NAT_RANGE_PERSISTENT)
Packit c5a612
		nft_print(octx, "%spersistent", delim);
Packit c5a612
}
Packit c5a612
Packit c5a612
const char *nat_etype2str(enum nft_nat_etypes type)
Packit c5a612
{
Packit c5a612
	static const char * const nat_types[] = {
Packit c5a612
		[NFT_NAT_SNAT]	= "snat",
Packit c5a612
		[NFT_NAT_DNAT]	= "dnat",
Packit c5a612
		[NFT_NAT_MASQ]	= "masquerade",
Packit c5a612
		[NFT_NAT_REDIR]	= "redirect",
Packit c5a612
	};
Packit c5a612
Packit c5a612
	return nat_types[type];
Packit c5a612
}
Packit c5a612
Packit c5a612
static void nat_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	nft_print(octx, "%s", nat_etype2str(stmt->nat.type));
Packit c5a612
	if (stmt->nat.addr || stmt->nat.proto) {
Packit c5a612
		switch (stmt->nat.family) {
Packit c5a612
		case NFPROTO_IPV4:
Packit c5a612
			nft_print(octx, " ip");
Packit c5a612
			break;
Packit c5a612
		case NFPROTO_IPV6:
Packit c5a612
			nft_print(octx, " ip6");
Packit c5a612
			break;
Packit c5a612
		}
Packit c5a612
Packit c5a612
		nft_print(octx, " to");
Packit c5a612
	}
Packit c5a612
Packit c5a612
	if (stmt->nat.addr) {
Packit c5a612
		nft_print(octx, " ");
Packit c5a612
		if (stmt->nat.proto) {
Packit c5a612
			if (stmt->nat.addr->etype == EXPR_VALUE &&
Packit c5a612
			    stmt->nat.addr->dtype->type == TYPE_IP6ADDR) {
Packit c5a612
				nft_print(octx, "[");
Packit c5a612
				expr_print(stmt->nat.addr, octx);
Packit c5a612
				nft_print(octx, "]");
Packit c5a612
			} else if (stmt->nat.addr->etype == EXPR_RANGE &&
Packit c5a612
				   stmt->nat.addr->left->dtype->type == TYPE_IP6ADDR) {
Packit c5a612
				nft_print(octx, "[");
Packit c5a612
				expr_print(stmt->nat.addr->left, octx);
Packit c5a612
				nft_print(octx, "]-[");
Packit c5a612
				expr_print(stmt->nat.addr->right, octx);
Packit c5a612
				nft_print(octx, "]");
Packit c5a612
			} else {
Packit c5a612
				expr_print(stmt->nat.addr, octx);
Packit c5a612
			}
Packit c5a612
		} else {
Packit c5a612
			expr_print(stmt->nat.addr, octx);
Packit c5a612
		}
Packit c5a612
	}
Packit c5a612
Packit c5a612
	if (stmt->nat.proto) {
Packit c5a612
		if (!stmt->nat.addr)
Packit c5a612
			nft_print(octx, " ");
Packit c5a612
		nft_print(octx, ":");
Packit c5a612
		expr_print(stmt->nat.proto, octx);
Packit c5a612
	}
Packit c5a612
Packit c5a612
	print_nf_nat_flags(stmt->nat.flags, octx);
Packit c5a612
}
Packit c5a612
Packit c5a612
static void nat_stmt_destroy(struct stmt *stmt)
Packit c5a612
{
Packit c5a612
	expr_free(stmt->nat.addr);
Packit c5a612
	expr_free(stmt->nat.proto);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops nat_stmt_ops = {
Packit c5a612
	.type		= STMT_NAT,
Packit c5a612
	.name		= "nat",
Packit c5a612
	.print		= nat_stmt_print,
Packit c5a612
	.json		= nat_stmt_json,
Packit c5a612
	.destroy	= nat_stmt_destroy,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *nat_stmt_alloc(const struct location *loc,
Packit c5a612
			    enum nft_nat_etypes type)
Packit c5a612
{
Packit c5a612
	struct stmt *stmt = stmt_alloc(loc, &nat_stmt_ops);
Packit c5a612
Packit c5a612
	stmt->nat.type = type;
Packit c5a612
	return stmt;
Packit c5a612
}
Packit c5a612
Packit c5a612
const char * const set_stmt_op_names[] = {
Packit c5a612
	[NFT_DYNSET_OP_ADD]	= "add",
Packit c5a612
	[NFT_DYNSET_OP_UPDATE]	= "update",
Packit c5a612
	[NFT_DYNSET_OP_DELETE]  = "delete",
Packit c5a612
};
Packit c5a612
Packit c5a612
static void set_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	unsigned int flags = octx->flags;
Packit c5a612
Packit c5a612
	nft_print(octx, "%s ", set_stmt_op_names[stmt->set.op]);
Packit c5a612
	expr_print(stmt->set.set, octx);
Packit c5a612
	nft_print(octx, " { ");
Packit c5a612
	expr_print(stmt->set.key, octx);
Packit c5a612
	if (stmt->set.stmt) {
Packit c5a612
		nft_print(octx, " ");
Packit c5a612
		octx->flags |= NFT_CTX_OUTPUT_STATELESS;
Packit c5a612
		stmt_print(stmt->set.stmt, octx);
Packit c5a612
		octx->flags = flags;
Packit c5a612
	}
Packit c5a612
	nft_print(octx, " }");
Packit c5a612
}
Packit c5a612
Packit c5a612
static void set_stmt_destroy(struct stmt *stmt)
Packit c5a612
{
Packit c5a612
	expr_free(stmt->set.key);
Packit c5a612
	expr_free(stmt->set.set);
Packit c5a612
	stmt_free(stmt->set.stmt);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops set_stmt_ops = {
Packit c5a612
	.type		= STMT_SET,
Packit c5a612
	.name		= "set",
Packit c5a612
	.print		= set_stmt_print,
Packit c5a612
	.json		= set_stmt_json,
Packit c5a612
	.destroy	= set_stmt_destroy,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *set_stmt_alloc(const struct location *loc)
Packit c5a612
{
Packit c5a612
	return stmt_alloc(loc, &set_stmt_ops);
Packit c5a612
}
Packit c5a612
Packit c5a612
static void map_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	unsigned int flags = octx->flags;
Packit c5a612
Packit c5a612
	nft_print(octx, "%s ", set_stmt_op_names[stmt->map.op]);
Packit c5a612
	expr_print(stmt->map.set, octx);
Packit c5a612
	nft_print(octx, " { ");
Packit c5a612
	expr_print(stmt->map.key, octx);
Packit c5a612
	if (stmt->map.stmt) {
Packit c5a612
		nft_print(octx, " ");
Packit c5a612
		octx->flags |= NFT_CTX_OUTPUT_STATELESS;
Packit c5a612
		stmt_print(stmt->map.stmt, octx);
Packit c5a612
		octx->flags = flags;
Packit c5a612
	}
Packit c5a612
	nft_print(octx, " : ");
Packit c5a612
	expr_print(stmt->map.data, octx);
Packit c5a612
	nft_print(octx, " }");
Packit c5a612
}
Packit c5a612
Packit c5a612
static void map_stmt_destroy(struct stmt *stmt)
Packit c5a612
{
Packit c5a612
	expr_free(stmt->map.key);
Packit c5a612
	expr_free(stmt->map.data);
Packit c5a612
	expr_free(stmt->map.set);
Packit c5a612
	stmt_free(stmt->map.stmt);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops map_stmt_ops = {
Packit c5a612
	.type		= STMT_MAP,
Packit c5a612
	.name		= "map",
Packit c5a612
	.print		= map_stmt_print,
Packit c5a612
	.destroy	= map_stmt_destroy,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *map_stmt_alloc(const struct location *loc)
Packit c5a612
{
Packit c5a612
	return stmt_alloc(loc, &map_stmt_ops);
Packit c5a612
}
Packit c5a612
Packit c5a612
static void dup_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	nft_print(octx, "dup");
Packit c5a612
	if (stmt->dup.to != NULL) {
Packit c5a612
		nft_print(octx, " to ");
Packit c5a612
		expr_print(stmt->dup.to, octx);
Packit c5a612
Packit c5a612
		if (stmt->dup.dev != NULL) {
Packit c5a612
			nft_print(octx, " device ");
Packit c5a612
			expr_print(stmt->dup.dev, octx);
Packit c5a612
		}
Packit c5a612
	}
Packit c5a612
}
Packit c5a612
Packit c5a612
static void dup_stmt_destroy(struct stmt *stmt)
Packit c5a612
{
Packit c5a612
	expr_free(stmt->dup.to);
Packit c5a612
	expr_free(stmt->dup.dev);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops dup_stmt_ops = {
Packit c5a612
	.type		= STMT_DUP,
Packit c5a612
	.name		= "dup",
Packit c5a612
	.print		= dup_stmt_print,
Packit c5a612
	.json		= dup_stmt_json,
Packit c5a612
	.destroy	= dup_stmt_destroy,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *dup_stmt_alloc(const struct location *loc)
Packit c5a612
{
Packit c5a612
	return stmt_alloc(loc, &dup_stmt_ops);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const char * const nfproto_family_name_array[NFPROTO_NUMPROTO] = {
Packit c5a612
	[NFPROTO_IPV4]	= "ip",
Packit c5a612
	[NFPROTO_IPV6]	= "ip6",
Packit c5a612
};
Packit c5a612
Packit c5a612
static const char *nfproto_family_name(uint8_t nfproto)
Packit c5a612
{
Packit c5a612
	if (nfproto >= NFPROTO_NUMPROTO || !nfproto_family_name_array[nfproto])
Packit c5a612
		return "unknown";
Packit c5a612
Packit c5a612
	return nfproto_family_name_array[nfproto];
Packit c5a612
}
Packit c5a612
Packit c5a612
static void fwd_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	if (stmt->fwd.addr) {
Packit c5a612
		nft_print(octx, "fwd %s to ",
Packit c5a612
			  nfproto_family_name(stmt->fwd.family));
Packit c5a612
		expr_print(stmt->fwd.addr, octx);
Packit c5a612
		nft_print(octx, " device ");
Packit c5a612
		expr_print(stmt->fwd.dev, octx);
Packit c5a612
	} else {
Packit c5a612
		nft_print(octx, "fwd to ");
Packit c5a612
		expr_print(stmt->fwd.dev, octx);
Packit c5a612
	}
Packit c5a612
}
Packit c5a612
Packit c5a612
static void fwd_stmt_destroy(struct stmt *stmt)
Packit c5a612
{
Packit c5a612
	expr_free(stmt->fwd.addr);
Packit c5a612
	expr_free(stmt->fwd.dev);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops fwd_stmt_ops = {
Packit c5a612
	.type		= STMT_FWD,
Packit c5a612
	.name		= "fwd",
Packit c5a612
	.print		= fwd_stmt_print,
Packit c5a612
	.json		= fwd_stmt_json,
Packit c5a612
	.destroy	= fwd_stmt_destroy,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *fwd_stmt_alloc(const struct location *loc)
Packit c5a612
{
Packit c5a612
	return stmt_alloc(loc, &fwd_stmt_ops);
Packit c5a612
}
Packit c5a612
Packit c5a612
static void tproxy_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	nft_print(octx, "tproxy");
Packit c5a612
Packit c5a612
	if (stmt->tproxy.table_family == NFPROTO_INET &&
Packit c5a612
	    stmt->tproxy.family != NFPROTO_UNSPEC)
Packit c5a612
		nft_print(octx, " %s", nfproto_family_name(stmt->tproxy.family));
Packit c5a612
	nft_print(octx, " to");
Packit c5a612
	if (stmt->tproxy.addr) {
Packit c5a612
		nft_print(octx, " ");
Packit c5a612
		if (stmt->tproxy.addr->etype == EXPR_VALUE &&
Packit c5a612
		    stmt->tproxy.addr->dtype->type == TYPE_IP6ADDR) {
Packit c5a612
			nft_print(octx, "[");
Packit c5a612
			expr_print(stmt->tproxy.addr, octx);
Packit c5a612
			nft_print(octx, "]");
Packit c5a612
		} else {
Packit c5a612
			expr_print(stmt->tproxy.addr, octx);
Packit c5a612
		}
Packit c5a612
	}
Packit c5a612
	if (stmt->tproxy.port && stmt->tproxy.port->etype == EXPR_VALUE) {
Packit c5a612
		if (!stmt->tproxy.addr)
Packit c5a612
			nft_print(octx, " ");
Packit c5a612
		nft_print(octx, ":");
Packit c5a612
		expr_print(stmt->tproxy.port, octx);
Packit c5a612
	}
Packit c5a612
}
Packit c5a612
Packit c5a612
static void tproxy_stmt_destroy(struct stmt *stmt)
Packit c5a612
{
Packit c5a612
	expr_free(stmt->tproxy.addr);
Packit c5a612
	expr_free(stmt->tproxy.port);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops tproxy_stmt_ops = {
Packit c5a612
	.type		= STMT_TPROXY,
Packit c5a612
	.name		= "tproxy",
Packit c5a612
	.print		= tproxy_stmt_print,
Packit c5a612
	.json		= tproxy_stmt_json,
Packit c5a612
	.destroy	= tproxy_stmt_destroy,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *tproxy_stmt_alloc(const struct location *loc)
Packit c5a612
{
Packit c5a612
	return stmt_alloc(loc, &tproxy_stmt_ops);
Packit c5a612
}
Packit c5a612
Packit c5a612
static void xt_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	xt_stmt_xlate(stmt, octx);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops xt_stmt_ops = {
Packit c5a612
	.type		= STMT_XT,
Packit c5a612
	.name		= "xt",
Packit c5a612
	.print		= xt_stmt_print,
Packit c5a612
	.destroy	= xt_stmt_destroy,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *xt_stmt_alloc(const struct location *loc)
Packit c5a612
{
Packit c5a612
	return stmt_alloc(loc, &xt_stmt_ops);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const char *synproxy_sack_to_str(const uint32_t flags)
Packit c5a612
{
Packit c5a612
	if (flags & NF_SYNPROXY_OPT_SACK_PERM)
Packit c5a612
		return " sack-perm";
Packit c5a612
Packit c5a612
	return "";
Packit c5a612
}
Packit c5a612
Packit c5a612
static const char *synproxy_timestamp_to_str(const uint32_t flags)
Packit c5a612
{
Packit c5a612
	if (flags & NF_SYNPROXY_OPT_TIMESTAMP)
Packit c5a612
		return " timestamp";
Packit c5a612
Packit c5a612
	return "";
Packit c5a612
}
Packit c5a612
Packit c5a612
static void synproxy_stmt_print(const struct stmt *stmt,
Packit c5a612
				struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	uint32_t flags = stmt->synproxy.flags;
Packit c5a612
	const char *ts_str = synproxy_timestamp_to_str(flags);
Packit c5a612
	const char *sack_str = synproxy_sack_to_str(flags);
Packit c5a612
Packit c5a612
	if (flags & (NF_SYNPROXY_OPT_MSS | NF_SYNPROXY_OPT_WSCALE))
Packit c5a612
		nft_print(octx, "synproxy mss %u wscale %u%s%s",
Packit c5a612
			  stmt->synproxy.mss, stmt->synproxy.wscale,
Packit c5a612
			  ts_str, sack_str);
Packit c5a612
	else if (flags & NF_SYNPROXY_OPT_MSS)
Packit c5a612
		nft_print(octx, "synproxy mss %u%s%s", stmt->synproxy.mss,
Packit c5a612
			  ts_str, sack_str);
Packit c5a612
	else if (flags & NF_SYNPROXY_OPT_WSCALE)
Packit c5a612
		nft_print(octx, "synproxy wscale %u%s%s", stmt->synproxy.wscale,
Packit c5a612
			  ts_str, sack_str);
Packit c5a612
	else
Packit c5a612
		nft_print(octx, "synproxy%s%s", ts_str, sack_str);
Packit c5a612
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops synproxy_stmt_ops = {
Packit c5a612
	.type		= STMT_SYNPROXY,
Packit c5a612
	.name		= "synproxy",
Packit c5a612
	.print		= synproxy_stmt_print,
Packit c5a612
	.json		= synproxy_stmt_json,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *synproxy_stmt_alloc(const struct location *loc)
Packit c5a612
{
Packit c5a612
	return stmt_alloc(loc, &synproxy_stmt_ops);
Packit c5a612
}