* [PATCH 0/3 various] netfilter: add fib expression
@ 2016-10-24 14:56 Florian Westphal
2016-10-24 14:56 ` [PATCH 1/3 nf-next] nf_tables: " Florian Westphal
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Florian Westphal @ 2016-10-24 14:56 UTC (permalink / raw)
To: netfilter-devel
This adds the FIB expression to query fib for oif and route/address type.
This provides functionality of the xtables 'rpfilter' and 'addrtype' matches.
The '--local' option supported by the rpfilter match is not supported
anymore, but it is possible to use extra rules (either checking for explicit
saddr/daddr pairs) or the 'fib type' result to make packets coming from
external source but with a locally configured address bypass 'fib oif'
queries.
oif is supported in prerouting, type can be used in all hooks. The
kernel validates that the combinations make sense (e.g., you cannot
ask for iif in OUTPUT).
ipv4, ipv6 and inet families are supported at the moment.
Comments welcome.
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/3 nf-next] nf_tables: add fib expression
2016-10-24 14:56 [PATCH 0/3 various] netfilter: add fib expression Florian Westphal
@ 2016-10-24 14:56 ` Florian Westphal
2016-10-27 18:16 ` Pablo Neira Ayuso
2016-10-24 14:56 ` [PATCH 2/3 libnftables] expr: " Florian Westphal
2016-10-24 14:56 ` [PATCH 3/3 nft] src: " Florian Westphal
2 siblings, 1 reply; 5+ messages in thread
From: Florian Westphal @ 2016-10-24 14:56 UTC (permalink / raw)
To: netfilter-devel; +Cc: Florian Westphal
Add FIB expression, supported for ipv4, ipv6 and inet family (the latter
just dispatches to ipv4 or ipv6 one based on nfproto).
Currently supports fetching output interface index/name and the
rtm_type associated with an address.
This can be used for adding path filtering. rtm_type is useful
to e.g. enforce a strong-end host model where packets
are only accepted if daddr is configured on the interface the
packet arrived on.
The fib expression is a native nftables alternative to the
xtables addrtype and rp_filter matches.
FIB result order for oif/oifname retrieval is as follows:
- if packet is local (skb has rtable, RTF_LOCAL set, this
will also catch looped-back multicast packets), set oif to
the loopback interface.
- if fib lookup returns an error, or result points to local,
store zero result. This means '--local' option of -m rpfilter
is not supported. It is possible to use 'fib type local' or add
explicit saddr/daddr matching rules to create exceptions if this
is really needed.
- store result in the destination register.
In case of multiple routes, search set for desired oif in case
strict matching is requested.
ipv4 and ipv6 behave fib expressions are supposed to behave the same.
Signed-off-by: Florian Westphal <fw@strlen.de>
---
NB: This conflicts with not-yet pushed patches from
Pablo, one of us needs to rebase.
include/net/netfilter/nft_fib.h | 31 ++++
include/uapi/linux/netfilter/nf_tables.h | 36 ++++
net/ipv4/netfilter/Kconfig | 8 +
net/ipv4/netfilter/Makefile | 1 +
net/ipv4/netfilter/nft_fib_ipv4.c | 233 ++++++++++++++++++++++++++
net/ipv6/netfilter/Kconfig | 8 +
net/ipv6/netfilter/Makefile | 1 +
net/ipv6/netfilter/nft_fib_ipv6.c | 275 +++++++++++++++++++++++++++++++
net/netfilter/Kconfig | 13 ++
net/netfilter/Makefile | 2 +
net/netfilter/nft_fib.c | 159 ++++++++++++++++++
net/netfilter/nft_fib_inet.c | 82 +++++++++
12 files changed, 849 insertions(+)
create mode 100644 include/net/netfilter/nft_fib.h
create mode 100644 net/ipv4/netfilter/nft_fib_ipv4.c
create mode 100644 net/ipv6/netfilter/nft_fib_ipv6.c
create mode 100644 net/netfilter/nft_fib.c
create mode 100644 net/netfilter/nft_fib_inet.c
diff --git a/include/net/netfilter/nft_fib.h b/include/net/netfilter/nft_fib.h
new file mode 100644
index 000000000000..cbedda077db2
--- /dev/null
+++ b/include/net/netfilter/nft_fib.h
@@ -0,0 +1,31 @@
+#ifndef _NFT_FIB_H_
+#define _NFT_FIB_H_
+
+struct nft_fib {
+ enum nft_registers dreg:8;
+ u8 result;
+ u32 flags;
+};
+
+extern const struct nla_policy nft_fib_policy[];
+
+int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr);
+int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+ const struct nlattr * const tb[]);
+int nft_fib_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
+ const struct nft_data **data);
+
+
+void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs,
+ const struct nft_pktinfo *pkt);
+void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs,
+ const struct nft_pktinfo *pkt);
+
+void nft_fib6_eval_type(const struct nft_expr *expr, struct nft_regs *regs,
+ const struct nft_pktinfo *pkt);
+void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs,
+ const struct nft_pktinfo *pkt);
+
+void nft_fib_store_result(void *reg, enum nft_fib_result r,
+ const struct nft_pktinfo *pkt, int index);
+#endif
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index c6c4477c136b..a054ad2c8853 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -1109,6 +1109,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/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
index d613309e3e5d..f80bb8f457c8 100644
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -54,6 +54,14 @@ config NFT_DUP_IPV4
help
This module enables IPv4 packet duplication support for nf_tables.
+config NFT_FIB_IPV4
+ select NFT_FIB
+ tristate "nf_tables fib / ip route lookup support"
+ help
+ This module enables IPv4 FIB lookups, e.g. for reverse path filtering.
+ It also allows query of the FIB for the route type, e.g. local, unicast,
+ multicast or blackhole.
+
endif # NF_TABLES_IPV4
config NF_TABLES_ARP
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
index 853328f8fd05..59d7786ddce1 100644
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_NF_TABLES_IPV4) += nf_tables_ipv4.o
obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV4) += nft_chain_route_ipv4.o
obj-$(CONFIG_NFT_CHAIN_NAT_IPV4) += nft_chain_nat_ipv4.o
obj-$(CONFIG_NFT_REJECT_IPV4) += nft_reject_ipv4.o
+obj-$(CONFIG_NFT_FIB_IPV4) += nft_fib_ipv4.o
obj-$(CONFIG_NFT_MASQ_IPV4) += nft_masq_ipv4.o
obj-$(CONFIG_NFT_REDIR_IPV4) += nft_redir_ipv4.o
obj-$(CONFIG_NFT_DUP_IPV4) += nft_dup_ipv4.o
diff --git a/net/ipv4/netfilter/nft_fib_ipv4.c b/net/ipv4/netfilter/nft_fib_ipv4.c
new file mode 100644
index 000000000000..6787c563cfc9
--- /dev/null
+++ b/net/ipv4/netfilter/nft_fib_ipv4.c
@@ -0,0 +1,233 @@
+/*
+ * 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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_core.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nft_fib.h>
+
+#include <net/ip_fib.h>
+#include <net/route.h>
+
+/* don't try to find route from mcast/bcast/zeronet */
+static __be32 get_saddr(__be32 addr)
+{
+ if (ipv4_is_multicast(addr) || ipv4_is_lbcast(addr) ||
+ ipv4_is_zeronet(addr))
+ return 0;
+ return addr;
+}
+
+static bool fib4_is_local(const struct sk_buff *skb)
+{
+ const struct rtable *rt = skb_rtable(skb);
+
+ return rt && (rt->rt_flags & RTCF_LOCAL);
+}
+
+#define DSCP_BITS 0xfc
+
+void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs,
+ const struct nft_pktinfo *pkt)
+{
+ const struct nft_fib *priv = nft_expr_priv(expr);
+ u32 *dst = ®s->data[priv->dreg];
+ const struct net_device *dev = NULL;
+ const struct iphdr *iph;
+ __be32 addr;
+
+ if (priv->flags & NFTA_FIB_F_IIF)
+ dev = pkt->in;
+ else if (priv->flags & NFTA_FIB_F_OIF)
+ dev = pkt->out;
+
+ iph = ip_hdr(pkt->skb);
+ if (priv->flags & NFTA_FIB_F_DADDR)
+ addr = iph->daddr;
+ else
+ addr = iph->saddr;
+
+ *dst = inet_dev_addr_type(pkt->net, dev, addr);
+}
+EXPORT_SYMBOL_GPL(nft_fib4_eval_type);
+
+static int get_ifindex(const struct net_device *dev)
+{
+ return dev ? dev->ifindex : 0;
+}
+
+void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs,
+ const struct nft_pktinfo *pkt)
+{
+ const struct nft_fib *priv = nft_expr_priv(expr);
+ u32 *dest = ®s->data[priv->dreg];
+ const struct iphdr *iph;
+ struct fib_result res;
+ struct flowi4 fl4 = {
+ .flowi4_scope = RT_SCOPE_UNIVERSE,
+ .flowi4_iif = LOOPBACK_IFINDEX,
+ };
+ const struct net_device *oif;
+ struct net_device *found;
+ int i;
+
+ /*
+ * Do not set flowi4_oif, it restricts results (for example, asking
+ * for oif 3 will get RTN_UNICAST result even if the daddr exits
+ * on another interface.
+ *
+ * Search results for the desired outinterface instead.
+ */
+ if (priv->flags & NFTA_FIB_F_OIF)
+ oif = pkt->out;
+ else if (priv->flags & NFTA_FIB_F_IIF)
+ oif = pkt->in;
+
+ if (pkt->hook == NF_INET_PRE_ROUTING && fib4_is_local(pkt->skb)) {
+ nft_fib_store_result(dest, priv->result, pkt, LOOPBACK_IFINDEX);
+ return;
+ }
+
+ iph = ip_hdr(pkt->skb);
+ if (ipv4_is_multicast(iph->daddr) &&
+ ipv4_is_zeronet(iph->saddr) &&
+ ipv4_is_local_multicast(iph->daddr)) {
+ nft_fib_store_result(dest, priv->result, pkt,
+ get_ifindex(pkt->skb->dev));
+ return;
+ }
+
+ if (priv->flags & NFTA_FIB_F_MARK)
+ fl4.flowi4_mark = pkt->skb->mark;
+
+ fl4.flowi4_tos = iph->tos & DSCP_BITS;
+
+ if (priv->flags & NFTA_FIB_F_DADDR) {
+ fl4.daddr = iph->daddr;
+ fl4.saddr = get_saddr(iph->saddr);
+ } else {
+ fl4.daddr = iph->saddr;
+ fl4.saddr = get_saddr(iph->daddr);
+ }
+
+ if (fib_lookup(pkt->net, &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE))
+ return;
+
+ switch (res.type) {
+ case RTN_UNICAST:
+ break;
+ case RTN_LOCAL: /* should not appear here, see fib4_is_local() above */
+ return;
+ default:
+ break;
+ }
+
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ for (i = 0; i < res.fi->fib_nhs; i++) {
+ struct fib_nh *nh = &res.fi->fib_nh[i];
+
+ if (nh->nh_dev == oif) {
+ found = nh->nh_dev;
+ goto ok;
+ }
+ }
+#endif
+ if (priv->flags & NFTA_FIB_F_OIF) {
+ found = FIB_RES_DEV(res);
+ if (found == oif)
+ goto ok;
+ return;
+ }
+
+ *dest = FIB_RES_DEV(res)->ifindex;
+ return;
+ok:
+ switch (priv->result) {
+ case NFT_FIB_RESULT_OIF:
+ *dest = found->ifindex;
+ break;
+ case NFT_FIB_RESULT_OIFNAME:
+ strncpy((char *)dest, found->name, IFNAMSIZ);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(nft_fib4_eval);
+
+static struct nft_expr_type nft_fib4_type;
+
+static const struct nft_expr_ops nft_fib4_type_ops = {
+ .type = &nft_fib4_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_fib)),
+ .eval = nft_fib4_eval_type,
+ .init = nft_fib_init,
+ .dump = nft_fib_dump,
+ .validate = nft_fib_validate,
+};
+
+static const struct nft_expr_ops nft_fib4_ops = {
+ .type = &nft_fib4_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_fib)),
+ .eval = nft_fib4_eval,
+ .init = nft_fib_init,
+ .dump = nft_fib_dump,
+ .validate = nft_fib_validate,
+};
+
+static const struct nft_expr_ops *
+nft_fib4_select_ops(const struct nft_ctx *ctx,
+ const struct nlattr * const tb[])
+{
+ enum nft_fib_result result;
+
+ if (!tb[NFTA_FIB_RESULT])
+ return ERR_PTR(-EINVAL);
+
+ result = htonl(nla_get_be32(tb[NFTA_FIB_RESULT]));
+
+ switch (result) {
+ case NFT_FIB_RESULT_OIF:
+ return &nft_fib4_ops;
+ case NFT_FIB_RESULT_OIFNAME:
+ return &nft_fib4_ops;
+ case NFT_FIB_RESULT_ADDRTYPE:
+ return &nft_fib4_type_ops;
+ default:
+ return ERR_PTR(-EOPNOTSUPP);
+ }
+}
+
+static struct nft_expr_type nft_fib4_type __read_mostly = {
+ .name = "fib",
+ .select_ops = &nft_fib4_select_ops,
+ .policy = nft_fib_policy,
+ .maxattr = NFTA_FIB_MAX,
+ .family = NFPROTO_IPV4,
+ .owner = THIS_MODULE,
+};
+
+static int __init nft_fib4_module_init(void)
+{
+ return nft_register_expr(&nft_fib4_type);
+}
+
+static void __exit nft_fib4_module_exit(void)
+{
+ nft_unregister_expr(&nft_fib4_type);
+}
+
+module_init(nft_fib4_module_init);
+module_exit(nft_fib4_module_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
+MODULE_ALIAS_NFT_AF_EXPR(2, "fib");
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index e10a04c9cdc7..144f1de33836 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -54,6 +54,14 @@ config NFT_DUP_IPV6
help
This module enables IPv6 packet duplication support for nf_tables.
+config NFT_FIB_IPV6
+ tristate "nf_tables fib / ipv6 route lookup support"
+ select NFT_FIB
+ help
+ This module enables IPv6 FIB lookups, e.g. for reverse path filtering.
+ It also allows query of the FIB for the route type, e.g. local, unicast,
+ multicast or blackhole.
+
endif # NF_TABLES_IPV6
endif # NF_TABLES
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile
index b4f7d0b4e2af..7984f3071f05 100644
--- a/net/ipv6/netfilter/Makefile
+++ b/net/ipv6/netfilter/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_NFT_REJECT_IPV6) += nft_reject_ipv6.o
obj-$(CONFIG_NFT_MASQ_IPV6) += nft_masq_ipv6.o
obj-$(CONFIG_NFT_REDIR_IPV6) += nft_redir_ipv6.o
obj-$(CONFIG_NFT_DUP_IPV6) += nft_dup_ipv6.o
+obj-$(CONFIG_NFT_FIB_IPV6) += nft_fib_ipv6.o
# matches
obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o
diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c
new file mode 100644
index 000000000000..ff1f1b6b4a4a
--- /dev/null
+++ b/net/ipv6/netfilter/nft_fib_ipv6.c
@@ -0,0 +1,275 @@
+/*
+ * 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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter_ipv6.h>
+#include <net/netfilter/nf_tables_core.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nft_fib.h>
+
+#include <net/ip6_fib.h>
+#include <net/ip6_route.h>
+
+static bool fib6_is_local(const struct sk_buff *skb)
+{
+ const struct rt6_info *rt = (const void *)skb_dst(skb);
+
+ return rt && (rt->rt6i_flags & RTF_LOCAL);
+}
+
+static int get_ifindex(const struct net_device *dev)
+{
+ return dev ? dev->ifindex : 0;
+}
+
+static int nft_fib6_flowi_init(struct flowi6 *fl6, const struct nft_fib *priv,
+ const struct nft_pktinfo *pkt,
+ const struct net_device *dev)
+{
+ const struct ipv6hdr *iph = ipv6_hdr(pkt->skb);
+ int lookup_flags = 0;
+
+ if (priv->flags & NFTA_FIB_F_DADDR) {
+ fl6->daddr = iph->daddr;
+ fl6->saddr = iph->saddr;
+ } else {
+ fl6->daddr = iph->saddr;
+ fl6->saddr = iph->daddr;
+ }
+
+ if (ipv6_addr_type(&fl6->daddr) & IPV6_ADDR_LINKLOCAL) {
+ lookup_flags |= RT6_LOOKUP_F_IFACE;
+ fl6->flowi6_oif = get_ifindex(dev ? dev : pkt->skb->dev);
+ }
+
+ if (ipv6_addr_type(&fl6->saddr) & IPV6_ADDR_UNICAST)
+ lookup_flags |= RT6_LOOKUP_F_HAS_SADDR;
+
+ if (priv->flags & NFTA_FIB_F_MARK)
+ fl6->flowi6_mark = pkt->skb->mark;
+
+ fl6->flowlabel = (*(__be32 *)iph) & IPV6_FLOWINFO_MASK;
+
+ return lookup_flags;
+}
+
+static u32 __nft_fib6_eval_type(const struct nft_fib *priv,
+ const struct nft_pktinfo *pkt)
+{
+ const struct net_device *dev = NULL;
+ const struct nf_ipv6_ops *v6ops;
+ const struct nf_afinfo *afinfo;
+ int route_err, addrtype;
+ struct rt6_info *rt;
+ struct flowi6 fl6 = {
+ .flowi6_iif = LOOPBACK_IFINDEX,
+ .flowi6_proto = pkt->tprot,
+ };
+ u32 ret = 0;
+
+ afinfo = nf_get_afinfo(NFPROTO_IPV6);
+ if (!afinfo)
+ return RTN_UNREACHABLE;
+
+ if (priv->flags & NFTA_FIB_F_IIF)
+ dev = pkt->in;
+ else if (priv->flags & NFTA_FIB_F_OIF)
+ dev = pkt->out;
+
+ nft_fib6_flowi_init(&fl6, priv, pkt, dev);
+
+ v6ops = nf_get_ipv6_ops();
+ if (dev && v6ops && v6ops->chk_addr(pkt->net, &fl6.daddr, dev, true))
+ ret = RTN_LOCAL;
+
+ route_err = afinfo->route(pkt->net, (struct dst_entry **)&rt,
+ flowi6_to_flowi(&fl6), false);
+ if (route_err)
+ goto err;
+
+ if (rt->rt6i_flags & RTF_REJECT) {
+ route_err = rt->dst.error;
+ dst_release(&rt->dst);
+ goto err;
+ }
+
+ if (ipv6_anycast_destination((struct dst_entry *)rt, &fl6.daddr))
+ ret = RTN_ANYCAST;
+ else if (!dev && rt->rt6i_flags & RTF_LOCAL)
+ ret = RTN_LOCAL;
+
+ dst_release(&rt->dst);
+
+ if (ret)
+ return ret;
+
+ addrtype = ipv6_addr_type(&fl6.daddr);
+
+ if (addrtype & IPV6_ADDR_MULTICAST)
+ return RTN_MULTICAST;
+ if (addrtype & IPV6_ADDR_UNICAST)
+ return RTN_UNICAST;
+
+ return RTN_UNSPEC;
+ err:
+ switch (route_err) {
+ case -EINVAL:
+ return RTN_BLACKHOLE;
+ case -EACCES:
+ return RTN_PROHIBIT;
+ case -EAGAIN:
+ return RTN_THROW;
+ default:
+ break;
+ }
+
+ return RTN_UNREACHABLE;
+}
+
+void nft_fib6_eval_type(const struct nft_expr *expr, struct nft_regs *regs,
+ const struct nft_pktinfo *pkt)
+{
+ const struct nft_fib *priv = nft_expr_priv(expr);
+ u32 *dest = ®s->data[priv->dreg];
+
+ *dest = __nft_fib6_eval_type(priv, pkt);
+}
+EXPORT_SYMBOL_GPL(nft_fib6_eval_type);
+
+void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs,
+ const struct nft_pktinfo *pkt)
+{
+ const struct nft_fib *priv = nft_expr_priv(expr);
+ const struct net_device *oif = NULL;
+ u32 *dest = ®s->data[priv->dreg];
+ struct flowi6 fl6 = {
+ .flowi6_iif = LOOPBACK_IFINDEX,
+ .flowi6_proto = pkt->tprot,
+ };
+ struct rt6_info *rt;
+ int lookup_flags;
+
+ if (priv->flags & NFTA_FIB_F_IIF)
+ oif = pkt->in;
+ else if (priv->flags & NFTA_FIB_F_OIF)
+ oif = pkt->out;
+
+ lookup_flags = nft_fib6_flowi_init(&fl6, priv, pkt, oif);
+
+ if (pkt->hook == NF_INET_PRE_ROUTING && fib6_is_local(pkt->skb)) {
+ nft_fib_store_result(dest, priv->result, pkt, LOOPBACK_IFINDEX);
+ return;
+ }
+
+ *dest = 0;
+ again:
+ rt = (void *)ip6_route_lookup(pkt->net, &fl6, lookup_flags);
+ if (rt->dst.error)
+ goto put_rt_err;
+
+ /* Should not see RTF_LOCAL here */
+ if (rt->rt6i_flags & (RTF_REJECT | RTF_ANYCAST | RTF_LOCAL))
+ goto put_rt_err;
+
+ if (oif && oif != rt->rt6i_idev->dev) {
+ /* multipath route? Try again with F_IFACE */
+ if ((lookup_flags & RT6_LOOKUP_F_IFACE) == 0) {
+ lookup_flags |= RT6_LOOKUP_F_IFACE;
+ fl6.flowi6_oif = oif->ifindex;
+ ip6_rt_put(rt);
+ goto again;
+ }
+ }
+
+ switch (priv->result) {
+ case NFT_FIB_RESULT_OIF:
+ *dest = rt->rt6i_idev->dev->ifindex;
+ break;
+ case NFT_FIB_RESULT_OIFNAME:
+ strncpy((char *)dest, rt->rt6i_idev->dev->name, IFNAMSIZ);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ break;
+ }
+
+ put_rt_err:
+ ip6_rt_put(rt);
+}
+EXPORT_SYMBOL_GPL(nft_fib6_eval);
+
+static struct nft_expr_type nft_fib6_type;
+
+static const struct nft_expr_ops nft_fib6_type_ops = {
+ .type = &nft_fib6_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_fib)),
+ .eval = nft_fib6_eval_type,
+ .init = nft_fib_init,
+ .dump = nft_fib_dump,
+ .validate = nft_fib_validate,
+};
+
+static const struct nft_expr_ops nft_fib6_ops = {
+ .type = &nft_fib6_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_fib)),
+ .eval = nft_fib6_eval,
+ .init = nft_fib_init,
+ .dump = nft_fib_dump,
+ .validate = nft_fib_validate,
+};
+
+static const struct nft_expr_ops *
+nft_fib6_select_ops(const struct nft_ctx *ctx,
+ const struct nlattr * const tb[])
+{
+ enum nft_fib_result result;
+
+ if (!tb[NFTA_FIB_RESULT])
+ return ERR_PTR(-EINVAL);
+
+ result = htonl(nla_get_be32(tb[NFTA_FIB_RESULT]));
+
+ switch (result) {
+ case NFT_FIB_RESULT_OIF:
+ return &nft_fib6_ops;
+ case NFT_FIB_RESULT_OIFNAME:
+ return &nft_fib6_ops;
+ case NFT_FIB_RESULT_ADDRTYPE:
+ return &nft_fib6_type_ops;
+ default:
+ return ERR_PTR(-EOPNOTSUPP);
+ }
+}
+
+static struct nft_expr_type nft_fib6_type __read_mostly = {
+ .name = "fib",
+ .select_ops = &nft_fib6_select_ops,
+ .policy = nft_fib_policy,
+ .maxattr = NFTA_FIB_MAX,
+ .family = NFPROTO_IPV6,
+ .owner = THIS_MODULE,
+};
+
+static int __init nft_fib6_module_init(void)
+{
+ return nft_register_expr(&nft_fib6_type);
+}
+
+static void __exit nft_fib6_module_exit(void)
+{
+ nft_unregister_expr(&nft_fib6_type);
+}
+module_init(nft_fib6_module_init);
+module_exit(nft_fib6_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
+MODULE_ALIAS_NFT_AF_EXPR(10, "fib");
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index e8d56d9a4df2..9bcf899ce16e 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -581,6 +581,19 @@ config NFT_HASH
This option adds the "hash" expression that you can use to perform
a hash operation on registers.
+config NFT_FIB
+ tristate
+
+config NFT_FIB_INET
+ depends on NF_TABLES_INET
+ depends on NFT_FIB_IPV4
+ depends on NFT_FIB_IPV6
+ tristate "Netfilter nf_tables fib inet support"
+ help
+ This option allows using the FIB expression from the inet table.
+ The lookup will be delegated to the IPv4 or IPv6 FIB depending
+ on the protocol of the packet.
+
if NF_TABLES_NETDEV
config NF_DUP_NETDEV
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index c23c3c84416f..8faa36c0686d 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -96,6 +96,8 @@ obj-$(CONFIG_NFT_LOG) += nft_log.o
obj-$(CONFIG_NFT_MASQ) += nft_masq.o
obj-$(CONFIG_NFT_REDIR) += nft_redir.o
obj-$(CONFIG_NFT_HASH) += nft_hash.o
+obj-$(CONFIG_NFT_FIB) += nft_fib.o
+obj-$(CONFIG_NFT_FIB_INET) += nft_fib_inet.o
# nf_tables netdev
obj-$(CONFIG_NFT_DUP_NETDEV) += nft_dup_netdev.o
diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c
new file mode 100644
index 000000000000..4944a8b7f7a7
--- /dev/null
+++ b/net/netfilter/nft_fib.c
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ *
+ * Generic part shared by ipv4 and ipv6 backends.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_core.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nft_fib.h>
+
+const struct nla_policy nft_fib_policy[NFTA_FIB_MAX + 1] = {
+ [NFTA_FIB_DREG] = { .type = NLA_U32 },
+ [NFTA_FIB_RESULT] = { .type = NLA_U32 },
+ [NFTA_FIB_FLAGS] = { .type = NLA_U32 },
+};
+EXPORT_SYMBOL(nft_fib_policy);
+
+#define NFTA_FIB_F_ALL (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR | \
+ NFTA_FIB_F_MARK | NFTA_FIB_F_IIF | NFTA_FIB_F_OIF)
+
+int nft_fib_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
+ const struct nft_data **data)
+{
+ const struct nft_fib *priv = nft_expr_priv(expr);
+ unsigned int hooks;
+
+ switch (priv->result) {
+ case NFT_FIB_RESULT_OIF: /* fallthrough */
+ case NFT_FIB_RESULT_OIFNAME:
+ hooks = (1 << NF_INET_PRE_ROUTING);
+ break;
+ case NFT_FIB_RESULT_ADDRTYPE:
+ if (priv->flags & NFTA_FIB_F_IIF)
+ hooks = (1 << NF_INET_PRE_ROUTING) |
+ (1 << NF_INET_LOCAL_IN) |
+ (1 << NF_INET_FORWARD);
+ else if (priv->flags & NFTA_FIB_F_OIF)
+ hooks = (1 << NF_INET_LOCAL_OUT) |
+ (1 << NF_INET_POST_ROUTING) |
+ (1 << NF_INET_FORWARD);
+ else
+ hooks = (1 << NF_INET_LOCAL_IN) |
+ (1 << NF_INET_LOCAL_OUT) |
+ (1 << NF_INET_FORWARD) |
+ (1 << NF_INET_PRE_ROUTING) |
+ (1 << NF_INET_POST_ROUTING);
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return nft_chain_validate_hooks(ctx->chain, hooks);
+}
+EXPORT_SYMBOL_GPL(nft_fib_validate);
+
+int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+ const struct nlattr * const tb[])
+{
+ struct nft_fib *priv = nft_expr_priv(expr);
+ unsigned int len;
+ int err;
+
+ if (!tb[NFTA_FIB_DREG] || !tb[NFTA_FIB_RESULT] || !tb[NFTA_FIB_FLAGS])
+ return -EINVAL;
+
+ priv->flags = ntohl(nla_get_be32(tb[NFTA_FIB_FLAGS]));
+
+ if (priv->flags == 0 || (priv->flags & ~NFTA_FIB_F_ALL))
+ return -EINVAL;
+
+ if ((priv->flags & (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) ==
+ (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR))
+ return -EINVAL;
+ if ((priv->flags & (NFTA_FIB_F_IIF | NFTA_FIB_F_OIF)) ==
+ (NFTA_FIB_F_IIF | NFTA_FIB_F_OIF))
+ return -EINVAL;
+ if ((priv->flags & (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) == 0)
+ return -EINVAL;
+
+ priv->result = htonl(nla_get_be32(tb[NFTA_FIB_RESULT]));
+ priv->dreg = nft_parse_register(tb[NFTA_FIB_DREG]);
+
+ switch (priv->result) {
+ case NFT_FIB_RESULT_OIF:
+ if (priv->flags & NFTA_FIB_F_OIF)
+ return -EINVAL;
+ len = sizeof(int);
+ break;
+ case NFT_FIB_RESULT_OIFNAME:
+ if (priv->flags & NFTA_FIB_F_OIF)
+ return -EINVAL;
+ len = IFNAMSIZ;
+ break;
+ case NFT_FIB_RESULT_ADDRTYPE:
+ len = sizeof(u32);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ err = nft_validate_register_store(ctx, priv->dreg, NULL,
+ NFT_DATA_VALUE, len);
+ if (err < 0)
+ return err;
+
+ return nft_fib_validate(ctx, expr, NULL);
+}
+EXPORT_SYMBOL_GPL(nft_fib_init);
+
+int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+ const struct nft_fib *priv = nft_expr_priv(expr);
+
+ if (nft_dump_register(skb, NFTA_FIB_DREG, priv->dreg))
+ return -1;
+
+ if (nla_put_be32(skb, NFTA_FIB_RESULT, htonl(priv->result)))
+ return -1;
+
+ if (nla_put_be32(skb, NFTA_FIB_FLAGS, htonl(priv->flags)))
+ return -1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nft_fib_dump);
+
+void nft_fib_store_result(void *reg, enum nft_fib_result r,
+ const struct nft_pktinfo *pkt, int index)
+{
+ struct net_device *dev;
+ u32 *dreg = reg;
+
+ switch (r) {
+ case NFT_FIB_RESULT_OIF:
+ *dreg = index;
+ break;
+ case NFT_FIB_RESULT_OIFNAME:
+ dev = dev_get_by_index_rcu(pkt->net, index);
+ strncpy(reg, dev ? dev->name : "", IFNAMSIZ);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ *dreg = 0;
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(nft_fib_store_result);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
diff --git a/net/netfilter/nft_fib_inet.c b/net/netfilter/nft_fib_inet.c
new file mode 100644
index 000000000000..fe8943b572b7
--- /dev/null
+++ b/net/netfilter/nft_fib_inet.c
@@ -0,0 +1,82 @@
+/*
+ * 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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_core.h>
+#include <net/netfilter/nf_tables.h>
+
+#include <net/netfilter/nft_fib.h>
+
+static void nft_fib_inet_eval(const struct nft_expr *expr,
+ struct nft_regs *regs,
+ const struct nft_pktinfo *pkt)
+{
+ const struct nft_fib *priv = nft_expr_priv(expr);
+
+ switch (pkt->pf) {
+ case NFPROTO_IPV4:
+ switch (priv->result) {
+ case NFT_FIB_RESULT_OIF:
+ case NFT_FIB_RESULT_OIFNAME:
+ return nft_fib4_eval(expr, regs, pkt);
+ case NFT_FIB_RESULT_ADDRTYPE:
+ return nft_fib4_eval_type(expr, regs, pkt);
+ }
+ break;
+ case NFPROTO_IPV6:
+ switch (priv->result) {
+ case NFT_FIB_RESULT_OIF:
+ case NFT_FIB_RESULT_OIFNAME:
+ return nft_fib6_eval(expr, regs, pkt);
+ case NFT_FIB_RESULT_ADDRTYPE:
+ return nft_fib6_eval_type(expr, regs, pkt);
+ }
+ break;
+ }
+
+ regs->verdict.code = NF_DROP;
+}
+
+static struct nft_expr_type nft_fib_inet_type;
+static const struct nft_expr_ops nft_fib_inet_ops = {
+ .type = &nft_fib_inet_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_fib)),
+ .eval = nft_fib_inet_eval,
+ .init = nft_fib_init,
+ .dump = nft_fib_dump,
+ .validate = nft_fib_validate,
+};
+
+static struct nft_expr_type nft_fib_inet_type __read_mostly = {
+ .family = NFPROTO_INET,
+ .name = "fib",
+ .ops = &nft_fib_inet_ops,
+ .policy = nft_fib_policy,
+ .maxattr = NFTA_FIB_MAX,
+ .owner = THIS_MODULE,
+};
+
+static int __init nft_fib_inet_module_init(void)
+{
+ return nft_register_expr(&nft_fib_inet_type);
+}
+
+static void __exit nft_fib_inet_module_exit(void)
+{
+ nft_unregister_expr(&nft_fib_inet_type);
+}
+
+module_init(nft_fib_inet_module_init);
+module_exit(nft_fib_inet_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
+MODULE_ALIAS_NFT_AF_EXPR(1, "fib");
--
2.7.3
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/3 libnftables] expr: add fib expression
2016-10-24 14:56 [PATCH 0/3 various] netfilter: add fib expression Florian Westphal
2016-10-24 14:56 ` [PATCH 1/3 nf-next] nf_tables: " Florian Westphal
@ 2016-10-24 14:56 ` Florian Westphal
2016-10-24 14:56 ` [PATCH 3/3 nft] src: " Florian Westphal
2 siblings, 0 replies; 5+ messages in thread
From: Florian Westphal @ 2016-10-24 14:56 UTC (permalink / raw)
To: netfilter-devel; +Cc: Florian Westphal
Signed-off-by: Florian Westphal <fw@strlen.de>
---
include/libnftnl/expr.h | 6 +
include/linux/netfilter/nf_tables.h | 36 +++++
src/Makefile.am | 1 +
src/expr/fib.c | 273 ++++++++++++++++++++++++++++++++++++
src/expr_ops.c | 2 +
5 files changed, 318 insertions(+)
create mode 100644 src/expr/fib.c
diff --git a/include/libnftnl/expr.h b/include/libnftnl/expr.h
index edf86a966fc0..ad765d28d472 100644
--- a/include/libnftnl/expr.h
+++ b/include/libnftnl/expr.h
@@ -222,6 +222,12 @@ enum {
NFTNL_EXPR_HASH_OFFSET,
};
+enum {
+ NFTNL_EXPR_FIB_DREG = NFTNL_EXPR_BASE,
+ NFTNL_EXPR_FIB_RESULT,
+ NFTNL_EXPR_FIB_FLAGS,
+};
+
/*
* Compat
*/
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 30e3b21418c5..8d950dc601d1 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -1103,6 +1103,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, not dst */
+ NFTA_FIB_F_DADDR = 1 << 1, /* look up dst (default) */
+ NFTA_FIB_F_MARK = 1 << 2, /* set fib 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 eac7a5678009..578374915647 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -30,6 +30,7 @@ libnftnl_la_SOURCES = utils.c \
expr/data_reg.c \
expr/dup.c \
expr/exthdr.c \
+ expr/fib.c \
expr/fwd.c \
expr/limit.c \
expr/log.c \
diff --git a/src/expr/fib.c b/src/expr/fib.c
new file mode 100644
index 000000000000..9e63621e7287
--- /dev/null
+++ b/src/expr/fib.c
@@ -0,0 +1,273 @@
+/*
+ * (C) 2016 Red Hat GmbH
+ * Author: Florian Westphal <fw@strlen.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_fib {
+ uint32_t flags;
+ uint32_t result;
+ enum nft_registers dreg;
+};
+
+static int
+nftnl_expr_fib_set(struct nftnl_expr *e, uint16_t result,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+
+ switch (result) {
+ case NFTNL_EXPR_FIB_RESULT:
+ fib->result = *((uint32_t *)data);
+ break;
+ case NFTNL_EXPR_FIB_DREG:
+ fib->dreg = *((uint32_t *)data);
+ break;
+ case NFTNL_EXPR_FIB_FLAGS:
+ fib->flags = *((uint32_t *)data);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_fib_get(const struct nftnl_expr *e, uint16_t result,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+
+ switch (result) {
+ case NFTNL_EXPR_FIB_RESULT:
+ *data_len = sizeof(fib->result);
+ return &fib->result;
+ case NFTNL_EXPR_FIB_DREG:
+ *data_len = sizeof(fib->dreg);
+ return &fib->dreg;
+ case NFTNL_EXPR_FIB_FLAGS:
+ *data_len = sizeof(fib->flags);
+ return &fib->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_fib_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_FIB_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_FIB_RESULT:
+ case NFTA_FIB_DREG:
+ case NFTA_FIB_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_fib_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_FIB_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_FIB_FLAGS, htonl(fib->flags));
+ if (e->flags & (1 << NFTNL_EXPR_FIB_RESULT))
+ mnl_attr_put_u32(nlh, NFTA_FIB_RESULT, htonl(fib->result));
+ if (e->flags & (1 << NFTNL_EXPR_FIB_DREG))
+ mnl_attr_put_u32(nlh, NFTA_FIB_DREG, htonl(fib->dreg));
+}
+
+static int
+nftnl_expr_fib_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_FIB_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_fib_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_FIB_RESULT]) {
+ fib->result = ntohl(mnl_attr_get_u32(tb[NFTA_FIB_RESULT]));
+ e->flags |= (1 << NFTNL_EXPR_FIB_RESULT);
+ }
+ if (tb[NFTA_FIB_DREG]) {
+ fib->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_FIB_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_FIB_DREG);
+ }
+ if (tb[NFTA_FIB_FLAGS]) {
+ fib->flags = ntohl(mnl_attr_get_u32(tb[NFTA_FIB_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_FIB_FLAGS);
+ }
+ return ret;
+}
+
+static int nftnl_expr_fib_json_parse(struct nftnl_expr *e, json_t *root,
+ struct nftnl_parse_err *err)
+{
+#ifdef JSON_PARSING
+ uint32_t result, flags, dreg;
+
+ if (nftnl_jansson_parse_reg(root, "result", NFTNL_TYPE_U32,
+ &result, err) == 0)
+ nftnl_expr_set_u32(e, NFTNL_EXPR_FIB_RESULT, result);
+
+ if (nftnl_jansson_parse_reg(root, "dreg", NFTNL_TYPE_U32,
+ &dreg, err) == 0)
+ nftnl_expr_set_u32(e, NFTNL_EXPR_FIB_DREG, dreg);
+
+ if (nftnl_jansson_parse_val(root, "flags", NFTNL_TYPE_U32,
+ &flags, err) == 0)
+ nftnl_expr_set_u32(e, NFTNL_EXPR_FIB_FLAGS, flags);
+
+ return 0;
+#else
+ errno = EOPNOTSUPP;
+ return -1;
+#endif
+}
+
+static const char *fib_type[NFT_FIB_RESULT_MAX + 1] = {
+ [NFT_FIB_RESULT_OIF] = "oif",
+ [NFT_FIB_RESULT_OIFNAME] = "oifname",
+ [NFT_FIB_RESULT_ADDRTYPE] = "type",
+};
+
+static const char *fib_type_str(enum nft_fib_result r)
+{
+ if (r <= NFT_FIB_RESULT_MAX)
+ return fib_type[r];
+
+ return "unknown";
+}
+
+static int
+nftnl_expr_fib_snprintf_default(char *buf, size_t size,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+ int len = size, offset = 0, ret, i;
+ uint32_t flags = fib->flags;
+ static const struct {
+ int bit;
+ const char *name;
+ } tab[] = {
+ { NFTA_FIB_F_SADDR, "saddr" },
+ { NFTA_FIB_F_DADDR, "daddr" },
+ { NFTA_FIB_F_MARK, "mark" },
+ { NFTA_FIB_F_IIF, "iif" },
+ { NFTA_FIB_F_OIF, "oif" },
+ };
+
+ for (i = 0; i < (sizeof(tab) / sizeof(tab[0])); i++) {
+ if (flags & tab[i].bit) {
+ ret = snprintf(buf + offset, len, "%s ", tab[i].name);
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+ flags &= ~tab[i].bit;
+ if (flags) {
+ ret = snprintf(buf + offset, len, ". ");
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+ }
+ }
+ }
+
+ if (flags) {
+ ret = snprintf(buf + offset, len, "unknown 0x%" PRIx32, flags);
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+ }
+
+ ret = snprintf(buf + offset, len, "%s => reg %d ", fib_type_str(fib->result), fib->dreg);
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+ return offset;
+}
+
+static int nftnl_expr_fib_export(char *buf, size_t size,
+ const struct nftnl_expr *e, int type)
+{
+ struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+
+ NFTNL_BUF_INIT(b, buf, size);
+
+ if (e->flags & (1 << NFTNL_EXPR_FIB_RESULT))
+ nftnl_buf_u32(&b, type, fib->result, OP);
+ if (e->flags & (1 << NFTNL_EXPR_FIB_DREG))
+ nftnl_buf_u32(&b, type, fib->dreg, DREG);
+ if (e->flags & (1 << NFTNL_EXPR_FIB_FLAGS))
+ nftnl_buf_u32(&b, type, fib->flags, FLAGS);
+
+ return nftnl_buf_done(&b);
+}
+
+static int
+nftnl_expr_fib_snprintf(char *buf, size_t len, uint32_t type,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ switch (type) {
+ case NFTNL_OUTPUT_DEFAULT:
+ return nftnl_expr_fib_snprintf_default(buf, len, e);
+ case NFTNL_OUTPUT_XML:
+ case NFTNL_OUTPUT_JSON:
+ return nftnl_expr_fib_export(buf, len, e, type);
+ default:
+ break;
+ }
+ return -1;
+}
+
+static bool nftnl_expr_fib_cmp(const struct nftnl_expr *e1,
+ const struct nftnl_expr *e2)
+{
+ struct nftnl_expr_fib *h1 = nftnl_expr_data(e1);
+ struct nftnl_expr_fib *h2 = nftnl_expr_data(e2);
+ bool eq = true;
+
+ if (e1->flags & (1 << NFTNL_EXPR_FIB_RESULT))
+ eq &= (h1->result == h2->result);
+ if (e1->flags & (1 << NFTNL_EXPR_FIB_DREG))
+ eq &= (h1->dreg == h2->dreg);
+ if (e1->flags & (1 << NFTNL_EXPR_FIB_FLAGS))
+ eq &= (h1->flags == h2->flags);
+
+ return eq;
+}
+
+struct expr_ops expr_ops_fib = {
+ .name = "fib",
+ .alloc_len = sizeof(struct nftnl_expr_fib),
+ .max_attr = NFTA_FIB_MAX,
+ .cmp = nftnl_expr_fib_cmp,
+ .set = nftnl_expr_fib_set,
+ .get = nftnl_expr_fib_get,
+ .parse = nftnl_expr_fib_parse,
+ .build = nftnl_expr_fib_build,
+ .snprintf = nftnl_expr_fib_snprintf,
+ .json_parse = nftnl_expr_fib_json_parse,
+};
diff --git a/src/expr_ops.c b/src/expr_ops.c
index 96b1f8df9d3b..897c002dabab 100644
--- a/src/expr_ops.c
+++ b/src/expr_ops.c
@@ -30,6 +30,7 @@ extern struct expr_ops expr_ops_quota;
extern struct expr_ops expr_ops_target;
extern struct expr_ops expr_ops_dynset;
extern struct expr_ops expr_ops_hash;
+extern struct expr_ops expr_ops_fib;
static struct expr_ops expr_ops_notrack = {
.name = "notrack",
@@ -63,6 +64,7 @@ static struct expr_ops *expr_ops[] = {
&expr_ops_target,
&expr_ops_dynset,
&expr_ops_hash,
+ &expr_ops_fib,
NULL,
};
--
2.7.3
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 3/3 nft] src: add fib expression
2016-10-24 14:56 [PATCH 0/3 various] netfilter: add fib expression Florian Westphal
2016-10-24 14:56 ` [PATCH 1/3 nf-next] nf_tables: " Florian Westphal
2016-10-24 14:56 ` [PATCH 2/3 libnftables] expr: " Florian Westphal
@ 2016-10-24 14:56 ` Florian Westphal
2 siblings, 0 replies; 5+ messages in thread
From: Florian Westphal @ 2016-10-24 14:56 UTC (permalink / raw)
To: netfilter-devel; +Cc: Florian Westphal
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.:
blackhole : drop,
prohibit : drop,
unicast : accept
}
Signed-off-by: Florian Westphal <fw@strlen.de>
---
doc/nft.xml | 103 ++++++++++++++++++++++++++
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 +
12 files changed, 387 insertions(+)
create mode 100644 include/fib.h
create mode 100644 src/fib.c
diff --git a/doc/nft.xml b/doc/nft.xml
index 3b215f8c34f4..986b1ff9e115 100644
--- a/doc/nft.xml
+++ b/doc/nft.xml
@@ -1222,6 +1222,109 @@ filter output oif eth0
</example>
</para>
</refsect2>
+
+ <refsect2>
+ <title>fib expressions</title>
+ <para>
+ <cmdsynopsis>
+ <command>fib</command>
+ <group choice="req">
+ <arg>saddr</arg>
+ <arg>daddr</arg>
+ <group choice="opt">
+ <arg>mark</arg>
+ <arg>iif</arg>
+ <arg>oif</arg>
+ </group>
+ </group>
+ <group choice="req">
+ <arg>oif</arg>
+ <arg>oifname</arg>
+ <arg>type</arg>
+ </group>
+ </cmdsynopsis>
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ fib expressions return or as qualified meta expressions.
+ </para>
+ <para>
+ <table frame="all">
+ <title>Meta expression types</title>
+ <tgroup cols='3' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1'/>
+ <colspec colname='c2'/>
+ <colspec colname='c3'/>
+ <thead>
+ <row>
+ <entry>Keyword</entry>
+ <entry>Description</entry>
+ <entry>Type</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>oif</entry>
+ <entry>Output interface index</entry>
+ <entry>integer (32 bit)</entry>
+ </row>
+ <row>
+ <entry>oifname</entry>
+ <entry>Output interface name</entry>
+ <entry>string</entry>
+ </row>
+ <row>
+ <entry>type</entry>
+ <entry>Address type</entry>
+ <entry>fib_addrtype</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </para>
+ <para>
+ <table frame="all">
+ <title>fib expression specific types</title>
+ <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1'/>
+ <colspec colname='c2'/>
+ <thead>
+ <row>
+ <entry>Type</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>fib_addrtype</entry>
+ <entry>
+ Address type stored in the fib.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </para>
+ <para>
+ <example>
+ <title>Using fib expressions</title>
+ <programlisting>
+# 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 }
+ </programlisting>
+ </example>
+ </para>
+ </refsect2>
</refsect1>
<refsect1>
diff --git a/include/datatype.h b/include/datatype.h
index 12ec46bcc7bb..9f3f711c7bb0 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 13ca315cb9e7..33f8abeee690 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -59,6 +59,7 @@ enum expr_types {
EXPR_RELATIONAL,
EXPR_NUMGEN,
EXPR_HASH,
+ EXPR_FIB,
};
enum ops {
@@ -178,6 +179,7 @@ enum expr_flags {
#include <payload.h>
#include <exthdr.h>
+#include <fib.h>
#include <numgen.h>
#include <meta.h>
#include <hash.h>
@@ -298,6 +300,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 000000000000..3a019e65c814
--- /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 b21a844cf5d5..e71df36fd097 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -1099,6 +1099,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 63bbef2cc151..60877b240014 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 \
numgen.c \
diff --git a/src/evaluate.c b/src/evaluate.c
index 45af3298537c..81b8c26bcc79 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1606,6 +1606,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 000000000000..346cce322da2
--- /dev/null
+++ b/src/fib.c
@@ -0,0 +1,144 @@
+/*
+ * FIB expression.
+ *
+ * Copyright (c) Red Hat GmbH. Author: Florian Westphal <fw@strlen.de>
+ *
+ * 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 <nftables.h>
+#include <erec.h>
+#include <expression.h>
+#include <datatype.h>
+#include <gmputil.h>
+#include <utils.h>
+#include <string.h>
+#include <fib.h>
+
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+
+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 d8d1d7d7aaa7..afa9b99a0559 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)
@@ -1103,6 +1120,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,
@@ -1725,6 +1743,7 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
case EXPR_META:
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 0072dca091ea..048e3979e6e9 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)
@@ -648,6 +662,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 baf0a539efa0..dd265db47a82 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"
@@ -595,6 +598,10 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%destructor { expr_free($$); } ct_expr
%type <val> ct_key ct_key_dir ct_key_counters
+%type <expr> fib_expr
+%destructor { expr_free($$); } fib_expr
+%type <val> fib_tuple fib_result fib_flag
+
%type <val> export_format
%type <string> monitor_event
%destructor { xfree($$); } monitor_event
@@ -1983,9 +1990,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 8b5a383bd095..a68def8d1293 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -477,6 +477,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"dup" { return DUP; }
"fwd" { return FWD; }
+"fib" { return FIB; }
+
"xml" { return XML; }
"json" { return JSON; }
--
2.7.3
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 1/3 nf-next] nf_tables: add fib expression
2016-10-24 14:56 ` [PATCH 1/3 nf-next] nf_tables: " Florian Westphal
@ 2016-10-27 18:16 ` Pablo Neira Ayuso
0 siblings, 0 replies; 5+ messages in thread
From: Pablo Neira Ayuso @ 2016-10-27 18:16 UTC (permalink / raw)
To: Florian Westphal; +Cc: netfilter-devel
On Mon, Oct 24, 2016 at 04:56:40PM +0200, Florian Westphal wrote:
> Add FIB expression, supported for ipv4, ipv6 and inet family (the latter
> just dispatches to ipv4 or ipv6 one based on nfproto).
>
> Currently supports fetching output interface index/name and the
> rtm_type associated with an address.
>
> This can be used for adding path filtering. rtm_type is useful
> to e.g. enforce a strong-end host model where packets
> are only accepted if daddr is configured on the interface the
> packet arrived on.
>
> The fib expression is a native nftables alternative to the
> xtables addrtype and rp_filter matches.
>
> FIB result order for oif/oifname retrieval is as follows:
> - if packet is local (skb has rtable, RTF_LOCAL set, this
> will also catch looped-back multicast packets), set oif to
> the loopback interface.
> - if fib lookup returns an error, or result points to local,
> store zero result. This means '--local' option of -m rpfilter
> is not supported. It is possible to use 'fib type local' or add
> explicit saddr/daddr matching rules to create exceptions if this
> is really needed.
> - store result in the destination register.
> In case of multiple routes, search set for desired oif in case
> strict matching is requested.
>
> ipv4 and ipv6 behave fib expressions are supposed to behave the same.
This looks great, applied, thanks Florian.
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2016-10-27 18:17 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-10-24 14:56 [PATCH 0/3 various] netfilter: add fib expression Florian Westphal
2016-10-24 14:56 ` [PATCH 1/3 nf-next] nf_tables: " Florian Westphal
2016-10-27 18:16 ` Pablo Neira Ayuso
2016-10-24 14:56 ` [PATCH 2/3 libnftables] expr: " Florian Westphal
2016-10-24 14:56 ` [PATCH 3/3 nft] src: " Florian Westphal
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).