Signed-off-by: Ken-ichirou MATSUZAWA <chamas@xxxxxxxxxxxxx> --- configure.ac | 2 +- examples/Makefile.am | 2 +- examples/mmap/Makefile.am | 1 + examples/mmap/min/Makefile.am | 26 +++ examples/mmap/min/nf-log.c | 292 ++++++++++++++++++++++++ examples/mmap/min/nf-queue.c | 333 ++++++++++++++++++++++++++++ examples/mmap/min/nfct-dump.c | 378 +++++++++++++++++++++++++++++++ examples/mmap/min/rtnl-addr-dump.c | 204 +++++++++++++++++ examples/mmap/min/rtnl-link-dump.c | 199 +++++++++++++++++ examples/mmap/min/rtnl-route-dump.c | 427 ++++++++++++++++++++++++++++++++++++ 10 files changed, 1862 insertions(+), 2 deletions(-) create mode 100644 examples/mmap/Makefile.am create mode 100644 examples/mmap/min/Makefile.am create mode 100644 examples/mmap/min/nf-log.c create mode 100644 examples/mmap/min/nf-queue.c create mode 100644 examples/mmap/min/nfct-dump.c create mode 100644 examples/mmap/min/rtnl-addr-dump.c create mode 100644 examples/mmap/min/rtnl-link-dump.c create mode 100644 examples/mmap/min/rtnl-route-dump.c diff --git a/configure.ac b/configure.ac index 313a015..1b0b2cd 100644 --- a/configure.ac +++ b/configure.ac @@ -27,5 +27,5 @@ regular_CFLAGS="-Wall -Waggregate-return -Wmissing-declarations \ -Wformat=2 -pipe" AC_SUBST([regular_CPPFLAGS]) AC_SUBST([regular_CFLAGS]) -AC_CONFIG_FILES([Makefile src/Makefile include/Makefile include/libmnl/Makefile include/linux/Makefile include/linux/netfilter/Makefile examples/Makefile examples/genl/Makefile examples/kobject/Makefile examples/netfilter/Makefile examples/rtnl/Makefile libmnl.pc doxygen.cfg]) +AC_CONFIG_FILES([Makefile src/Makefile include/Makefile include/libmnl/Makefile include/linux/Makefile include/linux/netfilter/Makefile examples/Makefile examples/genl/Makefile examples/kobject/Makefile examples/netfilter/Makefile examples/rtnl/Makefile examples/mmap/Makefile examples/mmap/min/Makefile libmnl.pc doxygen.cfg]) AC_OUTPUT diff --git a/examples/Makefile.am b/examples/Makefile.am index e5cb052..aad6f13 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1 +1 @@ -SUBDIRS = genl kobject netfilter rtnl +SUBDIRS = genl kobject netfilter rtnl mmap diff --git a/examples/mmap/Makefile.am b/examples/mmap/Makefile.am new file mode 100644 index 0000000..a532ec0 --- /dev/null +++ b/examples/mmap/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = min diff --git a/examples/mmap/min/Makefile.am b/examples/mmap/min/Makefile.am new file mode 100644 index 0000000..5b172c2 --- /dev/null +++ b/examples/mmap/min/Makefile.am @@ -0,0 +1,26 @@ +include $(top_srcdir)/Make_global.am + +check_PROGRAMS = nf-log \ + nf-queue \ + nfct-dump \ + rtnl-addr-dump \ + rtnl-link-dump \ + rtnl-route-dump + +nf_log_SOURCES = nf-log.c +nf_log_LDADD = ../../../src/libmnl.la + +nf_queue_SOURCES = nf-queue.c +nf_queue_LDADD = ../../../src/libmnl.la + +nfct_dump_SOURCES = nfct-dump.c +nfct_dump_LDADD = ../../../src/libmnl.la + +rtnl_addr_dump_SOURCES = rtnl-addr-dump.c +rtnl_addr_dump_LDADD = ../../../src/libmnl.la + +rtnl_link_dump_SOURCES = rtnl-link-dump.c +rtnl_link_dump_LDADD = ../../../src/libmnl.la + +rtnl_route_dump_SOURCES = rtnl-route-dump.c +rtnl_route_dump_LDADD = ../../../src/libmnl.la diff --git a/examples/mmap/min/nf-log.c b/examples/mmap/min/nf-log.c new file mode 100644 index 0000000..5dbf359 --- /dev/null +++ b/examples/mmap/min/nf-log.c @@ -0,0 +1,292 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <time.h> +#include <arpa/inet.h> +#include <errno.h> +#include <poll.h> + +#include <libmnl/libmnl.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nfnetlink.h> + +#ifndef aligned_be64 +#define aligned_be64 u_int64_t __attribute__((aligned(8))) +#endif + +#include <linux/netfilter/nfnetlink_log.h> + +static int parse_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, NFULA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFULA_MARK: + case NFULA_IFINDEX_INDEV: + case NFULA_IFINDEX_OUTDEV: + case NFULA_IFINDEX_PHYSINDEV: + case NFULA_IFINDEX_PHYSOUTDEV: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFULA_TIMESTAMP: + if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, + sizeof(struct nfulnl_msg_packet_timestamp)) < 0) { + perror("mnl_attr_validate2"); + return MNL_CB_ERROR; + } + break; + case NFULA_HWADDR: + if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, + sizeof(struct nfulnl_msg_packet_hw)) < 0) { + perror("mnl_attr_validate2"); + return MNL_CB_ERROR; + } + break; + case NFULA_PREFIX: + if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFULA_PAYLOAD: + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int log_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[NFULA_MAX+1] = {}; + struct nfulnl_msg_packet_hdr *ph = NULL; + const char *prefix = NULL; + uint32_t mark = 0; + + mnl_attr_parse(nlh, sizeof(struct nfgenmsg), parse_attr_cb, tb); + if (tb[NFULA_PACKET_HDR]) + ph = mnl_attr_get_payload(tb[NFULA_PACKET_HDR]); + if (tb[NFULA_PREFIX]) + prefix = mnl_attr_get_str(tb[NFULA_PREFIX]); + if (tb[NFULA_MARK]) + mark = ntohl(mnl_attr_get_u32(tb[NFULA_MARK])); + + printf("log received (prefix=\"%s\" hw=0x%04x hook=%u mark=%u)\n", + prefix ? prefix : "", ntohs(ph->hw_protocol), ph->hook, + mark); + + return MNL_CB_OK; +} + +static struct nlmsghdr * +nflog_build_cfg_pf_request(char *buf, uint8_t command) +{ + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG; + nlh->nlmsg_flags = NLM_F_REQUEST; + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_INET; + nfg->version = NFNETLINK_V0; + + struct nfulnl_msg_config_cmd cmd = { + .command = command, + }; + mnl_attr_put(nlh, NFULA_CFG_CMD, sizeof(cmd), &cmd); + + return nlh; +} + +static struct nlmsghdr * +nflog_build_cfg_request(char *buf, uint8_t command, int qnum) +{ + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG; + nlh->nlmsg_flags = NLM_F_REQUEST; + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_INET; + nfg->version = NFNETLINK_V0; + nfg->res_id = htons(qnum); + + struct nfulnl_msg_config_cmd cmd = { + .command = command, + }; + mnl_attr_put(nlh, NFULA_CFG_CMD, sizeof(cmd), &cmd); + + return nlh; +} + +static struct nlmsghdr * +nflog_build_cfg_params(char *buf, uint8_t mode, int range, int qnum) +{ + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG; + nlh->nlmsg_flags = NLM_F_REQUEST; + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_UNSPEC; + nfg->version = NFNETLINK_V0; + nfg->res_id = htons(qnum); + + struct nfulnl_msg_config_mode params = { + .copy_range = htonl(range), + .copy_mode = mode, + }; + mnl_attr_put(nlh, NFULA_CFG_MODE, sizeof(params), ¶ms); + + return nlh; +} + +static int mnl_socket_poll(struct mnl_socket *nl) +{ + struct pollfd pfds[1]; + + while (1) { + pfds[0].fd = mnl_socket_get_fd(nl); + pfds[0].events = POLLIN | POLLERR; + pfds[0].revents = 0; + + if (poll(pfds, 1, -1) < 0 && errno != -EINTR) + return -1; + + if (pfds[0].revents & POLLIN) + return 0; + if (pfds[0].revents & POLLERR) + return -1; + } +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + struct mnl_ring_socket *nlm; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nl_mmap_hdr *hdr; + struct nlmsghdr *nlh; + struct nl_mmap_req nlmr = MNL_MMAP_DEFAULT_REQ; + ssize_t len; + void *ptr; + int ret; + unsigned int portid, qnum; + + if (argc != 2) { + printf("Usage: %s [queue_num]\n", argv[0]); + exit(EXIT_FAILURE); + } + qnum = atoi(argv[1]); + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + nlm = mnl_ring_map(nl, &nlmr, &nlmr); + if (nlm == NULL) { + perror("mnl_ring_map"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + hdr = mnl_ring_get_frame(nlm, MNL_RING_TX); + nlh = nflog_build_cfg_pf_request(MNL_MMAP_MSGHDR(hdr), NFULNL_CFG_CMD_PF_UNBIND); + hdr->nm_len = nlh->nlmsg_len; + hdr->nm_status = NL_MMAP_STATUS_VALID; + /* + * nframes++; + * assert(nframes < nlmr.nm_frame_nr); + */ + if (mnl_socket_sendto(nl, NULL, 0) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + mnl_ring_advance(nlm, MNL_RING_TX); + + hdr = mnl_ring_get_frame(nlm, MNL_RING_TX); + nlh = nflog_build_cfg_pf_request(MNL_MMAP_MSGHDR(hdr), NFULNL_CFG_CMD_PF_BIND); + hdr->nm_len = nlh->nlmsg_len; + hdr->nm_status = NL_MMAP_STATUS_VALID; + if (mnl_socket_sendto(nl, NULL, 0) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + mnl_ring_advance(nlm, MNL_RING_TX); + + hdr = mnl_ring_get_frame(nlm, MNL_RING_TX); + nlh = nflog_build_cfg_request(MNL_MMAP_MSGHDR(hdr), NFULNL_CFG_CMD_BIND, qnum); + hdr->nm_len = nlh->nlmsg_len; + hdr->nm_status = NL_MMAP_STATUS_VALID; + if (mnl_socket_sendto(nl, NULL, 0) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + mnl_ring_advance(nlm, MNL_RING_TX); + + hdr = mnl_ring_get_frame(nlm, MNL_RING_TX); + nlh = nflog_build_cfg_params(MNL_MMAP_MSGHDR(hdr), NFULNL_COPY_PACKET, 0xFFFF, qnum); + hdr->nm_len = nlh->nlmsg_len; + hdr->nm_status = NL_MMAP_STATUS_VALID; + if (mnl_socket_sendto(nl, NULL, 0) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + mnl_ring_advance(nlm, MNL_RING_TX); + + while (1) { + ret = mnl_socket_poll(nl); + if (ret < 0) { + perror("mnl_socket_poll"); + exit(EXIT_FAILURE); + } + + while (1) { + hdr = mnl_ring_get_frame(nlm, MNL_RING_RX); + + if (hdr->nm_status == NL_MMAP_STATUS_VALID) { + ptr = MNL_MMAP_MSGHDR(hdr); + len = hdr->nm_len; + if (len == 0) + goto next; + } else if (hdr->nm_status == NL_MMAP_STATUS_COPY) { + len = recv(mnl_socket_get_fd(nl), + buf, sizeof(buf), MSG_DONTWAIT); + if (len <= 0) + break; + ptr = buf; + } else + break; + + ret = mnl_cb_run(ptr, len, 0, portid, log_cb, NULL); + if (ret < 0) + goto end; +next: + hdr->nm_status = NL_MMAP_STATUS_UNUSED; + mnl_ring_advance(nlm, MNL_RING_RX); + } + } +end: + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + + mnl_ring_unmap(nlm); + mnl_socket_close(nl); + + return 0; +} diff --git a/examples/mmap/min/nf-queue.c b/examples/mmap/min/nf-queue.c new file mode 100644 index 0000000..7efda6e --- /dev/null +++ b/examples/mmap/min/nf-queue.c @@ -0,0 +1,333 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <time.h> +#include <poll.h> +#include <errno.h> +#include <arpa/inet.h> + +#include <libmnl/libmnl.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nfnetlink.h> + +#ifndef aligned_be64 +#define aligned_be64 u_int64_t __attribute__((aligned(8))) +#endif + +#include <linux/netfilter/nfnetlink_queue.h> + +static int parse_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, NFQA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFQA_MARK: + case NFQA_IFINDEX_INDEV: + case NFQA_IFINDEX_OUTDEV: + case NFQA_IFINDEX_PHYSINDEV: + case NFQA_IFINDEX_PHYSOUTDEV: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFQA_TIMESTAMP: + if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, + sizeof(struct nfqnl_msg_packet_timestamp)) < 0) { + perror("mnl_attr_validate2"); + return MNL_CB_ERROR; + } + break; + case NFQA_HWADDR: + if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, + sizeof(struct nfqnl_msg_packet_hw)) < 0) { + perror("mnl_attr_validate2"); + return MNL_CB_ERROR; + } + break; + case NFQA_PAYLOAD: + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int queue_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[NFQA_MAX+1] = {}; + struct nfqnl_msg_packet_hdr *ph = NULL; + uint32_t id = 0; + + mnl_attr_parse(nlh, sizeof(struct nfgenmsg), parse_attr_cb, tb); + if (tb[NFQA_PACKET_HDR]) { + ph = mnl_attr_get_payload(tb[NFQA_PACKET_HDR]); + id = ntohl(ph->packet_id); + + printf("packet received (id=%u hw=0x%04x hook=%u)\n", + id, ntohs(ph->hw_protocol), ph->hook); + } + + return MNL_CB_OK + id; +} + +static struct nlmsghdr * +nfq_build_cfg_pf_request(struct mnl_ring_socket *nlm, uint8_t command) +{ + struct nl_mmap_hdr *hdr; + + hdr = mnl_ring_get_frame(nlm, MNL_RING_TX); + if (hdr->nm_status != NL_MMAP_STATUS_UNUSED) + return NULL; + mnl_ring_advance(nlm, MNL_RING_TX); + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(MNL_MMAP_MSGHDR(hdr)); + nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG; + nlh->nlmsg_flags = NLM_F_REQUEST; + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_UNSPEC; + nfg->version = NFNETLINK_V0; + + struct nfqnl_msg_config_cmd cmd = { + .command = command, + .pf = htons(AF_INET), + }; + mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(cmd), &cmd); + + hdr->nm_len = nlh->nlmsg_len; + hdr->nm_status = NL_MMAP_STATUS_VALID; + return nlh; +} + +static struct nlmsghdr * +nfq_build_cfg_request(struct mnl_ring_socket *nlm, uint8_t command, int queue_num) +{ + struct nl_mmap_hdr *hdr; + + hdr = mnl_ring_get_frame(nlm, MNL_RING_TX); + if (hdr->nm_status != NL_MMAP_STATUS_UNUSED) + return NULL; + mnl_ring_advance(nlm, MNL_RING_TX); + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(MNL_MMAP_MSGHDR(hdr)); + nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG; + nlh->nlmsg_flags = NLM_F_REQUEST; + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_UNSPEC; + nfg->version = NFNETLINK_V0; + nfg->res_id = htons(queue_num); + + struct nfqnl_msg_config_cmd cmd = { + .command = command, + .pf = htons(AF_INET), + }; + mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(cmd), &cmd); + + hdr->nm_len = nlh->nlmsg_len; + hdr->nm_status = NL_MMAP_STATUS_VALID; + return nlh; +} + +static struct nlmsghdr * +nfq_build_cfg_params(struct mnl_ring_socket *nlm, uint8_t mode, int range, int queue_num) +{ + struct nl_mmap_hdr *hdr; + + hdr = mnl_ring_get_frame(nlm, MNL_RING_TX); + if (hdr->nm_status != NL_MMAP_STATUS_UNUSED) + return NULL; + mnl_ring_advance(nlm, MNL_RING_TX); + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(MNL_MMAP_MSGHDR(hdr)); + nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG; + nlh->nlmsg_flags = NLM_F_REQUEST; + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_UNSPEC; + nfg->version = NFNETLINK_V0; + nfg->res_id = htons(queue_num); + + struct nfqnl_msg_config_params params = { + .copy_range = htonl(range), + .copy_mode = mode, + }; + mnl_attr_put(nlh, NFQA_CFG_PARAMS, sizeof(params), ¶ms); + + hdr->nm_len = nlh->nlmsg_len; + hdr->nm_status = NL_MMAP_STATUS_VALID; + return nlh; +} + +static struct nlmsghdr * +nfq_build_verdict(struct mnl_ring_socket *nlm, int id, int queue_num, int verd) +{ + struct nl_mmap_hdr *hdr; + + hdr = mnl_ring_get_frame(nlm, MNL_RING_TX); + if (hdr->nm_status != NL_MMAP_STATUS_UNUSED) + return NULL; + mnl_ring_advance(nlm, MNL_RING_TX); + + struct nlmsghdr *nlh; + struct nfgenmsg *nfg; + + nlh = mnl_nlmsg_put_header(MNL_MMAP_MSGHDR(hdr)); + nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_VERDICT; + nlh->nlmsg_flags = NLM_F_REQUEST; + nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_UNSPEC; + nfg->version = NFNETLINK_V0; + nfg->res_id = htons(queue_num); + + struct nfqnl_msg_verdict_hdr vh = { + .verdict = htonl(verd), + .id = htonl(id), + }; + mnl_attr_put(nlh, NFQA_VERDICT_HDR, sizeof(vh), &vh); + + hdr->nm_len = nlh->nlmsg_len; + hdr->nm_status = NL_MMAP_STATUS_VALID; + return nlh; +} + +static int mnl_socket_poll(struct mnl_socket *nl) +{ + struct pollfd pfds[1]; + + while (1) { + pfds[0].fd = mnl_socket_get_fd(nl); + pfds[0].events = POLLIN | POLLERR; + pfds[0].revents = 0; + + if (poll(pfds, 1, -1) < 0 && errno != -EINTR) + return -1; + + if (pfds[0].revents & POLLIN) + return 0; + if (pfds[0].revents & POLLERR) + return -1; + } +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + struct mnl_ring_socket *nlm; + char buf[16384]; + struct nl_mmap_hdr *hdr; + struct nlmsghdr *nlh; + struct nl_mmap_req req = MNL_MMAP_DEFAULT_REQ; + ssize_t len; + void *ptr; + int ret; + unsigned int portid, queue_num; + + if (argc != 2) { + printf("Usage: %s [queue_num]\n", argv[0]); + exit(EXIT_FAILURE); + } + queue_num = atoi(argv[1]); + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + nlm = mnl_ring_map(nl, &req, &req); + if (nlm == NULL) { + perror("mnl_ring_mmap"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + nlh = nfq_build_cfg_pf_request(nlm, NFQNL_CFG_CMD_PF_UNBIND); + + if (mnl_socket_sendto(nl, NULL, 0) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + nlh = nfq_build_cfg_pf_request(nlm, NFQNL_CFG_CMD_PF_BIND); + + if (mnl_socket_sendto(nl, NULL, 0) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + nlh = nfq_build_cfg_request(nlm, NFQNL_CFG_CMD_BIND, queue_num); + + if (mnl_socket_sendto(nl, NULL, 0) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + nlh = nfq_build_cfg_params(nlm, NFQNL_COPY_PACKET, 0xFFFF, queue_num); + + if (mnl_socket_sendto(nl, NULL, 0) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + + while (1) { + uint32_t id; + + ret = mnl_socket_poll(nl); + if (ret < 0) { + perror("mnl_socket_poll"); + exit(EXIT_FAILURE); + } + + while (1) { + hdr = mnl_ring_get_frame(nlm, MNL_RING_RX); + + if (hdr->nm_status == NL_MMAP_STATUS_VALID) { + ptr = MNL_MMAP_MSGHDR(hdr); + len = hdr->nm_len; + if (len == 0) + goto release; + } else if (hdr->nm_status == NL_MMAP_STATUS_COPY) { + len = recv(mnl_socket_get_fd(nl), + buf, sizeof(buf), MSG_DONTWAIT); + if (len <= 0) + break; + ptr = buf; + } else + break; + + ret = mnl_cb_run(ptr, len, 0, portid, queue_cb, NULL); + if (ret < 0){ + perror("mnl_cb_run"); + exit(EXIT_FAILURE); + } + + id = ret - MNL_CB_OK; + nlh = nfq_build_verdict(nlm, id, queue_num, NF_ACCEPT); + if (mnl_socket_sendto(nl, NULL, 0) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } +release: + hdr->nm_status = NL_MMAP_STATUS_UNUSED; + mnl_ring_advance(nlm, MNL_RING_RX); + } + } + + mnl_ring_unmap(nlm); + mnl_socket_close(nl); + + return 0; +} diff --git a/examples/mmap/min/nfct-dump.c b/examples/mmap/min/nfct-dump.c new file mode 100644 index 0000000..2b87caa --- /dev/null +++ b/examples/mmap/min/nfct-dump.c @@ -0,0 +1,378 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <arpa/inet.h> +#include <inttypes.h> +#include <errno.h> +#include <poll.h> + +#include <libmnl/libmnl.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nfnetlink_conntrack.h> + +static int parse_counters_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_COUNTERS_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_COUNTERS_PACKETS: + case CTA_COUNTERS_BYTES: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void print_counters(const struct nlattr *nest) +{ + struct nlattr *tb[CTA_COUNTERS_MAX+1] = {}; + + mnl_attr_parse_nested(nest, parse_counters_cb, tb); + if (tb[CTA_COUNTERS_PACKETS]) { + printf("packets=%"PRIu64" ", + be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_PACKETS]))); + } + if (tb[CTA_COUNTERS_BYTES]) { + printf("bytes=%"PRIu64" ", + be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_BYTES]))); + } +} + +static int parse_ip_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_IP_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_IP_V4_SRC: + case CTA_IP_V4_DST: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTA_IP_V6_SRC: + case CTA_IP_V6_DST: + if (mnl_attr_validate2(attr, MNL_TYPE_BINARY, + sizeof(struct in6_addr)) < 0) { + perror("mnl_attr_validate2"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void print_ip(const struct nlattr *nest) +{ + struct nlattr *tb[CTA_IP_MAX+1] = {}; + + mnl_attr_parse_nested(nest, parse_ip_cb, tb); + if (tb[CTA_IP_V4_SRC]) { + struct in_addr *in = mnl_attr_get_payload(tb[CTA_IP_V4_SRC]); + printf("src=%s ", inet_ntoa(*in)); + } + if (tb[CTA_IP_V4_DST]) { + struct in_addr *in = mnl_attr_get_payload(tb[CTA_IP_V4_DST]); + printf("dst=%s ", inet_ntoa(*in)); + } + if (tb[CTA_IP_V6_SRC]) { + struct in6_addr *in = mnl_attr_get_payload(tb[CTA_IP_V6_SRC]); + char out[INET6_ADDRSTRLEN]; + + if (!inet_ntop(AF_INET6, in, out, sizeof(out))) + printf("src=%s ", out); + } + if (tb[CTA_IP_V6_DST]) { + struct in6_addr *in = mnl_attr_get_payload(tb[CTA_IP_V6_DST]); + char out[INET6_ADDRSTRLEN]; + + if (!inet_ntop(AF_INET6, in, out, sizeof(out))) + printf("dst=%s ", out); + } +} + +static int parse_proto_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_PROTO_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_PROTO_NUM: + case CTA_PROTO_ICMP_TYPE: + case CTA_PROTO_ICMP_CODE: + if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTA_PROTO_SRC_PORT: + case CTA_PROTO_DST_PORT: + case CTA_PROTO_ICMP_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void print_proto(const struct nlattr *nest) +{ + struct nlattr *tb[CTA_PROTO_MAX+1] = {}; + + mnl_attr_parse_nested(nest, parse_proto_cb, tb); + if (tb[CTA_PROTO_NUM]) { + printf("proto=%u ", mnl_attr_get_u8(tb[CTA_PROTO_NUM])); + } + if (tb[CTA_PROTO_SRC_PORT]) { + printf("sport=%u ", + ntohs(mnl_attr_get_u16(tb[CTA_PROTO_SRC_PORT]))); + } + if (tb[CTA_PROTO_DST_PORT]) { + printf("dport=%u ", + ntohs(mnl_attr_get_u16(tb[CTA_PROTO_DST_PORT]))); + } + if (tb[CTA_PROTO_ICMP_ID]) { + printf("id=%u ", + ntohs(mnl_attr_get_u16(tb[CTA_PROTO_ICMP_ID]))); + } + if (tb[CTA_PROTO_ICMP_TYPE]) { + printf("type=%u ", mnl_attr_get_u8(tb[CTA_PROTO_ICMP_TYPE])); + } + if (tb[CTA_PROTO_ICMP_CODE]) { + printf("code=%u ", mnl_attr_get_u8(tb[CTA_PROTO_ICMP_CODE])); + } +} + +static int parse_tuple_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_TUPLE_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_TUPLE_IP: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTA_TUPLE_PROTO: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void print_tuple(const struct nlattr *nest) +{ + struct nlattr *tb[CTA_TUPLE_MAX+1] = {}; + + mnl_attr_parse_nested(nest, parse_tuple_cb, tb); + if (tb[CTA_TUPLE_IP]) { + print_ip(tb[CTA_TUPLE_IP]); + } + if (tb[CTA_TUPLE_PROTO]) { + print_proto(tb[CTA_TUPLE_PROTO]); + } +} + +static int data_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_TUPLE_ORIG: + case CTA_COUNTERS_ORIG: + case CTA_COUNTERS_REPLY: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case CTA_TIMEOUT: + case CTA_MARK: + case CTA_SECMARK: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[CTA_MAX+1] = {}; + struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*nfg), data_attr_cb, tb); + if (tb[CTA_TUPLE_ORIG]) + print_tuple(tb[CTA_TUPLE_ORIG]); + + if (tb[CTA_MARK]) + printf("mark=%u ", ntohl(mnl_attr_get_u32(tb[CTA_MARK]))); + + if (tb[CTA_SECMARK]) + printf("secmark=%u ", ntohl(mnl_attr_get_u32(tb[CTA_SECMARK]))); + + if (tb[CTA_COUNTERS_ORIG]) { + printf("original "); + print_counters(tb[CTA_COUNTERS_ORIG]); + } + + if (tb[CTA_COUNTERS_REPLY]) { + printf("reply "); + print_counters(tb[CTA_COUNTERS_REPLY]); + } + + printf("\n"); + return MNL_CB_OK; +} + +static int mnl_socket_poll(struct mnl_socket *nl) +{ + struct pollfd pfds[1]; + + while (1) { + pfds[0].fd = mnl_socket_get_fd(nl); + pfds[0].events = POLLIN | POLLERR; + pfds[0].revents = 0; + + if (poll(pfds, 1, -1) < 0 && errno != -EINTR) + return -1; + + if (pfds[0].revents & POLLIN) + return 0; + if (pfds[0].revents & POLLERR) + return -1; + } +} + +int main(void) +{ + struct mnl_socket *nl; + struct mnl_ring_socket *nlm; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nl_mmap_hdr *hdr; + struct nlmsghdr *nlh; + struct nfgenmsg *nfh; + struct nl_mmap_req nlmr = MNL_MMAP_DEFAULT_REQ; + ssize_t len; + void *ptr; + int ret; + uint32_t seq, portid; + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + nlm = mnl_ring_map(nl, &nlmr, &nlmr); + if (nlm == NULL) { + perror("mnl_ring_map"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + hdr = mnl_ring_get_frame(nlm, MNL_RING_TX); + nlh = mnl_nlmsg_put_header(MNL_MMAP_MSGHDR(hdr)); + nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET; + nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; + nlh->nlmsg_seq = seq = time(NULL); + + nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); + nfh->nfgen_family = AF_INET; + nfh->version = NFNETLINK_V0; + nfh->res_id = 0; + + hdr->nm_status = NL_MMAP_STATUS_VALID; + hdr->nm_len = nlh->nlmsg_len; + + if (mnl_socket_sendto(nl, NULL, 0) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + mnl_ring_advance(nlm, MNL_RING_TX); + + while (1) { + ret = mnl_socket_poll(nl); + if (ret < 0) { + perror("mnl_socket_poll"); + exit(EXIT_FAILURE); + } + + while (1) { + hdr = mnl_ring_get_frame(nlm, MNL_RING_RX); + + if (hdr->nm_status == NL_MMAP_STATUS_VALID) { + ptr = MNL_MMAP_MSGHDR(hdr); + len = hdr->nm_len; + if (len == 0) + goto next; + } else if (hdr->nm_status == NL_MMAP_STATUS_COPY) { + len = recv(mnl_socket_get_fd(nl), + buf, sizeof(buf), MSG_DONTWAIT); + if (len <= 0) + break; + ptr = buf; + } else + break; + + ret = mnl_cb_run(ptr, len, 0, portid, data_cb, NULL); + if (ret <= 0) + goto end; +next: + hdr->nm_status = NL_MMAP_STATUS_UNUSED; + mnl_ring_advance(nlm, MNL_RING_RX); + } + } +end: + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + + mnl_ring_unmap(nlm); + mnl_socket_close(nl); + + return 0; +} diff --git a/examples/mmap/min/rtnl-addr-dump.c b/examples/mmap/min/rtnl-addr-dump.c new file mode 100644 index 0000000..9679608 --- /dev/null +++ b/examples/mmap/min/rtnl-addr-dump.c @@ -0,0 +1,204 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <arpa/inet.h> +#include <errno.h> +#include <poll.h> + +#include <libmnl/libmnl.h> +#include <linux/if.h> +#include <linux/if_link.h> +#include <linux/rtnetlink.h> + +static int data_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, IFA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case IFA_ADDRESS: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[IFLA_MAX+1] = {}; + struct ifaddrmsg *ifa = mnl_nlmsg_get_payload(nlh); + + printf("index=%d family=%d ", ifa->ifa_index, ifa->ifa_family); + + mnl_attr_parse(nlh, sizeof(*ifa), data_attr_cb, tb); + printf("addr="); + if (tb[IFA_ADDRESS]) { + void *addr = mnl_attr_get_payload(tb[IFA_ADDRESS]); + char out[INET6_ADDRSTRLEN]; + + if (inet_ntop(ifa->ifa_family, addr, out, sizeof(out))) + printf("%s ", out); + } + printf("scope="); + switch(ifa->ifa_scope) { + case 0: + printf("global "); + break; + case 200: + printf("site "); + break; + case 253: + printf("link "); + break; + case 254: + printf("host "); + break; + case 255: + printf("nowhere "); + break; + default: + printf("%d ", ifa->ifa_scope); + break; + } + + printf("\n"); + return MNL_CB_OK; +} + +static int mnl_socket_poll(struct mnl_socket *nl) +{ + struct pollfd pfds[1]; + + while (1) { + pfds[0].fd = mnl_socket_get_fd(nl); + pfds[0].events = POLLIN | POLLERR; + pfds[0].revents = 0; + + if (poll(pfds, 1, -1) < 0 && errno != -EINTR) + return -1; + + if (pfds[0].revents & POLLIN) + return 0; + if (pfds[0].revents & POLLERR) + return -1; + } +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + struct mnl_ring_socket *nlm; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nl_mmap_hdr *hdr; + struct nlmsghdr *nlh; + struct rtgenmsg *rt; + struct nl_mmap_req nlmr = MNL_MMAP_DEFAULT_REQ; + ssize_t len; + void *ptr; + int ret; + unsigned int seq, portid; + unsigned char family = AF_INET; + + if (argc != 2) { + fprintf(stderr, "Usage: %s <inet|inet6>\n", argv[0]); + exit(EXIT_FAILURE); + } + if (strcmp(argv[1], "inet") == 0) + family = AF_INET; + else if (strcmp(argv[1], "inet6") == 0) + family = AF_INET6; + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + nlm = mnl_ring_map(nl, &nlmr, &nlmr); + if (nlm == NULL) { + perror("mnl_ring_map"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + hdr = mnl_ring_get_frame(nlm, MNL_RING_TX); + if (hdr == NULL) { + perror("mnl_ring_get_frame - MNL_RING_TX"); + exit(EXIT_FAILURE); + } + + nlh = mnl_nlmsg_put_header(MNL_MMAP_MSGHDR(hdr)); + nlh->nlmsg_type = RTM_GETADDR; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlh->nlmsg_seq = seq = time(NULL); + rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg)); + rt->rtgen_family = family; + + hdr->nm_len = nlh->nlmsg_len; + hdr->nm_status = NL_MMAP_STATUS_VALID; + + if (mnl_socket_sendto(nl, NULL, 0) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + mnl_ring_advance(nlm, MNL_RING_TX); + + while (1) { + ret = mnl_socket_poll(nl); + if (ret < 0) { + perror("mnl_socket_poll"); + exit(EXIT_FAILURE); + } + + while (1) { + hdr = mnl_ring_get_frame(nlm, MNL_RING_RX); + + if (hdr->nm_status == NL_MMAP_STATUS_VALID) { + ptr = MNL_MMAP_MSGHDR(hdr); + len = hdr->nm_len; + if (len == 0) + goto next; + } else if (hdr->nm_status == NL_MMAP_STATUS_COPY) { + len = recv(mnl_socket_get_fd(nl), + buf, sizeof(buf), MSG_DONTWAIT); + if (len <= 0) + break; + ptr = buf; + } else + break; + + ret = mnl_cb_run(ptr, len, seq, portid, data_cb, NULL); + if (ret <= MNL_CB_STOP) + goto end; +next: + hdr->nm_status = NL_MMAP_STATUS_UNUSED; + mnl_ring_advance(nlm, MNL_RING_RX); + } + } +end: + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + + mnl_ring_unmap(nlm); + mnl_socket_close(nl); + + return 0; +} diff --git a/examples/mmap/min/rtnl-link-dump.c b/examples/mmap/min/rtnl-link-dump.c new file mode 100644 index 0000000..8eea80d --- /dev/null +++ b/examples/mmap/min/rtnl-link-dump.c @@ -0,0 +1,199 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <arpa/inet.h> +#include <errno.h> +#include <poll.h> + +#include <libmnl/libmnl.h> +#include <linux/if.h> +#include <linux/if_link.h> +#include <linux/rtnetlink.h> + +static int data_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, IFLA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case IFLA_ADDRESS: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case IFLA_MTU: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case IFLA_IFNAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[IFLA_MAX+1] = {}; + struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh); + + printf("index=%d type=%d flags=%d family=%d ", + ifm->ifi_index, ifm->ifi_type, + ifm->ifi_flags, ifm->ifi_family); + + if (ifm->ifi_flags & IFF_RUNNING) + printf("[RUNNING] "); + else + printf("[NOT RUNNING] "); + + mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb); + if (tb[IFLA_MTU]) { + printf("mtu=%d ", mnl_attr_get_u32(tb[IFLA_MTU])); + } + if (tb[IFLA_IFNAME]) { + printf("name=%s ", mnl_attr_get_str(tb[IFLA_IFNAME])); + } + if (tb[IFLA_ADDRESS]) { + uint8_t *hwaddr = mnl_attr_get_payload(tb[IFLA_ADDRESS]); + int i; + + printf("hwaddr="); + for (i=0; i<mnl_attr_get_payload_len(tb[IFLA_ADDRESS]); i++) { + printf("%.2x", hwaddr[i] & 0xff); + if (i+1 != mnl_attr_get_payload_len(tb[IFLA_ADDRESS])) + printf(":"); + } + } + printf("\n"); + return MNL_CB_OK; +} + +static int mnl_socket_poll(struct mnl_socket *nl) +{ + struct pollfd pfds[1]; + + while (1) { + pfds[0].fd = mnl_socket_get_fd(nl); + pfds[0].events = POLLIN | POLLERR; + pfds[0].revents = 0; + + if (poll(pfds, 1, -1) < 0 && errno != -EINTR) + return -1; + + if (pfds[0].revents & POLLIN) + return 0; + if (pfds[0].revents & POLLERR) + return -1; + } +} + +int main(void) +{ + struct mnl_socket *nl; + struct mnl_ring_socket *nlm; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nl_mmap_hdr *hdr; + struct nlmsghdr *nlh; + struct rtgenmsg *rt; + struct nl_mmap_req nlmr = MNL_MMAP_DEFAULT_REQ; + ssize_t len; + void *ptr; + int ret; + unsigned int seq, portid; + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + nlm = mnl_ring_map(nl, &nlmr, &nlmr); + if (nlm == NULL) { + perror("mnl_ring_map"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + hdr = mnl_ring_get_frame(nlm, MNL_RING_TX); + if (hdr == NULL) { + perror("mnl_ring_get_frame - MNL_RING_TX"); + exit(EXIT_FAILURE); + } + + nlh = mnl_nlmsg_put_header(MNL_MMAP_MSGHDR(hdr)); + nlh->nlmsg_type = RTM_GETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlh->nlmsg_seq = seq = time(NULL); + rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg)); + rt->rtgen_family = AF_PACKET; + + hdr->nm_status = NL_MMAP_STATUS_VALID; + hdr->nm_len = nlh->nlmsg_len; + + if (mnl_socket_sendto(nl, NULL, 0) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + mnl_ring_advance(nlm, MNL_RING_TX); + + while (1) { + ret = mnl_socket_poll(nl); + if (ret < 0) { + perror("mnl_socket_poll"); + exit(EXIT_FAILURE); + } + + while (1) { + hdr = mnl_ring_get_frame(nlm, MNL_RING_RX); + + if (hdr->nm_status == NL_MMAP_STATUS_VALID) { + ptr = MNL_MMAP_MSGHDR(hdr); + len = hdr->nm_len; + if (len == 0) + goto next; + } else if (hdr->nm_status == NL_MMAP_STATUS_COPY) { + len = recv(mnl_socket_get_fd(nl), + buf, sizeof(buf), MSG_DONTWAIT); + if (len <= 0) + break; + ptr = buf; + } else + break; + + ret = mnl_cb_run(ptr, len, seq, portid, data_cb, NULL); + if (ret <= MNL_CB_STOP) + goto end; +next: + hdr->nm_status = NL_MMAP_STATUS_UNUSED; + mnl_ring_advance(nlm, MNL_RING_RX); + } + } +end: + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + + mnl_ring_unmap(nlm); + mnl_socket_close(nl); + + return 0; +} diff --git a/examples/mmap/min/rtnl-route-dump.c b/examples/mmap/min/rtnl-route-dump.c new file mode 100644 index 0000000..aecb6e8 --- /dev/null +++ b/examples/mmap/min/rtnl-route-dump.c @@ -0,0 +1,427 @@ +/* This example is placed in the public domain. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <arpa/inet.h> +#include <errno.h> +#include <poll.h> + +#include <libmnl/libmnl.h> +#include <linux/if.h> +#include <linux/if_link.h> +#include <linux/rtnetlink.h> + +static int data_attr_cb2(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, RTAX_MAX) < 0) + return MNL_CB_OK; + + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + + tb[mnl_attr_get_type(attr)] = attr; + return MNL_CB_OK; +} + +static void attributes_show_ipv4(struct nlattr *tb[]) +{ + if (tb[RTA_TABLE]) { + printf("table=%u ", mnl_attr_get_u32(tb[RTA_TABLE])); + } + if (tb[RTA_DST]) { + struct in_addr *addr = mnl_attr_get_payload(tb[RTA_DST]); + printf("dst=%s ", inet_ntoa(*addr)); + } + if (tb[RTA_SRC]) { + struct in_addr *addr = mnl_attr_get_payload(tb[RTA_SRC]); + printf("src=%s ", inet_ntoa(*addr)); + } + if (tb[RTA_OIF]) { + printf("oif=%u ", mnl_attr_get_u32(tb[RTA_OIF])); + } + if (tb[RTA_FLOW]) { + printf("flow=%u ", mnl_attr_get_u32(tb[RTA_FLOW])); + } + if (tb[RTA_PREFSRC]) { + struct in_addr *addr = mnl_attr_get_payload(tb[RTA_PREFSRC]); + printf("prefsrc=%s ", inet_ntoa(*addr)); + } + if (tb[RTA_GATEWAY]) { + struct in_addr *addr = mnl_attr_get_payload(tb[RTA_GATEWAY]); + printf("gw=%s ", inet_ntoa(*addr)); + } + if (tb[RTA_PRIORITY]) { + printf("prio=%u ", mnl_attr_get_u32(tb[RTA_PRIORITY])); + } + if (tb[RTA_METRICS]) { + int i; + struct nlattr *tbx[RTAX_MAX+1] = {}; + + mnl_attr_parse_nested(tb[RTA_METRICS], data_attr_cb2, tbx); + + for (i=0; i<RTAX_MAX; i++) { + if (tbx[i]) { + printf("metrics[%d]=%u ", + i, mnl_attr_get_u32(tbx[i])); + } + } + } +} + +/* like inet_ntoa(), not reentrant */ +static const char *inet6_ntoa(struct in6_addr in6) +{ + static char buf[INET6_ADDRSTRLEN]; + + return inet_ntop(AF_INET6, &in6.s6_addr, buf, sizeof(buf)); +} + +static void attributes_show_ipv6(struct nlattr *tb[]) +{ + if (tb[RTA_TABLE]) { + printf("table=%u ", mnl_attr_get_u32(tb[RTA_TABLE])); + } + if (tb[RTA_DST]) { + struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_DST]); + printf("dst=%s ", inet6_ntoa(*addr)); + } + if (tb[RTA_SRC]) { + struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_SRC]); + printf("src=%s ", inet6_ntoa(*addr)); + } + if (tb[RTA_OIF]) { + printf("oif=%u ", mnl_attr_get_u32(tb[RTA_OIF])); + } + if (tb[RTA_FLOW]) { + printf("flow=%u ", mnl_attr_get_u32(tb[RTA_FLOW])); + } + if (tb[RTA_PREFSRC]) { + struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_PREFSRC]); + printf("prefsrc=%s ", inet6_ntoa(*addr)); + } + if (tb[RTA_GATEWAY]) { + struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_GATEWAY]); + printf("gw=%s ", inet6_ntoa(*addr)); + } + if (tb[RTA_PRIORITY]) { + printf("prio=%u ", mnl_attr_get_u32(tb[RTA_PRIORITY])); + } + if (tb[RTA_METRICS]) { + int i; + struct nlattr *tbx[RTAX_MAX+1] = {}; + + mnl_attr_parse_nested(tb[RTA_METRICS], data_attr_cb2, tbx); + + for (i=0; i<RTAX_MAX; i++) { + if (tbx[i]) { + printf("metrics[%d]=%u ", + i, mnl_attr_get_u32(tbx[i])); + } + } + } +} + +static int data_ipv4_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, RTA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case RTA_TABLE: + case RTA_DST: + case RTA_SRC: + case RTA_OIF: + case RTA_FLOW: + case RTA_PREFSRC: + case RTA_GATEWAY: + case RTA_PRIORITY: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case RTA_METRICS: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_ipv6_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, RTA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case RTA_TABLE: + case RTA_OIF: + case RTA_FLOW: + case RTA_PRIORITY: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case RTA_DST: + case RTA_SRC: + case RTA_PREFSRC: + case RTA_GATEWAY: + if (mnl_attr_validate2(attr, MNL_TYPE_BINARY, + sizeof(struct in6_addr)) < 0) { + perror("mnl_attr_validate2"); + return MNL_CB_ERROR; + } + break; + case RTA_METRICS: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RTA_MAX+1] = {}; + struct rtmsg *rm = mnl_nlmsg_get_payload(nlh); + + /* protocol family = AF_INET | AF_INET6 */ + printf("family=%u ", rm->rtm_family); + + /* destination CIDR, eg. 24 or 32 for IPv4 */ + printf("dst_len=%u ", rm->rtm_dst_len); + + /* source CIDR */ + printf("src_len=%u ", rm->rtm_src_len); + + /* type of service (TOS), eg. 0 */ + printf("tos=%u ", rm->rtm_tos); + + /* table id: + * RT_TABLE_UNSPEC = 0 + * + * ... user defined values ... + * + * RT_TABLE_COMPAT = 252 + * RT_TABLE_DEFAULT = 253 + * RT_TABLE_MAIN = 254 + * RT_TABLE_LOCAL = 255 + * RT_TABLE_MAX = 0xFFFFFFFF + * + * Synonimous attribute: RTA_TABLE. + */ + printf("table=%u ", rm->rtm_table); + + /* type: + * RTN_UNSPEC = 0 + * RTN_UNICAST = 1 + * RTN_LOCAL = 2 + * RTN_BROADCAST = 3 + * RTN_ANYCAST = 4 + * RTN_MULTICAST = 5 + * RTN_BLACKHOLE = 6 + * RTN_UNREACHABLE = 7 + * RTN_PROHIBIT = 8 + * RTN_THROW = 9 + * RTN_NAT = 10 + * RTN_XRESOLVE = 11 + * __RTN_MAX = 12 + */ + printf("type=%u ", rm->rtm_type); + + /* scope: + * RT_SCOPE_UNIVERSE = 0 : everywhere in the universe + * + * ... user defined values ... + * + * RT_SCOPE_SITE = 200 + * RT_SCOPE_LINK = 253 : destination attached to link + * RT_SCOPE_HOST = 254 : local address + * RT_SCOPE_NOWHERE = 255 : not existing destination + */ + printf("scope=%u ", rm->rtm_scope); + + /* protocol: + * RTPROT_UNSPEC = 0 + * RTPROT_REDIRECT = 1 + * RTPROT_KERNEL = 2 : route installed by kernel + * RTPROT_BOOT = 3 : route installed during boot + * RTPROT_STATIC = 4 : route installed by administrator + * + * Values >= RTPROT_STATIC are not interpreted by kernel, they are + * just user-defined. + */ + printf("proto=%u ", rm->rtm_protocol); + + /* flags: + * RTM_F_NOTIFY = 0x100: notify user of route change + * RTM_F_CLONED = 0x200: this route is cloned + * RTM_F_EQUALIZE = 0x400: Multipath equalizer: NI + * RTM_F_PREFIX = 0x800: Prefix addresses + */ + printf("flags=%x ", rm->rtm_flags); + + switch(rm->rtm_family) { + case AF_INET: + mnl_attr_parse(nlh, sizeof(*rm), data_ipv4_attr_cb, tb); + attributes_show_ipv4(tb); + break; + case AF_INET6: + mnl_attr_parse(nlh, sizeof(*rm), data_ipv6_attr_cb, tb); + attributes_show_ipv6(tb); + break; + } + + printf("\n"); + return MNL_CB_OK; +} + +static int mnl_socket_poll(struct mnl_socket *nl) +{ + struct pollfd pfds[1]; + + while (1) { + pfds[0].fd = mnl_socket_get_fd(nl); + pfds[0].events = POLLIN | POLLERR; + pfds[0].revents = 0; + + if (poll(pfds, 1, -1) < 0 && errno != -EINTR) + return -1; + + if (pfds[0].revents & POLLIN) + return 0; + if (pfds[0].revents & POLLERR) + return -1; + } +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + struct mnl_ring_socket *nlm; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nl_mmap_hdr *hdr; + struct nlmsghdr *nlh; + struct rtmsg *rtm; + struct nl_mmap_req nlmr = MNL_MMAP_DEFAULT_REQ; + ssize_t len; + void *ptr; + int ret; + unsigned int seq, portid; + unsigned char family = AF_INET; + + if (argc != 2) { + fprintf(stderr, "Usage: %s <inet|inet6>\n", argv[0]); + exit(EXIT_FAILURE); + } + + if (strcmp(argv[1], "inet") == 0) + family = AF_INET; + else if (strcmp(argv[1], "inet6") == 0) + family = AF_INET6; + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + nlm = mnl_ring_map(nl, &nlmr, &nlmr); + if (nlm == NULL) { + perror("mnl_ring_map"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + hdr = mnl_ring_get_frame(nlm, MNL_RING_TX); + if (hdr == NULL) { + perror("mnl_ring_get_frame - MNL_RING_TX"); + exit(EXIT_FAILURE); + } + + nlh = mnl_nlmsg_put_header(MNL_MMAP_MSGHDR(hdr)); + nlh->nlmsg_type = RTM_GETROUTE; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlh->nlmsg_seq = seq = time(NULL); + rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg)); + rtm->rtm_family = family; + + hdr->nm_status = NL_MMAP_STATUS_VALID; + hdr->nm_len = nlh->nlmsg_len; + + if (mnl_socket_sendto(nl, NULL, 0) < 0) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + mnl_ring_advance(nlm, MNL_RING_TX); + + while (1) { + ret = mnl_socket_poll(nl); + if (ret < 0) { + perror("mnl_socket_poll"); + exit(EXIT_FAILURE); + } + + while (1) { + hdr = mnl_ring_get_frame(nlm, MNL_RING_RX); + + if (hdr->nm_status == NL_MMAP_STATUS_VALID) { + ptr = MNL_MMAP_MSGHDR(hdr); + len = hdr->nm_len; + if (len == 0) + goto next; + } else if (hdr->nm_status == NL_MMAP_STATUS_COPY) { + len = recv(mnl_socket_get_fd(nl), + buf, sizeof(buf), MSG_DONTWAIT); + if (len <= 0) + break; + ptr = buf; + } else + break; + + ret = mnl_cb_run(ptr, len, seq, portid, data_cb, NULL); + if (ret <= MNL_CB_STOP) + goto end; +next: + hdr->nm_status = NL_MMAP_STATUS_UNUSED; + mnl_ring_advance(nlm, MNL_RING_RX); + } + } +end: + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + + mnl_ring_unmap(nlm); + mnl_socket_close(nl); + + return 0; +} -- 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