diff options
Diffstat (limited to 'src/netlink.c')
-rw-r--r-- | src/netlink.c | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/src/netlink.c b/src/netlink.c new file mode 100644 index 00000000..4700cd7e --- /dev/null +++ b/src/netlink.c @@ -0,0 +1,476 @@ +/* + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <netlink/netfilter/nfnl.h> +#include <netlink/netfilter/netfilter.h> +#include <netlink/netfilter/nft_table.h> +#include <netlink/netfilter/nft_chain.h> +#include <netlink/netfilter/nft_rule.h> +#include <netlink/netfilter/nft_expr.h> +#include <netlink/netfilter/nft_data.h> +#include <linux/netfilter/nf_tables.h> + +#include <nftables.h> +#include <netlink.h> +#include <expression.h> +#include <utils.h> +#include <erec.h> + +#define TRACE 0 + +static struct nl_sock *nf_sock; + +static void __init netlink_open_sock(void) +{ + // FIXME: should be done dynamically by nft_set and based on set members + nlmsg_set_default_size(65536); + nf_sock = nl_socket_alloc(); + nfnl_connect(nf_sock); +} + +static void __exit netlink_close_sock(void) +{ + nl_socket_free(nf_sock); +} + +void netlink_dump_object(struct nl_object *obj) +{ + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_DETAILS, + }; + nl_object_dump(obj, ¶ms); +} + +static int netlink_io_error(struct netlink_ctx *ctx, const struct location *loc, + const char *fmt, ...) +{ + struct error_record *erec; + va_list ap; + + if (loc == NULL) + loc = &internal_location; + + va_start(ap, fmt); + erec = erec_vcreate(EREC_ERROR, loc, fmt, ap); + va_end(ap); + erec_queue(erec, ctx->msgs); + return -1; +} + +struct nfnl_nft_table *alloc_nft_table(const struct handle *h) +{ + struct nfnl_nft_table *nlt; + + nlt = nfnl_nft_table_alloc(); + if (nlt == NULL) + memory_allocation_error(); + nfnl_nft_table_set_family(nlt, h->family); + nfnl_nft_table_set_name(nlt, h->table, strlen(h->table) + 1); + return nlt; +} + +struct nfnl_nft_chain *alloc_nft_chain(const struct handle *h) +{ + struct nfnl_nft_chain *nlc; + + nlc = nfnl_nft_chain_alloc(); + if (nlc == NULL) + memory_allocation_error(); + nfnl_nft_chain_set_family(nlc, h->family); + nfnl_nft_chain_set_table(nlc, h->table, strlen(h->table) + 1); + if (h->chain != NULL) + nfnl_nft_chain_set_name(nlc, h->chain, strlen(h->chain) + 1); + return nlc; +} + +struct nfnl_nft_rule *alloc_nft_rule(const struct handle *h) +{ + struct nfnl_nft_rule *nlr; + + nlr = nfnl_nft_rule_alloc(); + if (nlr == NULL) + memory_allocation_error(); + nfnl_nft_rule_set_family(nlr, h->family); + nfnl_nft_rule_set_table(nlr, h->table, strlen(h->table) + 1); + if (h->chain != NULL) + nfnl_nft_rule_set_chain(nlr, h->chain, strlen(h->chain) + 1); + if (h->handle) + nfnl_nft_rule_set_handle(nlr, h->handle); + return nlr; +} + +struct nfnl_nft_expr *alloc_nft_expr(int (*init)(struct nfnl_nft_expr *)) +{ + struct nfnl_nft_expr *nle; + + nle = nfnl_nft_expr_alloc(); + if (nle == NULL || init(nle) != 0) + memory_allocation_error(); + return nle; +} + +struct nfnl_nft_data *alloc_nft_data(const void *data, unsigned int len) +{ + struct nfnl_nft_data *nfd; + + assert(len > 0); + nfd = nfnl_nft_data_alloc(data, len); + if (nfd == NULL) + memory_allocation_error(); + return nfd; +} + +int netlink_add_rule(struct netlink_ctx *ctx, const struct handle *h, + const struct rule *rule) +{ + struct nfnl_nft_rule *nlr; + int err; + + nlr = alloc_nft_rule(&rule->handle); + err = netlink_linearize_rule(ctx, nlr, rule); + if (err == 0) { + err = nfnl_nft_rule_add(nf_sock, nlr, NLM_F_EXCL | NLM_F_APPEND); + if (err < 0) + netlink_io_error(ctx, &rule->location, + "Could not add rule: %s", nl_geterror(err)); + } + nfnl_nft_rule_put(nlr); + return err; +} + +int netlink_delete_rule(struct netlink_ctx *ctx, const struct handle *h) +{ + struct nfnl_nft_rule *nlr; + int err; + + nlr = alloc_nft_rule(h); + err = nfnl_nft_rule_delete(nf_sock, nlr, 0); + nfnl_nft_rule_put(nlr); + + if (err < 0) + netlink_io_error(ctx, NULL, "Could not delete rule: %s", + nl_geterror(err)); + return err; +} + +static void list_rule_cb(struct nl_object *obj, void *arg) +{ + const struct nfnl_nft_rule *nlr = (struct nfnl_nft_rule *)obj; + struct netlink_ctx *ctx = arg; + struct rule *rule; +#if TRACE + printf("\n"); netlink_dump_object(obj); printf("\n"); +#endif + if (!nfnl_nft_rule_test_family(nlr) || + !nfnl_nft_rule_test_table(nlr) || + !nfnl_nft_rule_test_chain(nlr) || + !nfnl_nft_rule_test_handle(nlr)) { + netlink_io_error(ctx, NULL, "Incomplete rule received"); + return; + } + + rule = netlink_delinearize_rule(ctx, obj); + list_add_tail(&rule->list, &ctx->list); +} + +static int netlink_list_rules(struct netlink_ctx *ctx, const struct handle *h) +{ + struct nl_cache *rule_cache; + struct nfnl_nft_rule *nlr; + int err; + + err = nfnl_nft_rule_alloc_cache(nf_sock, &rule_cache); + if (err < 0) + return netlink_io_error(ctx, NULL, + "Could not receive rules from kernel: %s", + nl_geterror(err)); + + nlr = alloc_nft_rule(h); + nl_cache_foreach_filter(rule_cache, (struct nl_object *)nlr, + list_rule_cb, ctx); + nfnl_nft_rule_put(nlr); + nl_cache_free(rule_cache); + return 0; +} + +static int netlink_get_rule_cb(struct nl_msg *msg, void *arg) +{ + return nl_msg_parse(msg, list_rule_cb, arg); +} + +int netlink_get_rule(struct netlink_ctx *ctx, const struct handle *h) +{ + struct nfnl_nft_rule *nlr; + int err; + + nlr = alloc_nft_rule(h); + nfnl_nft_rule_query(nf_sock, nlr, 0); + nl_socket_modify_cb(nf_sock, NL_CB_VALID, NL_CB_CUSTOM, + netlink_get_rule_cb, ctx); + err = nl_recvmsgs_default(nf_sock); + nfnl_nft_rule_put(nlr); + + if (err < 0) + return netlink_io_error(ctx, NULL, + "Could not receive rule from kernel: %s", + nl_geterror(err)); + return err; +} + +static void flush_rule_cb(struct nl_object *obj, void *arg) +{ + struct netlink_ctx *ctx = arg; + int err; + + netlink_dump_object(obj); + err = nfnl_nft_rule_delete(nf_sock, (struct nfnl_nft_rule *)obj, 0); + if (err < 0) + netlink_io_error(ctx, NULL, "Could not delete rule: %s", + nl_geterror(err)); +} + +static int netlink_flush_rules(struct netlink_ctx *ctx, const struct handle *h) +{ + struct nl_cache *rule_cache; + struct nfnl_nft_rule *nlr; + int err; + + err = nfnl_nft_rule_alloc_cache(nf_sock, &rule_cache); + if (err < 0) + return netlink_io_error(ctx, NULL, + "Could not receive rules from kernel: %s", + nl_geterror(err)); + + nlr = alloc_nft_rule(h); + nl_cache_foreach_filter(rule_cache, (struct nl_object *)nlr, + flush_rule_cb, ctx); + nfnl_nft_rule_put(nlr); + nl_cache_free(rule_cache); + return 0; +} + +int netlink_add_chain(struct netlink_ctx *ctx, const struct handle *h, + const struct chain *chain) +{ + struct nfnl_nft_chain *nlc; + int err; + + nlc = alloc_nft_chain(h); + if (chain != NULL && (chain->hooknum || chain->priority)) { + nfnl_nft_chain_set_hooknum(nlc, chain->hooknum); + nfnl_nft_chain_set_priority(nlc, chain->priority); + } + err = nfnl_nft_chain_add(nf_sock, nlc, NLM_F_EXCL); + nfnl_nft_chain_put(nlc); + + if (err < 0) + netlink_io_error(ctx, NULL, "Could not add chain: %s", + nl_geterror(err)); + return err; +} + +int netlink_delete_chain(struct netlink_ctx *ctx, const struct handle *h) +{ + struct nfnl_nft_chain *nlc; + int err; + + nlc = alloc_nft_chain(h); + err = nfnl_nft_chain_delete(nf_sock, nlc, 0); + nfnl_nft_chain_put(nlc); + + if (err < 0) + netlink_io_error(ctx, NULL, "Could not delete chain: %s", + nl_geterror(err)); + return err; +} + +static void list_chain_cb(struct nl_object *obj, void *arg) +{ + struct nfnl_nft_chain *nlc = (struct nfnl_nft_chain *)obj; + struct netlink_ctx *ctx = arg; + struct chain *chain; +#if TRACE + netlink_dump_object(obj);; +#endif + if (!nfnl_nft_chain_test_family(nlc) || + !nfnl_nft_chain_test_table(nlc) || + !nfnl_nft_chain_test_name(nlc)) { + netlink_io_error(ctx, NULL, "Incomplete chain received"); + return; + } + + chain = chain_alloc(nfnl_nft_chain_get_name(nlc)); + chain->handle.family = nfnl_nft_chain_get_family(nlc); + chain->handle.table = xstrdup(nfnl_nft_chain_get_table(nlc)); + chain->hooknum = nfnl_nft_chain_get_hooknum(nlc); + chain->priority = nfnl_nft_chain_get_priority(nlc); + list_add_tail(&chain->list, &ctx->list); +} + +int netlink_list_chains(struct netlink_ctx *ctx, const struct handle *h) +{ + struct nl_cache *chain_cache; + struct nfnl_nft_chain *nlc; + int err; + + err = nfnl_nft_chain_alloc_cache(nf_sock, &chain_cache); + if (err < 0) + return netlink_io_error(ctx, NULL, + "Could not receive chains from kernel: %s", + nl_geterror(err)); + + nlc = alloc_nft_chain(h); + nl_cache_foreach_filter(chain_cache, (struct nl_object *)nlc, + list_chain_cb, ctx); + nfnl_nft_chain_put(nlc); + nl_cache_free(chain_cache); + return 0; +} + +static int netlink_get_chain_cb(struct nl_msg *msg, void *arg) +{ + return nl_msg_parse(msg, list_chain_cb, arg); +} + +int netlink_get_chain(struct netlink_ctx *ctx, const struct handle *h) +{ + struct nfnl_nft_chain *nlc; + int err; + + nlc = alloc_nft_chain(h); + nfnl_nft_chain_query(nf_sock, nlc, 0); + nl_socket_modify_cb(nf_sock, NL_CB_VALID, NL_CB_CUSTOM, + netlink_get_chain_cb, ctx); + err = nl_recvmsgs_default(nf_sock); + nfnl_nft_chain_put(nlc); + + if (err < 0) + return netlink_io_error(ctx, NULL, + "Could not receive chain from kernel: %s", + nl_geterror(err)); + return err; +} + +int netlink_list_chain(struct netlink_ctx *ctx, const struct handle *h) +{ + return netlink_list_rules(ctx, h); +} + +int netlink_flush_chain(struct netlink_ctx *ctx, const struct handle *h) +{ + return netlink_flush_rules(ctx, h); +} + +int netlink_add_table(struct netlink_ctx *ctx, const struct handle *h, + const struct table *table) +{ + struct nfnl_nft_table *nlt; + int err; + + nlt = alloc_nft_table(h); + err = nfnl_nft_table_add(nf_sock, nlt, NLM_F_EXCL); + nfnl_nft_table_put(nlt); + + if (err < 0) + netlink_io_error(ctx, NULL, "Could not add table: %s", + nl_geterror(err)); + return err; +} + +int netlink_delete_table(struct netlink_ctx *ctx, const struct handle *h) +{ + struct nfnl_nft_table *nlt; + int err; + + nlt = alloc_nft_table(h); + err = nfnl_nft_table_delete(nf_sock, nlt, 0); + nfnl_nft_table_put(nlt); + + if (err < 0) + netlink_io_error(ctx, NULL, "Could not delete table: %s", + nl_geterror(err)); + return err; +} + +static void list_table_cb(struct nl_object *obj, void *arg) +{ + struct nfnl_nft_table *nlt = (struct nfnl_nft_table *)obj; + struct netlink_ctx *ctx = arg; + struct table *table; +#if TRACE + netlink_dump_object(obj); +#endif + if (!nfnl_nft_table_test_family(nlt) || + !nfnl_nft_table_test_name(nlt)) { + netlink_io_error(ctx, NULL, "Incomplete table received"); + return; + } + + table = table_alloc(); + table->handle.family = nfnl_nft_table_get_family(nlt); + table->handle.table = xstrdup(nfnl_nft_table_get_name(nlt)); + list_add_tail(&table->list, &ctx->list); +} + +int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h) +{ + struct nl_cache *table_cache; + struct nfnl_nft_table *nlt; + int err; + + err = nfnl_nft_table_alloc_cache(nf_sock, &table_cache); + if (err < 0) + return netlink_io_error(ctx, NULL, + "Could not receive tables from kernel: %s", + nl_geterror(err)); + + nlt = alloc_nft_table(h); + nl_cache_foreach_filter(table_cache, (struct nl_object *)nlt, + list_table_cb, ctx); + nfnl_nft_table_put(nlt); + nl_cache_free(table_cache); + return 0; +} + +static int netlink_get_table_cb(struct nl_msg *msg, void *arg) +{ + return nl_msg_parse(msg, list_table_cb, arg); +} + +int netlink_get_table(struct netlink_ctx *ctx, const struct handle *h) +{ + struct nfnl_nft_table *nlt; + int err; + + nlt = alloc_nft_table(h); + nfnl_nft_table_query(nf_sock, nlt, 0); + nl_socket_modify_cb(nf_sock, NL_CB_VALID, NL_CB_CUSTOM, + netlink_get_table_cb, ctx); + err = nl_recvmsgs_default(nf_sock); + nfnl_nft_table_put(nlt); + + if (err < 0) + return netlink_io_error(ctx, NULL, + "Could not receive table from kernel: %s", + nl_geterror(err)); + return err; +} + + +int netlink_list_table(struct netlink_ctx *ctx, const struct handle *h) +{ + return netlink_list_rules(ctx, h); +} + +int netlink_flush_table(struct netlink_ctx *ctx, const struct handle *h) +{ + return netlink_flush_rules(ctx, h); +} |