/* * Copyright (c) 2013-2015 Pablo Neira Ayuso * * 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 "internal.h" #include #include #include struct nftnl_batch { uint32_t num_pages; struct nftnl_batch_page *current_page; uint32_t page_size; uint32_t page_overrun_size; struct list_head page_list; }; struct nftnl_batch_page { struct list_head head; struct mnl_nlmsg_batch *batch; }; static struct nftnl_batch_page *nftnl_batch_page_alloc(struct nftnl_batch *batch) { struct nftnl_batch_page *page; char *buf; page = malloc(sizeof(struct nftnl_batch_page)); if (page == NULL) return NULL; buf = malloc(batch->page_size + batch->page_overrun_size); if (buf == NULL) goto err1; page->batch = mnl_nlmsg_batch_start(buf, batch->page_size); if (page->batch == NULL) goto err2; return page; err2: free(buf); err1: free(page); return NULL; } static void nftnl_batch_add_page(struct nftnl_batch_page *page, struct nftnl_batch *batch) { batch->current_page = page; batch->num_pages++; list_add_tail(&page->head, &batch->page_list); } struct nftnl_batch *nftnl_batch_alloc(uint32_t pg_size, uint32_t pg_overrun_size) { struct nftnl_batch *batch; struct nftnl_batch_page *page; batch = calloc(1, sizeof(struct nftnl_batch)); if (batch == NULL) return NULL; batch->page_size = pg_size; batch->page_overrun_size = pg_overrun_size; INIT_LIST_HEAD(&batch->page_list); page = nftnl_batch_page_alloc(batch); if (page == NULL) goto err1; nftnl_batch_add_page(page, batch); return batch; err1: free(batch); return NULL; } EXPORT_SYMBOL(nftnl_batch_alloc, nft_batch_alloc); void nftnl_batch_free(struct nftnl_batch *batch) { struct nftnl_batch_page *page, *next; list_for_each_entry_safe(page, next, &batch->page_list, head) { free(mnl_nlmsg_batch_head(page->batch)); mnl_nlmsg_batch_stop(page->batch); free(page); } free(batch); } EXPORT_SYMBOL(nftnl_batch_free, nft_batch_free); int nftnl_batch_update(struct nftnl_batch *batch) { struct nftnl_batch_page *page; struct nlmsghdr *last_nlh; if (mnl_nlmsg_batch_next(batch->current_page->batch)) return 0; last_nlh = nftnl_batch_buffer(batch); page = nftnl_batch_page_alloc(batch); if (page == NULL) goto err1; nftnl_batch_add_page(page, batch); memcpy(nftnl_batch_buffer(batch), last_nlh, last_nlh->nlmsg_len); mnl_nlmsg_batch_next(batch->current_page->batch); return 0; err1: return -1; } EXPORT_SYMBOL(nftnl_batch_update, nft_batch_update); void *nftnl_batch_buffer(struct nftnl_batch *batch) { return mnl_nlmsg_batch_current(batch->current_page->batch); } EXPORT_SYMBOL(nftnl_batch_buffer, nft_batch_buffer); uint32_t nftnl_batch_buffer_len(struct nftnl_batch *batch) { return mnl_nlmsg_batch_size(batch->current_page->batch); } EXPORT_SYMBOL(nftnl_batch_buffer_len, nft_batch_buffer_len); int nftnl_batch_iovec_len(struct nftnl_batch *batch) { int num_pages = batch->num_pages; /* Skip last page if it's empty */ if (mnl_nlmsg_batch_is_empty(batch->current_page->batch)) num_pages--; return num_pages; } EXPORT_SYMBOL(nftnl_batch_iovec_len, nft_batch_iovec_len); void nftnl_batch_iovec(struct nftnl_batch *batch, struct iovec *iov, uint32_t iovlen) { struct nftnl_batch_page *page; int i = 0; list_for_each_entry(page, &batch->page_list, head) { if (i >= iovlen) break; iov[i].iov_base = mnl_nlmsg_batch_head(page->batch); iov[i].iov_len = mnl_nlmsg_batch_size(page->batch); i++; } } EXPORT_SYMBOL(nftnl_batch_iovec, nft_batch_iovec);