static int list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext, struct ip_set_ext *mext, u32 flags) { struct list_set *map = set->data; struct set_adt_elem *d = value; struct set_elem *e; bool flag_exist = flags & IPSET_FLAG_EXIST; u32 i, ret = 0; if (SET_WITH_TIMEOUT(set)) set_cleanup_entries(set); /* Check already added element */ for (i = 0; i < map->size; i++) { e = list_set_elem(set, map, i); if (e->id == IPSET_INVALID_ID) goto insert; else if (e->id != d->id) continue; if ((d->before > 1 && !id_eq(set, i + 1, d->refid)) || (d->before < 0 && (i == 0 || !id_eq(set, i - 1, d->refid)))) /* Before/after doesn't match */ return -IPSET_ERR_REF_EXIST; if (!flag_exist) /* Can't re-add */ return -IPSET_ERR_EXIST; /* Update extensions */ ip_set_ext_destroy(set, e); if (SET_WITH_TIMEOUT(set)) ip_set_timeout_set(ext_timeout(e, set), ext->timeout); if (SET_WITH_COUNTER(set)) ip_set_init_counter(ext_counter(e, set), ext); if (SET_WITH_COMMENT(set)) ip_set_init_comment(ext_comment(e, set), ext); /* Set is already added to the list */ ip_set_put_byindex(map->net, d->id); return 0; } insert: ret = -IPSET_ERR_LIST_FULL; for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) { e = list_set_elem(set, map, i); if (e->id == IPSET_INVALID_ID) ret = d->before != 0 ? -IPSET_ERR_REF_EXIST : list_set_add(set, i, d, ext); else if (e->id != d->refid) continue; else if (d->before > 0) ret = list_set_add(set, i, d, ext); else if (i + 1 < map->size) ret = list_set_add(set, i + 1, d, ext); } return ret; }
static int list_set_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { struct list_set *map = set->data; bool with_timeout = with_timeout(map->timeout); bool flag_exist = flags & IPSET_FLAG_EXIST; int before = 0; u32 timeout = map->timeout; ip_set_id_t id, refid = IPSET_INVALID_ID; const struct set_elem *elem; struct ip_set *s; u32 i; int ret = 0; if (unlikely(!tb[IPSET_ATTR_NAME] || !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) return -IPSET_ERR_PROTOCOL; if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s); if (id == IPSET_INVALID_ID) return -IPSET_ERR_NAME; /* "Loop detection" */ if (s->type->features & IPSET_TYPE_NAME) { ret = -IPSET_ERR_LOOP; goto finish; } if (tb[IPSET_ATTR_CADT_FLAGS]) { u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); before = f & IPSET_FLAG_BEFORE; } if (before && !tb[IPSET_ATTR_NAMEREF]) { ret = -IPSET_ERR_BEFORE; goto finish; } if (tb[IPSET_ATTR_NAMEREF]) { refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]), &s); if (refid == IPSET_INVALID_ID) { ret = -IPSET_ERR_NAMEREF; goto finish; } if (!before) before = -1; } if (tb[IPSET_ATTR_TIMEOUT]) { if (!with_timeout) { ret = -IPSET_ERR_TIMEOUT; goto finish; } timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } if (with_timeout && adt != IPSET_TEST) cleanup_entries(map); switch (adt) { case IPSET_TEST: for (i = 0; i < map->size && !ret; i++) { elem = list_set_elem(map, i); if (elem->id == IPSET_INVALID_ID || (before != 0 && i + 1 >= map->size)) break; else if (with_timeout && list_set_expired(map, i)) continue; else if (before > 0 && elem->id == id) ret = id_eq_timeout(map, i + 1, refid); else if (before < 0 && elem->id == refid) ret = id_eq_timeout(map, i + 1, id); else if (before == 0 && elem->id == id) ret = 1; } break; case IPSET_ADD: for (i = 0; i < map->size; i++) { elem = list_set_elem(map, i); if (elem->id != id) continue; if (!(with_timeout && flag_exist)) { ret = -IPSET_ERR_EXIST; goto finish; } else { struct set_telem *e = list_set_telem(map, i); if ((before > 1 && !id_eq(map, i + 1, refid)) || (before < 0 && (i == 0 || !id_eq(map, i - 1, refid)))) { ret = -IPSET_ERR_EXIST; goto finish; } e->timeout = ip_set_timeout_set(timeout); ip_set_put_byindex(id); ret = 0; goto finish; } } ret = -IPSET_ERR_LIST_FULL; for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) { elem = list_set_elem(map, i); if (elem->id == IPSET_INVALID_ID) ret = before != 0 ? -IPSET_ERR_REF_EXIST : list_set_add(map, i, id, timeout); else if (elem->id != refid) continue; else if (before > 0) ret = list_set_add(map, i, id, timeout); else if (i + 1 < map->size) ret = list_set_add(map, i + 1, id, timeout); } break; case IPSET_DEL: ret = -IPSET_ERR_EXIST; for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) { elem = list_set_elem(map, i); if (elem->id == IPSET_INVALID_ID) { ret = before != 0 ? -IPSET_ERR_REF_EXIST : -IPSET_ERR_EXIST; break; } else if (elem->id == id && (before == 0 || (before > 0 && id_eq(map, i + 1, refid)))) ret = list_set_del(map, i); else if (elem->id == refid && before < 0 && id_eq(map, i + 1, id)) ret = list_set_del(map, i + 1); } break; default: break; } finish: if (refid != IPSET_INVALID_ID) ip_set_put_byindex(refid); if (adt != IPSET_ADD || ret) ip_set_put_byindex(id); return ip_set_eexist(ret, flags) ? 0 : ret; }