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 20 struct 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 struct nft_set *map; 29 }; 30 31 static void nft_jhash_eval(const struct nft_expr *expr, 32 struct nft_regs *regs, 33 const struct nft_pktinfo *pkt) 34 { 35 struct nft_jhash *priv = nft_expr_priv(expr); 36 const void *data = ®s->data[priv->sreg]; 37 u32 h; 38 39 h = reciprocal_scale(jhash(data, priv->len, priv->seed), 40 priv->modulus); 41 42 regs->data[priv->dreg] = h + priv->offset; 43 } 44 45 static void nft_jhash_map_eval(const struct nft_expr *expr, 46 struct nft_regs *regs, 47 const struct nft_pktinfo *pkt) 48 { 49 struct nft_jhash *priv = nft_expr_priv(expr); 50 const void *data = ®s->data[priv->sreg]; 51 const struct nft_set *map = priv->map; 52 const struct nft_set_ext *ext; 53 u32 result; 54 bool found; 55 56 result = reciprocal_scale(jhash(data, priv->len, priv->seed), 57 priv->modulus) + priv->offset; 58 59 found = map->ops->lookup(nft_net(pkt), map, &result, &ext); 60 if (!found) 61 return; 62 63 nft_data_copy(®s->data[priv->dreg], 64 nft_set_ext_data(ext), map->dlen); 65 } 66 67 struct nft_symhash { 68 enum nft_registers dreg:8; 69 u32 modulus; 70 u32 offset; 71 struct nft_set *map; 72 }; 73 74 static void nft_symhash_eval(const struct nft_expr *expr, 75 struct nft_regs *regs, 76 const struct nft_pktinfo *pkt) 77 { 78 struct nft_symhash *priv = nft_expr_priv(expr); 79 struct sk_buff *skb = pkt->skb; 80 u32 h; 81 82 h = reciprocal_scale(__skb_get_hash_symmetric(skb), priv->modulus); 83 84 regs->data[priv->dreg] = h + priv->offset; 85 } 86 87 static void nft_symhash_map_eval(const struct nft_expr *expr, 88 struct nft_regs *regs, 89 const struct nft_pktinfo *pkt) 90 { 91 struct nft_symhash *priv = nft_expr_priv(expr); 92 struct sk_buff *skb = pkt->skb; 93 const struct nft_set *map = priv->map; 94 const struct nft_set_ext *ext; 95 u32 result; 96 bool found; 97 98 result = reciprocal_scale(__skb_get_hash_symmetric(skb), 99 priv->modulus) + priv->offset; 100 101 found = map->ops->lookup(nft_net(pkt), map, &result, &ext); 102 if (!found) 103 return; 104 105 nft_data_copy(®s->data[priv->dreg], 106 nft_set_ext_data(ext), map->dlen); 107 } 108 109 static const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = { 110 [NFTA_HASH_SREG] = { .type = NLA_U32 }, 111 [NFTA_HASH_DREG] = { .type = NLA_U32 }, 112 [NFTA_HASH_LEN] = { .type = NLA_U32 }, 113 [NFTA_HASH_MODULUS] = { .type = NLA_U32 }, 114 [NFTA_HASH_SEED] = { .type = NLA_U32 }, 115 [NFTA_HASH_OFFSET] = { .type = NLA_U32 }, 116 [NFTA_HASH_TYPE] = { .type = NLA_U32 }, 117 [NFTA_HASH_SET_NAME] = { .type = NLA_STRING, 118 .len = NFT_SET_MAXNAMELEN - 1 }, 119 [NFTA_HASH_SET_ID] = { .type = NLA_U32 }, 120 }; 121 122 static int nft_jhash_init(const struct nft_ctx *ctx, 123 const struct nft_expr *expr, 124 const struct nlattr * const tb[]) 125 { 126 struct nft_jhash *priv = nft_expr_priv(expr); 127 u32 len; 128 int err; 129 130 if (!tb[NFTA_HASH_SREG] || 131 !tb[NFTA_HASH_DREG] || 132 !tb[NFTA_HASH_LEN] || 133 !tb[NFTA_HASH_MODULUS]) 134 return -EINVAL; 135 136 if (tb[NFTA_HASH_OFFSET]) 137 priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET])); 138 139 priv->sreg = nft_parse_register(tb[NFTA_HASH_SREG]); 140 priv->dreg = nft_parse_register(tb[NFTA_HASH_DREG]); 141 142 err = nft_parse_u32_check(tb[NFTA_HASH_LEN], U8_MAX, &len); 143 if (err < 0) 144 return err; 145 if (len == 0) 146 return -ERANGE; 147 148 priv->len = len; 149 150 priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS])); 151 if (priv->modulus < 1) 152 return -ERANGE; 153 154 if (priv->offset + priv->modulus - 1 < priv->offset) 155 return -EOVERFLOW; 156 157 if (tb[NFTA_HASH_SEED]) { 158 priv->seed = ntohl(nla_get_be32(tb[NFTA_HASH_SEED])); 159 } else { 160 priv->autogen_seed = true; 161 get_random_bytes(&priv->seed, sizeof(priv->seed)); 162 } 163 164 return nft_validate_register_load(priv->sreg, len) && 165 nft_validate_register_store(ctx, priv->dreg, NULL, 166 NFT_DATA_VALUE, sizeof(u32)); 167 } 168 169 static int nft_jhash_map_init(const struct nft_ctx *ctx, 170 const struct nft_expr *expr, 171 const struct nlattr * const tb[]) 172 { 173 struct nft_jhash *priv = nft_expr_priv(expr); 174 u8 genmask = nft_genmask_next(ctx->net); 175 176 nft_jhash_init(ctx, expr, tb); 177 priv->map = nft_set_lookup_global(ctx->net, ctx->table, 178 tb[NFTA_HASH_SET_NAME], 179 tb[NFTA_HASH_SET_ID], genmask); 180 return PTR_ERR_OR_ZERO(priv->map); 181 } 182 183 static int nft_symhash_init(const struct nft_ctx *ctx, 184 const struct nft_expr *expr, 185 const struct nlattr * const tb[]) 186 { 187 struct nft_symhash *priv = nft_expr_priv(expr); 188 189 if (!tb[NFTA_HASH_DREG] || 190 !tb[NFTA_HASH_MODULUS]) 191 return -EINVAL; 192 193 if (tb[NFTA_HASH_OFFSET]) 194 priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET])); 195 196 priv->dreg = nft_parse_register(tb[NFTA_HASH_DREG]); 197 198 priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS])); 199 if (priv->modulus <= 1) 200 return -ERANGE; 201 202 if (priv->offset + priv->modulus - 1 < priv->offset) 203 return -EOVERFLOW; 204 205 return nft_validate_register_store(ctx, priv->dreg, NULL, 206 NFT_DATA_VALUE, sizeof(u32)); 207 } 208 209 static int nft_symhash_map_init(const struct nft_ctx *ctx, 210 const struct nft_expr *expr, 211 const struct nlattr * const tb[]) 212 { 213 struct nft_jhash *priv = nft_expr_priv(expr); 214 u8 genmask = nft_genmask_next(ctx->net); 215 216 nft_symhash_init(ctx, expr, tb); 217 priv->map = nft_set_lookup_global(ctx->net, ctx->table, 218 tb[NFTA_HASH_SET_NAME], 219 tb[NFTA_HASH_SET_ID], genmask); 220 return PTR_ERR_OR_ZERO(priv->map); 221 } 222 223 static int nft_jhash_dump(struct sk_buff *skb, 224 const struct nft_expr *expr) 225 { 226 const struct nft_jhash *priv = nft_expr_priv(expr); 227 228 if (nft_dump_register(skb, NFTA_HASH_SREG, priv->sreg)) 229 goto nla_put_failure; 230 if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg)) 231 goto nla_put_failure; 232 if (nla_put_be32(skb, NFTA_HASH_LEN, htonl(priv->len))) 233 goto nla_put_failure; 234 if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus))) 235 goto nla_put_failure; 236 if (!priv->autogen_seed && 237 nla_put_be32(skb, NFTA_HASH_SEED, htonl(priv->seed))) 238 goto nla_put_failure; 239 if (priv->offset != 0) 240 if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset))) 241 goto nla_put_failure; 242 if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_JENKINS))) 243 goto nla_put_failure; 244 return 0; 245 246 nla_put_failure: 247 return -1; 248 } 249 250 static int nft_jhash_map_dump(struct sk_buff *skb, 251 const struct nft_expr *expr) 252 { 253 const struct nft_jhash *priv = nft_expr_priv(expr); 254 255 if (nft_jhash_dump(skb, expr) || 256 nla_put_string(skb, NFTA_HASH_SET_NAME, priv->map->name)) 257 return -1; 258 259 return 0; 260 } 261 262 static int nft_symhash_dump(struct sk_buff *skb, 263 const struct nft_expr *expr) 264 { 265 const struct nft_symhash *priv = nft_expr_priv(expr); 266 267 if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg)) 268 goto nla_put_failure; 269 if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus))) 270 goto nla_put_failure; 271 if (priv->offset != 0) 272 if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset))) 273 goto nla_put_failure; 274 if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_SYM))) 275 goto nla_put_failure; 276 return 0; 277 278 nla_put_failure: 279 return -1; 280 } 281 282 static int nft_symhash_map_dump(struct sk_buff *skb, 283 const struct nft_expr *expr) 284 { 285 const struct nft_symhash *priv = nft_expr_priv(expr); 286 287 if (nft_symhash_dump(skb, expr) || 288 nla_put_string(skb, NFTA_HASH_SET_NAME, priv->map->name)) 289 return -1; 290 291 return 0; 292 } 293 294 static struct nft_expr_type nft_hash_type; 295 static const struct nft_expr_ops nft_jhash_ops = { 296 .type = &nft_hash_type, 297 .size = NFT_EXPR_SIZE(sizeof(struct nft_jhash)), 298 .eval = nft_jhash_eval, 299 .init = nft_jhash_init, 300 .dump = nft_jhash_dump, 301 }; 302 303 static const struct nft_expr_ops nft_jhash_map_ops = { 304 .type = &nft_hash_type, 305 .size = NFT_EXPR_SIZE(sizeof(struct nft_jhash)), 306 .eval = nft_jhash_map_eval, 307 .init = nft_jhash_map_init, 308 .dump = nft_jhash_map_dump, 309 }; 310 311 static const struct nft_expr_ops nft_symhash_ops = { 312 .type = &nft_hash_type, 313 .size = NFT_EXPR_SIZE(sizeof(struct nft_symhash)), 314 .eval = nft_symhash_eval, 315 .init = nft_symhash_init, 316 .dump = nft_symhash_dump, 317 }; 318 319 static const struct nft_expr_ops nft_symhash_map_ops = { 320 .type = &nft_hash_type, 321 .size = NFT_EXPR_SIZE(sizeof(struct nft_symhash)), 322 .eval = nft_symhash_map_eval, 323 .init = nft_symhash_map_init, 324 .dump = nft_symhash_map_dump, 325 }; 326 327 static const struct nft_expr_ops * 328 nft_hash_select_ops(const struct nft_ctx *ctx, 329 const struct nlattr * const tb[]) 330 { 331 u32 type; 332 333 if (!tb[NFTA_HASH_TYPE]) 334 return &nft_jhash_ops; 335 336 type = ntohl(nla_get_be32(tb[NFTA_HASH_TYPE])); 337 switch (type) { 338 case NFT_HASH_SYM: 339 if (tb[NFTA_HASH_SET_NAME]) 340 return &nft_symhash_map_ops; 341 return &nft_symhash_ops; 342 case NFT_HASH_JENKINS: 343 if (tb[NFTA_HASH_SET_NAME]) 344 return &nft_jhash_map_ops; 345 return &nft_jhash_ops; 346 default: 347 break; 348 } 349 return ERR_PTR(-EOPNOTSUPP); 350 } 351 352 static struct nft_expr_type nft_hash_type __read_mostly = { 353 .name = "hash", 354 .select_ops = nft_hash_select_ops, 355 .policy = nft_hash_policy, 356 .maxattr = NFTA_HASH_MAX, 357 .owner = THIS_MODULE, 358 }; 359 360 static int __init nft_hash_module_init(void) 361 { 362 return nft_register_expr(&nft_hash_type); 363 } 364 365 static void __exit nft_hash_module_exit(void) 366 { 367 nft_unregister_expr(&nft_hash_type); 368 } 369 370 module_init(nft_hash_module_init); 371 module_exit(nft_hash_module_exit); 372 373 MODULE_LICENSE("GPL"); 374 MODULE_AUTHOR("Laura Garcia <nevola@gmail.com>"); 375 MODULE_ALIAS_NFT_EXPR("hash"); 376