# nft list chain x y
 Error: No such file or directory; did you mean table ‘x’ in family ‘inet’?
 list chain x y
              ^

Signed-off-by: Pablo Neira Ayuso <pa...@netfilter.org>
---
 include/rule.h |  2 ++
 src/evaluate.c | 29 ++++++++++++++++++++---------
 src/rule.c     | 15 +++++++++++++++
 3 files changed, 37 insertions(+), 9 deletions(-)

diff --git a/include/rule.h b/include/rule.h
index a3e0bf117d68..331907a96c62 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -221,6 +221,8 @@ extern void chain_free(struct chain *chain);
 extern void chain_add_hash(struct chain *chain, struct table *table);
 extern struct chain *chain_lookup(const struct table *table,
                                  const struct handle *h);
+extern struct chain *chain_lookup_fuzzy(const struct handle *h,
+                                       const struct nft_cache *cache);
 
 extern const char *family2str(unsigned int family);
 extern const char *hooknum2str(unsigned int family, unsigned int hooknum);
diff --git a/src/evaluate.c b/src/evaluate.c
index 10d88c554e5e..972e62ff1490 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -181,6 +181,21 @@ static int table_not_found(struct eval_ctx *ctx)
                         family2str(table->handle.family));
 }
 
+static int chain_not_found(struct eval_ctx *ctx)
+{
+       struct chain *chain;
+
+       chain = chain_lookup_fuzzy(&ctx->cmd->handle, &ctx->nft->cache);
+       if (chain == NULL)
+               return cmd_error(ctx, &ctx->cmd->handle.chain.location,
+                                "%s", strerror(ENOENT));
+
+       return cmd_error(ctx, &ctx->cmd->handle.chain.location,
+                        "%s; did you mean table ‘%s’ in family ‘%s’?",
+                        strerror(ENOENT), chain->handle.table.name,
+                        family2str(chain->handle.family));
+}
+
 /*
  * Symbol expression: parse symbol and evaluate resulting expression.
  */
@@ -3109,9 +3124,7 @@ static int rule_translate_index(struct eval_ctx *ctx, 
struct rule *rule)
 
        chain = chain_lookup(table, &rule->handle);
        if (!chain)
-               return cmd_error(ctx, &rule->handle.chain.location,
-                               "Could not process rule: %s",
-                               strerror(ENOENT));
+               return chain_not_found(ctx);
 
        list_for_each_entry(r, &chain->rules, list) {
                if (++index < rule->handle.index.id)
@@ -3499,9 +3512,8 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct 
cmd *cmd)
                        return table_not_found(ctx);
 
                if (chain_lookup(table, &cmd->handle) == NULL)
-                       return cmd_error(ctx, &cmd->handle.chain.location,
-                                        "Could not process rule: %s",
-                                        strerror(ENOENT));
+                       return chain_not_found(ctx);
+
                return 0;
        case CMD_OBJ_QUOTA:
                return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_QUOTA);
@@ -3646,9 +3658,8 @@ static int cmd_evaluate_rename(struct eval_ctx *ctx, 
struct cmd *cmd)
                        return table_not_found(ctx);
 
                if (chain_lookup(table, &ctx->cmd->handle) == NULL)
-                       return cmd_error(ctx, &ctx->cmd->handle.chain.location,
-                                        "Could not process rule: %s",
-                                        strerror(ENOENT));
+                       return chain_not_found(ctx);
+
                break;
        default:
                BUG("invalid command object type %u\n", cmd->obj);
diff --git a/src/rule.c b/src/rule.c
index 3553b43def06..3ad31d7224a6 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -762,6 +762,21 @@ struct chain *chain_lookup(const struct table *table, 
const struct handle *h)
        return NULL;
 }
 
+struct chain *chain_lookup_fuzzy(const struct handle *h,
+                                const struct nft_cache *cache)
+{
+       struct table *table;
+       struct chain *chain;
+
+       list_for_each_entry(table, &cache->list, list) {
+               list_for_each_entry(chain, &table->chains, list) {
+                       if (!strcmp(chain->handle.chain.name, h->chain.name))
+                               return chain;
+               }
+       }
+       return NULL;
+}
+
 const char *family2str(unsigned int family)
 {
        switch (family) {
-- 
2.11.0

Reply via email to