nft_fib.c (5028B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * 4 * Generic part shared by ipv4 and ipv6 backends. 5 */ 6 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> 16 17const struct nla_policy nft_fib_policy[NFTA_FIB_MAX + 1] = { 18 [NFTA_FIB_DREG] = { .type = NLA_U32 }, 19 [NFTA_FIB_RESULT] = { .type = NLA_U32 }, 20 [NFTA_FIB_FLAGS] = { .type = NLA_U32 }, 21}; 22EXPORT_SYMBOL(nft_fib_policy); 23 24#define NFTA_FIB_F_ALL (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR | \ 25 NFTA_FIB_F_MARK | NFTA_FIB_F_IIF | NFTA_FIB_F_OIF | \ 26 NFTA_FIB_F_PRESENT) 27 28int nft_fib_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, 29 const struct nft_data **data) 30{ 31 const struct nft_fib *priv = nft_expr_priv(expr); 32 unsigned int hooks; 33 34 switch (priv->result) { 35 case NFT_FIB_RESULT_OIF: 36 case NFT_FIB_RESULT_OIFNAME: 37 hooks = (1 << NF_INET_PRE_ROUTING); 38 if (priv->flags & NFTA_FIB_F_IIF) { 39 hooks |= (1 << NF_INET_LOCAL_IN) | 40 (1 << NF_INET_FORWARD); 41 } 42 break; 43 case NFT_FIB_RESULT_ADDRTYPE: 44 if (priv->flags & NFTA_FIB_F_IIF) 45 hooks = (1 << NF_INET_PRE_ROUTING) | 46 (1 << NF_INET_LOCAL_IN) | 47 (1 << NF_INET_FORWARD); 48 else if (priv->flags & NFTA_FIB_F_OIF) 49 hooks = (1 << NF_INET_LOCAL_OUT) | 50 (1 << NF_INET_POST_ROUTING) | 51 (1 << NF_INET_FORWARD); 52 else 53 hooks = (1 << NF_INET_LOCAL_IN) | 54 (1 << NF_INET_LOCAL_OUT) | 55 (1 << NF_INET_FORWARD) | 56 (1 << NF_INET_PRE_ROUTING) | 57 (1 << NF_INET_POST_ROUTING); 58 59 break; 60 default: 61 return -EINVAL; 62 } 63 64 return nft_chain_validate_hooks(ctx->chain, hooks); 65} 66EXPORT_SYMBOL_GPL(nft_fib_validate); 67 68int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr, 69 const struct nlattr * const tb[]) 70{ 71 struct nft_fib *priv = nft_expr_priv(expr); 72 unsigned int len; 73 int err; 74 75 if (!tb[NFTA_FIB_DREG] || !tb[NFTA_FIB_RESULT] || !tb[NFTA_FIB_FLAGS]) 76 return -EINVAL; 77 78 priv->flags = ntohl(nla_get_be32(tb[NFTA_FIB_FLAGS])); 79 80 if (priv->flags == 0 || (priv->flags & ~NFTA_FIB_F_ALL)) 81 return -EINVAL; 82 83 if ((priv->flags & (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) == 84 (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) 85 return -EINVAL; 86 if ((priv->flags & (NFTA_FIB_F_IIF | NFTA_FIB_F_OIF)) == 87 (NFTA_FIB_F_IIF | NFTA_FIB_F_OIF)) 88 return -EINVAL; 89 if ((priv->flags & (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) == 0) 90 return -EINVAL; 91 92 priv->result = ntohl(nla_get_be32(tb[NFTA_FIB_RESULT])); 93 94 switch (priv->result) { 95 case NFT_FIB_RESULT_OIF: 96 if (priv->flags & NFTA_FIB_F_OIF) 97 return -EINVAL; 98 len = sizeof(int); 99 break; 100 case NFT_FIB_RESULT_OIFNAME: 101 if (priv->flags & NFTA_FIB_F_OIF) 102 return -EINVAL; 103 len = IFNAMSIZ; 104 break; 105 case NFT_FIB_RESULT_ADDRTYPE: 106 len = sizeof(u32); 107 break; 108 default: 109 return -EINVAL; 110 } 111 112 err = nft_parse_register_store(ctx, tb[NFTA_FIB_DREG], &priv->dreg, 113 NULL, NFT_DATA_VALUE, len); 114 if (err < 0) 115 return err; 116 117 return 0; 118} 119EXPORT_SYMBOL_GPL(nft_fib_init); 120 121int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr) 122{ 123 const struct nft_fib *priv = nft_expr_priv(expr); 124 125 if (nft_dump_register(skb, NFTA_FIB_DREG, priv->dreg)) 126 return -1; 127 128 if (nla_put_be32(skb, NFTA_FIB_RESULT, htonl(priv->result))) 129 return -1; 130 131 if (nla_put_be32(skb, NFTA_FIB_FLAGS, htonl(priv->flags))) 132 return -1; 133 134 return 0; 135} 136EXPORT_SYMBOL_GPL(nft_fib_dump); 137 138void nft_fib_store_result(void *reg, const struct nft_fib *priv, 139 const struct net_device *dev) 140{ 141 u32 *dreg = reg; 142 int index; 143 144 switch (priv->result) { 145 case NFT_FIB_RESULT_OIF: 146 index = dev ? dev->ifindex : 0; 147 *dreg = (priv->flags & NFTA_FIB_F_PRESENT) ? !!index : index; 148 break; 149 case NFT_FIB_RESULT_OIFNAME: 150 if (priv->flags & NFTA_FIB_F_PRESENT) 151 *dreg = !!dev; 152 else 153 strncpy(reg, dev ? dev->name : "", IFNAMSIZ); 154 break; 155 default: 156 WARN_ON_ONCE(1); 157 *dreg = 0; 158 break; 159 } 160} 161EXPORT_SYMBOL_GPL(nft_fib_store_result); 162 163bool nft_fib_reduce(struct nft_regs_track *track, 164 const struct nft_expr *expr) 165{ 166 const struct nft_fib *priv = nft_expr_priv(expr); 167 unsigned int len = NFT_REG32_SIZE; 168 const struct nft_fib *fib; 169 170 switch (priv->result) { 171 case NFT_FIB_RESULT_OIF: 172 break; 173 case NFT_FIB_RESULT_OIFNAME: 174 if (priv->flags & NFTA_FIB_F_PRESENT) 175 len = NFT_REG32_SIZE; 176 else 177 len = IFNAMSIZ; 178 break; 179 case NFT_FIB_RESULT_ADDRTYPE: 180 break; 181 default: 182 WARN_ON_ONCE(1); 183 break; 184 } 185 186 if (!nft_reg_track_cmp(track, expr, priv->dreg)) { 187 nft_reg_track_update(track, expr, priv->dreg, len); 188 return false; 189 } 190 191 fib = nft_expr_priv(track->regs[priv->dreg].selector); 192 if (priv->result != fib->result || 193 priv->flags != fib->flags) { 194 nft_reg_track_update(track, expr, priv->dreg, len); 195 return false; 196 } 197 198 if (!track->regs[priv->dreg].bitwise) 199 return true; 200 201 return false; 202} 203EXPORT_SYMBOL_GPL(nft_fib_reduce); 204 205MODULE_LICENSE("GPL"); 206MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");