1/*
2 * lib/netfilter/ct_obj.c	Conntrack Object
3 *
4 *	This library is free software; you can redistribute it and/or
5 *	modify it under the terms of the GNU Lesser General Public
6 *	License as published by the Free Software Foundation version 2.1
7 *	of the License.
8 *
9 * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
10 * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
11 * Copyright (c) 2007 Secure Computing Corporation
12 */
13
14#include <sys/types.h>
15#include <linux/netfilter/nfnetlink_conntrack.h>
16#include <linux/netfilter/nf_conntrack_common.h>
17#include <linux/netfilter/nf_conntrack_tcp.h>
18
19#include <netlink-local.h>
20#include <netlink/netfilter/nfnl.h>
21#include <netlink/netfilter/ct.h>
22
23/** @cond SKIP */
24#define CT_ATTR_FAMILY		(1UL << 0)
25#define CT_ATTR_PROTO		(1UL << 1)
26
27#define CT_ATTR_TCP_STATE	(1UL << 2)
28
29#define CT_ATTR_STATUS		(1UL << 3)
30#define CT_ATTR_TIMEOUT		(1UL << 4)
31#define CT_ATTR_MARK		(1UL << 5)
32#define CT_ATTR_USE		(1UL << 6)
33#define CT_ATTR_ID		(1UL << 7)
34
35#define CT_ATTR_ORIG_SRC	(1UL << 8)
36#define CT_ATTR_ORIG_DST	(1UL << 9)
37#define CT_ATTR_ORIG_SRC_PORT	(1UL << 10)
38#define CT_ATTR_ORIG_DST_PORT	(1UL << 11)
39#define CT_ATTR_ORIG_ICMP_ID	(1UL << 12)
40#define CT_ATTR_ORIG_ICMP_TYPE	(1UL << 13)
41#define CT_ATTR_ORIG_ICMP_CODE	(1UL << 14)
42#define CT_ATTR_ORIG_PACKETS	(1UL << 15)
43#define CT_ATTR_ORIG_BYTES	(1UL << 16)
44
45#define CT_ATTR_REPL_SRC	(1UL << 17)
46#define CT_ATTR_REPL_DST	(1UL << 18)
47#define CT_ATTR_REPL_SRC_PORT	(1UL << 19)
48#define CT_ATTR_REPL_DST_PORT	(1UL << 20)
49#define CT_ATTR_REPL_ICMP_ID	(1UL << 21)
50#define CT_ATTR_REPL_ICMP_TYPE	(1UL << 22)
51#define CT_ATTR_REPL_ICMP_CODE	(1UL << 23)
52#define CT_ATTR_REPL_PACKETS	(1UL << 24)
53#define CT_ATTR_REPL_BYTES	(1UL << 25)
54/** @endcond */
55
56static void ct_free_data(struct nl_object *c)
57{
58	struct nfnl_ct *ct = (struct nfnl_ct *) c;
59
60	if (ct == NULL)
61		return;
62
63	nl_addr_put(ct->ct_orig.src);
64	nl_addr_put(ct->ct_orig.dst);
65	nl_addr_put(ct->ct_repl.src);
66	nl_addr_put(ct->ct_repl.dst);
67}
68
69static int ct_clone(struct nl_object *_dst, struct nl_object *_src)
70{
71	struct nfnl_ct *dst = (struct nfnl_ct *) _dst;
72	struct nfnl_ct *src = (struct nfnl_ct *) _src;
73	struct nl_addr *addr;
74
75	if (src->ct_orig.src) {
76		addr = nl_addr_clone(src->ct_orig.src);
77		if (!addr)
78			return -NLE_NOMEM;
79		dst->ct_orig.src = addr;
80	}
81
82	if (src->ct_orig.dst) {
83		addr = nl_addr_clone(src->ct_orig.dst);
84		if (!addr)
85			return -NLE_NOMEM;
86		dst->ct_orig.dst = addr;
87	}
88
89	if (src->ct_repl.src) {
90		addr = nl_addr_clone(src->ct_repl.src);
91		if (!addr)
92			return -NLE_NOMEM;
93		dst->ct_repl.src = addr;
94	}
95
96	if (src->ct_repl.dst) {
97		addr = nl_addr_clone(src->ct_repl.dst);
98		if (!addr)
99			return -NLE_NOMEM;
100		dst->ct_repl.dst = addr;
101	}
102
103	return 0;
104}
105
106static void dump_addr(struct nl_dump_params *p, struct nl_addr *addr, int port)
107{
108	char buf[64];
109
110	if (addr)
111		nl_dump(p, "%s", nl_addr2str(addr, buf, sizeof(buf)));
112
113	if (port)
114		nl_dump(p, ":%u ", port);
115	else if (addr)
116		nl_dump(p, " ");
117}
118
119static void dump_icmp(struct nl_dump_params *p, struct nfnl_ct *ct, int reply)
120{
121	if (nfnl_ct_test_icmp_type(ct, reply))
122		nl_dump(p, "icmp type %d ", nfnl_ct_get_icmp_type(ct, reply));
123
124	if (nfnl_ct_test_icmp_type(ct, reply))
125		nl_dump(p, "code %d ", nfnl_ct_get_icmp_code(ct, reply));
126
127	if (nfnl_ct_test_icmp_type(ct, reply))
128		nl_dump(p, "id %d ", nfnl_ct_get_icmp_id(ct, reply));
129}
130
131static void ct_dump_tuples(struct nfnl_ct *ct, struct nl_dump_params *p)
132{
133	struct nl_addr *orig_src, *orig_dst, *reply_src, *reply_dst;
134	int orig_sport = 0, orig_dport = 0, reply_sport = 0, reply_dport = 0;
135	int sync = 0;
136
137	orig_src = nfnl_ct_get_src(ct, 0);
138	orig_dst = nfnl_ct_get_dst(ct, 0);
139	reply_src = nfnl_ct_get_src(ct, 1);
140	reply_dst = nfnl_ct_get_dst(ct, 1);
141
142	if (nfnl_ct_test_src_port(ct, 0))
143		orig_sport = nfnl_ct_get_src_port(ct, 0);
144
145	if (nfnl_ct_test_dst_port(ct, 0))
146		orig_dport = nfnl_ct_get_dst_port(ct, 0);
147
148	if (nfnl_ct_test_src_port(ct, 1))
149		reply_sport = nfnl_ct_get_src_port(ct, 1);
150
151	if (nfnl_ct_test_dst_port(ct, 1))
152		reply_dport = nfnl_ct_get_dst_port(ct, 1);
153
154	if (orig_src && orig_dst && reply_src && reply_dst &&
155	    orig_sport == reply_dport && orig_dport == reply_sport &&
156	    !nl_addr_cmp(orig_src, reply_dst) &&
157	    !nl_addr_cmp(orig_dst, reply_src))
158		sync = 1;
159
160	dump_addr(p, orig_src, orig_sport);
161	nl_dump(p, sync ? "<-> " : "-> ");
162	dump_addr(p, orig_dst, orig_dport);
163	dump_icmp(p, ct, 0);
164
165	if (!sync) {
166		dump_addr(p, reply_src, reply_sport);
167		nl_dump(p, "<- ");
168		dump_addr(p, reply_dst, reply_dport);
169		dump_icmp(p, ct, 1);
170	}
171}
172
173/* Compatible with /proc/net/nf_conntrack */
174static void ct_dump_line(struct nl_object *a, struct nl_dump_params *p)
175{
176	struct nfnl_ct *ct = (struct nfnl_ct *) a;
177	char buf[64];
178
179	nl_new_line(p);
180
181	if (nfnl_ct_test_proto(ct))
182		nl_dump(p, "%s ",
183		  nl_ip_proto2str(nfnl_ct_get_proto(ct), buf, sizeof(buf)));
184
185	if (nfnl_ct_test_tcp_state(ct))
186		nl_dump(p, "%s ",
187			nfnl_ct_tcp_state2str(nfnl_ct_get_tcp_state(ct),
188					      buf, sizeof(buf)));
189
190	ct_dump_tuples(ct, p);
191
192	if (nfnl_ct_test_mark(ct) && nfnl_ct_get_mark(ct))
193		nl_dump(p, "mark %u ", nfnl_ct_get_mark(ct));
194
195	nl_dump(p, "\n");
196}
197
198static void ct_dump_details(struct nl_object *a, struct nl_dump_params *p)
199{
200	struct nfnl_ct *ct = (struct nfnl_ct *) a;
201	char buf[64];
202	int fp = 0;
203
204	ct_dump_line(a, p);
205
206	nl_dump(p, "    id 0x%x ", ct->ct_id);
207	nl_dump_line(p, "family %s ",
208		nl_af2str(ct->ct_family, buf, sizeof(buf)));
209
210	if (nfnl_ct_test_use(ct))
211		nl_dump(p, "refcnt %u ", nfnl_ct_get_use(ct));
212
213	if (nfnl_ct_test_timeout(ct)) {
214		uint64_t timeout_ms = nfnl_ct_get_timeout(ct) * 1000UL;
215		nl_dump(p, "timeout %s ",
216			nl_msec2str(timeout_ms, buf, sizeof(buf)));
217	}
218
219	if (ct->ct_status)
220		nl_dump(p, "<");
221
222#define PRINT_FLAG(str) \
223	{ nl_dump(p, "%s%s", fp++ ? "," : "", (str)); }
224
225	if (ct->ct_status & IPS_EXPECTED)
226		PRINT_FLAG("EXPECTED");
227	if (!(ct->ct_status & IPS_SEEN_REPLY))
228		PRINT_FLAG("NOREPLY");
229	if (ct->ct_status & IPS_ASSURED)
230		PRINT_FLAG("ASSURED");
231	if (!(ct->ct_status & IPS_CONFIRMED))
232		PRINT_FLAG("NOTSENT");
233	if (ct->ct_status & IPS_SRC_NAT)
234		PRINT_FLAG("SNAT");
235	if (ct->ct_status & IPS_DST_NAT)
236		PRINT_FLAG("DNAT");
237	if (ct->ct_status & IPS_SEQ_ADJUST)
238		PRINT_FLAG("SEQADJUST");
239	if (!(ct->ct_status & IPS_SRC_NAT_DONE))
240		PRINT_FLAG("SNAT_INIT");
241	if (!(ct->ct_status & IPS_DST_NAT_DONE))
242		PRINT_FLAG("DNAT_INIT");
243	if (ct->ct_status & IPS_DYING)
244		PRINT_FLAG("DYING");
245	if (ct->ct_status & IPS_FIXED_TIMEOUT)
246		PRINT_FLAG("FIXED_TIMEOUT");
247#undef PRINT_FLAG
248
249	if (ct->ct_status)
250		nl_dump(p, ">");
251	nl_dump(p, "\n");
252}
253
254static void ct_dump_stats(struct nl_object *a, struct nl_dump_params *p)
255{
256	struct nfnl_ct *ct = (struct nfnl_ct *) a;
257	double res;
258	char *unit;
259
260	ct_dump_details(a, p);
261
262	nl_dump_line(p, "        # packets      volume\n");
263
264	res = nl_cancel_down_bytes(nfnl_ct_get_bytes(ct, 1), &unit);
265	nl_dump_line(p, "    rx %10llu %7.2f %s\n",
266		nfnl_ct_get_packets(ct, 1), res, unit);
267
268	res = nl_cancel_down_bytes(nfnl_ct_get_bytes(ct, 0), &unit);
269	nl_dump_line(p, "    tx %10llu %7.2f %s\n",
270		nfnl_ct_get_packets(ct, 0), res, unit);
271}
272
273static int ct_compare(struct nl_object *_a, struct nl_object *_b,
274			uint32_t attrs, int flags)
275{
276	struct nfnl_ct *a = (struct nfnl_ct *) _a;
277	struct nfnl_ct *b = (struct nfnl_ct *) _b;
278	int diff = 0;
279
280#define CT_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, CT_ATTR_##ATTR, a, b, EXPR)
281#define CT_DIFF_VAL(ATTR, FIELD) CT_DIFF(ATTR, a->FIELD != b->FIELD)
282#define CT_DIFF_ADDR(ATTR, FIELD) \
283	((flags & LOOSE_COMPARISON) \
284		? CT_DIFF(ATTR, nl_addr_cmp_prefix(a->FIELD, b->FIELD)) \
285		: CT_DIFF(ATTR, nl_addr_cmp(a->FIELD, b->FIELD)))
286
287	diff |= CT_DIFF_VAL(FAMILY,		ct_family);
288	diff |= CT_DIFF_VAL(PROTO,		ct_proto);
289	diff |= CT_DIFF_VAL(TCP_STATE,		ct_protoinfo.tcp.state);
290	diff |= CT_DIFF_VAL(TIMEOUT,		ct_timeout);
291	diff |= CT_DIFF_VAL(MARK,		ct_mark);
292	diff |= CT_DIFF_VAL(USE,		ct_use);
293	diff |= CT_DIFF_VAL(ID,			ct_id);
294	diff |= CT_DIFF_ADDR(ORIG_SRC,		ct_orig.src);
295	diff |= CT_DIFF_ADDR(ORIG_DST,		ct_orig.dst);
296	diff |= CT_DIFF_VAL(ORIG_SRC_PORT,	ct_orig.proto.port.src);
297	diff |= CT_DIFF_VAL(ORIG_DST_PORT,	ct_orig.proto.port.dst);
298	diff |= CT_DIFF_VAL(ORIG_ICMP_ID,	ct_orig.proto.icmp.id);
299	diff |= CT_DIFF_VAL(ORIG_ICMP_TYPE,	ct_orig.proto.icmp.type);
300	diff |= CT_DIFF_VAL(ORIG_ICMP_CODE,	ct_orig.proto.icmp.code);
301	diff |= CT_DIFF_VAL(ORIG_PACKETS,	ct_orig.packets);
302	diff |= CT_DIFF_VAL(ORIG_BYTES,		ct_orig.bytes);
303	diff |= CT_DIFF_ADDR(REPL_SRC,		ct_repl.src);
304	diff |= CT_DIFF_ADDR(REPL_DST,		ct_repl.dst);
305	diff |= CT_DIFF_VAL(REPL_SRC_PORT,	ct_repl.proto.port.src);
306	diff |= CT_DIFF_VAL(REPL_DST_PORT,	ct_repl.proto.port.dst);
307	diff |= CT_DIFF_VAL(REPL_ICMP_ID,	ct_repl.proto.icmp.id);
308	diff |= CT_DIFF_VAL(REPL_ICMP_TYPE,	ct_repl.proto.icmp.type);
309	diff |= CT_DIFF_VAL(REPL_ICMP_CODE,	ct_repl.proto.icmp.code);
310	diff |= CT_DIFF_VAL(REPL_PACKETS,	ct_repl.packets);
311	diff |= CT_DIFF_VAL(REPL_BYTES,		ct_repl.bytes);
312
313	if (flags & LOOSE_COMPARISON)
314		diff |= CT_DIFF(STATUS, (a->ct_status ^ b->ct_status) &
315					b->ct_status_mask);
316	else
317		diff |= CT_DIFF(STATUS, a->ct_status != b->ct_status);
318
319#undef CT_DIFF
320#undef CT_DIFF_VAL
321#undef CT_DIFF_ADDR
322
323	return diff;
324}
325
326static struct trans_tbl ct_attrs[] = {
327	__ADD(CT_ATTR_FAMILY,		family)
328	__ADD(CT_ATTR_PROTO,		proto)
329	__ADD(CT_ATTR_TCP_STATE,	tcpstate)
330	__ADD(CT_ATTR_STATUS,		status)
331	__ADD(CT_ATTR_TIMEOUT,		timeout)
332	__ADD(CT_ATTR_MARK,		mark)
333	__ADD(CT_ATTR_USE,		use)
334	__ADD(CT_ATTR_ID,		id)
335	__ADD(CT_ATTR_ORIG_SRC,		origsrc)
336	__ADD(CT_ATTR_ORIG_DST,		origdst)
337	__ADD(CT_ATTR_ORIG_SRC_PORT,	origsrcport)
338	__ADD(CT_ATTR_ORIG_DST_PORT,	origdstport)
339	__ADD(CT_ATTR_ORIG_ICMP_ID,	origicmpid)
340	__ADD(CT_ATTR_ORIG_ICMP_TYPE,	origicmptype)
341	__ADD(CT_ATTR_ORIG_ICMP_CODE,	origicmpcode)
342	__ADD(CT_ATTR_ORIG_PACKETS,	origpackets)
343	__ADD(CT_ATTR_ORIG_BYTES,	origbytes)
344	__ADD(CT_ATTR_REPL_SRC,		replysrc)
345	__ADD(CT_ATTR_REPL_DST,		replydst)
346	__ADD(CT_ATTR_REPL_SRC_PORT,	replysrcport)
347	__ADD(CT_ATTR_REPL_DST_PORT,	replydstport)
348	__ADD(CT_ATTR_REPL_ICMP_ID,	replyicmpid)
349	__ADD(CT_ATTR_REPL_ICMP_TYPE,	replyicmptype)
350	__ADD(CT_ATTR_REPL_ICMP_CODE,	replyicmpcode)
351	__ADD(CT_ATTR_REPL_PACKETS,	replypackets)
352	__ADD(CT_ATTR_REPL_BYTES,	replybytes)
353};
354
355static char *ct_attrs2str(int attrs, char *buf, size_t len)
356{
357	return __flags2str(attrs, buf, len, ct_attrs, ARRAY_SIZE(ct_attrs));
358}
359
360/**
361 * @name Allocation/Freeing
362 * @{
363 */
364
365struct nfnl_ct *nfnl_ct_alloc(void)
366{
367	return (struct nfnl_ct *) nl_object_alloc(&ct_obj_ops);
368}
369
370void nfnl_ct_get(struct nfnl_ct *ct)
371{
372	nl_object_get((struct nl_object *) ct);
373}
374
375void nfnl_ct_put(struct nfnl_ct *ct)
376{
377	nl_object_put((struct nl_object *) ct);
378}
379
380/** @} */
381
382/**
383 * @name Attributes
384 * @{
385 */
386
387void nfnl_ct_set_family(struct nfnl_ct *ct, uint8_t family)
388{
389	ct->ct_family = family;
390	ct->ce_mask |= CT_ATTR_FAMILY;
391}
392
393uint8_t nfnl_ct_get_family(const struct nfnl_ct *ct)
394{
395	if (ct->ce_mask & CT_ATTR_FAMILY)
396		return ct->ct_family;
397	else
398		return AF_UNSPEC;
399}
400
401void nfnl_ct_set_proto(struct nfnl_ct *ct, uint8_t proto)
402{
403	ct->ct_proto = proto;
404	ct->ce_mask |= CT_ATTR_PROTO;
405}
406
407int nfnl_ct_test_proto(const struct nfnl_ct *ct)
408{
409	return !!(ct->ce_mask & CT_ATTR_PROTO);
410}
411
412uint8_t nfnl_ct_get_proto(const struct nfnl_ct *ct)
413{
414	return ct->ct_proto;
415}
416
417void nfnl_ct_set_tcp_state(struct nfnl_ct *ct, uint8_t state)
418{
419	ct->ct_protoinfo.tcp.state = state;
420	ct->ce_mask |= CT_ATTR_TCP_STATE;
421}
422
423int nfnl_ct_test_tcp_state(const struct nfnl_ct *ct)
424{
425	return !!(ct->ce_mask & CT_ATTR_TCP_STATE);
426}
427
428uint8_t nfnl_ct_get_tcp_state(const struct nfnl_ct *ct)
429{
430	return ct->ct_protoinfo.tcp.state;
431}
432
433static struct trans_tbl tcp_states[] = {
434	__ADD(TCP_CONNTRACK_NONE,NONE)
435	__ADD(TCP_CONNTRACK_SYN_SENT,SYN_SENT)
436	__ADD(TCP_CONNTRACK_SYN_RECV,SYN_RECV)
437	__ADD(TCP_CONNTRACK_ESTABLISHED,ESTABLISHED)
438	__ADD(TCP_CONNTRACK_FIN_WAIT,FIN_WAIT)
439	__ADD(TCP_CONNTRACK_CLOSE_WAIT,CLOSE_WAIT)
440	__ADD(TCP_CONNTRACK_LAST_ACK,LAST_ACK)
441	__ADD(TCP_CONNTRACK_TIME_WAIT,TIME_WAIT)
442	__ADD(TCP_CONNTRACK_CLOSE,CLOSE)
443	__ADD(TCP_CONNTRACK_LISTEN,LISTEN)
444};
445
446char *nfnl_ct_tcp_state2str(uint8_t state, char *buf, size_t len)
447{
448	return __type2str(state, buf, len, tcp_states, ARRAY_SIZE(tcp_states));
449}
450
451int nfnl_ct_str2tcp_state(const char *name)
452{
453        return __str2type(name, tcp_states, ARRAY_SIZE(tcp_states));
454}
455
456void nfnl_ct_set_status(struct nfnl_ct *ct, uint32_t status)
457{
458	ct->ct_status_mask |= status;
459	ct->ct_status |= status;
460	ct->ce_mask |= CT_ATTR_STATUS;
461}
462
463void nfnl_ct_unset_status(struct nfnl_ct *ct, uint32_t status)
464{
465	ct->ct_status_mask |= status;
466	ct->ct_status &= ~status;
467	ct->ce_mask |= CT_ATTR_STATUS;
468}
469
470uint32_t nfnl_ct_get_status(const struct nfnl_ct *ct)
471{
472	return ct->ct_status;
473}
474
475static struct trans_tbl status_flags[] = {
476	__ADD(IPS_EXPECTED, expected)
477	__ADD(IPS_SEEN_REPLY, seen_reply)
478	__ADD(IPS_ASSURED, assured)
479	__ADD(IPS_CONFIRMED, confirmed)
480	__ADD(IPS_SRC_NAT, snat)
481	__ADD(IPS_DST_NAT, dnat)
482	__ADD(IPS_SEQ_ADJUST, seqadjust)
483	__ADD(IPS_SRC_NAT_DONE, snat_done)
484	__ADD(IPS_DST_NAT_DONE, dnat_done)
485	__ADD(IPS_DYING, dying)
486	__ADD(IPS_FIXED_TIMEOUT, fixed_timeout)
487};
488
489char * nfnl_ct_status2str(int flags, char *buf, size_t len)
490{
491	return __flags2str(flags, buf, len, status_flags,
492			   ARRAY_SIZE(status_flags));
493}
494
495int nfnl_ct_str2status(const char *name)
496{
497	return __str2flags(name, status_flags, ARRAY_SIZE(status_flags));
498}
499
500void nfnl_ct_set_timeout(struct nfnl_ct *ct, uint32_t timeout)
501{
502	ct->ct_timeout = timeout;
503	ct->ce_mask |= CT_ATTR_TIMEOUT;
504}
505
506int nfnl_ct_test_timeout(const struct nfnl_ct *ct)
507{
508	return !!(ct->ce_mask & CT_ATTR_TIMEOUT);
509}
510
511uint32_t nfnl_ct_get_timeout(const struct nfnl_ct *ct)
512{
513	return ct->ct_timeout;
514}
515
516void nfnl_ct_set_mark(struct nfnl_ct *ct, uint32_t mark)
517{
518	ct->ct_mark = mark;
519	ct->ce_mask |= CT_ATTR_MARK;
520}
521
522int nfnl_ct_test_mark(const struct nfnl_ct *ct)
523{
524	return !!(ct->ce_mask & CT_ATTR_MARK);
525}
526
527uint32_t nfnl_ct_get_mark(const struct nfnl_ct *ct)
528{
529	return ct->ct_mark;
530}
531
532void nfnl_ct_set_use(struct nfnl_ct *ct, uint32_t use)
533{
534	ct->ct_use = use;
535	ct->ce_mask |= CT_ATTR_USE;
536}
537
538int nfnl_ct_test_use(const struct nfnl_ct *ct)
539{
540	return !!(ct->ce_mask & CT_ATTR_USE);
541}
542
543uint32_t nfnl_ct_get_use(const struct nfnl_ct *ct)
544{
545	return ct->ct_use;
546}
547
548void nfnl_ct_set_id(struct nfnl_ct *ct, uint32_t id)
549{
550	ct->ct_id = id;
551	ct->ce_mask |= CT_ATTR_ID;
552}
553
554int nfnl_ct_test_id(const struct nfnl_ct *ct)
555{
556	return !!(ct->ce_mask & CT_ATTR_ID);
557}
558
559uint32_t nfnl_ct_get_id(const struct nfnl_ct *ct)
560{
561	return ct->ct_id;
562}
563
564static int ct_set_addr(struct nfnl_ct *ct, struct nl_addr *addr,
565		int attr, struct nl_addr ** ct_addr)
566{
567	if (ct->ce_mask & CT_ATTR_FAMILY) {
568		if (addr->a_family != ct->ct_family)
569			return -NLE_AF_MISMATCH;
570	} else
571		nfnl_ct_set_family(ct, addr->a_family);
572
573	if (*ct_addr)
574		nl_addr_put(*ct_addr);
575
576	nl_addr_get(addr);
577	*ct_addr = addr;
578	ct->ce_mask |= attr;
579
580	return 0;
581}
582
583int nfnl_ct_set_src(struct nfnl_ct *ct, int repl, struct nl_addr *addr)
584{
585	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
586	int attr = repl ? CT_ATTR_REPL_SRC : CT_ATTR_ORIG_SRC;
587	return ct_set_addr(ct, addr, attr, &dir->src);
588}
589
590int nfnl_ct_set_dst(struct nfnl_ct *ct, int repl, struct nl_addr *addr)
591{
592	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
593	int attr = repl ? CT_ATTR_REPL_DST : CT_ATTR_ORIG_DST;
594	return ct_set_addr(ct, addr, attr, &dir->dst);
595}
596
597struct nl_addr *nfnl_ct_get_src(const struct nfnl_ct *ct, int repl)
598{
599	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
600	int attr = repl ? CT_ATTR_REPL_SRC : CT_ATTR_ORIG_SRC;
601	if (!(ct->ce_mask & attr))
602		return NULL;
603	return dir->src;
604}
605
606struct nl_addr *nfnl_ct_get_dst(const struct nfnl_ct *ct, int repl)
607{
608	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
609	int attr = repl ? CT_ATTR_REPL_DST : CT_ATTR_ORIG_DST;
610	if (!(ct->ce_mask & attr))
611		return NULL;
612	return dir->dst;
613}
614
615void nfnl_ct_set_src_port(struct nfnl_ct *ct, int repl, uint16_t port)
616{
617	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
618	int attr = repl ? CT_ATTR_REPL_SRC_PORT : CT_ATTR_ORIG_SRC_PORT;
619
620	dir->proto.port.src = port;
621	ct->ce_mask |= attr;
622}
623
624int nfnl_ct_test_src_port(const struct nfnl_ct *ct, int repl)
625{
626	int attr = repl ? CT_ATTR_REPL_SRC_PORT : CT_ATTR_ORIG_SRC_PORT;
627	return !!(ct->ce_mask & attr);
628}
629
630uint16_t nfnl_ct_get_src_port(const struct nfnl_ct *ct, int repl)
631{
632	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
633
634	return dir->proto.port.src;
635}
636
637void nfnl_ct_set_dst_port(struct nfnl_ct *ct, int repl, uint16_t port)
638{
639	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
640	int attr = repl ? CT_ATTR_REPL_DST_PORT : CT_ATTR_ORIG_DST_PORT;
641
642	dir->proto.port.dst = port;
643	ct->ce_mask |= attr;
644}
645
646int nfnl_ct_test_dst_port(const struct nfnl_ct *ct, int repl)
647{
648	int attr = repl ? CT_ATTR_REPL_DST_PORT : CT_ATTR_ORIG_DST_PORT;
649	return !!(ct->ce_mask & attr);
650}
651
652uint16_t nfnl_ct_get_dst_port(const struct nfnl_ct *ct, int repl)
653{
654	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
655
656	return dir->proto.port.dst;
657}
658
659void nfnl_ct_set_icmp_id(struct nfnl_ct *ct, int repl, uint16_t id)
660{
661	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
662	int attr = repl ? CT_ATTR_REPL_ICMP_ID : CT_ATTR_ORIG_ICMP_ID;
663
664	dir->proto.icmp.id = id;
665	ct->ce_mask |= attr;
666}
667
668int nfnl_ct_test_icmp_id(const struct nfnl_ct *ct, int repl)
669{
670	int attr = repl ? CT_ATTR_REPL_ICMP_ID : CT_ATTR_ORIG_ICMP_ID;
671	return !!(ct->ce_mask & attr);
672}
673
674uint16_t nfnl_ct_get_icmp_id(const struct nfnl_ct *ct, int repl)
675{
676	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
677
678	return dir->proto.icmp.id;
679}
680
681void nfnl_ct_set_icmp_type(struct nfnl_ct *ct, int repl, uint8_t type)
682{
683	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
684	int attr = repl ? CT_ATTR_REPL_ICMP_TYPE : CT_ATTR_ORIG_ICMP_TYPE;
685
686	dir->proto.icmp.type = type;
687	ct->ce_mask |= attr;
688}
689
690int nfnl_ct_test_icmp_type(const struct nfnl_ct *ct, int repl)
691{
692	int attr = repl ? CT_ATTR_REPL_ICMP_TYPE : CT_ATTR_ORIG_ICMP_TYPE;
693	return !!(ct->ce_mask & attr);
694}
695
696uint8_t nfnl_ct_get_icmp_type(const struct nfnl_ct *ct, int repl)
697{
698	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
699
700	return dir->proto.icmp.type;
701}
702
703void nfnl_ct_set_icmp_code(struct nfnl_ct *ct, int repl, uint8_t code)
704{
705	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
706	int attr = repl ? CT_ATTR_REPL_ICMP_CODE : CT_ATTR_ORIG_ICMP_CODE;
707
708	dir->proto.icmp.code = code;
709	ct->ce_mask |= attr;
710}
711
712int nfnl_ct_test_icmp_code(const struct nfnl_ct *ct, int repl)
713{
714	int attr = repl ? CT_ATTR_REPL_ICMP_CODE : CT_ATTR_ORIG_ICMP_CODE;
715	return !!(ct->ce_mask & attr);
716}
717
718uint8_t nfnl_ct_get_icmp_code(const struct nfnl_ct *ct, int repl)
719{
720	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
721
722	return dir->proto.icmp.code;
723}
724
725void nfnl_ct_set_packets(struct nfnl_ct *ct, int repl, uint64_t packets)
726{
727	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
728	int attr = repl ? CT_ATTR_REPL_PACKETS : CT_ATTR_ORIG_PACKETS;
729
730	dir->packets = packets;
731	ct->ce_mask |= attr;
732}
733
734int nfnl_ct_test_packets(const struct nfnl_ct *ct, int repl)
735{
736	int attr = repl ? CT_ATTR_REPL_PACKETS : CT_ATTR_ORIG_PACKETS;
737	return !!(ct->ce_mask & attr);
738}
739
740uint64_t nfnl_ct_get_packets(const struct nfnl_ct *ct, int repl)
741{
742	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
743
744	return dir->packets;
745}
746
747void nfnl_ct_set_bytes(struct nfnl_ct *ct, int repl, uint64_t bytes)
748{
749	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
750	int attr = repl ? CT_ATTR_REPL_BYTES : CT_ATTR_ORIG_BYTES;
751
752	dir->bytes = bytes;
753	ct->ce_mask |= attr;
754}
755
756int nfnl_ct_test_bytes(const struct nfnl_ct *ct, int repl)
757{
758	int attr = repl ? CT_ATTR_REPL_BYTES : CT_ATTR_ORIG_BYTES;
759	return !!(ct->ce_mask & attr);
760}
761
762uint64_t nfnl_ct_get_bytes(const struct nfnl_ct *ct, int repl)
763{
764	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
765
766	return dir->bytes;
767}
768
769/** @} */
770
771struct nl_object_ops ct_obj_ops = {
772	.oo_name		= "netfilter/ct",
773	.oo_size		= sizeof(struct nfnl_ct),
774	.oo_free_data		= ct_free_data,
775	.oo_clone		= ct_clone,
776	.oo_dump = {
777	    [NL_DUMP_LINE]	= ct_dump_line,
778	    [NL_DUMP_DETAILS]	= ct_dump_details,
779	    [NL_DUMP_STATS]	= ct_dump_stats,
780	},
781	.oo_compare		= ct_compare,
782	.oo_attrs2str		= ct_attrs2str,
783};
784
785/** @} */
786