2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License version 2 as
4 * published by the Free Software Foundation.
7 #include <linux/kernel.h>
8 #include <linux/init.h>
9 #include <linux/module.h>
10 #include <linux/netlink.h>
11 #include <linux/netfilter.h>
12 #include <linux/netfilter/nf_tables.h>
13 #include <linux/netfilter_ipv6.h>
14 #include <net/netfilter/nf_tables_core.h>
15 #include <net/netfilter/nf_tables.h>
16 #include <net/netfilter/nft_fib.h>
18 #include <net/ip6_fib.h>
19 #include <net/ip6_route.h>
21 static int get_ifindex(const struct net_device
*dev
)
23 return dev
? dev
->ifindex
: 0;
26 static int nft_fib6_flowi_init(struct flowi6
*fl6
, const struct nft_fib
*priv
,
27 const struct nft_pktinfo
*pkt
,
28 const struct net_device
*dev
,
33 if (priv
->flags
& NFTA_FIB_F_DADDR
) {
34 fl6
->daddr
= iph
->daddr
;
35 fl6
->saddr
= iph
->saddr
;
37 fl6
->daddr
= iph
->saddr
;
38 fl6
->saddr
= iph
->daddr
;
41 if (ipv6_addr_type(&fl6
->daddr
) & IPV6_ADDR_LINKLOCAL
) {
42 lookup_flags
|= RT6_LOOKUP_F_IFACE
;
43 fl6
->flowi6_oif
= get_ifindex(dev
? dev
: pkt
->skb
->dev
);
46 if (ipv6_addr_type(&fl6
->saddr
) & IPV6_ADDR_UNICAST
)
47 lookup_flags
|= RT6_LOOKUP_F_HAS_SADDR
;
49 if (priv
->flags
& NFTA_FIB_F_MARK
)
50 fl6
->flowi6_mark
= pkt
->skb
->mark
;
52 fl6
->flowlabel
= (*(__be32
*)iph
) & IPV6_FLOWINFO_MASK
;
57 static u32
__nft_fib6_eval_type(const struct nft_fib
*priv
,
58 const struct nft_pktinfo
*pkt
,
61 const struct net_device
*dev
= NULL
;
62 int route_err
, addrtype
;
65 .flowi6_iif
= LOOPBACK_IFINDEX
,
66 .flowi6_proto
= pkt
->tprot
,
70 if (priv
->flags
& NFTA_FIB_F_IIF
)
72 else if (priv
->flags
& NFTA_FIB_F_OIF
)
75 nft_fib6_flowi_init(&fl6
, priv
, pkt
, dev
, iph
);
77 if (dev
&& nf_ipv6_chk_addr(nft_net(pkt
), &fl6
.daddr
, dev
, true))
80 route_err
= nf_ip6_route(nft_net(pkt
), (struct dst_entry
**)&rt
,
81 flowi6_to_flowi(&fl6
), false);
85 if (rt
->rt6i_flags
& RTF_REJECT
) {
86 route_err
= rt
->dst
.error
;
87 dst_release(&rt
->dst
);
91 if (ipv6_anycast_destination((struct dst_entry
*)rt
, &fl6
.daddr
))
93 else if (!dev
&& rt
->rt6i_flags
& RTF_LOCAL
)
96 dst_release(&rt
->dst
);
101 addrtype
= ipv6_addr_type(&fl6
.daddr
);
103 if (addrtype
& IPV6_ADDR_MULTICAST
)
104 return RTN_MULTICAST
;
105 if (addrtype
& IPV6_ADDR_UNICAST
)
112 return RTN_BLACKHOLE
;
121 return RTN_UNREACHABLE
;
124 void nft_fib6_eval_type(const struct nft_expr
*expr
, struct nft_regs
*regs
,
125 const struct nft_pktinfo
*pkt
)
127 const struct nft_fib
*priv
= nft_expr_priv(expr
);
128 int noff
= skb_network_offset(pkt
->skb
);
129 u32
*dest
= ®s
->data
[priv
->dreg
];
130 struct ipv6hdr
*iph
, _iph
;
132 iph
= skb_header_pointer(pkt
->skb
, noff
, sizeof(_iph
), &_iph
);
134 regs
->verdict
.code
= NFT_BREAK
;
138 *dest
= __nft_fib6_eval_type(priv
, pkt
, iph
);
140 EXPORT_SYMBOL_GPL(nft_fib6_eval_type
);
142 void nft_fib6_eval(const struct nft_expr
*expr
, struct nft_regs
*regs
,
143 const struct nft_pktinfo
*pkt
)
145 const struct nft_fib
*priv
= nft_expr_priv(expr
);
146 int noff
= skb_network_offset(pkt
->skb
);
147 const struct net_device
*oif
= NULL
;
148 u32
*dest
= ®s
->data
[priv
->dreg
];
149 struct ipv6hdr
*iph
, _iph
;
150 struct flowi6 fl6
= {
151 .flowi6_iif
= LOOPBACK_IFINDEX
,
152 .flowi6_proto
= pkt
->tprot
,
157 if (priv
->flags
& NFTA_FIB_F_IIF
)
159 else if (priv
->flags
& NFTA_FIB_F_OIF
)
162 iph
= skb_header_pointer(pkt
->skb
, noff
, sizeof(_iph
), &_iph
);
164 regs
->verdict
.code
= NFT_BREAK
;
168 lookup_flags
= nft_fib6_flowi_init(&fl6
, priv
, pkt
, oif
, iph
);
170 if (nft_hook(pkt
) == NF_INET_PRE_ROUTING
&&
171 nft_fib_is_loopback(pkt
->skb
, nft_in(pkt
))) {
172 nft_fib_store_result(dest
, priv
, nft_in(pkt
));
177 rt
= (void *)ip6_route_lookup(nft_net(pkt
), &fl6
, pkt
->skb
,
182 /* Should not see RTF_LOCAL here */
183 if (rt
->rt6i_flags
& (RTF_REJECT
| RTF_ANYCAST
| RTF_LOCAL
))
186 if (oif
&& oif
!= rt
->rt6i_idev
->dev
)
189 nft_fib_store_result(dest
, priv
, rt
->rt6i_idev
->dev
);
193 EXPORT_SYMBOL_GPL(nft_fib6_eval
);
195 static struct nft_expr_type nft_fib6_type
;
197 static const struct nft_expr_ops nft_fib6_type_ops
= {
198 .type
= &nft_fib6_type
,
199 .size
= NFT_EXPR_SIZE(sizeof(struct nft_fib
)),
200 .eval
= nft_fib6_eval_type
,
201 .init
= nft_fib_init
,
202 .dump
= nft_fib_dump
,
203 .validate
= nft_fib_validate
,
206 static const struct nft_expr_ops nft_fib6_ops
= {
207 .type
= &nft_fib6_type
,
208 .size
= NFT_EXPR_SIZE(sizeof(struct nft_fib
)),
209 .eval
= nft_fib6_eval
,
210 .init
= nft_fib_init
,
211 .dump
= nft_fib_dump
,
212 .validate
= nft_fib_validate
,
215 static const struct nft_expr_ops
*
216 nft_fib6_select_ops(const struct nft_ctx
*ctx
,
217 const struct nlattr
* const tb
[])
219 enum nft_fib_result result
;
221 if (!tb
[NFTA_FIB_RESULT
])
222 return ERR_PTR(-EINVAL
);
224 result
= ntohl(nla_get_be32(tb
[NFTA_FIB_RESULT
]));
227 case NFT_FIB_RESULT_OIF
:
228 return &nft_fib6_ops
;
229 case NFT_FIB_RESULT_OIFNAME
:
230 return &nft_fib6_ops
;
231 case NFT_FIB_RESULT_ADDRTYPE
:
232 return &nft_fib6_type_ops
;
234 return ERR_PTR(-EOPNOTSUPP
);
238 static struct nft_expr_type nft_fib6_type __read_mostly
= {
240 .select_ops
= nft_fib6_select_ops
,
241 .policy
= nft_fib_policy
,
242 .maxattr
= NFTA_FIB_MAX
,
243 .family
= NFPROTO_IPV6
,
244 .owner
= THIS_MODULE
,
247 static int __init
nft_fib6_module_init(void)
249 return nft_register_expr(&nft_fib6_type
);
252 static void __exit
nft_fib6_module_exit(void)
254 nft_unregister_expr(&nft_fib6_type
);
256 module_init(nft_fib6_module_init
);
257 module_exit(nft_fib6_module_exit
);
259 MODULE_LICENSE("GPL");
260 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
261 MODULE_ALIAS_NFT_AF_EXPR(10, "fib");