/*
 * (C) 2005-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include "internal/internal.h"
#include <limits.h>
#include <libmnl/libmnl.h>

static void __parse_ip(const struct nfattr *attr,
		       struct __nfct_tuple *tuple,
		       const int dir,
		       uint32_t *set)
{
	struct nfattr *tb[CTA_IP_MAX];

        nfnl_parse_nested(tb, CTA_IP_MAX, attr);

	if (tb[CTA_IP_V4_SRC-1]) {
		tuple->src.v4 = *(uint32_t *)NFA_DATA(tb[CTA_IP_V4_SRC-1]);
		switch(dir) {
		case __DIR_ORIG:
			set_bit(ATTR_ORIG_IPV4_SRC, set);
			break;
		case __DIR_REPL:
			set_bit(ATTR_REPL_IPV4_SRC, set);
			break;
		case __DIR_MASTER:
			set_bit(ATTR_MASTER_IPV4_SRC, set);
			break;
		}
	}

	if (tb[CTA_IP_V4_DST-1]) {
		tuple->dst.v4 = *(uint32_t *)NFA_DATA(tb[CTA_IP_V4_DST-1]);
		switch(dir) {
		case __DIR_ORIG:
			set_bit(ATTR_ORIG_IPV4_DST, set);
			break;
		case __DIR_REPL:
			set_bit(ATTR_REPL_IPV4_DST, set);
			break;
		case __DIR_MASTER:
			set_bit(ATTR_MASTER_IPV4_DST, set);
			break;
		}
	}

	if (tb[CTA_IP_V6_SRC-1]) {
		memcpy(&tuple->src.v6, NFA_DATA(tb[CTA_IP_V6_SRC-1]), 
		       sizeof(struct in6_addr));
		switch(dir) {
		case __DIR_ORIG:
			set_bit(ATTR_ORIG_IPV6_SRC, set);
			break;
		case __DIR_REPL:
			set_bit(ATTR_REPL_IPV6_SRC, set);
			break;
		case __DIR_MASTER:
			set_bit(ATTR_MASTER_IPV6_SRC, set);
			break;
		}
	}

	if (tb[CTA_IP_V6_DST-1]) {
		memcpy(&tuple->dst.v6, NFA_DATA(tb[CTA_IP_V6_DST-1]),
		       sizeof(struct in6_addr));
		switch(dir) {
		case __DIR_ORIG:
			set_bit(ATTR_ORIG_IPV6_DST, set);
			break;
		case __DIR_REPL:
			set_bit(ATTR_REPL_IPV6_DST, set);
			break;
		case __DIR_MASTER:
			set_bit(ATTR_MASTER_IPV6_DST, set);
			break;
		}
	}
}

static void __parse_proto(const struct nfattr *attr,
			  struct __nfct_tuple *tuple,
		   const int dir,
		   uint32_t *set)
{
	struct nfattr *tb[CTA_PROTO_MAX];

	nfnl_parse_nested(tb, CTA_PROTO_MAX, attr);

	if (tb[CTA_PROTO_NUM-1]) {
		tuple->protonum = *(uint8_t *)NFA_DATA(tb[CTA_PROTO_NUM-1]);
		switch(dir) {
		case __DIR_ORIG:
			set_bit(ATTR_ORIG_L4PROTO, set);
			break;
		case __DIR_REPL:
			set_bit(ATTR_REPL_L4PROTO, set);
			break;
		case __DIR_MASTER:
			set_bit(ATTR_MASTER_L4PROTO, set);
			break;
		}
	}

	if (tb[CTA_PROTO_SRC_PORT-1]) {
		tuple->l4src.tcp.port =
			*(uint16_t *)NFA_DATA(tb[CTA_PROTO_SRC_PORT-1]);
		switch(dir) {
		case __DIR_ORIG:
			set_bit(ATTR_ORIG_PORT_SRC, set);
			break;
		case __DIR_REPL:
			set_bit(ATTR_REPL_PORT_SRC, set);
			break;
		case __DIR_MASTER:
			set_bit(ATTR_MASTER_PORT_SRC, set);
			break;
		}
	}
	
	if (tb[CTA_PROTO_DST_PORT-1]) {
		tuple->l4dst.tcp.port =
			*(uint16_t *)NFA_DATA(tb[CTA_PROTO_DST_PORT-1]);
		switch(dir) {
		case __DIR_ORIG:
			set_bit(ATTR_ORIG_PORT_DST, set);
			break;
		case __DIR_REPL:
			set_bit(ATTR_REPL_PORT_DST, set);
			break;
		case __DIR_MASTER:
			set_bit(ATTR_MASTER_PORT_DST, set);
			break;
		}
	}
	
	if (tb[CTA_PROTO_ICMP_TYPE-1]) {
		tuple->l4dst.icmp.type =
			*(uint8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_TYPE-1]);
		set_bit(ATTR_ICMP_TYPE, set);
	}
	
	if (tb[CTA_PROTO_ICMP_CODE-1]) {
		tuple->l4dst.icmp.code =
			*(uint8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_CODE-1]);
		set_bit(ATTR_ICMP_CODE, set);
	}
	
	if (tb[CTA_PROTO_ICMP_ID-1]) {
		tuple->l4src.icmp.id =
			*(uint16_t *)NFA_DATA(tb[CTA_PROTO_ICMP_ID-1]);
		set_bit(ATTR_ICMP_ID, set);
	}

	if (tb[CTA_PROTO_ICMPV6_TYPE-1]) {
		tuple->l4dst.icmp.type =
			*(uint8_t *)NFA_DATA(tb[CTA_PROTO_ICMPV6_TYPE-1]);
		set_bit(ATTR_ICMP_TYPE, set);
	}
	
	if (tb[CTA_PROTO_ICMPV6_CODE-1]) {
		tuple->l4dst.icmp.code =
			*(uint8_t *)NFA_DATA(tb[CTA_PROTO_ICMPV6_CODE-1]);
		set_bit(ATTR_ICMP_CODE, set);
	}
	
	if (tb[CTA_PROTO_ICMPV6_ID-1]) {
		tuple->l4src.icmp.id =
			*(uint16_t *)NFA_DATA(tb[CTA_PROTO_ICMPV6_ID-1]);
		set_bit(ATTR_ICMP_ID, set);
	}
}

void __parse_tuple(const struct nfattr *attr,
		   struct __nfct_tuple *tuple, 
		   int dir,
		   uint32_t *set)
{
	struct nfattr *tb[CTA_TUPLE_MAX];

	nfnl_parse_nested(tb, CTA_TUPLE_MAX, attr);

	if (tb[CTA_TUPLE_IP-1])
		__parse_ip(tb[CTA_TUPLE_IP-1], tuple, dir, set);
	if (tb[CTA_TUPLE_PROTO-1])
		__parse_proto(tb[CTA_TUPLE_PROTO-1], tuple, dir, set);

	if (tb[CTA_TUPLE_ZONE-1]) {
		tuple->zone = ntohs(*(uint16_t *)NFA_DATA(tb[CTA_TUPLE_ZONE-1]));
		switch(dir) {
		case __DIR_ORIG:
			set_bit(ATTR_ORIG_ZONE, set);
			break;
		case __DIR_REPL:
			set_bit(ATTR_REPL_ZONE, set);
			break;
		}
	}
}

static void __parse_protoinfo_tcp(const struct nfattr *attr, 
				  struct nf_conntrack *ct)
{
	struct nfattr *tb[CTA_PROTOINFO_TCP_MAX];

	nfnl_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr);

	if (tb[CTA_PROTOINFO_TCP_STATE-1]) {
                ct->protoinfo.tcp.state =
                        *(uint8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_STATE-1]);
		set_bit(ATTR_TCP_STATE, ct->head.set);
	}

	if (tb[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL-1]) {
		memcpy(&ct->protoinfo.tcp.wscale[__DIR_ORIG],
		       NFA_DATA(tb[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL-1]),
		       sizeof(uint8_t));
		set_bit(ATTR_TCP_WSCALE_ORIG, ct->head.set);
	}

	if (tb[CTA_PROTOINFO_TCP_WSCALE_REPLY-1]) {
		memcpy(&ct->protoinfo.tcp.wscale[__DIR_REPL],
		       NFA_DATA(tb[CTA_PROTOINFO_TCP_WSCALE_REPLY-1]),
		       sizeof(uint8_t));
		set_bit(ATTR_TCP_WSCALE_REPL, ct->head.set);
	}

	if (tb[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL-1]) {
		memcpy(&ct->protoinfo.tcp.flags[0], 
		       NFA_DATA(tb[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL-1]),
		       sizeof(struct nf_ct_tcp_flags));
		set_bit(ATTR_TCP_FLAGS_ORIG, ct->head.set);
		set_bit(ATTR_TCP_MASK_ORIG, ct->head.set);
	}

	if (tb[CTA_PROTOINFO_TCP_FLAGS_REPLY-1]) {
		memcpy(&ct->protoinfo.tcp.flags[1], 
		       NFA_DATA(tb[CTA_PROTOINFO_TCP_FLAGS_REPLY-1]),
		       sizeof(struct nf_ct_tcp_flags));
		set_bit(ATTR_TCP_FLAGS_REPL, ct->head.set);
		set_bit(ATTR_TCP_MASK_REPL, ct->head.set);
	}
}

static void __parse_protoinfo_sctp(const struct nfattr *attr, 
				   struct nf_conntrack *ct)
{
	struct nfattr *tb[CTA_PROTOINFO_SCTP_MAX];

	nfnl_parse_nested(tb, CTA_PROTOINFO_SCTP_MAX, attr);

	if (tb[CTA_PROTOINFO_SCTP_STATE-1]) {
                ct->protoinfo.sctp.state =
                        *(uint8_t *)NFA_DATA(tb[CTA_PROTOINFO_SCTP_STATE-1]);
		set_bit(ATTR_SCTP_STATE, ct->head.set);
	}

	if (tb[CTA_PROTOINFO_SCTP_VTAG_ORIGINAL-1]) {
		ct->protoinfo.sctp.vtag[__DIR_ORIG] = 
			ntohl(*(uint32_t *)NFA_DATA(tb[CTA_PROTOINFO_SCTP_VTAG_ORIGINAL-1]));
		set_bit(ATTR_SCTP_VTAG_ORIG, ct->head.set);
	}

	if (tb[CTA_PROTOINFO_SCTP_VTAG_REPLY-1]) {
		ct->protoinfo.sctp.vtag[__DIR_REPL] = 
			ntohl(*(uint32_t *)NFA_DATA(tb[CTA_PROTOINFO_SCTP_VTAG_REPLY-1]));
		set_bit(ATTR_SCTP_VTAG_REPL, ct->head.set);
	}

}

static void __parse_protoinfo_dccp(const struct nfattr *attr, 
				   struct nf_conntrack *ct)
{
	struct nfattr *tb[CTA_PROTOINFO_DCCP_MAX];

	nfnl_parse_nested(tb, CTA_PROTOINFO_DCCP_MAX, attr);

	if (tb[CTA_PROTOINFO_DCCP_STATE-1]) {
                ct->protoinfo.dccp.state =
                        *(uint8_t *)NFA_DATA(tb[CTA_PROTOINFO_DCCP_STATE-1]);
		set_bit(ATTR_DCCP_STATE, ct->head.set);
	}
	if (tb[CTA_PROTOINFO_DCCP_ROLE-1]) {
                ct->protoinfo.dccp.role =
                        *(uint8_t *)NFA_DATA(tb[CTA_PROTOINFO_DCCP_ROLE-1]);
		set_bit(ATTR_DCCP_ROLE, ct->head.set);
	}
	if (tb[CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ-1]) {
		uint64_t tmp;
		memcpy(&tmp,
		       NFA_DATA(tb[CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ-1]),
		       sizeof(tmp));
		ct->protoinfo.dccp.handshake_seq = __be64_to_cpu(tmp);
		set_bit(ATTR_DCCP_HANDSHAKE_SEQ, ct->head.set);
	}
}

static void __parse_protoinfo(const struct nfattr *attr,
			      struct nf_conntrack *ct)
{
	struct nfattr *tb[CTA_PROTOINFO_MAX];

	nfnl_parse_nested(tb, CTA_PROTOINFO_MAX, attr);

	if (tb[CTA_PROTOINFO_TCP-1])
		__parse_protoinfo_tcp(tb[CTA_PROTOINFO_TCP-1], ct);

	if (tb[CTA_PROTOINFO_SCTP-1])
		__parse_protoinfo_sctp(tb[CTA_PROTOINFO_SCTP-1], ct);

	if (tb[CTA_PROTOINFO_DCCP-1])
		__parse_protoinfo_dccp(tb[CTA_PROTOINFO_DCCP-1], ct);
}

static void __parse_counters(const struct nfattr *attr,
			     struct nf_conntrack *ct,
			     int dir)
{
	struct nfattr *tb[CTA_COUNTERS_MAX];

	nfnl_parse_nested(tb, CTA_COUNTERS_MAX, attr);
	if (tb[CTA_COUNTERS_PACKETS-1] || tb[CTA_COUNTERS32_PACKETS-1]) {

		if (tb[CTA_COUNTERS32_PACKETS-1])
			ct->counters[dir].packets
				= ntohl(*(uint32_t *)
					NFA_DATA(tb[CTA_COUNTERS32_PACKETS-1]));

		if (tb[CTA_COUNTERS_PACKETS-1]) {
			uint64_t tmp;
			memcpy(&tmp,
			       NFA_DATA(tb[CTA_COUNTERS_PACKETS-1]),
			       sizeof(tmp));
			ct->counters[dir].packets = __be64_to_cpu(tmp);
		}

		switch(dir) {
		case __DIR_ORIG:
			set_bit(ATTR_ORIG_COUNTER_PACKETS, ct->head.set);
			break;
		case __DIR_REPL:
			set_bit(ATTR_REPL_COUNTER_PACKETS, ct->head.set);
			break;
		}
	}
	if (tb[CTA_COUNTERS_BYTES-1] || tb[CTA_COUNTERS32_BYTES-1]) {

		if (tb[CTA_COUNTERS32_BYTES-1])
			ct->counters[dir].bytes
				= ntohl(*(uint32_t *)
					NFA_DATA(tb[CTA_COUNTERS32_BYTES-1]));

		if (tb[CTA_COUNTERS_BYTES-1]) {
			uint64_t tmp;
			memcpy(&tmp,
			       NFA_DATA(tb[CTA_COUNTERS_BYTES-1]),
			       sizeof(tmp));
			ct->counters[dir].bytes = __be64_to_cpu(tmp);
		}

		switch(dir) {
		case __DIR_ORIG:
			set_bit(ATTR_ORIG_COUNTER_BYTES, ct->head.set);
			break;
		case __DIR_REPL:
			set_bit(ATTR_REPL_COUNTER_BYTES, ct->head.set);
			break;
		}
	}
}

static void 
__parse_nat_seq(const struct nfattr *attr, struct nf_conntrack *ct, int dir)
{
	struct nfattr *tb[CTA_NAT_SEQ_MAX];

	nfnl_parse_nested(tb, CTA_NAT_SEQ_MAX, attr);

	if (tb[CTA_NAT_SEQ_CORRECTION_POS-1]) {
		ct->natseq[dir].correction_pos =
			ntohl(*(uint32_t *)NFA_DATA(tb[CTA_NAT_SEQ_CORRECTION_POS-1]));
		switch(dir) {
		case __DIR_ORIG:
			set_bit(ATTR_ORIG_NAT_SEQ_CORRECTION_POS, ct->head.set);
			break;
		case __DIR_REPL:
			set_bit(ATTR_REPL_NAT_SEQ_CORRECTION_POS, ct->head.set);
			break;
		}
	}
					
	if (tb[CTA_NAT_SEQ_OFFSET_BEFORE-1]) {
		ct->natseq[dir].offset_before =
		ntohl(*(uint32_t *)NFA_DATA(tb[CTA_NAT_SEQ_OFFSET_BEFORE-1]));
		switch(dir) {
		case __DIR_ORIG:
			set_bit(ATTR_ORIG_NAT_SEQ_OFFSET_BEFORE, ct->head.set);
			break;
		case __DIR_REPL:
			set_bit(ATTR_REPL_NAT_SEQ_OFFSET_BEFORE, ct->head.set);
			break;
		}
	}

	if (tb[CTA_NAT_SEQ_OFFSET_AFTER-1]) {
		ct->natseq[dir].offset_after =
		ntohl(*(uint32_t *)NFA_DATA(tb[CTA_NAT_SEQ_OFFSET_AFTER-1]));
		switch(dir) {
		case __DIR_ORIG:
			set_bit(ATTR_ORIG_NAT_SEQ_OFFSET_AFTER, ct->head.set);
			break;
		case __DIR_REPL:
			set_bit(ATTR_REPL_NAT_SEQ_OFFSET_AFTER, ct->head.set);
			break;
		}
	}
}

static void __parse_synproxy(const struct nfattr *attr, struct nf_conntrack *ct)
{
	struct nfattr *tb[CTA_SYNPROXY_MAX];

	nfnl_parse_nested(tb, CTA_SYNPROXY_MAX, attr);

	if (tb[CTA_SYNPROXY_ISN - 1]) {
		ct->synproxy.isn =
			ntohl(*(uint32_t *)NFA_DATA(tb[CTA_SYNPROXY_ISN-1]));
		set_bit(ATTR_SYNPROXY_ISN, ct->head.set);
	}

	if (tb[CTA_SYNPROXY_ITS - 1]) {
		ct->synproxy.its =
			ntohl(*(uint32_t *)NFA_DATA(tb[CTA_SYNPROXY_ITS-1]));
		set_bit(ATTR_SYNPROXY_ITS, ct->head.set);
	}

	if (tb[CTA_SYNPROXY_TSOFF - 1]) {
		ct->synproxy.tsoff =
			ntohl(*(uint32_t *)NFA_DATA(tb[CTA_SYNPROXY_TSOFF-1]));
		set_bit(ATTR_SYNPROXY_TSOFF, ct->head.set);
	}
}

static void 
__parse_helper(const struct nfattr *attr, struct nf_conntrack *ct)
{
	struct nfattr *tb[CTA_HELP_MAX];

	nfnl_parse_nested(tb, CTA_HELP_MAX, attr);
	if (!tb[CTA_HELP_NAME-1])
		return;

	strncpy(ct->helper_name, 
		NFA_DATA(tb[CTA_HELP_NAME-1]),
		NFCT_HELPER_NAME_MAX);
	ct->helper_name[NFCT_HELPER_NAME_MAX-1] = '\0';
	set_bit(ATTR_HELPER_NAME, ct->head.set);
}

static void
__parse_secctx(const struct nfattr *attr, struct nf_conntrack *ct)
{
	struct nfattr *tb[CTA_SECCTX_MAX];

	nfnl_parse_nested(tb, CTA_SECCTX_MAX, attr);
	if (!tb[CTA_SECCTX_NAME-1])
		return;

	ct->secctx = strdup(NFA_DATA(tb[CTA_SECCTX_NAME-1]));
	if (ct->secctx)
		set_bit(ATTR_SECCTX, ct->head.set);
}

int __parse_message_type(const struct nlmsghdr *nlh)
{
	uint16_t type = NFNL_MSG_TYPE(nlh->nlmsg_type);
	uint16_t flags = nlh->nlmsg_flags;
	int ret = NFCT_T_UNKNOWN;

	if (type == IPCTNL_MSG_CT_NEW) {
		if (flags & (NLM_F_CREATE|NLM_F_EXCL))
			ret = NFCT_T_NEW;
		else
			ret = NFCT_T_UPDATE;
	} else if (type == IPCTNL_MSG_CT_DELETE)
		ret = NFCT_T_DESTROY;

	return ret;
}

static void
__parse_timestamp(const struct nfattr *attr, struct nf_conntrack *ct)
{
	struct nfattr *tb[CTA_TIMESTAMP_MAX];

	nfnl_parse_nested(tb, CTA_TIMESTAMP_MAX, attr);
	if (tb[CTA_TIMESTAMP_START-1]) {
		uint64_t tmp;
		memcpy(&tmp, NFA_DATA(tb[CTA_TIMESTAMP_START-1]), sizeof(tmp));
		ct->timestamp.start = __be64_to_cpu(tmp);
		set_bit(ATTR_TIMESTAMP_START, ct->head.set);
	}
	if (tb[CTA_TIMESTAMP_STOP-1]) {
		uint64_t tmp;
		memcpy(&tmp, NFA_DATA(tb[CTA_TIMESTAMP_STOP-1]), sizeof(tmp));
		ct->timestamp.stop = __be64_to_cpu(tmp);
		set_bit(ATTR_TIMESTAMP_STOP, ct->head.set);
	}
}

static void
__parse_labels(const struct nfattr *attr, struct nf_conntrack *ct)
{
	struct nfct_bitmask *mask;
	uint16_t len;

	len = NFA_PAYLOAD(attr);
	if (len) {
		mask = nfct_bitmask_new((len * CHAR_BIT) - 1);
		if (!mask)
			return;
		memcpy(mask->bits, NFA_DATA(attr), len);
		nfct_set_attr(ct, ATTR_CONNLABELS, mask);
	}
}

void __parse_conntrack(const struct nlmsghdr *nlh,
		       struct nfattr *cda[],
		       struct nf_conntrack *ct)
{
	struct nfgenmsg *nfhdr = NLMSG_DATA(nlh);

	if (cda[CTA_TUPLE_ORIG-1]) {
		ct->head.orig.l3protonum = nfhdr->nfgen_family;
		set_bit(ATTR_ORIG_L3PROTO, ct->head.set);

		__parse_tuple(cda[CTA_TUPLE_ORIG-1], 
			      &ct->head.orig, __DIR_ORIG, ct->head.set);
	}

	if (cda[CTA_TUPLE_REPLY-1]) {
		ct->repl.l3protonum = nfhdr->nfgen_family;
		set_bit(ATTR_REPL_L3PROTO, ct->head.set);

		__parse_tuple(cda[CTA_TUPLE_REPLY-1], 
			      &ct->repl, __DIR_REPL, ct->head.set);
	}

	if (cda[CTA_TUPLE_MASTER-1]) {
		ct->master.l3protonum = nfhdr->nfgen_family;
		set_bit(ATTR_MASTER_L3PROTO, ct->head.set);

		__parse_tuple(cda[CTA_TUPLE_MASTER-1], 
			      &ct->master, __DIR_MASTER, ct->head.set);
	}

	if (cda[CTA_NAT_SEQ_ADJ_ORIG-1])
		__parse_nat_seq(cda[CTA_NAT_SEQ_ADJ_ORIG-1], ct, __DIR_ORIG);

	if (cda[CTA_NAT_SEQ_ADJ_REPLY-1])
		__parse_nat_seq(cda[CTA_NAT_SEQ_ADJ_REPLY-1], ct, __DIR_REPL);

	if (cda[CTA_STATUS-1]) {
		ct->status = ntohl(*(uint32_t *)NFA_DATA(cda[CTA_STATUS-1]));
		set_bit(ATTR_STATUS, ct->head.set);
	}

	if (cda[CTA_PROTOINFO-1])
		__parse_protoinfo(cda[CTA_PROTOINFO-1], ct);

	if (cda[CTA_TIMEOUT-1]) {
		ct->timeout = ntohl(*(uint32_t *)NFA_DATA(cda[CTA_TIMEOUT-1]));
		set_bit(ATTR_TIMEOUT, ct->head.set);
	}
	
	if (cda[CTA_MARK-1]) {
		ct->mark = ntohl(*(uint32_t *)NFA_DATA(cda[CTA_MARK-1]));
		set_bit(ATTR_MARK, ct->head.set);
	}

	if (cda[CTA_SECMARK-1]) {
		ct->secmark = ntohl(*(uint32_t *)NFA_DATA(cda[CTA_SECMARK-1]));
		set_bit(ATTR_SECMARK, ct->head.set);
	}

	if (cda[CTA_COUNTERS_ORIG-1])
		__parse_counters(cda[CTA_COUNTERS_ORIG-1], ct, __DIR_ORIG);

	if (cda[CTA_COUNTERS_REPLY-1])
		__parse_counters(cda[CTA_COUNTERS_REPLY-1], ct, __DIR_REPL);

	if (cda[CTA_USE-1]) {
		ct->use = ntohl(*(uint32_t *)NFA_DATA(cda[CTA_USE-1]));
		set_bit(ATTR_USE, ct->head.set);
	}

	if (cda[CTA_ID-1]) {
		ct->id = ntohl(*(uint32_t *)NFA_DATA(cda[CTA_ID-1]));
		set_bit(ATTR_ID, ct->head.set);
	}

	if (cda[CTA_HELP-1])
		__parse_helper(cda[CTA_HELP-1], ct);

	if (cda[CTA_ZONE-1]) {
		ct->zone = ntohs(*(uint16_t *)NFA_DATA(cda[CTA_ZONE-1]));
		set_bit(ATTR_ZONE, ct->head.set);
	}

	if (cda[CTA_SECCTX-1])
		__parse_secctx(cda[CTA_SECCTX-1], ct);

	if (cda[CTA_TIMESTAMP-1])
		__parse_timestamp(cda[CTA_TIMESTAMP-1], ct);

	if (cda[CTA_LABELS-1])
		__parse_labels(cda[CTA_LABELS-1], ct);

	if (cda[CTA_SYNPROXY-1])
		__parse_synproxy(cda[CTA_SYNPROXY-1], ct);
}
