From b245e4092c5a7f09729e64868a42e13f48ac5db8 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 12 Oct 2010 13:24:08 +0200 Subject: src: allow to use nfct handler for conntrack and expectations at the same time This patch re-works the callback handling to allow the use the same socket to send/receive commands and listen to events of both conntrack and expectation subsystems. Now you can register one callback for conntrack and one for expectation with the same handler with no problems (before this patch, this was not possible, you required two different handlers). Signed-off-by: Pablo Neira Ayuso --- README | 4 ++ include/internal/object.h | 3 +- src/Makefile.am | 2 +- src/callback.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++ src/conntrack/Makefile.am | 2 +- src/conntrack/api.c | 36 +++++++-------- src/conntrack/bsf.c | 30 +++++++++++++ src/conntrack/callback.c | 55 ----------------------- src/expect/Makefile.am | 2 +- src/expect/api.c | 36 +++++++-------- src/expect/callback.c | 55 ----------------------- src/main.c | 24 +++++++--- utils/.gitignore | 1 + utils/Makefile.am | 7 ++- utils/ctexp_events.c | 70 +++++++++++++++++++++++++++++ 15 files changed, 282 insertions(+), 156 deletions(-) create mode 100644 src/callback.c delete mode 100644 src/conntrack/callback.c delete mode 100644 src/expect/callback.c create mode 100644 utils/ctexp_events.c diff --git a/README b/README index f0eed70..4a03627 100644 --- a/README +++ b/README @@ -62,6 +62,10 @@ library only provides the new API that solves former deficiencies. Thus, make sure you use recent versions of libnetfilter_conntrack and, in case that you are using the old API, consider porting your application to the new one. +Since libnetfilter_conntrack >= 0.9.1, you can use the same handler obtained +via nfct_open() to register conntrack and expectation callbacks (before this +version, this was not possible). + = References = [1] Pablo Neira Ayuso. Netfilter's Connection Tracking System: diff --git a/include/internal/object.h b/include/internal/object.h index 4263ef0..8d95aa1 100644 --- a/include/internal/object.h +++ b/include/internal/object.h @@ -16,7 +16,8 @@ struct nfct_handle { struct nfnl_subsys_handle *nfnlssh_exp; /* callback handler for the new API */ - struct nfnl_callback nfnl_cb; + struct nfnl_callback nfnl_cb_ct; + struct nfnl_callback nfnl_cb_exp; int (*cb)(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, diff --git a/src/Makefile.am b/src/Makefile.am index 2e9ccd0..82030c4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,4 +15,4 @@ libnetfilter_conntrack_la_LIBADD = conntrack/libnfconntrack.la \ ${LIBNFNETLINK_LIBS} libnetfilter_conntrack_la_LDFLAGS = -Wc,-nostartfiles -lnfnetlink -ldl \ -version-info $(LIBVERSION) -libnetfilter_conntrack_la_SOURCES = main.c +libnetfilter_conntrack_la_SOURCES = main.c callback.c diff --git a/src/callback.c b/src/callback.c new file mode 100644 index 0000000..fd8b788 --- /dev/null +++ b/src/callback.c @@ -0,0 +1,111 @@ +/* + * (C) 2006-2010 by Pablo Neira Ayuso + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include "internal/internal.h" + +static int __parse_message(const struct nlmsghdr *nlh) +{ + u_int16_t type = NFNL_MSG_TYPE(nlh->nlmsg_type); + u_int16_t flags = nlh->nlmsg_flags; + int ret = NFCT_T_UNKNOWN; + + switch(type) { + case IPCTNL_MSG_CT_NEW: /* same value for IPCTNL_MSG_EXP_NEW. */ + if (flags & (NLM_F_CREATE|NLM_F_EXCL)) + ret = NFCT_T_NEW; + else + ret = NFCT_T_UPDATE; + break; + case IPCTNL_MSG_CT_DELETE: /* same value for IPCTNL_MSG_EXP_DELETE. */ + ret = NFCT_T_DESTROY; + break; + } + return ret; +} + +int __callback(struct nlmsghdr *nlh, struct nfattr *nfa[], void *data) +{ + int ret = NFNL_CB_STOP; + unsigned int type; + struct nf_conntrack *ct = NULL; + struct nf_expect *exp = NULL; + struct __data_container *container = data; + u_int8_t subsys = NFNL_SUBSYS_ID(nlh->nlmsg_type); + + if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nfgenmsg))) { + errno = EINVAL; + return NFNL_CB_FAILURE; + } + type = __parse_message(nlh); + if (!(type & container->type)) + return NFNL_CB_CONTINUE; + + switch(subsys) { + case NFNL_SUBSYS_CTNETLINK: + ct = nfct_new(); + if (ct == NULL) + return NFNL_CB_FAILURE; + + __parse_conntrack(nlh, nfa, ct); + + if (container->h->cb) { + ret = container->h->cb(type, ct, container->data); + } else if (container->h->cb2) { + ret = container->h->cb2(nlh, type, ct, + container->data); + } + break; + case NFNL_SUBSYS_CTNETLINK_EXP: + exp = nfexp_new(); + if (exp == NULL) + return NFNL_CB_FAILURE; + + __parse_expect(nlh, nfa, exp); + + if (container->h->expect_cb) { + ret = container->h->expect_cb(type, exp, + container->data); + } else if (container->h->expect_cb2) { + ret = container->h->expect_cb2(nlh, type, exp, + container->data); + } + break; + default: + errno = ENOTSUP; + ret = NFNL_CB_FAILURE; + break; + } + + switch(ret) { + case NFCT_CB_FAILURE: + if (ct) + free(ct); + if (exp) + free(exp); + ret = NFNL_CB_FAILURE; + break; + case NFCT_CB_STOP: + if (ct) + free(ct); + if (exp) + free(exp); + ret = NFNL_CB_STOP; + break; + case NFCT_CB_CONTINUE: + if (ct) + free(ct); + if (exp) + free(exp); + ret = NFNL_CB_CONTINUE; + break; + case NFCT_CB_STOLEN: + ret = NFNL_CB_CONTINUE; + break; + } +out: + return ret; +} diff --git a/src/conntrack/Makefile.am b/src/conntrack/Makefile.am index 68c2d72..34afefb 100644 --- a/src/conntrack/Makefile.am +++ b/src/conntrack/Makefile.am @@ -4,7 +4,7 @@ AM_CFLAGS = -Wall ${LIBNFNETLINK_CFLAGS} noinst_LTLIBRARIES = libnfconntrack.la -libnfconntrack_la_SOURCES = api.c callback.c \ +libnfconntrack_la_SOURCES = api.c \ getter.c setter.c \ parse.c build.c \ snprintf.c \ diff --git a/src/conntrack/api.c b/src/conntrack/api.c index d7f16fe..825afba 100644 --- a/src/conntrack/api.c +++ b/src/conntrack/api.c @@ -226,17 +226,17 @@ int nfct_callback_register(struct nfct_handle *h, container->type = type; container->data = data; - h->nfnl_cb.call = __callback; - h->nfnl_cb.data = container; - h->nfnl_cb.attr_count = CTA_MAX; + h->nfnl_cb_ct.call = __callback; + h->nfnl_cb_ct.data = container; + h->nfnl_cb_ct.attr_count = CTA_MAX; nfnl_callback_register(h->nfnlssh_ct, IPCTNL_MSG_CT_NEW, - &h->nfnl_cb); + &h->nfnl_cb_ct); nfnl_callback_register(h->nfnlssh_ct, IPCTNL_MSG_CT_DELETE, - &h->nfnl_cb); + &h->nfnl_cb_ct); return 0; } @@ -253,11 +253,11 @@ void nfct_callback_unregister(struct nfct_handle *h) nfnl_callback_unregister(h->nfnlssh_ct, IPCTNL_MSG_CT_DELETE); h->cb = NULL; - free(h->nfnl_cb.data); + free(h->nfnl_cb_ct.data); - h->nfnl_cb.call = NULL; - h->nfnl_cb.data = NULL; - h->nfnl_cb.attr_count = 0; + h->nfnl_cb_ct.call = NULL; + h->nfnl_cb_ct.data = NULL; + h->nfnl_cb_ct.attr_count = 0; } /** @@ -300,17 +300,17 @@ int nfct_callback_register2(struct nfct_handle *h, container->type = type; container->data = data; - h->nfnl_cb.call = __callback; - h->nfnl_cb.data = container; - h->nfnl_cb.attr_count = CTA_MAX; + h->nfnl_cb_ct.call = __callback; + h->nfnl_cb_ct.data = container; + h->nfnl_cb_ct.attr_count = CTA_MAX; nfnl_callback_register(h->nfnlssh_ct, IPCTNL_MSG_CT_NEW, - &h->nfnl_cb); + &h->nfnl_cb_ct); nfnl_callback_register(h->nfnlssh_ct, IPCTNL_MSG_CT_DELETE, - &h->nfnl_cb); + &h->nfnl_cb_ct); return 0; } @@ -327,11 +327,11 @@ void nfct_callback_unregister2(struct nfct_handle *h) nfnl_callback_unregister(h->nfnlssh_ct, IPCTNL_MSG_CT_DELETE); h->cb2 = NULL; - free(h->nfnl_cb.data); + free(h->nfnl_cb_ct.data); - h->nfnl_cb.call = NULL; - h->nfnl_cb.data = NULL; - h->nfnl_cb.attr_count = 0; + h->nfnl_cb_ct.call = NULL; + h->nfnl_cb_ct.data = NULL; + h->nfnl_cb_ct.attr_count = 0; } /** diff --git a/src/conntrack/bsf.c b/src/conntrack/bsf.c index 69a7f14..ae7fa4a 100644 --- a/src/conntrack/bsf.c +++ b/src/conntrack/bsf.c @@ -8,6 +8,7 @@ #include "internal/internal.h" #include "internal/stack.h" #include +#include /* offsetof */ #ifndef SKF_AD_NLATTR #define SKF_AD_NLATTR 12 @@ -214,6 +215,33 @@ nfct_bsf_jump_to(struct sock_filter *this, int line, int pos) return NEW_POS(__code); }; +/* this helps to skip messages coming from the ctnetlink expectation subsys. */ +static int +bsf_cmp_subsys(struct sock_filter *this, int pos, u_int8_t subsys) +{ + struct sock_filter __code[] = { + [0] = { + /* X = offset to nlh->nlmsg_type */ + .code = BPF_LDX|BPF_IMM, + .k = offsetof(struct nlmsghdr, nlmsg_type), + }, + [1] = { + /* A = skb->data[X+k:B] (subsys_id) */ + .code = BPF_LD|BPF_B|BPF_IND, + .k = sizeof(u_int8_t), + }, + [2] = { + /* A == subsys ? jump +1 : accept */ + .code = BPF_JMP|BPF_JEQ|BPF_K, + .k = subsys, + .jt = 1, + .jf = 0, + }, + }; + memcpy(&this[pos], &__code, sizeof(__code)); + return NEW_POS(__code); +} + static int add_state_filter_cta(struct sock_filter *this, unsigned int cta_protoinfo_proto, @@ -584,6 +612,8 @@ int __setup_netlink_socket_filter(int fd, struct nfct_filter *f) memset(bsf, 0, sizeof(bsf)); + j += bsf_cmp_subsys(&bsf[j], j, NFNL_SUBSYS_CTNETLINK); + j += nfct_bsf_ret_verdict(bsf, NFCT_FILTER_ACCEPT, j); j += bsf_add_proto_filter(f, &bsf[j]); j += bsf_add_saddr_ipv4_filter(f, &bsf[j]); j += bsf_add_daddr_ipv4_filter(f, &bsf[j]); diff --git a/src/conntrack/callback.c b/src/conntrack/callback.c deleted file mode 100644 index c83a564..0000000 --- a/src/conntrack/callback.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * (C) 2006 by Pablo Neira Ayuso - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - */ - -#include "internal/internal.h" - -int __callback(struct nlmsghdr *nlh, struct nfattr *nfa[], void *data) -{ - int ret = NFNL_CB_STOP; - unsigned int type; - struct nf_conntrack *ct; - int len = nlh->nlmsg_len; - struct __data_container *container = data; - - len -= NLMSG_LENGTH(sizeof(struct nfgenmsg)); - if (len < 0) - return NFNL_CB_CONTINUE; - - type = __parse_message_type(nlh); - if (!(type & container->type)) - return NFNL_CB_CONTINUE; - - ct = nfct_new(); - if (!ct) - return NFNL_CB_CONTINUE; - - __parse_conntrack(nlh, nfa, ct); - - if (container->h->cb) - ret = container->h->cb(type, ct, container->data); - else if (container->h->cb2) - ret = container->h->cb2(nlh, type, ct, container->data); - - switch(ret) { - case NFCT_CB_FAILURE: - free(ct); - ret = NFNL_CB_FAILURE; - break; - case NFCT_CB_STOP: - free(ct); - ret = NFNL_CB_STOP; - break; - case NFCT_CB_CONTINUE: - free(ct); - ret = NFNL_CB_CONTINUE; - break; - case NFCT_CB_STOLEN: - ret = NFNL_CB_CONTINUE; - break; - } - return ret; -} diff --git a/src/expect/Makefile.am b/src/expect/Makefile.am index b0404ba..380befa 100644 --- a/src/expect/Makefile.am +++ b/src/expect/Makefile.am @@ -4,7 +4,7 @@ AM_CFLAGS = -Wall ${LIBNFNETLINK_CFLAGS} noinst_LTLIBRARIES = libnfexpect.la -libnfexpect_la_SOURCES = api.c callback.c \ +libnfexpect_la_SOURCES = api.c \ getter.c setter.c \ parse.c build.c \ snprintf.c \ diff --git a/src/expect/api.c b/src/expect/api.c index d560178..35c8672 100644 --- a/src/expect/api.c +++ b/src/expect/api.c @@ -139,17 +139,17 @@ int nfexp_callback_register(struct nfct_handle *h, container->type = type; container->data = data; - h->nfnl_cb.call = __expect_callback; - h->nfnl_cb.data = container; - h->nfnl_cb.attr_count = CTA_EXPECT_MAX; + h->nfnl_cb_exp.call = __callback; + h->nfnl_cb_exp.data = container; + h->nfnl_cb_exp.attr_count = CTA_EXPECT_MAX; nfnl_callback_register(h->nfnlssh_exp, IPCTNL_MSG_EXP_NEW, - &h->nfnl_cb); + &h->nfnl_cb_exp); nfnl_callback_register(h->nfnlssh_exp, IPCTNL_MSG_EXP_DELETE, - &h->nfnl_cb); + &h->nfnl_cb_exp); return 0; } @@ -166,11 +166,11 @@ void nfexp_callback_unregister(struct nfct_handle *h) nfnl_callback_unregister(h->nfnlssh_exp, IPCTNL_MSG_EXP_DELETE); h->expect_cb = NULL; - free(h->nfnl_cb.data); + free(h->nfnl_cb_exp.data); - h->nfnl_cb.call = NULL; - h->nfnl_cb.data = NULL; - h->nfnl_cb.attr_count = 0; + h->nfnl_cb_exp.call = NULL; + h->nfnl_cb_exp.data = NULL; + h->nfnl_cb_exp.attr_count = 0; } /** @@ -214,17 +214,17 @@ int nfexp_callback_register2(struct nfct_handle *h, container->type = type; container->data = data; - h->nfnl_cb.call = __expect_callback; - h->nfnl_cb.data = container; - h->nfnl_cb.attr_count = CTA_EXPECT_MAX; + h->nfnl_cb_exp.call = __callback; + h->nfnl_cb_exp.data = container; + h->nfnl_cb_exp.attr_count = CTA_EXPECT_MAX; nfnl_callback_register(h->nfnlssh_exp, IPCTNL_MSG_EXP_NEW, - &h->nfnl_cb); + &h->nfnl_cb_exp); nfnl_callback_register(h->nfnlssh_exp, IPCTNL_MSG_EXP_DELETE, - &h->nfnl_cb); + &h->nfnl_cb_exp); return 0; } @@ -241,11 +241,11 @@ void nfexp_callback_unregister2(struct nfct_handle *h) nfnl_callback_unregister(h->nfnlssh_exp, IPCTNL_MSG_EXP_DELETE); h->expect_cb2 = NULL; - free(h->nfnl_cb.data); + free(h->nfnl_cb_exp.data); - h->nfnl_cb.call = NULL; - h->nfnl_cb.data = NULL; - h->nfnl_cb.attr_count = 0; + h->nfnl_cb_exp.call = NULL; + h->nfnl_cb_exp.data = NULL; + h->nfnl_cb_exp.attr_count = 0; } /** diff --git a/src/expect/callback.c b/src/expect/callback.c deleted file mode 100644 index d2cc26e..0000000 --- a/src/expect/callback.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * (C) 2006-2007 by Pablo Neira Ayuso - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - */ - -#include "internal/internal.h" - -int __expect_callback(struct nlmsghdr *nlh, struct nfattr *nfa[], void *data) -{ - int ret = NFNL_CB_STOP; - unsigned int type; - struct nf_expect *exp; - int len = nlh->nlmsg_len; - struct __data_container *container = data; - - len -= NLMSG_LENGTH(sizeof(struct nfgenmsg)); - if (len < 0) - return NFNL_CB_CONTINUE; - - type = __parse_message_type(nlh); - if (!(type & container->type)) - return NFNL_CB_CONTINUE; - - exp = nfexp_new(); - if (!exp) - return NFNL_CB_CONTINUE; - - __parse_expect(nlh, nfa, exp); - - if (container->h->expect_cb) - ret = container->h->expect_cb(type, exp, container->data); - else if (container->h->expect_cb2) - ret = container->h->expect_cb2(nlh, type, exp, container->data); - - switch(ret) { - case NFCT_CB_FAILURE: - free(exp); - ret = NFNL_CB_FAILURE; - break; - case NFCT_CB_STOP: - free(exp); - ret = NFNL_CB_STOP; - break; - case NFCT_CB_CONTINUE: - free(exp); - ret = NFNL_CB_CONTINUE; - break; - case NFCT_CB_STOLEN: - ret = NFNL_CB_CONTINUE; - break; - } - return ret; -} diff --git a/src/main.c b/src/main.c index 6da4198..7a4f8d0 100644 --- a/src/main.c +++ b/src/main.c @@ -70,7 +70,16 @@ out_free: * \param subscriptions ctnetlink groups to subscribe to events * * This function returns a handler to send commands to and receive replies from - * kernel-space. On error, NULL is returned and errno is explicitly set. + * kernel-space. You can pass the following subsystem IDs: + * + * - NFNL_SUBSYS_CTNETLINK: if you are only interested in conntrack operations + * (excluding expectations). + * - NFNL_SUBSYS_CTNETLINK_EXP: if you are only interested in expectation + * operations (exclude conntracks). + * - NFNL_SUBSYS_NONE: if you are interested in both conntrack and expectation + * operations. + * + * On error, NULL is returned and errno is explicitly set. */ struct nfct_handle *nfct_open(u_int8_t subsys_id, unsigned subscriptions) { @@ -111,11 +120,16 @@ int nfct_close(struct nfct_handle *cth) cth->cb2 = NULL; cth->expect_cb = NULL; cth->expect_cb2 = NULL; - free(cth->nfnl_cb.data); + free(cth->nfnl_cb_ct.data); + free(cth->nfnl_cb_exp.data); + + cth->nfnl_cb_ct.call = NULL; + cth->nfnl_cb_ct.data = NULL; + cth->nfnl_cb_ct.attr_count = 0; - cth->nfnl_cb.call = NULL; - cth->nfnl_cb.data = NULL; - cth->nfnl_cb.attr_count = 0; + cth->nfnl_cb_exp.call = NULL; + cth->nfnl_cb_exp.data = NULL; + cth->nfnl_cb_exp.attr_count = 0; err = nfnl_close(cth->nfnlh); free(cth); diff --git a/utils/.gitignore b/utils/.gitignore index 7dd7bb8..04897ba 100644 --- a/utils/.gitignore +++ b/utils/.gitignore @@ -16,3 +16,4 @@ /expect_events /expect_flush /expect_get +/ctexp_events diff --git a/utils/Makefile.am b/utils/Makefile.am index ec4bb85..dcb3fec 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -6,7 +6,8 @@ check_PROGRAMS = expect_dump expect_create expect_get expect_delete \ conntrack_delete conntrack_flush conntrack_create_nat \ conntrack_get conntrack_events \ conntrack_master conntrack_filter \ - conntrack_grp_create + conntrack_grp_create \ + ctexp_events conntrack_grp_create_SOURCES = conntrack_grp_create.c conntrack_grp_create_LDADD = ../src/libnetfilter_conntrack.la @@ -79,3 +80,7 @@ expect_flush_LDFLAGS = -dynamic -ldl expect_events_SOURCES = expect_events.c expect_events_LDADD = ../src/libnetfilter_conntrack.la expect_events_LDFLAGS = -dynamic -ldl + +ctexp_events_SOURCES = ctexp_events.c +ctexp_events_LDADD = ../src/libnetfilter_conntrack.la +ctexp_events_LDFLAGS = -dynamic -ldl diff --git a/utils/ctexp_events.c b/utils/ctexp_events.c new file mode 100644 index 0000000..1df5729 --- /dev/null +++ b/utils/ctexp_events.c @@ -0,0 +1,70 @@ +#include +#include +#include + +#include + +static int n = 0; + +static int +ct_event_cb(enum nf_conntrack_msg_type type,struct nf_conntrack *ct, void *data) +{ + char buf[1024]; + + nfct_snprintf(buf, 1024, ct, type, NFCT_O_PLAIN, NFCT_OF_TIME); + printf("[CT] %s\n", buf); + + if (++n == 20) + return NFCT_CB_STOP; + + return NFCT_CB_CONTINUE; +} + +static int +exp_event_cb(enum nf_conntrack_msg_type type,struct nf_expect *exp, void *data) +{ + char buf[1024]; + + nfexp_snprintf(buf, 1024, exp, type, NFCT_O_DEFAULT, 0); + printf("[EXP] %s\n", buf); + + if (++n == 20) + return NFCT_CB_STOP; + + return NFCT_CB_CONTINUE; +} + +int main(void) +{ + int ret = 0; + struct nfct_handle *h; + + h = nfct_open(NFNL_SUBSYS_NONE, NF_NETLINK_CONNTRACK_EXP_NEW | + NF_NETLINK_CONNTRACK_EXP_UPDATE | + NF_NETLINK_CONNTRACK_EXP_DESTROY | + NF_NETLINK_CONNTRACK_NEW | + NF_NETLINK_CONNTRACK_UPDATE | + NF_NETLINK_CONNTRACK_DESTROY); + if (h == NULL) { + perror("nfct_open"); + return -1; + } + + nfexp_callback_register(h, NFCT_T_ALL, exp_event_cb, NULL); + nfct_callback_register(h, NFCT_T_ALL, ct_event_cb, NULL); + + printf("TEST: waiting for 20 expectation events...\n"); + + /* we may use nfexp_catch() instead, it would also work. */ + ret = nfct_catch(h); + + printf("TEST: expectation events "); + if (ret == -1) + printf("(%d)(%s)\n", ret, strerror(errno)); + else + printf("(OK)\n"); + + nfct_close(h); + + ret == -1 ? exit(EXIT_FAILURE) : exit(EXIT_SUCCESS); +} -- cgit v1.2.3