linux/net/ipv4/netfilter/nft_fib_ipv4.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 <net/netfilter/nf_tables_core.h>
  14#include <net/netfilter/nf_tables.h>
  15#include <net/netfilter/nft_fib.h>
  16
  17#include <net/ip_fib.h>
  18#include <net/route.h>
  19
  20/* don't try to find route from mcast/bcast/zeronet */
  21static __be32 get_saddr(__be32 addr)
  22{
  23        if (ipv4_is_multicast(addr) || ipv4_is_lbcast(addr) ||
  24            ipv4_is_zeronet(addr))
  25                return 0;
  26        return addr;
  27}
  28
  29#define DSCP_BITS     0xfc
  30
  31void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs,
  32                        const struct nft_pktinfo *pkt)
  33{
  34        const struct nft_fib *priv = nft_expr_priv(expr);
  35        int noff = skb_network_offset(pkt->skb);
  36        u32 *dst = &regs->data[priv->dreg];
  37        const struct net_device *dev = NULL;
  38        struct iphdr *iph, _iph;
  39        __be32 addr;
  40
  41        if (priv->flags & NFTA_FIB_F_IIF)
  42                dev = nft_in(pkt);
  43        else if (priv->flags & NFTA_FIB_F_OIF)
  44                dev = nft_out(pkt);
  45
  46        iph = skb_header_pointer(pkt->skb, noff, sizeof(_iph), &_iph);
  47        if (!iph) {
  48                regs->verdict.code = NFT_BREAK;
  49                return;
  50        }
  51
  52        if (priv->flags & NFTA_FIB_F_DADDR)
  53                addr = iph->daddr;
  54        else
  55                addr = iph->saddr;
  56
  57        *dst = inet_dev_addr_type(nft_net(pkt), dev, addr);
  58}
  59EXPORT_SYMBOL_GPL(nft_fib4_eval_type);
  60
  61static int get_ifindex(const struct net_device *dev)
  62{
  63        return dev ? dev->ifindex : 0;
  64}
  65
  66void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs,
  67                   const struct nft_pktinfo *pkt)
  68{
  69        const struct nft_fib *priv = nft_expr_priv(expr);
  70        int noff = skb_network_offset(pkt->skb);
  71        u32 *dest = &regs->data[priv->dreg];
  72        struct iphdr *iph, _iph;
  73        struct fib_result res;
  74        struct flowi4 fl4 = {
  75                .flowi4_scope = RT_SCOPE_UNIVERSE,
  76                .flowi4_iif = LOOPBACK_IFINDEX,
  77        };
  78        const struct net_device *oif;
  79        struct net_device *found;
  80#ifdef CONFIG_IP_ROUTE_MULTIPATH
  81        int i;
  82#endif
  83
  84        /*
  85         * Do not set flowi4_oif, it restricts results (for example, asking
  86         * for oif 3 will get RTN_UNICAST result even if the daddr exits
  87         * on another interface.
  88         *
  89         * Search results for the desired outinterface instead.
  90         */
  91        if (priv->flags & NFTA_FIB_F_OIF)
  92                oif = nft_out(pkt);
  93        else if (priv->flags & NFTA_FIB_F_IIF)
  94                oif = nft_in(pkt);
  95        else
  96                oif = NULL;
  97
  98        if (nft_hook(pkt) == NF_INET_PRE_ROUTING &&
  99            nft_fib_is_loopback(pkt->skb, nft_in(pkt))) {
 100                nft_fib_store_result(dest, priv, pkt,
 101                                     nft_in(pkt)->ifindex);
 102                return;
 103        }
 104
 105        iph = skb_header_pointer(pkt->skb, noff, sizeof(_iph), &_iph);
 106        if (!iph) {
 107                regs->verdict.code = NFT_BREAK;
 108                return;
 109        }
 110
 111        if (ipv4_is_zeronet(iph->saddr)) {
 112                if (ipv4_is_lbcast(iph->daddr) ||
 113                    ipv4_is_local_multicast(iph->daddr)) {
 114                        nft_fib_store_result(dest, priv, pkt,
 115                                             get_ifindex(pkt->skb->dev));
 116                        return;
 117                }
 118        }
 119
 120        if (priv->flags & NFTA_FIB_F_MARK)
 121                fl4.flowi4_mark = pkt->skb->mark;
 122
 123        fl4.flowi4_tos = iph->tos & DSCP_BITS;
 124
 125        if (priv->flags & NFTA_FIB_F_DADDR) {
 126                fl4.daddr = iph->daddr;
 127                fl4.saddr = get_saddr(iph->saddr);
 128        } else {
 129                fl4.daddr = iph->saddr;
 130                fl4.saddr = get_saddr(iph->daddr);
 131        }
 132
 133        *dest = 0;
 134
 135        if (fib_lookup(nft_net(pkt), &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE))
 136                return;
 137
 138        switch (res.type) {
 139        case RTN_UNICAST:
 140                break;
 141        case RTN_LOCAL: /* Should not see RTN_LOCAL here */
 142                return;
 143        default:
 144                break;
 145        }
 146
 147       if (!oif) {
 148               found = FIB_RES_DEV(res);
 149               goto ok;
 150       }
 151
 152#ifdef CONFIG_IP_ROUTE_MULTIPATH
 153        for (i = 0; i < res.fi->fib_nhs; i++) {
 154                struct fib_nh *nh = &res.fi->fib_nh[i];
 155
 156                if (nh->nh_dev == oif) {
 157                        found = nh->nh_dev;
 158                        goto ok;
 159                }
 160        }
 161        return;
 162#else
 163        found = FIB_RES_DEV(res);
 164        if (found != oif)
 165                return;
 166#endif
 167ok:
 168        switch (priv->result) {
 169        case NFT_FIB_RESULT_OIF:
 170                *dest = found->ifindex;
 171                break;
 172        case NFT_FIB_RESULT_OIFNAME:
 173                strncpy((char *)dest, found->name, IFNAMSIZ);
 174                break;
 175        default:
 176                WARN_ON_ONCE(1);
 177                break;
 178        }
 179}
 180EXPORT_SYMBOL_GPL(nft_fib4_eval);
 181
 182static struct nft_expr_type nft_fib4_type;
 183
 184static const struct nft_expr_ops nft_fib4_type_ops = {
 185        .type           = &nft_fib4_type,
 186        .size           = NFT_EXPR_SIZE(sizeof(struct nft_fib)),
 187        .eval           = nft_fib4_eval_type,
 188        .init           = nft_fib_init,
 189        .dump           = nft_fib_dump,
 190        .validate       = nft_fib_validate,
 191};
 192
 193static const struct nft_expr_ops nft_fib4_ops = {
 194        .type           = &nft_fib4_type,
 195        .size           = NFT_EXPR_SIZE(sizeof(struct nft_fib)),
 196        .eval           = nft_fib4_eval,
 197        .init           = nft_fib_init,
 198        .dump           = nft_fib_dump,
 199        .validate       = nft_fib_validate,
 200};
 201
 202static const struct nft_expr_ops *
 203nft_fib4_select_ops(const struct nft_ctx *ctx,
 204                    const struct nlattr * const tb[])
 205{
 206        enum nft_fib_result result;
 207
 208        if (!tb[NFTA_FIB_RESULT])
 209                return ERR_PTR(-EINVAL);
 210
 211        result = ntohl(nla_get_be32(tb[NFTA_FIB_RESULT]));
 212
 213        switch (result) {
 214        case NFT_FIB_RESULT_OIF:
 215                return &nft_fib4_ops;
 216        case NFT_FIB_RESULT_OIFNAME:
 217                return &nft_fib4_ops;
 218        case NFT_FIB_RESULT_ADDRTYPE:
 219                return &nft_fib4_type_ops;
 220        default:
 221                return ERR_PTR(-EOPNOTSUPP);
 222        }
 223}
 224
 225static struct nft_expr_type nft_fib4_type __read_mostly = {
 226        .name           = "fib",
 227        .select_ops     = nft_fib4_select_ops,
 228        .policy         = nft_fib_policy,
 229        .maxattr        = NFTA_FIB_MAX,
 230        .family         = NFPROTO_IPV4,
 231        .owner          = THIS_MODULE,
 232};
 233
 234static int __init nft_fib4_module_init(void)
 235{
 236        return nft_register_expr(&nft_fib4_type);
 237}
 238
 239static void __exit nft_fib4_module_exit(void)
 240{
 241        nft_unregister_expr(&nft_fib4_type);
 242}
 243
 244module_init(nft_fib4_module_init);
 245module_exit(nft_fib4_module_exit);
 246MODULE_LICENSE("GPL");
 247MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
 248MODULE_ALIAS_NFT_AF_EXPR(2, "fib");
 249