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