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