summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/datatype.c105
-rw-r--r--src/evaluate.c184
-rw-r--r--src/netlink_delinearize.c67
-rw-r--r--src/netlink_linearize.c5
-rw-r--r--src/parser.y54
-rw-r--r--src/payload.c31
-rw-r--r--src/scanner.l3
-rw-r--r--src/statement.c31
8 files changed, 470 insertions, 10 deletions
diff --git a/src/datatype.c b/src/datatype.c
index 7090b0ba..8ad211c1 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -24,6 +24,9 @@
#include <gmputil.h>
#include <erec.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+
static const struct datatype *datatypes[TYPE_MAX + 1] = {
[TYPE_INVALID] = &invalid_type,
[TYPE_VERDICT] = &verdict_type,
@@ -41,6 +44,9 @@ static const struct datatype *datatypes[TYPE_MAX + 1] = {
[TYPE_TIME] = &time_type,
[TYPE_MARK] = &mark_type,
[TYPE_ARPHRD] = &arphrd_type,
+ [TYPE_ICMP_CODE] = &icmp_code_type,
+ [TYPE_ICMPV6_CODE] = &icmpv6_code_type,
+ [TYPE_ICMPX_CODE] = &icmpx_code_type,
};
void datatype_register(const struct datatype *dtype)
@@ -685,6 +691,105 @@ const struct datatype mark_type = {
.flags = DTYPE_F_PREFIX,
};
+static const struct symbol_table icmp_code_tbl = {
+ .symbols = {
+ SYMBOL("net-unreachable", ICMP_NET_UNREACH),
+ SYMBOL("host-unreachable", ICMP_HOST_UNREACH),
+ SYMBOL("prot-unreachable", ICMP_PROT_UNREACH),
+ SYMBOL("port-unreachable", ICMP_PORT_UNREACH),
+ SYMBOL("net-prohibited", ICMP_NET_ANO),
+ SYMBOL("host-prohibited", ICMP_HOST_ANO),
+ SYMBOL("admin-prohibited", ICMP_PKT_FILTERED),
+ SYMBOL_LIST_END
+ },
+};
+
+static void icmp_code_type_print(const struct expr *expr)
+{
+ return symbolic_constant_print(&icmp_code_tbl, expr);
+}
+
+static struct error_record *icmp_code_type_parse(const struct expr *sym,
+ struct expr **res)
+{
+ return symbolic_constant_parse(sym, &icmp_code_tbl, res);
+}
+
+const struct datatype icmp_code_type = {
+ .type = TYPE_ICMP_CODE,
+ .name = "icmp code",
+ .desc = "icmp code",
+ .size = BITS_PER_BYTE,
+ .byteorder = BYTEORDER_BIG_ENDIAN,
+ .basetype = &integer_type,
+ .print = icmp_code_type_print,
+ .parse = icmp_code_type_parse,
+};
+
+static const struct symbol_table icmpv6_code_tbl = {
+ .symbols = {
+ SYMBOL("no-route", ICMP6_DST_UNREACH_NOROUTE),
+ SYMBOL("admin-prohibited", ICMP6_DST_UNREACH_ADMIN),
+ SYMBOL("addr-unreachable", ICMP6_DST_UNREACH_ADDR),
+ SYMBOL("port-unreachable", ICMP6_DST_UNREACH_NOPORT),
+ SYMBOL_LIST_END
+ },
+};
+
+static void icmpv6_code_type_print(const struct expr *expr)
+{
+ return symbolic_constant_print(&icmpv6_code_tbl, expr);
+}
+
+static struct error_record *icmpv6_code_type_parse(const struct expr *sym,
+ struct expr **res)
+{
+ return symbolic_constant_parse(sym, &icmpv6_code_tbl, res);
+}
+
+const struct datatype icmpv6_code_type = {
+ .type = TYPE_ICMPV6_CODE,
+ .name = "icmpv6 code",
+ .desc = "icmpv6 code",
+ .size = BITS_PER_BYTE,
+ .byteorder = BYTEORDER_BIG_ENDIAN,
+ .basetype = &integer_type,
+ .print = icmpv6_code_type_print,
+ .parse = icmpv6_code_type_parse,
+};
+
+static const struct symbol_table icmpx_code_tbl = {
+ .symbols = {
+ SYMBOL("port-unreachable", NFT_REJECT_ICMPX_PORT_UNREACH),
+ SYMBOL("admin-prohibited", NFT_REJECT_ICMPX_ADMIN_PROHIBITED),
+ SYMBOL("no-route", NFT_REJECT_ICMPX_NO_ROUTE),
+ SYMBOL("host-unreachable", NFT_REJECT_ICMPX_HOST_UNREACH),
+ SYMBOL_LIST_END
+ },
+};
+
+static void icmpx_code_type_print(const struct expr *expr)
+{
+ return symbolic_constant_print(&icmpx_code_tbl, expr);
+}
+
+static struct error_record *icmpx_code_type_parse(const struct expr *sym,
+ struct expr **res)
+{
+ return symbolic_constant_parse(sym, &icmpx_code_tbl, res);
+}
+
+const struct datatype icmpx_code_type = {
+ .type = TYPE_ICMPX_CODE,
+ .name = "icmpx code",
+ .desc = "icmpx code",
+ .size = BITS_PER_BYTE,
+ .byteorder = BYTEORDER_BIG_ENDIAN,
+ .basetype = &integer_type,
+ .print = icmpx_code_type_print,
+ .parse = icmpx_code_type_parse,
+};
+
static void time_type_print(const struct expr *expr)
{
uint64_t days, hours, minutes, seconds;
diff --git a/src/evaluate.c b/src/evaluate.c
index 52ce5487..602148c8 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -17,6 +17,8 @@
#include <linux/netfilter.h>
#include <linux/netfilter_arp.h>
#include <linux/netfilter/nf_tables.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
#include <expression.h>
#include <statement.h>
@@ -1126,12 +1128,192 @@ static int stmt_evaluate_meta(struct eval_ctx *ctx, struct stmt *stmt)
return 0;
}
-static int stmt_evaluate_reject(struct eval_ctx *ctx, struct stmt *stmt)
+static int reject_payload_gen_dependency_tcp(struct eval_ctx *ctx,
+ struct stmt *stmt,
+ struct expr **payload)
+{
+ const struct proto_desc *desc;
+
+ desc = ctx->pctx.protocol[PROTO_BASE_TRANSPORT_HDR].desc;
+ if (desc != NULL)
+ return 0;
+ *payload = payload_expr_alloc(&stmt->location, &proto_tcp,
+ TCPHDR_UNSPEC);
+ return 0;
+}
+
+static int reject_payload_gen_dependency_family(struct eval_ctx *ctx,
+ struct stmt *stmt,
+ struct expr **payload)
+{
+ const struct proto_desc *base;
+
+ base = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ if (base != NULL)
+ return 0;
+
+ if (stmt->reject.icmp_code < 0)
+ return stmt_error(ctx, stmt, "missing icmp error type");
+
+ /* Generate a network dependency */
+ switch (stmt->reject.family) {
+ case NFPROTO_IPV4:
+ *payload = payload_expr_alloc(&stmt->location, &proto_ip,
+ IPHDR_PROTOCOL);
+ break;
+ case NFPROTO_IPV6:
+ *payload = payload_expr_alloc(&stmt->location, &proto_ip6,
+ IP6HDR_NEXTHDR);
+ break;
+ default:
+ BUG("unknown reject family");
+ }
+ return 0;
+}
+
+static int stmt_reject_gen_dependency(struct eval_ctx *ctx, struct stmt *stmt,
+ struct expr *expr)
+{
+ struct expr *payload = NULL;
+ struct stmt *nstmt;
+
+ switch (stmt->reject.type) {
+ case NFT_REJECT_TCP_RST:
+ if (reject_payload_gen_dependency_tcp(ctx, stmt, &payload) < 0)
+ return -1;
+ break;
+ case NFT_REJECT_ICMP_UNREACH:
+ if (reject_payload_gen_dependency_family(ctx, stmt,
+ &payload) < 0)
+ return -1;
+ break;
+ default:
+ BUG("cannot generate reject dependency for type %d",
+ stmt->reject.type);
+ }
+
+ if (payload_gen_dependency(ctx, payload, &nstmt) < 0)
+ return -1;
+
+ list_add(&nstmt->list, &ctx->cmd->rule->stmts);
+ return 0;
+}
+
+static int stmt_evaluate_reject_family(struct eval_ctx *ctx, struct stmt *stmt,
+ struct expr *expr)
{
+ switch (ctx->pctx.family) {
+ case NFPROTO_ARP:
+ return stmt_error(ctx, stmt, "cannot use reject with arp");
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ switch (stmt->reject.type) {
+ case NFT_REJECT_TCP_RST:
+ if (stmt_reject_gen_dependency(ctx, stmt, expr) < 0)
+ return -1;
+ break;
+ case NFT_REJECT_ICMPX_UNREACH:
+ return stmt_error(ctx, stmt,
+ "abstracted ICMP unreachable not supported");
+ case NFT_REJECT_ICMP_UNREACH:
+ if (stmt->reject.family != ctx->pctx.family)
+ return stmt_error(ctx, stmt,
+ "conflicting protocols specified: ip vs ip6");
+ break;
+ }
+ break;
+ case NFPROTO_INET:
+ case NFPROTO_BRIDGE:
+ if (stmt->reject.type == NFT_REJECT_ICMPX_UNREACH)
+ break;
+ if (stmt_reject_gen_dependency(ctx, stmt, expr) < 0)
+ return -1;
+ break;
+ }
+
stmt->flags |= STMT_F_TERMINAL;
return 0;
}
+static int stmt_evaluate_reject_default(struct eval_ctx *ctx,
+ struct stmt *stmt)
+{
+ switch (ctx->pctx.family) {
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
+ stmt->reject.family = ctx->pctx.family;
+ if (ctx->pctx.family == NFPROTO_IPV4)
+ stmt->reject.icmp_code = ICMP_PORT_UNREACH;
+ else
+ stmt->reject.icmp_code = ICMP6_DST_UNREACH_NOPORT;
+ break;
+ case NFPROTO_INET:
+ case NFPROTO_BRIDGE:
+ stmt->reject.type = NFT_REJECT_ICMPX_UNREACH;
+ stmt->reject.icmp_code = NFT_REJECT_ICMPX_PORT_UNREACH;
+ break;
+ }
+ return 0;
+}
+
+static int stmt_evaluate_reject_icmp(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ struct error_record *erec;
+ struct expr *code;
+
+ erec = symbol_parse(stmt->reject.expr, &code);
+ if (erec != NULL) {
+ erec_queue(erec, ctx->msgs);
+ return -1;
+ }
+ stmt->reject.icmp_code = mpz_get_uint8(code->value);
+ return 0;
+}
+
+static int stmt_evaluate_reset(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ int protonum;
+ const struct proto_desc *desc, *base;
+ struct proto_ctx *pctx = &ctx->pctx;
+
+ base = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc;
+ if (desc == NULL)
+ return 0;
+
+ protonum = proto_find_num(base, desc);
+ switch (protonum) {
+ case IPPROTO_TCP:
+ break;
+ default:
+ if (stmt->reject.type == NFT_REJECT_TCP_RST) {
+ return stmt_error(ctx, stmt,
+ "you cannot use tcp reset with this protocol");
+ }
+ break;
+ }
+ return 0;
+}
+
+static int stmt_evaluate_reject(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ struct expr *expr = ctx->cmd->expr;
+
+ if (stmt->reject.icmp_code < 0) {
+ if (stmt_evaluate_reject_default(ctx, stmt) < 0)
+ return -1;
+ } else if (stmt->reject.expr != NULL) {
+ if (stmt_evaluate_reject_icmp(ctx, stmt) < 0)
+ return -1;
+ } else {
+ if (stmt_evaluate_reset(ctx, stmt) < 0)
+ return -1;
+ }
+
+ return stmt_evaluate_reject_family(ctx, stmt, expr);
+}
+
static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
{
struct proto_ctx *pctx = &ctx->pctx;
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 796b6327..397b65c0 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -14,6 +14,9 @@
#include <string.h>
#include <limits.h>
#include <linux/netfilter/nf_tables.h>
+#include <arpa/inet.h>
+#include <linux/netfilter.h>
+#include <net/ethernet.h>
#include <netlink.h>
#include <rule.h>
#include <statement.h>
@@ -472,8 +475,15 @@ static void netlink_parse_reject(struct netlink_parse_ctx *ctx,
const struct nft_rule_expr *expr)
{
struct stmt *stmt;
+ uint8_t icmp_code;
stmt = reject_stmt_alloc(loc);
+ stmt->reject.type = nft_rule_expr_get_u32(expr, NFT_EXPR_REJECT_TYPE);
+ icmp_code = nft_rule_expr_get_u8(expr, NFT_EXPR_REJECT_CODE);
+ stmt->reject.icmp_code = icmp_code;
+ stmt->reject.expr = constant_expr_alloc(loc, &integer_type,
+ BYTEORDER_HOST_ENDIAN, 8,
+ &icmp_code);
list_add_tail(&stmt->list, &ctx->rule->stmts);
}
@@ -899,6 +909,60 @@ static void expr_postprocess(struct rule_pp_ctx *ctx,
}
}
+static void stmt_reject_postprocess(struct rule_pp_ctx rctx, struct stmt *stmt)
+{
+ const struct proto_desc *desc, *base;
+ int protocol;
+
+ switch (rctx.pctx.family) {
+ case NFPROTO_IPV4:
+ stmt->reject.family = rctx.pctx.family;
+ stmt->reject.expr->dtype = &icmp_code_type;
+ break;
+ case NFPROTO_IPV6:
+ stmt->reject.family = rctx.pctx.family;
+ stmt->reject.expr->dtype = &icmpv6_code_type;
+ break;
+ case NFPROTO_INET:
+ if (stmt->reject.type == NFT_REJECT_ICMPX_UNREACH)
+ break;
+ base = rctx.pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = rctx.pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ protocol = proto_find_num(base, desc);
+ switch (protocol) {
+ case NFPROTO_IPV4:
+ stmt->reject.expr->dtype = &icmp_code_type;
+ break;
+ case NFPROTO_IPV6:
+ stmt->reject.expr->dtype = &icmpv6_code_type;
+ break;
+ }
+ stmt->reject.family = protocol;
+ break;
+ case NFPROTO_BRIDGE:
+ if (stmt->reject.type == NFT_REJECT_ICMPX_UNREACH)
+ break;
+ base = rctx.pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = rctx.pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ protocol = proto_find_num(base, desc);
+ switch (protocol) {
+ case __constant_htons(ETH_P_IP):
+ stmt->reject.family = NFPROTO_IPV4;
+ stmt->reject.expr->dtype = &icmp_code_type;
+ break;
+ case __constant_htons(ETH_P_IPV6):
+ stmt->reject.family = NFPROTO_IPV6;
+ stmt->reject.expr->dtype = &icmpv6_code_type;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *rule)
{
struct rule_pp_ctx rctx;
@@ -926,6 +990,9 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
if (stmt->nat.proto != NULL)
expr_postprocess(&rctx, stmt, &stmt->nat.proto);
break;
+ case STMT_REJECT:
+ stmt_reject_postprocess(rctx, stmt);
+ break;
default:
break;
}
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index c46b6d47..29f8e9ae 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -612,7 +612,10 @@ static void netlink_gen_reject_stmt(struct netlink_linearize_ctx *ctx,
nle = alloc_nft_expr("reject");
nft_rule_expr_set_u32(nle, NFT_EXPR_REJECT_TYPE, stmt->reject.type);
- nft_rule_expr_set_u8(nle, NFT_EXPR_REJECT_CODE, 0);
+ if (stmt->reject.icmp_code != -1)
+ nft_rule_expr_set_u8(nle, NFT_EXPR_REJECT_CODE,
+ stmt->reject.icmp_code);
+
nft_rule_add_expr(ctx->nlr, nle);
}
diff --git a/src/parser.y b/src/parser.y
index 4a8df7b7..03d6d138 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -19,6 +19,8 @@
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nf_conntrack_tuple_common.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
#include <libnftnl/common.h>
#include <libnftnl/set.h>
@@ -368,6 +370,9 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%token WEEK "week"
%token _REJECT "reject"
+%token RESET "reset"
+%token WITH "with"
+%token ICMPX "icmpx"
%token SNAT "snat"
%token DNAT "dnat"
@@ -431,8 +436,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%type <stmt> limit_stmt
%destructor { stmt_free($$); } limit_stmt
%type <val> time_unit
-%type <stmt> reject_stmt
-%destructor { stmt_free($$); } reject_stmt
+%type <stmt> reject_stmt reject_stmt_alloc
+%destructor { stmt_free($$); } reject_stmt reject_stmt_alloc
%type <stmt> nat_stmt nat_stmt_alloc
%destructor { stmt_free($$); } nat_stmt nat_stmt_alloc
%type <stmt> queue_stmt queue_stmt_alloc
@@ -1374,12 +1379,55 @@ time_unit : SECOND { $$ = 1ULL; }
| WEEK { $$ = 1ULL * 60 * 60 * 24 * 7; }
;
-reject_stmt : _REJECT
+reject_stmt : reject_stmt_alloc reject_opts
+ ;
+
+reject_stmt_alloc : _REJECT
{
$$ = reject_stmt_alloc(&@$);
}
;
+reject_opts : /* empty */
+ {
+ $<stmt>0->reject.type = -1;
+ $<stmt>0->reject.icmp_code = -1;
+ }
+ | WITH ICMP TYPE STRING
+ {
+ $<stmt>0->reject.family = NFPROTO_IPV4;
+ $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
+ $<stmt>0->reject.expr =
+ symbol_expr_alloc(&@$, SYMBOL_VALUE,
+ current_scope(state),
+ $4);
+ $<stmt>0->reject.expr->dtype = &icmp_code_type;
+ }
+ | WITH ICMP6 TYPE STRING
+ {
+ $<stmt>0->reject.family = NFPROTO_IPV6;
+ $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
+ $<stmt>0->reject.expr =
+ symbol_expr_alloc(&@$, SYMBOL_VALUE,
+ current_scope(state),
+ $4);
+ $<stmt>0->reject.expr->dtype = &icmpv6_code_type;
+ }
+ | WITH ICMPX TYPE STRING
+ {
+ $<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH;
+ $<stmt>0->reject.expr =
+ symbol_expr_alloc(&@$, SYMBOL_VALUE,
+ current_scope(state),
+ $4);
+ $<stmt>0->reject.expr->dtype = &icmpx_code_type;
+ }
+ | WITH TCP RESET
+ {
+ $<stmt>0->reject.type = NFT_REJECT_TCP_RST;
+ }
+ ;
+
nat_stmt : nat_stmt_alloc nat_stmt_args
;
diff --git a/src/payload.c b/src/payload.c
index b7b74edd..ebf8079b 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -198,11 +198,32 @@ int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
}
desc = ctx->pctx.protocol[expr->payload.base - 1].desc;
- /* Special case for mixed IPv4/IPv6 tables: use meta L4 proto */
- if (desc == NULL &&
- ctx->pctx.family == NFPROTO_INET &&
- expr->payload.base == PROTO_BASE_TRANSPORT_HDR)
- desc = &proto_inet_service;
+ /* Special case for mixed IPv4/IPv6 and bridge tables */
+ if (desc == NULL) {
+ switch (ctx->pctx.family) {
+ case NFPROTO_INET:
+ switch (expr->payload.base) {
+ case PROTO_BASE_TRANSPORT_HDR:
+ desc = &proto_inet_service;
+ break;
+ case PROTO_BASE_LL_HDR:
+ desc = &proto_inet;
+ break;
+ default:
+ break;
+ }
+ break;
+ case NFPROTO_BRIDGE:
+ switch (expr->payload.base) {
+ case PROTO_BASE_LL_HDR:
+ desc = &proto_eth;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
if (desc == NULL)
return expr_error(ctx->msgs, expr,
diff --git a/src/scanner.l b/src/scanner.l
index 35c9446f..9f054fdf 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -313,6 +313,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"week" { return WEEK; }
"reject" { return _REJECT; }
+"with" { return WITH; }
+"reset" { return RESET; }
+"icmpx" { return ICMPX; }
"snat" { return SNAT; }
"dnat" { return DNAT; }
diff --git a/src/statement.c b/src/statement.c
index 8e4b49e2..357f0948 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -16,6 +16,10 @@
#include <string.h>
#include <syslog.h>
+#include <arpa/inet.h>
+#include <linux/netfilter.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
#include <statement.h>
#include <utils.h>
#include <list.h>
@@ -227,6 +231,33 @@ struct stmt *queue_stmt_alloc(const struct location *loc)
static void reject_stmt_print(const struct stmt *stmt)
{
printf("reject");
+ switch (stmt->reject.type) {
+ case NFT_REJECT_TCP_RST:
+ printf(" with tcp reset");
+ break;
+ case NFT_REJECT_ICMPX_UNREACH:
+ if (stmt->reject.icmp_code == NFT_REJECT_ICMPX_PORT_UNREACH)
+ break;
+ printf(" with icmpx type ");
+ expr_print(stmt->reject.expr);
+ break;
+ case NFT_REJECT_ICMP_UNREACH:
+ switch (stmt->reject.family) {
+ case NFPROTO_IPV4:
+ if (stmt->reject.icmp_code == ICMP_PORT_UNREACH)
+ break;
+ printf(" with icmp type ");
+ expr_print(stmt->reject.expr);
+ break;
+ case NFPROTO_IPV6:
+ if (stmt->reject.icmp_code == ICMP6_DST_UNREACH_NOPORT)
+ break;
+ printf(" with icmpv6 type ");
+ expr_print(stmt->reject.expr);
+ break;
+ }
+ break;
+ }
}
static const struct stmt_ops reject_stmt_ops = {