Signed-off-by: Ken-ichirou MATSUZAWA <chamas@xxxxxxxxxxxxx> --- include/libmnl/libmnl.h | 44 ++++++++++ src/Makefile.am | 2 +- src/libmnl.map | 7 ++ src/mmap.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 src/mmap.c diff --git a/include/libmnl/libmnl.h b/include/libmnl/libmnl.h index 223709c..11abb04 100644 --- a/include/libmnl/libmnl.h +++ b/include/libmnl/libmnl.h @@ -180,6 +180,50 @@ extern int mnl_cb_run2(const void *buf, size_t numbytes, unsigned int seq, mnl_cb_t *cb_ctl_array, unsigned int cb_ctl_array_len); /* + * mmapped API + */ + +/* + * ring contains #nr blocks, block contains #nr frames + * ring size == nm_block_size * nm_block_nr + * == nm_frame_size * nm_frame_nr + * + * frames per block: nm_block_size / nm_frame_size + * nm_frame_nr : frames per block * nm_block_nr + * nm_block_size : n * getpagesize() + * nm_frame_size : >= NL_MMAP_HDRLEN to hold at least the frame header + * nm_frame_size : <= nm_block_size + */ +#define MNL_MMAP_DEFAULT_REQ { \ + .nm_block_size = MNL_SOCKET_BUFFER_SIZE * 16, \ + .nm_block_nr = 64, \ + .nm_frame_size = 16384, \ + .nm_frame_nr = 64 * MNL_SOCKET_BUFFER_SIZE * 16 / 16384, \ +} + +#define MNL_MMAP_SMALL_REQ { \ + .nm_block_size = getpagesize(), \ + .nm_block_nr = 4, \ + .nm_frame_size = getpagesize() / 2, \ + .nm_frame_nr = (4 * getpagesize()) / (getpagesize() / 2), \ +} + +#define MNL_MMAP_MSGHDR(nlmhdr) ((void *)(nlmhdr) + NL_MMAP_HDRLEN) + +enum mnl_ring_types { + MNL_RING_RX, + MNL_RING_TX, +}; + +struct mnl_ring_socket; + +extern struct mnl_ring_socket * +mnl_ring_map(const struct mnl_socket *nl, struct nl_mmap_req *tx_req, struct nl_mmap_req *rx_req); +extern int mnl_ring_unmap(struct mnl_ring_socket *nlm); +extern struct nl_mmap_hdr *mnl_ring_get_frame(struct mnl_ring_socket *nlm, enum mnl_ring_types type); +extern int mnl_ring_advance(struct mnl_ring_socket *nlm, enum mnl_ring_types type); + +/* * other declarations */ diff --git a/src/Makefile.am b/src/Makefile.am index 9aab516..f97958c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,4 +2,4 @@ include $(top_srcdir)/Make_global.am lib_LTLIBRARIES = libmnl.la libmnl_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libmnl.map -version-info $(LIBVERSION) -libmnl_la_SOURCES = socket.c callback.c nlmsg.c attr.c internal.h libmnl.map +libmnl_la_SOURCES = socket.c callback.c nlmsg.c attr.c mmap.c internal.h libmnl.map diff --git a/src/libmnl.map b/src/libmnl.map index dbc332e..73de08f 100644 --- a/src/libmnl.map +++ b/src/libmnl.map @@ -72,3 +72,10 @@ local: *; LIBMNL_1.1 { mnl_attr_parse_payload; } LIBMNL_1.0; + +LIBMNL_1.2 { + mnl_ring_map; + mnl_ring_unmap; + mnl_ring_get_frame; + mnl_ring_advance; +} LIBMNL_1.1; diff --git a/src/mmap.c b/src/mmap.c new file mode 100644 index 0000000..348fa89 --- /dev/null +++ b/src/mmap.c @@ -0,0 +1,227 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include <libmnl/libmnl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/mman.h> +#include <poll.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <errno.h> +#include "internal.h" + + +struct mnl_ring { + void *ring; + unsigned int frame_size; + unsigned int frame_max; + unsigned int block_size; + unsigned int head; +}; + +/* + * ring descriptor + */ +struct mnl_ring_socket { + const struct mnl_socket *sock; + + struct mnl_ring *tx_ring; + struct mnl_ring *rx_ring; +}; + + +static inline struct mnl_ring *get_mnl_ring(struct mnl_ring_socket *nlm, enum mnl_ring_types type) +{ + return type == MNL_RING_TX ? nlm->tx_ring : nlm->rx_ring; +} + + +static struct mnl_ring *alloc_ring(const struct nl_mmap_req *req) +{ + struct mnl_ring *ring; + + ring = calloc(sizeof(struct mnl_ring), 1); + if (ring == NULL) + return NULL; + + ring->frame_size = req->nm_frame_size; + ring->frame_max = req->nm_frame_nr - 1; + ring->block_size = req->nm_block_size; + + return ring; +} + +/** + * \defgroup socket Netlink mmapped socket helpers + * @{ + */ + +/** + * mnl_ring_map - setup a ring descriptor + * \param nl opend mnl_socket + * \param tx_req setsockopt param for tx ring + * \param rx_req setsockopt param for rx ring + * + * return NULL if fail + */ +struct mnl_ring_socket * +mnl_ring_map(const struct mnl_socket *nl, struct nl_mmap_req *tx_req, struct nl_mmap_req *rx_req) +{ + void *ring; + struct mnl_ring_socket *nlm; + size_t tx_size = 0, rx_size = 0; + + nlm = calloc(sizeof(struct mnl_ring_socket), 1); + if (nlm == NULL) + return NULL; + nlm->sock = nl; + + if (tx_req != NULL) { + nlm->tx_ring = alloc_ring(tx_req); + if (nlm->tx_ring == NULL) + goto error; + if (mnl_socket_setsockopt(nl, NETLINK_TX_RING, tx_req, sizeof(*tx_req)) < 0) + goto error; + tx_size = tx_req->nm_block_nr * tx_req->nm_block_size; + } + + if (rx_req != NULL) { + nlm->rx_ring = alloc_ring(rx_req); + if (nlm->rx_ring == NULL) + goto error; + if (mnl_socket_setsockopt(nl, NETLINK_RX_RING, rx_req, sizeof(*rx_req)) < 0) + goto error; + rx_size += rx_req->nm_block_nr * rx_req->nm_block_size; + } + + ring = mmap(NULL, tx_size + rx_size, PROT_READ | PROT_WRITE, MAP_SHARED, + mnl_socket_get_fd(nlm->sock), 0); + if (ring == MAP_FAILED) + goto error; + + if (rx_req != NULL && tx_req != NULL) { + nlm->rx_ring->ring = ring; + nlm->tx_ring->ring = ring + rx_size; + } else if (rx_req != NULL) { + nlm->rx_ring->ring = ring; + } else { + nlm->tx_ring->ring = ring; + } + + return nlm; + +error: + if (nlm->tx_ring != NULL) + free(nlm->tx_ring); + if (nlm->rx_ring != NULL) + free(nlm->rx_ring); + free(nlm); + return NULL; +} +EXPORT_SYMBOL(mnl_ring_map); + + +static int unmap_ring(struct mnl_ring *ring) +{ + size_t ring_size; + unsigned int frames_per_block; + int ret; + + frames_per_block = ring->block_size / ring->frame_size; + ring_size = (ring->frame_max + 1 + frames_per_block - 1) / frames_per_block * ring->block_size; + + ret = munmap(ring->ring, ring_size); + if (ret < 0) + return ret; + + ring->ring = NULL; + ring->frame_size = 0; + ring->frame_max = 0; + ring->block_size = 0; + ring->head = 0; + + return ret; +} + +/** + * mnl_ring_unmap - free a given ring descriptor + * \param nlm ring descriptor obtained via mnl_ring_map() + */ +int mnl_ring_unmap(struct mnl_ring_socket *nlm) +{ + int ret; + + /* XXX: how's sockopt going? */ + if (nlm->tx_ring != NULL) { + ret = unmap_ring(nlm->tx_ring); + if (ret < 0) + return ret; + free(nlm->tx_ring); + nlm->tx_ring = NULL; + } + if (nlm->rx_ring != NULL) { + ret = unmap_ring(nlm->rx_ring); + if (ret < 0) + return ret; + free(nlm->rx_ring); + nlm->rx_ring = NULL; + } + + free(nlm); + return 1; +} +EXPORT_SYMBOL(mnl_ring_unmap); + +/** + * mnl_ring_get_frame - get current frame + * \param nlm ring descriptor + * \param type ring type either MNL_RING_RX or MNL_RING_TX + */ +struct nl_mmap_hdr *mnl_ring_get_frame(struct mnl_ring_socket *nlm, enum mnl_ring_types type) +{ + unsigned int frames_per_block, block_pos, frame_off; + struct mnl_ring *ring; + + ring = get_mnl_ring(nlm, type); + if (ring == NULL) { + errno = EBADR; + return NULL; + } + + frames_per_block = ring->block_size / ring->frame_size; + block_pos = ring->head / frames_per_block; + frame_off = ring->head % frames_per_block; + + return (struct nl_mmap_hdr *)(ring->ring + block_pos * ring->block_size + frame_off * ring->frame_size); +} +EXPORT_SYMBOL(mnl_ring_get_frame); + +/** + * mnl_ring_advance - set forward frame pointer + * \param nlm ring descriptor + * \param type ring type either MNL_RING_RX or MNL_RING_TX + */ +int mnl_ring_advance(struct mnl_ring_socket *nlm, enum mnl_ring_types type) +{ + struct mnl_ring *ring; + + ring = get_mnl_ring(nlm, type); + if (ring == NULL) { + errno = EBADR; + return -1; + } + ring->head = ring->head != ring->frame_max ? ring->head + 1 : 0; + + return 1; /* ring->head; */ +} +EXPORT_SYMBOL(mnl_ring_advance); + +/** + * @} + */ -- 1.8.4.rc3 -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html