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 <net/netfilter/nf_tables_core.h>
14 #include <net/netfilter/nf_tables.h>
15 #include <net/netfilter/nft_fib.h>
17 #include <net/ip_fib.h>
18 #include <net/route.h>
20 /* don't try to find route from mcast/bcast/zeronet */
21 static __be32
get_saddr(__be32 addr
)
23 if (ipv4_is_multicast(addr
) || ipv4_is_lbcast(addr
) ||
24 ipv4_is_zeronet(addr
))
29 #define DSCP_BITS 0xfc
31 void nft_fib4_eval_type(const struct nft_expr
*expr
, struct nft_regs
*regs
,
32 const struct nft_pktinfo
*pkt
)
34 const struct nft_fib
*priv
= nft_expr_priv(expr
);
35 int noff
= skb_network_offset(pkt
->skb
);
36 u32
*dst
= ®s
->data
[priv
->dreg
];
37 const struct net_device
*dev
= NULL
;
38 struct iphdr
*iph
, _iph
;
41 if (priv
->flags
& NFTA_FIB_F_IIF
)
43 else if (priv
->flags
& NFTA_FIB_F_OIF
)
46 iph
= skb_header_pointer(pkt
->skb
, noff
, sizeof(_iph
), &_iph
);
48 regs
->verdict
.code
= NFT_BREAK
;
52 if (priv
->flags
& NFTA_FIB_F_DADDR
)
57 *dst
= inet_dev_addr_type(nft_net(pkt
), dev
, addr
);
59 EXPORT_SYMBOL_GPL(nft_fib4_eval_type
);
61 static int get_ifindex(const struct net_device
*dev
)
63 return dev
? dev
->ifindex
: 0;
66 void nft_fib4_eval(const struct nft_expr
*expr
, struct nft_regs
*regs
,
67 const struct nft_pktinfo
*pkt
)
69 const struct nft_fib
*priv
= nft_expr_priv(expr
);
70 int noff
= skb_network_offset(pkt
->skb
);
71 u32
*dest
= ®s
->data
[priv
->dreg
];
72 struct iphdr
*iph
, _iph
;
73 struct fib_result res
;
75 .flowi4_scope
= RT_SCOPE_UNIVERSE
,
76 .flowi4_iif
= LOOPBACK_IFINDEX
,
78 const struct net_device
*oif
;
79 const struct net_device
*found
;
82 * Do not set flowi4_oif, it restricts results (for example, asking
83 * for oif 3 will get RTN_UNICAST result even if the daddr exits
84 * on another interface.
86 * Search results for the desired outinterface instead.
88 if (priv
->flags
& NFTA_FIB_F_OIF
)
90 else if (priv
->flags
& NFTA_FIB_F_IIF
)
95 if (nft_hook(pkt
) == NF_INET_PRE_ROUTING
&&
96 nft_fib_is_loopback(pkt
->skb
, nft_in(pkt
))) {
97 nft_fib_store_result(dest
, priv
, pkt
,
98 nft_in(pkt
)->ifindex
);
102 iph
= skb_header_pointer(pkt
->skb
, noff
, sizeof(_iph
), &_iph
);
104 regs
->verdict
.code
= NFT_BREAK
;
108 if (ipv4_is_zeronet(iph
->saddr
)) {
109 if (ipv4_is_lbcast(iph
->daddr
) ||
110 ipv4_is_local_multicast(iph
->daddr
)) {
111 nft_fib_store_result(dest
, priv
, pkt
,
112 get_ifindex(pkt
->skb
->dev
));
117 if (priv
->flags
& NFTA_FIB_F_MARK
)
118 fl4
.flowi4_mark
= pkt
->skb
->mark
;
120 fl4
.flowi4_tos
= iph
->tos
& DSCP_BITS
;
122 if (priv
->flags
& NFTA_FIB_F_DADDR
) {
123 fl4
.daddr
= iph
->daddr
;
124 fl4
.saddr
= get_saddr(iph
->saddr
);
126 fl4
.daddr
= iph
->saddr
;
127 fl4
.saddr
= get_saddr(iph
->daddr
);
132 if (fib_lookup(nft_net(pkt
), &fl4
, &res
, FIB_LOOKUP_IGNORE_LINKSTATE
))
138 case RTN_LOCAL
: /* Should not see RTN_LOCAL here */
145 found
= FIB_RES_DEV(res
);
147 if (!fib_info_nh_uses_dev(res
.fi
, oif
))
153 switch (priv
->result
) {
154 case NFT_FIB_RESULT_OIF
:
155 *dest
= found
->ifindex
;
157 case NFT_FIB_RESULT_OIFNAME
:
158 strncpy((char *)dest
, found
->name
, IFNAMSIZ
);
165 EXPORT_SYMBOL_GPL(nft_fib4_eval
);
167 static struct nft_expr_type nft_fib4_type
;
169 static const struct nft_expr_ops nft_fib4_type_ops
= {
170 .type
= &nft_fib4_type
,
171 .size
= NFT_EXPR_SIZE(sizeof(struct nft_fib
)),
172 .eval
= nft_fib4_eval_type
,
173 .init
= nft_fib_init
,
174 .dump
= nft_fib_dump
,
175 .validate
= nft_fib_validate
,
178 static const struct nft_expr_ops nft_fib4_ops
= {
179 .type
= &nft_fib4_type
,
180 .size
= NFT_EXPR_SIZE(sizeof(struct nft_fib
)),
181 .eval
= nft_fib4_eval
,
182 .init
= nft_fib_init
,
183 .dump
= nft_fib_dump
,
184 .validate
= nft_fib_validate
,
187 static const struct nft_expr_ops
*
188 nft_fib4_select_ops(const struct nft_ctx
*ctx
,
189 const struct nlattr
* const tb
[])
191 enum nft_fib_result result
;
193 if (!tb
[NFTA_FIB_RESULT
])
194 return ERR_PTR(-EINVAL
);
196 result
= ntohl(nla_get_be32(tb
[NFTA_FIB_RESULT
]));
199 case NFT_FIB_RESULT_OIF
:
200 return &nft_fib4_ops
;
201 case NFT_FIB_RESULT_OIFNAME
:
202 return &nft_fib4_ops
;
203 case NFT_FIB_RESULT_ADDRTYPE
:
204 return &nft_fib4_type_ops
;
206 return ERR_PTR(-EOPNOTSUPP
);
210 static struct nft_expr_type nft_fib4_type __read_mostly
= {
212 .select_ops
= nft_fib4_select_ops
,
213 .policy
= nft_fib_policy
,
214 .maxattr
= NFTA_FIB_MAX
,
215 .family
= NFPROTO_IPV4
,
216 .owner
= THIS_MODULE
,
219 static int __init
nft_fib4_module_init(void)
221 return nft_register_expr(&nft_fib4_type
);
224 static void __exit
nft_fib4_module_exit(void)
226 nft_unregister_expr(&nft_fib4_type
);
229 module_init(nft_fib4_module_init
);
230 module_exit(nft_fib4_module_exit
);
231 MODULE_LICENSE("GPL");
232 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
233 MODULE_ALIAS_NFT_AF_EXPR(2, "fib");