diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/main.c | 12 | ||||
-rw-r--r-- | src/mnl.c | 414 | ||||
-rw-r--r-- | src/server.c | 19 | ||||
-rw-r--r-- | src/utils.c | 17 |
5 files changed, 458 insertions, 6 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 5c09b24..f888eb7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,11 +10,13 @@ CLEANFILES = config-parser.c \ nft_sync_SOURCES = event.c \ logging.c \ msg_buff.c \ + mnl.c \ server.c \ client.c \ tcp.c \ timer.c \ main.c \ + utils.c \ fd.c \ config-parser.y \ config-scanner.l @@ -24,6 +24,7 @@ #include "logging.h" #include "msg_buff.h" #include "proto.h" +#include "mnl.h" struct nft_sync_inst nfts_inst; @@ -99,6 +100,14 @@ int main(int argc, char *argv[]) strerror(errno)); goto err; } + + if (nfts_socket_open(&nfts_inst) < 0) { + nfts_log(NFTS_LOG_FATAL, + "Cannot open Netlink query socket: %s\n", + strerror(errno)); + goto err; + } + nfts_log(NFTS_LOG_INFO, "listening at %s", inet_ntoa(nfts_inst.tcp.server.ipv4.inet_addr)); } @@ -126,6 +135,9 @@ int main(int argc, char *argv[]) nft_sync_event_fini(); + if (nfts_inst.mode & NFTS_MODE_SERVER) + nfts_socket_close(&nfts_inst); + ret = EXIT_SUCCESS; err: nft_sync_log_fini(&nfts_inst); diff --git a/src/mnl.c b/src/mnl.c new file mode 100644 index 0000000..bd2227b --- /dev/null +++ b/src/mnl.c @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2013 Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com> + * + * Almost copied from 'nftables' project: + * + * Copyright (c) 2013 Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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. + * + * TODO: provide abstract of these functions in libnftnl, later. + */ + +#include <errno.h> +#include <stdlib.h> + +#include "mnl.h" +#include "linux/netfilter/nf_tables.h" +#include "linux/netfilter.h" + +static int seq; + +static int +nfts_mnl_talk(struct mnl_socket *nf_sock, const void *data, unsigned int len, + int (*cb)(const struct nlmsghdr *nlh, void *data), void *cb_data) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + uint32_t portid = mnl_socket_get_portid(nf_sock); + int ret; + + if (mnl_socket_sendto(nf_sock, data, len) < 0) + return -1; + + ret = mnl_socket_recvfrom(nf_sock, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, cb, cb_data); + if (ret <= 0) + goto out; + + ret = mnl_socket_recvfrom(nf_sock, buf, sizeof(buf)); + } +out: + if (ret < 0 && errno == EAGAIN) + return 0; + + return ret; +} + +/* + * Rule + */ +static int rule_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nft_rule_list *nlr_list = data; + struct nft_rule *r; + + r = nft_rule_alloc(); + if (r == NULL) + return -1; + + if (nft_rule_nlmsg_parse(nlh, r) < 0) + goto err_free; + + nft_rule_list_add_tail(r, nlr_list); + return MNL_CB_OK; + +err_free: + nft_rule_free(r); + return MNL_CB_OK; +} + +struct nft_rule_list *mnl_rule_dump(struct mnl_socket *nf_sock, int family) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_rule_list *nlr_list; + int ret; + + nlr_list = nft_rule_list_alloc(); + if (nlr_list == NULL) + return NULL; + + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family, + NLM_F_DUMP, seq); + + ret = nfts_mnl_talk(nf_sock, nlh, nlh->nlmsg_len, rule_cb, nlr_list); + if (ret < 0) + goto err; + + return nlr_list; +err: + nft_rule_list_free(nlr_list); + return NULL; +} + +/* + * Chain + */ +static int chain_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nft_chain_list *nlc_list = data; + struct nft_chain *c; + + c = nft_chain_alloc(); + if (c == NULL) + return -1; + + if (nft_chain_nlmsg_parse(nlh, c) < 0) + goto err_free; + + nft_chain_list_add_tail(c, nlc_list); + return MNL_CB_OK; + +err_free: + nft_chain_free(c); + return MNL_CB_OK; +} + +struct nft_chain_list *mnl_chain_dump(struct mnl_socket *nf_sock, int family) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_chain_list *nlc_list; + int ret; + + nlc_list = nft_chain_list_alloc(); + if (nlc_list == NULL) + return NULL; + + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, family, + NLM_F_DUMP, seq); + + ret = nfts_mnl_talk(nf_sock, nlh, nlh->nlmsg_len, chain_cb, nlc_list); + if (ret < 0) + goto err; + + return nlc_list; +err: + nft_chain_list_free(nlc_list); + return NULL; +} + +/* + * Table + */ + +static int table_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nft_table_list *nlt_list = data; + struct nft_table *t; + + t = nft_table_alloc(); + if (t == NULL) + return -1; + + if (nft_table_nlmsg_parse(nlh, t) < 0) + goto err_free; + + nft_table_list_add_tail(t, nlt_list); + return MNL_CB_OK; + +err_free: + nft_table_free(t); + return MNL_CB_OK; +} + +struct nft_table_list *mnl_table_dump(struct mnl_socket *nf_sock, int family) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_table_list *nlt_list; + int ret; + + nlt_list = nft_table_list_alloc(); + if (nlt_list == NULL) + return NULL; + + nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, family, + NLM_F_DUMP, seq); + + ret = nfts_mnl_talk(nf_sock, nlh, nlh->nlmsg_len, table_cb, nlt_list); + if (ret < 0) + goto err; + + return nlt_list; +err: + nft_table_list_free(nlt_list); + return NULL; +} + +/* + * Set + */ + +static int set_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nft_set_list *nls_list = data; + struct nft_set *s; + + s = nft_set_alloc(); + if (s == NULL) + return -1; + + if (nft_set_nlmsg_parse(nlh, s) < 0) + goto err_free; + + nft_set_list_add_tail(s, nls_list); + return MNL_CB_OK; + +err_free: + nft_set_free(s); + return MNL_CB_OK; +} + +struct nft_set_list * +mnl_set_dump(struct mnl_socket *nf_sock, int family, const char *table) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nft_set *s; + struct nft_set_list *nls_list; + int ret; + + s = nft_set_alloc(); + if (s == NULL) + return NULL; + + nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_GETSET, family, + NLM_F_DUMP|NLM_F_ACK, seq); + nft_set_attr_set(s, NFT_SET_ATTR_TABLE, table); + nft_set_nlmsg_build_payload(nlh, s); + nft_set_free(s); + + nls_list = nft_set_list_alloc(); + if (nls_list == NULL) + goto err; + + ret = nfts_mnl_talk(nf_sock, nlh, nlh->nlmsg_len, set_cb, nls_list); + if (ret < 0) + goto err; + + return nls_list; +err: + nft_set_list_free(nls_list); + return NULL; +} + +static void +nft_set_list_merge(struct nft_set_list *dest, struct nft_set_list *orig) +{ + struct nft_set_list_iter *it; + struct nft_set *o; + + it = nft_set_list_iter_create(orig); + if (it == NULL) + return; + + o = nft_set_list_iter_next(it); + while (o != NULL) { + nft_set_list_add_tail(o, dest); + o = nft_set_list_iter_next(it); + } + + nft_set_list_iter_destroy(it); +} + + +/* + * Set elements + */ + +static int set_elem_cb(const struct nlmsghdr *nlh, void *data) +{ + nft_set_elems_nlmsg_parse(nlh, data); + return MNL_CB_OK; +} + +int mnl_setelem_get(struct mnl_socket *nf_sock, struct nft_set *nls) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t family = nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY); + + nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_GETSETELEM, family, + NLM_F_DUMP|NLM_F_ACK, seq); + nft_set_nlmsg_build_payload(nlh, nls); + + return nfts_mnl_talk(nf_sock, nlh, nlh->nlmsg_len, set_elem_cb, nls); +} + +/* + * ruleset + */ + +struct nft_ruleset *mnl_ruleset_dump(struct mnl_socket *nf_sock) +{ + struct nft_ruleset *rs; + struct nft_rule_list *r; + struct nft_chain_list *c; + struct nft_set_list *complete_set_list = NULL, *s; + struct nft_table_list *t; + struct nft_table_list_iter *it; + struct nft_table *o; + const char *table; + uint16_t family; + + t = mnl_table_dump(nf_sock, NFPROTO_UNSPEC); + if (t == NULL) + return NULL; + + rs = nft_ruleset_alloc(); + if (rs == NULL) + return NULL; + + nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_TABLELIST, t); + + c = mnl_chain_dump(nf_sock, NFPROTO_UNSPEC); + if (c != NULL) + nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_CHAINLIST, c); + + r = mnl_rule_dump(nf_sock, NFPROTO_UNSPEC); + if (r != NULL) + nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_RULELIST, r); + + it = nft_table_list_iter_create(t); + if (it == NULL) + return NULL; + + o = nft_table_list_iter_next(it); + while (o != NULL) { + table = nft_table_attr_get_str(o, NFT_TABLE_ATTR_NAME); + family = nft_table_attr_get_u32(o, NFT_TABLE_ATTR_FAMILY); + + s = mnl_set_dump(nf_sock, family, table); + if (s != NULL) { + if (complete_set_list == NULL) { + complete_set_list = nft_set_list_alloc(); + if (complete_set_list == NULL) + return NULL; + } + + nft_set_list_merge(complete_set_list, s); + nft_set_list_free(s); + } + o = nft_table_list_iter_next(it); + } + nft_table_list_iter_destroy(it); + + if (complete_set_list != NULL) + nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_SETLIST, + complete_set_list); + + return rs; +} + +/* + * netlink + */ +static struct mnl_socket *netlink_socket_open(void) +{ + return mnl_socket_open(NETLINK_NETFILTER); +} + +int nfts_socket_open(struct nft_sync_inst *inst) +{ + struct mnl_socket *s = netlink_socket_open(); + if (s == NULL) + return -1; + + inst->nl_query_sock = s; + return 0; +} + +void nfts_socket_close(struct nft_sync_inst *inst) +{ + mnl_socket_close(inst->nl_query_sock); +} + +#define SNPRINTF_BUFSIZ 4096 + +const char *netlink_dump_ruleset(struct mnl_socket *s) +{ + struct nft_ruleset *rs; + size_t bufsiz = SNPRINTF_BUFSIZ; + char *buf; + int ret; + + buf = calloc(1, bufsiz); + if (buf == NULL) + return NULL; + + rs = mnl_ruleset_dump(s); + if (rs == NULL) { + free(buf); + return NULL; + } + + ret = nft_ruleset_snprintf(buf, bufsiz, rs, NFT_OUTPUT_XML, 0); + if (ret > SNPRINTF_BUFSIZ) { + free(buf); + buf = calloc(1, ret); + if (buf == NULL) { + nft_ruleset_free(rs); + return NULL; + } + + bufsiz = ret; + ret = nft_ruleset_snprintf(buf, bufsiz, rs, NFT_OUTPUT_XML, 0); + } + + nft_ruleset_free(rs); + return buf; +} diff --git a/src/server.c b/src/server.c index cd4ac0a..1b6e0d8 100644 --- a/src/server.c +++ b/src/server.c @@ -22,24 +22,31 @@ #include "proto.h" #include "config.h" #include "proto.h" +#include "mnl.h" +#include "utils.h" static int send_ruleset(struct nft_fd *nfd) { struct msg_buff *msgb; struct nft_sync_hdr *hdr; - /* TODO: send real ruleset in json/xml format here, replace this - * code with the real libnftnl code. - */ - const char *ruleset = "this is the ruleset in XML/JSON format"; - int ret, ruleset_len = strlen(ruleset); + int ret, ruleset_len; + const char *ruleset = netlink_dump_ruleset(nfts_inst.nl_query_sock); + + if (ruleset == NULL) + return 0; + + ruleset_len = strlen(ruleset); msgb = msgb_alloc(sizeof(struct nft_sync_hdr) + ruleset_len); - if (msgb == NULL) + if (msgb == NULL) { + xfree(ruleset); return -1; + } hdr = msgb_put(msgb, sizeof(struct nft_sync_hdr) + ruleset_len); hdr->len = htonl(sizeof(struct nft_sync_hdr) + ruleset_len); memcpy(hdr->data, ruleset, ruleset_len); + xfree(ruleset); ret = send(nfd->fd, msgb_data(msgb), msgb_len(msgb), 0); msgb_free(msgb); diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..f1886c4 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2013 Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com> + * + * 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. + * + */ + +#include <stdlib.h> + +#include "utils.h" + +void xfree(const void *ptr) +{ + free((void *)ptr); +} |