2 * Copyright (c) 2015 Pablo Neira Ayuso <pablo@netfilter.org>
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published by
6 * the Free Software Foundation.
9 #include <linux/kernel.h>
10 #include <linux/init.h>
11 #include <linux/module.h>
12 #include <linux/netlink.h>
13 #include <linux/netfilter.h>
14 #include <linux/netfilter/nf_tables.h>
16 #include <linux/ipv6.h>
17 #include <net/netfilter/nf_tables.h>
18 #include <net/netfilter/nf_dup_netdev.h>
19 #include <net/neighbour.h>
22 struct nft_fwd_netdev
{
23 enum nft_registers sreg_dev
:8;
26 static void nft_fwd_netdev_eval(const struct nft_expr
*expr
,
27 struct nft_regs
*regs
,
28 const struct nft_pktinfo
*pkt
)
30 struct nft_fwd_netdev
*priv
= nft_expr_priv(expr
);
31 int oif
= regs
->data
[priv
->sreg_dev
];
33 nf_fwd_netdev_egress(pkt
, oif
);
34 regs
->verdict
.code
= NF_STOLEN
;
37 static const struct nla_policy nft_fwd_netdev_policy
[NFTA_FWD_MAX
+ 1] = {
38 [NFTA_FWD_SREG_DEV
] = { .type
= NLA_U32
},
39 [NFTA_FWD_SREG_ADDR
] = { .type
= NLA_U32
},
40 [NFTA_FWD_NFPROTO
] = { .type
= NLA_U32
},
43 static int nft_fwd_netdev_init(const struct nft_ctx
*ctx
,
44 const struct nft_expr
*expr
,
45 const struct nlattr
* const tb
[])
47 struct nft_fwd_netdev
*priv
= nft_expr_priv(expr
);
49 if (tb
[NFTA_FWD_SREG_DEV
] == NULL
)
52 priv
->sreg_dev
= nft_parse_register(tb
[NFTA_FWD_SREG_DEV
]);
53 return nft_validate_register_load(priv
->sreg_dev
, sizeof(int));
56 static int nft_fwd_netdev_dump(struct sk_buff
*skb
, const struct nft_expr
*expr
)
58 struct nft_fwd_netdev
*priv
= nft_expr_priv(expr
);
60 if (nft_dump_register(skb
, NFTA_FWD_SREG_DEV
, priv
->sreg_dev
))
69 struct nft_fwd_neigh
{
70 enum nft_registers sreg_dev
:8;
71 enum nft_registers sreg_addr
:8;
75 static void nft_fwd_neigh_eval(const struct nft_expr
*expr
,
76 struct nft_regs
*regs
,
77 const struct nft_pktinfo
*pkt
)
79 struct nft_fwd_neigh
*priv
= nft_expr_priv(expr
);
80 void *addr
= ®s
->data
[priv
->sreg_addr
];
81 int oif
= regs
->data
[priv
->sreg_dev
];
82 unsigned int verdict
= NF_STOLEN
;
83 struct sk_buff
*skb
= pkt
->skb
;
84 struct net_device
*dev
;
87 switch (priv
->nfproto
) {
91 if (skb
->protocol
!= htons(ETH_P_IP
)) {
95 if (skb_try_make_writable(skb
, sizeof(*iph
))) {
100 ip_decrease_ttl(iph
);
101 neigh_table
= NEIGH_ARP_TABLE
;
105 struct ipv6hdr
*ip6h
;
107 if (skb
->protocol
!= htons(ETH_P_IPV6
)) {
111 if (skb_try_make_writable(skb
, sizeof(*ip6h
))) {
115 ip6h
= ipv6_hdr(skb
);
117 neigh_table
= NEIGH_ND_TABLE
;
125 dev
= dev_get_by_index_rcu(nft_net(pkt
), oif
);
130 neigh_xmit(neigh_table
, dev
, addr
, skb
);
132 regs
->verdict
.code
= verdict
;
135 static int nft_fwd_neigh_init(const struct nft_ctx
*ctx
,
136 const struct nft_expr
*expr
,
137 const struct nlattr
* const tb
[])
139 struct nft_fwd_neigh
*priv
= nft_expr_priv(expr
);
140 unsigned int addr_len
;
143 if (!tb
[NFTA_FWD_SREG_DEV
] ||
144 !tb
[NFTA_FWD_SREG_ADDR
] ||
145 !tb
[NFTA_FWD_NFPROTO
])
148 priv
->sreg_dev
= nft_parse_register(tb
[NFTA_FWD_SREG_DEV
]);
149 priv
->sreg_addr
= nft_parse_register(tb
[NFTA_FWD_SREG_ADDR
]);
150 priv
->nfproto
= ntohl(nla_get_be32(tb
[NFTA_FWD_NFPROTO
]));
152 switch (priv
->nfproto
) {
154 addr_len
= sizeof(struct in_addr
);
157 addr_len
= sizeof(struct in6_addr
);
163 err
= nft_validate_register_load(priv
->sreg_dev
, sizeof(int));
167 return nft_validate_register_load(priv
->sreg_addr
, addr_len
);
170 static int nft_fwd_neigh_dump(struct sk_buff
*skb
, const struct nft_expr
*expr
)
172 struct nft_fwd_neigh
*priv
= nft_expr_priv(expr
);
174 if (nft_dump_register(skb
, NFTA_FWD_SREG_DEV
, priv
->sreg_dev
) ||
175 nft_dump_register(skb
, NFTA_FWD_SREG_ADDR
, priv
->sreg_addr
) ||
176 nla_put_be32(skb
, NFTA_FWD_NFPROTO
, htonl(priv
->nfproto
)))
177 goto nla_put_failure
;
185 static struct nft_expr_type nft_fwd_netdev_type
;
186 static const struct nft_expr_ops nft_fwd_neigh_netdev_ops
= {
187 .type
= &nft_fwd_netdev_type
,
188 .size
= NFT_EXPR_SIZE(sizeof(struct nft_fwd_neigh
)),
189 .eval
= nft_fwd_neigh_eval
,
190 .init
= nft_fwd_neigh_init
,
191 .dump
= nft_fwd_neigh_dump
,
194 static const struct nft_expr_ops nft_fwd_netdev_ops
= {
195 .type
= &nft_fwd_netdev_type
,
196 .size
= NFT_EXPR_SIZE(sizeof(struct nft_fwd_netdev
)),
197 .eval
= nft_fwd_netdev_eval
,
198 .init
= nft_fwd_netdev_init
,
199 .dump
= nft_fwd_netdev_dump
,
202 static const struct nft_expr_ops
*
203 nft_fwd_select_ops(const struct nft_ctx
*ctx
,
204 const struct nlattr
* const tb
[])
206 if (tb
[NFTA_FWD_SREG_ADDR
])
207 return &nft_fwd_neigh_netdev_ops
;
208 if (tb
[NFTA_FWD_SREG_DEV
])
209 return &nft_fwd_netdev_ops
;
211 return ERR_PTR(-EOPNOTSUPP
);
214 static struct nft_expr_type nft_fwd_netdev_type __read_mostly
= {
215 .family
= NFPROTO_NETDEV
,
217 .select_ops
= nft_fwd_select_ops
,
218 .policy
= nft_fwd_netdev_policy
,
219 .maxattr
= NFTA_FWD_MAX
,
220 .owner
= THIS_MODULE
,
223 static int __init
nft_fwd_netdev_module_init(void)
225 return nft_register_expr(&nft_fwd_netdev_type
);
228 static void __exit
nft_fwd_netdev_module_exit(void)
230 nft_unregister_expr(&nft_fwd_netdev_type
);
233 module_init(nft_fwd_netdev_module_init
);
234 module_exit(nft_fwd_netdev_module_exit
);
236 MODULE_LICENSE("GPL");
237 MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
238 MODULE_ALIAS_NFT_AF_EXPR(5, "fwd");