Right now this object merely contains the nftables version and release
name as well as a JSON schema version, but it could be extended
arbitrarily. In the future, this will also allow for non-compatible
schema changes should the need for this arise.

Adjust the parser to accept metainfo objects and make it verify
json_schema_version to be less than or equal to the one hard-coded in
the library.

Signed-off-by: Phil Sutter <[email protected]>
---
 doc/libnftables-json.adoc | 25 +++++++++++++++++++++++--
 include/json.h            |  2 ++
 src/json.c                | 19 ++++++++++++++++---
 src/parser_json.c         | 27 +++++++++++++++++++++++++++
 4 files changed, 68 insertions(+), 5 deletions(-)

diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc
index c174a35487d46..59bac17fd0051 100644
--- a/doc/libnftables-json.adoc
+++ b/doc/libnftables-json.adoc
@@ -16,13 +16,14 @@ libnftables-json - Supported JSON schema by libnftables
 
 'CMD_OBJECTS' := 'CMD_OBJECT' [ *,* 'CMD_OBJECTS' ]
 
-'CMD_OBJECT' := *{* 'CMD'*:* 'LIST_OBJECT' *}*
+'CMD_OBJECT' := *{* 'CMD'*:* 'LIST_OBJECT' *}* | 'METAINFO_OBJECT'
 
 'CMD' := *"add"* | *"replace"* | *"create"* | *"insert"* | *"delete"* |
          *"list"* | *"reset"* | *"flush"* | *"rename"*
 
 'LIST_OBJECT' := 'TABLE' | 'CHAIN' | 'RULE' | 'SET' | 'MAP' | 'ELEMENT' |
-                'FLOWTABLE' | 'COUNTER' | 'QUOTA' | 'CT_HELPER' | 'LIMIT'
+                'FLOWTABLE' | 'COUNTER' | 'QUOTA' | 'CT_HELPER' | 'LIMIT' |
+                'METAINFO_OBJECT'
 
 == DESCRIPTION
 libnftables supports JSON formatted input and output. This is implemented as an
@@ -47,6 +48,26 @@ It's value is a ruleset element - basically identical to 
output elements apart
 from certain properties which may be interpreted differently or are required
 when output generally omits them.
 
+== METAINFO OBJECT
+In output, the first object in *nftables* array is a special one containing
+library information. Its content is as follows:
+
+[verse]
+*{ "metainfo": {
+       "version":* 'STRING'*,
+       "release_name":* 'STRING'*,
+       "json_schema_version":* 'NUMBER'
+*}}*
+
+The values of *version* and *release_name* properties are equal to the package
+version and release name as printed by *nft -v*. The value of
+*json_schema_version* property is an integer indicating the schema version.
+
+If supplied in library input, the parser will verify *json_schema_version* 
value
+to not exceed the internally hardcoded one (to make sure the given schema is
+fully understood). In future, a lower number than the internal one may activate
+compatibility mode to parse outdated and incompatible JSON input.
+
 == COMMAND OBJECTS
 The structure accepts an arbitrary amount of commands which are interpreted in
 order of appearance. For instance, the following standard syntax input:
diff --git a/include/json.h b/include/json.h
index b75512b8475fb..7967dd20fc83e 100644
--- a/include/json.h
+++ b/include/json.h
@@ -15,6 +15,8 @@ struct table;
 
 #ifdef HAVE_LIBJANSSON
 
+#define JSON_SCHEMA_VERSION 1
+
 #include <jansson.h>
 
 json_t *binop_expr_json(const struct expr *expr, struct output_ctx *octx);
diff --git a/src/json.c b/src/json.c
index 199ca0a308bcd..8d55885fa3916 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1499,6 +1499,14 @@ static json_t *do_list_flowtables_json(struct 
netlink_ctx *ctx, struct cmd *cmd)
        return root;
 }
 
+static json_t *generate_json_metainfo(void)
+{
+       return json_pack("{s: {s:s, s:s, s:i}}", "metainfo",
+                        "version", PACKAGE_VERSION,
+                        "release_name", RELEASE_NAME,
+                        "json_schema_version", JSON_SCHEMA_VERSION);
+}
+
 int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd)
 {
        struct table *table = NULL;
@@ -1565,10 +1573,15 @@ int do_command_list_json(struct netlink_ctx *ctx, 
struct cmd *cmd)
                BUG("invalid command object type %u\n", cmd->obj);
        }
 
-       if (json_is_array(root) && !json_array_size(root)) {
-               json_decref(root);
-               root = json_null();
+       if (!json_is_array(root)) {
+               json_t *tmp = json_array();
+
+               json_array_append_new(tmp, root);
+               root = tmp;
        }
+
+       json_array_insert_new(root, 0, generate_json_metainfo());
+
        root = json_pack("{s:o}", "nftables", root);
        json_dumpf(root, ctx->octx->output_fp, 0);
        json_decref(root);
diff --git a/src/parser_json.c b/src/parser_json.c
index 404dbe9fa82a6..ce85b798e908e 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -3060,6 +3060,22 @@ static struct cmd *json_parse_cmd(struct json_ctx *ctx, 
json_t *root)
        return NULL;
 }
 
+static int json_verify_metainfo(struct json_ctx *ctx, json_t *root)
+{
+       int schema_version;
+
+       if (!json_unpack(root, "{s:i}", "json_schema_version", &schema_version))
+                       return 0;
+
+       if (schema_version > JSON_SCHEMA_VERSION) {
+               json_error(ctx, "Schema version %d not supported, maximum 
supported version is %d\n",
+                          schema_version, JSON_SCHEMA_VERSION);
+               return 1;
+       }
+
+       return 0;
+}
+
 static int __json_parse(struct json_ctx *ctx, json_t *root)
 {
        struct eval_ctx ectx = {
@@ -3084,11 +3100,22 @@ static int __json_parse(struct json_ctx *ctx, json_t 
*root)
                /* this is more or less from parser_bison.y:716 */
                LIST_HEAD(list);
                struct cmd *cmd;
+               json_t *tmp2;
 
                if (!json_is_object(value)) {
                        json_error(ctx, "Unexpected command array element of 
type %s, expected object.", json_typename(value));
                        return -1;
                }
+
+               tmp2 = json_object_get(value, "metainfo");
+               if (tmp2) {
+                       if (json_verify_metainfo(ctx, tmp2)) {
+                               json_error(ctx, "Metainfo verification 
failed.");
+                               return -1;
+                       }
+                       continue;
+               }
+
                cmd = json_parse_cmd(ctx, value);
 
                if (!cmd) {
-- 
2.18.0

Reply via email to