From 4db878d6f81fd64029c48003f4e1ae57069a7c65 Mon Sep 17 00:00:00 2001 From: "/C=EU/ST=EU/CN=Pablo Neira Ayuso/emailAddress=pablo@netfilter.org" Date: Tue, 1 May 2007 18:30:03 +0000 Subject: introduce the new expectation API --- configure.in | 2 +- include/internal.h | 29 ++ .../libnetfilter_conntrack.h | 83 ++++ src/Makefile.am | 5 +- src/conntrack/parse.c | 17 +- src/conntrack/snprintf_default.c | 6 +- src/expect/Makefile.am | 16 + src/expect/api.c | 545 +++++++++++++++++++++ src/expect/build.c | 58 +++ src/expect/callback.c | 53 ++ src/expect/getter.c | 35 ++ src/expect/parse.c | 57 +++ src/expect/setter.c | 35 ++ src/expect/snprintf.c | 33 ++ src/expect/snprintf_default.c | 62 +++ utils/Makefile.am | 6 +- utils/expect_api_test.c | 160 ++++++ 17 files changed, 1182 insertions(+), 20 deletions(-) create mode 100644 src/expect/Makefile.am create mode 100644 src/expect/api.c create mode 100644 src/expect/build.c create mode 100644 src/expect/callback.c create mode 100644 src/expect/getter.c create mode 100644 src/expect/parse.c create mode 100644 src/expect/setter.c create mode 100644 src/expect/snprintf.c create mode 100644 src/expect/snprintf_default.c create mode 100644 utils/expect_api_test.c diff --git a/configure.in b/configure.in index 5581f94..578a82b 100644 --- a/configure.in +++ b/configure.in @@ -70,5 +70,5 @@ LIBNFCONNTRACK_LIBS="$LIBNFNETLINK_LIBS" AC_SUBST(LIBNFCONNTRACK_LIBS) dnl Output the makefile -AC_OUTPUT(Makefile src/Makefile include/Makefile utils/Makefile include/libnetfilter_conntrack/Makefile l3extensions/Makefile extensions/Makefile src/conntrack/Makefile libnetfilter_conntrack.pc) +AC_OUTPUT(Makefile src/Makefile include/Makefile utils/Makefile include/libnetfilter_conntrack/Makefile l3extensions/Makefile extensions/Makefile src/conntrack/Makefile src/expect/Makefile libnetfilter_conntrack.pc) diff --git a/include/internal.h b/include/internal.h index 78020f3..a51e8b2 100644 --- a/include/internal.h +++ b/include/internal.h @@ -50,6 +50,9 @@ struct nfct_handle { int(*cb)(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data); + int(*expect_cb)(enum nf_conntrack_msg_type type, + struct nf_expect *exp, + void *data); }; union __nfct_l4 { @@ -122,6 +125,17 @@ struct nf_conntrack { u_int32_t set[2]; }; +struct nf_expect { + struct nf_conntrack master; + struct nf_conntrack expected; + struct nf_conntrack mask; + u_int32_t timeout; + u_int32_t id; + u_int16_t expectfn_queue_id; + + u_int32_t set[1]; +}; + /* container used to pass data to nfnl callbacks */ struct __data_container { struct nfct_handle *h; @@ -145,9 +159,13 @@ static inline int test_bit(int nr, const u_int32_t *addr) } int __build_conntrack(struct nfnl_subsys_handle *ssh, struct nfnlhdr *req, size_t size, u_int16_t type, u_int16_t flags, const struct nf_conntrack *ct); +void __build_tuple(struct nfnlhdr *req, size_t size, const struct __nfct_tuple *t, const int type); int __parse_message_type(const struct nlmsghdr *nlh); void __parse_conntrack(const struct nlmsghdr *nlh, const struct nfattr *cda[], struct nf_conntrack *ct); +void __parse_tuple(const struct nfattr *attr, struct __nfct_tuple *tuple, int dir, u_int32_t *set); int __snprintf_conntrack(char *buf, unsigned int len, const struct nf_conntrack *ct, unsigned int type, unsigned int msg_output, unsigned int flags); +int __snprintf_address(char *buf, unsigned int len, const struct __nfct_tuple *tuple); +int __snprintf_protocol(char *buf, unsigned int len, const struct nf_conntrack *ct); int __snprintf_conntrack_default(char *buf, unsigned int len, const struct nf_conntrack *ct, const unsigned int msg_type, const unsigned int flags); int __snprintf_conntrack_xml(char *buf, unsigned int len, const struct nf_conntrack *ct, const unsigned int msg_type, const unsigned int flags); @@ -158,4 +176,15 @@ int __setobjopt(struct nf_conntrack *ct, unsigned int option); int __getobjopt(const struct nf_conntrack *ct, unsigned int option); int __compare(const struct nf_conntrack *ct1, const struct nf_conntrack *ct2); +typedef void (*set_exp_attr)(struct nf_expect *exp, const void *value); +typedef const void *(*get_exp_attr)(const struct nf_expect *exp); + +extern set_exp_attr set_exp_attr_array[]; +extern get_exp_attr get_exp_attr_array[]; + +int __build_expect(struct nfnl_subsys_handle *ssh, struct nfnlhdr *req, size_t size, u_int16_t type, u_int16_t flags, const struct nf_expect *exp); +int __parse_expect_message_type(const struct nlmsghdr *nlh); +void __parse_expect(const struct nlmsghdr *nlh, const struct nfattr *cda[], struct nf_expect *exp); +int __expect_callback(struct nlmsghdr *nlh, struct nfattr *nfa[], void *data); + #endif diff --git a/include/libnetfilter_conntrack/libnetfilter_conntrack.h b/include/libnetfilter_conntrack/libnetfilter_conntrack.h index e35e626..3beeef6 100644 --- a/include/libnetfilter_conntrack/libnetfilter_conntrack.h +++ b/include/libnetfilter_conntrack/libnetfilter_conntrack.h @@ -559,6 +559,89 @@ extern int nfct_build_query(struct nfnl_subsys_handle *ssh, void *req, unsigned int size); +/* expectation object */ +struct nf_expect; + +/* expect attributes */ +enum nf_expect_attr { + ATTR_EXP_MASTER = 0, /* pointer to conntrack object */ + ATTR_EXP_EXPECTED, /* pointer to conntrack object */ + ATTR_EXP_MASK, /* pointer to conntrack object */ + ATTR_EXP_TIMEOUT, /* u32 bits */ + ATTR_EXP_MAX +}; + +/* constructor / destructor */ +extern struct nf_expect *nfexp_new(void); +extern void nfexp_destroy(struct nf_expect *exp); + +/* clone */ +extern struct nf_expect *nfexp_clone(const struct nf_expect *exp); + +/* register / unregister callback */ + +extern int nfexp_callback_register(struct nfct_handle *h, + enum nf_conntrack_msg_type type, + int (*cb)(enum nf_conntrack_msg_type type, + struct nf_expect *exp, + void *data), + void *data); + +extern void nfexp_callback_unregister(struct nfct_handle *h); + +/* setter */ +extern void nfexp_set_attr(struct nf_expect *exp, + const enum nf_expect_attr type, + const void *value); + +extern void nfexp_set_attr_u8(struct nf_expect *exp, + const enum nf_expect_attr type, + u_int8_t value); + +extern void nfexp_set_attr_u16(struct nf_expect *exp, + const enum nf_expect_attr type, + u_int16_t value); + +extern void nfexp_set_attr_u32(struct nf_expect *exp, + const enum nf_expect_attr type, + u_int32_t value); + +/* getter */ +extern const void *nfexp_get_attr(const struct nf_expect *exp, + const enum nf_expect_attr type); + +extern u_int8_t nfexp_get_attr_u8(const struct nf_expect *exp, + const enum nf_expect_attr type); + +extern u_int16_t nfexp_get_attr_u16(const struct nf_expect *exp, + const enum nf_expect_attr type); + +extern u_int32_t nfexp_get_attr_u32(const struct nf_expect *exp, + const enum nf_expect_attr type); + +/* checker */ +extern int nfexp_attr_is_set(const struct nf_expect *exp, + const enum nf_expect_attr type); + +/* unsetter */ +extern int nfexp_attr_unset(struct nf_expect *exp, + const enum nf_expect_attr type); + +/* query */ +extern int nfexp_query(struct nfct_handle *h, + const enum nf_conntrack_query qt, + const void *data); + +/* print */ +extern int nfexp_snprintf(char *buf, + unsigned int size, + const struct nf_expect *exp, + const unsigned int msg_type, + const unsigned int out_type, + const unsigned int out_flags); + +extern int nfexp_catch(struct nfct_handle *h); + #ifdef __cplusplus } #endif diff --git a/src/Makefile.am b/src/Makefile.am index 87911ce..544311a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,14 +4,15 @@ include $(top_srcdir)/Make_global.am #EXTRA_DIST = $(man_MANS) acinclude.m4 -SUBDIRS=conntrack +SUBDIRS=conntrack expect AM_CFLAGS = -fPIC -Wall LIBS = @LIBNFCONNTRACK_LIBS@ lib_LTLIBRARIES = libnetfilter_conntrack.la -libnetfilter_conntrack_la_LIBADD = conntrack/libnetfilter_conntrack_new_api.la +libnetfilter_conntrack_la_LIBADD = conntrack/libnetfilter_conntrack_new_api.la \ + expect/libnetfilter_conntrack_expect.la libnetfilter_conntrack_la_LDFLAGS = -Wc,-nostartfiles -lnfnetlink -ldl \ -version-info $(LIBVERSION) libnetfilter_conntrack_la_SOURCES = libnetfilter_conntrack.c diff --git a/src/conntrack/parse.c b/src/conntrack/parse.c index 5cbd302..b9bc095 100644 --- a/src/conntrack/parse.c +++ b/src/conntrack/parse.c @@ -133,10 +133,10 @@ static void __parse_proto(const struct nfattr *attr, } } -static void __parse_tuple(const struct nfattr *attr, - struct __nfct_tuple *tuple, - int dir, - u_int32_t *set) +void __parse_tuple(const struct nfattr *attr, + struct __nfct_tuple *tuple, + int dir, + u_int32_t *set) { struct nfattr *tb[CTA_TUPLE_MAX]; @@ -148,15 +148,6 @@ static void __parse_tuple(const struct nfattr *attr, __parse_proto(tb[CTA_TUPLE_PROTO-1], tuple, dir, set); } -static void __parse_mask(const struct nfattr *attr, - struct __nfct_tuple *tuple, - const u_int8_t l3protonum, - const u_int16_t protonum, - u_int32_t *set) -{ - __parse_tuple(attr, tuple, __DIR_ORIG, set); -} - static void __parse_protoinfo_tcp(const struct nfattr *attr, struct nf_conntrack *ct) { diff --git a/src/conntrack/snprintf_default.c b/src/conntrack/snprintf_default.c index e826511..7f63d4c 100644 --- a/src/conntrack/snprintf_default.c +++ b/src/conntrack/snprintf_default.c @@ -42,9 +42,9 @@ static int __snprintf_l3protocol(char *buf, ct->tuple[__DIR_ORIG].l3protonum)); } -static int __snprintf_protocol(char *buf, - unsigned int len, - const struct nf_conntrack *ct) +int __snprintf_protocol(char *buf, + unsigned int len, + const struct nf_conntrack *ct) { return (snprintf(buf, len, "%-8s %u ", proto2str[ct->tuple[__DIR_ORIG].protonum] == NULL ? diff --git a/src/expect/Makefile.am b/src/expect/Makefile.am new file mode 100644 index 0000000..80bef3c --- /dev/null +++ b/src/expect/Makefile.am @@ -0,0 +1,16 @@ +include $(top_srcdir)/Make_global.am + +#AUTOMAKE_OPTIONS = no-dependencies foreign + +#EXTRA_DIST = $(man_MANS) acinclude.m4 + +AM_CFLAGS = -fPIC -Wall +LIBS = @LIBNFCONNTRACK_LIBS@ + +noinst_LTLIBRARIES = libnetfilter_conntrack_expect.la + +libnetfilter_conntrack_expect_la_SOURCES = api.c callback.c \ + getter.c setter.c \ + parse.c build.c \ + snprintf.c \ + snprintf_default.c diff --git a/src/expect/api.c b/src/expect/api.c new file mode 100644 index 0000000..127846a --- /dev/null +++ b/src/expect/api.c @@ -0,0 +1,545 @@ +/* + * (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 +#include /* for memset */ +#include +#include + +#include "internal.h" + +/** + * nfexp_new - allocate a new expectation + * + * In case of success, this function returns a valid pointer to a memory blob, + * otherwise NULL is returned and errno is set appropiately. + */ +struct nf_expect *nfexp_new() +{ + struct nf_expect *exp; + + exp = malloc(sizeof(struct nf_expect)); + if (!exp) + return NULL; + + memset(exp, 0, sizeof(struct nf_expect)); + + return exp; +} + +/** + * nfexp_destroy - release an expectation object + * @exp: pointer to the expectation object + */ +void nfexp_destroy(struct nf_expect *exp) +{ + assert(exp != NULL); + free(exp); + exp = NULL; /* bugtrap */ +} + +/** + * nfexp_clone - clone a expectation object + * @exp: pointer to a valid expectation object + * + * On error, NULL is returned and errno is appropiately set. Otherwise, + * a valid pointer to the clone expect is returned. + */ +struct nf_expect *nfexp_clone(const struct nf_expect *exp) +{ + struct nf_expect *clone; + + assert(exp != NULL); + + if ((clone = nfexp_new()) == NULL) + return NULL; + memcpy(clone, exp, sizeof(*exp)); + + return clone; +} + +/** + * nfexp_callback_register - register a callback + * @h: library handler + * @cb: callback used to process expect received + * @data: data used by the callback, if any. + * + * This function register a callback to handle the expect received, + * in case of error -1 is returned and errno is set appropiately, otherwise + * 0 is returned. + * + * Note that the data parameter is optional, if you do not want to pass any + * data to your callback, then use NULL. + */ +int nfexp_callback_register(struct nfct_handle *h, + enum nf_conntrack_msg_type type, + int (*cb)(enum nf_conntrack_msg_type type, + struct nf_expect *exp, + void *data), + void *data) +{ + struct __data_container *container; + + assert(h != NULL); + + container = malloc(sizeof(struct __data_container)); + if (!container) + return -1; + memset(container, 0, sizeof(struct __data_container)); + + h->expect_cb = cb; + container->h = 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; + + nfnl_callback_register(h->nfnlssh_exp, + IPCTNL_MSG_EXP_NEW, + &h->nfnl_cb); + + nfnl_callback_register(h->nfnlssh_exp, + IPCTNL_MSG_EXP_DELETE, + &h->nfnl_cb); + + return 0; +} + +/** + * nfexp_callback_unregister - unregister a callback + * @h: library handler + */ +void nfexp_callback_unregister(struct nfct_handle *h) +{ + assert(h != NULL); + + nfnl_callback_unregister(h->nfnlssh_exp, IPCTNL_MSG_EXP_NEW); + nfnl_callback_unregister(h->nfnlssh_exp, IPCTNL_MSG_EXP_DELETE); + + h->expect_cb = NULL; + free(h->nfnl_cb.data); + + h->nfnl_cb.call = NULL; + h->nfnl_cb.data = NULL; + h->nfnl_cb.attr_count = 0; +} + +/** + * nfexp_set_attr - set the value of a certain expect attribute + * @exp: pointer to a valid expect + * @type: attribute type + * @value: pointer to the attribute value + * + * Note that certain attributes are unsettable: + * - ATTR_EXP_USE + * - ATTR_EXP_ID + * - ATTR_EXP_*_COUNTER_* + * The call of this function for such attributes do nothing. + */ +void nfexp_set_attr(struct nf_expect *exp, + const enum nf_expect_attr type, + const void *value) +{ + assert(exp != NULL); + assert(value != NULL); + + if (type >= ATTR_EXP_MAX) + return; + + if (set_exp_attr_array[type]) { + set_exp_attr_array[type](exp, value); + set_bit(type, exp->set); + } +} + +/** + * nfexp_set_attr_u8 - set the value of a certain expect attribute + * @exp: pointer to a valid expect + * @type: attribute type + * @value: unsigned 8 bits attribute value + */ +void nfexp_set_attr_u8(struct nf_expect *exp, + const enum nf_expect_attr type, + u_int8_t value) +{ + nfexp_set_attr(exp, type, &value); +} + +/** + * nfexp_set_attr_u16 - set the value of a certain expect attribute + * @exp: pointer to a valid expect + * @type: attribute type + * @value: unsigned 16 bits attribute value + */ +void nfexp_set_attr_u16(struct nf_expect *exp, + const enum nf_expect_attr type, + u_int16_t value) +{ + nfexp_set_attr(exp, type, &value); +} + +/** + * nfexp_set_attr_u32 - set the value of a certain expect attribute + * @exp: pointer to a valid expect + * @type: attribute type + * @value: unsigned 32 bits attribute value + */ +void nfexp_set_attr_u32(struct nf_expect *exp, + const enum nf_expect_attr type, + u_int32_t value) +{ + nfexp_set_attr(exp, type, &value); +} + +/** + * nfexp_get_attr - get an expect attribute + * exp: pointer to a valid expect + * @type: attribute type + * + * In case of success a valid pointer to the attribute requested is returned, + * on error NULL is returned and errno is set appropiately. + */ +const void *nfexp_get_attr(const struct nf_expect *exp, + const enum nf_expect_attr type) +{ + assert(exp != NULL); + + if (type >= ATTR_EXP_MAX) { + errno = EINVAL; + return NULL; + } + + if (!test_bit(type, exp->set)) { + errno = ENODATA; + return NULL; + } + + return get_exp_attr_array[type](exp); +} + +/** + * nfexp_get_attr_u8 - get attribute of unsigned 8-bits long + * @exp: pointer to a valid expectation + * @type: attribute type + * + * Returns the value of the requested attribute, if the attribute is not + * set, 0 is returned. In order to check if the attribute is set or not, + * use nfexp_attr_is_set. + */ +u_int8_t nfexp_get_attr_u8(const struct nf_expect *exp, + const enum nf_expect_attr type) +{ + const int *ret = nfexp_get_attr(exp, type); + return ret == NULL ? 0 : *ret; +} + +/** + * nfexp_get_attr_u16 - get attribute of unsigned 16-bits long + * @exp: pointer to a valid expectation + * @type: attribute type + * + * Returns the value of the requested attribute, if the attribute is not + * set, 0 is returned. In order to check if the attribute is set or not, + * use nfexp_attr_is_set. + */ +u_int16_t nfexp_get_attr_u16(const struct nf_expect *exp, + const enum nf_expect_attr type) +{ + const int *ret = nfexp_get_attr(exp, type); + return ret == NULL ? 0 : *ret; +} + +/** + * nfexp_get_attr_u32 - get attribute of unsigned 32-bits long + * @exp: pointer to a valid expectation + * @type: attribute type + * + * Returns the value of the requested attribute, if the attribute is not + * set, 0 is returned. In order to check if the attribute is set or not, + * use nfexp_attr_is_set. + */ +u_int32_t nfexp_get_attr_u32(const struct nf_expect *exp, + const enum nf_expect_attr type) +{ + const int *ret = nfexp_get_attr(exp, type); + return ret == NULL ? 0 : *ret; +} + +/** + * nfexp_attr_is_set - check if a certain attribute is set + * @exp: pointer to a valid expectation object + * @type: attribute type + * + * On error, -1 is returned and errno is set appropiately, otherwise + * the value of the attribute is returned. + */ +int nfexp_attr_is_set(const struct nf_expect *exp, + const enum nf_expect_attr type) +{ + assert(exp != NULL); + + if (type >= ATTR_EXP_MAX) { + errno = EINVAL; + return -1; + } + return test_bit(type, exp->set); +} + +/** + * nfexp_attr_unset - unset a certain attribute + * @type: attribute type + * @exp: pointer to a valid expectation object + * + * On error, -1 is returned and errno is set appropiately, otherwise + * 0 is returned. + */ +int nfexp_attr_unset(struct nf_expect *exp, + const enum nf_expect_attr type) +{ + assert(exp != NULL); + + if (type >= ATTR_EXP_MAX) { + errno = EINVAL; + return -1; + } + unset_bit(type, exp->set); + + return 0; +} + +/** + * nfexp_build_expect - build a netlink message from a conntrack object + * @ssh: nfnetlink subsystem handler + * @req: buffer used to build the netlink message + * @size: size of the buffer passed + * @type: netlink message type + * @flags: netlink flags + * @exp: pointer to a conntrack object + * + * This is a low level function for those that require to be close to + * netlink details via libnfnetlink. If you do want to obviate the netlink + * details then we suggest you to use nfexp_query. + * + * On error, -1 is returned and errno is appropiately set. + * On success, 0 is returned. + */ +int nfexp_build_expect(struct nfnl_subsys_handle *ssh, + void *req, + size_t size, + u_int16_t type, + u_int16_t flags, + const struct nf_expect *exp) +{ + assert(ssh != NULL); + assert(req != NULL); + assert(exp != NULL); + + return __build_expect(ssh, req, size, type, flags, exp); +} + +/** + * nfexp_build_query - build a query in netlink message format for ctnetlink + * @ssh: nfnetlink subsystem handler + * @qt: query type + * @data: data required to build the query + * @req: buffer to build the netlink message + * @size: size of the buffer passed + * + * This is a low level function, use it if you want to require to work + * with netlink details via libnfnetlink, otherwise we suggest you to + * use nfexp_query. + * + * The pointer to data can be a conntrack object or the protocol family + * depending on the request. + * + * For query types: + * NFEXP_Q_CREATE + * NFEXP_Q_DESTROY + * + * Pass a valid pointer to an expectation object. + * + * For query types: + * NFEXP_Q_FLUSH + * NFEXP_Q_DUMP + * + * Pass a valid pointer to the protocol family (u_int8_t) + * + * On success, 0 is returned. On error, -1 is returned and errno is set + * appropiately. + */ +int nfexp_build_query(struct nfnl_subsys_handle *ssh, + const enum nf_conntrack_query qt, + const void *data, + void *buffer, + unsigned int size) +{ + struct nfnlhdr *req = buffer; + const u_int8_t *family = data; + + assert(ssh != NULL); + assert(data != NULL); + assert(req != NULL); + + memset(req, 0, size); + + switch(qt) { + case NFCT_Q_CREATE: + nfexp_build_expect(ssh, req, size, IPCTNL_MSG_EXP_NEW, NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK|NLM_F_EXCL, data); + break; + case NFCT_Q_GET: + nfexp_build_expect(ssh, req, size, IPCTNL_MSG_EXP_GET, NLM_F_REQUEST|NLM_F_ACK, data); + break; + case NFCT_Q_DESTROY: + nfexp_build_expect(ssh, req, size, IPCTNL_MSG_EXP_DELETE, NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST|NLM_F_ACK, data); + break; + case NFCT_Q_FLUSH: + nfnl_fill_hdr(ssh, &req->nlh, 0, *family, 0, IPCTNL_MSG_EXP_DELETE, NLM_F_REQUEST|NLM_F_ACK); + break; + case NFCT_Q_DUMP: + nfnl_fill_hdr(ssh, &req->nlh, 0, *family, 0, IPCTNL_MSG_EXP_GET, NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST|NLM_F_DUMP); + break; + default: + errno = ENOTSUP; + return -1; + } + return 1; +} + +/** + * nfexp_parse_expect - translate a netlink message to a conntrack object + * @type: do the translation iif the message type is of a certain type + * @nlh: pointer to the netlink message + * @exp: pointer to the conntrack object + * + * This is a low level function, use it in case that you require to work + * with netlink details via libnfnetlink. Otherwise, we suggest you to + * use the high level API. + * + * The message types are: + * + * NFEXP_T_NEW: parse messages with new conntracks + * NFEXP_T_UPDATE: parse messages with conntrack updates + * NFEXP_T_DESTROY: parse messages with conntrack destroy + * NFEXP_T_ALL: all message types + * + * The message type is a flag, therefore the can be combined, ie. + * NFEXP_T_NEW | NFEXP_T_DESTROY to parse only new and destroy messages + * + * On error, NFEXP_T_ERROR is returned and errno is set appropiately. If + * the message received is not of the requested type then 0 is returned, + * otherwise this function returns the message type parsed. + */ +int nfexp_parse_expect(enum nf_conntrack_msg_type type, + const struct nlmsghdr *nlh, + struct nf_expect *exp) +{ + unsigned int flags; + int len = nlh->nlmsg_len; + struct nfgenmsg *nfhdr = NLMSG_DATA(nlh); + struct nfattr *cda[CTA_EXPECT_MAX]; + + assert(nlh != NULL); + assert(exp != NULL); + + len -= NLMSG_LENGTH(sizeof(struct nfgenmsg)); + if (len < 0) { + errno = EINVAL; + return NFCT_T_ERROR; + } + + flags = __parse_expect_message_type(nlh); + if (!(flags & type)) + return 0; + + nfnl_parse_attr(cda, CTA_EXPECT_MAX, NFA_DATA(nfhdr), len); + + __parse_expect(nlh, cda, exp); + + return flags; +} + +/** + * nfexp_query - send a query to ctnetlink + * @h: library handler + * @qt: query type + * @data: data required to send the query + * + * On error, -1 is returned and errno is explicitely set. On success, 0 + * is returned. + */ +int nfexp_query(struct nfct_handle *h, + const enum nf_conntrack_query qt, + const void *data) +{ + size_t size = 4096; /* enough for now */ + char buffer[4096]; + struct nfnlhdr *req = (struct nfnlhdr *) buffer; + + assert(h != NULL); + assert(data != NULL); + + if (nfexp_build_query(h->nfnlssh_exp, qt, data, req, size) == -1) + return -1; + + return nfnl_query(h->nfnlh, &req->nlh); +} + +/** + * nfexp_catch - catch events + * @h: library handler + * + * On error, -1 is returned and errno is set appropiately. On success, + * a value greater or equal to 0 is returned indicating the callback + * verdiexp: NFEXP_CB_STOP, NFEXP_CB_CONTINUE or NFEXP_CB_STOLEN + */ +int nfexp_catch(struct nfct_handle *h) +{ + assert(h != NULL); + + return nfnl_catch(h->nfnlh); +} + +/** + * nfexp_snprintf - print a conntrack object to a buffer + * @buf: buffer used to build the printable conntrack + * @size: size of the buffer + * @exp: pointer to a valid expectation object + * @message_type: print message type (NFEXP_T_UNKNOWN, NFEXP_T_NEW,...) + * @output_type: print type (NFEXP_O_DEFAULT, NFEXP_O_XML, ...) + * @flags: extra flags for the output type (NFEXP_OF_LAYER3) + * + * If you are listening to events, probably you want to display the message + * type as well. In that case, set the message type parameter to any of the + * known existing types, ie. NFEXP_T_NEW, NFEXP_T_UPDATE, NFEXP_T_DESTROY. + * If you pass NFEXP_T_UNKNOWN, the message type will not be output. + * + * Currently, the output available are: + * - NFEXP_O_DEFAULT: default /proc-like output + * - NFEXP_O_XML: XML output + * + * The output flags are: + * - NFEXP_O_LAYER: include layer 3 information in the output, this is + * *only* required by NFEXP_O_DEFAULT. + * + * On error, -1 is returned and errno is set appropiately. Otherwise, + * 0 is returned. + */ +int nfexp_snprintf(char *buf, + unsigned int size, + const struct nf_expect *exp, + unsigned int msg_type, + unsigned int out_type, + unsigned int flags) +{ + assert(buf != NULL); + assert(size > 0); + assert(exp != NULL); + + return __snprintf_expect(buf, size, exp, msg_type, out_type, flags); +} diff --git a/src/expect/build.c b/src/expect/build.c new file mode 100644 index 0000000..501263a --- /dev/null +++ b/src/expect/build.c @@ -0,0 +1,58 @@ +/* + * (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.h" + +static void __build_timeout(struct nfnlhdr *req, + size_t size, + const struct nf_expect *exp) +{ + nfnl_addattr32(&req->nlh, size, CTA_EXPECT_TIMEOUT,htonl(exp->timeout)); +} + +int __build_expect(struct nfnl_subsys_handle *ssh, + struct nfnlhdr *req, + size_t size, + u_int16_t type, + u_int16_t flags, + const struct nf_expect *exp) +{ + u_int8_t l3num = exp->master.tuple[NFCT_DIR_ORIGINAL].l3protonum; + + if (!test_bit(ATTR_ORIG_L3PROTO, exp->master.set)) { + errno = EINVAL; + return -1; + } + + memset(req, 0, size); + + nfnl_fill_hdr(ssh, &req->nlh, 0, l3num, 0, type, flags); + + __build_tuple(req, + size, + &exp->expected.tuple[__DIR_ORIG], + CTA_EXPECT_TUPLE); + + /* get and delete only require the expectation tuple */ + if (type == IPCTNL_MSG_EXP_GET || type == IPCTNL_MSG_EXP_DELETE) + return 0; + + __build_tuple(req, + size, + &exp->master.tuple[__DIR_ORIG], + CTA_EXPECT_MASTER); + + __build_tuple(req, + size, + &exp->mask.tuple[__DIR_ORIG], + CTA_EXPECT_MASK); + + if (test_bit(ATTR_EXP_TIMEOUT, exp->set)) + __build_timeout(req, size, exp); + + return 0; +} diff --git a/src/expect/callback.c b/src/expect/callback.c new file mode 100644 index 0000000..df4ffe7 --- /dev/null +++ b/src/expect/callback.c @@ -0,0 +1,53 @@ +/* + * (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.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); + + 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/expect/getter.c b/src/expect/getter.c new file mode 100644 index 0000000..2cbebe6 --- /dev/null +++ b/src/expect/getter.c @@ -0,0 +1,35 @@ +/* + * (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.h" + +static const void *get_exp_attr_master(const struct nf_expect *exp) +{ + return &exp->master; +} + +static const void *get_exp_attr_expected(const struct nf_expect *exp) +{ + return &exp->expected; +} + +static const void *get_exp_attr_mask(const struct nf_expect *exp) +{ + return &exp->mask; +} + +static const void *get_exp_attr_timeout(const struct nf_expect *exp) +{ + return &exp->timeout; +} + +get_exp_attr get_exp_attr_array[] = { + [ATTR_EXP_MASTER] = get_exp_attr_master, + [ATTR_EXP_EXPECTED] = get_exp_attr_expected, + [ATTR_EXP_MASK] = get_exp_attr_mask, + [ATTR_EXP_TIMEOUT] = get_exp_attr_timeout, +}; diff --git a/src/expect/parse.c b/src/expect/parse.c new file mode 100644 index 0000000..5fe0bce --- /dev/null +++ b/src/expect/parse.c @@ -0,0 +1,57 @@ +/* + * (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.h" + +int __parse_expect_message_type(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; + + if (type == IPCTNL_MSG_EXP_NEW) { + if (flags & (NLM_F_CREATE|NLM_F_EXCL)) + ret = NFCT_T_NEW; + else + ret = NFCT_T_UPDATE; + } else if (type == IPCTNL_MSG_EXP_DELETE) + ret = NFCT_T_DESTROY; + + return ret; +} + +void __parse_expect(const struct nlmsghdr *nlh, + const struct nfattr *cda[], + struct nf_expect *exp) +{ + struct nfgenmsg *nfhdr = NLMSG_DATA(nlh); + + /* XXX: this is ugly, clean it up, please */ + exp->expected.tuple[__DIR_ORIG].l3protonum = nfhdr->nfgen_family; + set_bit(ATTR_ORIG_L3PROTO, exp->expected.set); + + exp->mask.tuple[__DIR_REPL].l3protonum = nfhdr->nfgen_family; + set_bit(ATTR_ORIG_L3PROTO, exp->mask.set); + + if (cda[CTA_EXPECT_TUPLE-1]) + __parse_tuple(cda[CTA_EXPECT_TUPLE-1], + &exp->expected.tuple[__DIR_ORIG], + __DIR_ORIG, + exp->set); + + if (cda[CTA_EXPECT_MASK-1]) + __parse_tuple(cda[CTA_EXPECT_MASK-1], + &exp->mask.tuple[__DIR_ORIG], + __DIR_ORIG, + exp->set); + + if (cda[CTA_EXPECT_TIMEOUT-1]) { + exp->timeout = + ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_EXPECT_TIMEOUT-1])); + set_bit(ATTR_EXP_TIMEOUT, exp->set); + } +} diff --git a/src/expect/setter.c b/src/expect/setter.c new file mode 100644 index 0000000..ae80ca8 --- /dev/null +++ b/src/expect/setter.c @@ -0,0 +1,35 @@ +/* + * (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.h" + +static void set_exp_attr_master(struct nf_expect *exp, const void *value) +{ + exp->master = *((struct nf_conntrack *) value); +} + +static void set_exp_attr_expected(struct nf_expect *exp, const void *value) +{ + exp->expected = *((struct nf_conntrack *) value); +} + +static void set_exp_attr_mask(struct nf_expect *exp, const void *value) +{ + exp->mask = *((struct nf_conntrack *) value); +} + +static void set_exp_attr_timeout(struct nf_expect *exp, const void *value) +{ + exp->timeout = *((u_int32_t *) value); +} + +set_exp_attr set_exp_attr_array[] = { + [ATTR_EXP_MASTER] = set_exp_attr_master, + [ATTR_EXP_EXPECTED] = set_exp_attr_expected, + [ATTR_EXP_MASK] = set_exp_attr_mask, + [ATTR_EXP_TIMEOUT] = set_exp_attr_timeout, +}; diff --git a/src/expect/snprintf.c b/src/expect/snprintf.c new file mode 100644 index 0000000..b981625 --- /dev/null +++ b/src/expect/snprintf.c @@ -0,0 +1,33 @@ +/* + * (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.h" + +int __snprintf_expect(char *buf, + unsigned int len, + const struct nf_expect *exp, + unsigned int type, + unsigned int msg_output, + unsigned int flags) +{ + int size; + + switch(msg_output) { + case NFCT_O_DEFAULT: + size = __snprintf_expect_default(buf, len, exp, type, flags); + break; + default: + errno = ENOENT; + return -1; + } + + /* NULL terminated string */ + if (snprintf(buf+size, len-size, "\0") == -1) + return -1; + + return size; +} diff --git a/src/expect/snprintf_default.c b/src/expect/snprintf_default.c new file mode 100644 index 0000000..1b64eb2 --- /dev/null +++ b/src/expect/snprintf_default.c @@ -0,0 +1,62 @@ +/* + * (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.h" + +static int __snprintf_expect_proto(char *buf, + unsigned int len, + const struct nf_expect *exp) +{ + return(snprintf(buf, len, "%u proto=%d ", + exp->timeout, + exp->expected.tuple[__DIR_ORIG].protonum)); +} + +int __snprintf_expect_default(char *buf, + unsigned int remain, + const struct nf_expect *exp, + unsigned int msg_type, + unsigned int flags) +{ + int ret = 0, size = 0; + + switch(msg_type) { + case NFCT_T_NEW: + ret = snprintf(buf, remain, "%9s ", "[NEW]"); + break; + default: + break; + } + + if (ret == -1) + return -1; + size += ret; + remain -= ret; + + ret = __snprintf_expect_proto(buf+size, remain, exp); + if (ret == -1) + return -1; + size += ret; + remain -= ret; + + ret = __snprintf_address(buf+size, remain, &exp->expected.tuple[__DIR_ORIG]); + if (ret == -1) + return -1; + size += ret; + remain -= ret; + + ret = __snprintf_proto(buf+size, remain, &exp->expected.tuple[__DIR_ORIG]); + if (ret == -1) + return -1; + size += ret; + remain -= ret; + + /* Delete the last blank space */ + size--; + + return size; +} diff --git a/utils/Makefile.am b/utils/Makefile.am index ea32da0..e2f387d 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -1,11 +1,15 @@ include $(top_srcdir)/Make_global.am -bin_PROGRAMS = ctnl_test new_api_test +bin_PROGRAMS = ctnl_test new_api_test expect_api_test new_api_test_SOURCES = new_api_test.c new_api_test_LDADD = ../src/libnetfilter_conntrack.la new_api_test_LDFLAGS = -dynamic -ldl +expect_api_test_SOURCES = expect_api_test.c +expect_api_test_LDADD = ../src/libnetfilter_conntrack.la +expect_api_test_LDFLAGS = -dynamic -ldl + ctnl_test_SOURCES = ctnl_test.c ctnl_test_LDADD = ../src/libnetfilter_conntrack.la ctnl_test_LDFLAGS = -dynamic -ldl diff --git a/utils/expect_api_test.c b/utils/expect_api_test.c new file mode 100644 index 0000000..e075b10 --- /dev/null +++ b/utils/expect_api_test.c @@ -0,0 +1,160 @@ +#include +#include +#include + +#include +#include + +static int cb(enum nf_conntrack_msg_type type, + struct nf_expect *exp, + void *data) +{ + char buf[1024]; + + nfexp_snprintf(buf, 1024, exp, NFCT_T_UNKNOWN, NFCT_O_DEFAULT, 0); + printf("%s\n", buf); + + return NFCT_CB_CONTINUE; +} + +static int event_cb(enum nf_conntrack_msg_type type, + struct nf_expect *exp, + void *data) +{ + static int n = 0; + char buf[1024]; + + nfexp_snprintf(buf, 1024, exp, type, NFCT_O_DEFAULT, 0); + printf("%s\n", buf); + + if (++n == 10) + return NFCT_CB_STOP; + + return NFCT_CB_CONTINUE; +} + +int main() +{ + int ret; + u_int8_t family = AF_INET; + struct nfct_handle *h; + struct nf_conntrack *master, *expected, *mask; + struct nf_expect *exp; + char buf[1024]; + + printf("Test for NEW expectation libnetfilter_conntrack API\n"); + printf("===================================================\n"); + + master = nfct_new(); + if (!master) { + perror("nfct_new"); + exit(EXIT_FAILURE); + } + + nfct_set_attr_u8(master, ATTR_ORIG_L3PROTO, AF_INET); + nfct_set_attr_u32(master, ATTR_ORIG_IPV4_SRC, inet_addr("1.1.1.1")); + nfct_set_attr_u32(master, ATTR_ORIG_IPV4_DST, inet_addr("2.2.2.2")); + + nfct_set_attr_u8(master, ATTR_ORIG_L4PROTO, IPPROTO_TCP); + nfct_set_attr_u16(master, ATTR_ORIG_PORT_SRC, htons(1025)); + nfct_set_attr_u16(master, ATTR_ORIG_PORT_DST, htons(21)); + + nfct_set_attr_u8(master, ATTR_REPL_L3PROTO, AF_INET); + nfct_set_attr_u32(master, ATTR_REPL_IPV4_SRC, inet_addr("2.2.2.2")); + nfct_set_attr_u32(master, ATTR_REPL_IPV4_DST, inet_addr("1.1.1.1")); + + nfct_set_attr_u8(master, ATTR_REPL_L4PROTO, IPPROTO_TCP); + nfct_set_attr_u16(master, ATTR_REPL_PORT_SRC, htons(21)); + nfct_set_attr_u16(master, ATTR_REPL_PORT_DST, htons(1025)); + + nfct_set_attr_u8(master, ATTR_TCP_STATE, TCP_CONNTRACK_LISTEN); + nfct_set_attr_u32(master, ATTR_TIMEOUT, 200); + + h = nfct_open(CONNTRACK, 0); + if (!h) { + perror("nfct_open"); + return -1; + } + + ret = nfct_query(h, NFCT_Q_CREATE, master); + + printf("TEST 1: create conntrack (%d)(%s)\n", ret, strerror(errno)); + + nfct_close(h); + + expected = nfct_new(); + if (!expected) { + perror("nfct_new"); + exit(EXIT_FAILURE); + } + + nfct_set_attr_u8(expected, ATTR_ORIG_L3PROTO, AF_INET); + nfct_set_attr_u32(expected, ATTR_ORIG_IPV4_SRC, inet_addr("4.4.4.4")); + nfct_set_attr_u32(expected, ATTR_ORIG_IPV4_DST, inet_addr("5.5.5.5")); + + nfct_set_attr_u8(expected, ATTR_ORIG_L4PROTO, IPPROTO_TCP); + nfct_set_attr_u16(expected, ATTR_ORIG_PORT_SRC, htons(10240)); + nfct_set_attr_u16(expected, ATTR_ORIG_PORT_DST, htons(10241)); + + mask = nfct_new(); + if (!mask) { + perror("nfct_new"); + exit(EXIT_FAILURE); + } + + nfct_set_attr_u8(mask, ATTR_ORIG_L3PROTO, AF_INET); + nfct_set_attr_u32(mask, ATTR_ORIG_IPV4_SRC, 0xffffffff); + nfct_set_attr_u32(mask, ATTR_ORIG_IPV4_DST, 0xffffffff); + + nfct_set_attr_u8(mask, ATTR_ORIG_L4PROTO, IPPROTO_TCP); + nfct_set_attr_u16(mask, ATTR_ORIG_PORT_SRC, 0xffff); + nfct_set_attr_u16(mask, ATTR_ORIG_PORT_DST, 0xffff); + + exp = nfexp_new(); + if (!exp) { + perror("nfexp_new"); + exit(EXIT_FAILURE); + } + + nfexp_set_attr(exp, ATTR_EXP_MASTER, master); + nfexp_set_attr(exp, ATTR_EXP_EXPECTED, expected); + nfexp_set_attr(exp, ATTR_EXP_MASK, mask); + nfexp_set_attr_u32(exp, ATTR_EXP_TIMEOUT, 200); + + h = nfct_open(EXPECT, 0); + if (!h) { + perror("nfct_open"); + return -1; + } + + ret = nfexp_query(h, NFCT_Q_CREATE, exp); + + printf("TEST 2: create expectation (%d)(%s)\n", ret, strerror(errno)); + + nfexp_callback_register(h, NFCT_T_ALL, cb, NULL); + ret = nfexp_query(h, NFCT_Q_GET, exp); + + printf("TEST 3: get expectation (%d)(%s)\n", ret, strerror(errno)); + + ret = nfexp_query(h, NFCT_Q_DESTROY, exp); + + printf("TEST 4: destroy expectation (%d)(%s)\n", ret, strerror(errno)); + + nfct_close(h); + + h = nfct_open(EXPECT, NF_NETLINK_CONNTRACK_EXP_NEW); + if (!h) { + perror("nfct_open"); + return -1; + } + + nfexp_callback_register(h, NFCT_T_ALL, event_cb, NULL); + + printf("TEST 5: waiting for 10 events...\n"); + + ret = nfexp_catch(h); + + printf("TEST 5: OK (%d)(%s)\n", ret, strerror(errno)); + + nfct_close(h); +} -- cgit v1.2.3