@@ -165,4 +165,13 @@ struct netlink_mon_handler {
extern int netlink_monitor(struct netlink_mon_handler *monhandler);
bool netlink_batch_supported(void);
+struct ruleset_parse {
+ struct netlink_ctx *nl_ctx;
+ struct cmd *cmd;
+};
+
+struct nft_parse_ctx;
+
+int netlink_ruleset_parse_cb(const struct nft_parse_ctx *ctx);
+
#endif /* NFTABLES_NETLINK_H */
@@ -221,6 +221,7 @@ extern void set_print_plain(const struct set *s);
* @CMD_LIST: list container
* @CMD_FLUSH: flush container
* @CMD_RENAME: rename object
+ * @CMD_IMPORT: import a ruleset in a given format
* @CMD_EXPORT: export the ruleset in a given format
* @CMD_MONITOR: event listener
* @CMD_DESCRIBE: describe an expression
@@ -234,6 +235,7 @@ enum cmd_ops {
CMD_LIST,
CMD_FLUSH,
CMD_RENAME,
+ CMD_IMPORT,
CMD_EXPORT,
CMD_MONITOR,
CMD_DESCRIBE,
@@ -252,7 +254,7 @@ enum cmd_ops {
* @CMD_OBJ_RULESET: ruleset
* @CMD_OBJ_EXPR: expression
* @CMD_OBJ_MONITOR: monitor
- * @CMD_OBJ_EXPORT: export
+ * @CMD_OBJ_MARKUP: import/export
*/
enum cmd_obj {
CMD_OBJ_INVALID,
@@ -265,15 +267,15 @@ enum cmd_obj {
CMD_OBJ_RULESET,
CMD_OBJ_EXPR,
CMD_OBJ_MONITOR,
- CMD_OBJ_EXPORT,
+ CMD_OBJ_MARKUP,
};
-struct export {
+struct markup {
uint32_t format;
};
-struct export *export_alloc(uint32_t format);
-void export_free(struct export *e);
+struct markup *markup_alloc(uint32_t format);
+void markup_free(struct markup *m);
enum {
CMD_MONITOR_OBJ_ANY,
@@ -324,7 +326,7 @@ struct cmd {
struct chain *chain;
struct table *table;
struct monitor *monitor;
- struct export *export;
+ struct markup *markup;
};
const void *arg;
};
@@ -1966,6 +1966,7 @@ int cmd_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
case CMD_LIST:
case CMD_FLUSH:
case CMD_RENAME:
+ case CMD_IMPORT:
case CMD_EXPORT:
case CMD_DESCRIBE:
return 0;
@@ -22,6 +22,7 @@
#include <libnftnl/expr.h>
#include <libnftnl/set.h>
#include <libnftnl/common.h>
+#include <libnftnl/ruleset.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter.h>
@@ -2061,6 +2062,295 @@ int netlink_monitor(struct netlink_mon_handler *monhandler)
monhandler);
}
+static int netlink_build_setelems_nlmsg(const struct nft_parse_ctx *ctx)
+{
+ const struct ruleset_parse *rp;
+ struct nft_set *set;
+ uint32_t cmd;
+ int ret = -1;
+
+ set = nft_ruleset_ctx_get(ctx, NFT_RULESET_CTX_SET);
+ rp = nft_ruleset_ctx_get(ctx, NFT_RULESET_CTX_DATA);
+
+ cmd = nft_ruleset_ctx_get_u32(ctx, NFT_RULESET_CTX_CMD);
+ switch (cmd) {
+ case NFT_CMD_ADD:
+ ret = mnl_nft_setelem_batch_add(set, 0, rp->nl_ctx->seqnum);
+ break;
+ case NFT_CMD_DELETE:
+ ret = mnl_nft_setelem_batch_del(set, 0, rp->nl_ctx->seqnum);
+ break;
+ default:
+ errno = EOPNOTSUPP;
+ break;
+ }
+
+ if (ret < 0)
+ netlink_io_error(rp->nl_ctx, &rp->cmd->location,
+ "Could not import set_elems: %s",
+ strerror(errno));
+
+ return ret;
+}
+
+static int netlink_build_set_nlmsg(const struct nft_parse_ctx *ctx)
+{
+ const struct ruleset_parse *rp;
+ struct nft_set *set;
+ uint32_t cmd;
+ int ret = -1;
+
+ set = nft_ruleset_ctx_get(ctx, NFT_RULESET_CTX_SET);
+ rp = nft_ruleset_ctx_get(ctx, NFT_RULESET_CTX_DATA);
+
+ cmd = nft_ruleset_ctx_get_u32(ctx, NFT_RULESET_CTX_CMD);
+ switch (cmd) {
+ case NFT_CMD_ADD:
+ ret = mnl_nft_set_batch_add(set, NLM_F_EXCL,
+ rp->nl_ctx->seqnum);
+ break;
+ case NFT_CMD_DELETE:
+ ret = mnl_nft_set_batch_del(set, 0, rp->nl_ctx->seqnum);
+ break;
+ default:
+ errno = EOPNOTSUPP;
+ break;
+ }
+
+ if (ret < 0) {
+ netlink_io_error(rp->nl_ctx, &rp->cmd->location,
+ "Could not import set: %s", strerror(errno));
+ return ret;
+ }
+
+ return netlink_build_setelems_nlmsg(ctx);
+}
+
+static int netlink_build_rule_batch(const struct nft_parse_ctx *ctx,
+ uint32_t cmd, struct nft_rule *rule)
+{
+ const struct ruleset_parse *rp;
+ uint32_t nl_flags;
+ int ret = -1;
+
+ rp = nft_ruleset_ctx_get(ctx, NFT_RULESET_CTX_DATA);
+
+ switch (cmd) {
+ case NFT_CMD_ADD:
+ nl_flags = NLM_F_APPEND | NLM_F_CREATE;
+ nft_rule_attr_unset(rule, NFT_RULE_ATTR_HANDLE);
+ ret = mnl_nft_rule_batch_add(rule, nl_flags,
+ rp->nl_ctx->seqnum);
+ break;
+ case NFT_CMD_DELETE:
+ ret = mnl_nft_rule_batch_del(rule, 0, rp->nl_ctx->seqnum);
+ break;
+ case NFT_CMD_REPLACE:
+ nl_flags = NLM_F_REPLACE;
+ ret = mnl_nft_rule_batch_add(rule, nl_flags,
+ rp->nl_ctx->seqnum);
+ break;
+ case NFT_CMD_INSERT:
+ nl_flags = NLM_F_CREATE;
+ nft_rule_attr_unset(rule, NFT_RULE_ATTR_HANDLE);
+ ret = mnl_nft_rule_batch_add(rule, nl_flags,
+ rp->nl_ctx->seqnum);
+ break;
+ default:
+ errno = EOPNOTSUPP;
+ break;
+ }
+
+ if (ret < 0)
+ netlink_io_error(rp->nl_ctx, &rp->cmd->location,
+ "Could not import rule: %s", strerror(errno));
+
+ return ret;
+}
+
+static int netlink_build_rule_nlmsg(const struct nft_parse_ctx *ctx)
+{
+ struct nft_rule *rule;
+ uint32_t cmd;
+
+ cmd = nft_ruleset_ctx_get_u32(ctx, NFT_RULESET_CTX_CMD);
+ rule = nft_ruleset_ctx_get(ctx, NFT_RULESET_CTX_RULE);
+
+ return netlink_build_rule_batch(ctx, cmd, rule);
+}
+
+static int netlink_build_flush_rules_nlmsg(const struct nft_parse_ctx *ctx)
+{
+ struct nft_rule *rule;
+ struct nft_table *table;
+ struct nft_chain *chain;
+ uint32_t type;
+ int ret = -1;
+
+ rule = nft_rule_alloc();
+ if (rule == NULL)
+ return -1;
+
+ type = nft_ruleset_ctx_get_u32(ctx, NFT_RULESET_CTX_TYPE);
+ switch (type) {
+ case NFT_RULESET_TABLE:
+ table = nft_ruleset_ctx_get(ctx, NFT_RULESET_CTX_TABLE);
+
+ nft_rule_attr_set(rule, NFT_RULE_ATTR_TABLE,
+ nft_table_attr_get(table,
+ NFT_TABLE_ATTR_NAME));
+ nft_rule_attr_set(rule, NFT_RULE_ATTR_FAMILY,
+ nft_table_attr_get(table,
+ NFT_TABLE_ATTR_FAMILY));
+ break;
+ case NFT_RULESET_CHAIN:
+ chain = nft_ruleset_ctx_get(ctx, NFT_RULESET_CTX_CHAIN);
+
+ nft_rule_attr_set(rule, NFT_RULE_ATTR_TABLE,
+ nft_chain_attr_get(chain,
+ NFT_CHAIN_ATTR_TABLE));
+ nft_rule_attr_set(rule, NFT_RULE_ATTR_CHAIN,
+ nft_chain_attr_get(chain,
+ NFT_CHAIN_ATTR_NAME));
+ nft_rule_attr_set(rule, NFT_RULE_ATTR_FAMILY,
+ nft_chain_attr_get(chain,
+ NFT_TABLE_ATTR_FAMILY));
+ break;
+ default:
+ errno = EOPNOTSUPP;
+ goto err;
+ }
+
+ ret = netlink_build_rule_batch(ctx, NFT_CMD_DELETE, rule);
+err:
+ nft_rule_free(rule);
+ return ret;
+}
+
+static int netlink_build_chain_nlmsg(const struct nft_parse_ctx *ctx)
+{
+ const struct ruleset_parse *rp;
+ struct nft_chain *chain;
+ uint32_t cmd;
+ int ret = -1;
+
+ chain = nft_ruleset_ctx_get(ctx, NFT_RULESET_CTX_CHAIN);
+ rp = nft_ruleset_ctx_get(ctx, NFT_RULESET_CTX_DATA);
+
+ nft_chain_attr_unset(chain, NFT_CHAIN_ATTR_HANDLE);
+
+ cmd = nft_ruleset_ctx_get_u32(ctx, NFT_RULESET_CTX_CMD);
+ switch (cmd) {
+ case NFT_CMD_ADD:
+ ret = mnl_nft_chain_batch_add(chain, 0, rp->nl_ctx->seqnum);
+ break;
+ case NFT_CMD_DELETE:
+ ret = mnl_nft_chain_batch_del(chain, 0, rp->nl_ctx->seqnum);
+ break;
+ case NFT_CMD_FLUSH:
+ ret = netlink_build_flush_rules_nlmsg(ctx);
+ break;
+ default:
+ errno = EOPNOTSUPP;
+ break;
+ }
+
+ if (ret < 0)
+ netlink_io_error(rp->nl_ctx, &rp->cmd->location,
+ "Could not import chain: %s", strerror(errno));
+
+ return ret;
+}
+
+static int netlink_build_table_batch(const struct nft_parse_ctx *ctx,
+ uint32_t cmd, struct nft_table *table)
+{
+ int ret = -1;
+ struct ruleset_parse *rp;
+
+ rp = nft_ruleset_ctx_get(ctx, NFT_RULESET_CTX_DATA);
+
+ switch (cmd) {
+ case NFT_CMD_ADD:
+ ret = mnl_nft_table_batch_add(table, 0, rp->nl_ctx->seqnum);
+ break;
+ case NFT_CMD_DELETE:
+ ret = mnl_nft_table_batch_del(table, 0, rp->nl_ctx->seqnum);
+ break;
+ case NFT_CMD_FLUSH:
+ ret = netlink_build_flush_rules_nlmsg(ctx);
+ break;
+ default:
+ errno = EOPNOTSUPP;
+ break;
+ }
+
+ if (ret < 0)
+ netlink_io_error(rp->nl_ctx, &rp->cmd->location,
+ "Could not import table: %s", strerror(errno));
+
+ return ret;
+
+}
+
+static int netlink_build_table_nlmsg(const struct nft_parse_ctx *ctx)
+{
+ struct nft_table *table;
+ uint32_t cmd;
+
+ cmd = nft_ruleset_ctx_get_u32(ctx, NFT_RULESET_CTX_CMD);
+ table = nft_ruleset_ctx_get(ctx, NFT_RULESET_CTX_TABLE);
+
+ return netlink_build_table_batch(ctx, cmd, table);
+}
+
+static int netlink_build_flush_nlmsg(const struct nft_parse_ctx *ctx)
+{
+ struct nft_table *table;
+ int ret;
+
+ table = nft_table_alloc();
+ if (table == NULL)
+ return -1;
+
+ ret = netlink_build_table_batch(ctx, NFT_CMD_DELETE, table);
+ nft_table_free(table);
+
+ return ret;
+}
+
+int netlink_ruleset_parse_cb(const struct nft_parse_ctx *ctx)
+{
+ uint32_t type;
+
+ type = nft_ruleset_ctx_get_u32(ctx, NFT_RULESET_CTX_TYPE);
+ switch (type) {
+ case NFT_RULESET_TABLE:
+ netlink_build_table_nlmsg(ctx);
+ break;
+ case NFT_RULESET_CHAIN:
+ netlink_build_chain_nlmsg(ctx);
+ break;
+ case NFT_RULESET_RULE:
+ netlink_build_rule_nlmsg(ctx);
+ break;
+ case NFT_RULESET_SET:
+ netlink_build_set_nlmsg(ctx);
+ break;
+ case NFT_RULESET_SET_ELEMS:
+ netlink_build_setelems_nlmsg(ctx);
+ break;
+ case NFT_RULESET_RULESET:
+ netlink_build_flush_nlmsg(ctx);
+ break;
+ default:
+ return -1;
+ }
+
+ nft_ruleset_ctx_free(ctx);
+ return 0;
+}
+
bool netlink_batch_supported(void)
{
return mnl_batch_supported(nf_sock);
@@ -188,6 +188,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%token FLUSH "flush"
%token RENAME "rename"
%token DESCRIBE "describe"
+%token IMPORT "import"
%token EXPORT "export"
%token MONITOR "monitor"
@@ -402,8 +403,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%type <cmd> line
%destructor { cmd_free($$); } line
-%type <cmd> base_cmd add_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd
-%destructor { cmd_free($$); } base_cmd add_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd
+%type <cmd> base_cmd add_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd
+%destructor { cmd_free($$); } base_cmd add_cmd create_cmd insert_cmd delete_cmd list_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd
%type <handle> table_spec tables_spec chain_spec chain_identifier ruleid_spec ruleset_spec
%destructor { handle_free(&$$); } table_spec tables_spec chain_spec chain_identifier ruleid_spec ruleset_spec
@@ -535,7 +536,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%destructor { expr_free($$); } ct_expr
%type <val> ct_key
-%type <val> export_format
+%type <val> markup_format
%type <string> monitor_event
%destructor { xfree($$); } monitor_event
%type <val> monitor_object monitor_format
@@ -637,6 +638,7 @@ base_cmd : /* empty */ add_cmd { $$ = $1; }
| LIST list_cmd { $$ = $2; }
| FLUSH flush_cmd { $$ = $2; }
| RENAME rename_cmd { $$ = $2; }
+ | IMPORT import_cmd { $$ = $2; }
| EXPORT export_cmd { $$ = $2; }
| MONITOR monitor_cmd { $$ = $2; }
| DESCRIBE describe_cmd { $$ = $2; }
@@ -801,11 +803,19 @@ rename_cmd : CHAIN chain_spec identifier
}
;
-export_cmd : export_format
+import_cmd : markup_format
{
struct handle h = { .family = NFPROTO_UNSPEC };
- struct export *export = export_alloc($1);
- $$ = cmd_alloc(CMD_EXPORT, CMD_OBJ_EXPORT, &h, &@$, export);
+ struct markup *markup = markup_alloc($1);
+ $$ = cmd_alloc(CMD_IMPORT, CMD_OBJ_MARKUP, &h, &@$, markup);
+ }
+ ;
+
+export_cmd : markup_format
+ {
+ struct handle h = { .family = NFPROTO_UNSPEC };
+ struct markup *markup = markup_alloc($1);
+ $$ = cmd_alloc(CMD_EXPORT, CMD_OBJ_MARKUP, &h, &@$, markup);
}
;
@@ -831,11 +841,11 @@ monitor_object : /* empty */ { $$ = CMD_MONITOR_OBJ_ANY; }
;
monitor_format : /* empty */ { $$ = NFT_OUTPUT_DEFAULT; }
- | export_format
+ | markup_format
;
-export_format : XML { $$ = NFT_OUTPUT_XML; }
- | JSON { $$ = NFT_OUTPUT_JSON; }
+markup_format : XML { $$ = NFT_PARSE_XML; }
+ | JSON { $$ = NFT_PARSE_JSON; }
;
describe_cmd : primary_expr
@@ -540,17 +540,17 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
return cmd;
}
-struct export *export_alloc(uint32_t format)
+struct markup *markup_alloc(uint32_t format)
{
- struct export *export;
+ struct markup *markup;
- export = xmalloc(sizeof(struct export));
- export->format = format;
+ markup = xmalloc(sizeof(struct markup));
+ markup->format = format;
- return export;
+ return markup;
}
-void export_free(struct export *e)
+void markup_free(struct markup *e)
{
xfree(e);
}
@@ -599,8 +599,8 @@ void cmd_free(struct cmd *cmd)
case CMD_OBJ_MONITOR:
monitor_free(cmd->monitor);
break;
- case CMD_OBJ_EXPORT:
- export_free(cmd->export);
+ case CMD_OBJ_MARKUP:
+ markup_free(cmd->markup);
break;
default:
BUG("invalid command object type %u\n", cmd->obj);
@@ -754,7 +754,7 @@ static int do_command_export(struct netlink_ctx *ctx, struct cmd *cmd)
if (rs == NULL)
return -1;
- nft_ruleset_fprintf(stdout, rs, cmd->export->format, 0);
+ nft_ruleset_fprintf(stdout, rs, cmd->markup->format, 0);
fprintf(stdout, "\n");
nft_ruleset_free(rs);
@@ -1006,6 +1006,28 @@ static int do_command_describe(struct netlink_ctx *ctx, struct cmd *cmd)
return 0;
}
+static int do_command_import(struct netlink_ctx *ctx, struct cmd *cmd)
+{
+ int ret;
+ struct nft_parse_err *err;
+ struct ruleset_parse rp = {
+ .nl_ctx = ctx,
+ .cmd = cmd
+ };
+
+ err = nft_parse_err_alloc();
+ if (err == NULL)
+ return -1;
+
+ ret = nft_ruleset_parse_file_cb(cmd->markup->format, stdin, err, &rp,
+ netlink_ruleset_parse_cb);
+ if (ret < 0)
+ nft_parse_perror("unable to import: parsing failed", err);
+
+ nft_parse_err_free(err);
+ return ret;
+}
+
int do_command(struct netlink_ctx *ctx, struct cmd *cmd)
{
switch (cmd->op) {
@@ -1023,6 +1045,8 @@ int do_command(struct netlink_ctx *ctx, struct cmd *cmd)
return do_command_flush(ctx, cmd);
case CMD_RENAME:
return do_command_rename(ctx, cmd);
+ case CMD_IMPORT:
+ return do_command_import(ctx, cmd);
case CMD_EXPORT:
return do_command_export(ctx, cmd);
case CMD_MONITOR:
@@ -261,6 +261,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"list" { return LIST; }
"flush" { return FLUSH; }
"rename" { return RENAME; }
+"import" { return IMPORT; }
"export" { return EXPORT; }
"monitor" { return MONITOR; }
A basic way to test this new functionality is: % cat file.json | nft import json where the file.json is a ruleset exported in json format. This new operation allows to import ruleset in json and xml and to make incremental changes using the new parse functions of libnftnl. Based in a patch of Arturo Borrero. Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com> --- [changes in v4] * Refactored the structure and the functions to use the object export in both path (import and export operation). * Moved the code to make the netlink message from rule to netlink. * Changed an error message to use a better format. * Renamed the functions to build the netlink message using the parser context. include/netlink.h | 9 ++ include/rule.h | 14 +-- src/evaluate.c | 1 + src/netlink.c | 290 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/parser_bison.y | 28 +++-- src/rule.c | 42 ++++++-- src/scanner.l | 1 + 7 files changed, 361 insertions(+), 24 deletions(-)