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
{
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
];
30 struct sk_buff
*skb
= pkt
->skb
;
32 /* This is used by ifb only. */
33 skb
->skb_iif
= skb
->dev
->ifindex
;
34 skb_set_redirected(skb
, nft_hook(pkt
) == NF_NETDEV_INGRESS
);
36 nf_fwd_netdev_egress(pkt
, oif
);
37 regs
->verdict
.code
= NF_STOLEN
;
40 static const struct nla_policy nft_fwd_netdev_policy
[NFTA_FWD_MAX
+ 1] = {
41 [NFTA_FWD_SREG_DEV
] = { .type
= NLA_U32
},
42 [NFTA_FWD_SREG_ADDR
] = { .type
= NLA_U32
},
43 [NFTA_FWD_NFPROTO
] = NLA_POLICY_MAX(NLA_BE32
, 255),
46 static int nft_fwd_netdev_init(const struct nft_ctx
*ctx
,
47 const struct nft_expr
*expr
,
48 const struct nlattr
* const tb
[])
50 struct nft_fwd_netdev
*priv
= nft_expr_priv(expr
);
52 if (tb
[NFTA_FWD_SREG_DEV
] == NULL
)
55 return nft_parse_register_load(ctx
, tb
[NFTA_FWD_SREG_DEV
], &priv
->sreg_dev
,
59 static int nft_fwd_netdev_dump(struct sk_buff
*skb
,
60 const struct nft_expr
*expr
, bool reset
)
62 struct nft_fwd_netdev
*priv
= nft_expr_priv(expr
);
64 if (nft_dump_register(skb
, NFTA_FWD_SREG_DEV
, priv
->sreg_dev
))
73 static int nft_fwd_netdev_offload(struct nft_offload_ctx
*ctx
,
74 struct nft_flow_rule
*flow
,
75 const struct nft_expr
*expr
)
77 const struct nft_fwd_netdev
*priv
= nft_expr_priv(expr
);
78 int oif
= ctx
->regs
[priv
->sreg_dev
].data
.data
[0];
80 return nft_fwd_dup_netdev_offload(ctx
, flow
, FLOW_ACTION_REDIRECT
, oif
);
83 static bool nft_fwd_netdev_offload_action(const struct nft_expr
*expr
)
88 struct nft_fwd_neigh
{
94 static void nft_fwd_neigh_eval(const struct nft_expr
*expr
,
95 struct nft_regs
*regs
,
96 const struct nft_pktinfo
*pkt
)
98 struct nft_fwd_neigh
*priv
= nft_expr_priv(expr
);
99 void *addr
= ®s
->data
[priv
->sreg_addr
];
100 int oif
= regs
->data
[priv
->sreg_dev
];
101 unsigned int verdict
= NF_STOLEN
;
102 struct sk_buff
*skb
= pkt
->skb
;
103 struct net_device
*dev
;
106 switch (priv
->nfproto
) {
110 if (skb
->protocol
!= htons(ETH_P_IP
)) {
114 if (skb_try_make_writable(skb
, sizeof(*iph
))) {
119 ip_decrease_ttl(iph
);
120 neigh_table
= NEIGH_ARP_TABLE
;
124 struct ipv6hdr
*ip6h
;
126 if (skb
->protocol
!= htons(ETH_P_IPV6
)) {
130 if (skb_try_make_writable(skb
, sizeof(*ip6h
))) {
134 ip6h
= ipv6_hdr(skb
);
136 neigh_table
= NEIGH_ND_TABLE
;
144 dev
= dev_get_by_index_rcu(nft_net(pkt
), oif
);
149 skb_clear_tstamp(skb
);
150 neigh_xmit(neigh_table
, dev
, addr
, skb
);
152 regs
->verdict
.code
= verdict
;
155 static int nft_fwd_neigh_init(const struct nft_ctx
*ctx
,
156 const struct nft_expr
*expr
,
157 const struct nlattr
* const tb
[])
159 struct nft_fwd_neigh
*priv
= nft_expr_priv(expr
);
160 unsigned int addr_len
;
163 if (!tb
[NFTA_FWD_SREG_DEV
] ||
164 !tb
[NFTA_FWD_SREG_ADDR
] ||
165 !tb
[NFTA_FWD_NFPROTO
])
168 priv
->nfproto
= ntohl(nla_get_be32(tb
[NFTA_FWD_NFPROTO
]));
170 switch (priv
->nfproto
) {
172 addr_len
= sizeof(struct in_addr
);
175 addr_len
= sizeof(struct in6_addr
);
181 err
= nft_parse_register_load(ctx
, tb
[NFTA_FWD_SREG_DEV
], &priv
->sreg_dev
,
186 return nft_parse_register_load(ctx
, tb
[NFTA_FWD_SREG_ADDR
], &priv
->sreg_addr
,
190 static int nft_fwd_neigh_dump(struct sk_buff
*skb
,
191 const struct nft_expr
*expr
, bool reset
)
193 struct nft_fwd_neigh
*priv
= nft_expr_priv(expr
);
195 if (nft_dump_register(skb
, NFTA_FWD_SREG_DEV
, priv
->sreg_dev
) ||
196 nft_dump_register(skb
, NFTA_FWD_SREG_ADDR
, priv
->sreg_addr
) ||
197 nla_put_be32(skb
, NFTA_FWD_NFPROTO
, htonl(priv
->nfproto
)))
198 goto nla_put_failure
;
206 static int nft_fwd_validate(const struct nft_ctx
*ctx
,
207 const struct nft_expr
*expr
)
209 return nft_chain_validate_hooks(ctx
->chain
, (1 << NF_NETDEV_INGRESS
) |
210 (1 << NF_NETDEV_EGRESS
));
213 static struct nft_expr_type nft_fwd_netdev_type
;
214 static const struct nft_expr_ops nft_fwd_neigh_netdev_ops
= {
215 .type
= &nft_fwd_netdev_type
,
216 .size
= NFT_EXPR_SIZE(sizeof(struct nft_fwd_neigh
)),
217 .eval
= nft_fwd_neigh_eval
,
218 .init
= nft_fwd_neigh_init
,
219 .dump
= nft_fwd_neigh_dump
,
220 .validate
= nft_fwd_validate
,
221 .reduce
= NFT_REDUCE_READONLY
,
224 static const struct nft_expr_ops nft_fwd_netdev_ops
= {
225 .type
= &nft_fwd_netdev_type
,
226 .size
= NFT_EXPR_SIZE(sizeof(struct nft_fwd_netdev
)),
227 .eval
= nft_fwd_netdev_eval
,
228 .init
= nft_fwd_netdev_init
,
229 .dump
= nft_fwd_netdev_dump
,
230 .validate
= nft_fwd_validate
,
231 .reduce
= NFT_REDUCE_READONLY
,
232 .offload
= nft_fwd_netdev_offload
,
233 .offload_action
= nft_fwd_netdev_offload_action
,
236 static const struct nft_expr_ops
*
237 nft_fwd_select_ops(const struct nft_ctx
*ctx
,
238 const struct nlattr
* const tb
[])
240 if (tb
[NFTA_FWD_SREG_ADDR
])
241 return &nft_fwd_neigh_netdev_ops
;
242 if (tb
[NFTA_FWD_SREG_DEV
])
243 return &nft_fwd_netdev_ops
;
245 return ERR_PTR(-EOPNOTSUPP
);
248 static struct nft_expr_type nft_fwd_netdev_type __read_mostly
= {
249 .family
= NFPROTO_NETDEV
,
251 .select_ops
= nft_fwd_select_ops
,
252 .policy
= nft_fwd_netdev_policy
,
253 .maxattr
= NFTA_FWD_MAX
,
254 .owner
= THIS_MODULE
,
257 static int __init
nft_fwd_netdev_module_init(void)
259 return nft_register_expr(&nft_fwd_netdev_type
);
262 static void __exit
nft_fwd_netdev_module_exit(void)
264 nft_unregister_expr(&nft_fwd_netdev_type
);
267 module_init(nft_fwd_netdev_module_init
);
268 module_exit(nft_fwd_netdev_module_exit
);
270 MODULE_LICENSE("GPL");
271 MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
272 MODULE_DESCRIPTION("nftables netdev packet forwarding support");
273 MODULE_ALIAS_NFT_AF_EXPR(5, "fwd");