diff options
Diffstat (limited to 'examples/netfilter')
-rw-r--r-- | examples/netfilter/Makefile.am | 10 | ||||
-rw-r--r-- | examples/netfilter/nf-log.c | 17 | ||||
-rw-r--r-- | examples/netfilter/nf-queue.c | 24 | ||||
-rw-r--r-- | examples/netfilter/nfct-create-batch.c | 6 | ||||
-rw-r--r-- | examples/netfilter/nfct-daemon.c | 364 | ||||
-rw-r--r-- | examples/netfilter/nfct-dump.c | 319 |
6 files changed, 711 insertions, 29 deletions
diff --git a/examples/netfilter/Makefile.am b/examples/netfilter/Makefile.am index 98458fd..4bae05f 100644 --- a/examples/netfilter/Makefile.am +++ b/examples/netfilter/Makefile.am @@ -2,8 +2,10 @@ include $(top_srcdir)/Make_global.am check_PROGRAMS = nf-queue \ nf-log \ + nfct-dump \ nfct-event \ - nfct-create-batch + nfct-create-batch \ + nfct-daemon nf_queue_SOURCES = nf-queue.c nf_queue_LDADD = ../../src/libmnl.la @@ -11,6 +13,12 @@ nf_queue_LDADD = ../../src/libmnl.la nf_log_SOURCES = nf-log.c nf_log_LDADD = ../../src/libmnl.la +nfct_dump_SOURCES = nfct-dump.c +nfct_dump_LDADD = ../../src/libmnl.la + +nfct_daemon_SOURCES = nfct-daemon.c +nfct_daemon_LDADD = ../../src/libmnl.la + nfct_event_SOURCES = nfct-event.c nfct_event_LDADD = ../../src/libmnl.la diff --git a/examples/netfilter/nf-log.c b/examples/netfilter/nf-log.c index a862912..4383b66 100644 --- a/examples/netfilter/nf-log.c +++ b/examples/netfilter/nf-log.c @@ -9,11 +9,6 @@ #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) @@ -39,14 +34,14 @@ static int parse_attr_cb(const struct nlattr *attr, void *data) case NFULA_TIMESTAMP: if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(struct nfulnl_msg_packet_timestamp)) < 0) { - perror("mnl_attr_validate"); + 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_validate"); + perror("mnl_attr_validate2"); return MNL_CB_ERROR; } break; @@ -174,28 +169,28 @@ int main(int argc, char *argv[]) nlh = nflog_build_cfg_pf_request(buf, NFULNL_CFG_CMD_PF_UNBIND); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - perror("mnl_socket_send"); + perror("mnl_socket_sendto"); exit(EXIT_FAILURE); } nlh = nflog_build_cfg_pf_request(buf, NFULNL_CFG_CMD_PF_BIND); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - perror("mnl_socket_send"); + perror("mnl_socket_sendto"); exit(EXIT_FAILURE); } nlh = nflog_build_cfg_request(buf, NFULNL_CFG_CMD_BIND, qnum); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - perror("mnl_socket_send"); + perror("mnl_socket_sendto"); exit(EXIT_FAILURE); } nlh = nflog_build_cfg_params(buf, NFULNL_COPY_PACKET, 0xFFFF, qnum); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - perror("mnl_socket_send"); + perror("mnl_socket_sendto"); exit(EXIT_FAILURE); } diff --git a/examples/netfilter/nf-queue.c b/examples/netfilter/nf-queue.c index c66611f..957e365 100644 --- a/examples/netfilter/nf-queue.c +++ b/examples/netfilter/nf-queue.c @@ -9,11 +9,6 @@ #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) @@ -39,14 +34,14 @@ static int parse_attr_cb(const struct nlattr *attr, void *data) case NFQA_TIMESTAMP: if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(struct nfqnl_msg_packet_timestamp)) < 0) { - perror("mnl_attr_validate"); + 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_validate"); + perror("mnl_attr_validate2"); return MNL_CB_ERROR; } break; @@ -67,9 +62,10 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) 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); } - printf("packet received (id=%u hw=0x%04x hook=%u)\n", - id, ntohs(ph->hw_protocol), ph->hook); return MNL_CB_OK + id; } @@ -188,28 +184,28 @@ int main(int argc, char *argv[]) nlh = nfq_build_cfg_pf_request(buf, NFQNL_CFG_CMD_PF_UNBIND); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - perror("mnl_socket_send"); + perror("mnl_socket_sendto"); exit(EXIT_FAILURE); } nlh = nfq_build_cfg_pf_request(buf, NFQNL_CFG_CMD_PF_BIND); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - perror("mnl_socket_send"); + perror("mnl_socket_sendto"); exit(EXIT_FAILURE); } nlh = nfq_build_cfg_request(buf, NFQNL_CFG_CMD_BIND, queue_num); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - perror("mnl_socket_send"); + perror("mnl_socket_sendto"); exit(EXIT_FAILURE); } nlh = nfq_build_cfg_params(buf, NFQNL_COPY_PACKET, 0xFFFF, queue_num); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - perror("mnl_socket_send"); + perror("mnl_socket_sendto"); exit(EXIT_FAILURE); } @@ -230,7 +226,7 @@ int main(int argc, char *argv[]) id = ret - MNL_CB_OK; nlh = nfq_build_verdict(buf, id, queue_num, NF_ACCEPT); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - perror("mnl_socket_send"); + perror("mnl_socket_sendto"); exit(EXIT_FAILURE); } diff --git a/examples/netfilter/nfct-create-batch.c b/examples/netfilter/nfct-create-batch.c index dd6623f..4675789 100644 --- a/examples/netfilter/nfct-create-batch.c +++ b/examples/netfilter/nfct-create-batch.c @@ -67,7 +67,7 @@ static void put_msg(char *buf, uint16_t i, int seq) static int cb_err(const struct nlmsghdr *nlh, void *data) { - struct nlmsgerr *err = (void *)(nlh + 1); + struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh); if (err->error != 0) printf("message with seq %u has failed: %s\n", nlh->nlmsg_seq, strerror(-err->error)); @@ -87,7 +87,7 @@ send_batch(struct mnl_socket *nl, struct mnl_nlmsg_batch *b, int portid) ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(b), len); if (ret == -1) { - perror("mnl_socket_recvfrom"); + perror("mnl_socket_sendto"); exit(EXIT_FAILURE); } @@ -116,7 +116,7 @@ send_batch(struct mnl_socket *nl, struct mnl_nlmsg_batch *b, int portid) NULL, NULL, cb_ctl_array, MNL_ARRAY_SIZE(cb_ctl_array)); if (ret == -1) { - perror("mnl_cb_run"); + perror("mnl_cb_run2"); exit(EXIT_FAILURE); } diff --git a/examples/netfilter/nfct-daemon.c b/examples/netfilter/nfct-daemon.c new file mode 100644 index 0000000..a97c2ec --- /dev/null +++ b/examples/netfilter/nfct-daemon.c @@ -0,0 +1,364 @@ +/* A very simple skeleton code that implements a daemon that collects + * conntrack statistics from ctnetlink. + * + * This example is placed in the public domain. + */ +#include <endian.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <inttypes.h> +#include <errno.h> + +#include <libmnl/libmnl.h> +#include <linux/netlink.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nfnetlink_conntrack.h> + +#include <sys/queue.h> + +struct nstats { + LIST_ENTRY(nstats) list; + + uint8_t family; + + union { + struct in_addr ip; + struct in6_addr ip6; + }; + uint64_t pkts, bytes; +}; + +static LIST_HEAD(nstats_head, nstats) nstats_head; + +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 parse_counters(const struct nlattr *nest, struct nstats *ns) +{ + struct nlattr *tb[CTA_COUNTERS_MAX+1] = {}; + + mnl_attr_parse_nested(nest, parse_counters_cb, tb); + if (tb[CTA_COUNTERS_PACKETS]) + ns->pkts += be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_PACKETS])); + + if (tb[CTA_COUNTERS_BYTES]) + ns->bytes += 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 parse_ip(const struct nlattr *nest, struct nstats *ns) +{ + 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]); + ns->ip = *in; + ns->family = AF_INET; + } + if (tb[CTA_IP_V6_SRC]) { + struct in6_addr *in = mnl_attr_get_payload(tb[CTA_IP_V6_SRC]); + ns->ip6 = *in; + ns->family = AF_INET6; + } +} + +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; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void parse_tuple(const struct nlattr *nest, struct nstats *ns) +{ + struct nlattr *tb[CTA_TUPLE_MAX+1] = {}; + + mnl_attr_parse_nested(nest, parse_tuple_cb, tb); + if (tb[CTA_TUPLE_IP]) + parse_ip(tb[CTA_TUPLE_IP], ns); +} + +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; + } + 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); + struct nstats ns = {}, *cur, *new; + + mnl_attr_parse(nlh, sizeof(*nfg), data_attr_cb, tb); + if (tb[CTA_TUPLE_ORIG]) + parse_tuple(tb[CTA_TUPLE_ORIG], &ns); + + if (tb[CTA_COUNTERS_ORIG]) + parse_counters(tb[CTA_COUNTERS_ORIG], &ns); + + if (tb[CTA_COUNTERS_REPLY]) + parse_counters(tb[CTA_COUNTERS_REPLY], &ns); + + /* Look up for existing statistics object ... */ + LIST_FOREACH(cur, &nstats_head, list) { + if (memcmp(&ns.ip6, &cur->ip6, sizeof(struct in6_addr)) == 0) { + /* ... and sum counters */ + cur->pkts += ns.pkts; + cur->bytes += ns.bytes; + return MNL_CB_OK; + } + } + + /* ... if it does not exist, add new stats object */ + new = calloc(1, sizeof(struct nstats)); + if (!new) + return MNL_CB_OK; + + new->family = ns.family; + new->ip6 = ns.ip6; + new->pkts = ns.pkts; + new->bytes = ns.bytes; + + LIST_INSERT_HEAD(&nstats_head, new, list); + + return MNL_CB_OK; +} + +static int handle(struct mnl_socket *nl) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + int ret; + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + /* It only happens if NETLINK_NO_ENOBUFS is not set, it means + * we are leaking statistics. + */ + if (errno == ENOBUFS) { + fprintf(stderr, "The daemon has hit ENOBUFS, you can " + "increase the size of your receiver " + "buffer to mitigate this or enable " + "reliable delivery.\n"); + } else { + perror("mnl_socket_recvfrom"); + } + return -1; + } + + ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + return -1; + } else if (ret <= MNL_CB_STOP) + return 0; + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nfgenmsg *nfh; + struct nstats *cur; + struct timeval tv = {}; + int ret, secs, on = 1, buffersize = (1 << 22); + + if (argc != 2) { + printf("Usage: %s <poll-secs>\n", argv[0]); + exit(EXIT_FAILURE); + } + secs = atoi(argv[1]); + + LIST_INIT(&nstats_head); + + printf("Polling every %d seconds from kernel...\n", secs); + + /* Set high priority for this process, less chances to overrun + * the netlink receiver buffer since the scheduler gives this process + * more chances to run. + */ + nice(-20); + + /* Open netlink socket to operate with netfilter */ + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + /* Subscribe to destroy events to avoid leaking counters. The same + * socket is used to periodically atomically dump and reset counters. + */ + if (mnl_socket_bind(nl, NF_NETLINK_CONNTRACK_DESTROY, + MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + + /* Set netlink receiver buffer to 16 MBytes, to avoid packet drops */ + setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_RCVBUFFORCE, + &buffersize, sizeof(socklen_t)); + + /* The two tweaks below enable reliable event delivery, packets may + * be dropped if the netlink receiver buffer overruns. This happens ... + * + * a) if the kernel spams this user-space process until the receiver + * is filled. + * + * or: + * + * b) if the user-space process does not pull messages from the + * receiver buffer so often. + */ + mnl_socket_setsockopt(nl, NETLINK_BROADCAST_ERROR, &on, sizeof(int)); + mnl_socket_setsockopt(nl, NETLINK_NO_ENOBUFS, &on, sizeof(int)); + + nlh = mnl_nlmsg_put_header(buf); + /* Counters are atomically zeroed in each dump */ + nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | + IPCTNL_MSG_CT_GET_CTRZERO; + nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; + + nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); + nfh->nfgen_family = AF_INET; + nfh->version = NFNETLINK_V0; + nfh->res_id = 0; + + /* Filter by mark: We only want to dump entries whose mark is zero */ + mnl_attr_put_u32(nlh, CTA_MARK, htonl(0)); + mnl_attr_put_u32(nlh, CTA_MARK_MASK, htonl(0xffffffff)); + + while (1) { + int fd_max = mnl_socket_get_fd(nl); + fd_set readfds; + + /* Every N seconds ... */ + if (tv.tv_sec == 0 && tv.tv_usec == 0) { + /* ... request a fresh dump of the table from kernel */ + ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len); + if (ret == -1) { + perror("mnl_socket_sendto"); + return -1; + } + tv.tv_sec = secs; + tv.tv_usec = 0; + + /* print the content of the list */ + LIST_FOREACH(cur, &nstats_head, list) { + char out[INET6_ADDRSTRLEN]; + + if (inet_ntop(cur->family, &cur->ip, out, sizeof(out))) + printf("src=%s ", out); + + printf("counters %"PRIu64" %"PRIu64"\n", + cur->pkts, cur->bytes); + } + } + + FD_ZERO(&readfds); + FD_SET(mnl_socket_get_fd(nl), &readfds); + + ret = select(fd_max+1, &readfds, NULL, NULL, &tv); + if (ret < 0) { + if (errno == EINTR) + continue; + + perror("select"); + exit(EXIT_FAILURE); + } + + /* Handled event and periodic atomic-dump-and-reset messages */ + if (FD_ISSET(mnl_socket_get_fd(nl), &readfds)) { + if (handle(nl) < 0) + return EXIT_FAILURE; + } + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/examples/netfilter/nfct-dump.c b/examples/netfilter/nfct-dump.c new file mode 100644 index 0000000..114af61 --- /dev/null +++ b/examples/netfilter/nfct-dump.c @@ -0,0 +1,319 @@ +/* This example is placed in the public domain. */ +#include <endian.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <arpa/inet.h> +#include <inttypes.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; +} + +int main(void) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nfgenmsg *nfh; + uint32_t seq, portid; + int ret; + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + + nlh = mnl_nlmsg_put_header(buf); + 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; + + ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len); + if (ret == -1) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + while (1) { + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + + ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + exit(EXIT_FAILURE); + } else if (ret <= MNL_CB_STOP) + break; + } + + mnl_socket_close(nl); + + return 0; +} |