1/*
2 * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
3 * (C) 2011 by Vyatta Inc. <http://www.vyatta.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20#include "conntrackd.h"
21#include "sync.h"
22#include "network.h"
23#include "alarm.h"
24#include "cache.h"
25#include "queue.h"
26
27#include <stdlib.h>
28#include <string.h>
29
30struct cache_alarm {
31	struct queue_node	qnode;
32	struct cache_object	*obj;
33	struct alarm_block	alarm;
34};
35
36static void alarm_enqueue(struct cache_object *obj, int query);
37
38static void refresher(struct alarm_block *a, void *data)
39{
40	struct cache_object *obj = data;
41
42	add_alarm(a,
43		  random() % CONFIG(refresh) + 1,
44		  ((random() % 5 + 1)  * 200000) - 1);
45
46	alarm_enqueue(obj, NET_T_STATE_CT_UPD);
47}
48
49static void cache_alarm_add(struct cache_object *obj, void *data)
50{
51	struct cache_alarm *ca = data;
52
53	queue_node_init(&ca->qnode, Q_ELEM_OBJ);
54	ca->obj = obj;
55	init_alarm(&ca->alarm, obj, refresher);
56	add_alarm(&ca->alarm,
57		  random() % CONFIG(refresh) + 1,
58		  ((random() % 5 + 1)  * 200000) - 1);
59}
60
61static void cache_alarm_update(struct cache_object *obj, void *data)
62{
63	struct cache_alarm *ca = data;
64	add_alarm(&ca->alarm,
65		  random() % CONFIG(refresh) + 1,
66		  ((random() % 5 + 1)  * 200000) - 1);
67}
68
69static void cache_alarm_destroy(struct cache_object *obj, void *data)
70{
71	struct cache_alarm *ca = data;
72	queue_del(&ca->qnode);
73	del_alarm(&ca->alarm);
74}
75
76static struct cache_extra cache_alarm_extra = {
77	.size 		= sizeof(struct cache_alarm),
78	.add		= cache_alarm_add,
79	.update		= cache_alarm_update,
80	.destroy	= cache_alarm_destroy
81};
82
83static int alarm_recv(const struct nethdr *net)
84{
85	unsigned int exp_seq;
86
87	/*
88	 * Ignore error messages: Although this message type is not ever
89	 * generated in alarm mode, we don't want to crash the daemon
90	 * if someone nuts mixes ftfw and alarm.
91	 */
92	if (net->flags)
93		return 1;
94
95	/*
96	 * Multicast sequence tracking: we keep track of multicast messages
97	 * although we don't do any explicit message recovery. So, why do
98	 * we do sequence tracking? Just to let know the sysadmin.
99	 *
100	 * Let t be 1 < t < RefreshTime. To ensure consistency, conntrackd
101	 * retransmit every t seconds a message with the state of a certain
102	 * entry even if such entry did not change. This mechanism also
103	 * provides passive resynchronization, in other words, there is
104	 * no facility to request a full synchronization from new nodes that
105	 * just joined the cluster, instead they just get resynchronized in
106	 * RefreshTime seconds at worst case.
107	 */
108	nethdr_track_seq(net->seq, &exp_seq);
109
110	return 0;
111}
112
113static void alarm_enqueue(struct cache_object *obj, int query)
114{
115	struct cache_alarm *ca = cache_get_extra(obj);
116	if (queue_add(STATE_SYNC(tx_queue), &ca->qnode) > 0)
117		cache_object_get(obj);
118}
119
120static int tx_queue_xmit(struct queue_node *n, const void *data)
121{
122	struct nethdr *net;
123
124	queue_del(n);
125
126	switch(n->type) {
127	case Q_ELEM_CTL:
128		net = queue_node_data(n);
129		nethdr_set_ctl(net);
130		HDR_HOST2NETWORK(net);
131		multichannel_send(STATE_SYNC(channel), net);
132		queue_object_free((struct queue_object *)n);
133		break;
134	case Q_ELEM_OBJ: {
135		struct cache_alarm *ca;
136		int type;
137
138		ca = (struct cache_alarm *)n;
139		type = object_status_to_network_type(ca->obj);
140		net = ca->obj->cache->ops->build_msg(ca->obj, type);
141		multichannel_send(STATE_SYNC(channel), net);
142		cache_object_put(ca->obj);
143		break;
144	}
145	}
146	return 0;
147}
148
149static void alarm_xmit(void)
150{
151	queue_iterate(STATE_SYNC(tx_queue), NULL, tx_queue_xmit);
152}
153
154struct sync_mode sync_alarm = {
155	.internal_cache_flags	= NO_FEATURES,
156	.external_cache_flags	= TIMER,
157	.internal_cache_extra	= &cache_alarm_extra,
158	.recv 			= alarm_recv,
159	.enqueue		= alarm_enqueue,
160	.xmit			= alarm_xmit,
161};
162