/* * (C) 2005-2011 by Pablo Neira Ayuso * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include /* for memset */ #include #include #include "internal/internal.h" /** * \mainpage * * libnetfilter_conntrack is a userspace library providing a programming * interface (API) to the in-kernel connection tracking state table. The * library libnetfilter_conntrack has been previously known as * libnfnetlink_conntrack and libctnetlink. This library is currently used by * conntrack-tools among many other applications. * * libnetfilter_conntrack homepage is: * http://netfilter.org/projects/libnetfilter_conntrack/ * * \section Dependencies * libnetfilter_conntrack requires libnfnetlink and a kernel that includes the * nf_conntrack_netlink subsystem (i.e. 2.6.14 or later, >= 2.6.18 recommended). * * \section Main Features * - listing/retrieving entries from the kernel connection tracking table. * - inserting/modifying/deleting entries from the kernel connection tracking * table. * - listing/retrieving entries from the kernel expect table. * - inserting/modifying/deleting entries from the kernel expect table. * \section Git Tree * The current development version of libnetfilter_conntrack can be accessed at * https://git.netfilter.org/cgi-bin/gitweb.cgi?p=libnetfilter_conntrack.git * * \section Privileges * You need the CAP_NET_ADMIN capability in order to allow your application * to receive events from and to send commands to kernel-space, excepting * the conntrack table dumping operation. * * \section using Using libnetfilter_conntrack * To write your own program using libnetfilter_conntrack, you should start by * reading the doxygen documentation (start by \link LibrarySetup \endlink page) * and check examples available under utils/ in the libnetfilter_conntrack * source code tree. You can compile these examples by invoking `make check'. * * \section Authors * libnetfilter_conntrack has been almost entirely written by Pablo Neira Ayuso. * * \section python Python Binding * pynetfilter_conntrack is a Python binding of libnetfilter_conntrack written * by Victor Stinner. You can visit his official web site at * http://software.inl.fr/trac/trac.cgi/wiki/pynetfilter_conntrack. */ /** * \defgroup ct Conntrack object handling * @{ */ /** * nfct_conntrack_new - allocate a new conntrack * * 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_conntrack *nfct_new(void) { struct nf_conntrack *ct; ct = malloc(sizeof(struct nf_conntrack)); if (!ct) return NULL; memset(ct, 0, sizeof(struct nf_conntrack)); return ct; } /** * nf_conntrack_destroy - release a conntrack object * \param ct pointer to the conntrack object */ void nfct_destroy(struct nf_conntrack *ct) { assert(ct != NULL); if (ct->secctx) free(ct->secctx); if (ct->helper_info) free(ct->helper_info); if (ct->connlabels) nfct_bitmask_destroy(ct->connlabels); if (ct->connlabels_mask) nfct_bitmask_destroy(ct->connlabels_mask); free(ct); ct = NULL; /* bugtrap */ } /** * nf_sizeof - return the size in bytes of a certain conntrack object * \param ct pointer to the conntrack object * * This function is DEPRECATED, don't use it in your code. */ size_t nfct_sizeof(const struct nf_conntrack *ct) { assert(ct != NULL); return sizeof(*ct); } /** * nfct_maxsize - return the maximum size in bytes of a conntrack object * * Use this function if you want to allocate a conntrack object in the stack * instead of the heap. For example: * \verbatim char buf[nfct_maxsize()]; struct nf_conntrack *ct = (struct nf_conntrack *) buf; memset(ct, 0, nfct_maxsize()); \endverbatim * Note: As for now this function returns the same size that nfct_sizeof(ct) * does although _this could change in the future_. Therefore, do not assume * that nfct_sizeof(ct) == nfct_maxsize(). * * This function is DEPRECATED, don't use it in your code. */ size_t nfct_maxsize(void) { return sizeof(struct nf_conntrack); } /** * nfct_clone - clone a conntrack object * \param ct pointer to a valid conntrack object * * On error, NULL is returned and errno is appropiately set. Otherwise, * a valid pointer to the clone conntrack is returned. */ struct nf_conntrack *nfct_clone(const struct nf_conntrack *ct) { struct nf_conntrack *clone; assert(ct != NULL); if ((clone = nfct_new()) == NULL) return NULL; nfct_copy(clone, ct, NFCT_CP_OVERRIDE); return clone; } /** * nfct_setobjopt - set a certain option for a conntrack object * \param ct conntrack object * \param option option parameter * * In case of error, -1 is returned and errno is appropiately set. On success, * 0 is returned. */ int nfct_setobjopt(struct nf_conntrack *ct, unsigned int option) { assert(ct != NULL); if (unlikely(option > NFCT_SOPT_MAX)) { errno = EOPNOTSUPP; return -1; } return __setobjopt(ct, option); } /** * nfct_getobjopt - get a certain option for a conntrack object * \param ct conntrack object * \param option option parameter * * In case of error, -1 is returned and errno is appropiately set. On success, * 0 is returned. */ int nfct_getobjopt(const struct nf_conntrack *ct, unsigned int option) { assert(ct != NULL); if (unlikely(option > NFCT_GOPT_MAX)) { errno = EOPNOTSUPP; return -1; } return __getobjopt(ct, option); } /** * @} */ /** * \defgroup LibrarySetup Library setup * @{ */ /** * nf_callback_register - register a callback * \param h library handler * \param type message type (see enum nf_conntrack_msg_type definition) * \param cb callback used to process conntrack received * \param data data used by the callback, if any. * * This function register a callback to handle the conntrack 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 nfct_callback_register(struct nfct_handle *h, enum nf_conntrack_msg_type type, int (*cb)(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, 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->cb = cb; container->h = h; container->type = type; container->data = data; 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_ct); nfnl_callback_register(h->nfnlssh_ct, IPCTNL_MSG_CT_DELETE, &h->nfnl_cb_ct); return 0; } /** * nfct_callback_unregister - unregister a callback * \param h library handler */ void nfct_callback_unregister(struct nfct_handle *h) { assert(h != NULL); nfnl_callback_unregister(h->nfnlssh_ct, IPCTNL_MSG_CT_NEW); nfnl_callback_unregister(h->nfnlssh_ct, IPCTNL_MSG_CT_DELETE); h->cb = NULL; free(h->nfnl_cb_ct.data); h->nfnl_cb_ct.call = NULL; h->nfnl_cb_ct.data = NULL; h->nfnl_cb_ct.attr_count = 0; } /** * nf_callback_register2 - register a callback * \param h library handler * \param cb callback used to process conntrack received * \param data data used by the callback, if any. * * This function register a callback to handle the conntrack 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. * * NOTICE: The difference with nf_callback_register() is that this function * uses the new callback interface that includes the Netlink header. * * WARNING: Don't mix nf_callback_register() and nf_callback_register2() * calls, use only once at a time. */ int nfct_callback_register2(struct nfct_handle *h, enum nf_conntrack_msg_type type, int (*cb)(const struct nlmsghdr *nlh, enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data), void *data) { struct __data_container *container; assert(h != NULL); container = calloc(sizeof(struct __data_container), 1); if (container == NULL) return -1; h->cb2 = cb; container->h = h; container->type = type; container->data = data; 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_ct); nfnl_callback_register(h->nfnlssh_ct, IPCTNL_MSG_CT_DELETE, &h->nfnl_cb_ct); return 0; } /** * nfct_callback_unregister2 - unregister a callback * \param h library handler */ void nfct_callback_unregister2(struct nfct_handle *h) { assert(h != NULL); nfnl_callback_unregister(h->nfnlssh_ct, IPCTNL_MSG_CT_NEW); nfnl_callback_unregister(h->nfnlssh_ct, IPCTNL_MSG_CT_DELETE); h->cb2 = NULL; free(h->nfnl_cb_ct.data); h->nfnl_cb_ct.call = NULL; h->nfnl_cb_ct.data = NULL; h->nfnl_cb_ct.attr_count = 0; } /** * @} */ /** * \defgroup ct Conntrack object handling * @{ */ /** * nfct_set_attr_l - set the value of a certain conntrack attribute * \param ct pointer to a valid conntrack * \param type attribute type * \param pointer to attribute value * \param length of attribute value (in bytes) */ void nfct_set_attr_l(struct nf_conntrack *ct, const enum nf_conntrack_attr type, const void *value, size_t len) { assert(ct != NULL); assert(value != NULL); if (unlikely(type >= ATTR_MAX)) return; if (set_attr_array[type]) { set_attr_array[type](ct, value, len); set_bit(type, ct->head.set); } } /** * nfct_set_attr - set the value of a certain conntrack attribute * \param ct pointer to a valid conntrack * \param type attribute type * \param value pointer to the attribute value * * Note that certain attributes are unsettable: * - ATTR_USE * - ATTR_ID * - ATTR_*_COUNTER_* * - ATTR_SECCTX * - ATTR_TIMESTAMP_* * The call of this function for such attributes do nothing. */ void nfct_set_attr(struct nf_conntrack *ct, const enum nf_conntrack_attr type, const void *value) { /* We assume the setter knows the size of the passed pointer. */ nfct_set_attr_l(ct, type, value, 0); } /** * nfct_set_attr_u8 - set the value of a certain conntrack attribute * \param ct pointer to a valid conntrack * \param type attribute type * \param value unsigned 8 bits attribute value */ void nfct_set_attr_u8(struct nf_conntrack *ct, const enum nf_conntrack_attr type, u_int8_t value) { nfct_set_attr_l(ct, type, &value, sizeof(u_int8_t)); } /** * nfct_set_attr_u16 - set the value of a certain conntrack attribute * \param ct pointer to a valid conntrack * \param type attribute type * \param value unsigned 16 bits attribute value */ void nfct_set_attr_u16(struct nf_conntrack *ct, const enum nf_conntrack_attr type, u_int16_t value) { nfct_set_attr_l(ct, type, &value, sizeof(u_int16_t)); } /** * nfct_set_attr_u32 - set the value of a certain conntrack attribute * \param ct pointer to a valid conntrack * \param type attribute type * \param value unsigned 32 bits attribute value */ void nfct_set_attr_u32(struct nf_conntrack *ct, const enum nf_conntrack_attr type, u_int32_t value) { nfct_set_attr_l(ct, type, &value, sizeof(u_int32_t)); } /** * nfct_set_attr_u64 - set the value of a certain conntrack attribute * \param ct pointer to a valid conntrack * \param type attribute type * \param value unsigned 64 bits attribute value */ void nfct_set_attr_u64(struct nf_conntrack *ct, const enum nf_conntrack_attr type, u_int64_t value) { nfct_set_attr_l(ct, type, &value, sizeof(u_int64_t)); } /** * nfct_get_attr - get a conntrack attribute * \param ct pointer to a valid conntrack * \param 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 *nfct_get_attr(const struct nf_conntrack *ct, const enum nf_conntrack_attr type) { assert(ct != NULL); if (unlikely(type >= ATTR_MAX)) { errno = EINVAL; return NULL; } if (!test_bit(type, ct->head.set)) { errno = ENODATA; return NULL; } assert(get_attr_array[type]); return get_attr_array[type](ct); } /** * nfct_get_attr_u8 - get attribute of unsigned 8-bits long * \param ct pointer to a valid conntrack * \param 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 nfct_attr_is_set. */ u_int8_t nfct_get_attr_u8(const struct nf_conntrack *ct, const enum nf_conntrack_attr type) { const u_int8_t *ret = nfct_get_attr(ct, type); return ret == NULL ? 0 : *ret; } /** * nfct_get_attr_u16 - get attribute of unsigned 16-bits long * \param ct pointer to a valid conntrack * \param 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 nfct_attr_is_set. */ u_int16_t nfct_get_attr_u16(const struct nf_conntrack *ct, const enum nf_conntrack_attr type) { const u_int16_t *ret = nfct_get_attr(ct, type); return ret == NULL ? 0 : *ret; } /** * nfct_get_attr_u32 - get attribute of unsigned 32-bits long * \param ct pointer to a valid conntrack * \param 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 nfct_attr_is_set. */ u_int32_t nfct_get_attr_u32(const struct nf_conntrack *ct, const enum nf_conntrack_attr type) { const u_int32_t *ret = nfct_get_attr(ct, type); return ret == NULL ? 0 : *ret; } /** * nfct_get_attr_u64 - get attribute of unsigned 32-bits long * \param ct pointer to a valid conntrack * \param 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 nfct_attr_is_set. */ u_int64_t nfct_get_attr_u64(const struct nf_conntrack *ct, const enum nf_conntrack_attr type) { const u_int64_t *ret = nfct_get_attr(ct, type); return ret == NULL ? 0 : *ret; } /** * nfct_attr_is_set - check if a certain attribute is set * \param ct pointer to a valid conntrack object * \param type attribute type * * On error, -1 is returned and errno is set appropiately, otherwise * the value of the attribute is returned. */ int nfct_attr_is_set(const struct nf_conntrack *ct, const enum nf_conntrack_attr type) { assert(ct != NULL); if (unlikely(type >= ATTR_MAX)) { errno = EINVAL; return -1; } return test_bit(type, ct->head.set); } /** * nfct_attr_is_set_array - check if an array of attribute types is set * \param ct pointer to a valid conntrack object * \param array attribute type array * \param size size of the array * * On error, -1 is returned and errno is set appropiately, otherwise * the value of the attribute is returned. */ int nfct_attr_is_set_array(const struct nf_conntrack *ct, const enum nf_conntrack_attr *type_array, int size) { int i; assert(ct != NULL); for (i=0; i= ATTR_MAX)) { errno = EINVAL; return -1; } if (!test_bit(type_array[i], ct->head.set)) return 0; } return 1; } /** * nfct_attr_unset - unset a certain attribute * \param type attribute type * \param ct pointer to a valid conntrack object * * On error, -1 is returned and errno is set appropiately, otherwise * 0 is returned. */ int nfct_attr_unset(struct nf_conntrack *ct, const enum nf_conntrack_attr type) { assert(ct != NULL); if (unlikely(type >= ATTR_MAX)) { errno = EINVAL; return -1; } unset_bit(type, ct->head.set); return 0; } /** * nfct_set_attr_grp - set a group of attributes * \param ct pointer to a valid conntrack object * \param type attribute group (see ATTR_GRP_*) * \param data pointer to struct (see struct nfct_attr_grp_*) * * Note that calling this function for ATTR_GRP_COUNTER_* and ATTR_GRP_ADDR_* * have no effect. */ void nfct_set_attr_grp(struct nf_conntrack *ct, const enum nf_conntrack_attr_grp type, const void *data) { assert(ct != NULL); if (unlikely(type >= ATTR_GRP_MAX)) return; if (set_attr_grp_array[type]) { set_attr_grp_array[type](ct, data); set_bitmask_u32(ct->head.set, attr_grp_bitmask[type].bitmask, __NFCT_BITSET); } } /** * nfct_get_attr_grp - get an attribute group * \param ct pointer to a valid conntrack object * \param type attribute group (see ATTR_GRP_*) * \param data pointer to struct (see struct nfct_attr_grp_*) * * On error, it returns -1 and errno is appropriately set. On success, the * data pointer contains the attribute group. */ int nfct_get_attr_grp(const struct nf_conntrack *ct, const enum nf_conntrack_attr_grp type, void *data) { assert(ct != NULL); if (unlikely(type >= ATTR_GRP_MAX)) { errno = EINVAL; return -1; } switch(attr_grp_bitmask[type].type) { case NFCT_BITMASK_AND: if (!test_bitmask_u32(ct->head.set, attr_grp_bitmask[type].bitmask, __NFCT_BITSET)) { errno = ENODATA; return -1; } break; case NFCT_BITMASK_OR: if (!test_bitmask_u32_or(ct->head.set, attr_grp_bitmask[type].bitmask, __NFCT_BITSET)) { errno = ENODATA; return -1; } break; } assert(get_attr_grp_array[type]); get_attr_grp_array[type](ct, data); return 0; } /** * nfct_attr_grp_is_set - check if an attribute group is set * \param ct pointer to a valid conntrack object * \param type attribute group (see ATTR_GRP_*) * * If the attribute group is set, this function returns 1, otherwise 0. */ int nfct_attr_grp_is_set(const struct nf_conntrack *ct, const enum nf_conntrack_attr_grp type) { assert(ct != NULL); if (unlikely(type >= ATTR_GRP_MAX)) { errno = EINVAL; return -1; } switch(attr_grp_bitmask[type].type) { case NFCT_BITMASK_AND: if (test_bitmask_u32(ct->head.set, attr_grp_bitmask[type].bitmask, __NFCT_BITSET)) { return 1; } break; case NFCT_BITMASK_OR: if (test_bitmask_u32_or(ct->head.set, attr_grp_bitmask[type].bitmask, __NFCT_BITSET)) { return 1; } break; } return 0; } /** * nfct_attr_grp_unset - unset an attribute group * \param ct pointer to a valid conntrack object * \param type attribute group (see ATTR_GRP_*) * * On error, it returns -1 and errno is appropriately set. On success, * this function returns 0. */ int nfct_attr_grp_unset(struct nf_conntrack *ct, const enum nf_conntrack_attr_grp type) { assert(ct != NULL); if (unlikely(type >= ATTR_GRP_MAX)) { errno = EINVAL; return -1; } unset_bitmask_u32(ct->head.set, attr_grp_bitmask[type].bitmask, __NFCT_BITSET); return 0; } /** * @} */ /** * \defgroup nl Low level object to Netlink message * @{ */ /** * nfct_build_conntrack - build a netlink message from a conntrack object * \param ssh nfnetlink subsystem handler * \param req buffer used to build the netlink message * \param size size of the buffer passed * \param type netlink message type * \param flags netlink flags * \param ct 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 nfct_query. * * On error, -1 is returned and errno is appropiately set. * On success, 0 is returned. */ int nfct_build_conntrack(struct nfnl_subsys_handle *ssh, void *req, size_t size, u_int16_t type, u_int16_t flags, const struct nf_conntrack *ct) { assert(ssh != NULL); assert(req != NULL); assert(ct != NULL); return __build_conntrack(ssh, req, size, type, flags, ct); } static int __build_query_ct(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_int32_t *family = data; assert(ssh != NULL); assert(data != NULL); assert(req != NULL); memset(req, 0, size); switch(qt) { case NFCT_Q_CREATE: __build_conntrack(ssh, req, size, IPCTNL_MSG_CT_NEW, NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK|NLM_F_EXCL, data); break; case NFCT_Q_UPDATE: __build_conntrack(ssh, req, size, IPCTNL_MSG_CT_NEW, NLM_F_REQUEST|NLM_F_ACK, data); break; case NFCT_Q_DESTROY: __build_conntrack(ssh, req, size, IPCTNL_MSG_CT_DELETE, NLM_F_REQUEST|NLM_F_ACK, data); break; case NFCT_Q_GET: __build_conntrack(ssh, req, size, IPCTNL_MSG_CT_GET, NLM_F_REQUEST|NLM_F_ACK, data); break; case NFCT_Q_FLUSH: nfnl_fill_hdr(ssh, &req->nlh, 0, *family, 0, IPCTNL_MSG_CT_DELETE, NLM_F_REQUEST|NLM_F_ACK); break; case NFCT_Q_DUMP: nfnl_fill_hdr(ssh, &req->nlh, 0, *family, 0, IPCTNL_MSG_CT_GET, NLM_F_REQUEST|NLM_F_DUMP); break; case NFCT_Q_DUMP_RESET: nfnl_fill_hdr(ssh, &req->nlh, 0, *family, 0, IPCTNL_MSG_CT_GET_CTRZERO, NLM_F_REQUEST|NLM_F_DUMP); break; case NFCT_Q_CREATE_UPDATE: __build_conntrack(ssh, req, size, IPCTNL_MSG_CT_NEW, NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK, data); break; case NFCT_Q_DUMP_FILTER: nfnl_fill_hdr(ssh, &req->nlh, 0, AF_UNSPEC, 0, IPCTNL_MSG_CT_GET, NLM_F_REQUEST|NLM_F_DUMP); __build_filter_dump(req, size, data); break; case NFCT_Q_DUMP_FILTER_RESET: nfnl_fill_hdr(ssh, &req->nlh, 0, AF_UNSPEC, 0, IPCTNL_MSG_CT_GET_CTRZERO, NLM_F_REQUEST|NLM_F_DUMP); __build_filter_dump(req, size, data); break; default: errno = ENOTSUP; return -1; } return 1; } /** * nfct_build_query - build a query in netlink message format for ctnetlink * \param ssh nfnetlink subsystem handler * \param qt query type * \param data data required to build the query * \param req buffer to build the netlink message * \param 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 nfct_query. * * The pointer to data can be a conntrack object or the protocol family * depending on the request. * * For query types: * - NFCT_Q_CREATE: add a new conntrack, if it exists, fail * - NFCT_O_CREATE_UPDATE: add a new conntrack, if it exists, update it * - NFCT_Q_UPDATE: update a conntrack * - NFCT_Q_DESTROY: destroy a conntrack * - NFCT_Q_GET: get a conntrack * * Pass a valid pointer to a conntrack object. * * For query types: * - NFCT_Q_FLUSH: flush the conntrack table * - NFCT_Q_DUMP: dump the conntrack table * - NFCT_Q_DUMP_RESET: dump the conntrack table and reset counters * - NFCT_Q_DUMP_FILTER: dump the conntrack table * - NFCT_Q_DUMP_FILTER_RESET: dump the conntrack table and reset counters * * Pass a valid pointer to the protocol family (u_int32_t) * * On success, 0 is returned. On error, -1 is returned and errno is set * appropiately. */ int nfct_build_query(struct nfnl_subsys_handle *ssh, const enum nf_conntrack_query qt, const void *data, void *buffer, unsigned int size) { return __build_query_ct(ssh, qt, data, buffer, size); } /** * nfct_parse_conntrack - translate a netlink message to a conntrack object * \param type do the translation iif the message type is of a certain type * \param nlh pointer to the netlink message * \param ct 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: * * - NFCT_T_NEW: parse messages with new conntracks * - NFCT_T_UPDATE: parse messages with conntrack updates * - NFCT_T_DESTROY: parse messages with conntrack destroy * - NFCT_T_ALL: all message types * * The message type is a flag, therefore the can be combined, ie. * NFCT_T_NEW | NFCT_T_DESTROY to parse only new and destroy messages * * On error, NFCT_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 nfct_parse_conntrack(enum nf_conntrack_msg_type type, const struct nlmsghdr *nlh, struct nf_conntrack *ct) { unsigned int flags; int len = nlh->nlmsg_len; struct nfgenmsg *nfhdr = NLMSG_DATA(nlh); struct nfattr *cda[CTA_MAX]; assert(nlh != NULL); assert(ct != NULL); len -= NLMSG_LENGTH(sizeof(struct nfgenmsg)); if (len < 0) { errno = EINVAL; return NFCT_T_ERROR; } flags = __parse_message_type(nlh); if (!(flags & type)) return 0; nfnl_parse_attr(cda, CTA_MAX, NFA_DATA(nfhdr), len); __parse_conntrack(nlh, cda, ct); return flags; } /** * @} */ /** * \defgroup cmd Send commands to kernel-space and receive replies * @{ */ /** * nfct_query - send a query to ctnetlink and handle the reply * \param h library handler * \param qt query type * \param data data required to send the query * * On error, -1 is returned and errno is explicitely set. On success, 0 * is returned. */ int nfct_query(struct nfct_handle *h, const enum nf_conntrack_query qt, const void *data) { size_t size = 4096; /* enough for now */ union { char buffer[size]; struct nfnlhdr req; } u; assert(h != NULL); assert(data != NULL); if (__build_query_ct(h->nfnlssh_ct, qt, data, &u.req, size) == -1) return -1; return nfnl_query(h->nfnlh, &u.req.nlh); } /** * nfct_send - send a query to ctnetlink * \param h library handler * \param qt query type * \param data data required to send the query * * Like nfct_query but we do not wait for the reply from ctnetlink. * You can use nfct_send() and nfct_catch() to emulate nfct_query(). * This is particularly useful when the socket is non-blocking. * * On error, -1 is returned and errno is explicitely set. On success, 0 * is returned. */ int nfct_send(struct nfct_handle *h, const enum nf_conntrack_query qt, const void *data) { size_t size = 4096; /* enough for now */ union { char buffer[size]; struct nfnlhdr req; } u; assert(h != NULL); assert(data != NULL); if (__build_query_ct(h->nfnlssh_ct, qt, data, &u.req, size) == -1) return -1; return nfnl_send(h->nfnlh, &u.req.nlh); } /** * nfct_catch - catch events * \param 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 * verdict: NFCT_CB_STOP, NFCT_CB_CONTINUE or NFCT_CB_STOLEN */ int nfct_catch(struct nfct_handle *h) { assert(h != NULL); return nfnl_catch(h->nfnlh); } /** * @} */ /** * \defgroup ct Conntrack object handling * @{ */ /** * nfct_snprintf - print a conntrack object to a buffer * \param buf buffer used to build the printable conntrack * \param size size of the buffer * \param ct pointer to a valid conntrack object * \param message_type print message type (NFCT_T_UNKNOWN, NFCT_T_NEW,...) * \param output_type print type (NFCT_O_DEFAULT, NFCT_O_XML, ...) * \param flags extra flags for the output type (NFCT_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. NFCT_T_NEW, NFCT_T_UPDATE, NFCT_T_DESTROY. * If you pass NFCT_T_UNKNOWN, the message type will not be output. * * Currently, the output available are: * - NFCT_O_DEFAULT: default /proc-like output * - NFCT_O_XML: XML output * * The output flags are: * - NFCT_OF_SHOW_LAYER3: include layer 3 information in the output, * this is *only* required by NFCT_O_DEFAULT. * - NFCT_OF_TIME: display current time. * - NFCT_OF_ID: display the ID number. * - NFCT_OF_TIMESTAMP: display creation and (if exists) deletion time. * * To use NFCT_OF_TIMESTAMP, you have to: * \verbatim * $ echo 1 > /proc/sys/net/netfilter/nf_conntrack_timestamp \endverbatim * This requires a Linux kernel >= 2.6.38. * * Note that NFCT_OF_TIME displays the current time when nfct_snprintf() has * been called. Thus, it can be used to know when a flow was destroy if you * print the message just after you receive the destroy event. If you want * more accurate timestamping, use NFCT_OF_TIMESTAMP. * * This function returns the size of the information that _would_ have been * written to the buffer, even if there was no room for it. Thus, the * behaviour is similar to snprintf. */ int nfct_snprintf(char *buf, unsigned int size, const struct nf_conntrack *ct, unsigned int msg_type, unsigned int out_type, unsigned int flags) { assert(buf != NULL); assert(size > 0); assert(ct != NULL); return __snprintf_conntrack(buf, size, ct, msg_type, out_type, flags); } /** * nfct_compare - compare two conntrack objects * \param ct1 pointer to a valid conntrack object * \param ct2 pointer to a valid conntrack object * * This function only compare attribute set in both objects, ie. if a certain * attribute is not set in ct1 but it is in ct2, then the value of such * attribute is not used in the comparison. * * If both conntrack object are equal, this function returns 1, otherwise * 0 is returned. * * NOTICE: The use nfct_cmp is preferred. */ int nfct_compare(const struct nf_conntrack *ct1, const struct nf_conntrack *ct2) { assert(ct1 != NULL); assert(ct2 != NULL); return __compare(ct1, ct2, NFCT_CMP_ALL); } /** * nfct_cmp - compare two conntrack objects * \param ct1 pointer to a valid conntrack object * \param ct2 pointer to a valid conntrack object * \param flags flags * * This function only compare attribute set in both objects, by default * the comparison is not strict, ie. if a certain attribute is not set in one * of the objects, then such attribute is not used in the comparison. * If you want more strict comparisons, you can use the appropriate flags * to modify this behaviour (see NFCT_CMP_STRICT and NFCT_CMP_MASK). * * The available flags are: * * - NFCT_CMP_STRICT: the compared objects must have the same attributes * and the same values, otherwise it returns that the objects are * different. * - NFCT_CMP_MASK: the first object is used as mask, this means that * if an attribute is present in ct1 but not in ct2, this function * returns that the objects are different. * - NFCT_CMP_ALL: full comparison of both objects * - NFCT_CMP_ORIG: it only compares the source and destination address; * source and destination ports; the layer 3 and 4 protocol numbers * of the original direction; and the id (if present). * - NFCT_CMP_REPL: like NFCT_CMP_REPL but it compares the flow * information that goes in the reply direction. * - NFCT_CMP_TIMEOUT_EQ: timeout(ct1) == timeout(ct2) * - NFCT_CMP_TIMEOUT_GT: timeout(ct1) > timeout(ct2) * - NFCT_CMP_TIMEOUT_LT: timeout(ct1) < timeout(ct2) * - NFCT_CMP_TIMEOUT_GE: timeout(ct1) >= timeout(ct2) * - NFCT_CMP_TIMEOUT_LE: timeout(ct1) <= timeout(ct2) * * The status bits comparison is status(ct1) & status(ct2) == status(ct1). * * If both conntrack object are equal, this function returns 1, otherwise * 0 is returned. */ int nfct_cmp(const struct nf_conntrack *ct1, const struct nf_conntrack *ct2, unsigned int flags) { assert(ct1 != NULL); assert(ct2 != NULL); return __compare(ct1, ct2, flags); } /** * nfct_copy - copy part of one source object to another * \param ct1 destination object * \param ct2 source object * \param flags flags * * This function copies one part of the source object to the target. * It behaves like clone but: * * 1) You have to pass an already allocated space for the target object * 2) You can copy only a part of the source object to the target * * The current supported flags are: * - NFCT_CP_ALL: that copies the object entirely. * - NFCT_CP_ORIG and NFCT_CP_REPL: that can be used to copy the * information that identifies a flow in the original and the reply * direction. This information is usually composed of: source and * destination IP address; source and destination ports; layer 3 * and 4 protocol number. * - NFCT_CP_META: that copies the metainformation * (all the attributes >= ATTR_TCP_STATE) * - NFCT_CP_OVERRIDE: changes the default behaviour of nfct_copy() since * it overrides the destination object. After the copy, the destination * is a clone of the origin. This flag provides faster copying. */ void nfct_copy(struct nf_conntrack *ct1, const struct nf_conntrack *ct2, unsigned int flags) { int i; assert(ct1 != NULL); assert(ct2 != NULL); if (flags & NFCT_CP_OVERRIDE) { __copy_fast(ct1, ct2); return; } if (flags == NFCT_CP_ALL) { for (i=0; ihead.set)) { assert(copy_attr_array[i]); copy_attr_array[i](ct1, ct2); set_bit(i, ct1->head.set); } } return; } static const int cp_orig_mask[] = { ATTR_ORIG_IPV4_SRC, ATTR_ORIG_IPV4_DST, ATTR_ORIG_IPV6_SRC, ATTR_ORIG_IPV6_DST, ATTR_ORIG_PORT_SRC, ATTR_ORIG_PORT_DST, ATTR_ICMP_TYPE, ATTR_ICMP_CODE, ATTR_ICMP_ID, ATTR_ORIG_L3PROTO, ATTR_ORIG_L4PROTO, }; #define __CP_ORIG_MAX sizeof(cp_orig_mask)/sizeof(int) if (flags & NFCT_CP_ORIG) { for (i=0; i<__CP_ORIG_MAX; i++) { if (test_bit(cp_orig_mask[i], ct2->head.set)) { assert(copy_attr_array[i]); copy_attr_array[cp_orig_mask[i]](ct1, ct2); set_bit(cp_orig_mask[i], ct1->head.set); } } } static const int cp_repl_mask[] = { ATTR_REPL_IPV4_SRC, ATTR_REPL_IPV4_DST, ATTR_REPL_IPV6_SRC, ATTR_REPL_IPV6_DST, ATTR_REPL_PORT_SRC, ATTR_REPL_PORT_DST, ATTR_REPL_L3PROTO, ATTR_REPL_L4PROTO, }; #define __CP_REPL_MAX sizeof(cp_repl_mask)/sizeof(int) if (flags & NFCT_CP_REPL) { for (i=0; i<__CP_REPL_MAX; i++) { if (test_bit(cp_repl_mask[i], ct2->head.set)) { assert(copy_attr_array[i]); copy_attr_array[cp_repl_mask[i]](ct1, ct2); set_bit(cp_repl_mask[i], ct1->head.set); } } } if (flags & NFCT_CP_META) { for (i=ATTR_TCP_STATE; ihead.set)) { assert(copy_attr_array[i]), copy_attr_array[i](ct1, ct2); set_bit(i, ct1->head.set); } } } } /** * nfct_copy_attr - copy an attribute of one source object to another * \param ct1 destination object * \param ct2 source object * \param flags flags * * This function copies one attribute (if present) to another object. */ void nfct_copy_attr(struct nf_conntrack *ct1, const struct nf_conntrack *ct2, const enum nf_conntrack_attr type) { if (test_bit(type, ct2->head.set)) { assert(copy_attr_array[type]); copy_attr_array[type](ct1, ct2); set_bit(type, ct1->head.set); } } /** * @} */ /** * \defgroup bsf Kernel-space filtering for events * * @{ */ /** * nfct_filter_create - create a filter * * This function returns a valid pointer on success, otherwise NULL is * returned and errno is appropriately set. */ struct nfct_filter *nfct_filter_create(void) { return calloc(sizeof(struct nfct_filter), 1); } /** * nfct_filter_destroy - destroy a filter * \param filter filter that we want to destroy * * This function releases the memory that is used by the filter object. * However, please note that this function does *not* detach an already * attached filter. */ void nfct_filter_destroy(struct nfct_filter *filter) { assert(filter != NULL); free(filter); filter = NULL; } /** * nfct_filter_add_attr - add a filter attribute of the filter object * \param filter filter object that we want to modify * \param type filter attribute type * \param value pointer to the value of the filter attribute * * Limitations: You can add up to 127 IPv4 addresses and masks for * NFCT_FILTER_SRC_IPV4 and, similarly, 127 for NFCT_FILTER_DST_IPV4. */ void nfct_filter_add_attr(struct nfct_filter *filter, const enum nfct_filter_attr type, const void *value) { assert(filter != NULL); assert(value != NULL); if (unlikely(type >= NFCT_FILTER_MAX)) return; if (filter_attr_array[type]) { filter_attr_array[type](filter, value); set_bit(type, filter->set); } } /** * nfct_filter_add_attr_u32 - add an u32 filter attribute of the filter object * \param filter filter object that we want to modify * \param type filter attribute type * \param value value of the filter attribute using unsigned int (32 bits). * * Limitations: You can add up to 255 protocols which is a reasonable limit. */ void nfct_filter_add_attr_u32(struct nfct_filter *filter, const enum nfct_filter_attr type, u_int32_t value) { nfct_filter_add_attr(filter, type, &value); } /** * nfct_filter_set_logic - set the filter logic for an attribute type * \param filter filter object that we want to modify * \param type filter attribute type * \param logic filter logic that we want to use * * You can only use this function once to set the filtering logic for * one attribute. You can define two logics: NFCT_FILTER_POSITIVE_LOGIC * that accept events that match the filter, and NFCT_FILTER_NEGATIVE_LOGIC * that rejects events that match the filter. Default filtering logic is * NFCT_FILTER_POSITIVE_LOGIC. * * On error, it returns -1 and errno is appropriately set. On success, it * returns 0. */ int nfct_filter_set_logic(struct nfct_filter *filter, const enum nfct_filter_attr type, const enum nfct_filter_logic logic) { if (unlikely(type >= NFCT_FILTER_MAX)) { errno = ENOTSUP; return -1; } if (filter->logic[type]) { errno = EBUSY; return -1; } filter->logic[type] = logic; return 0; } /** * nfct_filter_attach - attach a filter to a socket descriptor * \param fd socket descriptor * \param filter filter that we want to attach to the socket * * This function returns -1 on error and set errno appropriately. If the * function returns EINVAL probably you have found a bug in it. Please, * report this. */ int nfct_filter_attach(int fd, struct nfct_filter *filter) { assert(filter != NULL); return __setup_netlink_socket_filter(fd, filter); } /** * nfct_filter_detach - detach an existing filter * \param fd socket descriptor * * This function returns -1 on error and set errno appropriately. */ int nfct_filter_detach(int fd) { int val = 0; return setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &val, sizeof(val)); } /** * @} */ /** * \defgroup dumpfilter Kernel-space filtering for dumping * * @{ */ /** * nfct_filter_dump_create - create a dump filter * * This function returns a valid pointer on success, otherwise NULL is * returned and errno is appropriately set. */ struct nfct_filter_dump *nfct_filter_dump_create(void) { return calloc(sizeof(struct nfct_filter_dump), 1); } /** * nfct_filter_dump_destroy - destroy a dump filter * \param filter filter that we want to destroy * * This function releases the memory that is used by the filter object. */ void nfct_filter_dump_destroy(struct nfct_filter_dump *filter) { assert(filter != NULL); free(filter); filter = NULL; } /** * nfct_filter_dump_attr_set - set filter attribute * \param filter dump filter object that we want to modify * \param type filter attribute type * \param value pointer to the value of the filter attribute */ void nfct_filter_dump_set_attr(struct nfct_filter_dump *filter_dump, const enum nfct_filter_dump_attr type, const void *value) { assert(filter_dump != NULL); assert(value != NULL); if (unlikely(type >= NFCT_FILTER_DUMP_MAX)) return; if (set_filter_dump_attr_array[type]) { set_filter_dump_attr_array[type](filter_dump, value); filter_dump->set |= (1 << type); } } /** * nfct_filter_dump_attr_set_u8 - set u8 dump filter attribute * \param filter dump filter object that we want to modify * \param type filter attribute type * \param value value of the filter attribute using unsigned int (32 bits). */ void nfct_filter_dump_set_attr_u8(struct nfct_filter_dump *filter_dump, const enum nfct_filter_dump_attr type, u_int8_t value) { nfct_filter_dump_set_attr(filter_dump, type, &value); } /** * @} */ /** * \defgroup label Conntrack labels * * @{ */ /** * nfct_labelmap_get_name - get name of the label bit * * \param m label map obtained from nfct_label_open * \param bit whose name should be returned * * returns a pointer to the name associated with the label. * If no name has been configured, the empty string is returned. * If bit is out of range, NULL is returned. */ const char *nfct_labelmap_get_name(struct nfct_labelmap *m, unsigned int bit) { return __labelmap_get_name(m, bit); } /** * nfct_labelmap_get_bit - get bit associated with the name * * \param h label handle obtained from nfct_labelmap_new * \param name name of the label * * returns the bit associated with the name, or negative value on error. */ int nfct_labelmap_get_bit(struct nfct_labelmap *m, const char *name) { return __labelmap_get_bit(m, name); } /** * nfct_labelmap_new - create a new label map * * \param mapfile the file containing the bit <-> name mapping * * If mapfile is NULL, the default mapping file is used. * returns a new label map, or NULL on error. */ struct nfct_labelmap *nfct_labelmap_new(const char *mapfile) { return __labelmap_new(mapfile); } /** * nfct_labelmap_destroy - destroy nfct_labelmap object * * \param map the label object to destroy. * * This function releases the memory that is used by the labelmap object. */ void nfct_labelmap_destroy(struct nfct_labelmap *map) { __labelmap_destroy(map); } /** * @} */ /* * \defgroup bitmask bitmask object * * @{ */ /** * nfct_bitmask_new - allocate a new bitmask * * \param max highest valid bit that can be set/unset. * * In case of success, this function returns a valid pointer to a memory blob, * otherwise NULL is returned and errno is set appropiately. */ struct nfct_bitmask *nfct_bitmask_new(unsigned int max) { struct nfct_bitmask *b; unsigned int bytes, words; if (max > 0xffff) return NULL; words = DIV_ROUND_UP(max+1, 32); bytes = words * sizeof(b->bits[0]); b = malloc(sizeof(*b) + bytes); if (b) { memset(b->bits, 0, bytes); b->words = words; } return b; } /* * nfct_bitmask_clone - duplicate a bitmask object * * \param b pointer to the bitmask object to duplicate * * returns an identical copy of the bitmask. */ struct nfct_bitmask *nfct_bitmask_clone(const struct nfct_bitmask *b) { unsigned int bytes = b->words * sizeof(b->bits[0]); struct nfct_bitmask *copy; bytes += sizeof(*b); copy = malloc(bytes); if (copy) memcpy(copy, b, bytes); return copy; } /* * nfct_bitmask_set_bit - set bit in the bitmask * * \param b pointer to the bitmask object * \param bit the bit to set */ void nfct_bitmask_set_bit(struct nfct_bitmask *b, unsigned int bit) { unsigned int bits = b->words * 32; if (bit < bits) set_bit(bit, b->bits); } /* * nfct_bitmask_test_bit - test if a bit in the bitmask is set * * \param b pointer to the bitmask object * \param bit the bit to test * * returns 0 if the bit is not set. */ int nfct_bitmask_test_bit(const struct nfct_bitmask *b, unsigned int bit) { unsigned int bits = b->words * 32; return bit < bits && test_bit(bit, b->bits); } /* * nfct_bitmask_unset_bit - unset bit in the bitmask * * \param b pointer to the bitmask object * \param bit the bit to clear */ void nfct_bitmask_unset_bit(struct nfct_bitmask *b, unsigned int bit) { unsigned int bits = b->words * 32; if (bit < bits) unset_bit(bit, b->bits); } /* * nfct_bitmask_maxbit - return highest bit that may be set/unset * * \param b pointer to the bitmask object */ unsigned int nfct_bitmask_maxbit(const struct nfct_bitmask *b) { return (b->words * 32) - 1; } /* * nfct_bitmask_destroy - destroy bitmask object * * \param b pointer to the bitmask object * * This function releases the memory that is used by the bitmask object. * * If you assign a bitmask object to a nf_conntrack object using * nfct_set_attr ATTR_CONNLABEL, then the ownership of the bitmask * object passes on to the nf_conntrack object. The nfct_bitmask object * will be destroyed when the nf_conntrack object is destroyed. */ void nfct_bitmask_destroy(struct nfct_bitmask *b) { free(b); } /** * @} */