@@ -42,6 +42,10 @@ config NFT_CHAIN_ROUTE_IPV6
fields such as the source, destination, flowlabel, hop-limit and
the packet mark.
+config NFT_RT_IPV6
+ default NFT_RT
+ tristate
+
config NFT_REJECT_IPV6
select NF_REJECT_IPV6
default NFT_REJECT
@@ -36,6 +36,7 @@ obj-$(CONFIG_NF_DUP_IPV6) += nf_dup_ipv6.o
obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o
obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o
obj-$(CONFIG_NFT_CHAIN_NAT_IPV6) += nft_chain_nat_ipv6.o
+obj-$(CONFIG_NFT_RT_IPV6) += nft_rt_ipv6.o
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
new file mode 100644
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2016 Anders K. Pedersen <akp@cohaesio.com>
+ *
+ * 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/ip6_route.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nft_rt.h>
+
+static void nft_rt_ipv6_get_eval(const struct nft_expr *expr,
+ struct nft_regs *regs,
+ const struct nft_pktinfo *pkt)
+{
+ const struct nft_rt *priv = nft_expr_priv(expr);
+ const struct sk_buff *skb = pkt->skb;
+ u32 *dest = ®s->data[priv->dreg];
+
+ switch (priv->key) {
+ case NFT_RT_NEXTHOP: {
+ struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
+
+ if (!rt)
+ goto err;
+ memcpy(dest, rt6_nexthop(rt, &ipv6_hdr(skb)->daddr),
+ sizeof(struct in6_addr));
+ break;
+ }
+ default:
+ return nft_rt_get_eval(expr, regs, pkt);
+ }
+
+ return;
+err:
+ regs->verdict.code = NFT_BREAK;
+}
+
+static int nft_rt_ipv6_get_init(const struct nft_ctx *ctx,
+ const struct nft_expr *expr,
+ const struct nlattr * const tb[])
+{
+ struct nft_rt *priv = nft_expr_priv(expr);
+ unsigned int len;
+
+ if (tb[NFTA_RT_KEY] == NULL ||
+ tb[NFTA_RT_DREG] == NULL ||
+ tb[NFTA_RT_FAMILY] == NULL)
+ return -EINVAL;
+
+ priv->key = ntohl(nla_get_be32(tb[NFTA_RT_KEY]));
+ switch (priv->key) {
+ case NFT_RT_NEXTHOP:
+ len = sizeof(struct in6_addr);
+ break;
+ default:
+ return nft_rt_get_init(ctx, expr, tb);
+ }
+
+ priv->family = ntohl(nla_get_be32(tb[NFTA_RT_FAMILY]));
+ if (priv->family != NFPROTO_IPV6)
+ return -EINVAL;
+
+ priv->dreg = nft_parse_register(tb[NFTA_RT_DREG]);
+ return nft_validate_register_store(ctx, priv->dreg, NULL,
+ NFT_DATA_VALUE, len);
+}
+
+static struct nft_expr_type nft_rt_ipv6_type;
+static const struct nft_expr_ops nft_rt_ipv6_get_ops = {
+ .type = &nft_rt_ipv6_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_rt)),
+ .eval = nft_rt_ipv6_get_eval,
+ .init = nft_rt_ipv6_get_init,
+ .dump = nft_rt_get_dump,
+};
+
+static struct nft_expr_type nft_rt_ipv6_type __read_mostly = {
+ .family = NFPROTO_IPV6,
+ .name = "rt",
+ .ops = &nft_rt_ipv6_get_ops,
+ .policy = nft_rt_policy,
+ .maxattr = NFTA_RT_MAX,
+ .owner = THIS_MODULE,
+};
+
+static int __init nft_rt_ipv6_module_init(void)
+{
+ return nft_register_expr(&nft_rt_ipv6_type);
+}
+
+static void __exit nft_rt_ipv6_module_exit(void)
+{
+ nft_unregister_expr(&nft_rt_ipv6_type);
+}
+
+module_init(nft_rt_ipv6_module_init);
+module_exit(nft_rt_ipv6_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anders K. Pedersen <akp@cohaesio.com>");
+MODULE_ALIAS_NFT_AF_EXPR(AF_INET6, "rt");