linux/net/netfilter/ipset/ip_set_list_set.c
<<
>>
Prefs
   1/* Copyright (C) 2008-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
   2 *
   3 * This program is free software; you can redistribute it and/or modify
   4 * it under the terms of the GNU General Public License version 2 as
   5 * published by the Free Software Foundation.
   6 */
   7
   8/* Kernel module implementing an IP set type: the list:set type */
   9
  10#include <linux/module.h>
  11#include <linux/ip.h>
  12#include <linux/skbuff.h>
  13#include <linux/errno.h>
  14
  15#include <linux/netfilter/ipset/ip_set.h>
  16#include <linux/netfilter/ipset/ip_set_timeout.h>
  17#include <linux/netfilter/ipset/ip_set_list.h>
  18
  19#define REVISION_MIN    0
  20#define REVISION_MAX    0
  21
  22MODULE_LICENSE("GPL");
  23MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
  24IP_SET_MODULE_DESC("list:set", REVISION_MIN, REVISION_MAX);
  25MODULE_ALIAS("ip_set_list:set");
  26
  27/* Member elements without and with timeout */
  28struct set_elem {
  29        ip_set_id_t id;
  30};
  31
  32struct set_telem {
  33        ip_set_id_t id;
  34        unsigned long timeout;
  35};
  36
  37/* Type structure */
  38struct list_set {
  39        size_t dsize;           /* element size */
  40        u32 size;               /* size of set list array */
  41        u32 timeout;            /* timeout value */
  42        struct timer_list gc;   /* garbage collection */
  43        struct set_elem members[0]; /* the set members */
  44};
  45
  46static inline struct set_elem *
  47list_set_elem(const struct list_set *map, u32 id)
  48{
  49        return (struct set_elem *)((void *)map->members + id * map->dsize);
  50}
  51
  52static inline struct set_telem *
  53list_set_telem(const struct list_set *map, u32 id)
  54{
  55        return (struct set_telem *)((void *)map->members + id * map->dsize);
  56}
  57
  58static inline bool
  59list_set_timeout(const struct list_set *map, u32 id)
  60{
  61        const struct set_telem *elem = list_set_telem(map, id);
  62
  63        return ip_set_timeout_test(elem->timeout);
  64}
  65
  66static inline bool
  67list_set_expired(const struct list_set *map, u32 id)
  68{
  69        const struct set_telem *elem = list_set_telem(map, id);
  70
  71        return ip_set_timeout_expired(elem->timeout);
  72}
  73
  74/* Set list without and with timeout */
  75
  76static int
  77list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
  78              const struct xt_action_param *par,
  79              enum ipset_adt adt, const struct ip_set_adt_opt *opt)
  80{
  81        struct list_set *map = set->data;
  82        struct set_elem *elem;
  83        u32 i;
  84        int ret;
  85
  86        for (i = 0; i < map->size; i++) {
  87                elem = list_set_elem(map, i);
  88                if (elem->id == IPSET_INVALID_ID)
  89                        return 0;
  90                if (with_timeout(map->timeout) && list_set_expired(map, i))
  91                        continue;
  92                switch (adt) {
  93                case IPSET_TEST:
  94                        ret = ip_set_test(elem->id, skb, par, opt);
  95                        if (ret > 0)
  96                                return ret;
  97                        break;
  98                case IPSET_ADD:
  99                        ret = ip_set_add(elem->id, skb, par, opt);
 100                        if (ret == 0)
 101                                return ret;
 102                        break;
 103                case IPSET_DEL:
 104                        ret = ip_set_del(elem->id, skb, par, opt);
 105                        if (ret == 0)
 106                                return ret;
 107                        break;
 108                default:
 109                        break;
 110                }
 111        }
 112        return -EINVAL;
 113}
 114
 115static bool
 116id_eq(const struct list_set *map, u32 i, ip_set_id_t id)
 117{
 118        const struct set_elem *elem;
 119
 120        if (i < map->size) {
 121                elem = list_set_elem(map, i);
 122                return elem->id == id;
 123        }
 124
 125        return 0;
 126}
 127
 128static bool
 129id_eq_timeout(const struct list_set *map, u32 i, ip_set_id_t id)
 130{
 131        const struct set_elem *elem;
 132
 133        if (i < map->size) {
 134                elem = list_set_elem(map, i);
 135                return !!(elem->id == id &&
 136                          !(with_timeout(map->timeout) &&
 137                            list_set_expired(map, i)));
 138        }
 139
 140        return 0;
 141}
 142
 143static void
 144list_elem_add(struct list_set *map, u32 i, ip_set_id_t id)
 145{
 146        struct set_elem *e;
 147
 148        for (; i < map->size; i++) {
 149                e = list_set_elem(map, i);
 150                swap(e->id, id);
 151                if (e->id == IPSET_INVALID_ID)
 152                        break;
 153        }
 154}
 155
 156static void
 157list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id,
 158               unsigned long timeout)
 159{
 160        struct set_telem *e;
 161
 162        for (; i < map->size; i++) {
 163                e = list_set_telem(map, i);
 164                swap(e->id, id);
 165                swap(e->timeout, timeout);
 166                if (e->id == IPSET_INVALID_ID)
 167                        break;
 168        }
 169}
 170
 171static int
 172list_set_add(struct list_set *map, u32 i, ip_set_id_t id,
 173             unsigned long timeout)
 174{
 175        const struct set_elem *e = list_set_elem(map, i);
 176
 177        if (i == map->size - 1 && e->id != IPSET_INVALID_ID)
 178                /* Last element replaced: e.g. add new,before,last */
 179                ip_set_put_byindex(e->id);
 180        if (with_timeout(map->timeout))
 181                list_elem_tadd(map, i, id, ip_set_timeout_set(timeout));
 182        else
 183                list_elem_add(map, i, id);
 184
 185        return 0;
 186}
 187
 188static int
 189list_set_del(struct list_set *map, u32 i)
 190{
 191        struct set_elem *a = list_set_elem(map, i), *b;
 192
 193        ip_set_put_byindex(a->id);
 194
 195        for (; i < map->size - 1; i++) {
 196                b = list_set_elem(map, i + 1);
 197                a->id = b->id;
 198                if (with_timeout(map->timeout))
 199                        ((struct set_telem *)a)->timeout =
 200                                ((struct set_telem *)b)->timeout;
 201                a = b;
 202                if (a->id == IPSET_INVALID_ID)
 203                        break;
 204        }
 205        /* Last element */
 206        a->id = IPSET_INVALID_ID;
 207        return 0;
 208}
 209
 210static void
 211cleanup_entries(struct list_set *map)
 212{
 213        struct set_telem *e;
 214        u32 i;
 215
 216        for (i = 0; i < map->size; i++) {
 217                e = list_set_telem(map, i);
 218                if (e->id != IPSET_INVALID_ID && list_set_expired(map, i))
 219                        list_set_del(map, i);
 220        }
 221}
 222
 223static int
 224list_set_uadt(struct ip_set *set, struct nlattr *tb[],
 225              enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 226{
 227        struct list_set *map = set->data;
 228        bool with_timeout = with_timeout(map->timeout);
 229        bool flag_exist = flags & IPSET_FLAG_EXIST;
 230        int before = 0;
 231        u32 timeout = map->timeout;
 232        ip_set_id_t id, refid = IPSET_INVALID_ID;
 233        const struct set_elem *elem;
 234        struct ip_set *s;
 235        u32 i;
 236        int ret = 0;
 237
 238        if (unlikely(!tb[IPSET_ATTR_NAME] ||
 239                     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
 240                     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
 241                return -IPSET_ERR_PROTOCOL;
 242
 243        if (tb[IPSET_ATTR_LINENO])
 244                *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 245
 246        id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
 247        if (id == IPSET_INVALID_ID)
 248                return -IPSET_ERR_NAME;
 249        /* "Loop detection" */
 250        if (s->type->features & IPSET_TYPE_NAME) {
 251                ret = -IPSET_ERR_LOOP;
 252                goto finish;
 253        }
 254
 255        if (tb[IPSET_ATTR_CADT_FLAGS]) {
 256                u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
 257                before = f & IPSET_FLAG_BEFORE;
 258        }
 259
 260        if (before && !tb[IPSET_ATTR_NAMEREF]) {
 261                ret = -IPSET_ERR_BEFORE;
 262                goto finish;
 263        }
 264
 265        if (tb[IPSET_ATTR_NAMEREF]) {
 266                refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
 267                                          &s);
 268                if (refid == IPSET_INVALID_ID) {
 269                        ret = -IPSET_ERR_NAMEREF;
 270                        goto finish;
 271                }
 272                if (!before)
 273                        before = -1;
 274        }
 275        if (tb[IPSET_ATTR_TIMEOUT]) {
 276                if (!with_timeout) {
 277                        ret = -IPSET_ERR_TIMEOUT;
 278                        goto finish;
 279                }
 280                timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 281        }
 282        if (with_timeout && adt != IPSET_TEST)
 283                cleanup_entries(map);
 284
 285        switch (adt) {
 286        case IPSET_TEST:
 287                for (i = 0; i < map->size && !ret; i++) {
 288                        elem = list_set_elem(map, i);
 289                        if (elem->id == IPSET_INVALID_ID ||
 290                            (before != 0 && i + 1 >= map->size))
 291                                break;
 292                        else if (with_timeout && list_set_expired(map, i))
 293                                continue;
 294                        else if (before > 0 && elem->id == id)
 295                                ret = id_eq_timeout(map, i + 1, refid);
 296                        else if (before < 0 && elem->id == refid)
 297                                ret = id_eq_timeout(map, i + 1, id);
 298                        else if (before == 0 && elem->id == id)
 299                                ret = 1;
 300                }
 301                break;
 302        case IPSET_ADD:
 303                for (i = 0; i < map->size; i++) {
 304                        elem = list_set_elem(map, i);
 305                        if (elem->id != id)
 306                                continue;
 307                        if (!(with_timeout && flag_exist)) {
 308                                ret = -IPSET_ERR_EXIST;
 309                                goto finish;
 310                        } else {
 311                                struct set_telem *e = list_set_telem(map, i);
 312
 313                                if ((before > 1 &&
 314                                     !id_eq(map, i + 1, refid)) ||
 315                                    (before < 0 &&
 316                                     (i == 0 || !id_eq(map, i - 1, refid)))) {
 317                                        ret = -IPSET_ERR_EXIST;
 318                                        goto finish;
 319                                }
 320                                e->timeout = ip_set_timeout_set(timeout);
 321                                ip_set_put_byindex(id);
 322                                ret = 0;
 323                                goto finish;
 324                        }
 325                }
 326                ret = -IPSET_ERR_LIST_FULL;
 327                for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
 328                        elem = list_set_elem(map, i);
 329                        if (elem->id == IPSET_INVALID_ID)
 330                                ret = before != 0 ? -IPSET_ERR_REF_EXIST
 331                                        : list_set_add(map, i, id, timeout);
 332                        else if (elem->id != refid)
 333                                continue;
 334                        else if (before > 0)
 335                                ret = list_set_add(map, i, id, timeout);
 336                        else if (i + 1 < map->size)
 337                                ret = list_set_add(map, i + 1, id, timeout);
 338                }
 339                break;
 340        case IPSET_DEL:
 341                ret = -IPSET_ERR_EXIST;
 342                for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) {
 343                        elem = list_set_elem(map, i);
 344                        if (elem->id == IPSET_INVALID_ID) {
 345                                ret = before != 0 ? -IPSET_ERR_REF_EXIST
 346                                                  : -IPSET_ERR_EXIST;
 347                                break;
 348                        } else if (elem->id == id &&
 349                                   (before == 0 ||
 350                                    (before > 0 && id_eq(map, i + 1, refid))))
 351                                ret = list_set_del(map, i);
 352                        else if (elem->id == refid &&
 353                                 before < 0 && id_eq(map, i + 1, id))
 354                                ret = list_set_del(map, i + 1);
 355                }
 356                break;
 357        default:
 358                break;
 359        }
 360
 361finish:
 362        if (refid != IPSET_INVALID_ID)
 363                ip_set_put_byindex(refid);
 364        if (adt != IPSET_ADD || ret)
 365                ip_set_put_byindex(id);
 366
 367        return ip_set_eexist(ret, flags) ? 0 : ret;
 368}
 369
 370static void
 371list_set_flush(struct ip_set *set)
 372{
 373        struct list_set *map = set->data;
 374        struct set_elem *elem;
 375        u32 i;
 376
 377        for (i = 0; i < map->size; i++) {
 378                elem = list_set_elem(map, i);
 379                if (elem->id != IPSET_INVALID_ID) {
 380                        ip_set_put_byindex(elem->id);
 381                        elem->id = IPSET_INVALID_ID;
 382                }
 383        }
 384}
 385
 386static void
 387list_set_destroy(struct ip_set *set)
 388{
 389        struct list_set *map = set->data;
 390
 391        if (with_timeout(map->timeout))
 392                del_timer_sync(&map->gc);
 393        list_set_flush(set);
 394        kfree(map);
 395
 396        set->data = NULL;
 397}
 398
 399static int
 400list_set_head(struct ip_set *set, struct sk_buff *skb)
 401{
 402        const struct list_set *map = set->data;
 403        struct nlattr *nested;
 404
 405        nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
 406        if (!nested)
 407                goto nla_put_failure;
 408        if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) ||
 409            (with_timeout(map->timeout) &&
 410             nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) ||
 411            nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
 412            nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
 413                          htonl(sizeof(*map) + map->size * map->dsize)))
 414                goto nla_put_failure;
 415        ipset_nest_end(skb, nested);
 416
 417        return 0;
 418nla_put_failure:
 419        return -EMSGSIZE;
 420}
 421
 422static int
 423list_set_list(const struct ip_set *set,
 424              struct sk_buff *skb, struct netlink_callback *cb)
 425{
 426        const struct list_set *map = set->data;
 427        struct nlattr *atd, *nested;
 428        u32 i, first = cb->args[2];
 429        const struct set_elem *e;
 430
 431        atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
 432        if (!atd)
 433                return -EMSGSIZE;
 434        for (; cb->args[2] < map->size; cb->args[2]++) {
 435                i = cb->args[2];
 436                e = list_set_elem(map, i);
 437                if (e->id == IPSET_INVALID_ID)
 438                        goto finish;
 439                if (with_timeout(map->timeout) && list_set_expired(map, i))
 440                        continue;
 441                nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
 442                if (!nested) {
 443                        if (i == first) {
 444                                nla_nest_cancel(skb, atd);
 445                                return -EMSGSIZE;
 446                        } else
 447                                goto nla_put_failure;
 448                }
 449                if (nla_put_string(skb, IPSET_ATTR_NAME,
 450                                   ip_set_name_byindex(e->id)))
 451                        goto nla_put_failure;
 452                if (with_timeout(map->timeout)) {
 453                        const struct set_telem *te =
 454                                (const struct set_telem *) e;
 455                        __be32 to = htonl(ip_set_timeout_get(te->timeout));
 456                        if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT, to))
 457                                goto nla_put_failure;
 458                }
 459                ipset_nest_end(skb, nested);
 460        }
 461finish:
 462        ipset_nest_end(skb, atd);
 463        /* Set listing finished */
 464        cb->args[2] = 0;
 465        return 0;
 466
 467nla_put_failure:
 468        nla_nest_cancel(skb, nested);
 469        ipset_nest_end(skb, atd);
 470        if (unlikely(i == first)) {
 471                cb->args[2] = 0;
 472                return -EMSGSIZE;
 473        }
 474        return 0;
 475}
 476
 477static bool
 478list_set_same_set(const struct ip_set *a, const struct ip_set *b)
 479{
 480        const struct list_set *x = a->data;
 481        const struct list_set *y = b->data;
 482
 483        return x->size == y->size &&
 484               x->timeout == y->timeout;
 485}
 486
 487static const struct ip_set_type_variant list_set = {
 488        .kadt   = list_set_kadt,
 489        .uadt   = list_set_uadt,
 490        .destroy = list_set_destroy,
 491        .flush  = list_set_flush,
 492        .head   = list_set_head,
 493        .list   = list_set_list,
 494        .same_set = list_set_same_set,
 495};
 496
 497static void
 498list_set_gc(unsigned long ul_set)
 499{
 500        struct ip_set *set = (struct ip_set *) ul_set;
 501        struct list_set *map = set->data;
 502
 503        write_lock_bh(&set->lock);
 504        cleanup_entries(map);
 505        write_unlock_bh(&set->lock);
 506
 507        map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
 508        add_timer(&map->gc);
 509}
 510
 511static void
 512list_set_gc_init(struct ip_set *set)
 513{
 514        struct list_set *map = set->data;
 515
 516        init_timer(&map->gc);
 517        map->gc.data = (unsigned long) set;
 518        map->gc.function = list_set_gc;
 519        map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
 520        add_timer(&map->gc);
 521}
 522
 523/* Create list:set type of sets */
 524
 525static bool
 526init_list_set(struct ip_set *set, u32 size, size_t dsize,
 527              unsigned long timeout)
 528{
 529        struct list_set *map;
 530        struct set_elem *e;
 531        u32 i;
 532
 533        map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL);
 534        if (!map)
 535                return false;
 536
 537        map->size = size;
 538        map->dsize = dsize;
 539        map->timeout = timeout;
 540        set->data = map;
 541
 542        for (i = 0; i < size; i++) {
 543                e = list_set_elem(map, i);
 544                e->id = IPSET_INVALID_ID;
 545        }
 546
 547        return true;
 548}
 549
 550static int
 551list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 552{
 553        u32 size = IP_SET_LIST_DEFAULT_SIZE;
 554
 555        if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
 556                     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
 557                return -IPSET_ERR_PROTOCOL;
 558
 559        if (tb[IPSET_ATTR_SIZE])
 560                size = ip_set_get_h32(tb[IPSET_ATTR_SIZE]);
 561        if (size < IP_SET_LIST_MIN_SIZE)
 562                size = IP_SET_LIST_MIN_SIZE;
 563
 564        if (tb[IPSET_ATTR_TIMEOUT]) {
 565                if (!init_list_set(set, size, sizeof(struct set_telem),
 566                                   ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT])))
 567                        return -ENOMEM;
 568
 569                list_set_gc_init(set);
 570        } else {
 571                if (!init_list_set(set, size, sizeof(struct set_elem),
 572                                   IPSET_NO_TIMEOUT))
 573                        return -ENOMEM;
 574        }
 575        set->variant = &list_set;
 576        return 0;
 577}
 578
 579static struct ip_set_type list_set_type __read_mostly = {
 580        .name           = "list:set",
 581        .protocol       = IPSET_PROTOCOL,
 582        .features       = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
 583        .dimension      = IPSET_DIM_ONE,
 584        .family         = NFPROTO_UNSPEC,
 585        .revision_min   = REVISION_MIN,
 586        .revision_max   = REVISION_MAX,
 587        .create         = list_set_create,
 588        .create_policy  = {
 589                [IPSET_ATTR_SIZE]       = { .type = NLA_U32 },
 590                [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
 591        },
 592        .adt_policy     = {
 593                [IPSET_ATTR_NAME]       = { .type = NLA_STRING,
 594                                            .len = IPSET_MAXNAMELEN },
 595                [IPSET_ATTR_NAMEREF]    = { .type = NLA_STRING,
 596                                            .len = IPSET_MAXNAMELEN },
 597                [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
 598                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
 599                [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
 600        },
 601        .me             = THIS_MODULE,
 602};
 603
 604static int __init
 605list_set_init(void)
 606{
 607        return ip_set_type_register(&list_set_type);
 608}
 609
 610static void __exit
 611list_set_fini(void)
 612{
 613        ip_set_type_unregister(&list_set_type);
 614}
 615
 616module_init(list_set_init);
 617module_exit(list_set_fini);
 618