]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
nft: complete reject support
authorAlvaro Neira <alvaroneay@gmail.com>
Tue, 30 Sep 2014 15:21:40 +0000 (17:21 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Thu, 9 Oct 2014 11:53:11 +0000 (13:53 +0200)
This patch allows to use the reject action in rules. For example:

  nft add rule filter input udp dport 22 reject

In this rule, we assume that the reason is network unreachable. Also
we can specify the reason with the option "with" and the reason. For example:

  nft add rule filter input tcp dport 22 reject with icmp type host-unreachable

In the bridge tables and inet tables, we can use this action too. For example:

  nft add rule inet filter input reject with icmp type host-unreachable

In this rule above, this generates a meta nfproto dependency to match
ipv4 traffic because we use a icmpv4 reason to reject.

If the reason is not specified, we infer it from the context.

Moreover, we have the new icmpx datatype. You can use this datatype for
the bridge and the inet tables to simplify your ruleset. For example:

  nft add rule inet filter input reject with icmpx type host-unreachable

We have four icmpx reason and the mapping is:

ICMPX reason            |       ICMPv6          |      ICMPv4
                        |                       |
admin-prohibited        | admin-prohibited      | admin-prohibited
port-unreachable        | port-unreachable      | port-unreachable
no-route                | no-route              | net-unreachable
host-unreachable        | addr-unreachable      | host-unreachable

Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
12 files changed:
include/datatype.h
include/linux/netfilter/nf_tables.h
include/proto.h
include/statement.h
src/datatype.c
src/evaluate.c
src/netlink_delinearize.c
src/netlink_linearize.c
src/parser.y
src/payload.c
src/scanner.l
src/statement.c

index 518226370e540205412a60c5bac2ea00652174d3..15fea4418d5ecae5f0df827141e2c0a92ba03223 100644 (file)
@@ -36,6 +36,9 @@
  * @TYPE_ICMP6_TYPE:   ICMPv6 type codes (integer subtype)
  * @TYPE_CT_LABEL:     Conntrack Label (bitmask subtype)
  * @TYPE_PKTTYPE:      packet type (integer subtype)
+ * @TYPE_ICMP_CODE:    icmp code (integer subtype)
+ * @TYPE_ICMPV6_CODE:  icmpv6 code (integer subtype)
+ * @TYPE_ICMPX_CODE:   icmpx code (integer subtype)
  */
 enum datatypes {
        TYPE_INVALID,
@@ -70,6 +73,9 @@ enum datatypes {
        TYPE_ICMP6_TYPE,
        TYPE_CT_LABEL,
        TYPE_PKTTYPE,
+       TYPE_ICMP_CODE,
+       TYPE_ICMPV6_CODE,
+       TYPE_ICMPX_CODE,
        __TYPE_MAX
 };
 #define TYPE_MAX               (__TYPE_MAX - 1)
@@ -194,6 +200,9 @@ extern const struct datatype arphrd_type;
 extern const struct datatype inet_protocol_type;
 extern const struct datatype inet_service_type;
 extern const struct datatype mark_type;
+extern const struct datatype icmp_code_type;
+extern const struct datatype icmpv6_code_type;
+extern const struct datatype icmpx_code_type;
 extern const struct datatype time_type;
 
 extern const struct datatype *concat_type_alloc(const struct expr *expr);
index b72ccfeaf86527e12b61230597798d1cb902f949..f04d997a4cfd138886aece01d263722a1bdd400e 100644 (file)
@@ -749,12 +749,33 @@ enum nft_queue_attributes {
  *
  * @NFT_REJECT_ICMP_UNREACH: reject using ICMP unreachable
  * @NFT_REJECT_TCP_RST: reject using TCP RST
+ * @NFT_REJECT_ICMPX_UNREACH: abstracted ICMP unreachable for bridge and inet
  */
 enum nft_reject_types {
        NFT_REJECT_ICMP_UNREACH,
        NFT_REJECT_TCP_RST,
+       NFT_REJECT_ICMPX_UNREACH,
 };
 
+/**
+ * enum nft_reject_code - Abstracted reject codes
+ *
+ * @NFT_REJECT_ICMPX_NO_ROUTE: no route to host - network unreachable
+ * @NFT_REJECT_ICMPX_PORT_UNREACH: port unreachable
+ * @NFT_REJECT_ICMPX_HOST_UNREACH: host unreachable
+ * @NFT_REJECT_ICMPX_ADMIN_PROHIBITED: administratevely prohibited
+ *
+ * These codes are mapped to real ICMP and ICMPv6 codes.
+ */
+enum nft_reject_inet_code {
+       NFT_REJECT_ICMPX_NO_ROUTE       = 0,
+       NFT_REJECT_ICMPX_PORT_UNREACH,
+       NFT_REJECT_ICMPX_HOST_UNREACH,
+       NFT_REJECT_ICMPX_ADMIN_PROHIBITED,
+       __NFT_REJECT_ICMPX_MAX
+};
+#define NFT_REJECT_ICMPX_MAX   (__NFT_REJECT_ICMPX_MAX + 1)
+
 /**
  * enum nft_reject_attributes - nf_tables reject expression netlink attributes
  *
index cc1f51f0bf5e963712b9510fd9d2ccdc97e21987..0e531b241d908adc23afb609ead33efd541fa8fc 100644 (file)
@@ -252,6 +252,7 @@ enum udp_hdr_fields {
 
 enum tcp_hdr_fields {
        TCPHDR_INVALID,
+       TCPHDR_UNSPEC = TCPHDR_INVALID,
        TCPHDR_SPORT,
        TCPHDR_DPORT,
        TCPHDR_SEQ,
index 7a57f7ddd7ec96f73ada76e592c3ece4d7c53b6b..574835cd4d4796595537fe8587fe860896b41362 100644 (file)
@@ -56,7 +56,10 @@ struct limit_stmt {
 extern struct stmt *limit_stmt_alloc(const struct location *loc);
 
 struct reject_stmt {
+       struct expr             *expr;
        enum nft_reject_types   type;
+       int8_t                  icmp_code;
+       unsigned int            family;
 };
 
 extern struct stmt *reject_stmt_alloc(const struct location *loc);
index 7090b0ba7fb415a9602522cff280a4391971597c..8ad211c1d0d5c543e5236427eecdc46baae3c168 100644 (file)
@@ -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;
index 52ce5487f2f0aa678e8371bf0615412348647977..602148c80d55f3c730e8cecf1258fc7fcad81abf 100644 (file)
@@ -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;
index 796b6327b7510ed0cfe644b97f4935c5127de92d..397b65c0179e53f285fc9d30189c8112be891fb8 100644 (file)
@@ -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;
                }
index c46b6d4722d2bb0a84799cb26a529cf6141dc0f5..29f8e9ae40dff67ba8fc1673739a8656b44d656f 100644 (file)
@@ -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);
 }
 
index 4a8df7b74bb57b5ab26013c286f78afc8cb2ab57..03d6d1384aea3631d02ded5da60742d28381cace 100644 (file)
@@ -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
                        ;
 
index b7b74edd2049a88d6c8072501032688ee5cee8d6..ebf8079b00a87a358f70e4d0382bd2b17d25371b 100644 (file)
@@ -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,
index 35c9446fbd33a5ae5121bf6b76651bf782f464eb..9f054fdf6068fc0b2f46d66fe9151f102c77e2cc 100644 (file)
@@ -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; }
index 8e4b49e2335273c9ca385dd698999878d568721d..357f094894e8ebadd1af861908a8fa9e2f983768 100644 (file)
 #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 = {