/* * (C) 2008 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. * * 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 "us-conntrack.h" #include "queue.h" #include "debug.h" #include "network.h" #include "log.h" #include "cache.h" #include "event.h" #include static LIST_HEAD(tx_list); static unsigned int tx_list_len; static struct queue *tx_queue; struct cache_notrack { struct list_head tx_list; }; static void cache_notrack_add(struct us_conntrack *u, void *data) { struct cache_notrack *cn = data; INIT_LIST_HEAD(&cn->tx_list); } static void cache_notrack_del(struct us_conntrack *u, void *data) { struct cache_notrack *cn = data; if (!list_empty(&cn->tx_list)) { list_del(&cn->tx_list); tx_list_len--; } } static struct cache_extra cache_notrack_extra = { .size = sizeof(struct cache_notrack), .add = cache_notrack_add, .destroy = cache_notrack_del }; static void tx_queue_add_ctlmsg(uint32_t flags, uint32_t from, uint32_t to) { struct nethdr_ack ack = { .flags = flags, .from = from, .to = to, }; queue_add(tx_queue, &ack, NETHDR_ACK_SIZ); write_evfd(STATE_SYNC(evfd)); } static int notrack_init(void) { tx_queue = queue_create(~0U); if (tx_queue == NULL) { dlog(LOG_ERR, "cannot create tx queue"); return -1; } return 0; } static void notrack_kill(void) { queue_destroy(tx_queue); } static int do_cache_to_tx(void *data1, void *data2) { struct us_conntrack *u = data2; struct cache_notrack *cn = cache_get_extra(STATE_SYNC(internal), u); if (!list_empty(&cn->tx_list)) return 0; /* add to tx list */ list_add_tail(&cn->tx_list, &tx_list); tx_list_len++; write_evfd(STATE_SYNC(evfd)); return 0; } static int notrack_local(int fd, int type, void *data) { int ret = 1; switch(type) { case REQUEST_DUMP: dlog(LOG_NOTICE, "request resync"); tx_queue_add_ctlmsg(NET_F_RESYNC, 0, 0); break; case SEND_BULK: dlog(LOG_NOTICE, "sending bulk update"); cache_iterate(STATE_SYNC(internal), NULL, do_cache_to_tx); break; default: ret = 0; break; } return ret; } static int digest_msg(const struct nethdr *net) { if (IS_DATA(net)) return MSG_DATA; if (IS_RESYNC(net)) { cache_iterate(STATE_SYNC(internal), NULL, do_cache_to_tx); return MSG_CTL; } return MSG_BAD; } static int notrack_recv(const struct nethdr *net) { int ret; unsigned int exp_seq; mcast_track_seq(net->seq, &exp_seq); ret = digest_msg(net); if (ret != MSG_BAD) mcast_track_update_seq(net->seq); return ret; } static int tx_queue_xmit(void *data1, const void *data2) { struct nethdr *net = data1; size_t len = prepare_send_netmsg(STATE_SYNC(mcast_client), net); mcast_buffered_send_netmsg(STATE_SYNC(mcast_client), net, len); queue_del(tx_queue, net); return 0; } static int tx_list_xmit(struct list_head *i, struct us_conntrack *u, int type) { int ret; struct nethdr *net = BUILD_NETMSG(u->ct, type); size_t len = prepare_send_netmsg(STATE_SYNC(mcast_client), net); list_del_init(i); tx_list_len--; ret = mcast_buffered_send_netmsg(STATE_SYNC(mcast_client), net, len); return ret; } static void notrack_run(void) { struct cache_notrack *cn, *tmp; /* send messages in the tx_queue */ queue_iterate(tx_queue, NULL, tx_queue_xmit); /* send conntracks in the tx_list */ list_for_each_entry_safe(cn, tmp, &tx_list, tx_list) { struct us_conntrack *u; u = cache_get_conntrack(STATE_SYNC(internal), cn); tx_list_xmit(&cn->tx_list, u, NFCT_Q_UPDATE); } } struct sync_mode sync_notrack = { .internal_cache_flags = LIFETIME, .external_cache_flags = LIFETIME, .internal_cache_extra = &cache_notrack_extra, .init = notrack_init, .kill = notrack_kill, .local = notrack_local, .recv = notrack_recv, .run = notrack_run, };