/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include /* assert */ #include /* errno */ #include /* calloc, free */ #include /* time */ #include /* hto* */ #include /* enum ipset_cmd */ #include /* D() */ #include /* ipset_session_handle */ #include /* IPSET_ENV_EXIST */ #include /* UNUSED */ #include /* prototypes */ #ifndef NFNL_SUBSYS_IPSET #define NFNL_SUBSYS_IPSET 6 #endif /* Internal data structure for the kernel-userspace communication parameters */ struct ipset_handle { struct mnl_socket *h; /* the mnl socket */ unsigned int seq; /* netlink message sequence number */ unsigned int portid; /* the socket port identifier */ mnl_cb_t *cb_ctl; /* control block callbacks */ void *data; /* data pointer */ }; /* Netlink flags of the commands */ static const uint16_t cmdflags[] = { [IPSET_CMD_CREATE-1] = NLM_F_REQUEST|NLM_F_ACK| NLM_F_CREATE|NLM_F_EXCL, [IPSET_CMD_DESTROY-1] = NLM_F_REQUEST|NLM_F_ACK, [IPSET_CMD_FLUSH-1] = NLM_F_REQUEST|NLM_F_ACK, [IPSET_CMD_RENAME-1] = NLM_F_REQUEST|NLM_F_ACK, [IPSET_CMD_SWAP-1] = NLM_F_REQUEST|NLM_F_ACK, [IPSET_CMD_LIST-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_DUMP, [IPSET_CMD_SAVE-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_DUMP, [IPSET_CMD_ADD-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL, [IPSET_CMD_DEL-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL, [IPSET_CMD_TEST-1] = NLM_F_REQUEST|NLM_F_ACK, [IPSET_CMD_HEADER-1] = NLM_F_REQUEST, [IPSET_CMD_TYPE-1] = NLM_F_REQUEST, [IPSET_CMD_PROTOCOL-1] = NLM_F_REQUEST, }; /** * ipset_get_nlmsg_type - get ipset netlink message type * @nlh: pointer to the netlink message header * * Returns the ipset netlink message type, i.e. the ipset command. */ int ipset_get_nlmsg_type(const struct nlmsghdr *nlh) { return nlh->nlmsg_type & ~(NFNL_SUBSYS_IPSET << 8); } static void ipset_mnl_fill_hdr(struct ipset_handle *handle, enum ipset_cmd cmd, void *buffer, size_t len UNUSED, uint8_t envflags) { struct nlmsghdr *nlh; struct nfgenmsg *nfg; assert(handle); assert(buffer); assert(cmd > IPSET_CMD_NONE && cmd < IPSET_MSG_MAX); nlh = mnl_nlmsg_put_header(buffer); nlh->nlmsg_type = cmd | (NFNL_SUBSYS_IPSET << 8); nlh->nlmsg_flags = cmdflags[cmd - 1]; if (envflags & IPSET_ENV_EXIST) nlh->nlmsg_flags &= ~NLM_F_EXCL; nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); nfg->nfgen_family = AF_INET; nfg->version = NFNETLINK_V0; nfg->res_id = htons(0); } static int ipset_mnl_query(struct ipset_handle *handle, void *buffer, size_t len) { struct nlmsghdr *nlh = buffer; int ret; assert(handle); assert(buffer); nlh->nlmsg_seq = ++handle->seq; #ifdef IPSET_DEBUG ipset_debug_msg("sent", nlh, nlh->nlmsg_len); #endif if (mnl_socket_sendto(handle->h, nlh, nlh->nlmsg_len) < 0) return -ECOMM; ret = mnl_socket_recvfrom(handle->h, buffer, len); #ifdef IPSET_DEBUG ipset_debug_msg("received", buffer, ret); #endif while (ret > 0) { ret = mnl_cb_run2(buffer, ret, handle->seq, handle->portid, handle->cb_ctl[NLMSG_MIN_TYPE], handle->data, handle->cb_ctl, NLMSG_MIN_TYPE); D("nfln_cb_run2, ret: %d, errno %d", ret, errno); if (ret <= 0) break; ret = mnl_socket_recvfrom(handle->h, buffer, len); D("message received, ret: %d", ret); } return ret > 0 ? 0 : ret; } static struct ipset_handle * ipset_mnl_init(mnl_cb_t *cb_ctl, void *data) { struct ipset_handle *handle; assert(cb_ctl); assert(data); handle = calloc(1, sizeof(*handle)); if (!handle) return NULL; handle->h = mnl_socket_open(NETLINK_NETFILTER); if (!handle->h) goto free_handle; if (mnl_socket_bind(handle->h, 0, MNL_SOCKET_AUTOPID) < 0) goto close_nl; handle->portid = mnl_socket_get_portid(handle->h); handle->cb_ctl = cb_ctl; handle->data = data; handle->seq = time(NULL); return handle; close_nl: mnl_socket_close(handle->h); free_handle: free(handle); return NULL; } static int ipset_mnl_fini(struct ipset_handle *handle) { assert(handle); if (handle->h) mnl_socket_close(handle->h); free(handle); return 0; } const struct ipset_transport ipset_mnl_transport = { .init = ipset_mnl_init, .fini = ipset_mnl_fini, .fill_hdr = ipset_mnl_fill_hdr, .query = ipset_mnl_query, };