/* * (C) 2006-2011 by Pablo Neira Ayuso * (C) 2011 by Vyatta Inc. * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "conntrackd.h" #include "sync.h" #include "log.h" #include "cache.h" #include "origin.h" #include "external.h" #include "netlink.h" #include #include #include static struct nfct_handle *inject; struct { uint32_t add_ok; uint32_t add_fail; uint32_t upd_ok; uint32_t upd_fail; uint32_t del_ok; uint32_t del_fail; } external_inject_stat; static int external_inject_init(void) { /* handler to directly inject conntracks into kernel-space */ inject = nfct_open(CONFIG(netlink).subsys_id, 0); if (inject == NULL) { dlog(LOG_ERR, "can't open netlink handler: %s", strerror(errno)); dlog(LOG_ERR, "no ctnetlink kernel support?"); return -1; } /* we are directly injecting the entries into the kernel */ origin_register(inject, CTD_ORIGIN_INJECT); return 0; } static void external_inject_close(void) { origin_unregister(inject); nfct_close(inject); } static void external_inject_ct_new(struct nf_conntrack *ct) { int ret, retry = 1; retry: if (nl_create_conntrack(inject, ct, 0) == -1) { /* if the state entry exists, we delete and try again */ if (errno == EEXIST && retry == 1) { ret = nl_destroy_conntrack(inject, ct); if (ret == 0 || (ret == -1 && errno == ENOENT)) { if (retry) { retry = 0; goto retry; } } external_inject_stat.add_fail++; dlog(LOG_ERR, "inject-add1: %s", strerror(errno)); dlog_ct(STATE(log), ct, NFCT_O_PLAIN); return; } external_inject_stat.add_fail++; dlog(LOG_ERR, "inject-add2: %s", strerror(errno)); dlog_ct(STATE(log), ct, NFCT_O_PLAIN); } else { external_inject_stat.add_ok++; } } static void external_inject_ct_upd(struct nf_conntrack *ct) { int ret; /* if we successfully update the entry, everything is OK */ if (nl_update_conntrack(inject, ct, 0) != -1) { external_inject_stat.upd_ok++; return; } /* state entries does not exist, we have to create it */ if (errno == ENOENT) { if (nl_create_conntrack(inject, ct, 0) == -1) { external_inject_stat.upd_fail++; dlog(LOG_ERR, "inject-upd1: %s", strerror(errno)); dlog_ct(STATE(log), ct, NFCT_O_PLAIN); } else { external_inject_stat.upd_ok++; } return; } /* we failed to update the entry, there are some operations that * may trigger this error, eg. unset some status bits. Try harder, * delete the existing entry and create a new one. */ ret = nl_destroy_conntrack(inject, ct); if (ret == 0 || (ret == -1 && errno == ENOENT)) { if (nl_create_conntrack(inject, ct, 0) == -1) { external_inject_stat.upd_fail++; dlog(LOG_ERR, "inject-upd2: %s", strerror(errno)); dlog_ct(STATE(log), ct, NFCT_O_PLAIN); } else { external_inject_stat.upd_ok++; } return; } external_inject_stat.upd_fail++; dlog(LOG_ERR, "inject-upd3: %s", strerror(errno)); dlog_ct(STATE(log), ct, NFCT_O_PLAIN); } static void external_inject_ct_del(struct nf_conntrack *ct) { if (nl_destroy_conntrack(inject, ct) == -1) { if (errno != ENOENT) { external_inject_stat.del_fail++; dlog(LOG_ERR, "inject-del: %s", strerror(errno)); dlog_ct(STATE(log), ct, NFCT_O_PLAIN); } } else { external_inject_stat.del_ok++; } } static void external_inject_ct_dump(int fd, int type) { } static int external_inject_ct_commit(struct nfct_handle *h, int fd) { /* close the commit socket. */ return LOCAL_RET_OK; } static void external_inject_ct_flush(void) { } static void external_inject_ct_stats(int fd) { char buf[512]; int size; size = sprintf(buf, "external inject:\n" "connections created:\t\t%12u\tfailed:\t%12u\n" "connections updated:\t\t%12u\tfailed:\t%12u\n" "connections destroyed:\t\t%12u\tfailed:\t%12u\n\n", external_inject_stat.add_ok, external_inject_stat.add_fail, external_inject_stat.upd_ok, external_inject_stat.upd_fail, external_inject_stat.del_ok, external_inject_stat.del_fail); send(fd, buf, size, 0); } struct { uint32_t add_ok; uint32_t add_fail; uint32_t upd_ok; uint32_t upd_fail; uint32_t del_ok; uint32_t del_fail; } exp_external_inject_stat; static void external_inject_exp_new(struct nf_expect *exp) { int ret, retry = 1; retry: if (nl_create_expect(inject, exp, 0) == -1) { /* if the state entry exists, we delete and try again */ if (errno == EEXIST && retry == 1) { ret = nl_destroy_expect(inject, exp); if (ret == 0 || (ret == -1 && errno == ENOENT)) { if (retry) { retry = 0; goto retry; } } exp_external_inject_stat.add_fail++; dlog(LOG_ERR, "inject-add1: %s", strerror(errno)); dlog_exp(STATE(log), exp, NFCT_O_PLAIN); return; } exp_external_inject_stat.add_fail++; dlog(LOG_ERR, "inject-add2: %s", strerror(errno)); dlog_exp(STATE(log), exp, NFCT_O_PLAIN); } else { exp_external_inject_stat.add_ok++; } } static void external_inject_exp_del(struct nf_expect *exp) { if (nl_destroy_expect(inject, exp) == -1) { if (errno != ENOENT) { exp_external_inject_stat.del_fail++; dlog(LOG_ERR, "inject-del: %s", strerror(errno)); dlog_exp(STATE(log), exp, NFCT_O_PLAIN); } } else { exp_external_inject_stat.del_ok++; } } static void external_inject_exp_dump(int fd, int type) { } static int external_inject_exp_commit(struct nfct_handle *h, int fd) { /* close the commit socket. */ return LOCAL_RET_OK; } static void external_inject_exp_flush(void) { } static void external_inject_exp_stats(int fd) { char buf[512]; int size; size = sprintf(buf, "external inject:\n" "connections created:\t\t%12u\tfailed:\t%12u\n" "connections updated:\t\t%12u\tfailed:\t%12u\n" "connections destroyed:\t\t%12u\tfailed:\t%12u\n\n", exp_external_inject_stat.add_ok, exp_external_inject_stat.add_fail, exp_external_inject_stat.upd_ok, exp_external_inject_stat.upd_fail, exp_external_inject_stat.del_ok, exp_external_inject_stat.del_fail); send(fd, buf, size, 0); } struct external_handler external_inject = { .init = external_inject_init, .close = external_inject_close, .ct = { .new = external_inject_ct_new, .upd = external_inject_ct_upd, .del = external_inject_ct_del, .dump = external_inject_ct_dump, .commit = external_inject_ct_commit, .flush = external_inject_ct_flush, .stats = external_inject_ct_stats, .stats_ext = external_inject_ct_stats, }, .exp = { .new = external_inject_exp_new, .upd = external_inject_exp_new, .del = external_inject_exp_del, .dump = external_inject_exp_dump, .commit = external_inject_exp_commit, .flush = external_inject_exp_flush, .stats = external_inject_exp_stats, .stats_ext = external_inject_exp_stats, }, };