1/*
2 * (C) 2005-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 */
9
10#include "internal/internal.h"
11
12static int __snprintf_l3protocol(char *buf,
13				 unsigned int len,
14				 const struct nf_conntrack *ct)
15{
16	return (snprintf(buf, len, "%-8s %u ",
17		l3proto2str[ct->head.orig.l3protonum] == NULL ?
18		"unknown" : l3proto2str[ct->head.orig.l3protonum],
19		 ct->head.orig.l3protonum));
20}
21
22int __snprintf_protocol(char *buf,
23			unsigned int len,
24			const struct nf_conntrack *ct)
25{
26	return (snprintf(buf, len, "%-8s %u ",
27		proto2str[ct->head.orig.protonum] == NULL ?
28		"unknown" : proto2str[ct->head.orig.protonum],
29		 ct->head.orig.protonum));
30}
31
32static int __snprintf_timeout(char *buf,
33			      unsigned int len,
34			      const struct nf_conntrack *ct)
35{
36	return snprintf(buf, len, "%u ", ct->timeout);
37}
38
39static int __snprintf_protoinfo(char *buf,
40				unsigned int len,
41				const struct nf_conntrack *ct)
42{
43	return snprintf(buf, len, "%s ",
44			ct->protoinfo.tcp.state < TCP_CONNTRACK_MAX ?
45			states[ct->protoinfo.tcp.state] :
46			states[TCP_CONNTRACK_NONE]);
47}
48
49static int __snprintf_protoinfo_sctp(char *buf,
50				     unsigned int len,
51				     const struct nf_conntrack *ct)
52{
53	return snprintf(buf, len, "%s ",
54			ct->protoinfo.sctp.state < SCTP_CONNTRACK_MAX ?
55			sctp_states[ct->protoinfo.sctp.state] :
56			sctp_states[SCTP_CONNTRACK_NONE]);
57}
58
59static int __snprintf_protoinfo_dccp(char *buf,
60				     unsigned int len,
61				     const struct nf_conntrack *ct)
62{
63	return snprintf(buf, len, "%s ",
64			ct->protoinfo.dccp.state < DCCP_CONNTRACK_MAX ?
65			sctp_states[ct->protoinfo.dccp.state] :
66			sctp_states[DCCP_CONNTRACK_NONE]);
67}
68
69static int __snprintf_address_ipv4(char *buf,
70				   unsigned int len,
71				   const struct __nfct_tuple *tuple,
72				   const char *src_tag,
73				   const char *dst_tag)
74{
75	int ret, size = 0, offset = 0;
76	struct in_addr src = { .s_addr = tuple->src.v4 };
77	struct in_addr dst = { .s_addr = tuple->dst.v4 };
78
79	ret = snprintf(buf, len, "%s=%s ", src_tag, inet_ntoa(src));
80	BUFFER_SIZE(ret, size, len, offset);
81
82	ret = snprintf(buf+offset, len, "%s=%s ", dst_tag, inet_ntoa(dst));
83	BUFFER_SIZE(ret, size, len, offset);
84
85	return size;
86}
87
88static int __snprintf_address_ipv6(char *buf,
89				   unsigned int len,
90				   const struct __nfct_tuple *tuple,
91				   const char *src_tag,
92				   const char *dst_tag)
93{
94	int ret, size = 0, offset = 0;
95	struct in6_addr src;
96	struct in6_addr dst;
97	char tmp[INET6_ADDRSTRLEN];
98
99	memcpy(&src, &tuple->src.v6, sizeof(struct in6_addr));
100	memcpy(&dst, &tuple->dst.v6, sizeof(struct in6_addr));
101
102	if (!inet_ntop(AF_INET6, &src, tmp, sizeof(tmp)))
103		return -1;
104
105	ret = snprintf(buf, len, "%s=%s ", src_tag, tmp);
106	BUFFER_SIZE(ret, size, len, offset);
107
108	if (!inet_ntop(AF_INET6, &dst, tmp, sizeof(tmp)))
109		return -1;
110
111	ret = snprintf(buf+offset, len-size, "%s=%s ", dst_tag, tmp);
112	BUFFER_SIZE(ret, size, len, offset);
113
114	return size;
115}
116
117int __snprintf_address(char *buf,
118		       unsigned int len,
119		       const struct __nfct_tuple *tuple,
120		       const char *src_tag,
121		       const char *dst_tag)
122{
123	int size = 0;
124
125	switch (tuple->l3protonum) {
126	case AF_INET:
127		size = __snprintf_address_ipv4(buf, len, tuple,
128						src_tag, dst_tag);
129		break;
130	case AF_INET6:
131		size = __snprintf_address_ipv6(buf, len, tuple,
132						src_tag, dst_tag);
133		break;
134	}
135
136	return size;
137}
138
139int __snprintf_proto(char *buf,
140		     unsigned int len,
141		     const struct __nfct_tuple *tuple)
142{
143	int size = 0;
144
145	switch(tuple->protonum) {
146	case IPPROTO_TCP:
147	case IPPROTO_UDP:
148	case IPPROTO_UDPLITE:
149	case IPPROTO_SCTP:
150	case IPPROTO_DCCP:
151		return snprintf(buf, len, "sport=%u dport=%u ",
152			        ntohs(tuple->l4src.tcp.port),
153			        ntohs(tuple->l4dst.tcp.port));
154		break;
155	case IPPROTO_GRE:
156		return snprintf(buf, len, "srckey=0x%x dstkey=0x%x ",
157			        ntohs(tuple->l4src.all),
158			        ntohs(tuple->l4dst.all));
159		break;
160	case IPPROTO_ICMP:
161	case IPPROTO_ICMPV6:
162		/* The ID only makes sense some ICMP messages but we want to
163		 * display the same output that /proc/net/ip_conntrack does */
164		return (snprintf(buf, len, "type=%d code=%d id=%d ",
165			tuple->l4dst.icmp.type,
166			tuple->l4dst.icmp.code,
167			ntohs(tuple->l4src.icmp.id)));
168		break;
169	}
170
171	return size;
172}
173
174static int
175__snprintf_tuple_zone(char *buf, unsigned int len, const char *pfx,
176		      const struct __nfct_tuple *tuple)
177{
178	return (snprintf(buf, len, "zone-%s=%u ", pfx, tuple->zone));
179}
180
181static int __snprintf_status_assured(char *buf,
182				     unsigned int len,
183				     const struct nf_conntrack *ct)
184{
185	int size = 0;
186
187	if (ct->status & IPS_ASSURED)
188		size = snprintf(buf, len, "[ASSURED] ");
189
190	return size;
191}
192
193static int __snprintf_status_not_seen_reply(char *buf,
194					    unsigned int len,
195					    const struct nf_conntrack *ct)
196{
197	int size = 0;
198
199        if (!(ct->status & IPS_SEEN_REPLY))
200                size = snprintf(buf, len, "[UNREPLIED] ");
201
202	return size;
203}
204
205static int __snprintf_counters(char *buf,
206			       unsigned int len,
207			       const struct nf_conntrack *ct,
208			       int dir)
209{
210	return (snprintf(buf, len, "packets=%llu bytes=%llu ",
211			 (unsigned long long) ct->counters[dir].packets,
212			 (unsigned long long) ct->counters[dir].bytes));
213}
214
215static int
216__snprintf_mark(char *buf, unsigned int len, const struct nf_conntrack *ct)
217{
218	return (snprintf(buf, len, "mark=%u ", ct->mark));
219}
220
221static int
222__snprintf_secmark(char *buf, unsigned int len, const struct nf_conntrack *ct)
223{
224	return (snprintf(buf, len, "secmark=%u ", ct->secmark));
225}
226
227static int
228__snprintf_use(char *buf, unsigned int len, const struct nf_conntrack *ct)
229{
230	return (snprintf(buf, len, "use=%u ", ct->use));
231}
232
233static int
234__snprintf_id(char *buf, unsigned int len, const struct nf_conntrack *ct)
235{
236	return (snprintf(buf, len, "id=%u ", ct->id));
237}
238
239static int
240__snprintf_zone(char *buf, unsigned int len, const struct nf_conntrack *ct)
241{
242	return (snprintf(buf, len, "zone=%u ", ct->zone));
243}
244
245static int
246__snprintf_secctx(char *buf, unsigned int len, const struct nf_conntrack *ct)
247{
248	return (snprintf(buf, len, "secctx=%s ", ct->secctx));
249}
250
251static int
252__snprintf_timestamp_start(char *buf, unsigned int len,
253			   const struct nf_conntrack *ct)
254{
255	time_t start = (time_t)(ct->timestamp.start / NSEC_PER_SEC);
256	char *tmp = ctime(&start);
257
258	/* overwrite \n in the ctime() output. */
259	tmp[strlen(tmp)-1] = '\0';
260	return (snprintf(buf, len, "[start=%s] ", tmp));
261}
262
263static int
264__snprintf_timestamp_stop(char *buf, unsigned int len,
265			  const struct nf_conntrack *ct)
266{
267	time_t stop = (time_t)(ct->timestamp.stop / NSEC_PER_SEC);
268	char *tmp = ctime(&stop);
269
270	/* overwrite \n in the ctime() output. */
271	tmp[strlen(tmp)-1] = '\0';
272	return (snprintf(buf, len, "[stop=%s] ", tmp));
273}
274
275static int
276__snprintf_timestamp_delta(char *buf, unsigned int len,
277			   const struct nf_conntrack *ct)
278{
279	time_t delta_time, stop;
280
281	if (ct->timestamp.stop == 0)
282		time(&stop);
283	else
284		stop = (time_t)(ct->timestamp.stop / NSEC_PER_SEC);
285
286	delta_time = stop - (time_t)(ct->timestamp.start / NSEC_PER_SEC);
287
288	return (snprintf(buf, len, "delta-time=%llu ",
289			(unsigned long long)delta_time));
290}
291
292static int
293__snprintf_helper_name(char *buf, unsigned int len, const struct nf_conntrack *ct)
294{
295	return (snprintf(buf, len, "helper=%s ", ct->helper_name));
296}
297
298int
299__snprintf_connlabels(char *buf, unsigned int len,
300		      struct nfct_labelmap *map,
301		      const struct nfct_bitmask *b, const char *fmt)
302{
303	unsigned int i, max;
304	int ret, size = 0, offset = 0;
305
306	max = nfct_bitmask_maxbit(b);
307	for (i = 0; i <= max && len; i++) {
308		const char *name;
309		if (!nfct_bitmask_test_bit(b, i))
310			continue;
311		name = nfct_labelmap_get_name(map, i);
312		if (!name || strcmp(name, "") == 0)
313			continue;
314
315		ret = snprintf(buf + offset, len, fmt, name);
316		BUFFER_SIZE(ret, size, len, offset);
317	}
318	return size;
319}
320
321static int
322__snprintf_clabels(char *buf, unsigned int len,
323		   const struct nf_conntrack *ct, struct nfct_labelmap *map)
324{
325	const struct nfct_bitmask *b = nfct_get_attr(ct, ATTR_CONNLABELS);
326	int ret, size = 0, offset = 0;
327
328	if (!b)
329		return 0;
330
331	ret = snprintf(buf, len, "labels=");
332	BUFFER_SIZE(ret, size, len, offset);
333
334	ret = __snprintf_connlabels(buf + offset, len, map, b, "%s,");
335
336	BUFFER_SIZE(ret, size, len, offset);
337
338	offset--; /* remove last , */
339	size--;
340	ret = snprintf(buf + offset, len, " ");
341	BUFFER_SIZE(ret, size, len, offset);
342
343	return size;
344}
345
346int __snprintf_conntrack_default(char *buf,
347				 unsigned int len,
348				 const struct nf_conntrack *ct,
349				 unsigned int msg_type,
350				 unsigned int flags,
351				 struct nfct_labelmap *map)
352{
353	int ret = 0, size = 0, offset = 0;
354
355	switch(msg_type) {
356		case NFCT_T_NEW:
357			ret = snprintf(buf, len, "%9s ", "[NEW]");
358			break;
359		case NFCT_T_UPDATE:
360			ret = snprintf(buf, len, "%9s ", "[UPDATE]");
361			break;
362		case NFCT_T_DESTROY:
363			ret = snprintf(buf, len, "%9s ", "[DESTROY]");
364			break;
365		default:
366			break;
367	}
368
369	BUFFER_SIZE(ret, size, len, offset);
370
371	if (flags & NFCT_OF_SHOW_LAYER3) {
372		ret = __snprintf_l3protocol(buf+offset, len, ct);
373		BUFFER_SIZE(ret, size, len, offset);
374	}
375
376	ret = __snprintf_protocol(buf+offset, len, ct);
377	BUFFER_SIZE(ret, size, len, offset);
378
379	if (test_bit(ATTR_TIMEOUT, ct->head.set)) {
380		ret = __snprintf_timeout(buf+offset, len, ct);
381		BUFFER_SIZE(ret, size, len, offset);
382	}
383
384        if (test_bit(ATTR_TCP_STATE, ct->head.set)) {
385		ret = __snprintf_protoinfo(buf+offset, len, ct);
386		BUFFER_SIZE(ret, size, len, offset);
387	}
388
389	if (test_bit(ATTR_SCTP_STATE, ct->head.set)) {
390		ret = __snprintf_protoinfo_sctp(buf+offset, len, ct);
391		BUFFER_SIZE(ret, size, len, offset);
392	}
393
394	if (test_bit(ATTR_DCCP_STATE, ct->head.set)) {
395		ret = __snprintf_protoinfo_dccp(buf+offset, len, ct);
396		BUFFER_SIZE(ret, size, len, offset);
397	}
398
399	ret = __snprintf_address(buf+offset, len, &ct->head.orig,
400				 "src", "dst");
401	BUFFER_SIZE(ret, size, len, offset);
402
403	ret = __snprintf_proto(buf+offset, len, &ct->head.orig);
404	BUFFER_SIZE(ret, size, len, offset);
405
406	if (test_bit(ATTR_ORIG_ZONE, ct->head.set)) {
407		ret = __snprintf_tuple_zone(buf+offset, len, "orig", &ct->head.orig);
408		BUFFER_SIZE(ret, size, len, offset);
409	}
410
411	if (test_bit(ATTR_ORIG_COUNTER_PACKETS, ct->head.set) &&
412	    test_bit(ATTR_ORIG_COUNTER_BYTES, ct->head.set)) {
413		ret = __snprintf_counters(buf+offset, len, ct, __DIR_ORIG);
414		BUFFER_SIZE(ret, size, len, offset);
415	}
416
417	if (test_bit(ATTR_STATUS, ct->head.set)) {
418		ret = __snprintf_status_not_seen_reply(buf+offset, len, ct);
419		BUFFER_SIZE(ret, size, len, offset);
420	}
421
422	ret = __snprintf_address(buf+offset, len, &ct->repl,
423				 "src", "dst");
424	BUFFER_SIZE(ret, size, len, offset);
425
426	ret = __snprintf_proto(buf+offset, len, &ct->repl);
427	BUFFER_SIZE(ret, size, len, offset);
428
429	if (test_bit(ATTR_REPL_ZONE, ct->head.set)) {
430		ret = __snprintf_tuple_zone(buf+offset, len, "reply", &ct->repl);
431		BUFFER_SIZE(ret, size, len, offset);
432	}
433
434	if (test_bit(ATTR_REPL_COUNTER_PACKETS, ct->head.set) &&
435	    test_bit(ATTR_REPL_COUNTER_BYTES, ct->head.set)) {
436		ret = __snprintf_counters(buf+offset, len, ct, __DIR_REPL);
437		BUFFER_SIZE(ret, size, len, offset);
438	}
439
440	if (test_bit(ATTR_STATUS, ct->head.set)) {
441		ret = __snprintf_status_assured(buf+offset, len, ct);
442		BUFFER_SIZE(ret, size, len, offset);
443	}
444
445	if (test_bit(ATTR_MARK, ct->head.set)) {
446		ret = __snprintf_mark(buf+offset, len, ct);
447		BUFFER_SIZE(ret, size, len, offset);
448	}
449
450	if (test_bit(ATTR_SECMARK, ct->head.set)) {
451		ret = __snprintf_secmark(buf+offset, len, ct);
452		BUFFER_SIZE(ret, size, len, offset);
453	}
454
455	if (test_bit(ATTR_SECCTX, ct->head.set)) {
456		ret = __snprintf_secctx(buf+offset, len, ct);
457		BUFFER_SIZE(ret, size, len, offset);
458	}
459
460	if (test_bit(ATTR_ZONE, ct->head.set)) {
461		ret = __snprintf_zone(buf+offset, len, ct);
462		BUFFER_SIZE(ret, size, len, offset);
463	}
464
465	if (test_bit(ATTR_TIMESTAMP_START, ct->head.set)) {
466		ret = __snprintf_timestamp_delta(buf+offset, len, ct);
467		BUFFER_SIZE(ret, size, len, offset);
468	}
469	if (flags & NFCT_OF_TIMESTAMP) {
470		if (test_bit(ATTR_TIMESTAMP_START, ct->head.set)) {
471			ret = __snprintf_timestamp_start(buf+offset, len, ct);
472			BUFFER_SIZE(ret, size, len, offset);
473		}
474		if (test_bit(ATTR_TIMESTAMP_STOP, ct->head.set)) {
475			ret = __snprintf_timestamp_stop(buf+offset, len, ct);
476			BUFFER_SIZE(ret, size, len, offset);
477		}
478	}
479
480	if (test_bit(ATTR_HELPER_NAME, ct->head.set)) {
481		ret = __snprintf_helper_name(buf+offset, len, ct);
482		BUFFER_SIZE(ret, size, len, offset);
483	}
484
485	if (test_bit(ATTR_USE, ct->head.set)) {
486		ret = __snprintf_use(buf+offset, len, ct);
487		BUFFER_SIZE(ret, size, len, offset);
488	}
489
490	if (flags & NFCT_OF_ID && test_bit(ATTR_ID, ct->head.set)) {
491		ret = __snprintf_id(buf+offset, len, ct);
492		BUFFER_SIZE(ret, size, len, offset);
493	}
494
495	if (map && test_bit(ATTR_CONNLABELS, ct->head.set)) {
496		ret = __snprintf_clabels(buf+offset, len, ct, map);
497		BUFFER_SIZE(ret, size, len, offset);
498	}
499
500	/* Delete the last blank space */
501	size--;
502
503	return size;
504}
505