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_dup_netdev.h>
16 #include <net/neighbour.h>
19 struct nft_fwd_netdev
{
20 enum nft_registers sreg_dev
:8;
23 static void nft_fwd_netdev_eval(const struct nft_expr
*expr
,
24 struct nft_regs
*regs
,
25 const struct nft_pktinfo
*pkt
)
27 struct nft_fwd_netdev
*priv
= nft_expr_priv(expr
);
28 int oif
= regs
->data
[priv
->sreg_dev
];
30 nf_fwd_netdev_egress(pkt
, oif
);
31 regs
->verdict
.code
= NF_STOLEN
;
34 static const struct nla_policy nft_fwd_netdev_policy
[NFTA_FWD_MAX
+ 1] = {
35 [NFTA_FWD_SREG_DEV
] = { .type
= NLA_U32
},
36 [NFTA_FWD_SREG_ADDR
] = { .type
= NLA_U32
},
37 [NFTA_FWD_NFPROTO
] = { .type
= NLA_U32
},
40 static int nft_fwd_netdev_init(const struct nft_ctx
*ctx
,
41 const struct nft_expr
*expr
,
42 const struct nlattr
* const tb
[])
44 struct nft_fwd_netdev
*priv
= nft_expr_priv(expr
);
46 if (tb
[NFTA_FWD_SREG_DEV
] == NULL
)
49 priv
->sreg_dev
= nft_parse_register(tb
[NFTA_FWD_SREG_DEV
]);
50 return nft_validate_register_load(priv
->sreg_dev
, sizeof(int));
53 static int nft_fwd_netdev_dump(struct sk_buff
*skb
, const struct nft_expr
*expr
)
55 struct nft_fwd_netdev
*priv
= nft_expr_priv(expr
);
57 if (nft_dump_register(skb
, NFTA_FWD_SREG_DEV
, priv
->sreg_dev
))
66 struct nft_fwd_neigh
{
67 enum nft_registers sreg_dev
:8;
68 enum nft_registers sreg_addr
:8;
72 static void nft_fwd_neigh_eval(const struct nft_expr
*expr
,
73 struct nft_regs
*regs
,
74 const struct nft_pktinfo
*pkt
)
76 struct nft_fwd_neigh
*priv
= nft_expr_priv(expr
);
77 void *addr
= ®s
->data
[priv
->sreg_addr
];
78 int oif
= regs
->data
[priv
->sreg_dev
];
79 unsigned int verdict
= NF_STOLEN
;
80 struct sk_buff
*skb
= pkt
->skb
;
81 struct net_device
*dev
;
84 switch (priv
->nfproto
) {
88 if (skb
->protocol
!= htons(ETH_P_IP
)) {
92 if (skb_try_make_writable(skb
, sizeof(*iph
))) {
98 neigh_table
= NEIGH_ARP_TABLE
;
102 struct ipv6hdr
*ip6h
;
104 if (skb
->protocol
!= htons(ETH_P_IPV6
)) {
108 if (skb_try_make_writable(skb
, sizeof(*ip6h
))) {
112 ip6h
= ipv6_hdr(skb
);
114 neigh_table
= NEIGH_ND_TABLE
;
122 dev
= dev_get_by_index_rcu(nft_net(pkt
), oif
);
127 neigh_xmit(neigh_table
, dev
, addr
, skb
);
129 regs
->verdict
.code
= verdict
;
132 static int nft_fwd_neigh_init(const struct nft_ctx
*ctx
,
133 const struct nft_expr
*expr
,
134 const struct nlattr
* const tb
[])
136 struct nft_fwd_neigh
*priv
= nft_expr_priv(expr
);
137 unsigned int addr_len
;
140 if (!tb
[NFTA_FWD_SREG_DEV
] ||
141 !tb
[NFTA_FWD_SREG_ADDR
] ||
142 !tb
[NFTA_FWD_NFPROTO
])
145 priv
->sreg_dev
= nft_parse_register(tb
[NFTA_FWD_SREG_DEV
]);
146 priv
->sreg_addr
= nft_parse_register(tb
[NFTA_FWD_SREG_ADDR
]);
147 priv
->nfproto
= ntohl(nla_get_be32(tb
[NFTA_FWD_NFPROTO
]));
149 switch (priv
->nfproto
) {
151 addr_len
= sizeof(struct in_addr
);
154 addr_len
= sizeof(struct in6_addr
);
160 err
= nft_validate_register_load(priv
->sreg_dev
, sizeof(int));
164 return nft_validate_register_load(priv
->sreg_addr
, addr_len
);
167 static int nft_fwd_neigh_dump(struct sk_buff
*skb
, const struct nft_expr
*expr
)
169 struct nft_fwd_neigh
*priv
= nft_expr_priv(expr
);
171 if (nft_dump_register(skb
, NFTA_FWD_SREG_DEV
, priv
->sreg_dev
) ||
172 nft_dump_register(skb
, NFTA_FWD_SREG_ADDR
, priv
->sreg_addr
) ||
173 nla_put_be32(skb
, NFTA_FWD_NFPROTO
, htonl(priv
->nfproto
)))
174 goto nla_put_failure
;
182 static struct nft_expr_type nft_fwd_netdev_type
;
183 static const struct nft_expr_ops nft_fwd_neigh_netdev_ops
= {
184 .type
= &nft_fwd_netdev_type
,
185 .size
= NFT_EXPR_SIZE(sizeof(struct nft_fwd_neigh
)),
186 .eval
= nft_fwd_neigh_eval
,
187 .init
= nft_fwd_neigh_init
,
188 .dump
= nft_fwd_neigh_dump
,
191 static const struct nft_expr_ops nft_fwd_netdev_ops
= {
192 .type
= &nft_fwd_netdev_type
,
193 .size
= NFT_EXPR_SIZE(sizeof(struct nft_fwd_netdev
)),
194 .eval
= nft_fwd_netdev_eval
,
195 .init
= nft_fwd_netdev_init
,
196 .dump
= nft_fwd_netdev_dump
,
199 static const struct nft_expr_ops
*
200 nft_fwd_select_ops(const struct nft_ctx
*ctx
,
201 const struct nlattr
* const tb
[])
203 if (tb
[NFTA_FWD_SREG_ADDR
])
204 return &nft_fwd_neigh_netdev_ops
;
205 if (tb
[NFTA_FWD_SREG_DEV
])
206 return &nft_fwd_netdev_ops
;
208 return ERR_PTR(-EOPNOTSUPP
);
211 static struct nft_expr_type nft_fwd_netdev_type __read_mostly
= {
212 .family
= NFPROTO_NETDEV
,
214 .select_ops
= nft_fwd_select_ops
,
215 .policy
= nft_fwd_netdev_policy
,
216 .maxattr
= NFTA_FWD_MAX
,
217 .owner
= THIS_MODULE
,
220 static int __init
nft_fwd_netdev_module_init(void)
222 return nft_register_expr(&nft_fwd_netdev_type
);
225 static void __exit
nft_fwd_netdev_module_exit(void)
227 nft_unregister_expr(&nft_fwd_netdev_type
);
230 module_init(nft_fwd_netdev_module_init
);
231 module_exit(nft_fwd_netdev_module_exit
);
233 MODULE_LICENSE("GPL");
234 MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
235 MODULE_ALIAS_NFT_AF_EXPR(5, "fwd");