Hello Pablo, all. # May you all have a wonderful new year I found original nl-mmap branch is probably enough and follow them. Would you revew my patch? Two things I've changed to nl-mmap. One is holding frame params for each of TX and RX ring. Another is adding two functions instead of mnl_socket_set_ring() - mnl_socket_set_ringopt() and mnl_socket_map_ring(). I think it's difficult to notify what's wrong on error in one function. Any advice is welcome, thanks. --- include/libmnl/libmnl.h | 13 ++++ include/linux/netlink.h | 40 +++++++++- src/libmnl.map | 6 +- src/nlmsg.c | 29 ++++++++ src/socket.c | 193 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 276 insertions(+), 5 deletions(-) diff --git a/include/libmnl/libmnl.h b/include/libmnl/libmnl.h index 223709c..348cd3c 100644 --- a/include/libmnl/libmnl.h +++ b/include/libmnl/libmnl.h @@ -21,8 +21,20 @@ extern "C" { struct mnl_socket; +enum mnl_ring_types { + MNL_RING_RX, + MNL_RING_TX, +}; + +#define MNL_RING_MSGHDR(hdr) ((void *)(hdr) + NL_MMAP_HDRLEN) + extern struct mnl_socket *mnl_socket_open(int type); extern int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid); +extern int mnl_socket_set_ringopt(struct mnl_socket *nl, struct nl_mmap_req *req, + enum mnl_ring_types type); +extern int mnl_socket_map_ring(struct mnl_socket *nl); +extern struct nl_mmap_hdr *mnl_socket_get_frame(const struct mnl_socket *nl, enum mnl_ring_types type); +extern int mnl_socket_advance_ring(const struct mnl_socket *nl, enum mnl_ring_types type); extern int mnl_socket_close(struct mnl_socket *nl); extern int mnl_socket_get_fd(const struct mnl_socket *nl); extern unsigned int mnl_socket_get_portid(const struct mnl_socket *nl); @@ -74,6 +86,7 @@ extern void mnl_nlmsg_batch_reset(struct mnl_nlmsg_batch *b); extern void *mnl_nlmsg_batch_head(struct mnl_nlmsg_batch *b); extern void *mnl_nlmsg_batch_current(struct mnl_nlmsg_batch *b); extern bool mnl_nlmsg_batch_is_empty(struct mnl_nlmsg_batch *b); +extern void mnl_nlmsg_batch_reset_buffer(struct mnl_nlmsg_batch *b, void *buf, size_t limit); /* * Netlink attributes API diff --git a/include/linux/netlink.h b/include/linux/netlink.h index ced0e1a..1a85940 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -1,6 +1,7 @@ -#ifndef __LINUX_NETLINK_H -#define __LINUX_NETLINK_H +#ifndef _UAPI__LINUX_NETLINK_H +#define _UAPI__LINUX_NETLINK_H +#include <linux/kernel.h> #include <linux/socket.h> /* for __kernel_sa_family_t */ #include <linux/types.h> @@ -78,7 +79,7 @@ struct nlmsghdr { #define NLMSG_ALIGNTO 4U #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) #define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) -#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN)) +#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN) #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) #define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) #define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ @@ -105,11 +106,42 @@ struct nlmsgerr { #define NETLINK_PKTINFO 3 #define NETLINK_BROADCAST_ERROR 4 #define NETLINK_NO_ENOBUFS 5 +#define NETLINK_RX_RING 6 +#define NETLINK_TX_RING 7 struct nl_pktinfo { __u32 group; }; +struct nl_mmap_req { + unsigned int nm_block_size; + unsigned int nm_block_nr; + unsigned int nm_frame_size; + unsigned int nm_frame_nr; +}; + +struct nl_mmap_hdr { + unsigned int nm_status; + unsigned int nm_len; + __u32 nm_group; + /* credentials */ + __u32 nm_pid; + __u32 nm_uid; + __u32 nm_gid; +}; + +enum nl_mmap_status { + NL_MMAP_STATUS_UNUSED, + NL_MMAP_STATUS_RESERVED, + NL_MMAP_STATUS_VALID, + NL_MMAP_STATUS_COPY, + NL_MMAP_STATUS_SKIP, +}; + +#define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO +#define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT) +#define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr)) + #define NET_MAJOR 36 /* Major 36 is reserved for networking */ enum { @@ -150,4 +182,4 @@ struct nlattr { #define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) -#endif /* __LINUX_NETLINK_H */ +#endif /* _UAPI__LINUX_NETLINK_H */ diff --git a/src/libmnl.map b/src/libmnl.map index dbc332e..26269c2 100644 --- a/src/libmnl.map +++ b/src/libmnl.map @@ -65,10 +65,14 @@ global: mnl_socket_recvfrom; mnl_socket_sendto; mnl_socket_setsockopt; - local: *; }; LIBMNL_1.1 { mnl_attr_parse_payload; + mnl_socket_set_ringopt; + mnl_socket_map_ring; + mnl_socket_get_frame; + mnl_socket_advance_ring; + mnl_nlmsg_batch_reset_buffer; } LIBMNL_1.0; diff --git a/src/nlmsg.c b/src/nlmsg.c index fdb7af8..5dbe1ba 100644 --- a/src/nlmsg.c +++ b/src/nlmsg.c @@ -569,5 +569,34 @@ bool mnl_nlmsg_batch_is_empty(struct mnl_nlmsg_batch *b) EXPORT_SYMBOL(mnl_nlmsg_batch_is_empty); /** + * mnl_nlmsg_batch_reset_buffer - reset to the new buffer + * \param buf pointer to the new (mmaped tx frame) buffer that will store this + * batch + * \param limit maximum size of the batch (should be half of + * nl_mmap_req.frame_size) + * + * This function is for mmap tx frame, allows to set new buffer (frame) and + * reset a batch, so you can reuse it to create a new one. This function moves + * the last message which does not fit the batch to the head of the new buffer, + * if any. + */ +void mnl_nlmsg_batch_reset_buffer(struct mnl_nlmsg_batch *b, void *buf, size_t limit) +{ + if (b->overflow) { + struct nlmsghdr *nlh = b->cur; + memcpy(buf, b->cur, nlh->nlmsg_len); + b->buflen = nlh->nlmsg_len; + b->cur = buf + b->buflen; + b->overflow = false; + } else { + b->buflen = 0; + b->cur = buf; + } + b->buf = buf; + b->limit = limit; +} +EXPORT_SYMBOL(mnl_nlmsg_batch_reset_buffer); + +/** * @} */ diff --git a/src/socket.c b/src/socket.c index 676a08a..f68047d 100644 --- a/src/socket.c +++ b/src/socket.c @@ -10,6 +10,7 @@ #include <libmnl/libmnl.h> #include <sys/types.h> #include <sys/socket.h> +#include <sys/mman.h> #include <stdlib.h> #include <unistd.h> #include <time.h> @@ -66,9 +67,19 @@ * code tree. */ +struct mnl_ring { + unsigned int head; + void *ring; + unsigned int frame_size; + unsigned int frame_max; + unsigned int block_size; +}; + struct mnl_socket { int fd; struct sockaddr_nl addr; + struct mnl_ring *rx_ring; + struct mnl_ring *tx_ring; }; /** @@ -168,6 +179,184 @@ int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid) } EXPORT_SYMBOL(mnl_socket_bind); +static inline struct mnl_ring *get_mnl_ring(const struct mnl_socket *nl, enum mnl_ring_types type) +{ + struct mnl_ring *ring; + + switch (type) { + case MNL_RING_RX: + ring = nl->rx_ring; + break; + case MNL_RING_TX: + ring = nl->tx_ring; + break; + default: + errno = EINVAL; + return NULL; + break; + } + if (ring == NULL || ring->ring == NULL) { + errno = EBADR; + return NULL; + } + return 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; +} + +/** + * mnl_socket_set_ringopt - set ring opt to prepare for mnl_socket_map_ring() + * \param nl netlink socket obtained via mnl_socket_open() + * \param req setsockopt param for ring + * \param type ring type either MNL_RING_RX or MNL_RING_TX + * + * On success, 0 is returned. On error, this function returns -1, errno is + * appropriately set. + */ +int mnl_socket_set_ringopt(struct mnl_socket *nl, struct nl_mmap_req *req, + enum mnl_ring_types type) +{ + int optype, pre_errno, ret; + struct mnl_ring **ring; + + switch (type) { + case MNL_RING_RX: + ring = &nl->rx_ring; + optype = NETLINK_RX_RING; + break; + case MNL_RING_TX: + ring = &nl->tx_ring; + optype = NETLINK_TX_RING; + break; + default: + errno = EINVAL; + return -1; + break; + } + + if (*ring != NULL) { + errno = EALREADY; + return -1; + } + *ring = alloc_ring(req); + if (*ring == NULL) + return -1; + + ret = mnl_socket_setsockopt(nl, optype, req, sizeof(*req)); + if (ret == -1) { + pre_errno = errno; + free(*ring); + *ring = NULL; + errno = pre_errno; + } + return ret; +} +EXPORT_SYMBOL(mnl_socket_set_ringopt); + +/** + * mnl_socket_map_ring - setup a ring for mnl_socket + * \param nl netlink socket obtained via mnl_socket_open() + * + * This function must be called after setting up by mnl_socket_set_ringopt(). + * On success, 0 is returned. On error, this function returns -1, errno is + * appropriately set and req parameter + */ +int mnl_socket_map_ring(struct mnl_socket *nl) +{ + size_t rx_size = 0, tx_size = 0; + struct mnl_ring *rx_ring = nl->tx_ring, *tx_ring = nl->tx_ring; + void *ring; + + if (rx_ring == NULL && tx_ring == NULL) { + errno = EBADR; + return -1; + } + + if (rx_ring != NULL) + rx_size = ((rx_ring->frame_max + 1) * rx_ring->frame_size + rx_ring->block_size - 1) + & ~(rx_ring->block_size - 1); + if (tx_ring != NULL) + tx_size = ((tx_ring->frame_max + 1) * tx_ring->frame_size + tx_ring->block_size - 1) + & ~(tx_ring->block_size - 1); + ring = mmap(NULL, tx_size + rx_size, PROT_READ | PROT_WRITE, MAP_SHARED, nl->fd, 0); + if (ring == MAP_FAILED) + return -1; + + if (rx_ring != NULL && tx_ring != NULL) { + nl->rx_ring->ring = ring; + nl->tx_ring->ring = ring + rx_size; + } else if (rx_ring != NULL) { + nl->rx_ring->ring = ring; + } else { + nl->tx_ring->ring = ring; + } + + return 0; +} +EXPORT_SYMBOL(mnl_socket_map_ring); + +/** + * mnl_socket_get_frame - get current frame + * \param nl netlink socket obtained via mnl_socket_open() + * \param type ring type either MNL_RING_RX or MNL_RING_TX + * + * On error, it returns NULL and errno is appropriately set. Otherwise, this + * function returns the current nl_mmap_hdr frame in the ring specified by type + * - MNL_RING_RX for receiving or MNL_RING_TX for sending. + */ +struct nl_mmap_hdr *mnl_socket_get_frame(const struct mnl_socket *nl, + enum mnl_ring_types type) +{ + unsigned int frames_per_block, block_pos, frame_off; + struct mnl_ring *ring; + + ring = get_mnl_ring(nl, type); + if (ring == NULL) + 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_socket_get_frame); + +/** + * mnl_socket_advance_ring - set forward frame pointer + * \param nl netlink socket obtained via mnl_socket_open() + * \param type ring type either MNL_RING_RX or MNL_RING_TX + * + * On error, this function returns -1 and errno is appropriately set. On + * success, 0 is returned. + */ +int mnl_socket_advance_ring(const struct mnl_socket *nl, enum mnl_ring_types type) +{ + struct mnl_ring *ring; + + ring = get_mnl_ring(nl, type); + if (ring == NULL) + return -1; + + ring->head = ring->head != ring->frame_max ? ring->head + 1 : 0; + + return 0; +} +EXPORT_SYMBOL(mnl_socket_advance_ring); + /** * mnl_socket_sendto - send a netlink message of a certain size * \param nl netlink socket obtained via mnl_socket_open() @@ -246,6 +435,10 @@ EXPORT_SYMBOL(mnl_socket_recvfrom); int mnl_socket_close(struct mnl_socket *nl) { int ret = close(nl->fd); + if (nl->rx_ring) + free(nl->rx_ring); + if (nl->tx_ring) + free(nl->tx_ring); free(nl); return ret; } -- 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