/* * (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 "queue.h" #include "network.h" #include "log.h" #include "cache.h" #include "fds.h" #include static struct alarm_block alive_alarm; /* XXX: alive message expiration configurable */ #define ALIVE_INT 1 struct cache_notrack { struct queue_node qnode; struct cache_object *obj; }; static void cache_notrack_add(struct cache_object *obj, void *data) { struct cache_notrack *cn = data; queue_node_init(&cn->qnode, Q_ELEM_OBJ); cn->obj = obj; } static void cache_notrack_del(struct cache_object *obj, void *data) { struct cache_notrack *cn = data; queue_del(&cn->qnode); } 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 queue_object *qobj; struct nethdr_ack *ack; qobj = queue_object_new(Q_ELEM_CTL, sizeof(struct nethdr_ack)); if (qobj == NULL) return; ack = (struct nethdr_ack *)qobj->data; ack->type = NET_T_CTL; ack->flags = flags; ack->from = from; ack->to = to; if (queue_add(STATE_SYNC(tx_queue), &qobj->qnode) < 0) queue_object_free(qobj); } static int do_cache_to_tx(void *data1, void *data2) { struct cache_object *obj = data2; struct cache_notrack *cn = cache_get_extra(obj); if (queue_add(STATE_SYNC(tx_queue), &cn->qnode) > 0) cache_object_get(obj); return 0; } static int kernel_resync_cb(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data) { struct nethdr *net; net = BUILD_NETMSG_FROM_CT(ct, NET_T_STATE_CT_NEW); multichannel_send(STATE_SYNC(channel), net); return NFCT_CB_CONTINUE; } /* Only used if the internal cache is disabled. */ static void kernel_resync(void) { struct nfct_handle *h; u_int32_t family = AF_UNSPEC; int ret; h = nfct_open(CONFIG(netlink).subsys_id, 0); if (h == NULL) { dlog(LOG_ERR, "can't allocate memory for the internal cache"); return; } nfct_callback_register(h, NFCT_T_ALL, kernel_resync_cb, NULL); ret = nfct_query(h, NFCT_Q_DUMP, &family); if (ret == -1) { dlog(LOG_ERR, "can't dump kernel table"); } nfct_close(h); } static int notrack_local(int fd, int type, void *data) { int ret = LOCAL_RET_OK; 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"); if (CONFIG(sync).internal_cache_disable) { kernel_resync(); } else { cache_iterate(STATE(mode)->internal->ct.data, NULL, do_cache_to_tx); cache_iterate(STATE(mode)->internal->exp.data, 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)) { if (CONFIG(sync).internal_cache_disable) { kernel_resync(); } else { cache_iterate(STATE(mode)->internal->ct.data, NULL, do_cache_to_tx); cache_iterate(STATE(mode)->internal->exp.data, NULL, do_cache_to_tx); } return MSG_CTL; } if (IS_ALIVE(net)) return MSG_CTL; return MSG_BAD; } static int notrack_recv(const struct nethdr *net) { int ret; unsigned int exp_seq; nethdr_track_seq(net->seq, &exp_seq); ret = digest_msg(net); if (ret != MSG_BAD) nethdr_track_update_seq(net->seq); return ret; } static int tx_queue_xmit(struct queue_node *n, const void *data2) { switch (n->type) { case Q_ELEM_CTL: { struct nethdr *net = queue_node_data(n); if (IS_RESYNC(net)) nethdr_set_ack(net); else nethdr_set_ctl(net); HDR_HOST2NETWORK(net); multichannel_send(STATE_SYNC(channel), net); queue_del(n); queue_object_free((struct queue_object *)n); break; } case Q_ELEM_OBJ: { struct cache_notrack *cn; int type; struct nethdr *net; cn = (struct cache_notrack *)n; type = object_status_to_network_type(cn->obj); net = cn->obj->cache->ops->build_msg(cn->obj, type); multichannel_send(STATE_SYNC(channel), net); queue_del(n); cache_object_put(cn->obj); break; } } return 0; } static void notrack_xmit(void) { queue_iterate(STATE_SYNC(tx_queue), NULL, tx_queue_xmit); add_alarm(&alive_alarm, ALIVE_INT, 0); } static void notrack_enqueue(struct cache_object *obj, int query) { struct cache_notrack *cn = cache_get_extra(obj); if (queue_add(STATE_SYNC(tx_queue), &cn->qnode) > 0) cache_object_get(obj); } static void tx_queue_add_ctlmsg2(uint32_t flags) { struct queue_object *qobj; struct nethdr *ctl; qobj = queue_object_new(Q_ELEM_CTL, sizeof(struct nethdr_ack)); if (qobj == NULL) return; ctl = (struct nethdr *)qobj->data; ctl->type = NET_T_CTL; ctl->flags = flags; if (queue_add(STATE_SYNC(tx_queue), &qobj->qnode) < 0) queue_object_free(qobj); } static void do_alive_alarm(struct alarm_block *a, void *data) { tx_queue_add_ctlmsg2(NET_F_ALIVE); add_alarm(&alive_alarm, ALIVE_INT, 0); } static int notrack_init(void) { init_alarm(&alive_alarm, NULL, do_alive_alarm); add_alarm(&alive_alarm, ALIVE_INT, 0); return 0; } struct sync_mode sync_notrack = { .internal_cache_flags = NO_FEATURES, .external_cache_flags = NO_FEATURES, .internal_cache_extra = &cache_notrack_extra, .init = notrack_init, .local = notrack_local, .recv = notrack_recv, .enqueue = notrack_enqueue, .xmit = notrack_xmit, };