1/*
2 * (C) 2005-2012 by Pablo Neira Ayuso <pablo@netfilter.org>
3 * (C) 2012 by Intra2net AG <http://www.intra2net.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 * Note:
20 *	Yes, portions of this code has been stolen from iptables ;)
21 *	Special thanks to the the Netfilter Core Team.
22 *	Thanks to Javier de Miguel Rodriguez <jmiguel at talika.eii.us.es>
23 *	for introducing me to advanced firewalling stuff.
24 *
25 *						--pablo 13/04/2005
26 *
27 * 2005-04-16 Harald Welte <laforge@netfilter.org>:
28 * 	Add support for conntrack accounting and conntrack mark
29 * 2005-06-23 Harald Welte <laforge@netfilter.org>:
30 * 	Add support for expect creation
31 * 2005-09-24 Harald Welte <laforge@netfilter.org>:
32 * 	Remove remaints of "-A"
33 * 2007-04-22 Pablo Neira Ayuso <pablo@netfilter.org>:
34 * 	Ported to the new libnetfilter_conntrack API
35 * 2008-04-13 Pablo Neira Ayuso <pablo@netfilter.org>:
36 *	Way more flexible update and delete operations
37 *
38 * Part of this code has been funded by Sophos Astaro <http://www.sophos.com>
39 */
40
41#include "conntrack.h"
42
43#include <stdio.h>
44#include <getopt.h>
45#include <stdlib.h>
46#include <stdarg.h>
47#include <errno.h>
48#include <unistd.h>
49#include <netinet/in.h>
50#include <sys/types.h>
51#include <sys/socket.h>
52#include <sys/time.h>
53#include <time.h>
54#ifdef HAVE_ARPA_INET_H
55#include <arpa/inet.h>
56#endif
57#include <signal.h>
58#include <string.h>
59#include <netdb.h>
60#include <sys/stat.h>
61#include <fcntl.h>
62#include <libmnl/libmnl.h>
63#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
64
65struct u32_mask {
66	uint32_t value;
67	uint32_t mask;
68};
69
70/* These are the template objects that are used to send commands. */
71static struct {
72	struct nf_conntrack *ct;
73	struct nf_expect *exp;
74	/* Expectations require the expectation tuple and the mask. */
75	struct nf_conntrack *exptuple, *mask;
76
77	/* Allows filtering/setting specific bits in the ctmark */
78	struct u32_mask mark;
79
80	/* Allow to filter by mark from kernel-space. */
81	struct nfct_filter_dump_mark filter_mark_kernel;
82} tmpl;
83
84static int alloc_tmpl_objects(void)
85{
86	tmpl.ct = nfct_new();
87	tmpl.exptuple = nfct_new();
88	tmpl.mask = nfct_new();
89	tmpl.exp = nfexp_new();
90
91	memset(&tmpl.mark, 0, sizeof(tmpl.mark));
92
93	return tmpl.ct != NULL && tmpl.exptuple != NULL &&
94	       tmpl.mask != NULL && tmpl.exp != NULL;
95}
96
97static void free_tmpl_objects(void)
98{
99	if (tmpl.ct)
100		nfct_destroy(tmpl.ct);
101	if (tmpl.exptuple)
102		nfct_destroy(tmpl.exptuple);
103	if (tmpl.mask)
104		nfct_destroy(tmpl.mask);
105	if (tmpl.exp)
106		nfexp_destroy(tmpl.exp);
107}
108
109enum ct_command {
110	CT_NONE		= 0,
111
112	CT_LIST_BIT 	= 0,
113	CT_LIST 	= (1 << CT_LIST_BIT),
114
115	CT_CREATE_BIT	= 1,
116	CT_CREATE	= (1 << CT_CREATE_BIT),
117
118	CT_UPDATE_BIT	= 2,
119	CT_UPDATE	= (1 << CT_UPDATE_BIT),
120
121	CT_DELETE_BIT	= 3,
122	CT_DELETE	= (1 << CT_DELETE_BIT),
123
124	CT_GET_BIT	= 4,
125	CT_GET		= (1 << CT_GET_BIT),
126
127	CT_FLUSH_BIT	= 5,
128	CT_FLUSH	= (1 << CT_FLUSH_BIT),
129
130	CT_EVENT_BIT	= 6,
131	CT_EVENT	= (1 << CT_EVENT_BIT),
132
133	CT_VERSION_BIT	= 7,
134	CT_VERSION	= (1 << CT_VERSION_BIT),
135
136	CT_HELP_BIT	= 8,
137	CT_HELP		= (1 << CT_HELP_BIT),
138
139	EXP_LIST_BIT 	= 9,
140	EXP_LIST 	= (1 << EXP_LIST_BIT),
141
142	EXP_CREATE_BIT	= 10,
143	EXP_CREATE	= (1 << EXP_CREATE_BIT),
144
145	EXP_DELETE_BIT	= 11,
146	EXP_DELETE	= (1 << EXP_DELETE_BIT),
147
148	EXP_GET_BIT	= 12,
149	EXP_GET		= (1 << EXP_GET_BIT),
150
151	EXP_FLUSH_BIT	= 13,
152	EXP_FLUSH	= (1 << EXP_FLUSH_BIT),
153
154	EXP_EVENT_BIT	= 14,
155	EXP_EVENT	= (1 << EXP_EVENT_BIT),
156
157	CT_COUNT_BIT	= 15,
158	CT_COUNT	= (1 << CT_COUNT_BIT),
159
160	EXP_COUNT_BIT	= 16,
161	EXP_COUNT	= (1 << EXP_COUNT_BIT),
162
163	CT_STATS_BIT	= 17,
164	CT_STATS	= (1 << CT_STATS_BIT),
165
166	EXP_STATS_BIT	= 18,
167	EXP_STATS	= (1 << EXP_STATS_BIT),
168};
169/* If you add a new command, you have to update NUMBER_OF_CMD in conntrack.h */
170
171enum ct_options {
172	CT_OPT_ORIG_SRC_BIT	= 0,
173	CT_OPT_ORIG_SRC 	= (1 << CT_OPT_ORIG_SRC_BIT),
174
175	CT_OPT_ORIG_DST_BIT	= 1,
176	CT_OPT_ORIG_DST		= (1 << CT_OPT_ORIG_DST_BIT),
177
178	CT_OPT_ORIG		= (CT_OPT_ORIG_SRC | CT_OPT_ORIG_DST),
179
180	CT_OPT_REPL_SRC_BIT	= 2,
181	CT_OPT_REPL_SRC		= (1 << CT_OPT_REPL_SRC_BIT),
182
183	CT_OPT_REPL_DST_BIT	= 3,
184	CT_OPT_REPL_DST		= (1 << CT_OPT_REPL_DST_BIT),
185
186	CT_OPT_REPL		= (CT_OPT_REPL_SRC | CT_OPT_REPL_DST),
187
188	CT_OPT_PROTO_BIT	= 4,
189	CT_OPT_PROTO		= (1 << CT_OPT_PROTO_BIT),
190
191	CT_OPT_TUPLE_ORIG	= (CT_OPT_ORIG | CT_OPT_PROTO),
192	CT_OPT_TUPLE_REPL	= (CT_OPT_REPL | CT_OPT_PROTO),
193
194	CT_OPT_TIMEOUT_BIT	= 5,
195	CT_OPT_TIMEOUT		= (1 << CT_OPT_TIMEOUT_BIT),
196
197	CT_OPT_STATUS_BIT	= 6,
198	CT_OPT_STATUS		= (1 << CT_OPT_STATUS_BIT),
199
200	CT_OPT_ZERO_BIT		= 7,
201	CT_OPT_ZERO		= (1 << CT_OPT_ZERO_BIT),
202
203	CT_OPT_EVENT_MASK_BIT	= 8,
204	CT_OPT_EVENT_MASK	= (1 << CT_OPT_EVENT_MASK_BIT),
205
206	CT_OPT_EXP_SRC_BIT	= 9,
207	CT_OPT_EXP_SRC		= (1 << CT_OPT_EXP_SRC_BIT),
208
209	CT_OPT_EXP_DST_BIT	= 10,
210	CT_OPT_EXP_DST		= (1 << CT_OPT_EXP_DST_BIT),
211
212	CT_OPT_MASK_SRC_BIT	= 11,
213	CT_OPT_MASK_SRC		= (1 << CT_OPT_MASK_SRC_BIT),
214
215	CT_OPT_MASK_DST_BIT	= 12,
216	CT_OPT_MASK_DST		= (1 << CT_OPT_MASK_DST_BIT),
217
218	CT_OPT_NATRANGE_BIT	= 13,
219	CT_OPT_NATRANGE		= (1 << CT_OPT_NATRANGE_BIT),
220
221	CT_OPT_MARK_BIT		= 14,
222	CT_OPT_MARK		= (1 << CT_OPT_MARK_BIT),
223
224	CT_OPT_ID_BIT		= 15,
225	CT_OPT_ID		= (1 << CT_OPT_ID_BIT),
226
227	CT_OPT_FAMILY_BIT	= 16,
228	CT_OPT_FAMILY		= (1 << CT_OPT_FAMILY_BIT),
229
230	CT_OPT_SRC_NAT_BIT	= 17,
231	CT_OPT_SRC_NAT		= (1 << CT_OPT_SRC_NAT_BIT),
232
233	CT_OPT_DST_NAT_BIT	= 18,
234	CT_OPT_DST_NAT		= (1 << CT_OPT_DST_NAT_BIT),
235
236	CT_OPT_OUTPUT_BIT	= 19,
237	CT_OPT_OUTPUT		= (1 << CT_OPT_OUTPUT_BIT),
238
239	CT_OPT_SECMARK_BIT	= 20,
240	CT_OPT_SECMARK		= (1 << CT_OPT_SECMARK_BIT),
241
242	CT_OPT_BUFFERSIZE_BIT	= 21,
243	CT_OPT_BUFFERSIZE	= (1 << CT_OPT_BUFFERSIZE_BIT),
244
245	CT_OPT_ANY_NAT_BIT	= 22,
246	CT_OPT_ANY_NAT		= (1 << CT_OPT_ANY_NAT_BIT),
247
248	CT_OPT_ZONE_BIT		= 23,
249	CT_OPT_ZONE		= (1 << CT_OPT_ZONE_BIT),
250};
251/* If you add a new option, you have to update NUMBER_OF_OPT in conntrack.h */
252
253/* Update this mask to allow to filter based on new options. */
254#define CT_COMPARISON (CT_OPT_PROTO | CT_OPT_ORIG | CT_OPT_REPL | \
255		       CT_OPT_MARK | CT_OPT_SECMARK |  CT_OPT_STATUS | \
256		       CT_OPT_ID | CT_OPT_ZONE)
257
258static const char *optflags[NUMBER_OF_OPT] = {
259	[CT_OPT_ORIG_SRC_BIT] 	= "src",
260	[CT_OPT_ORIG_DST_BIT]	= "dst",
261	[CT_OPT_REPL_SRC_BIT]	= "reply-src",
262	[CT_OPT_REPL_DST_BIT]	= "reply-dst",
263	[CT_OPT_PROTO_BIT]	= "protonum",
264	[CT_OPT_TIMEOUT_BIT]	= "timeout",
265	[CT_OPT_STATUS_BIT]	= "status",
266	[CT_OPT_ZERO_BIT]	= "zero",
267	[CT_OPT_EVENT_MASK_BIT]	= "event-mask",
268	[CT_OPT_EXP_SRC_BIT]	= "tuple-src",
269	[CT_OPT_EXP_DST_BIT]	= "tuple-dst",
270	[CT_OPT_MASK_SRC_BIT]	= "mask-src",
271	[CT_OPT_MASK_DST_BIT]	= "mask-dst",
272	[CT_OPT_NATRANGE_BIT]	= "nat-range",
273	[CT_OPT_MARK_BIT]	= "mark",
274	[CT_OPT_ID_BIT]		= "id",
275	[CT_OPT_FAMILY_BIT]	= "family",
276	[CT_OPT_SRC_NAT_BIT]	= "src-nat",
277	[CT_OPT_DST_NAT_BIT]	= "dst-nat",
278	[CT_OPT_OUTPUT_BIT]	= "output",
279	[CT_OPT_SECMARK_BIT]	= "secmark",
280	[CT_OPT_BUFFERSIZE_BIT]	= "buffer-size",
281	[CT_OPT_ANY_NAT_BIT]	= "any-nat",
282	[CT_OPT_ZONE_BIT]	= "zone",
283};
284
285static struct option original_opts[] = {
286	{"dump", 2, 0, 'L'},
287	{"create", 2, 0, 'I'},
288	{"delete", 2, 0, 'D'},
289	{"update", 2, 0, 'U'},
290	{"get", 2, 0, 'G'},
291	{"flush", 2, 0, 'F'},
292	{"event", 2, 0, 'E'},
293	{"counter", 2, 0, 'C'},
294	{"stats", 0, 0, 'S'},
295	{"version", 0, 0, 'V'},
296	{"help", 0, 0, 'h'},
297	{"orig-src", 1, 0, 's'},
298	{"src", 1, 0, 's'},
299	{"orig-dst", 1, 0, 'd'},
300	{"dst", 1, 0, 'd'},
301	{"reply-src", 1, 0, 'r'},
302	{"reply-dst", 1, 0, 'q'},
303	{"protonum", 1, 0, 'p'},
304	{"timeout", 1, 0, 't'},
305	{"status", 1, 0, 'u'},
306	{"zero", 0, 0, 'z'},
307	{"event-mask", 1, 0, 'e'},
308	{"tuple-src", 1, 0, '['},
309	{"tuple-dst", 1, 0, ']'},
310	{"mask-src", 1, 0, '{'},
311	{"mask-dst", 1, 0, '}'},
312	{"nat-range", 1, 0, 'a'},	/* deprecated */
313	{"mark", 1, 0, 'm'},
314	{"secmark", 1, 0, 'c'},
315	{"id", 2, 0, 'i'},		/* deprecated */
316	{"family", 1, 0, 'f'},
317	{"src-nat", 2, 0, 'n'},
318	{"dst-nat", 2, 0, 'g'},
319	{"output", 1, 0, 'o'},
320	{"buffer-size", 1, 0, 'b'},
321	{"any-nat", 2, 0, 'j'},
322	{"zone", 1, 0, 'w'},
323	{0, 0, 0, 0}
324};
325
326static const char *getopt_str = "L::I::U::D::G::E::F::hVs:d:r:q:"
327				"p:t:u:e:a:z[:]:{:}:m:i:f:o:n::"
328				"g::c:b:C::Sj::w:";
329
330/* Table of legal combinations of commands and options.  If any of the
331 * given commands make an option legal, that option is legal (applies to
332 * CMD_LIST and CMD_ZERO only).
333 * Key:
334 *  0  illegal
335 *  1  compulsory
336 *  2  optional
337 *  3  undecided, see flag combination checkings in generic_opt_check()
338 */
339
340static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
341/* Well, it's better than "Re: Linux vs FreeBSD" */
342{
343          /*   s d r q p t u z e [ ] { } a m i f n g o c b j w*/
344/*CT_LIST*/   {2,2,2,2,2,0,2,2,0,0,0,0,0,0,2,0,2,2,2,2,2,0,2,2},
345/*CT_CREATE*/ {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,2},
346/*CT_UPDATE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0},
347/*CT_DELETE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,2},
348/*CT_GET*/    {3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0},
349/*CT_FLUSH*/  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
350/*CT_EVENT*/  {2,2,2,2,2,0,0,0,2,0,0,0,0,0,2,0,0,2,2,2,2,2,2,2},
351/*VERSION*/   {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
352/*HELP*/      {0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
353/*EXP_LIST*/  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0},
354/*EXP_CREATE*/{1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0},
355/*EXP_DELETE*/{1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
356/*EXP_GET*/   {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
357/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
358/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0},
359/*CT_COUNT*/  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
360/*EXP_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
361/*CT_STATS*/  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
362/*EXP_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
363};
364
365static const int cmd2type[][2] = {
366	['L']	= { CT_LIST, 	EXP_LIST },
367	['I']	= { CT_CREATE,	EXP_CREATE },
368	['D']	= { CT_DELETE,	EXP_DELETE },
369	['G']	= { CT_GET,	EXP_GET },
370	['F']	= { CT_FLUSH,	EXP_FLUSH },
371	['E']	= { CT_EVENT,	EXP_EVENT },
372	['V']	= { CT_VERSION,	CT_VERSION },
373	['h']	= { CT_HELP,	CT_HELP },
374	['C']	= { CT_COUNT,	EXP_COUNT },
375	['S']	= { CT_STATS,	EXP_STATS },
376};
377
378static const int opt2type[] = {
379	['s']	= CT_OPT_ORIG_SRC,
380	['d']	= CT_OPT_ORIG_DST,
381	['r']	= CT_OPT_REPL_SRC,
382	['q']	= CT_OPT_REPL_DST,
383	['{']	= CT_OPT_MASK_SRC,
384	['}']	= CT_OPT_MASK_DST,
385	['[']	= CT_OPT_EXP_SRC,
386	[']']	= CT_OPT_EXP_DST,
387	['n']	= CT_OPT_SRC_NAT,
388	['g']	= CT_OPT_DST_NAT,
389	['m']	= CT_OPT_MARK,
390	['c']	= CT_OPT_SECMARK,
391	['i']	= CT_OPT_ID,
392	['j']	= CT_OPT_ANY_NAT,
393	['w']	= CT_OPT_ZONE,
394};
395
396static const int opt2family_attr[][2] = {
397	['s']	= { ATTR_ORIG_IPV4_SRC,	ATTR_ORIG_IPV6_SRC },
398	['d']	= { ATTR_ORIG_IPV4_DST,	ATTR_ORIG_IPV6_DST },
399	['r']	= { ATTR_REPL_IPV4_SRC, ATTR_REPL_IPV6_SRC },
400	['q']	= { ATTR_REPL_IPV4_DST, ATTR_REPL_IPV6_DST },
401	['{']	= { ATTR_ORIG_IPV4_SRC,	ATTR_ORIG_IPV6_SRC },
402	['}']	= { ATTR_ORIG_IPV4_DST,	ATTR_ORIG_IPV6_DST },
403	['[']	= { ATTR_ORIG_IPV4_SRC, ATTR_ORIG_IPV6_SRC },
404	[']']	= { ATTR_ORIG_IPV4_DST, ATTR_ORIG_IPV6_DST },
405};
406
407static const int opt2attr[] = {
408	['s']	= ATTR_ORIG_L3PROTO,
409	['d']	= ATTR_ORIG_L3PROTO,
410	['r']	= ATTR_REPL_L3PROTO,
411	['q']	= ATTR_REPL_L3PROTO,
412	['m']	= ATTR_MARK,
413	['c']	= ATTR_SECMARK,
414	['i']	= ATTR_ID,
415	['w']	= ATTR_ZONE,
416};
417
418static char exit_msg[NUMBER_OF_CMD][64] = {
419	[CT_LIST_BIT] 		= "%d flow entries have been shown.\n",
420	[CT_CREATE_BIT]		= "%d flow entries have been created.\n",
421	[CT_UPDATE_BIT]		= "%d flow entries have been updated.\n",
422	[CT_DELETE_BIT]		= "%d flow entries have been deleted.\n",
423	[CT_GET_BIT] 		= "%d flow entries have been shown.\n",
424	[CT_EVENT_BIT]		= "%d flow events have been shown.\n",
425	[EXP_LIST_BIT]		= "%d expectations have been shown.\n",
426	[EXP_DELETE_BIT]	= "%d expectations have been shown.\n",
427};
428
429static const char usage_commands[] =
430	"Commands:\n"
431	"  -L [table] [options]\t\tList conntrack or expectation table\n"
432	"  -G [table] parameters\t\tGet conntrack or expectation\n"
433	"  -D [table] parameters\t\tDelete conntrack or expectation\n"
434	"  -I [table] parameters\t\tCreate a conntrack or expectation\n"
435	"  -U [table] parameters\t\tUpdate a conntrack\n"
436	"  -E [table] [options]\t\tShow events\n"
437	"  -F [table]\t\t\tFlush table\n"
438	"  -C [table]\t\t\tShow counter\n"
439	"  -S\t\t\t\tShow statistics\n";
440
441static const char usage_tables[] =
442	"Tables: conntrack, expect\n";
443
444static const char usage_conntrack_parameters[] =
445	"Conntrack parameters and options:\n"
446	"  -n, --src-nat ip\t\t\tsource NAT ip\n"
447	"  -g, --dst-nat ip\t\t\tdestination NAT ip\n"
448	"  -j, --any-nat ip\t\t\tsource or destination NAT ip\n"
449	"  -m, --mark mark\t\t\tSet mark\n"
450	"  -c, --secmark secmark\t\t\tSet selinux secmark\n"
451	"  -e, --event-mask eventmask\t\tEvent mask, eg. NEW,DESTROY\n"
452	"  -z, --zero \t\t\t\tZero counters while listing\n"
453	"  -o, --output type[,...]\t\tOutput format, eg. xml\n";
454
455static const char usage_expectation_parameters[] =
456	"Expectation parameters and options:\n"
457	"  --tuple-src ip\tSource address in expect tuple\n"
458	"  --tuple-dst ip\tDestination address in expect tuple\n"
459	"  --mask-src ip\t\tSource mask address\n"
460	"  --mask-dst ip\t\tDestination mask address\n";
461
462static const char usage_parameters[] =
463	"Common parameters and options:\n"
464	"  -s, --orig-src ip\t\tSource address from original direction\n"
465	"  -d, --orig-dst ip\t\tDestination address from original direction\n"
466	"  -r, --reply-src ip\t\tSource addres from reply direction\n"
467	"  -q, --reply-dst ip\t\tDestination address from reply direction\n"
468	"  -p, --protonum proto\t\tLayer 4 Protocol, eg. 'tcp'\n"
469	"  -f, --family proto\t\tLayer 3 Protocol, eg. 'ipv6'\n"
470	"  -t, --timeout timeout\t\tSet timeout\n"
471	"  -u, --status status\t\tSet status, eg. ASSURED\n"
472	"  -w, --zone value\t\tSet conntrack zone\n"
473	"  -b, --buffer-size\t\tNetlink socket buffer size\n"
474	;
475
476#define OPTION_OFFSET 256
477
478static struct nfct_handle *cth, *ith;
479static struct option *opts = original_opts;
480static unsigned int global_option_offset = 0;
481
482#define ADDR_VALID_FLAGS_MAX   2
483static unsigned int addr_valid_flags[ADDR_VALID_FLAGS_MAX] = {
484	CT_OPT_ORIG_SRC | CT_OPT_ORIG_DST,
485	CT_OPT_REPL_SRC | CT_OPT_REPL_DST,
486};
487
488static LIST_HEAD(proto_list);
489
490static unsigned int options;
491
492void register_proto(struct ctproto_handler *h)
493{
494	if (strcmp(h->version, VERSION) != 0) {
495		fprintf(stderr, "plugin `%s': version %s (I'm %s)\n",
496			h->name, h->version, VERSION);
497		exit(1);
498	}
499	list_add(&h->head, &proto_list);
500}
501
502extern struct ctproto_handler ct_proto_unknown;
503
504static struct ctproto_handler *findproto(char *name, int *pnum)
505{
506	struct ctproto_handler *cur;
507	struct protoent *pent;
508	int protonum;
509
510	/* is it in the list of supported protocol? */
511	list_for_each_entry(cur, &proto_list, head) {
512		if (strcmp(cur->name, name) == 0) {
513			*pnum = cur->protonum;
514			return cur;
515		}
516	}
517	/* using the protocol name for an unsupported protocol? */
518	if ((pent = getprotobyname(name))) {
519		*pnum = pent->p_proto;
520		return &ct_proto_unknown;
521	}
522	/* using a protocol number? */
523	protonum = atoi(name);
524	if (protonum > 0 && protonum <= IPPROTO_MAX) {
525		/* try lookup by number, perhaps this protocol is supported */
526		list_for_each_entry(cur, &proto_list, head) {
527			if (cur->protonum == protonum) {
528				*pnum = protonum;
529				return cur;
530			}
531		}
532		*pnum = protonum;
533		return &ct_proto_unknown;
534	}
535
536	return NULL;
537}
538
539static void
540extension_help(struct ctproto_handler *h, int protonum)
541{
542	const char *name;
543
544	if (h == &ct_proto_unknown) {
545		struct protoent *pent;
546
547		pent = getprotobynumber(protonum);
548		if (!pent)
549			name = h->name;
550		else
551			name = pent->p_name;
552	} else {
553		name = h->name;
554	}
555
556	fprintf(stdout, "Proto `%s' help:\n", name);
557	h->help();
558}
559
560static void __attribute__((noreturn))
561exit_tryhelp(int status)
562{
563	fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
564			PROGNAME, PROGNAME);
565	exit(status);
566}
567
568static void free_options(void)
569{
570	if (opts != original_opts) {
571		free(opts);
572		opts = original_opts;
573		global_option_offset = 0;
574	}
575}
576
577void __attribute__((noreturn))
578exit_error(enum exittype status, const char *msg, ...)
579{
580	va_list args;
581
582	free_options();
583	va_start(args, msg);
584	fprintf(stderr,"%s v%s (conntrack-tools): ", PROGNAME, VERSION);
585	vfprintf(stderr, msg, args);
586	fprintf(stderr, "\n");
587	va_end(args);
588	if (status == PARAMETER_PROBLEM)
589		exit_tryhelp(status);
590	/* release template objects that were allocated in the setup stage. */
591	free_tmpl_objects();
592	exit(status);
593}
594
595static int bit2cmd(int command)
596{
597	int i;
598
599	for (i = 0; i < NUMBER_OF_CMD; i++)
600		if (command & (1<<i))
601			break;
602
603	return i;
604}
605
606int generic_opt_check(int local_options, int num_opts,
607		      char *optset, const char *optflg[],
608		      unsigned int *coupled_flags, int coupled_flags_size,
609		      int *partial)
610{
611	int i, matching = -1, special_case = 0;
612
613	for (i = 0; i < num_opts; i++) {
614		if (!(local_options & (1<<i))) {
615			if (optset[i] == 1)
616				exit_error(PARAMETER_PROBLEM,
617					   "You need to supply the "
618					   "`--%s' option for this "
619					   "command", optflg[i]);
620		} else {
621			if (optset[i] == 0)
622				exit_error(PARAMETER_PROBLEM, "Illegal "
623					   "option `--%s' with this "
624					   "command", optflg[i]);
625		}
626		if (optset[i] == 3)
627			special_case = 1;
628	}
629
630	/* no weird flags combinations, leave */
631	if (!special_case || coupled_flags == NULL)
632		return 1;
633
634	*partial = -1;
635	for (i=0; i<coupled_flags_size; i++) {
636		/* we look for an exact matching to ensure this is correct */
637		if ((local_options & coupled_flags[i]) == coupled_flags[i]) {
638			matching = i;
639			break;
640		}
641		/* ... otherwise look for the first partial matching */
642		if ((local_options & coupled_flags[i]) && *partial < 0) {
643			*partial = i;
644		}
645	}
646
647	/* we found an exact matching, game over */
648	if (matching >= 0)
649		return 1;
650
651	/* report a partial matching to suggest something */
652	return 0;
653}
654
655static struct option *
656merge_options(struct option *oldopts, const struct option *newopts,
657	      unsigned int *option_offset)
658{
659	unsigned int num_old, num_new, i;
660	struct option *merge;
661
662	for (num_old = 0; oldopts[num_old].name; num_old++);
663	for (num_new = 0; newopts[num_new].name; num_new++);
664
665	global_option_offset += OPTION_OFFSET;
666	*option_offset = global_option_offset;
667
668	merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
669	if (merge == NULL)
670		return NULL;
671
672	memcpy(merge, oldopts, num_old * sizeof(struct option));
673	for (i = 0; i < num_new; i++) {
674		merge[num_old + i] = newopts[i];
675		merge[num_old + i].val += *option_offset;
676	}
677	memset(merge + num_old + num_new, 0, sizeof(struct option));
678
679	return merge;
680}
681
682/* From linux/errno.h */
683#define ENOTSUPP        524     /* Operation is not supported */
684
685/* Translates errno numbers into more human-readable form than strerror. */
686static const char *
687err2str(int err, enum ct_command command)
688{
689	unsigned int i;
690	struct table_struct {
691		enum ct_command act;
692		int err;
693		const char *message;
694	} table [] =
695	  { { CT_LIST, ENOTSUPP, "function not implemented" },
696	    { 0xFFFF, EINVAL, "invalid parameters" },
697	    { CT_CREATE, EEXIST, "Such conntrack exists, try -U to update" },
698	    { CT_CREATE|CT_GET|CT_DELETE, ENOENT,
699		    "such conntrack doesn't exist" },
700	    { CT_CREATE|CT_GET, ENOMEM, "not enough memory" },
701	    { CT_GET, EAFNOSUPPORT, "protocol not supported" },
702	    { CT_CREATE, ETIME, "conntrack has expired" },
703	    { EXP_CREATE, ENOENT, "master conntrack not found" },
704	    { EXP_CREATE, EINVAL, "invalid parameters" },
705	    { ~0U, EPERM, "sorry, you must be root or get "
706		    	   "CAP_NET_ADMIN capability to do this"}
707	  };
708
709	for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
710		if ((table[i].act & command) && table[i].err == err)
711			return table[i].message;
712	}
713
714	return strerror(err);
715}
716
717static int mark_cmp(const struct u32_mask *m, const struct nf_conntrack *ct)
718{
719	return nfct_attr_is_set(ct, ATTR_MARK) &&
720		(nfct_get_attr_u32(ct, ATTR_MARK) & m->mask) == m->value;
721}
722
723#define PARSE_STATUS 0
724#define PARSE_EVENT 1
725#define PARSE_OUTPUT 2
726#define PARSE_MAX 3
727
728enum {
729	_O_XML	= (1 << 0),
730	_O_EXT	= (1 << 1),
731	_O_TMS	= (1 << 2),
732	_O_ID	= (1 << 3),
733	_O_KTMS	= (1 << 4),
734};
735
736enum {
737	CT_EVENT_F_NEW	= (1 << 0),
738	CT_EVENT_F_UPD	= (1 << 1),
739	CT_EVENT_F_DEL 	= (1 << 2),
740	CT_EVENT_F_ALL	= CT_EVENT_F_NEW | CT_EVENT_F_UPD | CT_EVENT_F_DEL,
741};
742
743static struct parse_parameter {
744	const char	*parameter[6];
745	size_t  size;
746	unsigned int value[6];
747} parse_array[PARSE_MAX] = {
748	{ {"ASSURED", "SEEN_REPLY", "UNSET", "FIXED_TIMEOUT", "EXPECTED"}, 5,
749	  { IPS_ASSURED, IPS_SEEN_REPLY, 0, IPS_FIXED_TIMEOUT, IPS_EXPECTED} },
750	{ {"ALL", "NEW", "UPDATES", "DESTROY"}, 4,
751	  { CT_EVENT_F_ALL, CT_EVENT_F_NEW, CT_EVENT_F_UPD, CT_EVENT_F_DEL } },
752	{ {"xml", "extended", "timestamp", "id", "ktimestamp"}, 5,
753	  { _O_XML, _O_EXT, _O_TMS, _O_ID, _O_KTMS },
754	},
755};
756
757static int
758do_parse_parameter(const char *str, size_t str_length, unsigned int *value,
759		   int parse_type)
760{
761	size_t i;
762	int ret = 0;
763	struct parse_parameter *p = &parse_array[parse_type];
764
765	if (strncasecmp(str, "SRC_NAT", str_length) == 0) {
766		fprintf(stderr, "WARNING: ignoring SRC_NAT, "
767				"use --src-nat instead\n");
768		return 1;
769	}
770
771	if (strncasecmp(str, "DST_NAT", str_length) == 0) {
772		fprintf(stderr, "WARNING: ignoring DST_NAT, "
773				"use --dst-nat instead\n");
774		return 1;
775	}
776
777	for (i = 0; i < p->size; i++)
778		if (strncasecmp(str, p->parameter[i], str_length) == 0) {
779			*value |= p->value[i];
780			ret = 1;
781			break;
782		}
783
784	return ret;
785}
786
787static void
788parse_parameter(const char *arg, unsigned int *status, int parse_type)
789{
790	const char *comma;
791
792	while ((comma = strchr(arg, ',')) != NULL) {
793		if (comma == arg
794		    || !do_parse_parameter(arg, comma-arg, status, parse_type))
795			exit_error(PARAMETER_PROBLEM,"Bad parameter `%s'", arg);
796		arg = comma+1;
797	}
798
799	if (strlen(arg) == 0
800	    || !do_parse_parameter(arg, strlen(arg), status, parse_type))
801		exit_error(PARAMETER_PROBLEM, "Bad parameter `%s'", arg);
802}
803
804static void
805parse_u32_mask(const char *arg, struct u32_mask *m)
806{
807	char *end;
808
809	m->value = (uint32_t) strtoul(arg, &end, 0);
810
811	if (*end == '/')
812		m->mask = (uint32_t) strtoul(end+1, NULL, 0);
813	else
814		m->mask = ~0;
815}
816
817static void
818add_command(unsigned int *cmd, const int newcmd)
819{
820	if (*cmd)
821		exit_error(PARAMETER_PROBLEM, "Invalid commands combination");
822	*cmd |= newcmd;
823}
824
825static unsigned int
826check_type(int argc, char *argv[])
827{
828	char *table = NULL;
829
830	/* Nasty bug or feature in getopt_long ?
831	 * It seems that it behaves badly with optional arguments.
832	 * Fortunately, I just stole the fix from iptables ;) */
833	if (optarg)
834		return 0;
835	else if (optind < argc && argv[optind][0] != '-'
836			&& argv[optind][0] != '!')
837		table = argv[optind++];
838
839	if (!table)
840		return 0;
841
842	if (strncmp("expect", table, strlen(table)) == 0)
843		return 1;
844	else if (strncmp("conntrack", table, strlen(table)) == 0)
845		return 0;
846	else
847		exit_error(PARAMETER_PROBLEM, "unknown type `%s'", table);
848
849	return 0;
850}
851
852static void set_family(int *family, int new)
853{
854	if (*family == AF_UNSPEC)
855		*family = new;
856	else if (*family != new)
857		exit_error(PARAMETER_PROBLEM, "mismatched address family");
858}
859
860struct addr_parse {
861	struct in_addr addr;
862	struct in6_addr addr6;
863	unsigned int family;
864};
865
866static int
867parse_inetaddr(const char *cp, struct addr_parse *parse)
868{
869	if (inet_aton(cp, &parse->addr))
870		return AF_INET;
871#ifdef HAVE_INET_PTON_IPV6
872	else if (inet_pton(AF_INET6, cp, &parse->addr6) > 0)
873		return AF_INET6;
874#endif
875	return AF_UNSPEC;
876}
877
878union ct_address {
879	uint32_t v4;
880	uint32_t v6[4];
881};
882
883static int
884parse_addr(const char *cp, union ct_address *address)
885{
886	struct addr_parse parse;
887	int ret;
888
889	if ((ret = parse_inetaddr(cp, &parse)) == AF_INET)
890		address->v4 = parse.addr.s_addr;
891	else if (ret == AF_INET6)
892		memcpy(address->v6, &parse.addr6, sizeof(parse.addr6));
893
894	return ret;
895}
896
897static void
898nat_parse(char *arg, struct nf_conntrack *obj, int type)
899{
900	char *colon, *error;
901	union ct_address parse;
902
903	colon = strchr(arg, ':');
904
905	if (colon) {
906		uint16_t port;
907
908		*colon = '\0';
909
910		port = (uint16_t)atoi(colon+1);
911		if (port == 0) {
912			if (strlen(colon+1) == 0) {
913				exit_error(PARAMETER_PROBLEM,
914					   "No port specified after `:'");
915			} else {
916				exit_error(PARAMETER_PROBLEM,
917					   "Port `%s' not valid", colon+1);
918			}
919		}
920
921		error = strchr(colon+1, ':');
922		if (error)
923			exit_error(PARAMETER_PROBLEM,
924				   "Invalid port:port syntax");
925
926		if (type == CT_OPT_SRC_NAT)
927			nfct_set_attr_u16(tmpl.ct, ATTR_SNAT_PORT, ntohs(port));
928		else if (type == CT_OPT_DST_NAT)
929			nfct_set_attr_u16(tmpl.ct, ATTR_DNAT_PORT, ntohs(port));
930		else if (type == CT_OPT_ANY_NAT) {
931			nfct_set_attr_u16(tmpl.ct, ATTR_SNAT_PORT, ntohs(port));
932			nfct_set_attr_u16(tmpl.ct, ATTR_DNAT_PORT, ntohs(port));
933		}
934	}
935
936	if (parse_addr(arg, &parse) == AF_UNSPEC) {
937		if (strlen(arg) == 0) {
938			exit_error(PARAMETER_PROBLEM, "No IP specified");
939		} else {
940			exit_error(PARAMETER_PROBLEM,
941					"Invalid IP address `%s'", arg);
942		}
943	}
944
945	if (type == CT_OPT_SRC_NAT || type == CT_OPT_ANY_NAT)
946		nfct_set_attr_u32(tmpl.ct, ATTR_SNAT_IPV4, parse.v4);
947	else if (type == CT_OPT_DST_NAT || type == CT_OPT_ANY_NAT)
948		nfct_set_attr_u32(tmpl.ct, ATTR_DNAT_IPV4, parse.v4);
949}
950
951static void
952usage(char *prog)
953{
954	fprintf(stdout, "Command line interface for the connection "
955			"tracking system. Version %s\n", VERSION);
956	fprintf(stdout, "Usage: %s [commands] [options]\n", prog);
957
958	fprintf(stdout, "\n%s", usage_commands);
959	fprintf(stdout, "\n%s", usage_tables);
960	fprintf(stdout, "\n%s", usage_conntrack_parameters);
961	fprintf(stdout, "\n%s", usage_expectation_parameters);
962	fprintf(stdout, "\n%s\n", usage_parameters);
963}
964
965static unsigned int output_mask;
966
967
968static int
969filter_mark(const struct nf_conntrack *ct)
970{
971	if ((options & CT_OPT_MARK) &&
972	     !mark_cmp(&tmpl.mark, ct))
973		return 1;
974	return 0;
975}
976
977
978static int
979filter_nat(const struct nf_conntrack *obj, const struct nf_conntrack *ct)
980{
981	int check_srcnat = options & CT_OPT_SRC_NAT ? 1 : 0;
982	int check_dstnat = options & CT_OPT_DST_NAT ? 1 : 0;
983	int has_srcnat = 0, has_dstnat = 0;
984	uint32_t ip;
985	uint16_t port;
986
987	if (options & CT_OPT_ANY_NAT)
988		check_srcnat = check_dstnat = 1;
989
990	if (check_srcnat) {
991		int check_address = 0, check_port = 0;
992
993		if (nfct_attr_is_set(obj, ATTR_SNAT_IPV4)) {
994			check_address = 1;
995			ip = nfct_get_attr_u32(obj, ATTR_SNAT_IPV4);
996			if (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT) &&
997			    ip == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST))
998				has_srcnat = 1;
999		}
1000		if (nfct_attr_is_set(obj, ATTR_SNAT_PORT)) {
1001			int ret = 0;
1002
1003			check_port = 1;
1004			port = nfct_get_attr_u16(obj, ATTR_SNAT_PORT);
1005			if (nfct_getobjopt(ct, NFCT_GOPT_IS_SPAT) &&
1006			    port == nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST))
1007				ret = 1;
1008
1009			/* the address matches but the port does not. */
1010			if (check_address && has_srcnat && !ret)
1011				has_srcnat = 0;
1012			if (!check_address && ret)
1013				has_srcnat = 1;
1014		}
1015		if (!check_address && !check_port &&
1016		    (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT) ||
1017		     nfct_getobjopt(ct, NFCT_GOPT_IS_SPAT)))
1018		  	has_srcnat = 1;
1019	}
1020	if (check_dstnat) {
1021		int check_address = 0, check_port = 0;
1022
1023		if (nfct_attr_is_set(obj, ATTR_DNAT_IPV4)) {
1024			check_address = 1;
1025			ip = nfct_get_attr_u32(obj, ATTR_DNAT_IPV4);
1026			if (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT) &&
1027			    ip == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC))
1028				has_dstnat = 1;
1029		}
1030		if (nfct_attr_is_set(obj, ATTR_DNAT_PORT)) {
1031			int ret = 0;
1032
1033			check_port = 1;
1034			port = nfct_get_attr_u16(obj, ATTR_DNAT_PORT);
1035			if (nfct_getobjopt(ct, NFCT_GOPT_IS_DPAT) &&
1036			    port == nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC))
1037				ret = 1;
1038
1039			/* the address matches but the port does not. */
1040			if (check_address && has_dstnat && !ret)
1041				has_dstnat = 0;
1042			if (!check_address && ret)
1043				has_dstnat = 1;
1044		}
1045		if (!check_address && !check_port &&
1046		    (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT) ||
1047		     nfct_getobjopt(ct, NFCT_GOPT_IS_DPAT)))
1048			has_dstnat = 1;
1049	}
1050	if (options & CT_OPT_ANY_NAT)
1051		return !(has_srcnat || has_dstnat);
1052	else if ((options & CT_OPT_SRC_NAT) && (options & CT_OPT_DST_NAT))
1053		return !(has_srcnat && has_dstnat);
1054	else if (options & CT_OPT_SRC_NAT)
1055		return !has_srcnat;
1056	else if (options & CT_OPT_DST_NAT)
1057		return !has_dstnat;
1058
1059	return 0;
1060}
1061
1062static int counter;
1063static int dump_xml_header_done = 1;
1064
1065static void __attribute__((noreturn))
1066event_sighandler(int s)
1067{
1068	if (dump_xml_header_done == 0) {
1069		printf("</conntrack>\n");
1070		fflush(stdout);
1071	}
1072
1073	fprintf(stderr, "%s v%s (conntrack-tools): ", PROGNAME, VERSION);
1074	fprintf(stderr, "%d flow events have been shown.\n", counter);
1075	nfct_close(cth);
1076	exit(0);
1077}
1078
1079static void __attribute__((noreturn))
1080exp_event_sighandler(int s)
1081{
1082	if (dump_xml_header_done == 0) {
1083		printf("</expect>\n");
1084		fflush(stdout);
1085	}
1086
1087	fprintf(stderr, "%s v%s (conntrack-tools): ", PROGNAME, VERSION);
1088	fprintf(stderr, "%d expectation events have been shown.\n", counter);
1089	nfct_close(cth);
1090	exit(0);
1091}
1092
1093static int event_cb(enum nf_conntrack_msg_type type,
1094		    struct nf_conntrack *ct,
1095		    void *data)
1096{
1097	char buf[1024];
1098	struct nf_conntrack *obj = data;
1099	unsigned int op_type = NFCT_O_DEFAULT;
1100	unsigned int op_flags = 0;
1101
1102	if (filter_nat(obj, ct))
1103		return NFCT_CB_CONTINUE;
1104
1105	if (filter_mark(ct))
1106		return NFCT_CB_CONTINUE;
1107
1108	if (options & CT_COMPARISON &&
1109	    !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK))
1110		return NFCT_CB_CONTINUE;
1111
1112	if (output_mask & _O_XML) {
1113		op_type = NFCT_O_XML;
1114		if (dump_xml_header_done) {
1115			dump_xml_header_done = 0;
1116			printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
1117			       "<conntrack>\n");
1118		}
1119	}
1120	if (output_mask & _O_EXT)
1121		op_flags = NFCT_OF_SHOW_LAYER3;
1122	if (output_mask & _O_TMS) {
1123		if (!(output_mask & _O_XML)) {
1124			struct timeval tv;
1125			gettimeofday(&tv, NULL);
1126			printf("[%-8ld.%-6ld]\t", tv.tv_sec, tv.tv_usec);
1127		} else
1128			op_flags |= NFCT_OF_TIME;
1129	}
1130	if (output_mask & _O_KTMS)
1131		op_flags |= NFCT_OF_TIMESTAMP;
1132	if (output_mask & _O_ID)
1133		op_flags |= NFCT_OF_ID;
1134
1135	nfct_snprintf(buf, sizeof(buf), ct, type, op_type, op_flags);
1136
1137	printf("%s\n", buf);
1138	fflush(stdout);
1139
1140	counter++;
1141
1142	return NFCT_CB_CONTINUE;
1143}
1144
1145static int dump_cb(enum nf_conntrack_msg_type type,
1146		   struct nf_conntrack *ct,
1147		   void *data)
1148{
1149	char buf[1024];
1150	struct nf_conntrack *obj = data;
1151	unsigned int op_type = NFCT_O_DEFAULT;
1152	unsigned int op_flags = 0;
1153
1154	if (filter_nat(obj, ct))
1155		return NFCT_CB_CONTINUE;
1156
1157	if (filter_mark(ct))
1158		return NFCT_CB_CONTINUE;
1159
1160	if (options & CT_COMPARISON &&
1161	    !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK))
1162		return NFCT_CB_CONTINUE;
1163
1164	if (output_mask & _O_XML) {
1165		op_type = NFCT_O_XML;
1166		if (dump_xml_header_done) {
1167			dump_xml_header_done = 0;
1168			printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
1169			       "<conntrack>\n");
1170		}
1171	}
1172	if (output_mask & _O_EXT)
1173		op_flags = NFCT_OF_SHOW_LAYER3;
1174	if (output_mask & _O_KTMS)
1175		op_flags |= NFCT_OF_TIMESTAMP;
1176	if (output_mask & _O_ID)
1177		op_flags |= NFCT_OF_ID;
1178
1179	nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags);
1180	printf("%s\n", buf);
1181
1182	counter++;
1183
1184	return NFCT_CB_CONTINUE;
1185}
1186
1187static int delete_cb(enum nf_conntrack_msg_type type,
1188		     struct nf_conntrack *ct,
1189		     void *data)
1190{
1191	int res;
1192	char buf[1024];
1193	struct nf_conntrack *obj = data;
1194	unsigned int op_type = NFCT_O_DEFAULT;
1195	unsigned int op_flags = 0;
1196
1197	if (filter_nat(obj, ct))
1198		return NFCT_CB_CONTINUE;
1199
1200	if (filter_mark(ct))
1201		return NFCT_CB_CONTINUE;
1202
1203	if (options & CT_COMPARISON &&
1204	    !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK))
1205		return NFCT_CB_CONTINUE;
1206
1207	res = nfct_query(ith, NFCT_Q_DESTROY, ct);
1208	if (res < 0)
1209		exit_error(OTHER_PROBLEM,
1210			   "Operation failed: %s",
1211			   err2str(errno, CT_DELETE));
1212
1213	if (output_mask & _O_XML)
1214		op_type = NFCT_O_XML;
1215	if (output_mask & _O_EXT)
1216		op_flags = NFCT_OF_SHOW_LAYER3;
1217	if (output_mask & _O_ID)
1218		op_flags |= NFCT_OF_ID;
1219
1220	nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags);
1221	printf("%s\n", buf);
1222
1223	counter++;
1224
1225	return NFCT_CB_CONTINUE;
1226}
1227
1228static int print_cb(enum nf_conntrack_msg_type type,
1229		    struct nf_conntrack *ct,
1230		    void *data)
1231{
1232	char buf[1024];
1233	unsigned int op_type = NFCT_O_DEFAULT;
1234	unsigned int op_flags = 0;
1235
1236	if (output_mask & _O_XML)
1237		op_type = NFCT_O_XML;
1238	if (output_mask & _O_EXT)
1239		op_flags = NFCT_OF_SHOW_LAYER3;
1240	if (output_mask & _O_ID)
1241		op_flags |= NFCT_OF_ID;
1242
1243	nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags);
1244	printf("%s\n", buf);
1245
1246	return NFCT_CB_CONTINUE;
1247}
1248
1249static void copy_mark(struct nf_conntrack *tmp,
1250		      const struct nf_conntrack *ct,
1251		      const struct u32_mask *m)
1252{
1253	if (options & CT_OPT_MARK) {
1254		uint32_t mark = nfct_get_attr_u32(ct, ATTR_MARK);
1255		mark = (mark & ~m->mask) ^ m->value;
1256		nfct_set_attr_u32(tmp, ATTR_MARK, mark);
1257	}
1258}
1259
1260static void copy_status(struct nf_conntrack *tmp, const struct nf_conntrack *ct)
1261{
1262	if (options & CT_OPT_STATUS) {
1263		/* copy existing flags, we only allow setting them. */
1264		uint32_t status = nfct_get_attr_u32(ct, ATTR_STATUS);
1265		status |= nfct_get_attr_u32(tmp, ATTR_STATUS);
1266		nfct_set_attr_u32(tmp, ATTR_STATUS, status);
1267	}
1268}
1269
1270static int update_cb(enum nf_conntrack_msg_type type,
1271		     struct nf_conntrack *ct,
1272		     void *data)
1273{
1274	int res;
1275	struct nf_conntrack *obj = data, *tmp;
1276
1277	if (filter_nat(obj, ct))
1278		return NFCT_CB_CONTINUE;
1279
1280	if (nfct_attr_is_set(obj, ATTR_ID) && nfct_attr_is_set(ct, ATTR_ID) &&
1281	    nfct_get_attr_u32(obj, ATTR_ID) != nfct_get_attr_u32(ct, ATTR_ID))
1282	    	return NFCT_CB_CONTINUE;
1283
1284	if (options & CT_OPT_TUPLE_ORIG && !nfct_cmp(obj, ct, NFCT_CMP_ORIG))
1285		return NFCT_CB_CONTINUE;
1286	if (options & CT_OPT_TUPLE_REPL && !nfct_cmp(obj, ct, NFCT_CMP_REPL))
1287		return NFCT_CB_CONTINUE;
1288
1289	tmp = nfct_new();
1290	if (tmp == NULL)
1291		exit_error(OTHER_PROBLEM, "out of memory");
1292
1293	nfct_copy(tmp, ct, NFCT_CP_ORIG);
1294	nfct_copy(tmp, obj, NFCT_CP_META);
1295	copy_mark(tmp, ct, &tmpl.mark);
1296	copy_status(tmp, ct);
1297
1298	/* do not send NFCT_Q_UPDATE if ct appears unchanged */
1299	if (nfct_cmp(tmp, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) {
1300		nfct_destroy(tmp);
1301		return NFCT_CB_CONTINUE;
1302	}
1303
1304	res = nfct_query(ith, NFCT_Q_UPDATE, tmp);
1305	if (res < 0) {
1306		nfct_destroy(tmp);
1307		exit_error(OTHER_PROBLEM,
1308			   "Operation failed: %s",
1309			   err2str(errno, CT_UPDATE));
1310	}
1311	nfct_callback_register(ith, NFCT_T_ALL, print_cb, NULL);
1312
1313	res = nfct_query(ith, NFCT_Q_GET, tmp);
1314	if (res < 0) {
1315		nfct_destroy(tmp);
1316		/* the entry has vanish in middle of the update */
1317		if (errno == ENOENT) {
1318			nfct_callback_unregister(ith);
1319			return NFCT_CB_CONTINUE;
1320		}
1321		exit_error(OTHER_PROBLEM,
1322			   "Operation failed: %s",
1323			   err2str(errno, CT_UPDATE));
1324	}
1325	nfct_destroy(tmp);
1326	nfct_callback_unregister(ith);
1327
1328	counter++;
1329
1330	return NFCT_CB_CONTINUE;
1331}
1332
1333static int dump_exp_cb(enum nf_conntrack_msg_type type,
1334		      struct nf_expect *exp,
1335		      void *data)
1336{
1337	char buf[1024];
1338	unsigned int op_type = NFCT_O_DEFAULT;
1339	unsigned int op_flags = 0;
1340
1341	if (output_mask & _O_XML) {
1342		op_type = NFCT_O_XML;
1343		if (dump_xml_header_done) {
1344			dump_xml_header_done = 0;
1345			printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
1346			       "<expect>\n");
1347		}
1348	}
1349	if (output_mask & _O_TMS) {
1350		if (!(output_mask & _O_XML)) {
1351			struct timeval tv;
1352			gettimeofday(&tv, NULL);
1353			printf("[%-8ld.%-6ld]\t", tv.tv_sec, tv.tv_usec);
1354		} else
1355			op_flags |= NFCT_OF_TIME;
1356	}
1357
1358	nfexp_snprintf(buf,sizeof(buf), exp, NFCT_T_UNKNOWN, op_type, op_flags);
1359	printf("%s\n", buf);
1360	counter++;
1361
1362	return NFCT_CB_CONTINUE;
1363}
1364
1365static int event_exp_cb(enum nf_conntrack_msg_type type,
1366			struct nf_expect *exp, void *data)
1367{
1368	char buf[1024];
1369	unsigned int op_type = NFCT_O_DEFAULT;
1370	unsigned int op_flags = 0;
1371
1372	if (output_mask & _O_XML) {
1373		op_type = NFCT_O_XML;
1374		if (dump_xml_header_done) {
1375			dump_xml_header_done = 0;
1376			printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
1377			       "<expect>\n");
1378		}
1379	}
1380	if (output_mask & _O_TMS) {
1381		if (!(output_mask & _O_XML)) {
1382			struct timeval tv;
1383			gettimeofday(&tv, NULL);
1384			printf("[%-8ld.%-6ld]\t", tv.tv_sec, tv.tv_usec);
1385		} else
1386			op_flags |= NFCT_OF_TIME;
1387	}
1388
1389	nfexp_snprintf(buf,sizeof(buf), exp, type, op_type, op_flags);
1390	printf("%s\n", buf);
1391	fflush(stdout);
1392	counter++;
1393
1394	return NFCT_CB_CONTINUE;
1395}
1396
1397static int count_exp_cb(enum nf_conntrack_msg_type type,
1398			struct nf_expect *exp,
1399			void *data)
1400{
1401	counter++;
1402	return NFCT_CB_CONTINUE;
1403}
1404
1405#ifndef CT_STATS_PROC
1406#define CT_STATS_PROC "/proc/net/stat/nf_conntrack"
1407#endif
1408
1409/* As of 2.6.29, we have 16 entries, this is enough */
1410#ifndef CT_STATS_ENTRIES_MAX
1411#define CT_STATS_ENTRIES_MAX 64
1412#endif
1413
1414/* maximum string length currently is 13 characters */
1415#ifndef CT_STATS_STRING_MAX
1416#define CT_STATS_STRING_MAX 64
1417#endif
1418
1419static int display_proc_conntrack_stats(void)
1420{
1421	int ret = 0;
1422	FILE *fd;
1423	char buf[4096], *token, *nl;
1424	char output[CT_STATS_ENTRIES_MAX][CT_STATS_STRING_MAX];
1425	unsigned int value[CT_STATS_ENTRIES_MAX], i, max;
1426
1427	fd = fopen(CT_STATS_PROC, "r");
1428	if (fd == NULL)
1429		return -1;
1430
1431	if (fgets(buf, sizeof(buf), fd) == NULL) {
1432		ret = -1;
1433		goto out_err;
1434	}
1435
1436	/* trim off trailing \n */
1437	nl = strchr(buf, '\n');
1438	if (nl != NULL)
1439		*nl = '\0';
1440
1441	token = strtok(buf, " ");
1442	for (i=0; token != NULL && i<CT_STATS_ENTRIES_MAX; i++) {
1443		strncpy(output[i], token, CT_STATS_STRING_MAX);
1444		output[i][CT_STATS_STRING_MAX-1]='\0';
1445		token = strtok(NULL, " ");
1446	}
1447	max = i;
1448
1449	if (fgets(buf, sizeof(buf), fd) == NULL) {
1450		ret = -1;
1451		goto out_err;
1452	}
1453
1454	nl = strchr(buf, '\n');
1455	while (nl != NULL) {
1456		*nl = '\0';
1457		nl = strchr(buf, '\n');
1458	}
1459	token = strtok(buf, " ");
1460	for (i=0; token != NULL && i<CT_STATS_ENTRIES_MAX; i++) {
1461		value[i] = (unsigned int) strtol(token, (char**) NULL, 16);
1462		token = strtok(NULL, " ");
1463	}
1464
1465	for (i=0; i<max; i++)
1466		printf("%-10s\t\t%-8u\n", output[i], value[i]);
1467
1468out_err:
1469	fclose(fd);
1470	return ret;
1471}
1472
1473static struct nfct_mnl_socket {
1474	struct mnl_socket	*mnl;
1475	uint32_t		portid;
1476} sock;
1477
1478static int nfct_mnl_socket_open(void)
1479{
1480	sock.mnl = mnl_socket_open(NETLINK_NETFILTER);
1481	if (sock.mnl == NULL) {
1482		perror("mnl_socket_open");
1483		return -1;
1484	}
1485	if (mnl_socket_bind(sock.mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
1486		perror("mnl_socket_bind");
1487		return -1;
1488	}
1489	sock.portid = mnl_socket_get_portid(sock.mnl);
1490
1491	return 0;
1492}
1493
1494static struct nlmsghdr *
1495nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type)
1496{
1497	struct nlmsghdr *nlh;
1498	struct nfgenmsg *nfh;
1499
1500	nlh = mnl_nlmsg_put_header(buf);
1501	nlh->nlmsg_type = (subsys << 8) | type;
1502	nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
1503	nlh->nlmsg_seq = time(NULL);
1504
1505	nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
1506	nfh->nfgen_family = AF_INET;
1507	nfh->version = NFNETLINK_V0;
1508	nfh->res_id = 0;
1509
1510	return nlh;
1511}
1512
1513static void nfct_mnl_socket_close(void)
1514{
1515	mnl_socket_close(sock.mnl);
1516}
1517
1518static int
1519nfct_mnl_dump(uint16_t subsys, uint16_t type, mnl_cb_t cb)
1520{
1521	char buf[MNL_SOCKET_BUFFER_SIZE];
1522	struct nlmsghdr *nlh;
1523	int res;
1524
1525	nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type);
1526
1527	res = mnl_socket_sendto(sock.mnl, nlh, nlh->nlmsg_len);
1528	if (res < 0)
1529		return res;
1530
1531	res = mnl_socket_recvfrom(sock.mnl, buf, sizeof(buf));
1532	while (res > 0) {
1533		res = mnl_cb_run(buf, res, nlh->nlmsg_seq, sock.portid,
1534					 cb, NULL);
1535		if (res <= MNL_CB_STOP)
1536			break;
1537
1538		res = mnl_socket_recvfrom(sock.mnl, buf, sizeof(buf));
1539	}
1540
1541	return res;
1542}
1543
1544static int
1545nfct_mnl_get(uint16_t subsys, uint16_t type, mnl_cb_t cb)
1546{
1547	char buf[MNL_SOCKET_BUFFER_SIZE];
1548	struct nlmsghdr *nlh;
1549	int res;
1550
1551	nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type);
1552
1553	res = mnl_socket_sendto(sock.mnl, nlh, nlh->nlmsg_len);
1554	if (res < 0)
1555		return res;
1556
1557	res = mnl_socket_recvfrom(sock.mnl, buf, sizeof(buf));
1558	if (res < 0)
1559		return res;
1560
1561	return mnl_cb_run(buf, res, nlh->nlmsg_seq, sock.portid, cb, NULL);
1562}
1563
1564static int nfct_stats_attr_cb(const struct nlattr *attr, void *data)
1565{
1566	const struct nlattr **tb = data;
1567	int type = mnl_attr_get_type(attr);
1568
1569	if (mnl_attr_type_valid(attr, CTA_STATS_MAX) < 0)
1570		return MNL_CB_OK;
1571
1572	if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
1573		perror("mnl_attr_validate");
1574		return MNL_CB_ERROR;
1575	}
1576
1577	tb[type] = attr;
1578	return MNL_CB_OK;
1579}
1580
1581static int nfct_stats_cb(const struct nlmsghdr *nlh, void *data)
1582{
1583	struct nlattr *tb[CTA_STATS_MAX+1] = {};
1584	struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
1585	const char *attr2name[CTA_STATS_MAX+1] = {
1586		[CTA_STATS_SEARCHED]	= "searched",
1587		[CTA_STATS_FOUND]	= "found",
1588		[CTA_STATS_NEW]		= "new",
1589		[CTA_STATS_INVALID]	= "invalid",
1590		[CTA_STATS_IGNORE]	= "ignore",
1591		[CTA_STATS_DELETE]	= "delete",
1592		[CTA_STATS_DELETE_LIST]	= "delete_list",
1593		[CTA_STATS_INSERT]	= "insert",
1594		[CTA_STATS_INSERT_FAILED] = "insert_failed",
1595		[CTA_STATS_DROP]	= "drop",
1596		[CTA_STATS_EARLY_DROP]	= "early_drop",
1597		[CTA_STATS_ERROR]	= "error",
1598		[CTA_STATS_SEARCH_RESTART] = "search_restart",
1599	};
1600	int i;
1601
1602	mnl_attr_parse(nlh, sizeof(*nfg), nfct_stats_attr_cb, tb);
1603
1604	printf("cpu=%-4u\t", ntohs(nfg->res_id));
1605
1606	for (i=0; i<CTA_STATS_MAX+1; i++) {
1607		if (tb[i]) {
1608			printf("%s=%u ",
1609				attr2name[i], ntohl(mnl_attr_get_u32(tb[i])));
1610		}
1611	}
1612	printf("\n");
1613	return MNL_CB_OK;
1614}
1615
1616static int nfexp_stats_attr_cb(const struct nlattr *attr, void *data)
1617{
1618	const struct nlattr **tb = data;
1619	int type = mnl_attr_get_type(attr);
1620
1621	if (mnl_attr_type_valid(attr, CTA_STATS_EXP_MAX) < 0)
1622		return MNL_CB_OK;
1623
1624	if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
1625		perror("mnl_attr_validate");
1626		return MNL_CB_ERROR;
1627	}
1628
1629	tb[type] = attr;
1630	return MNL_CB_OK;
1631}
1632
1633static int nfexp_stats_cb(const struct nlmsghdr *nlh, void *data)
1634{
1635	struct nlattr *tb[CTA_STATS_EXP_MAX+1] = {};
1636	struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
1637	const char *attr2name[CTA_STATS_EXP_MAX+1] = {
1638		[CTA_STATS_EXP_NEW] = "expect_new",
1639		[CTA_STATS_EXP_CREATE] = "expect_create",
1640		[CTA_STATS_EXP_DELETE] = "expect_delete",
1641	};
1642	int i;
1643
1644	mnl_attr_parse(nlh, sizeof(*nfg), nfexp_stats_attr_cb, tb);
1645
1646	printf("cpu=%-4u\t", ntohs(nfg->res_id));
1647
1648	for (i=0; i<CTA_STATS_EXP_MAX+1; i++) {
1649		if (tb[i]) {
1650			printf("%s=%u ",
1651				attr2name[i], ntohl(mnl_attr_get_u32(tb[i])));
1652		}
1653	}
1654	printf("\n");
1655	return MNL_CB_OK;
1656}
1657
1658static int nfct_stats_global_attr_cb(const struct nlattr *attr, void *data)
1659{
1660	const struct nlattr **tb = data;
1661	int type = mnl_attr_get_type(attr);
1662
1663	if (mnl_attr_type_valid(attr, CTA_STATS_GLOBAL_MAX) < 0)
1664		return MNL_CB_OK;
1665
1666	if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
1667		perror("mnl_attr_validate");
1668		return MNL_CB_ERROR;
1669	}
1670
1671	tb[type] = attr;
1672	return MNL_CB_OK;
1673}
1674
1675static int nfct_global_stats_cb(const struct nlmsghdr *nlh, void *data)
1676{
1677	struct nlattr *tb[CTA_STATS_GLOBAL_MAX+1] = {};
1678	struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
1679
1680	mnl_attr_parse(nlh, sizeof(*nfg), nfct_stats_global_attr_cb, tb);
1681
1682	if (tb[CTA_STATS_GLOBAL_ENTRIES]) {
1683		printf("%d\n",
1684			ntohl(mnl_attr_get_u32(tb[CTA_STATS_GLOBAL_ENTRIES])));
1685	}
1686	return MNL_CB_OK;
1687}
1688
1689static struct ctproto_handler *h;
1690
1691int main(int argc, char *argv[])
1692{
1693	int c, cmd;
1694	unsigned int type = 0, event_mask = 0, l4flags = 0, status = 0;
1695	int res = 0, partial;
1696	size_t socketbuffersize = 0;
1697	int family = AF_UNSPEC;
1698	int l3protonum, protonum = 0;
1699	union ct_address ad;
1700	unsigned int command = 0;
1701
1702	/* we release these objects in the exit_error() path. */
1703	if (!alloc_tmpl_objects())
1704		exit_error(OTHER_PROBLEM, "out of memory");
1705
1706	register_tcp();
1707	register_udp();
1708	register_udplite();
1709	register_sctp();
1710	register_dccp();
1711	register_icmp();
1712	register_icmpv6();
1713	register_gre();
1714	register_unknown();
1715
1716	/* disable explicit missing arguments error output from getopt_long */
1717	opterr = 0;
1718
1719	while ((c = getopt_long(argc, argv, getopt_str, opts, NULL)) != -1) {
1720	switch(c) {
1721		/* commands */
1722		case 'L':
1723		case 'I':
1724		case 'D':
1725		case 'G':
1726		case 'F':
1727		case 'E':
1728		case 'V':
1729		case 'h':
1730		case 'C':
1731		case 'S':
1732			type = check_type(argc, argv);
1733			add_command(&command, cmd2type[c][type]);
1734			break;
1735		case 'U':
1736			type = check_type(argc, argv);
1737			if (type == 0)
1738				add_command(&command, CT_UPDATE);
1739			else
1740				exit_error(PARAMETER_PROBLEM,
1741					   "Can't update expectations");
1742			break;
1743		/* options */
1744		case 's':
1745		case 'd':
1746		case 'r':
1747		case 'q':
1748			options |= opt2type[c];
1749
1750			l3protonum = parse_addr(optarg, &ad);
1751			if (l3protonum == AF_UNSPEC) {
1752				exit_error(PARAMETER_PROBLEM,
1753					   "Invalid IP address `%s'", optarg);
1754			}
1755			set_family(&family, l3protonum);
1756			if (l3protonum == AF_INET) {
1757				nfct_set_attr_u32(tmpl.ct,
1758						  opt2family_attr[c][0],
1759						  ad.v4);
1760			} else if (l3protonum == AF_INET6) {
1761				nfct_set_attr(tmpl.ct,
1762					      opt2family_attr[c][1],
1763					      &ad.v6);
1764			}
1765			nfct_set_attr_u8(tmpl.ct, opt2attr[c], l3protonum);
1766			break;
1767		case '{':
1768		case '}':
1769		case '[':
1770		case ']':
1771			options |= opt2type[c];
1772			l3protonum = parse_addr(optarg, &ad);
1773			if (l3protonum == AF_UNSPEC) {
1774				exit_error(PARAMETER_PROBLEM,
1775					   "Invalid IP address `%s'", optarg);
1776			}
1777			set_family(&family, l3protonum);
1778			if (l3protonum == AF_INET) {
1779				nfct_set_attr_u32(tmpl.mask,
1780						  opt2family_attr[c][0],
1781						  ad.v4);
1782			} else if (l3protonum == AF_INET6) {
1783				nfct_set_attr(tmpl.mask,
1784					      opt2family_attr[c][1],
1785					      &ad.v6);
1786			}
1787			nfct_set_attr_u8(tmpl.mask,
1788					 ATTR_ORIG_L3PROTO, l3protonum);
1789			break;
1790		case 'p':
1791			options |= CT_OPT_PROTO;
1792			h = findproto(optarg, &protonum);
1793			if (!h)
1794				exit_error(PARAMETER_PROBLEM,
1795					   "`%s' unsupported protocol",
1796					   optarg);
1797
1798			opts = merge_options(opts, h->opts, &h->option_offset);
1799			if (opts == NULL)
1800				exit_error(OTHER_PROBLEM, "out of memory");
1801
1802			nfct_set_attr_u8(tmpl.ct, ATTR_L4PROTO, protonum);
1803			break;
1804		case 't':
1805			options |= CT_OPT_TIMEOUT;
1806			nfct_set_attr_u32(tmpl.ct, ATTR_TIMEOUT, atol(optarg));
1807			nfexp_set_attr_u32(tmpl.exp,
1808					   ATTR_EXP_TIMEOUT, atol(optarg));
1809			break;
1810		case 'u':
1811			options |= CT_OPT_STATUS;
1812			parse_parameter(optarg, &status, PARSE_STATUS);
1813			nfct_set_attr_u32(tmpl.ct, ATTR_STATUS, status);
1814			break;
1815		case 'e':
1816			options |= CT_OPT_EVENT_MASK;
1817			parse_parameter(optarg, &event_mask, PARSE_EVENT);
1818			break;
1819		case 'o':
1820			options |= CT_OPT_OUTPUT;
1821			parse_parameter(optarg, &output_mask, PARSE_OUTPUT);
1822			break;
1823		case 'z':
1824			options |= CT_OPT_ZERO;
1825			break;
1826		case 'n':
1827		case 'g':
1828		case 'j': {
1829			char *tmp = NULL;
1830
1831			options |= opt2type[c];
1832
1833			if (optarg)
1834				continue;
1835			else if (optind < argc && argv[optind][0] != '-'
1836				 && argv[optind][0] != '!')
1837				tmp = argv[optind++];
1838
1839			if (tmp == NULL)
1840				continue;
1841
1842			set_family(&family, AF_INET);
1843			nat_parse(tmp, tmpl.ct, opt2type[c]);
1844			break;
1845		}
1846		case 'w':
1847			options |= opt2type[c];
1848			nfct_set_attr_u16(tmpl.ct,
1849					  opt2attr[c],
1850					  strtoul(optarg, NULL, 0));
1851			break;
1852		case 'i':
1853		case 'c':
1854			options |= opt2type[c];
1855			nfct_set_attr_u32(tmpl.ct,
1856					  opt2attr[c],
1857					  strtoul(optarg, NULL, 0));
1858			break;
1859		case 'm':
1860			options |= opt2type[c];
1861			parse_u32_mask(optarg, &tmpl.mark);
1862			tmpl.filter_mark_kernel.val = tmpl.mark.value;
1863			tmpl.filter_mark_kernel.mask = tmpl.mark.mask;
1864			break;
1865		case 'a':
1866			fprintf(stderr, "WARNING: ignoring -%c, "
1867					"deprecated option.\n", c);
1868			break;
1869		case 'f':
1870			options |= CT_OPT_FAMILY;
1871			if (strncmp(optarg, "ipv4", strlen("ipv4")) == 0)
1872				set_family(&family, AF_INET);
1873			else if (strncmp(optarg, "ipv6", strlen("ipv6")) == 0)
1874				set_family(&family, AF_INET6);
1875			else
1876				exit_error(PARAMETER_PROBLEM,
1877					   "`%s' unsupported protocol",
1878					   optarg);
1879			break;
1880		case 'b':
1881			socketbuffersize = atol(optarg);
1882			options |= CT_OPT_BUFFERSIZE;
1883			break;
1884		case '?':
1885			if (optopt)
1886				exit_error(PARAMETER_PROBLEM,
1887					   "option `%s' requires an "
1888					   "argument", argv[optind-1]);
1889			else
1890				exit_error(PARAMETER_PROBLEM,
1891					   "unknown option `%s'",
1892					   argv[optind-1]);
1893			break;
1894		default:
1895			if (h && h->parse_opts
1896			    &&!h->parse_opts(c - h->option_offset, tmpl.ct,
1897			    		     tmpl.exptuple, tmpl.mask,
1898					     &l4flags))
1899				exit_error(PARAMETER_PROBLEM, "parse error");
1900			break;
1901		}
1902	}
1903
1904	/* default family */
1905	if (family == AF_UNSPEC)
1906		family = AF_INET;
1907
1908	/* we cannot check this combination with generic_opt_check. */
1909	if (options & CT_OPT_ANY_NAT &&
1910	   ((options & CT_OPT_SRC_NAT) || (options & CT_OPT_DST_NAT))) {
1911		exit_error(PARAMETER_PROBLEM, "cannot specify `--src-nat' or "
1912					      "`--dst-nat' with `--any-nat'");
1913	}
1914	cmd = bit2cmd(command);
1915	res = generic_opt_check(options, NUMBER_OF_OPT,
1916				commands_v_options[cmd], optflags,
1917				addr_valid_flags, ADDR_VALID_FLAGS_MAX,
1918				&partial);
1919	if (!res) {
1920		switch(partial) {
1921		case -1:
1922		case 0:
1923			exit_error(PARAMETER_PROBLEM, "you have to specify "
1924						      "`--src' and `--dst'");
1925			break;
1926		case 1:
1927			exit_error(PARAMETER_PROBLEM, "you have to specify "
1928						      "`--reply-src' and "
1929						      "`--reply-dst'");
1930			break;
1931		}
1932	}
1933	if (!(command & CT_HELP) && h && h->final_check)
1934		h->final_check(l4flags, cmd, tmpl.ct);
1935
1936	switch(command) {
1937	struct nfct_filter_dump *filter_dump;
1938
1939	case CT_LIST:
1940		cth = nfct_open(CONNTRACK, 0);
1941		if (!cth)
1942			exit_error(OTHER_PROBLEM, "Can't open handler");
1943
1944		if (options & CT_COMPARISON &&
1945		    options & CT_OPT_ZERO)
1946			exit_error(PARAMETER_PROBLEM, "Can't use -z with "
1947						      "filtering parameters");
1948
1949		nfct_callback_register(cth, NFCT_T_ALL, dump_cb, tmpl.ct);
1950
1951		filter_dump = nfct_filter_dump_create();
1952		if (filter_dump == NULL)
1953			exit_error(OTHER_PROBLEM, "OOM");
1954
1955		nfct_filter_dump_set_attr(filter_dump, NFCT_FILTER_DUMP_MARK,
1956					  &tmpl.filter_mark_kernel);
1957		nfct_filter_dump_set_attr_u8(filter_dump,
1958					     NFCT_FILTER_DUMP_L3NUM,
1959					     family);
1960
1961		if (options & CT_OPT_ZERO)
1962			res = nfct_query(cth, NFCT_Q_DUMP_FILTER_RESET,
1963					filter_dump);
1964		else
1965			res = nfct_query(cth, NFCT_Q_DUMP_FILTER, filter_dump);
1966
1967		nfct_filter_dump_destroy(filter_dump);
1968
1969		if (dump_xml_header_done == 0) {
1970			printf("</conntrack>\n");
1971			fflush(stdout);
1972		}
1973
1974		nfct_close(cth);
1975		break;
1976
1977	case EXP_LIST:
1978		cth = nfct_open(EXPECT, 0);
1979		if (!cth)
1980			exit_error(OTHER_PROBLEM, "Can't open handler");
1981
1982		nfexp_callback_register(cth, NFCT_T_ALL, dump_exp_cb, NULL);
1983		res = nfexp_query(cth, NFCT_Q_DUMP, &family);
1984		nfct_close(cth);
1985
1986		if (dump_xml_header_done == 0) {
1987			printf("</expect>\n");
1988			fflush(stdout);
1989		}
1990		break;
1991
1992	case CT_CREATE:
1993		if ((options & CT_OPT_ORIG) && !(options & CT_OPT_REPL))
1994		    	nfct_setobjopt(tmpl.ct, NFCT_SOPT_SETUP_REPLY);
1995		else if (!(options & CT_OPT_ORIG) && (options & CT_OPT_REPL))
1996			nfct_setobjopt(tmpl.ct, NFCT_SOPT_SETUP_ORIGINAL);
1997
1998		if (options & CT_OPT_MARK)
1999			nfct_set_attr_u32(tmpl.ct, ATTR_MARK, tmpl.mark.value);
2000
2001		cth = nfct_open(CONNTRACK, 0);
2002		if (!cth)
2003			exit_error(OTHER_PROBLEM, "Can't open handler");
2004
2005		res = nfct_query(cth, NFCT_Q_CREATE, tmpl.ct);
2006		if (res != -1)
2007			counter++;
2008		nfct_close(cth);
2009		break;
2010
2011	case EXP_CREATE:
2012		nfexp_set_attr(tmpl.exp, ATTR_EXP_MASTER, tmpl.ct);
2013		nfexp_set_attr(tmpl.exp, ATTR_EXP_EXPECTED, tmpl.exptuple);
2014		nfexp_set_attr(tmpl.exp, ATTR_EXP_MASK, tmpl.mask);
2015
2016		cth = nfct_open(EXPECT, 0);
2017		if (!cth)
2018			exit_error(OTHER_PROBLEM, "Can't open handler");
2019
2020		res = nfexp_query(cth, NFCT_Q_CREATE, tmpl.exp);
2021		nfct_close(cth);
2022		break;
2023
2024	case CT_UPDATE:
2025		cth = nfct_open(CONNTRACK, 0);
2026		/* internal handler for delete_cb, otherwise we hit EILSEQ */
2027		ith = nfct_open(CONNTRACK, 0);
2028		if (!cth || !ith)
2029			exit_error(OTHER_PROBLEM, "Can't open handler");
2030
2031		nfct_callback_register(cth, NFCT_T_ALL, update_cb, tmpl.ct);
2032
2033		res = nfct_query(cth, NFCT_Q_DUMP, &family);
2034		nfct_close(ith);
2035		nfct_close(cth);
2036		break;
2037
2038	case CT_DELETE:
2039		cth = nfct_open(CONNTRACK, 0);
2040		ith = nfct_open(CONNTRACK, 0);
2041		if (!cth || !ith)
2042			exit_error(OTHER_PROBLEM, "Can't open handler");
2043
2044		nfct_callback_register(cth, NFCT_T_ALL, delete_cb, tmpl.ct);
2045
2046		filter_dump = nfct_filter_dump_create();
2047		if (filter_dump == NULL)
2048			exit_error(OTHER_PROBLEM, "OOM");
2049
2050		nfct_filter_dump_set_attr(filter_dump, NFCT_FILTER_DUMP_MARK,
2051					  &tmpl.filter_mark_kernel);
2052		nfct_filter_dump_set_attr_u8(filter_dump,
2053					     NFCT_FILTER_DUMP_L3NUM,
2054					     family);
2055
2056		res = nfct_query(cth, NFCT_Q_DUMP_FILTER, filter_dump);
2057
2058		nfct_filter_dump_destroy(filter_dump);
2059
2060		nfct_close(ith);
2061		nfct_close(cth);
2062		break;
2063
2064	case EXP_DELETE:
2065		nfexp_set_attr(tmpl.exp, ATTR_EXP_EXPECTED, tmpl.ct);
2066
2067		cth = nfct_open(EXPECT, 0);
2068		if (!cth)
2069			exit_error(OTHER_PROBLEM, "Can't open handler");
2070
2071		res = nfexp_query(cth, NFCT_Q_DESTROY, tmpl.exp);
2072		nfct_close(cth);
2073		break;
2074
2075	case CT_GET:
2076		cth = nfct_open(CONNTRACK, 0);
2077		if (!cth)
2078			exit_error(OTHER_PROBLEM, "Can't open handler");
2079
2080		nfct_callback_register(cth, NFCT_T_ALL, dump_cb, tmpl.ct);
2081		res = nfct_query(cth, NFCT_Q_GET, tmpl.ct);
2082		nfct_close(cth);
2083		break;
2084
2085	case EXP_GET:
2086		nfexp_set_attr(tmpl.exp, ATTR_EXP_MASTER, tmpl.ct);
2087
2088		cth = nfct_open(EXPECT, 0);
2089		if (!cth)
2090			exit_error(OTHER_PROBLEM, "Can't open handler");
2091
2092		nfexp_callback_register(cth, NFCT_T_ALL, dump_exp_cb, NULL);
2093		res = nfexp_query(cth, NFCT_Q_GET, tmpl.exp);
2094		nfct_close(cth);
2095		break;
2096
2097	case CT_FLUSH:
2098		cth = nfct_open(CONNTRACK, 0);
2099		if (!cth)
2100			exit_error(OTHER_PROBLEM, "Can't open handler");
2101		res = nfct_query(cth, NFCT_Q_FLUSH, &family);
2102		nfct_close(cth);
2103		fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION);
2104		fprintf(stderr,"connection tracking table has been emptied.\n");
2105		break;
2106
2107	case EXP_FLUSH:
2108		cth = nfct_open(EXPECT, 0);
2109		if (!cth)
2110			exit_error(OTHER_PROBLEM, "Can't open handler");
2111		res = nfexp_query(cth, NFCT_Q_FLUSH, &family);
2112		nfct_close(cth);
2113		fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION);
2114		fprintf(stderr,"expectation table has been emptied.\n");
2115		break;
2116
2117	case CT_EVENT:
2118		if (options & CT_OPT_EVENT_MASK) {
2119			unsigned int nl_events = 0;
2120
2121			if (event_mask & CT_EVENT_F_NEW)
2122				nl_events |= NF_NETLINK_CONNTRACK_NEW;
2123			if (event_mask & CT_EVENT_F_UPD)
2124				nl_events |= NF_NETLINK_CONNTRACK_UPDATE;
2125			if (event_mask & CT_EVENT_F_DEL)
2126				nl_events |= NF_NETLINK_CONNTRACK_DESTROY;
2127
2128			cth = nfct_open(CONNTRACK, nl_events);
2129		} else {
2130			cth = nfct_open(CONNTRACK,
2131					NF_NETLINK_CONNTRACK_NEW |
2132					NF_NETLINK_CONNTRACK_UPDATE |
2133					NF_NETLINK_CONNTRACK_DESTROY);
2134		}
2135
2136		if (!cth)
2137			exit_error(OTHER_PROBLEM, "Can't open handler");
2138
2139		if (options & CT_OPT_BUFFERSIZE) {
2140			size_t ret;
2141			ret = nfnl_rcvbufsiz(nfct_nfnlh(cth), socketbuffersize);
2142			fprintf(stderr, "NOTICE: Netlink socket buffer size "
2143					"has been set to %zu bytes.\n", ret);
2144		}
2145		signal(SIGINT, event_sighandler);
2146		signal(SIGTERM, event_sighandler);
2147		nfct_callback_register(cth, NFCT_T_ALL, event_cb, tmpl.ct);
2148		res = nfct_catch(cth);
2149		if (res == -1) {
2150			if (errno == ENOBUFS) {
2151				fprintf(stderr,
2152					"WARNING: We have hit ENOBUFS! We "
2153					"are losing events.\nThis message "
2154					"means that the current netlink "
2155					"socket buffer size is too small.\n"
2156					"Please, check --buffer-size in "
2157					"conntrack(8) manpage.\n");
2158			}
2159		}
2160		nfct_close(cth);
2161		break;
2162
2163	case EXP_EVENT:
2164		if (options & CT_OPT_EVENT_MASK) {
2165			unsigned int nl_events = 0;
2166
2167			if (event_mask & CT_EVENT_F_NEW)
2168				nl_events |= NF_NETLINK_CONNTRACK_EXP_NEW;
2169			if (event_mask & CT_EVENT_F_UPD)
2170				nl_events |= NF_NETLINK_CONNTRACK_EXP_UPDATE;
2171			if (event_mask & CT_EVENT_F_DEL)
2172				nl_events |= NF_NETLINK_CONNTRACK_EXP_DESTROY;
2173
2174			cth = nfct_open(CONNTRACK, nl_events);
2175		} else {
2176			cth = nfct_open(EXPECT,
2177					NF_NETLINK_CONNTRACK_EXP_NEW |
2178					NF_NETLINK_CONNTRACK_EXP_UPDATE |
2179					NF_NETLINK_CONNTRACK_EXP_DESTROY);
2180		}
2181
2182		if (!cth)
2183			exit_error(OTHER_PROBLEM, "Can't open handler");
2184		signal(SIGINT, exp_event_sighandler);
2185		signal(SIGTERM, exp_event_sighandler);
2186		nfexp_callback_register(cth, NFCT_T_ALL, event_exp_cb, NULL);
2187		res = nfexp_catch(cth);
2188		nfct_close(cth);
2189		break;
2190	case CT_COUNT:
2191		/* If we fail with netlink, fall back to /proc to ensure
2192		 * backward compatibility.
2193		 */
2194		if (nfct_mnl_socket_open() < 0)
2195			goto try_proc_count;
2196
2197		res = nfct_mnl_get(NFNL_SUBSYS_CTNETLINK,
2198				   IPCTNL_MSG_CT_GET_STATS,
2199				   nfct_global_stats_cb);
2200
2201		nfct_mnl_socket_close();
2202
2203		/* don't look at /proc, we got the information via ctnetlink */
2204		if (res >= 0)
2205			break;
2206
2207try_proc_count:
2208		{
2209#define NF_CONNTRACK_COUNT_PROC "/proc/sys/net/netfilter/nf_conntrack_count"
2210		FILE *fd;
2211		int count;
2212		fd = fopen(NF_CONNTRACK_COUNT_PROC, "r");
2213		if (fd == NULL) {
2214			exit_error(OTHER_PROBLEM, "Can't open %s",
2215				   NF_CONNTRACK_COUNT_PROC);
2216		}
2217		if (fscanf(fd, "%d", &count) != 1) {
2218			exit_error(OTHER_PROBLEM, "Can't read %s",
2219				   NF_CONNTRACK_COUNT_PROC);
2220		}
2221		fclose(fd);
2222		printf("%d\n", count);
2223		break;
2224	}
2225	case EXP_COUNT:
2226		cth = nfct_open(EXPECT, 0);
2227		if (!cth)
2228			exit_error(OTHER_PROBLEM, "Can't open handler");
2229
2230		nfexp_callback_register(cth, NFCT_T_ALL, count_exp_cb, NULL);
2231		res = nfexp_query(cth, NFCT_Q_DUMP, &family);
2232		nfct_close(cth);
2233		printf("%d\n", counter);
2234		break;
2235	case CT_STATS:
2236		/* If we fail with netlink, fall back to /proc to ensure
2237		 * backward compatibility.
2238		 */
2239		if (nfct_mnl_socket_open() < 0)
2240			goto try_proc;
2241
2242		res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK,
2243				    IPCTNL_MSG_CT_GET_STATS_CPU,
2244				    nfct_stats_cb);
2245
2246		nfct_mnl_socket_close();
2247
2248		/* don't look at /proc, we got the information via ctnetlink */
2249		if (res >= 0)
2250			break;
2251
2252		goto try_proc;
2253
2254	case EXP_STATS:
2255		/* If we fail with netlink, fall back to /proc to ensure
2256		 * backward compatibility.
2257		 */
2258		if (nfct_mnl_socket_open() < 0)
2259			goto try_proc;
2260
2261		res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK_EXP,
2262				    IPCTNL_MSG_EXP_GET_STATS_CPU,
2263				    nfexp_stats_cb);
2264
2265		nfct_mnl_socket_close();
2266
2267		/* don't look at /proc, we got the information via ctnetlink */
2268		if (res >= 0)
2269			break;
2270try_proc:
2271		if (display_proc_conntrack_stats() < 0)
2272			exit_error(OTHER_PROBLEM, "Can't open /proc interface");
2273		break;
2274	case CT_VERSION:
2275		printf("%s v%s (conntrack-tools)\n", PROGNAME, VERSION);
2276		break;
2277	case CT_HELP:
2278		usage(argv[0]);
2279		if (options & CT_OPT_PROTO)
2280			extension_help(h, protonum);
2281		break;
2282	default:
2283		usage(argv[0]);
2284		break;
2285	}
2286
2287	if (res < 0)
2288		exit_error(OTHER_PROBLEM, "Operation failed: %s",
2289			   err2str(errno, command));
2290
2291	free_tmpl_objects();
2292	free_options();
2293
2294	if (command && exit_msg[cmd][0]) {
2295		fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION);
2296		fprintf(stderr, exit_msg[cmd], counter);
2297		if (counter == 0 && !(command & (CT_LIST | EXP_LIST)))
2298			return EXIT_FAILURE;
2299	}
2300
2301	return EXIT_SUCCESS;
2302}
2303