root/net/netfilter/nft_fwd_netdev.c

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

DEFINITIONS

This source file includes following definitions.
  1. nft_fwd_netdev_eval
  2. nft_fwd_netdev_init
  3. nft_fwd_netdev_dump
  4. nft_fwd_netdev_offload
  5. nft_fwd_neigh_eval
  6. nft_fwd_neigh_init
  7. nft_fwd_neigh_dump
  8. nft_fwd_validate
  9. nft_fwd_select_ops
  10. nft_fwd_netdev_module_init
  11. nft_fwd_netdev_module_exit

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright (c) 2015 Pablo Neira Ayuso <pablo@netfilter.org>
   4  */
   5 
   6 #include <linux/kernel.h>
   7 #include <linux/init.h>
   8 #include <linux/module.h>
   9 #include <linux/netlink.h>
  10 #include <linux/netfilter.h>
  11 #include <linux/netfilter/nf_tables.h>
  12 #include <linux/ip.h>
  13 #include <linux/ipv6.h>
  14 #include <net/netfilter/nf_tables.h>
  15 #include <net/netfilter/nf_tables_offload.h>
  16 #include <net/netfilter/nf_dup_netdev.h>
  17 #include <net/neighbour.h>
  18 #include <net/ip.h>
  19 
  20 struct nft_fwd_netdev {
  21         enum nft_registers      sreg_dev:8;
  22 };
  23 
  24 static void nft_fwd_netdev_eval(const struct nft_expr *expr,
  25                                 struct nft_regs *regs,
  26                                 const struct nft_pktinfo *pkt)
  27 {
  28         struct nft_fwd_netdev *priv = nft_expr_priv(expr);
  29         int oif = regs->data[priv->sreg_dev];
  30 
  31         /* This is used by ifb only. */
  32         skb_set_redirected(pkt->skb, true);
  33 
  34         nf_fwd_netdev_egress(pkt, oif);
  35         regs->verdict.code = NF_STOLEN;
  36 }
  37 
  38 static const struct nla_policy nft_fwd_netdev_policy[NFTA_FWD_MAX + 1] = {
  39         [NFTA_FWD_SREG_DEV]     = { .type = NLA_U32 },
  40         [NFTA_FWD_SREG_ADDR]    = { .type = NLA_U32 },
  41         [NFTA_FWD_NFPROTO]      = { .type = NLA_U32 },
  42 };
  43 
  44 static int nft_fwd_netdev_init(const struct nft_ctx *ctx,
  45                                const struct nft_expr *expr,
  46                                const struct nlattr * const tb[])
  47 {
  48         struct nft_fwd_netdev *priv = nft_expr_priv(expr);
  49 
  50         if (tb[NFTA_FWD_SREG_DEV] == NULL)
  51                 return -EINVAL;
  52 
  53         priv->sreg_dev = nft_parse_register(tb[NFTA_FWD_SREG_DEV]);
  54         return nft_validate_register_load(priv->sreg_dev, sizeof(int));
  55 }
  56 
  57 static int nft_fwd_netdev_dump(struct sk_buff *skb, const struct nft_expr *expr)
  58 {
  59         struct nft_fwd_netdev *priv = nft_expr_priv(expr);
  60 
  61         if (nft_dump_register(skb, NFTA_FWD_SREG_DEV, priv->sreg_dev))
  62                 goto nla_put_failure;
  63 
  64         return 0;
  65 
  66 nla_put_failure:
  67         return -1;
  68 }
  69 
  70 static int nft_fwd_netdev_offload(struct nft_offload_ctx *ctx,
  71                                   struct nft_flow_rule *flow,
  72                                   const struct nft_expr *expr)
  73 {
  74         const struct nft_fwd_netdev *priv = nft_expr_priv(expr);
  75         int oif = ctx->regs[priv->sreg_dev].data.data[0];
  76 
  77         return nft_fwd_dup_netdev_offload(ctx, flow, FLOW_ACTION_REDIRECT, oif);
  78 }
  79 
  80 struct nft_fwd_neigh {
  81         enum nft_registers      sreg_dev:8;
  82         enum nft_registers      sreg_addr:8;
  83         u8                      nfproto;
  84 };
  85 
  86 static void nft_fwd_neigh_eval(const struct nft_expr *expr,
  87                               struct nft_regs *regs,
  88                               const struct nft_pktinfo *pkt)
  89 {
  90         struct nft_fwd_neigh *priv = nft_expr_priv(expr);
  91         void *addr = &regs->data[priv->sreg_addr];
  92         int oif = regs->data[priv->sreg_dev];
  93         unsigned int verdict = NF_STOLEN;
  94         struct sk_buff *skb = pkt->skb;
  95         struct net_device *dev;
  96         int neigh_table;
  97 
  98         switch (priv->nfproto) {
  99         case NFPROTO_IPV4: {
 100                 struct iphdr *iph;
 101 
 102                 if (skb->protocol != htons(ETH_P_IP)) {
 103                         verdict = NFT_BREAK;
 104                         goto out;
 105                 }
 106                 if (skb_try_make_writable(skb, sizeof(*iph))) {
 107                         verdict = NF_DROP;
 108                         goto out;
 109                 }
 110                 iph = ip_hdr(skb);
 111                 ip_decrease_ttl(iph);
 112                 neigh_table = NEIGH_ARP_TABLE;
 113                 break;
 114                 }
 115         case NFPROTO_IPV6: {
 116                 struct ipv6hdr *ip6h;
 117 
 118                 if (skb->protocol != htons(ETH_P_IPV6)) {
 119                         verdict = NFT_BREAK;
 120                         goto out;
 121                 }
 122                 if (skb_try_make_writable(skb, sizeof(*ip6h))) {
 123                         verdict = NF_DROP;
 124                         goto out;
 125                 }
 126                 ip6h = ipv6_hdr(skb);
 127                 ip6h->hop_limit--;
 128                 neigh_table = NEIGH_ND_TABLE;
 129                 break;
 130                 }
 131         default:
 132                 verdict = NFT_BREAK;
 133                 goto out;
 134         }
 135 
 136         dev = dev_get_by_index_rcu(nft_net(pkt), oif);
 137         if (dev == NULL)
 138                 return;
 139 
 140         skb->dev = dev;
 141         neigh_xmit(neigh_table, dev, addr, skb);
 142 out:
 143         regs->verdict.code = verdict;
 144 }
 145 
 146 static int nft_fwd_neigh_init(const struct nft_ctx *ctx,
 147                               const struct nft_expr *expr,
 148                               const struct nlattr * const tb[])
 149 {
 150         struct nft_fwd_neigh *priv = nft_expr_priv(expr);
 151         unsigned int addr_len;
 152         int err;
 153 
 154         if (!tb[NFTA_FWD_SREG_DEV] ||
 155             !tb[NFTA_FWD_SREG_ADDR] ||
 156             !tb[NFTA_FWD_NFPROTO])
 157                 return -EINVAL;
 158 
 159         priv->sreg_dev = nft_parse_register(tb[NFTA_FWD_SREG_DEV]);
 160         priv->sreg_addr = nft_parse_register(tb[NFTA_FWD_SREG_ADDR]);
 161         priv->nfproto = ntohl(nla_get_be32(tb[NFTA_FWD_NFPROTO]));
 162 
 163         switch (priv->nfproto) {
 164         case NFPROTO_IPV4:
 165                 addr_len = sizeof(struct in_addr);
 166                 break;
 167         case NFPROTO_IPV6:
 168                 addr_len = sizeof(struct in6_addr);
 169                 break;
 170         default:
 171                 return -EOPNOTSUPP;
 172         }
 173 
 174         err = nft_validate_register_load(priv->sreg_dev, sizeof(int));
 175         if (err < 0)
 176                 return err;
 177 
 178         return nft_validate_register_load(priv->sreg_addr, addr_len);
 179 }
 180 
 181 static int nft_fwd_neigh_dump(struct sk_buff *skb, const struct nft_expr *expr)
 182 {
 183         struct nft_fwd_neigh *priv = nft_expr_priv(expr);
 184 
 185         if (nft_dump_register(skb, NFTA_FWD_SREG_DEV, priv->sreg_dev) ||
 186             nft_dump_register(skb, NFTA_FWD_SREG_ADDR, priv->sreg_addr) ||
 187             nla_put_be32(skb, NFTA_FWD_NFPROTO, htonl(priv->nfproto)))
 188                 goto nla_put_failure;
 189 
 190         return 0;
 191 
 192 nla_put_failure:
 193         return -1;
 194 }
 195 
 196 static int nft_fwd_validate(const struct nft_ctx *ctx,
 197                             const struct nft_expr *expr,
 198                             const struct nft_data **data)
 199 {
 200         return nft_chain_validate_hooks(ctx->chain, (1 << NF_NETDEV_INGRESS));
 201 }
 202 
 203 static struct nft_expr_type nft_fwd_netdev_type;
 204 static const struct nft_expr_ops nft_fwd_neigh_netdev_ops = {
 205         .type           = &nft_fwd_netdev_type,
 206         .size           = NFT_EXPR_SIZE(sizeof(struct nft_fwd_neigh)),
 207         .eval           = nft_fwd_neigh_eval,
 208         .init           = nft_fwd_neigh_init,
 209         .dump           = nft_fwd_neigh_dump,
 210         .validate       = nft_fwd_validate,
 211 };
 212 
 213 static const struct nft_expr_ops nft_fwd_netdev_ops = {
 214         .type           = &nft_fwd_netdev_type,
 215         .size           = NFT_EXPR_SIZE(sizeof(struct nft_fwd_netdev)),
 216         .eval           = nft_fwd_netdev_eval,
 217         .init           = nft_fwd_netdev_init,
 218         .dump           = nft_fwd_netdev_dump,
 219         .validate       = nft_fwd_validate,
 220         .offload        = nft_fwd_netdev_offload,
 221 };
 222 
 223 static const struct nft_expr_ops *
 224 nft_fwd_select_ops(const struct nft_ctx *ctx,
 225                    const struct nlattr * const tb[])
 226 {
 227         if (tb[NFTA_FWD_SREG_ADDR])
 228                 return &nft_fwd_neigh_netdev_ops;
 229         if (tb[NFTA_FWD_SREG_DEV])
 230                 return &nft_fwd_netdev_ops;
 231 
 232         return ERR_PTR(-EOPNOTSUPP);
 233 }
 234 
 235 static struct nft_expr_type nft_fwd_netdev_type __read_mostly = {
 236         .family         = NFPROTO_NETDEV,
 237         .name           = "fwd",
 238         .select_ops     = nft_fwd_select_ops,
 239         .policy         = nft_fwd_netdev_policy,
 240         .maxattr        = NFTA_FWD_MAX,
 241         .owner          = THIS_MODULE,
 242 };
 243 
 244 static int __init nft_fwd_netdev_module_init(void)
 245 {
 246         return nft_register_expr(&nft_fwd_netdev_type);
 247 }
 248 
 249 static void __exit nft_fwd_netdev_module_exit(void)
 250 {
 251         nft_unregister_expr(&nft_fwd_netdev_type);
 252 }
 253 
 254 module_init(nft_fwd_netdev_module_init);
 255 module_exit(nft_fwd_netdev_module_exit);
 256 
 257 MODULE_LICENSE("GPL");
 258 MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
 259 MODULE_ALIAS_NFT_AF_EXPR(5, "fwd");

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