From 4a75ed32132d8e2292dd276f3ea7f4edec4f3d06 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 15 Sep 2016 17:28:00 +0200 Subject: src: add fib expression This adds the 'fib' expression which can be used to obtain the output interface from the route table based on either source or destination address of a packet. This can be used to e.g. add reverse path filtering: # drop if not coming from the same interface packet # arrived on # nft add rule x prerouting fib saddr . iif oif eq 0 drop # accept only if from eth0 # nft add rule x prerouting fib saddr . iif oif eq "eth0" accept # accept if from any valid interface # nft add rule x prerouting fib saddr oif accept Querying of address type is also supported. This can be used to e.g. only accept packets to addresses configured in the same interface: # fib daddr . iif type local Its also possible to use mark and verdict map, e.g.: # nft add rule x prerouting meta mark set 0xdead fib daddr . mark type vmap { blackhole : drop, prohibit : drop, unicast : accept } Signed-off-by: Florian Westphal --- doc/nft.xml | 75 +++++++++++++++++++ include/datatype.h | 1 + include/expression.h | 7 ++ include/fib.h | 7 ++ include/linux/netfilter/nf_tables.h | 36 +++++++++ src/Makefile.am | 1 + src/evaluate.c | 1 + src/fib.c | 144 ++++++++++++++++++++++++++++++++++++ src/netlink_delinearize.c | 19 +++++ src/netlink_linearize.c | 16 ++++ src/parser_bison.y | 50 +++++++++++++ src/scanner.l | 2 + tests/py/inet/fib.t | 14 ++++ tests/py/inet/fib.t.payload | 22 ++++++ 14 files changed, 395 insertions(+) create mode 100644 include/fib.h create mode 100644 src/fib.c create mode 100644 tests/py/inet/fib.t create mode 100644 tests/py/inet/fib.t.payload diff --git a/doc/nft.xml b/doc/nft.xml index e6b98ae7..1ff70b07 100644 --- a/doc/nft.xml +++ b/doc/nft.xml @@ -1222,7 +1222,82 @@ filter output oif eth0 + + fib expressions + + + fib + + saddr + daddr + + mark + iif + oif + + + + oif + oifname + type + + + + + A fib expression queries the fib (forwarding information base) + to obtain information such as the output interface index a particular address would use. The input is a tuple of elements that is used as input to the fib lookup + functions. + + + + fib expression specific types + + + + + + + Keyword + Description + Type + + + + + oif + Output interface index + integer (32 bit) + + + oifname + Output interface name + string + + + type + Address type + fib_addrtype + + + +
+
+ + + Using fib expressions + +# drop packets without a reverse path +filter prerouting fib saddr . iif oif eq 0 drop +# drop packets to address not configured on ininterface +filter input fib daddr . iif type not { local, broadcast, multicast } drop + +# perform lookup in a specific 'blackhole' table (0xdead, needs ip appropriate ip rule) +filter prerouting meta mark set 0xdead fib daddr . mark type vmap { backhole : drop, prohibit : jump prohibited, unreachable : drop } + + + +
Routing expressions diff --git a/include/datatype.h b/include/datatype.h index 12ec46bc..9f3f711c 100644 --- a/include/datatype.h +++ b/include/datatype.h @@ -81,6 +81,7 @@ enum datatypes { TYPE_DEVGROUP, TYPE_DSCP, TYPE_ECN, + TYPE_FIB_ADDR, __TYPE_MAX }; #define TYPE_MAX (__TYPE_MAX - 1) diff --git a/include/expression.h b/include/expression.h index 3ae4e804..3a52a45c 100644 --- a/include/expression.h +++ b/include/expression.h @@ -61,6 +61,7 @@ enum expr_types { EXPR_NUMGEN, EXPR_HASH, EXPR_RT, + EXPR_FIB, }; enum ops { @@ -180,6 +181,7 @@ enum expr_flags { #include #include +#include #include #include #include @@ -306,6 +308,11 @@ struct expr { uint32_t mod; uint32_t seed; } hash; + struct { + /* EXPR_FIB */ + uint32_t flags; + uint32_t result; + } fib; }; }; diff --git a/include/fib.h b/include/fib.h new file mode 100644 index 00000000..3a019e65 --- /dev/null +++ b/include/fib.h @@ -0,0 +1,7 @@ +#ifndef NFTABLES_FIB_H +#define NFTABLES_FIB_H + +extern struct expr *fib_expr_alloc(const struct location *loc, + unsigned int flags, + unsigned int result); +#endif /* NFTABLES_FIB_H */ diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 2d477847..c6567ace 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -1126,6 +1126,42 @@ enum nft_gen_attributes { }; #define NFTA_GEN_MAX (__NFTA_GEN_MAX - 1) +/* + * enum nft_fib_attributes - nf_tables fib expression netlink attributes + * + * @NFTA_FIB_DREG: destination register (NLA_U32) + * @NFTA_FIB_RESULT: desired result (NLA_U32) + * @NFTA_FIB_FLAGS: flowi fields to initialize when querying the FIB (NLA_U32) + * + * The FIB expression performs a route lookup according + * to the packet data. + */ +enum nft_fib_attributes { + NFTA_FIB_UNSPEC, + NFTA_FIB_DREG, + NFTA_FIB_RESULT, + NFTA_FIB_FLAGS, + __NFTA_FIB_MAX +}; +#define NFTA_FIB_MAX (__NFTA_FIB_MAX - 1) + +enum nft_fib_result { + NFT_FIB_RESULT_UNSPEC, + NFT_FIB_RESULT_OIF, + NFT_FIB_RESULT_OIFNAME, + NFT_FIB_RESULT_ADDRTYPE, + __NFT_FIB_RESULT_MAX +}; +#define NFT_FIB_RESULT_MAX (__NFT_FIB_RESULT_MAX - 1) + +enum nft_fib_flags { + NFTA_FIB_F_SADDR = 1 << 0, /* look up src */ + NFTA_FIB_F_DADDR = 1 << 1, /* look up dst */ + NFTA_FIB_F_MARK = 1 << 2, /* use skb->mark */ + NFTA_FIB_F_IIF = 1 << 3, /* restrict to iif */ + NFTA_FIB_F_OIF = 1 << 4, /* restrict to oif */ +}; + /** * enum nft_trace_attributes - nf_tables trace netlink attributes * diff --git a/src/Makefile.am b/src/Makefile.am index 9a151bd4..d021cb7f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -36,6 +36,7 @@ nft_SOURCES = main.c \ proto.c \ payload.c \ exthdr.c \ + fib.c \ hash.c \ meta.c \ rt.c \ diff --git a/src/evaluate.c b/src/evaluate.c index ab0dd9ef..a2e5dbb9 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -1638,6 +1638,7 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr) return expr_evaluate_exthdr(ctx, expr); case EXPR_VERDICT: case EXPR_META: + case EXPR_FIB: return expr_evaluate_primary(ctx, expr); case EXPR_PAYLOAD: return expr_evaluate_payload(ctx, expr); diff --git a/src/fib.c b/src/fib.c new file mode 100644 index 00000000..346cce32 --- /dev/null +++ b/src/fib.c @@ -0,0 +1,144 @@ +/* + * FIB expression. + * + * Copyright (c) Red Hat GmbH. Author: Florian Westphal + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static const char *fib_result[NFT_FIB_RESULT_MAX + 1] = { + [NFT_FIB_RESULT_OIF] = "oif", + [NFT_FIB_RESULT_OIFNAME] = "oifname", + [NFT_FIB_RESULT_ADDRTYPE] = "type", +}; + +static const struct symbol_table addrtype_tbl = { + .symbols = { + SYMBOL("unspec", RTN_UNSPEC), + SYMBOL("unicast", RTN_UNICAST), + SYMBOL("local", RTN_LOCAL), + SYMBOL("broadcast", RTN_BROADCAST), + SYMBOL("anycast", RTN_ANYCAST), + SYMBOL("multicast", RTN_MULTICAST), + SYMBOL("blackhole", RTN_BLACKHOLE), + SYMBOL("unreachable", RTN_UNREACHABLE), + SYMBOL("prohibit", RTN_PROHIBIT), + SYMBOL_LIST_END + } +}; + +static const struct datatype fib_addr_type = { + .type = TYPE_FIB_ADDR, + .name = "fib_addrtype", + .desc = "fib address type", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 4 * BITS_PER_BYTE, + .basetype = &integer_type, + .sym_tbl = &addrtype_tbl, +}; + +static const char *fib_result_str(enum nft_fib_result result) +{ + if (result <= NFT_FIB_RESULT_MAX) + return fib_result[result]; + + return "unknown"; +} + +static void __fib_expr_print_f(unsigned int *flags, unsigned int f, const char *s) +{ + if ((*flags & f) == 0) + return; + + printf("%s", s); + *flags &= ~f; + if (*flags) + printf(" . "); +} + +static void fib_expr_print(const struct expr *expr) +{ + unsigned int flags = expr->fib.flags; + + printf("fib "); + __fib_expr_print_f(&flags, NFTA_FIB_F_SADDR, "saddr"); + __fib_expr_print_f(&flags, NFTA_FIB_F_DADDR, "daddr"); + __fib_expr_print_f(&flags, NFTA_FIB_F_MARK, "mark"); + __fib_expr_print_f(&flags, NFTA_FIB_F_IIF, "iif"); + __fib_expr_print_f(&flags, NFTA_FIB_F_OIF, "oif"); + + if (flags) + printf("0x%x", flags); + + printf(" %s", fib_result_str(expr->fib.result)); +} + +static bool fib_expr_cmp(const struct expr *e1, const struct expr *e2) +{ + return e1->fib.result == e2->fib.result && + e1->fib.flags == e2->fib.flags; +} + +static void fib_expr_clone(struct expr *new, const struct expr *expr) +{ + new->fib.result = expr->fib.result; + new->fib.flags= expr->fib.flags; +} + +static const struct expr_ops fib_expr_ops = { + .type = EXPR_FIB, + .name = "fib", + .print = fib_expr_print, + .cmp = fib_expr_cmp, + .clone = fib_expr_clone, +}; + +struct expr *fib_expr_alloc(const struct location *loc, + unsigned int flags, unsigned int result) +{ + const struct datatype *type; + unsigned int len = 4 * BITS_PER_BYTE; + struct expr *expr; + + switch (result) { + case NFT_FIB_RESULT_OIF: + type = &ifindex_type; + break; + case NFT_FIB_RESULT_OIFNAME: + type = &string_type; + len = IFNAMSIZ * BITS_PER_BYTE; + break; + case NFT_FIB_RESULT_ADDRTYPE: + type = &fib_addr_type; + break; + default: + BUG("Unknown result %d\n", result); + } + + expr = expr_alloc(loc, &fib_expr_ops, type, + BYTEORDER_HOST_ENDIAN, len); + + expr->fib.result = result; + expr->fib.flags = flags; + + return expr; +} + +static void __init fib_init(void) +{ + datatype_register(&fib_addr_type); +} diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index dc9cc837..f0df8848 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -538,6 +538,23 @@ static void netlink_parse_hash(struct netlink_parse_ctx *ctx, netlink_set_register(ctx, dreg, expr); } +static void netlink_parse_fib(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers dreg; + struct expr *expr; + uint32_t flags, result; + + flags = nftnl_expr_get_u32(nle, NFTNL_EXPR_FIB_FLAGS); + result = nftnl_expr_get_u32(nle, NFTNL_EXPR_FIB_RESULT); + + expr = fib_expr_alloc(loc, flags, result); + + dreg = netlink_parse_register(nle, NFTNL_EXPR_FIB_DREG); + netlink_set_register(ctx, dreg, expr); +} + static void netlink_parse_meta_expr(struct netlink_parse_ctx *ctx, const struct location *loc, const struct nftnl_expr *nle) @@ -1120,6 +1137,7 @@ static const struct { { .name = "quota", .parse = netlink_parse_quota }, { .name = "numgen", .parse = netlink_parse_numgen }, { .name = "hash", .parse = netlink_parse_hash }, + { .name = "fib", .parse = netlink_parse_fib }, }; static int netlink_parse_expr(const struct nftnl_expr *nle, @@ -1743,6 +1761,7 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp) case EXPR_RT: case EXPR_VERDICT: case EXPR_NUMGEN: + case EXPR_FIB: break; case EXPR_HASH: expr_postprocess(ctx, &expr->hash.expr); diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index a5d4502f..b5967d49 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -104,6 +104,20 @@ static void netlink_gen_concat(struct netlink_linearize_ctx *ctx, } } +static void netlink_gen_fib(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("fib"); + netlink_put_register(nle, NFTNL_EXPR_FIB_DREG, dreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_FIB_RESULT, expr->fib.result); + nftnl_expr_set_u32(nle, NFTNL_EXPR_FIB_FLAGS, expr->fib.flags); + + nftnl_rule_add_expr(ctx->nlr, nle); +} + static void netlink_gen_hash(struct netlink_linearize_ctx *ctx, const struct expr *expr, enum nft_registers dreg) @@ -663,6 +677,8 @@ static void netlink_gen_expr(struct netlink_linearize_ctx *ctx, return netlink_gen_numgen(ctx, expr, dreg); case EXPR_HASH: return netlink_gen_hash(ctx, expr, dreg); + case EXPR_FIB: + return netlink_gen_fib(ctx, expr, dreg); default: BUG("unknown expression type %s\n", expr->ops->name); } diff --git a/src/parser_bison.y b/src/parser_bison.y index dc02fd23..106df271 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -162,10 +162,13 @@ static void location_update(struct location *loc, struct location *rhs, int n) %token DASH "-" %token AT "@" %token VMAP "vmap" +%token LOOKUP "lookup" %token INCLUDE "include" %token DEFINE "define" +%token FIB "fib" + %token HOOK "hook" %token DEVICE "device" %token TABLE "table" @@ -601,6 +604,10 @@ static void location_update(struct location *loc, struct location *rhs, int n) %destructor { expr_free($$); } ct_expr %type ct_key ct_key_dir ct_key_counters +%type fib_expr +%destructor { expr_free($$); } fib_expr +%type fib_tuple fib_result fib_flag + %type export_format %type monitor_event %destructor { xfree($$); } monitor_event @@ -2006,9 +2013,52 @@ primary_expr : symbol_expr { $$ = $1; } | ct_expr { $$ = $1; } | numgen_expr { $$ = $1; } | hash_expr { $$ = $1; } + | fib_expr { $$ = $1; } | '(' basic_expr ')' { $$ = $2; } ; +fib_expr : FIB fib_tuple fib_result + { + if (($2 & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == 0) { + erec_queue(error(&@2, "fib: need either saddr or daddr"), state->msgs); + YYERROR; + } + + if (($2 & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == + (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) { + erec_queue(error(&@2, "fib: saddr and daddr are mutually exclusive"), state->msgs); + YYERROR; + } + + if (($2 & (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) == + (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) { + erec_queue(error(&@2, "fib: iif and oif are mutually exclusive"), state->msgs); + YYERROR; + } + + $$ = fib_expr_alloc(&@$, $2, $3); + } + ; + +fib_result : OIF { $$ =NFT_FIB_RESULT_OIF; } + | OIFNAME { $$ =NFT_FIB_RESULT_OIFNAME; } + | TYPE { $$ =NFT_FIB_RESULT_ADDRTYPE; } + ; + +fib_flag : SADDR { $$ = NFTA_FIB_F_SADDR; } + | DADDR { $$ = NFTA_FIB_F_DADDR; } + | MARK { $$ = NFTA_FIB_F_MARK; } + | IIF { $$ = NFTA_FIB_F_IIF; } + | OIF { $$ = NFTA_FIB_F_OIF; } + ; + +fib_tuple : fib_flag DOT fib_tuple + { + $$ = $1 | $3; + } + | fib_flag + ; + shift_expr : primary_expr | shift_expr LSHIFT primary_expr { diff --git a/src/scanner.l b/src/scanner.l index c8352014..9cb8d778 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -465,6 +465,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) "dup" { return DUP; } "fwd" { return FWD; } +"fib" { return FIB; } + "xml" { return XML; } "json" { return JSON; } diff --git a/tests/py/inet/fib.t b/tests/py/inet/fib.t new file mode 100644 index 00000000..9ee282ab --- /dev/null +++ b/tests/py/inet/fib.t @@ -0,0 +1,14 @@ +:prerouting;type filter hook prerouting priority 0 + +*ip;test-ip;prerouting +*ip6;test-ip6;prerouting + +fib saddr . daddr oif lo;fail +fib iif . oif . daddr oif lo;fail +fib mark oif lo;fail +fib saddr . iif oif ne 0;ok;fib saddr . iif oif != 0 +fib saddr . iif oifname "lo";ok + +fib daddr . iif type local;ok +fib daddr . iif type vmap { blackhole : drop, prohibit : drop, unicast : accept };ok +fib daddr . oif type local;fail diff --git a/tests/py/inet/fib.t.payload b/tests/py/inet/fib.t.payload new file mode 100644 index 00000000..f5258165 --- /dev/null +++ b/tests/py/inet/fib.t.payload @@ -0,0 +1,22 @@ +# fib saddr . iif oif ne 0 +ip test-ip prerouting + [ fib saddr . iif oif => reg 1 ] + [ cmp neq reg 1 0x00000000 ] + +# fib saddr . iif oifname "lo" +ip test-ip prerouting + [ fib saddr . iif oifname => reg 1 ] + [ cmp eq reg 1 0x00006f6c 0x00000000 0x00000000 0x00000000 ] + +# fib daddr . iif type local +ip test-ip prerouting + [ fib daddr . iif type => reg 1 ] + [ cmp eq reg 1 0x00000002 ] + +# fib daddr . iif type vmap { blackhole : drop, prohibit : drop, unicast : accept } +__map%d test-ip b +__map%d test-ip 0 + element 00000006 : 0 [end] element 00000008 : 0 [end] element 00000001 : 0 [end] +ip test-ip prerouting + [ fib daddr . iif type => reg 1 ] + [ lookup reg 1 set __map%d dreg 0 ] -- cgit v1.2.3