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 bool fib6_is_local(const struct sk_buff
*skb
)
23 const struct rt6_info
*rt
= (const void *)skb_dst(skb
);
25 return rt
&& (rt
->rt6i_flags
& RTF_LOCAL
);
28 static int get_ifindex(const struct net_device
*dev
)
30 return dev
? dev
->ifindex
: 0;
33 static int nft_fib6_flowi_init(struct flowi6
*fl6
, const struct nft_fib
*priv
,
34 const struct nft_pktinfo
*pkt
,
35 const struct net_device
*dev
)
37 const struct ipv6hdr
*iph
= ipv6_hdr(pkt
->skb
);
40 if (priv
->flags
& NFTA_FIB_F_DADDR
) {
41 fl6
->daddr
= iph
->daddr
;
42 fl6
->saddr
= iph
->saddr
;
44 fl6
->daddr
= iph
->saddr
;
45 fl6
->saddr
= iph
->daddr
;
48 if (ipv6_addr_type(&fl6
->daddr
) & IPV6_ADDR_LINKLOCAL
) {
49 lookup_flags
|= RT6_LOOKUP_F_IFACE
;
50 fl6
->flowi6_oif
= get_ifindex(dev
? dev
: pkt
->skb
->dev
);
53 if (ipv6_addr_type(&fl6
->saddr
) & IPV6_ADDR_UNICAST
)
54 lookup_flags
|= RT6_LOOKUP_F_HAS_SADDR
;
56 if (priv
->flags
& NFTA_FIB_F_MARK
)
57 fl6
->flowi6_mark
= pkt
->skb
->mark
;
59 fl6
->flowlabel
= (*(__be32
*)iph
) & IPV6_FLOWINFO_MASK
;
64 static u32
__nft_fib6_eval_type(const struct nft_fib
*priv
,
65 const struct nft_pktinfo
*pkt
)
67 const struct net_device
*dev
= NULL
;
68 const struct nf_ipv6_ops
*v6ops
;
69 const struct nf_afinfo
*afinfo
;
70 int route_err
, addrtype
;
73 .flowi6_iif
= LOOPBACK_IFINDEX
,
74 .flowi6_proto
= pkt
->tprot
,
78 afinfo
= nf_get_afinfo(NFPROTO_IPV6
);
80 return RTN_UNREACHABLE
;
82 if (priv
->flags
& NFTA_FIB_F_IIF
)
84 else if (priv
->flags
& NFTA_FIB_F_OIF
)
87 nft_fib6_flowi_init(&fl6
, priv
, pkt
, dev
);
89 v6ops
= nf_get_ipv6_ops();
90 if (dev
&& v6ops
&& v6ops
->chk_addr(nft_net(pkt
), &fl6
.daddr
, dev
, true))
93 route_err
= afinfo
->route(nft_net(pkt
), (struct dst_entry
**)&rt
,
94 flowi6_to_flowi(&fl6
), false);
98 if (rt
->rt6i_flags
& RTF_REJECT
) {
99 route_err
= rt
->dst
.error
;
100 dst_release(&rt
->dst
);
104 if (ipv6_anycast_destination((struct dst_entry
*)rt
, &fl6
.daddr
))
106 else if (!dev
&& rt
->rt6i_flags
& RTF_LOCAL
)
109 dst_release(&rt
->dst
);
114 addrtype
= ipv6_addr_type(&fl6
.daddr
);
116 if (addrtype
& IPV6_ADDR_MULTICAST
)
117 return RTN_MULTICAST
;
118 if (addrtype
& IPV6_ADDR_UNICAST
)
125 return RTN_BLACKHOLE
;
134 return RTN_UNREACHABLE
;
137 void nft_fib6_eval_type(const struct nft_expr
*expr
, struct nft_regs
*regs
,
138 const struct nft_pktinfo
*pkt
)
140 const struct nft_fib
*priv
= nft_expr_priv(expr
);
141 u32
*dest
= ®s
->data
[priv
->dreg
];
143 *dest
= __nft_fib6_eval_type(priv
, pkt
);
145 EXPORT_SYMBOL_GPL(nft_fib6_eval_type
);
147 void nft_fib6_eval(const struct nft_expr
*expr
, struct nft_regs
*regs
,
148 const struct nft_pktinfo
*pkt
)
150 const struct nft_fib
*priv
= nft_expr_priv(expr
);
151 const struct net_device
*oif
= NULL
;
152 u32
*dest
= ®s
->data
[priv
->dreg
];
153 struct flowi6 fl6
= {
154 .flowi6_iif
= LOOPBACK_IFINDEX
,
155 .flowi6_proto
= pkt
->tprot
,
160 if (priv
->flags
& NFTA_FIB_F_IIF
)
162 else if (priv
->flags
& NFTA_FIB_F_OIF
)
165 lookup_flags
= nft_fib6_flowi_init(&fl6
, priv
, pkt
, oif
);
167 if (nft_hook(pkt
) == NF_INET_PRE_ROUTING
&& fib6_is_local(pkt
->skb
)) {
168 nft_fib_store_result(dest
, priv
->result
, pkt
, LOOPBACK_IFINDEX
);
174 rt
= (void *)ip6_route_lookup(nft_net(pkt
), &fl6
, lookup_flags
);
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
) {
183 /* multipath route? Try again with F_IFACE */
184 if ((lookup_flags
& RT6_LOOKUP_F_IFACE
) == 0) {
185 lookup_flags
|= RT6_LOOKUP_F_IFACE
;
186 fl6
.flowi6_oif
= oif
->ifindex
;
192 switch (priv
->result
) {
193 case NFT_FIB_RESULT_OIF
:
194 *dest
= rt
->rt6i_idev
->dev
->ifindex
;
196 case NFT_FIB_RESULT_OIFNAME
:
197 strncpy((char *)dest
, rt
->rt6i_idev
->dev
->name
, IFNAMSIZ
);
207 EXPORT_SYMBOL_GPL(nft_fib6_eval
);
209 static struct nft_expr_type nft_fib6_type
;
211 static const struct nft_expr_ops nft_fib6_type_ops
= {
212 .type
= &nft_fib6_type
,
213 .size
= NFT_EXPR_SIZE(sizeof(struct nft_fib
)),
214 .eval
= nft_fib6_eval_type
,
215 .init
= nft_fib_init
,
216 .dump
= nft_fib_dump
,
217 .validate
= nft_fib_validate
,
220 static const struct nft_expr_ops nft_fib6_ops
= {
221 .type
= &nft_fib6_type
,
222 .size
= NFT_EXPR_SIZE(sizeof(struct nft_fib
)),
223 .eval
= nft_fib6_eval
,
224 .init
= nft_fib_init
,
225 .dump
= nft_fib_dump
,
226 .validate
= nft_fib_validate
,
229 static const struct nft_expr_ops
*
230 nft_fib6_select_ops(const struct nft_ctx
*ctx
,
231 const struct nlattr
* const tb
[])
233 enum nft_fib_result result
;
235 if (!tb
[NFTA_FIB_RESULT
])
236 return ERR_PTR(-EINVAL
);
238 result
= ntohl(nla_get_be32(tb
[NFTA_FIB_RESULT
]));
241 case NFT_FIB_RESULT_OIF
:
242 return &nft_fib6_ops
;
243 case NFT_FIB_RESULT_OIFNAME
:
244 return &nft_fib6_ops
;
245 case NFT_FIB_RESULT_ADDRTYPE
:
246 return &nft_fib6_type_ops
;
248 return ERR_PTR(-EOPNOTSUPP
);
252 static struct nft_expr_type nft_fib6_type __read_mostly
= {
254 .select_ops
= &nft_fib6_select_ops
,
255 .policy
= nft_fib_policy
,
256 .maxattr
= NFTA_FIB_MAX
,
257 .family
= NFPROTO_IPV6
,
258 .owner
= THIS_MODULE
,
261 static int __init
nft_fib6_module_init(void)
263 return nft_register_expr(&nft_fib6_type
);
266 static void __exit
nft_fib6_module_exit(void)
268 nft_unregister_expr(&nft_fib6_type
);
270 module_init(nft_fib6_module_init
);
271 module_exit(nft_fib6_module_exit
);
273 MODULE_LICENSE("GPL");
274 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
275 MODULE_ALIAS_NFT_AF_EXPR(10, "fib");