summaryrefslogtreecommitdiffstats
path: root/src/statement.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/statement.c')
-rw-r--r--src/statement.c208
1 files changed, 176 insertions, 32 deletions
diff --git a/src/statement.c b/src/statement.c
index be35bcef..ab144d63 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -8,19 +8,21 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
#include <inttypes.h>
-#include <string.h>
#include <syslog.h>
+#include <rule.h>
#include <arpa/inet.h>
#include <linux/netfilter.h>
+#include <linux/netfilter/nf_log.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
#include <statement.h>
+#include <tcpopt.h>
#include <utils.h>
#include <list.h>
#include <xt.h>
@@ -49,7 +51,7 @@ void stmt_free(struct stmt *stmt)
return;
if (stmt->ops->destroy)
stmt->ops->destroy(stmt);
- xfree(stmt);
+ free(stmt);
}
void stmt_list_free(struct list_head *list)
@@ -111,6 +113,50 @@ struct stmt *verdict_stmt_alloc(const struct location *loc, struct expr *expr)
return stmt;
}
+static const char *chain_verdict(const struct expr *expr)
+{
+ switch (expr->verdict) {
+ case NFT_JUMP:
+ return "jump";
+ case NFT_GOTO:
+ return "goto";
+ default:
+ BUG("unknown chain verdict");
+ }
+}
+
+static void chain_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
+{
+ nft_print(octx, "%s {\n", chain_verdict(stmt->chain.expr));
+ chain_rules_print(stmt->chain.chain, octx, "\t");
+ nft_print(octx, "\t\t}");
+}
+
+static void chain_stmt_destroy(struct stmt *stmt)
+{
+ expr_free(stmt->chain.expr);
+}
+
+static const struct stmt_ops chain_stmt_ops = {
+ .type = STMT_CHAIN,
+ .name = "chain",
+ .print = chain_stmt_print,
+ .destroy = chain_stmt_destroy,
+};
+
+struct stmt *chain_stmt_alloc(const struct location *loc, struct chain *chain,
+ enum nft_verdicts verdict)
+{
+ struct stmt *stmt;
+
+ stmt = stmt_alloc(loc, &chain_stmt_ops);
+ stmt->chain.chain = chain;
+ stmt->chain.expr = verdict_expr_alloc(loc, verdict, NULL);
+ stmt->chain.expr->chain_id = chain->handle.chain_id;
+
+ return stmt;
+}
+
static void meter_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
unsigned int flags = octx->flags;
@@ -137,7 +183,7 @@ static void meter_stmt_destroy(struct stmt *stmt)
expr_free(stmt->meter.key);
expr_free(stmt->meter.set);
stmt_free(stmt->meter.stmt);
- xfree(stmt->meter.name);
+ free_const(stmt->meter.name);
}
static const struct stmt_ops meter_stmt_ops = {
@@ -155,7 +201,7 @@ struct stmt *meter_stmt_alloc(const struct location *loc)
static void connlimit_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
- nft_print(octx, "ct count %s%u ",
+ nft_print(octx, "ct count %s%u",
stmt->connlimit.flags ? "over " : "", stmt->connlimit.count);
}
@@ -202,6 +248,37 @@ struct stmt *counter_stmt_alloc(const struct location *loc)
return stmt;
}
+static void last_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
+{
+ nft_print(octx, "last");
+
+ if (nft_output_stateless(octx))
+ return;
+
+ nft_print(octx, " used ");
+
+ if (stmt->last.set)
+ time_print(stmt->last.used, octx);
+ else
+ nft_print(octx, "never");
+}
+
+static const struct stmt_ops last_stmt_ops = {
+ .type = STMT_LAST,
+ .name = "last",
+ .print = last_stmt_print,
+ .json = last_stmt_json,
+};
+
+struct stmt *last_stmt_alloc(const struct location *loc)
+{
+ struct stmt *stmt;
+
+ stmt = stmt_alloc(loc, &last_stmt_ops);
+ stmt->flags |= STMT_F_STATEFUL;
+ return stmt;
+}
+
static const char *objref_type[NFT_OBJECT_MAX + 1] = {
[NFT_OBJECT_COUNTER] = "counter",
[NFT_OBJECT_QUOTA] = "quota",
@@ -300,8 +377,12 @@ int log_level_parse(const char *level)
static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
nft_print(octx, "log");
- if (stmt->log.flags & STMT_LOG_PREFIX)
- nft_print(octx, " prefix \"%s\"", stmt->log.prefix);
+ if (stmt->log.flags & STMT_LOG_PREFIX) {
+ char prefix[NF_LOG_PREFIXLEN] = {};
+
+ expr_to_string(stmt->log.prefix, prefix);
+ nft_print(octx, " prefix \"%s\"", prefix);
+ }
if (stmt->log.flags & STMT_LOG_GROUP)
nft_print(octx, " group %u", stmt->log.group);
if (stmt->log.flags & STMT_LOG_SNAPLEN)
@@ -338,7 +419,7 @@ static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
static void log_stmt_destroy(struct stmt *stmt)
{
- xfree(stmt->log.prefix);
+ expr_free(stmt->log.prefix);
}
static const struct stmt_ops log_stmt_ops = {
@@ -404,9 +485,7 @@ static void limit_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
nft_print(octx, "limit rate %s%" PRIu64 "/%s",
inv ? "over " : "", stmt->limit.rate,
get_unit(stmt->limit.unit));
- if (stmt->limit.burst && stmt->limit.burst != 5)
- nft_print(octx, " burst %u packets",
- stmt->limit.burst);
+ nft_print(octx, " burst %u packets", stmt->limit.burst);
break;
case NFT_LIMIT_PKT_BYTES:
data_unit = get_rate(stmt->limit.rate, &rate);
@@ -414,7 +493,7 @@ static void limit_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
nft_print(octx, "limit rate %s%" PRIu64 " %s/%s",
inv ? "over " : "", rate, data_unit,
get_unit(stmt->limit.unit));
- if (stmt->limit.burst > 0) {
+ if (stmt->limit.burst != 0) {
uint64_t burst;
data_unit = get_rate(stmt->limit.burst, &burst);
@@ -443,20 +522,25 @@ struct stmt *limit_stmt_alloc(const struct location *loc)
static void queue_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
- const char *delim = " ";
+ struct expr *e = stmt->queue.queue;
+ const char *delim = " flags ";
nft_print(octx, "queue");
- if (stmt->queue.queue != NULL) {
- nft_print(octx, " num ");
- expr_print(stmt->queue.queue, octx);
- }
+
if (stmt->queue.flags & NFT_QUEUE_FLAG_BYPASS) {
nft_print(octx, "%sbypass", delim);
delim = ",";
}
+
if (stmt->queue.flags & NFT_QUEUE_FLAG_CPU_FANOUT)
nft_print(octx, "%sfanout", delim);
+ if (e) {
+ nft_print(octx, " to ");
+ expr_print(stmt->queue.queue, octx);
+ } else {
+ nft_print(octx, " to 0");
+ }
}
static void queue_stmt_destroy(struct stmt *stmt)
@@ -472,9 +556,15 @@ static const struct stmt_ops queue_stmt_ops = {
.destroy = queue_stmt_destroy,
};
-struct stmt *queue_stmt_alloc(const struct location *loc)
+struct stmt *queue_stmt_alloc(const struct location *loc, struct expr *e, uint16_t flags)
{
- return stmt_alloc(loc, &queue_stmt_ops);
+ struct stmt *stmt;
+
+ stmt = stmt_alloc(loc, &queue_stmt_ops);
+ stmt->queue.queue = e;
+ stmt->queue.flags = flags;
+
+ return stmt;
}
static void quota_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
@@ -519,7 +609,7 @@ static void reject_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
case NFT_REJECT_ICMPX_UNREACH:
if (stmt->reject.icmp_code == NFT_REJECT_ICMPX_PORT_UNREACH)
break;
- nft_print(octx, " with icmpx type ");
+ nft_print(octx, " with icmpx ");
expr_print(stmt->reject.expr, octx);
break;
case NFT_REJECT_ICMP_UNREACH:
@@ -528,14 +618,14 @@ static void reject_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
if (!stmt->reject.verbose_print &&
stmt->reject.icmp_code == ICMP_PORT_UNREACH)
break;
- nft_print(octx, " with icmp type ");
+ nft_print(octx, " with icmp ");
expr_print(stmt->reject.expr, octx);
break;
case NFPROTO_IPV6:
if (!stmt->reject.verbose_print &&
stmt->reject.icmp_code == ICMP6_DST_UNREACH_NOPORT)
break;
- nft_print(octx, " with icmpv6 type ");
+ nft_print(octx, " with icmpv6 ");
expr_print(stmt->reject.expr, octx);
break;
}
@@ -607,6 +697,9 @@ static void nat_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
break;
}
+ if (stmt->nat.type_flags & STMT_NAT_F_PREFIX)
+ nft_print(octx, " prefix");
+
nft_print(octx, " to");
}
@@ -675,15 +768,16 @@ const char * const set_stmt_op_names[] = {
static void set_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
unsigned int flags = octx->flags;
+ struct stmt *this;
nft_print(octx, "%s ", set_stmt_op_names[stmt->set.op]);
expr_print(stmt->set.set, octx);
nft_print(octx, " { ");
expr_print(stmt->set.key, octx);
- if (stmt->set.stmt) {
+ list_for_each_entry(this, &stmt->set.stmt_list, list) {
nft_print(octx, " ");
octx->flags |= NFT_CTX_OUTPUT_STATELESS;
- stmt_print(stmt->set.stmt, octx);
+ stmt_print(this, octx);
octx->flags = flags;
}
nft_print(octx, " }");
@@ -691,9 +785,12 @@ static void set_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
static void set_stmt_destroy(struct stmt *stmt)
{
+ struct stmt *this, *next;
+
expr_free(stmt->set.key);
expr_free(stmt->set.set);
- stmt_free(stmt->set.stmt);
+ list_for_each_entry_safe(this, next, &stmt->set.stmt_list, list)
+ stmt_free(this);
}
static const struct stmt_ops set_stmt_ops = {
@@ -706,21 +803,27 @@ static const struct stmt_ops set_stmt_ops = {
struct stmt *set_stmt_alloc(const struct location *loc)
{
- return stmt_alloc(loc, &set_stmt_ops);
+ struct stmt *stmt;
+
+ stmt = stmt_alloc(loc, &set_stmt_ops);
+ init_list_head(&stmt->set.stmt_list);
+
+ return stmt;
}
static void map_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
unsigned int flags = octx->flags;
+ struct stmt *this;
nft_print(octx, "%s ", set_stmt_op_names[stmt->map.op]);
expr_print(stmt->map.set, octx);
nft_print(octx, " { ");
expr_print(stmt->map.key, octx);
- if (stmt->map.stmt) {
+ list_for_each_entry(this, &stmt->map.stmt_list, list) {
nft_print(octx, " ");
octx->flags |= NFT_CTX_OUTPUT_STATELESS;
- stmt_print(stmt->map.stmt, octx);
+ stmt_print(this, octx);
octx->flags = flags;
}
nft_print(octx, " : ");
@@ -730,10 +833,13 @@ static void map_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
static void map_stmt_destroy(struct stmt *stmt)
{
+ struct stmt *this, *next;
+
expr_free(stmt->map.key);
expr_free(stmt->map.data);
expr_free(stmt->map.set);
- stmt_free(stmt->map.stmt);
+ list_for_each_entry_safe(this, next, &stmt->map.stmt_list, list)
+ stmt_free(this);
}
static const struct stmt_ops map_stmt_ops = {
@@ -741,11 +847,17 @@ static const struct stmt_ops map_stmt_ops = {
.name = "map",
.print = map_stmt_print,
.destroy = map_stmt_destroy,
+ .json = map_stmt_json,
};
struct stmt *map_stmt_alloc(const struct location *loc)
{
- return stmt_alloc(loc, &map_stmt_ops);
+ struct stmt *stmt;
+
+ stmt = stmt_alloc(loc, &map_stmt_ops);
+ init_list_head(&stmt->map.stmt_list);
+
+ return stmt;
}
static void dup_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
@@ -827,6 +939,37 @@ struct stmt *fwd_stmt_alloc(const struct location *loc)
return stmt_alloc(loc, &fwd_stmt_ops);
}
+static void optstrip_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
+{
+ const struct expr *expr = stmt->optstrip.expr;
+
+ nft_print(octx, "reset ");
+ expr_print(expr, octx);
+}
+
+static void optstrip_stmt_destroy(struct stmt *stmt)
+{
+ expr_free(stmt->optstrip.expr);
+}
+
+static const struct stmt_ops optstrip_stmt_ops = {
+ .type = STMT_OPTSTRIP,
+ .name = "optstrip",
+ .print = optstrip_stmt_print,
+ .json = optstrip_stmt_json,
+ .destroy = optstrip_stmt_destroy,
+};
+
+struct stmt *optstrip_stmt_alloc(const struct location *loc, struct expr *e)
+{
+ struct stmt *stmt = stmt_alloc(loc, &optstrip_stmt_ops);
+
+ e->exthdr.flags |= NFT_EXTHDR_F_PRESENT;
+ stmt->optstrip.expr = e;
+
+ return stmt;
+}
+
static void tproxy_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
nft_print(octx, "tproxy");
@@ -846,7 +989,7 @@ static void tproxy_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
expr_print(stmt->tproxy.addr, octx);
}
}
- if (stmt->tproxy.port && stmt->tproxy.port->etype == EXPR_VALUE) {
+ if (stmt->tproxy.port) {
if (!stmt->tproxy.addr)
nft_print(octx, " ");
nft_print(octx, ":");
@@ -883,6 +1026,7 @@ static const struct stmt_ops xt_stmt_ops = {
.name = "xt",
.print = xt_stmt_print,
.destroy = xt_stmt_destroy,
+ .json = xt_stmt_json,
};
struct stmt *xt_stmt_alloc(const struct location *loc)