linux/net/ipv6/netfilter/nft_fib_ipv6.c
<<
>>
Prefs
   1/*
   2 * This program is free software; you can redistribute it and/or modify
   3 * it under the terms of the GNU General Public License version 2 as
   4 * published by the Free Software Foundation.
   5 */
   6
   7#include <linux/kernel.h>
   8#include <linux/init.h>
   9#include <linux/module.h>
  10#include <linux/netlink.h>
  11#include <linux/netfilter.h>
  12#include <linux/netfilter/nf_tables.h>
  13#include <linux/netfilter_ipv6.h>
  14#include <net/netfilter/nf_tables_core.h>
  15#include <net/netfilter/nf_tables.h>
  16#include <net/netfilter/nft_fib.h>
  17
  18#include <net/ip6_fib.h>
  19#include <net/ip6_route.h>
  20
  21static int get_ifindex(const struct net_device *dev)
  22{
  23        return dev ? dev->ifindex : 0;
  24}
  25
  26static int nft_fib6_flowi_init(struct flowi6 *fl6, const struct nft_fib *priv,
  27                               const struct nft_pktinfo *pkt,
  28                               const struct net_device *dev)
  29{
  30        const struct ipv6hdr *iph = ipv6_hdr(pkt->skb);
  31        int lookup_flags = 0;
  32
  33        if (priv->flags & NFTA_FIB_F_DADDR) {
  34                fl6->daddr = iph->daddr;
  35                fl6->saddr = iph->saddr;
  36        } else {
  37                fl6->daddr = iph->saddr;
  38                fl6->saddr = iph->daddr;
  39        }
  40
  41        if (ipv6_addr_type(&fl6->daddr) & IPV6_ADDR_LINKLOCAL) {
  42                lookup_flags |= RT6_LOOKUP_F_IFACE;
  43                fl6->flowi6_oif = get_ifindex(dev ? dev : pkt->skb->dev);
  44        }
  45
  46        if (ipv6_addr_type(&fl6->saddr) & IPV6_ADDR_UNICAST)
  47                lookup_flags |= RT6_LOOKUP_F_HAS_SADDR;
  48
  49        if (priv->flags & NFTA_FIB_F_MARK)
  50                fl6->flowi6_mark = pkt->skb->mark;
  51
  52        fl6->flowlabel = (*(__be32 *)iph) & IPV6_FLOWINFO_MASK;
  53
  54        return lookup_flags;
  55}
  56
  57static u32 __nft_fib6_eval_type(const struct nft_fib *priv,
  58                                const struct nft_pktinfo *pkt)
  59{
  60        const struct net_device *dev = NULL;
  61        const struct nf_ipv6_ops *v6ops;
  62        const struct nf_afinfo *afinfo;
  63        int route_err, addrtype;
  64        struct rt6_info *rt;
  65        struct flowi6 fl6 = {
  66                .flowi6_iif = LOOPBACK_IFINDEX,
  67                .flowi6_proto = pkt->tprot,
  68        };
  69        u32 ret = 0;
  70
  71        afinfo = nf_get_afinfo(NFPROTO_IPV6);
  72        if (!afinfo)
  73                return RTN_UNREACHABLE;
  74
  75        if (priv->flags & NFTA_FIB_F_IIF)
  76                dev = nft_in(pkt);
  77        else if (priv->flags & NFTA_FIB_F_OIF)
  78                dev = nft_out(pkt);
  79
  80        nft_fib6_flowi_init(&fl6, priv, pkt, dev);
  81
  82        v6ops = nf_get_ipv6_ops();
  83        if (dev && v6ops && v6ops->chk_addr(nft_net(pkt), &fl6.daddr, dev, true))
  84                ret = RTN_LOCAL;
  85
  86        route_err = afinfo->route(nft_net(pkt), (struct dst_entry **)&rt,
  87                                  flowi6_to_flowi(&fl6), false);
  88        if (route_err)
  89                goto err;
  90
  91        if (rt->rt6i_flags & RTF_REJECT) {
  92                route_err = rt->dst.error;
  93                dst_release(&rt->dst);
  94                goto err;
  95        }
  96
  97        if (ipv6_anycast_destination((struct dst_entry *)rt, &fl6.daddr))
  98                ret = RTN_ANYCAST;
  99        else if (!dev && rt->rt6i_flags & RTF_LOCAL)
 100                ret = RTN_LOCAL;
 101
 102        dst_release(&rt->dst);
 103
 104        if (ret)
 105                return ret;
 106
 107        addrtype = ipv6_addr_type(&fl6.daddr);
 108
 109        if (addrtype & IPV6_ADDR_MULTICAST)
 110                return RTN_MULTICAST;
 111        if (addrtype & IPV6_ADDR_UNICAST)
 112                return RTN_UNICAST;
 113
 114        return RTN_UNSPEC;
 115 err:
 116        switch (route_err) {
 117        case -EINVAL:
 118                return RTN_BLACKHOLE;
 119        case -EACCES:
 120                return RTN_PROHIBIT;
 121        case -EAGAIN:
 122                return RTN_THROW;
 123        default:
 124                break;
 125        }
 126
 127        return RTN_UNREACHABLE;
 128}
 129
 130void nft_fib6_eval_type(const struct nft_expr *expr, struct nft_regs *regs,
 131                        const struct nft_pktinfo *pkt)
 132{
 133        const struct nft_fib *priv = nft_expr_priv(expr);
 134        u32 *dest = &regs->data[priv->dreg];
 135
 136        *dest = __nft_fib6_eval_type(priv, pkt);
 137}
 138EXPORT_SYMBOL_GPL(nft_fib6_eval_type);
 139
 140void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs,
 141                   const struct nft_pktinfo *pkt)
 142{
 143        const struct nft_fib *priv = nft_expr_priv(expr);
 144        const struct net_device *oif = NULL;
 145        u32 *dest = &regs->data[priv->dreg];
 146        struct flowi6 fl6 = {
 147                .flowi6_iif = LOOPBACK_IFINDEX,
 148                .flowi6_proto = pkt->tprot,
 149        };
 150        struct rt6_info *rt;
 151        int lookup_flags;
 152
 153        if (priv->flags & NFTA_FIB_F_IIF)
 154                oif = nft_in(pkt);
 155        else if (priv->flags & NFTA_FIB_F_OIF)
 156                oif = nft_out(pkt);
 157
 158        lookup_flags = nft_fib6_flowi_init(&fl6, priv, pkt, oif);
 159
 160        if (nft_hook(pkt) == NF_INET_PRE_ROUTING &&
 161            nft_fib_is_loopback(pkt->skb, nft_in(pkt))) {
 162                nft_fib_store_result(dest, priv, pkt,
 163                                     nft_in(pkt)->ifindex);
 164                return;
 165        }
 166
 167        *dest = 0;
 168 again:
 169        rt = (void *)ip6_route_lookup(nft_net(pkt), &fl6, lookup_flags);
 170        if (rt->dst.error)
 171                goto put_rt_err;
 172
 173        /* Should not see RTF_LOCAL here */
 174        if (rt->rt6i_flags & (RTF_REJECT | RTF_ANYCAST | RTF_LOCAL))
 175                goto put_rt_err;
 176
 177        if (oif && oif != rt->rt6i_idev->dev) {
 178                /* multipath route? Try again with F_IFACE */
 179                if ((lookup_flags & RT6_LOOKUP_F_IFACE) == 0) {
 180                        lookup_flags |= RT6_LOOKUP_F_IFACE;
 181                        fl6.flowi6_oif = oif->ifindex;
 182                        ip6_rt_put(rt);
 183                        goto again;
 184                }
 185        }
 186
 187        switch (priv->result) {
 188        case NFT_FIB_RESULT_OIF:
 189                *dest = rt->rt6i_idev->dev->ifindex;
 190                break;
 191        case NFT_FIB_RESULT_OIFNAME:
 192                strncpy((char *)dest, rt->rt6i_idev->dev->name, IFNAMSIZ);
 193                break;
 194        default:
 195                WARN_ON_ONCE(1);
 196                break;
 197        }
 198
 199 put_rt_err:
 200        ip6_rt_put(rt);
 201}
 202EXPORT_SYMBOL_GPL(nft_fib6_eval);
 203
 204static struct nft_expr_type nft_fib6_type;
 205
 206static const struct nft_expr_ops nft_fib6_type_ops = {
 207        .type           = &nft_fib6_type,
 208        .size           = NFT_EXPR_SIZE(sizeof(struct nft_fib)),
 209        .eval           = nft_fib6_eval_type,
 210        .init           = nft_fib_init,
 211        .dump           = nft_fib_dump,
 212        .validate       = nft_fib_validate,
 213};
 214
 215static const struct nft_expr_ops nft_fib6_ops = {
 216        .type           = &nft_fib6_type,
 217        .size           = NFT_EXPR_SIZE(sizeof(struct nft_fib)),
 218        .eval           = nft_fib6_eval,
 219        .init           = nft_fib_init,
 220        .dump           = nft_fib_dump,
 221        .validate       = nft_fib_validate,
 222};
 223
 224static const struct nft_expr_ops *
 225nft_fib6_select_ops(const struct nft_ctx *ctx,
 226                    const struct nlattr * const tb[])
 227{
 228        enum nft_fib_result result;
 229
 230        if (!tb[NFTA_FIB_RESULT])
 231                return ERR_PTR(-EINVAL);
 232
 233        result = ntohl(nla_get_be32(tb[NFTA_FIB_RESULT]));
 234
 235        switch (result) {
 236        case NFT_FIB_RESULT_OIF:
 237                return &nft_fib6_ops;
 238        case NFT_FIB_RESULT_OIFNAME:
 239                return &nft_fib6_ops;
 240        case NFT_FIB_RESULT_ADDRTYPE:
 241                return &nft_fib6_type_ops;
 242        default:
 243                return ERR_PTR(-EOPNOTSUPP);
 244        }
 245}
 246
 247static struct nft_expr_type nft_fib6_type __read_mostly = {
 248        .name           = "fib",
 249        .select_ops     = nft_fib6_select_ops,
 250        .policy         = nft_fib_policy,
 251        .maxattr        = NFTA_FIB_MAX,
 252        .family         = NFPROTO_IPV6,
 253        .owner          = THIS_MODULE,
 254};
 255
 256static int __init nft_fib6_module_init(void)
 257{
 258        return nft_register_expr(&nft_fib6_type);
 259}
 260
 261static void __exit nft_fib6_module_exit(void)
 262{
 263        nft_unregister_expr(&nft_fib6_type);
 264}
 265module_init(nft_fib6_module_init);
 266module_exit(nft_fib6_module_exit);
 267
 268MODULE_LICENSE("GPL");
 269MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
 270MODULE_ALIAS_NFT_AF_EXPR(10, "fib");
 271