blob: 0ad34780634bdc19fede15cb56fda6ac4a5d30da [file] [log] [blame]
/*
* (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
* (C) 2011 by Vyatta Inc. <http://www.vyatta.com>
*
* 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 <libnetfilter_conntrack/libnetfilter_conntrack.h>
#include <errno.h>
#include <stdlib.h>
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,
},
};