0001
0002
0003
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
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 = ®s->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");