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                               struct ipv6hdr *iph)
  30{
  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                                struct ipv6hdr *iph)
  60{
  61        const struct net_device *dev = NULL;
  62        const struct nf_ipv6_ops *v6ops;
  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        v6ops = nf_get_ipv6_ops();
  72        if (!v6ops)
  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, iph);
  81
  82        if (dev && v6ops->chk_addr(nft_net(pkt), &fl6.daddr, dev, true))
  83                ret = RTN_LOCAL;
  84
  85        route_err = v6ops->route(nft_net(pkt), (struct dst_entry **)&rt,
  86                                 flowi6_to_flowi(&fl6), false);
  87        if (route_err)
  88                goto err;
  89
  90        if (rt->rt6i_flags & RTF_REJECT) {
  91                route_err = rt->dst.error;
  92                dst_release(&rt->dst);
  93                goto err;
  94        }
  95
  96        if (ipv6_anycast_destination((struct dst_entry *)rt, &fl6.daddr))
  97                ret = RTN_ANYCAST;
  98        else if (!dev && rt->rt6i_flags & RTF_LOCAL)
  99                ret = RTN_LOCAL;
 100
 101        dst_release(&rt->dst);
 102
 103        if (ret)
 104                return ret;
 105
 106        addrtype = ipv6_addr_type(&fl6.daddr);
 107
 108        if (addrtype & IPV6_ADDR_MULTICAST)
 109                return RTN_MULTICAST;
 110        if (addrtype & IPV6_ADDR_UNICAST)
 111                return RTN_UNICAST;
 112
 113        return RTN_UNSPEC;
 114 err:
 115        switch (route_err) {
 116        case -EINVAL:
 117                return RTN_BLACKHOLE;
 118        case -EACCES:
 119                return RTN_PROHIBIT;
 120        case -EAGAIN:
 121                return RTN_THROW;
 122        default:
 123                break;
 124        }
 125
 126        return RTN_UNREACHABLE;
 127}
 128
 129void nft_fib6_eval_type(const struct nft_expr *expr, struct nft_regs *regs,
 130                        const struct nft_pktinfo *pkt)
 131{
 132        const struct nft_fib *priv = nft_expr_priv(expr);
 133        int noff = skb_network_offset(pkt->skb);
 134        u32 *dest = &regs->data[priv->dreg];
 135        struct ipv6hdr *iph, _iph;
 136
 137        iph = skb_header_pointer(pkt->skb, noff, sizeof(_iph), &_iph);
 138        if (!iph) {
 139                regs->verdict.code = NFT_BREAK;
 140                return;
 141        }
 142
 143        *dest = __nft_fib6_eval_type(priv, pkt, iph);
 144}
 145EXPORT_SYMBOL_GPL(nft_fib6_eval_type);
 146
 147void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs,
 148                   const struct nft_pktinfo *pkt)
 149{
 150        const struct nft_fib *priv = nft_expr_priv(expr);
 151        int noff = skb_network_offset(pkt->skb);
 152        const struct net_device *oif = NULL;
 153        u32 *dest = &regs->data[priv->dreg];
 154        struct ipv6hdr *iph, _iph;
 155        struct flowi6 fl6 = {
 156                .flowi6_iif = LOOPBACK_IFINDEX,
 157                .flowi6_proto = pkt->tprot,
 158        };
 159        struct rt6_info *rt;
 160        int lookup_flags;
 161
 162        if (priv->flags & NFTA_FIB_F_IIF)
 163                oif = nft_in(pkt);
 164        else if (priv->flags & NFTA_FIB_F_OIF)
 165                oif = nft_out(pkt);
 166
 167        iph = skb_header_pointer(pkt->skb, noff, sizeof(_iph), &_iph);
 168        if (!iph) {
 169                regs->verdict.code = NFT_BREAK;
 170                return;
 171        }
 172
 173        lookup_flags = nft_fib6_flowi_init(&fl6, priv, pkt, oif, iph);
 174
 175        if (nft_hook(pkt) == NF_INET_PRE_ROUTING &&
 176            nft_fib_is_loopback(pkt->skb, nft_in(pkt))) {
 177                nft_fib_store_result(dest, priv, pkt,
 178                                     nft_in(pkt)->ifindex);
 179                return;
 180        }
 181
 182        *dest = 0;
 183        rt = (void *)ip6_route_lookup(nft_net(pkt), &fl6, pkt->skb,
 184                                      lookup_flags);
 185        if (rt->dst.error)
 186                goto put_rt_err;
 187
 188        /* Should not see RTF_LOCAL here */
 189        if (rt->rt6i_flags & (RTF_REJECT | RTF_ANYCAST | RTF_LOCAL))
 190                goto put_rt_err;
 191
 192        if (oif && oif != rt->rt6i_idev->dev)
 193                goto put_rt_err;
 194
 195        switch (priv->result) {
 196        case NFT_FIB_RESULT_OIF:
 197                *dest = rt->rt6i_idev->dev->ifindex;
 198                break;
 199        case NFT_FIB_RESULT_OIFNAME:
 200                strncpy((char *)dest, rt->rt6i_idev->dev->name, IFNAMSIZ);
 201                break;
 202        default:
 203                WARN_ON_ONCE(1);
 204                break;
 205        }
 206
 207 put_rt_err:
 208        ip6_rt_put(rt);
 209}
 210EXPORT_SYMBOL_GPL(nft_fib6_eval);
 211
 212static struct nft_expr_type nft_fib6_type;
 213
 214static const struct nft_expr_ops nft_fib6_type_ops = {
 215        .type           = &nft_fib6_type,
 216        .size           = NFT_EXPR_SIZE(sizeof(struct nft_fib)),
 217        .eval           = nft_fib6_eval_type,
 218        .init           = nft_fib_init,
 219        .dump           = nft_fib_dump,
 220        .validate       = nft_fib_validate,
 221};
 222
 223static const struct nft_expr_ops nft_fib6_ops = {
 224        .type           = &nft_fib6_type,
 225        .size           = NFT_EXPR_SIZE(sizeof(struct nft_fib)),
 226        .eval           = nft_fib6_eval,
 227        .init           = nft_fib_init,
 228        .dump           = nft_fib_dump,
 229        .validate       = nft_fib_validate,
 230};
 231
 232static const struct nft_expr_ops *
 233nft_fib6_select_ops(const struct nft_ctx *ctx,
 234                    const struct nlattr * const tb[])
 235{
 236        enum nft_fib_result result;
 237
 238        if (!tb[NFTA_FIB_RESULT])
 239                return ERR_PTR(-EINVAL);
 240
 241        result = ntohl(nla_get_be32(tb[NFTA_FIB_RESULT]));
 242
 243        switch (result) {
 244        case NFT_FIB_RESULT_OIF:
 245                return &nft_fib6_ops;
 246        case NFT_FIB_RESULT_OIFNAME:
 247                return &nft_fib6_ops;
 248        case NFT_FIB_RESULT_ADDRTYPE:
 249                return &nft_fib6_type_ops;
 250        default:
 251                return ERR_PTR(-EOPNOTSUPP);
 252        }
 253}
 254
 255static struct nft_expr_type nft_fib6_type __read_mostly = {
 256        .name           = "fib",
 257        .select_ops     = nft_fib6_select_ops,
 258        .policy         = nft_fib_policy,
 259        .maxattr        = NFTA_FIB_MAX,
 260        .family         = NFPROTO_IPV6,
 261        .owner          = THIS_MODULE,
 262};
 263
 264static int __init nft_fib6_module_init(void)
 265{
 266        return nft_register_expr(&nft_fib6_type);
 267}
 268
 269static void __exit nft_fib6_module_exit(void)
 270{
 271        nft_unregister_expr(&nft_fib6_type);
 272}
 273module_init(nft_fib6_module_init);
 274module_exit(nft_fib6_module_exit);
 275
 276MODULE_LICENSE("GPL");
 277MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
 278MODULE_ALIAS_NFT_AF_EXPR(10, "fib");
 279