1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2015 Pablo Neira Ayuso <pablo@netfilter.org>
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>
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>
20 struct nft_fwd_netdev
{
21 enum nft_registers sreg_dev
:8;
24 static void nft_fwd_netdev_eval(const struct nft_expr
*expr
,
25 struct nft_regs
*regs
,
26 const struct nft_pktinfo
*pkt
)
28 struct nft_fwd_netdev
*priv
= nft_expr_priv(expr
);
29 int oif
= regs
->data
[priv
->sreg_dev
];
31 /* This is used by ifb only. */
32 skb_set_redirected(pkt
->skb
, true);
34 nf_fwd_netdev_egress(pkt
, oif
);
35 regs
->verdict
.code
= NF_STOLEN
;
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
},
44 static int nft_fwd_netdev_init(const struct nft_ctx
*ctx
,
45 const struct nft_expr
*expr
,
46 const struct nlattr
* const tb
[])
48 struct nft_fwd_netdev
*priv
= nft_expr_priv(expr
);
50 if (tb
[NFTA_FWD_SREG_DEV
] == NULL
)
53 priv
->sreg_dev
= nft_parse_register(tb
[NFTA_FWD_SREG_DEV
]);
54 return nft_validate_register_load(priv
->sreg_dev
, sizeof(int));
57 static int nft_fwd_netdev_dump(struct sk_buff
*skb
, const struct nft_expr
*expr
)
59 struct nft_fwd_netdev
*priv
= nft_expr_priv(expr
);
61 if (nft_dump_register(skb
, NFTA_FWD_SREG_DEV
, priv
->sreg_dev
))
70 static int nft_fwd_netdev_offload(struct nft_offload_ctx
*ctx
,
71 struct nft_flow_rule
*flow
,
72 const struct nft_expr
*expr
)
74 const struct nft_fwd_netdev
*priv
= nft_expr_priv(expr
);
75 int oif
= ctx
->regs
[priv
->sreg_dev
].data
.data
[0];
77 return nft_fwd_dup_netdev_offload(ctx
, flow
, FLOW_ACTION_REDIRECT
, oif
);
80 struct nft_fwd_neigh
{
81 enum nft_registers sreg_dev
:8;
82 enum nft_registers sreg_addr
:8;
86 static void nft_fwd_neigh_eval(const struct nft_expr
*expr
,
87 struct nft_regs
*regs
,
88 const struct nft_pktinfo
*pkt
)
90 struct nft_fwd_neigh
*priv
= nft_expr_priv(expr
);
91 void *addr
= ®s
->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
;
98 switch (priv
->nfproto
) {
102 if (skb
->protocol
!= htons(ETH_P_IP
)) {
106 if (skb_try_make_writable(skb
, sizeof(*iph
))) {
111 ip_decrease_ttl(iph
);
112 neigh_table
= NEIGH_ARP_TABLE
;
116 struct ipv6hdr
*ip6h
;
118 if (skb
->protocol
!= htons(ETH_P_IPV6
)) {
122 if (skb_try_make_writable(skb
, sizeof(*ip6h
))) {
126 ip6h
= ipv6_hdr(skb
);
128 neigh_table
= NEIGH_ND_TABLE
;
136 dev
= dev_get_by_index_rcu(nft_net(pkt
), oif
);
142 neigh_xmit(neigh_table
, dev
, addr
, skb
);
144 regs
->verdict
.code
= verdict
;
147 static int nft_fwd_neigh_init(const struct nft_ctx
*ctx
,
148 const struct nft_expr
*expr
,
149 const struct nlattr
* const tb
[])
151 struct nft_fwd_neigh
*priv
= nft_expr_priv(expr
);
152 unsigned int addr_len
;
155 if (!tb
[NFTA_FWD_SREG_DEV
] ||
156 !tb
[NFTA_FWD_SREG_ADDR
] ||
157 !tb
[NFTA_FWD_NFPROTO
])
160 priv
->sreg_dev
= nft_parse_register(tb
[NFTA_FWD_SREG_DEV
]);
161 priv
->sreg_addr
= nft_parse_register(tb
[NFTA_FWD_SREG_ADDR
]);
162 priv
->nfproto
= ntohl(nla_get_be32(tb
[NFTA_FWD_NFPROTO
]));
164 switch (priv
->nfproto
) {
166 addr_len
= sizeof(struct in_addr
);
169 addr_len
= sizeof(struct in6_addr
);
175 err
= nft_validate_register_load(priv
->sreg_dev
, sizeof(int));
179 return nft_validate_register_load(priv
->sreg_addr
, addr_len
);
182 static int nft_fwd_neigh_dump(struct sk_buff
*skb
, const struct nft_expr
*expr
)
184 struct nft_fwd_neigh
*priv
= nft_expr_priv(expr
);
186 if (nft_dump_register(skb
, NFTA_FWD_SREG_DEV
, priv
->sreg_dev
) ||
187 nft_dump_register(skb
, NFTA_FWD_SREG_ADDR
, priv
->sreg_addr
) ||
188 nla_put_be32(skb
, NFTA_FWD_NFPROTO
, htonl(priv
->nfproto
)))
189 goto nla_put_failure
;
197 static int nft_fwd_validate(const struct nft_ctx
*ctx
,
198 const struct nft_expr
*expr
,
199 const struct nft_data
**data
)
201 return nft_chain_validate_hooks(ctx
->chain
, (1 << NF_NETDEV_INGRESS
));
204 static struct nft_expr_type nft_fwd_netdev_type
;
205 static const struct nft_expr_ops nft_fwd_neigh_netdev_ops
= {
206 .type
= &nft_fwd_netdev_type
,
207 .size
= NFT_EXPR_SIZE(sizeof(struct nft_fwd_neigh
)),
208 .eval
= nft_fwd_neigh_eval
,
209 .init
= nft_fwd_neigh_init
,
210 .dump
= nft_fwd_neigh_dump
,
211 .validate
= nft_fwd_validate
,
214 static const struct nft_expr_ops nft_fwd_netdev_ops
= {
215 .type
= &nft_fwd_netdev_type
,
216 .size
= NFT_EXPR_SIZE(sizeof(struct nft_fwd_netdev
)),
217 .eval
= nft_fwd_netdev_eval
,
218 .init
= nft_fwd_netdev_init
,
219 .dump
= nft_fwd_netdev_dump
,
220 .validate
= nft_fwd_validate
,
221 .offload
= nft_fwd_netdev_offload
,
224 static const struct nft_expr_ops
*
225 nft_fwd_select_ops(const struct nft_ctx
*ctx
,
226 const struct nlattr
* const tb
[])
228 if (tb
[NFTA_FWD_SREG_ADDR
])
229 return &nft_fwd_neigh_netdev_ops
;
230 if (tb
[NFTA_FWD_SREG_DEV
])
231 return &nft_fwd_netdev_ops
;
233 return ERR_PTR(-EOPNOTSUPP
);
236 static struct nft_expr_type nft_fwd_netdev_type __read_mostly
= {
237 .family
= NFPROTO_NETDEV
,
239 .select_ops
= nft_fwd_select_ops
,
240 .policy
= nft_fwd_netdev_policy
,
241 .maxattr
= NFTA_FWD_MAX
,
242 .owner
= THIS_MODULE
,
245 static int __init
nft_fwd_netdev_module_init(void)
247 return nft_register_expr(&nft_fwd_netdev_type
);
250 static void __exit
nft_fwd_netdev_module_exit(void)
252 nft_unregister_expr(&nft_fwd_netdev_type
);
255 module_init(nft_fwd_netdev_module_init
);
256 module_exit(nft_fwd_netdev_module_exit
);
258 MODULE_LICENSE("GPL");
259 MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
260 MODULE_ALIAS_NFT_AF_EXPR(5, "fwd");