root/net/ipv6/netfilter/nft_fib_ipv6.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_ifindex
  2. nft_fib6_flowi_init
  3. __nft_fib6_eval_type
  4. nft_fib6_eval_type
  5. nft_fib6_eval
  6. nft_fib6_select_ops
  7. nft_fib6_module_init
  8. nft_fib6_module_exit

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

/* [<][>][^][v][top][bottom][index][help] */