<arg choice="opt"><replaceable>family</replaceable></arg>
<replaceable>table</replaceable>
<replaceable>chain</replaceable>
- <arg choice="opt">
- <group choice="req">
- <arg>handle</arg>
- <arg>position</arg>
- </group>
- <replaceable>handle</replaceable>
- </arg>
+ <group choice="opt">
+ <arg>
+ <group choice="req">
+ <arg>handle</arg>
+ <arg>position</arg>
+ </group>
+ <replaceable>handle</replaceable>
+ </arg>
+ <arg>
+ <literal>index</literal>
+ <replaceable>index</replaceable>
+ </arg>
+ </group>
<replaceable>statement</replaceable>...
</cmdsynopsis>
<cmdsynopsis>
Rules are constructed from two kinds of components according to a set
of grammatical rules: expressions and statements.
</para>
+ <para>
+ The <literal>add</literal> and <literal>insert</literal> commands support an optional
+ location specifier, which is either a <replaceable>handle</replaceable> of an existing
+ rule or an absolute <replaceable>index</replaceable> (starting at zero). Internally,
+ rule locations are always identified by <replaceable>handle</replaceable> and the
+ translation from <replaceable>index</replaceable> happens in userspace. This has two
+ potential implications in case a concurrent ruleset change happens after the translation
+ was done: The effective rule index might change if a rule was inserted or deleted before
+ the referred one. If the referred rule was deleted, the command is rejected by the
+ kernel just as if an invalid <replaceable>handle</replaceable> was given.
+ </para>
<variablelist>
<varlistentry>
const char *flowtable;
struct handle_spec handle;
struct position_spec position;
+ struct position_spec index;
uint32_t set_id;
};
return 0;
}
+/* Convert rule's handle.index into handle.position. */
+static int rule_translate_index(struct eval_ctx *ctx, struct rule *rule)
+{
+ struct table *table;
+ struct chain *chain;
+ uint64_t index = 0;
+ struct rule *r;
+ int ret;
+
+ /* update cache with CMD_LIST so that rules are fetched, too */
+ ret = cache_update(ctx->nf_sock, ctx->cache, CMD_LIST,
+ ctx->msgs, ctx->debug_mask, ctx->octx);
+ if (ret < 0)
+ return ret;
+
+ table = table_lookup(&rule->handle, ctx->cache);
+ if (!table)
+ return cmd_error(ctx, &rule->handle.table.location,
+ "Could not process rule: %s",
+ strerror(ENOENT));
+
+ chain = chain_lookup(table, &rule->handle);
+ if (!chain)
+ return cmd_error(ctx, &rule->handle.chain.location,
+ "Could not process rule: %s",
+ strerror(ENOENT));
+
+ list_for_each_entry(r, &chain->rules, list) {
+ if (++index < rule->handle.index.id)
+ continue;
+ rule->handle.position.id = r->handle.handle.id;
+ rule->handle.position.location = rule->handle.index.location;
+ break;
+ }
+ if (!rule->handle.position.id)
+ return cmd_error(ctx, &rule->handle.index.location,
+ "Could not process rule: %s",
+ strerror(EINVAL));
+ return 0;
+}
+
static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule)
{
struct stmt *stmt, *tstmt = NULL;
return -1;
}
+ if (rule->handle.index.id &&
+ rule_translate_index(ctx, rule))
+ return -1;
+
return 0;
}
%token SEED "seed"
%token POSITION "position"
+%token INDEX "index"
%token COMMENT "comment"
%token XML "xml"
%type <cmd> base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd
%destructor { cmd_free($$); } base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd
-%type <handle> table_spec tableid_spec chain_spec chainid_spec flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
-%destructor { handle_free(&$$); } table_spec tableid_spec chain_spec chainid_spec flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec
+%type <handle> table_spec tableid_spec chain_spec chainid_spec flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec index_spec
+%destructor { handle_free(&$$); } table_spec tableid_spec chain_spec chainid_spec flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec index_spec
%type <handle> set_spec setid_spec set_identifier flowtable_identifier obj_spec objid_spec obj_identifier
%destructor { handle_free(&$$); } set_spec setid_spec set_identifier obj_spec objid_spec obj_identifier
%type <val> family_spec family_spec_explicit chain_policy prio_spec
}
;
+index_spec : INDEX NUM
+ {
+ memset(&$$, 0, sizeof($$));
+ $$.index.location = @$;
+ $$.index.id = $2 + 1;
+ }
+ ;
+
rule_position : chain_spec
{
$$ = $1;
handle_merge(&$1, &$2);
$$ = $1;
}
+ | chain_spec index_spec
+ {
+ handle_merge(&$1, &$2);
+ $$ = $1;
+ }
;
ruleid_spec : chain_spec handle_spec
dst->handle = src->handle;
if (dst->position.id == 0)
dst->position = src->position;
+ if (dst->index.id == 0)
+ dst->index = src->index;
}
static int cache_init_tables(struct netlink_ctx *ctx, struct handle *h,
"monitor" { return MONITOR; }
"position" { return POSITION; }
+"index" { return INDEX; }
"comment" { return COMMENT; }
"constant" { return CONSTANT; }