Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.14-rc4 252 lines 6.5 kB view raw
1/* 2 * Copyright (c) 2016 Laura Garcia <nevola@gmail.com> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 * 8 */ 9 10#include <linux/kernel.h> 11#include <linux/init.h> 12#include <linux/module.h> 13#include <linux/netlink.h> 14#include <linux/netfilter.h> 15#include <linux/netfilter/nf_tables.h> 16#include <net/netfilter/nf_tables.h> 17#include <net/netfilter/nf_tables_core.h> 18#include <linux/jhash.h> 19 20struct nft_jhash { 21 enum nft_registers sreg:8; 22 enum nft_registers dreg:8; 23 u8 len; 24 bool autogen_seed:1; 25 u32 modulus; 26 u32 seed; 27 u32 offset; 28}; 29 30static void nft_jhash_eval(const struct nft_expr *expr, 31 struct nft_regs *regs, 32 const struct nft_pktinfo *pkt) 33{ 34 struct nft_jhash *priv = nft_expr_priv(expr); 35 const void *data = &regs->data[priv->sreg]; 36 u32 h; 37 38 h = reciprocal_scale(jhash(data, priv->len, priv->seed), priv->modulus); 39 regs->data[priv->dreg] = h + priv->offset; 40} 41 42struct nft_symhash { 43 enum nft_registers dreg:8; 44 u32 modulus; 45 u32 offset; 46}; 47 48static void nft_symhash_eval(const struct nft_expr *expr, 49 struct nft_regs *regs, 50 const struct nft_pktinfo *pkt) 51{ 52 struct nft_symhash *priv = nft_expr_priv(expr); 53 struct sk_buff *skb = pkt->skb; 54 u32 h; 55 56 h = reciprocal_scale(__skb_get_hash_symmetric(skb), priv->modulus); 57 58 regs->data[priv->dreg] = h + priv->offset; 59} 60 61static const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = { 62 [NFTA_HASH_SREG] = { .type = NLA_U32 }, 63 [NFTA_HASH_DREG] = { .type = NLA_U32 }, 64 [NFTA_HASH_LEN] = { .type = NLA_U32 }, 65 [NFTA_HASH_MODULUS] = { .type = NLA_U32 }, 66 [NFTA_HASH_SEED] = { .type = NLA_U32 }, 67 [NFTA_HASH_OFFSET] = { .type = NLA_U32 }, 68 [NFTA_HASH_TYPE] = { .type = NLA_U32 }, 69}; 70 71static int nft_jhash_init(const struct nft_ctx *ctx, 72 const struct nft_expr *expr, 73 const struct nlattr * const tb[]) 74{ 75 struct nft_jhash *priv = nft_expr_priv(expr); 76 u32 len; 77 int err; 78 79 if (!tb[NFTA_HASH_SREG] || 80 !tb[NFTA_HASH_DREG] || 81 !tb[NFTA_HASH_LEN] || 82 !tb[NFTA_HASH_MODULUS]) 83 return -EINVAL; 84 85 if (tb[NFTA_HASH_OFFSET]) 86 priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET])); 87 88 priv->sreg = nft_parse_register(tb[NFTA_HASH_SREG]); 89 priv->dreg = nft_parse_register(tb[NFTA_HASH_DREG]); 90 91 err = nft_parse_u32_check(tb[NFTA_HASH_LEN], U8_MAX, &len); 92 if (err < 0) 93 return err; 94 if (len == 0) 95 return -ERANGE; 96 97 priv->len = len; 98 99 priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS])); 100 if (priv->modulus <= 1) 101 return -ERANGE; 102 103 if (priv->offset + priv->modulus - 1 < priv->offset) 104 return -EOVERFLOW; 105 106 if (tb[NFTA_HASH_SEED]) { 107 priv->seed = ntohl(nla_get_be32(tb[NFTA_HASH_SEED])); 108 } else { 109 priv->autogen_seed = true; 110 get_random_bytes(&priv->seed, sizeof(priv->seed)); 111 } 112 113 return nft_validate_register_load(priv->sreg, len) && 114 nft_validate_register_store(ctx, priv->dreg, NULL, 115 NFT_DATA_VALUE, sizeof(u32)); 116} 117 118static int nft_symhash_init(const struct nft_ctx *ctx, 119 const struct nft_expr *expr, 120 const struct nlattr * const tb[]) 121{ 122 struct nft_symhash *priv = nft_expr_priv(expr); 123 124 if (!tb[NFTA_HASH_DREG] || 125 !tb[NFTA_HASH_MODULUS]) 126 return -EINVAL; 127 128 if (tb[NFTA_HASH_OFFSET]) 129 priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET])); 130 131 priv->dreg = nft_parse_register(tb[NFTA_HASH_DREG]); 132 133 priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS])); 134 if (priv->modulus <= 1) 135 return -ERANGE; 136 137 if (priv->offset + priv->modulus - 1 < priv->offset) 138 return -EOVERFLOW; 139 140 return nft_validate_register_store(ctx, priv->dreg, NULL, 141 NFT_DATA_VALUE, sizeof(u32)); 142} 143 144static int nft_jhash_dump(struct sk_buff *skb, 145 const struct nft_expr *expr) 146{ 147 const struct nft_jhash *priv = nft_expr_priv(expr); 148 149 if (nft_dump_register(skb, NFTA_HASH_SREG, priv->sreg)) 150 goto nla_put_failure; 151 if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg)) 152 goto nla_put_failure; 153 if (nla_put_be32(skb, NFTA_HASH_LEN, htonl(priv->len))) 154 goto nla_put_failure; 155 if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus))) 156 goto nla_put_failure; 157 if (!priv->autogen_seed && 158 nla_put_be32(skb, NFTA_HASH_SEED, htonl(priv->seed))) 159 goto nla_put_failure; 160 if (priv->offset != 0) 161 if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset))) 162 goto nla_put_failure; 163 if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_JENKINS))) 164 goto nla_put_failure; 165 return 0; 166 167nla_put_failure: 168 return -1; 169} 170 171static int nft_symhash_dump(struct sk_buff *skb, 172 const struct nft_expr *expr) 173{ 174 const struct nft_symhash *priv = nft_expr_priv(expr); 175 176 if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg)) 177 goto nla_put_failure; 178 if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus))) 179 goto nla_put_failure; 180 if (priv->offset != 0) 181 if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset))) 182 goto nla_put_failure; 183 if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_SYM))) 184 goto nla_put_failure; 185 return 0; 186 187nla_put_failure: 188 return -1; 189} 190 191static struct nft_expr_type nft_hash_type; 192static const struct nft_expr_ops nft_jhash_ops = { 193 .type = &nft_hash_type, 194 .size = NFT_EXPR_SIZE(sizeof(struct nft_jhash)), 195 .eval = nft_jhash_eval, 196 .init = nft_jhash_init, 197 .dump = nft_jhash_dump, 198}; 199 200static const struct nft_expr_ops nft_symhash_ops = { 201 .type = &nft_hash_type, 202 .size = NFT_EXPR_SIZE(sizeof(struct nft_symhash)), 203 .eval = nft_symhash_eval, 204 .init = nft_symhash_init, 205 .dump = nft_symhash_dump, 206}; 207 208static const struct nft_expr_ops * 209nft_hash_select_ops(const struct nft_ctx *ctx, 210 const struct nlattr * const tb[]) 211{ 212 u32 type; 213 214 if (!tb[NFTA_HASH_TYPE]) 215 return &nft_jhash_ops; 216 217 type = ntohl(nla_get_be32(tb[NFTA_HASH_TYPE])); 218 switch (type) { 219 case NFT_HASH_SYM: 220 return &nft_symhash_ops; 221 case NFT_HASH_JENKINS: 222 return &nft_jhash_ops; 223 default: 224 break; 225 } 226 return ERR_PTR(-EOPNOTSUPP); 227} 228 229static struct nft_expr_type nft_hash_type __read_mostly = { 230 .name = "hash", 231 .select_ops = nft_hash_select_ops, 232 .policy = nft_hash_policy, 233 .maxattr = NFTA_HASH_MAX, 234 .owner = THIS_MODULE, 235}; 236 237static int __init nft_hash_module_init(void) 238{ 239 return nft_register_expr(&nft_hash_type); 240} 241 242static void __exit nft_hash_module_exit(void) 243{ 244 nft_unregister_expr(&nft_hash_type); 245} 246 247module_init(nft_hash_module_init); 248module_exit(nft_hash_module_exit); 249 250MODULE_LICENSE("GPL"); 251MODULE_AUTHOR("Laura Garcia <nevola@gmail.com>"); 252MODULE_ALIAS_NFT_EXPR("hash");