Index: linux-3.8.y/net/netfilter/xt_hashlimit.c =================================================================== --- linux-3.8.y.orig/net/netfilter/xt_hashlimit.c +++ linux-3.8.y/net/netfilter/xt_hashlimit.c @@ -214,6 +214,18 @@ dsthash_free(struct xt_hashlimit_htable } static void htable_gc(unsigned long htlong); +static void +htable_update_cfg(struct xt_hashlimit_htable *hinfo, + const struct xt_hashlimit_mtinfo1 *minfo) +{ + /* copy match config into hashtable config */ + memcpy(&hinfo->cfg, &minfo->cfg, sizeof(hinfo->cfg)); + if (hinfo->cfg.max == 0) + hinfo->cfg.max = 8 * hinfo->cfg.size; + else if (hinfo->cfg.max < hinfo->cfg.size) + hinfo->cfg.max = hinfo->cfg.size; +} + static int htable_create(struct net *net, struct xt_hashlimit_mtinfo1 *minfo, u_int8_t family) { @@ -239,13 +251,8 @@ static int htable_create(struct net *net return -ENOMEM; minfo->hinfo = hinfo; - /* copy match config into hashtable config */ - memcpy(&hinfo->cfg, &minfo->cfg, sizeof(hinfo->cfg)); hinfo->cfg.size = size; - if (hinfo->cfg.max == 0) - hinfo->cfg.max = 8 * hinfo->cfg.size; - else if (hinfo->cfg.max < hinfo->cfg.size) - hinfo->cfg.max = hinfo->cfg.size; + htable_update_cfg(hinfo, minfo); for (i = 0; i < hinfo->cfg.size; i++) INIT_HLIST_HEAD(&hinfo->hash[i]); @@ -318,6 +325,27 @@ static void htable_gc(unsigned long htlo add_timer(&ht->timer); } +static int +htable_update(struct xt_hashlimit_mtinfo1 *minfo, + u_int8_t family) +{ + struct xt_hashlimit_htable *hinfo = minfo->hinfo; + + if (hinfo == NULL) + return -ENOENT; + + if (minfo->cfg.size && hinfo->cfg.size != minfo->cfg.size) + return -EBUSY; + if (hinfo->family != family) + return -EBUSY; + + hinfo->use++; + htable_update_cfg(hinfo, minfo); + htable_selective_cleanup(hinfo, select_all); + + return 0; +} + static void htable_destroy(struct xt_hashlimit_htable *hinfo) { struct hashlimit_net *hashlimit_net = hashlimit_pernet(hinfo->net); @@ -691,20 +719,28 @@ static int hashlimit_mt_check(const stru info->hinfo = htable_find_get(net, info->name, par->family); if (info->hinfo == NULL) { ret = htable_create(net, info, par->family); - if (ret < 0) { - mutex_unlock(&hashlimit_mutex); - return ret; - } + if (ret < 0) + goto err_unlock; + } else { + ret = htable_update(info, par->family); + if (ret < 0) + goto err_unlock; } + mutex_unlock(&hashlimit_mutex); return 0; + +err_unlock: + mutex_unlock(&hashlimit_mutex); + return ret; } static void hashlimit_mt_destroy(const struct xt_mtdtor_param *par) { - const struct xt_hashlimit_mtinfo1 *info = par->matchinfo; + struct xt_hashlimit_mtinfo1 *info = par->matchinfo; htable_put(info->hinfo); + info->hinfo = NULL; } static struct xt_match hashlimit_mt_reg[] __read_mostly = {