1 /* 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. 5 * 6 * Generic part shared by ipv4 and ipv6 backends. 7 */ 8 9 #include <linux/kernel.h> 10 #include <linux/init.h> 11 #include <linux/module.h> 12 #include <linux/netlink.h> 13 #include <linux/netfilter.h> 14 #include <linux/netfilter/nf_tables.h> 15 #include <net/netfilter/nf_tables_core.h> 16 #include <net/netfilter/nf_tables.h> 17 #include <net/netfilter/nft_fib.h> 18 19 const struct nla_policy nft_fib_policy[NFTA_FIB_MAX + 1] = { 20 [NFTA_FIB_DREG] = { .type = NLA_U32 }, 21 [NFTA_FIB_RESULT] = { .type = NLA_U32 }, 22 [NFTA_FIB_FLAGS] = { .type = NLA_U32 }, 23 }; 24 EXPORT_SYMBOL(nft_fib_policy); 25 26 #define NFTA_FIB_F_ALL (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR | \ 27 NFTA_FIB_F_MARK | NFTA_FIB_F_IIF | NFTA_FIB_F_OIF | \ 28 NFTA_FIB_F_PRESENT) 29 30 int nft_fib_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, 31 const struct nft_data **data) 32 { 33 const struct nft_fib *priv = nft_expr_priv(expr); 34 unsigned int hooks; 35 36 switch (priv->result) { 37 case NFT_FIB_RESULT_OIF: /* fallthrough */ 38 case NFT_FIB_RESULT_OIFNAME: 39 hooks = (1 << NF_INET_PRE_ROUTING); 40 break; 41 case NFT_FIB_RESULT_ADDRTYPE: 42 if (priv->flags & NFTA_FIB_F_IIF) 43 hooks = (1 << NF_INET_PRE_ROUTING) | 44 (1 << NF_INET_LOCAL_IN) | 45 (1 << NF_INET_FORWARD); 46 else if (priv->flags & NFTA_FIB_F_OIF) 47 hooks = (1 << NF_INET_LOCAL_OUT) | 48 (1 << NF_INET_POST_ROUTING) | 49 (1 << NF_INET_FORWARD); 50 else 51 hooks = (1 << NF_INET_LOCAL_IN) | 52 (1 << NF_INET_LOCAL_OUT) | 53 (1 << NF_INET_FORWARD) | 54 (1 << NF_INET_PRE_ROUTING) | 55 (1 << NF_INET_POST_ROUTING); 56 57 break; 58 default: 59 return -EINVAL; 60 } 61 62 return nft_chain_validate_hooks(ctx->chain, hooks); 63 } 64 EXPORT_SYMBOL_GPL(nft_fib_validate); 65 66 int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr, 67 const struct nlattr * const tb[]) 68 { 69 struct nft_fib *priv = nft_expr_priv(expr); 70 unsigned int len; 71 int err; 72 73 if (!tb[NFTA_FIB_DREG] || !tb[NFTA_FIB_RESULT] || !tb[NFTA_FIB_FLAGS]) 74 return -EINVAL; 75 76 priv->flags = ntohl(nla_get_be32(tb[NFTA_FIB_FLAGS])); 77 78 if (priv->flags == 0 || (priv->flags & ~NFTA_FIB_F_ALL)) 79 return -EINVAL; 80 81 if ((priv->flags & (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) == 82 (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) 83 return -EINVAL; 84 if ((priv->flags & (NFTA_FIB_F_IIF | NFTA_FIB_F_OIF)) == 85 (NFTA_FIB_F_IIF | NFTA_FIB_F_OIF)) 86 return -EINVAL; 87 if ((priv->flags & (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) == 0) 88 return -EINVAL; 89 90 priv->result = ntohl(nla_get_be32(tb[NFTA_FIB_RESULT])); 91 priv->dreg = nft_parse_register(tb[NFTA_FIB_DREG]); 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_validate_register_store(ctx, priv->dreg, NULL, 112 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) 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 nft_pktinfo *pkt, int index) 139 { 140 struct net_device *dev; 141 u32 *dreg = reg; 142 143 switch (priv->result) { 144 case NFT_FIB_RESULT_OIF: 145 *dreg = (priv->flags & NFTA_FIB_F_PRESENT) ? !!index : index; 146 break; 147 case NFT_FIB_RESULT_OIFNAME: 148 dev = dev_get_by_index_rcu(nft_net(pkt), index); 149 if (priv->flags & NFTA_FIB_F_PRESENT) 150 *dreg = !!dev; 151 else 152 strncpy(reg, dev ? dev->name : "", IFNAMSIZ); 153 break; 154 default: 155 WARN_ON_ONCE(1); 156 *dreg = 0; 157 break; 158 } 159 } 160 EXPORT_SYMBOL_GPL(nft_fib_store_result); 161 162 MODULE_LICENSE("GPL"); 163 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>"); 164