Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: ipset: Fix "don't update counters" mode when counters used at the matching

The matching of the counters was not taken into account, fixed.

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Jozsef Kadlecsik and committed by
Pablo Neira Ayuso
4750005a c0453377

+114 -128
+6
include/linux/netfilter/ipset/ip_set.h
··· 122 122 u64 bytes; 123 123 char *comment; 124 124 u32 timeout; 125 + u8 packets_op; 126 + u8 bytes_op; 125 127 }; 126 128 127 129 struct ip_set; ··· 341 339 struct ip_set_ext *ext); 342 340 extern int ip_set_put_extensions(struct sk_buff *skb, const struct ip_set *set, 343 341 const void *e, bool active); 342 + extern bool ip_set_match_extensions(struct ip_set *set, 343 + const struct ip_set_ext *ext, 344 + struct ip_set_ext *mext, 345 + u32 flags, void *data); 344 346 345 347 static inline int 346 348 ip_set_get_hostipaddr4(struct nlattr *nla, u32 *ipaddr)
+19 -6
include/linux/netfilter/ipset/ip_set_counter.h
··· 34 34 return (u64)atomic64_read(&(counter)->packets); 35 35 } 36 36 37 + static inline bool 38 + ip_set_match_counter(u64 counter, u64 match, u8 op) 39 + { 40 + switch (op) { 41 + case IPSET_COUNTER_NONE: 42 + return true; 43 + case IPSET_COUNTER_EQ: 44 + return counter == match; 45 + case IPSET_COUNTER_NE: 46 + return counter != match; 47 + case IPSET_COUNTER_LT: 48 + return counter < match; 49 + case IPSET_COUNTER_GT: 50 + return counter > match; 51 + } 52 + return false; 53 + } 54 + 37 55 static inline void 38 56 ip_set_update_counter(struct ip_set_counter *counter, 39 - const struct ip_set_ext *ext, 40 - struct ip_set_ext *mext, u32 flags) 57 + const struct ip_set_ext *ext, u32 flags) 41 58 { 42 59 if (ext->packets != ULLONG_MAX && 43 60 !(flags & IPSET_FLAG_SKIP_COUNTER_UPDATE)) { 44 61 ip_set_add_bytes(ext->bytes, counter); 45 62 ip_set_add_packets(ext->packets, counter); 46 - } 47 - if (flags & IPSET_FLAG_MATCH_COUNTERS) { 48 - mext->packets = ip_set_get_packets(counter); 49 - mext->bytes = ip_set_get_bytes(counter); 50 63 } 51 64 } 52 65
+1 -8
net/netfilter/ipset/ip_set_bitmap_gen.h
··· 127 127 128 128 if (ret <= 0) 129 129 return ret; 130 - if (SET_WITH_TIMEOUT(set) && 131 - ip_set_timeout_expired(ext_timeout(x, set))) 132 - return 0; 133 - if (SET_WITH_COUNTER(set)) 134 - ip_set_update_counter(ext_counter(x, set), ext, mext, flags); 135 - if (SET_WITH_SKBINFO(set)) 136 - ip_set_get_skbinfo(ext_skbinfo(x, set), ext, mext, flags); 137 - return 1; 130 + return ip_set_match_extensions(set, ext, mext, flags, x); 138 131 } 139 132 140 133 static int
+25
net/netfilter/ipset/ip_set_core.c
··· 472 472 } 473 473 EXPORT_SYMBOL_GPL(ip_set_put_extensions); 474 474 475 + bool 476 + ip_set_match_extensions(struct ip_set *set, const struct ip_set_ext *ext, 477 + struct ip_set_ext *mext, u32 flags, void *data) 478 + { 479 + if (SET_WITH_TIMEOUT(set) && 480 + ip_set_timeout_expired(ext_timeout(data, set))) 481 + return false; 482 + if (SET_WITH_COUNTER(set)) { 483 + struct ip_set_counter *counter = ext_counter(data, set); 484 + 485 + if (flags & IPSET_FLAG_MATCH_COUNTERS && 486 + !(ip_set_match_counter(ip_set_get_packets(counter), 487 + mext->packets, mext->packets_op) && 488 + ip_set_match_counter(ip_set_get_bytes(counter), 489 + mext->bytes, mext->bytes_op))) 490 + return false; 491 + ip_set_update_counter(counter, ext, flags); 492 + } 493 + if (SET_WITH_SKBINFO(set)) 494 + ip_set_get_skbinfo(ext_skbinfo(data, set), 495 + ext, mext, flags); 496 + return true; 497 + } 498 + EXPORT_SYMBOL_GPL(ip_set_match_extensions); 499 + 475 500 /* Creating/destroying/renaming/swapping affect the existence and 476 501 * the properties of a set. All of these can be executed from userspace 477 502 * only and serialized by the nfnl mutex indirectly from nfnetlink.
+14 -23
net/netfilter/ipset/ip_set_hash_gen.h
··· 917 917 mtype_data_match(struct mtype_elem *data, const struct ip_set_ext *ext, 918 918 struct ip_set_ext *mext, struct ip_set *set, u32 flags) 919 919 { 920 - if (SET_WITH_COUNTER(set)) 921 - ip_set_update_counter(ext_counter(data, set), 922 - ext, mext, flags); 923 - if (SET_WITH_SKBINFO(set)) 924 - ip_set_get_skbinfo(ext_skbinfo(data, set), 925 - ext, mext, flags); 920 + if (!ip_set_match_extensions(set, ext, mext, flags, data)) 921 + return 0; 922 + /* nomatch entries return -ENOTEMPTY */ 926 923 return mtype_do_data_match(data); 927 924 } 928 925 ··· 938 941 struct mtype_elem *data; 939 942 #if IPSET_NET_COUNT == 2 940 943 struct mtype_elem orig = *d; 941 - int i, j = 0, k; 944 + int ret, i, j = 0, k; 942 945 #else 943 - int i, j = 0; 946 + int ret, i, j = 0; 944 947 #endif 945 948 u32 key, multi = 0; 946 949 ··· 966 969 data = ahash_data(n, i, set->dsize); 967 970 if (!mtype_data_equal(data, d, &multi)) 968 971 continue; 969 - if (SET_WITH_TIMEOUT(set)) { 970 - if (!ip_set_timeout_expired( 971 - ext_timeout(data, set))) 972 - return mtype_data_match(data, ext, 973 - mext, set, 974 - flags); 972 + ret = mtype_data_match(data, ext, mext, set, flags); 973 + if (ret != 0) 974 + return ret; 975 975 #ifdef IP_SET_HASH_WITH_MULTI 976 - multi = 0; 976 + /* No match, reset multiple match flag */ 977 + multi = 0; 977 978 #endif 978 - } else 979 - return mtype_data_match(data, ext, 980 - mext, set, flags); 981 979 } 982 980 #if IPSET_NET_COUNT == 2 983 981 } ··· 1019 1027 if (!test_bit(i, n->used)) 1020 1028 continue; 1021 1029 data = ahash_data(n, i, set->dsize); 1022 - if (mtype_data_equal(data, d, &multi) && 1023 - !(SET_WITH_TIMEOUT(set) && 1024 - ip_set_timeout_expired(ext_timeout(data, set)))) { 1025 - ret = mtype_data_match(data, ext, mext, set, flags); 1030 + if (!mtype_data_equal(data, d, &multi)) 1031 + continue; 1032 + ret = mtype_data_match(data, ext, mext, set, flags); 1033 + if (ret != 0) 1026 1034 goto out; 1027 - } 1028 1035 } 1029 1036 out: 1030 1037 return ret;
+6 -15
net/netfilter/ipset/ip_set_list_set.c
··· 55 55 struct ip_set_adt_opt *opt, const struct ip_set_ext *ext) 56 56 { 57 57 struct list_set *map = set->data; 58 + struct ip_set_ext *mext = &opt->ext; 58 59 struct set_elem *e; 59 - u32 cmdflags = opt->cmdflags; 60 + u32 flags = opt->cmdflags; 60 61 int ret; 61 62 62 63 /* Don't lookup sub-counters at all */ ··· 65 64 if (opt->cmdflags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE) 66 65 opt->cmdflags &= ~IPSET_FLAG_SKIP_COUNTER_UPDATE; 67 66 list_for_each_entry_rcu(e, &map->members, list) { 68 - if (SET_WITH_TIMEOUT(set) && 69 - ip_set_timeout_expired(ext_timeout(e, set))) 70 - continue; 71 67 ret = ip_set_test(e->id, skb, par, opt); 72 - if (ret > 0) { 73 - if (SET_WITH_COUNTER(set)) 74 - ip_set_update_counter(ext_counter(e, set), 75 - ext, &opt->ext, 76 - cmdflags); 77 - if (SET_WITH_SKBINFO(set)) 78 - ip_set_get_skbinfo(ext_skbinfo(e, set), 79 - ext, &opt->ext, 80 - cmdflags); 81 - return ret; 82 - } 68 + if (ret <= 0) 69 + continue; 70 + if (ip_set_match_extensions(set, ext, mext, flags, e)) 71 + return 1; 83 72 } 84 73 return 0; 85 74 }
+43 -76
net/netfilter/xt_set.c
··· 39 39 return inv; 40 40 } 41 41 42 - #define ADT_OPT(n, f, d, fs, cfs, t) \ 43 - struct ip_set_adt_opt n = { \ 44 - .family = f, \ 45 - .dim = d, \ 46 - .flags = fs, \ 47 - .cmdflags = cfs, \ 48 - .ext.timeout = t, \ 42 + #define ADT_OPT(n, f, d, fs, cfs, t, p, b, po, bo) \ 43 + struct ip_set_adt_opt n = { \ 44 + .family = f, \ 45 + .dim = d, \ 46 + .flags = fs, \ 47 + .cmdflags = cfs, \ 48 + .ext.timeout = t, \ 49 + .ext.packets = p, \ 50 + .ext.bytes = b, \ 51 + .ext.packets_op = po, \ 52 + .ext.bytes_op = bo, \ 49 53 } 50 54 51 55 /* Revision 0 interface: backward compatible with netfilter/iptables */ ··· 60 56 const struct xt_set_info_match_v0 *info = par->matchinfo; 61 57 62 58 ADT_OPT(opt, xt_family(par), info->match_set.u.compat.dim, 63 - info->match_set.u.compat.flags, 0, UINT_MAX); 59 + info->match_set.u.compat.flags, 0, UINT_MAX, 60 + 0, 0, 0, 0); 64 61 65 62 return match_set(info->match_set.index, skb, par, &opt, 66 63 info->match_set.u.compat.flags & IPSET_INV_MATCH); ··· 124 119 const struct xt_set_info_match_v1 *info = par->matchinfo; 125 120 126 121 ADT_OPT(opt, xt_family(par), info->match_set.dim, 127 - info->match_set.flags, 0, UINT_MAX); 122 + info->match_set.flags, 0, UINT_MAX, 123 + 0, 0, 0, 0); 128 124 129 125 if (opt.flags & IPSET_RETURN_NOMATCH) 130 126 opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH; ··· 167 161 /* Revision 3 match */ 168 162 169 163 static bool 170 - match_counter0(u64 counter, const struct ip_set_counter_match0 *info) 171 - { 172 - switch (info->op) { 173 - case IPSET_COUNTER_NONE: 174 - return true; 175 - case IPSET_COUNTER_EQ: 176 - return counter == info->value; 177 - case IPSET_COUNTER_NE: 178 - return counter != info->value; 179 - case IPSET_COUNTER_LT: 180 - return counter < info->value; 181 - case IPSET_COUNTER_GT: 182 - return counter > info->value; 183 - } 184 - return false; 185 - } 186 - 187 - static bool 188 164 set_match_v3(const struct sk_buff *skb, struct xt_action_param *par) 189 165 { 190 166 const struct xt_set_info_match_v3 *info = par->matchinfo; 191 - int ret; 192 167 193 168 ADT_OPT(opt, xt_family(par), info->match_set.dim, 194 - info->match_set.flags, info->flags, UINT_MAX); 169 + info->match_set.flags, info->flags, UINT_MAX, 170 + info->packets.value, info->bytes.value, 171 + info->packets.op, info->bytes.op); 195 172 196 173 if (info->packets.op != IPSET_COUNTER_NONE || 197 174 info->bytes.op != IPSET_COUNTER_NONE) 198 175 opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS; 199 176 200 - ret = match_set(info->match_set.index, skb, par, &opt, 201 - info->match_set.flags & IPSET_INV_MATCH); 202 - 203 - if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS)) 204 - return ret; 205 - 206 - if (!match_counter0(opt.ext.packets, &info->packets)) 207 - return false; 208 - return match_counter0(opt.ext.bytes, &info->bytes); 177 + return match_set(info->match_set.index, skb, par, &opt, 178 + info->match_set.flags & IPSET_INV_MATCH); 209 179 } 210 180 211 181 #define set_match_v3_checkentry set_match_v1_checkentry ··· 190 208 /* Revision 4 match */ 191 209 192 210 static bool 193 - match_counter(u64 counter, const struct ip_set_counter_match *info) 194 - { 195 - switch (info->op) { 196 - case IPSET_COUNTER_NONE: 197 - return true; 198 - case IPSET_COUNTER_EQ: 199 - return counter == info->value; 200 - case IPSET_COUNTER_NE: 201 - return counter != info->value; 202 - case IPSET_COUNTER_LT: 203 - return counter < info->value; 204 - case IPSET_COUNTER_GT: 205 - return counter > info->value; 206 - } 207 - return false; 208 - } 209 - 210 - static bool 211 211 set_match_v4(const struct sk_buff *skb, struct xt_action_param *par) 212 212 { 213 213 const struct xt_set_info_match_v4 *info = par->matchinfo; 214 - int ret; 215 214 216 215 ADT_OPT(opt, xt_family(par), info->match_set.dim, 217 - info->match_set.flags, info->flags, UINT_MAX); 216 + info->match_set.flags, info->flags, UINT_MAX, 217 + info->packets.value, info->bytes.value, 218 + info->packets.op, info->bytes.op); 218 219 219 220 if (info->packets.op != IPSET_COUNTER_NONE || 220 221 info->bytes.op != IPSET_COUNTER_NONE) 221 222 opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS; 222 223 223 - ret = match_set(info->match_set.index, skb, par, &opt, 224 - info->match_set.flags & IPSET_INV_MATCH); 225 - 226 - if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS)) 227 - return ret; 228 - 229 - if (!match_counter(opt.ext.packets, &info->packets)) 230 - return false; 231 - return match_counter(opt.ext.bytes, &info->bytes); 224 + return match_set(info->match_set.index, skb, par, &opt, 225 + info->match_set.flags & IPSET_INV_MATCH); 232 226 } 233 227 234 228 #define set_match_v4_checkentry set_match_v1_checkentry ··· 218 260 const struct xt_set_info_target_v0 *info = par->targinfo; 219 261 220 262 ADT_OPT(add_opt, xt_family(par), info->add_set.u.compat.dim, 221 - info->add_set.u.compat.flags, 0, UINT_MAX); 263 + info->add_set.u.compat.flags, 0, UINT_MAX, 264 + 0, 0, 0, 0); 222 265 ADT_OPT(del_opt, xt_family(par), info->del_set.u.compat.dim, 223 - info->del_set.u.compat.flags, 0, UINT_MAX); 266 + info->del_set.u.compat.flags, 0, UINT_MAX, 267 + 0, 0, 0, 0); 224 268 225 269 if (info->add_set.index != IPSET_INVALID_ID) 226 270 ip_set_add(info->add_set.index, skb, par, &add_opt); ··· 293 333 const struct xt_set_info_target_v1 *info = par->targinfo; 294 334 295 335 ADT_OPT(add_opt, xt_family(par), info->add_set.dim, 296 - info->add_set.flags, 0, UINT_MAX); 336 + info->add_set.flags, 0, UINT_MAX, 337 + 0, 0, 0, 0); 297 338 ADT_OPT(del_opt, xt_family(par), info->del_set.dim, 298 - info->del_set.flags, 0, UINT_MAX); 339 + info->del_set.flags, 0, UINT_MAX, 340 + 0, 0, 0, 0); 299 341 300 342 if (info->add_set.index != IPSET_INVALID_ID) 301 343 ip_set_add(info->add_set.index, skb, par, &add_opt); ··· 364 402 const struct xt_set_info_target_v2 *info = par->targinfo; 365 403 366 404 ADT_OPT(add_opt, xt_family(par), info->add_set.dim, 367 - info->add_set.flags, info->flags, info->timeout); 405 + info->add_set.flags, info->flags, info->timeout, 406 + 0, 0, 0, 0); 368 407 ADT_OPT(del_opt, xt_family(par), info->del_set.dim, 369 - info->del_set.flags, 0, UINT_MAX); 408 + info->del_set.flags, 0, UINT_MAX, 409 + 0, 0, 0, 0); 370 410 371 411 /* Normalize to fit into jiffies */ 372 412 if (add_opt.ext.timeout != IPSET_NO_TIMEOUT && ··· 396 432 int ret; 397 433 398 434 ADT_OPT(add_opt, xt_family(par), info->add_set.dim, 399 - info->add_set.flags, info->flags, info->timeout); 435 + info->add_set.flags, info->flags, info->timeout, 436 + 0, 0, 0, 0); 400 437 ADT_OPT(del_opt, xt_family(par), info->del_set.dim, 401 - info->del_set.flags, 0, UINT_MAX); 438 + info->del_set.flags, 0, UINT_MAX, 439 + 0, 0, 0, 0); 402 440 ADT_OPT(map_opt, xt_family(par), info->map_set.dim, 403 - info->map_set.flags, 0, UINT_MAX); 441 + info->map_set.flags, 0, UINT_MAX, 442 + 0, 0, 0, 0); 404 443 405 444 /* Normalize to fit into jiffies */ 406 445 if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&