Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2015 Pablo Neira Ayuso <pablo@netfilter.org>
0004  */
0005 
0006 #include <linux/kernel.h>
0007 #include <linux/init.h>
0008 #include <linux/module.h>
0009 #include <linux/netlink.h>
0010 #include <linux/netfilter.h>
0011 #include <linux/netfilter/nf_tables.h>
0012 #include <linux/ip.h>
0013 #include <linux/ipv6.h>
0014 #include <net/netfilter/nf_tables.h>
0015 #include <net/netfilter/nf_tables_offload.h>
0016 #include <net/netfilter/nf_dup_netdev.h>
0017 #include <net/neighbour.h>
0018 #include <net/ip.h>
0019 
0020 struct nft_fwd_netdev {
0021     u8  sreg_dev;
0022 };
0023 
0024 static void nft_fwd_netdev_eval(const struct nft_expr *expr,
0025                 struct nft_regs *regs,
0026                 const struct nft_pktinfo *pkt)
0027 {
0028     struct nft_fwd_netdev *priv = nft_expr_priv(expr);
0029     int oif = regs->data[priv->sreg_dev];
0030     struct sk_buff *skb = pkt->skb;
0031 
0032     /* This is used by ifb only. */
0033     skb->skb_iif = skb->dev->ifindex;
0034     skb_set_redirected(skb, nft_hook(pkt) == NF_NETDEV_INGRESS);
0035 
0036     nf_fwd_netdev_egress(pkt, oif);
0037     regs->verdict.code = NF_STOLEN;
0038 }
0039 
0040 static const struct nla_policy nft_fwd_netdev_policy[NFTA_FWD_MAX + 1] = {
0041     [NFTA_FWD_SREG_DEV] = { .type = NLA_U32 },
0042     [NFTA_FWD_SREG_ADDR]    = { .type = NLA_U32 },
0043     [NFTA_FWD_NFPROTO]  = { .type = NLA_U32 },
0044 };
0045 
0046 static int nft_fwd_netdev_init(const struct nft_ctx *ctx,
0047                    const struct nft_expr *expr,
0048                    const struct nlattr * const tb[])
0049 {
0050     struct nft_fwd_netdev *priv = nft_expr_priv(expr);
0051 
0052     if (tb[NFTA_FWD_SREG_DEV] == NULL)
0053         return -EINVAL;
0054 
0055     return nft_parse_register_load(tb[NFTA_FWD_SREG_DEV], &priv->sreg_dev,
0056                        sizeof(int));
0057 }
0058 
0059 static int nft_fwd_netdev_dump(struct sk_buff *skb, const struct nft_expr *expr)
0060 {
0061     struct nft_fwd_netdev *priv = nft_expr_priv(expr);
0062 
0063     if (nft_dump_register(skb, NFTA_FWD_SREG_DEV, priv->sreg_dev))
0064         goto nla_put_failure;
0065 
0066     return 0;
0067 
0068 nla_put_failure:
0069     return -1;
0070 }
0071 
0072 static int nft_fwd_netdev_offload(struct nft_offload_ctx *ctx,
0073                   struct nft_flow_rule *flow,
0074                   const struct nft_expr *expr)
0075 {
0076     const struct nft_fwd_netdev *priv = nft_expr_priv(expr);
0077     int oif = ctx->regs[priv->sreg_dev].data.data[0];
0078 
0079     return nft_fwd_dup_netdev_offload(ctx, flow, FLOW_ACTION_REDIRECT, oif);
0080 }
0081 
0082 static bool nft_fwd_netdev_offload_action(const struct nft_expr *expr)
0083 {
0084     return true;
0085 }
0086 
0087 struct nft_fwd_neigh {
0088     u8          sreg_dev;
0089     u8          sreg_addr;
0090     u8          nfproto;
0091 };
0092 
0093 static void nft_fwd_neigh_eval(const struct nft_expr *expr,
0094                   struct nft_regs *regs,
0095                   const struct nft_pktinfo *pkt)
0096 {
0097     struct nft_fwd_neigh *priv = nft_expr_priv(expr);
0098     void *addr = &regs->data[priv->sreg_addr];
0099     int oif = regs->data[priv->sreg_dev];
0100     unsigned int verdict = NF_STOLEN;
0101     struct sk_buff *skb = pkt->skb;
0102     struct net_device *dev;
0103     int neigh_table;
0104 
0105     switch (priv->nfproto) {
0106     case NFPROTO_IPV4: {
0107         struct iphdr *iph;
0108 
0109         if (skb->protocol != htons(ETH_P_IP)) {
0110             verdict = NFT_BREAK;
0111             goto out;
0112         }
0113         if (skb_try_make_writable(skb, sizeof(*iph))) {
0114             verdict = NF_DROP;
0115             goto out;
0116         }
0117         iph = ip_hdr(skb);
0118         ip_decrease_ttl(iph);
0119         neigh_table = NEIGH_ARP_TABLE;
0120         break;
0121         }
0122     case NFPROTO_IPV6: {
0123         struct ipv6hdr *ip6h;
0124 
0125         if (skb->protocol != htons(ETH_P_IPV6)) {
0126             verdict = NFT_BREAK;
0127             goto out;
0128         }
0129         if (skb_try_make_writable(skb, sizeof(*ip6h))) {
0130             verdict = NF_DROP;
0131             goto out;
0132         }
0133         ip6h = ipv6_hdr(skb);
0134         ip6h->hop_limit--;
0135         neigh_table = NEIGH_ND_TABLE;
0136         break;
0137         }
0138     default:
0139         verdict = NFT_BREAK;
0140         goto out;
0141     }
0142 
0143     dev = dev_get_by_index_rcu(nft_net(pkt), oif);
0144     if (dev == NULL)
0145         return;
0146 
0147     skb->dev = dev;
0148     skb_clear_tstamp(skb);
0149     neigh_xmit(neigh_table, dev, addr, skb);
0150 out:
0151     regs->verdict.code = verdict;
0152 }
0153 
0154 static int nft_fwd_neigh_init(const struct nft_ctx *ctx,
0155                   const struct nft_expr *expr,
0156                   const struct nlattr * const tb[])
0157 {
0158     struct nft_fwd_neigh *priv = nft_expr_priv(expr);
0159     unsigned int addr_len;
0160     int err;
0161 
0162     if (!tb[NFTA_FWD_SREG_DEV] ||
0163         !tb[NFTA_FWD_SREG_ADDR] ||
0164         !tb[NFTA_FWD_NFPROTO])
0165         return -EINVAL;
0166 
0167     priv->nfproto = ntohl(nla_get_be32(tb[NFTA_FWD_NFPROTO]));
0168 
0169     switch (priv->nfproto) {
0170     case NFPROTO_IPV4:
0171         addr_len = sizeof(struct in_addr);
0172         break;
0173     case NFPROTO_IPV6:
0174         addr_len = sizeof(struct in6_addr);
0175         break;
0176     default:
0177         return -EOPNOTSUPP;
0178     }
0179 
0180     err = nft_parse_register_load(tb[NFTA_FWD_SREG_DEV], &priv->sreg_dev,
0181                       sizeof(int));
0182     if (err < 0)
0183         return err;
0184 
0185     return nft_parse_register_load(tb[NFTA_FWD_SREG_ADDR], &priv->sreg_addr,
0186                        addr_len);
0187 }
0188 
0189 static int nft_fwd_neigh_dump(struct sk_buff *skb, const struct nft_expr *expr)
0190 {
0191     struct nft_fwd_neigh *priv = nft_expr_priv(expr);
0192 
0193     if (nft_dump_register(skb, NFTA_FWD_SREG_DEV, priv->sreg_dev) ||
0194         nft_dump_register(skb, NFTA_FWD_SREG_ADDR, priv->sreg_addr) ||
0195         nla_put_be32(skb, NFTA_FWD_NFPROTO, htonl(priv->nfproto)))
0196         goto nla_put_failure;
0197 
0198     return 0;
0199 
0200 nla_put_failure:
0201     return -1;
0202 }
0203 
0204 static int nft_fwd_validate(const struct nft_ctx *ctx,
0205                 const struct nft_expr *expr,
0206                 const struct nft_data **data)
0207 {
0208     return nft_chain_validate_hooks(ctx->chain, (1 << NF_NETDEV_INGRESS) |
0209                             (1 << NF_NETDEV_EGRESS));
0210 }
0211 
0212 static struct nft_expr_type nft_fwd_netdev_type;
0213 static const struct nft_expr_ops nft_fwd_neigh_netdev_ops = {
0214     .type       = &nft_fwd_netdev_type,
0215     .size       = NFT_EXPR_SIZE(sizeof(struct nft_fwd_neigh)),
0216     .eval       = nft_fwd_neigh_eval,
0217     .init       = nft_fwd_neigh_init,
0218     .dump       = nft_fwd_neigh_dump,
0219     .validate   = nft_fwd_validate,
0220     .reduce     = NFT_REDUCE_READONLY,
0221 };
0222 
0223 static const struct nft_expr_ops nft_fwd_netdev_ops = {
0224     .type       = &nft_fwd_netdev_type,
0225     .size       = NFT_EXPR_SIZE(sizeof(struct nft_fwd_netdev)),
0226     .eval       = nft_fwd_netdev_eval,
0227     .init       = nft_fwd_netdev_init,
0228     .dump       = nft_fwd_netdev_dump,
0229     .validate   = nft_fwd_validate,
0230     .reduce     = NFT_REDUCE_READONLY,
0231     .offload    = nft_fwd_netdev_offload,
0232     .offload_action = nft_fwd_netdev_offload_action,
0233 };
0234 
0235 static const struct nft_expr_ops *
0236 nft_fwd_select_ops(const struct nft_ctx *ctx,
0237            const struct nlattr * const tb[])
0238 {
0239     if (tb[NFTA_FWD_SREG_ADDR])
0240         return &nft_fwd_neigh_netdev_ops;
0241     if (tb[NFTA_FWD_SREG_DEV])
0242         return &nft_fwd_netdev_ops;
0243 
0244         return ERR_PTR(-EOPNOTSUPP);
0245 }
0246 
0247 static struct nft_expr_type nft_fwd_netdev_type __read_mostly = {
0248     .family     = NFPROTO_NETDEV,
0249     .name       = "fwd",
0250     .select_ops = nft_fwd_select_ops,
0251     .policy     = nft_fwd_netdev_policy,
0252     .maxattr    = NFTA_FWD_MAX,
0253     .owner      = THIS_MODULE,
0254 };
0255 
0256 static int __init nft_fwd_netdev_module_init(void)
0257 {
0258     return nft_register_expr(&nft_fwd_netdev_type);
0259 }
0260 
0261 static void __exit nft_fwd_netdev_module_exit(void)
0262 {
0263     nft_unregister_expr(&nft_fwd_netdev_type);
0264 }
0265 
0266 module_init(nft_fwd_netdev_module_init);
0267 module_exit(nft_fwd_netdev_module_exit);
0268 
0269 MODULE_LICENSE("GPL");
0270 MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
0271 MODULE_ALIAS_NFT_AF_EXPR(5, "fwd");