1 // SPDX-License-Identifier: GPL-2.0-only
3 #include <linux/kernel.h>
4 #include <linux/init.h>
5 #include <linux/module.h>
6 #include <linux/netlink.h>
7 #include <linux/netfilter.h>
8 #include <linux/netfilter/nf_tables.h>
9 #include <linux/netfilter_ipv6.h>
10 #include <net/netfilter/nf_tables_core.h>
11 #include <net/netfilter/nf_tables.h>
12 #include <net/netfilter/nft_fib.h>
14 #include <net/ip6_fib.h>
15 #include <net/ip6_route.h>
17 static int get_ifindex(const struct net_device
*dev
)
19 return dev
? dev
->ifindex
: 0;
22 static int nft_fib6_flowi_init(struct flowi6
*fl6
, const struct nft_fib
*priv
,
23 const struct nft_pktinfo
*pkt
,
24 const struct net_device
*dev
,
29 if (priv
->flags
& NFTA_FIB_F_DADDR
) {
30 fl6
->daddr
= iph
->daddr
;
31 fl6
->saddr
= iph
->saddr
;
33 fl6
->daddr
= iph
->saddr
;
34 fl6
->saddr
= iph
->daddr
;
37 if (ipv6_addr_type(&fl6
->daddr
) & IPV6_ADDR_LINKLOCAL
) {
38 lookup_flags
|= RT6_LOOKUP_F_IFACE
;
39 fl6
->flowi6_oif
= get_ifindex(dev
? dev
: pkt
->skb
->dev
);
42 if (ipv6_addr_type(&fl6
->saddr
) & IPV6_ADDR_UNICAST
)
43 lookup_flags
|= RT6_LOOKUP_F_HAS_SADDR
;
45 if (priv
->flags
& NFTA_FIB_F_MARK
)
46 fl6
->flowi6_mark
= pkt
->skb
->mark
;
48 fl6
->flowlabel
= (*(__be32
*)iph
) & IPV6_FLOWINFO_MASK
;
53 static u32
__nft_fib6_eval_type(const struct nft_fib
*priv
,
54 const struct nft_pktinfo
*pkt
,
57 const struct net_device
*dev
= NULL
;
58 int route_err
, addrtype
;
61 .flowi6_iif
= LOOPBACK_IFINDEX
,
62 .flowi6_proto
= pkt
->tprot
,
66 if (priv
->flags
& NFTA_FIB_F_IIF
)
68 else if (priv
->flags
& NFTA_FIB_F_OIF
)
71 nft_fib6_flowi_init(&fl6
, priv
, pkt
, dev
, iph
);
73 if (dev
&& nf_ipv6_chk_addr(nft_net(pkt
), &fl6
.daddr
, dev
, true))
76 route_err
= nf_ip6_route(nft_net(pkt
), (struct dst_entry
**)&rt
,
77 flowi6_to_flowi(&fl6
), false);
81 if (rt
->rt6i_flags
& RTF_REJECT
) {
82 route_err
= rt
->dst
.error
;
83 dst_release(&rt
->dst
);
87 if (ipv6_anycast_destination((struct dst_entry
*)rt
, &fl6
.daddr
))
89 else if (!dev
&& rt
->rt6i_flags
& RTF_LOCAL
)
92 dst_release(&rt
->dst
);
97 addrtype
= ipv6_addr_type(&fl6
.daddr
);
99 if (addrtype
& IPV6_ADDR_MULTICAST
)
100 return RTN_MULTICAST
;
101 if (addrtype
& IPV6_ADDR_UNICAST
)
108 return RTN_BLACKHOLE
;
117 return RTN_UNREACHABLE
;
120 void nft_fib6_eval_type(const struct nft_expr
*expr
, struct nft_regs
*regs
,
121 const struct nft_pktinfo
*pkt
)
123 const struct nft_fib
*priv
= nft_expr_priv(expr
);
124 int noff
= skb_network_offset(pkt
->skb
);
125 u32
*dest
= ®s
->data
[priv
->dreg
];
126 struct ipv6hdr
*iph
, _iph
;
128 iph
= skb_header_pointer(pkt
->skb
, noff
, sizeof(_iph
), &_iph
);
130 regs
->verdict
.code
= NFT_BREAK
;
134 *dest
= __nft_fib6_eval_type(priv
, pkt
, iph
);
136 EXPORT_SYMBOL_GPL(nft_fib6_eval_type
);
138 void nft_fib6_eval(const struct nft_expr
*expr
, struct nft_regs
*regs
,
139 const struct nft_pktinfo
*pkt
)
141 const struct nft_fib
*priv
= nft_expr_priv(expr
);
142 int noff
= skb_network_offset(pkt
->skb
);
143 const struct net_device
*oif
= NULL
;
144 u32
*dest
= ®s
->data
[priv
->dreg
];
145 struct ipv6hdr
*iph
, _iph
;
146 struct flowi6 fl6
= {
147 .flowi6_iif
= LOOPBACK_IFINDEX
,
148 .flowi6_proto
= pkt
->tprot
,
153 if (priv
->flags
& NFTA_FIB_F_IIF
)
155 else if (priv
->flags
& NFTA_FIB_F_OIF
)
158 iph
= skb_header_pointer(pkt
->skb
, noff
, sizeof(_iph
), &_iph
);
160 regs
->verdict
.code
= NFT_BREAK
;
164 lookup_flags
= nft_fib6_flowi_init(&fl6
, priv
, pkt
, oif
, iph
);
166 if (nft_hook(pkt
) == NF_INET_PRE_ROUTING
&&
167 nft_fib_is_loopback(pkt
->skb
, nft_in(pkt
))) {
168 nft_fib_store_result(dest
, priv
, nft_in(pkt
));
173 rt
= (void *)ip6_route_lookup(nft_net(pkt
), &fl6
, pkt
->skb
,
178 /* Should not see RTF_LOCAL here */
179 if (rt
->rt6i_flags
& (RTF_REJECT
| RTF_ANYCAST
| RTF_LOCAL
))
182 if (oif
&& oif
!= rt
->rt6i_idev
->dev
)
185 nft_fib_store_result(dest
, priv
, rt
->rt6i_idev
->dev
);
189 EXPORT_SYMBOL_GPL(nft_fib6_eval
);
191 static struct nft_expr_type nft_fib6_type
;
193 static const struct nft_expr_ops nft_fib6_type_ops
= {
194 .type
= &nft_fib6_type
,
195 .size
= NFT_EXPR_SIZE(sizeof(struct nft_fib
)),
196 .eval
= nft_fib6_eval_type
,
197 .init
= nft_fib_init
,
198 .dump
= nft_fib_dump
,
199 .validate
= nft_fib_validate
,
202 static const struct nft_expr_ops nft_fib6_ops
= {
203 .type
= &nft_fib6_type
,
204 .size
= NFT_EXPR_SIZE(sizeof(struct nft_fib
)),
205 .eval
= nft_fib6_eval
,
206 .init
= nft_fib_init
,
207 .dump
= nft_fib_dump
,
208 .validate
= nft_fib_validate
,
211 static const struct nft_expr_ops
*
212 nft_fib6_select_ops(const struct nft_ctx
*ctx
,
213 const struct nlattr
* const tb
[])
215 enum nft_fib_result result
;
217 if (!tb
[NFTA_FIB_RESULT
])
218 return ERR_PTR(-EINVAL
);
220 result
= ntohl(nla_get_be32(tb
[NFTA_FIB_RESULT
]));
223 case NFT_FIB_RESULT_OIF
:
224 return &nft_fib6_ops
;
225 case NFT_FIB_RESULT_OIFNAME
:
226 return &nft_fib6_ops
;
227 case NFT_FIB_RESULT_ADDRTYPE
:
228 return &nft_fib6_type_ops
;
230 return ERR_PTR(-EOPNOTSUPP
);
234 static struct nft_expr_type nft_fib6_type __read_mostly
= {
236 .select_ops
= nft_fib6_select_ops
,
237 .policy
= nft_fib_policy
,
238 .maxattr
= NFTA_FIB_MAX
,
239 .family
= NFPROTO_IPV6
,
240 .owner
= THIS_MODULE
,
243 static int __init
nft_fib6_module_init(void)
245 return nft_register_expr(&nft_fib6_type
);
248 static void __exit
nft_fib6_module_exit(void)
250 nft_unregister_expr(&nft_fib6_type
);
252 module_init(nft_fib6_module_init
);
253 module_exit(nft_fib6_module_exit
);
255 MODULE_LICENSE("GPL");
256 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
257 MODULE_ALIAS_NFT_AF_EXPR(10, "fib");