From 95b3bf6576165e1bbe23d55d5f15b7ebf9c98dc4 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 13 Apr 2012 13:13:34 +0200 Subject: conntrack: add new API to build/parse ctnetlink messages using libmnl This patch adds support to build and to parse netlink messages from/to one user-space nf_conntrack object. It uses libmnl, thus libnetfilter_conntrack now depends on this library. This is the first patch in the direction of removing the dependency on the veteran libnfnetlink. I have decided to update LIBVERSION in this patch. I know it's recommended to do this before releasing the software. I prefer to do this so snapshot packages get the correct LIBVERSION. Signed-off-by: Pablo Neira Ayuso --- Make_global.am | 2 +- configure.ac | 1 + .../libnetfilter_conntrack.h | 6 + src/Makefile.am | 2 +- src/conntrack/Makefile.am | 1 + src/conntrack/build_mnl.c | 474 +++++++++++ src/conntrack/parse_mnl.c | 934 +++++++++++++++++++++ 7 files changed, 1418 insertions(+), 2 deletions(-) create mode 100644 src/conntrack/build_mnl.c create mode 100644 src/conntrack/parse_mnl.c diff --git a/Make_global.am b/Make_global.am index afcb270..557900d 100644 --- a/Make_global.am +++ b/Make_global.am @@ -5,4 +5,4 @@ LIBVERSION=6:0:3 AM_CPPFLAGS = -I$(top_srcdir)/include -AM_CFLAGS = -Wall ${LIBNFNETLINK_CFLAGS} +AM_CFLAGS = -Wall ${LIBNFNETLINK_CFLAGS} ${LIBMNL_CFLAGS} diff --git a/configure.ac b/configure.ac index ba7a6f3..6c75c13 100644 --- a/configure.ac +++ b/configure.ac @@ -24,6 +24,7 @@ esac dnl Dependencies PKG_CHECK_MODULES([LIBNFNETLINK], [libnfnetlink >= 1.0.0]) +PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.3]) AC_CHECK_HEADERS(arpa/inet.h) dnl Check for inet_ntop diff --git a/include/libnetfilter_conntrack/libnetfilter_conntrack.h b/include/libnetfilter_conntrack/libnetfilter_conntrack.h index 5518418..87c73a3 100644 --- a/include/libnetfilter_conntrack/libnetfilter_conntrack.h +++ b/include/libnetfilter_conntrack/libnetfilter_conntrack.h @@ -533,6 +533,12 @@ int nfct_build_query(struct nfnl_subsys_handle *ssh, void *req, unsigned int size); +/* New low level API: netlink functions */ + +extern int nfct_nlmsg_build(struct nlmsghdr *nlh, const struct nf_conntrack *ct); +extern int nfct_nlmsg_parse(const struct nlmsghdr *nlh, struct nf_conntrack *ct); +extern int nfct_payload_parse(const void *payload, size_t payload_len, uint16_t l3num, struct nf_conntrack *ct); + /* * NEW expectation API */ diff --git a/src/Makefile.am b/src/Makefile.am index a6d3343..7d6c905 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,7 +10,7 @@ lib_LTLIBRARIES = libnetfilter_conntrack.la libnetfilter_conntrack_la_LIBADD = conntrack/libnfconntrack.la \ expect/libnfexpect.la \ - ${LIBNFNETLINK_LIBS} + ${LIBNFNETLINK_LIBS} ${LIBMNL_LIBS} libnetfilter_conntrack_la_LDFLAGS = -Wc,-nostartfiles -lnfnetlink -ldl \ -version-info $(LIBVERSION) libnetfilter_conntrack_la_SOURCES = main.c callback.c diff --git a/src/conntrack/Makefile.am b/src/conntrack/Makefile.am index 3f7fb64..01fed53 100644 --- a/src/conntrack/Makefile.am +++ b/src/conntrack/Makefile.am @@ -5,6 +5,7 @@ noinst_LTLIBRARIES = libnfconntrack.la libnfconntrack_la_SOURCES = api.c \ getter.c setter.c \ parse.c build.c \ + parse_mnl.c build_mnl.c \ snprintf.c \ snprintf_default.c snprintf_xml.c \ objopt.c \ diff --git a/src/conntrack/build_mnl.c b/src/conntrack/build_mnl.c new file mode 100644 index 0000000..7aceaf1 --- /dev/null +++ b/src/conntrack/build_mnl.c @@ -0,0 +1,474 @@ +/* + * (C) 2005-2012 by Pablo Neira Ayuso + * + * 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. + * + * This code has been sponsored by Vyatta Inc. + */ + +#include "internal/internal.h" +#include + +static int +nfct_build_tuple_ip(struct nlmsghdr *nlh, const struct __nfct_tuple *t) +{ + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, CTA_TUPLE_IP); + if (nest == NULL) + return -1; + + switch(t->l3protonum) { + case AF_INET: + mnl_attr_put_u32(nlh, CTA_IP_V4_SRC, t->src.v4); + mnl_attr_put_u32(nlh, CTA_IP_V4_DST, t->dst.v4); + break; + case AF_INET6: + mnl_attr_put(nlh, CTA_IP_V6_SRC, sizeof(struct in6_addr), + &t->src.v6); + mnl_attr_put(nlh, CTA_IP_V6_DST, sizeof(struct in6_addr), + &t->dst.v6); + break; + default: + mnl_attr_nest_cancel(nlh, nest); + return -1; + } + mnl_attr_nest_end(nlh, nest); + return 0; +} + +static int +nfct_build_tuple_proto(struct nlmsghdr *nlh, const struct __nfct_tuple *t) +{ + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, CTA_TUPLE_PROTO); + if (nest == NULL) + return -1; + + mnl_attr_put_u8(nlh, CTA_PROTO_NUM, t->protonum); + + switch(t->protonum) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPPROTO_SCTP: + case IPPROTO_DCCP: + case IPPROTO_GRE: + case IPPROTO_UDPLITE: + mnl_attr_put_u16(nlh, CTA_PROTO_SRC_PORT, t->l4src.tcp.port); + mnl_attr_put_u16(nlh, CTA_PROTO_DST_PORT, t->l4dst.tcp.port); + break; + case IPPROTO_ICMP: + mnl_attr_put_u8(nlh, CTA_PROTO_ICMP_CODE, t->l4dst.icmp.code); + mnl_attr_put_u8(nlh, CTA_PROTO_ICMP_TYPE, t->l4dst.icmp.type); + mnl_attr_put_u16(nlh, CTA_PROTO_ICMP_ID, t->l4src.icmp.id); + break; + case IPPROTO_ICMPV6: + mnl_attr_put_u8(nlh, CTA_PROTO_ICMPV6_CODE, t->l4dst.icmp.code); + mnl_attr_put_u8(nlh, CTA_PROTO_ICMPV6_TYPE, t->l4dst.icmp.type); + mnl_attr_put_u16(nlh, CTA_PROTO_ICMPV6_ID, t->l4src.icmp.id); + break; + default: + mnl_attr_nest_cancel(nlh, nest); + return -1; + } + mnl_attr_nest_end(nlh, nest); + return 0; +} + +static int +nfct_build_tuple(struct nlmsghdr *nlh, const struct __nfct_tuple *t, int type) +{ + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, type); + if (nest == NULL) + return -1; + + if (nfct_build_tuple_ip(nlh, t) < 0) + goto err; + if (nfct_build_tuple_proto(nlh, t) < 0) + goto err; + + mnl_attr_nest_end(nlh, nest); + return 0; +err: + mnl_attr_nest_cancel(nlh, nest); + return -1; +} + +static int +nfct_build_protoinfo(struct nlmsghdr *nlh, const struct nf_conntrack *ct) +{ + struct nlattr *nest, *nest_proto; + + switch(ct->head.orig.protonum) { + case IPPROTO_TCP: + /* Preliminary attribute check to avoid sending an empty + * CTA_PROTOINFO_TCP nest, which results in EINVAL in + * Linux kernel <= 2.6.25. */ + if (!(test_bit(ATTR_TCP_STATE, ct->head.set) || + test_bit(ATTR_TCP_FLAGS_ORIG, ct->head.set) || + test_bit(ATTR_TCP_FLAGS_REPL, ct->head.set) || + test_bit(ATTR_TCP_MASK_ORIG, ct->head.set) || + test_bit(ATTR_TCP_MASK_REPL, ct->head.set) || + test_bit(ATTR_TCP_WSCALE_ORIG, ct->head.set) || + test_bit(ATTR_TCP_WSCALE_REPL, ct->head.set))) { + break; + } + nest = mnl_attr_nest_start(nlh, CTA_PROTOINFO); + nest_proto = mnl_attr_nest_start(nlh, CTA_PROTOINFO_TCP); + if (test_bit(ATTR_TCP_STATE, ct->head.set)) { + mnl_attr_put_u8(nlh, CTA_PROTOINFO_TCP_STATE, + ct->protoinfo.tcp.state); + } + if (test_bit(ATTR_TCP_FLAGS_ORIG, ct->head.set) && + test_bit(ATTR_TCP_MASK_ORIG, ct->head.set)) { + mnl_attr_put(nlh, CTA_PROTOINFO_TCP_FLAGS_ORIGINAL, + sizeof(struct nf_ct_tcp_flags), + &ct->protoinfo.tcp.flags[0]); + } + if (test_bit(ATTR_TCP_FLAGS_REPL, ct->head.set) && + test_bit(ATTR_TCP_MASK_REPL, ct->head.set)) { + mnl_attr_put(nlh, CTA_PROTOINFO_TCP_FLAGS_REPLY, + sizeof(struct nf_ct_tcp_flags), + &ct->protoinfo.tcp.flags[1]); + } + if (test_bit(ATTR_TCP_WSCALE_ORIG, ct->head.set)) { + mnl_attr_put_u8(nlh, CTA_PROTOINFO_TCP_WSCALE_ORIGINAL, + ct->protoinfo.tcp.wscale[__DIR_ORIG]); + } + if (test_bit(ATTR_TCP_WSCALE_REPL, ct->head.set)) { + mnl_attr_put_u8(nlh, CTA_PROTOINFO_TCP_WSCALE_REPLY, + ct->protoinfo.tcp.wscale[__DIR_REPL]); + } + mnl_attr_nest_end(nlh, nest_proto); + mnl_attr_nest_end(nlh, nest); + break; + case IPPROTO_SCTP: + /* See comment above on TCP. */ + if (!(test_bit(ATTR_SCTP_STATE, ct->head.set) || + test_bit(ATTR_SCTP_VTAG_ORIG, ct->head.set) || + test_bit(ATTR_SCTP_VTAG_REPL, ct->head.set))) { + break; + } + nest = mnl_attr_nest_start(nlh, CTA_PROTOINFO); + nest_proto = mnl_attr_nest_start(nlh, CTA_PROTOINFO_SCTP); + + if (test_bit(ATTR_SCTP_STATE, ct->head.set)) { + mnl_attr_put_u8(nlh, CTA_PROTOINFO_SCTP_STATE, + ct->protoinfo.sctp.state); + } + if (test_bit(ATTR_SCTP_VTAG_ORIG, ct->head.set)) { + mnl_attr_put_u32(nlh, CTA_PROTOINFO_SCTP_VTAG_ORIGINAL, + htonl(ct->protoinfo.sctp.vtag[__DIR_ORIG])); + } + if (test_bit(ATTR_SCTP_VTAG_REPL, ct->head.set)) { + mnl_attr_put_u32(nlh, CTA_PROTOINFO_SCTP_VTAG_REPLY, + htonl(ct->protoinfo.sctp.vtag[__DIR_REPL])); + } + mnl_attr_nest_end(nlh, nest_proto); + mnl_attr_nest_end(nlh, nest); + break; + case IPPROTO_DCCP: + /* See comment above on TCP. */ + if (!(test_bit(ATTR_DCCP_STATE, ct->head.set) || + test_bit(ATTR_DCCP_ROLE, ct->head.set) || + test_bit(ATTR_DCCP_HANDSHAKE_SEQ, ct->head.set))) { + break; + } + nest = mnl_attr_nest_start(nlh, CTA_PROTOINFO); + nest_proto = mnl_attr_nest_start(nlh, CTA_PROTOINFO_DCCP); + if (test_bit(ATTR_DCCP_STATE, ct->head.set)) { + mnl_attr_put_u8(nlh, CTA_PROTOINFO_DCCP_STATE, + ct->protoinfo.dccp.state); + } + if (test_bit(ATTR_DCCP_ROLE, ct->head.set)) { + mnl_attr_put_u8(nlh, CTA_PROTOINFO_DCCP_ROLE, + ct->protoinfo.dccp.role); + } + if (test_bit(ATTR_DCCP_HANDSHAKE_SEQ, ct->head.set)) { + uint64_t handshake_seq = + be64toh(ct->protoinfo.dccp.handshake_seq); + + mnl_attr_put_u64(nlh, CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ, + handshake_seq); + } + mnl_attr_nest_end(nlh, nest_proto); + mnl_attr_nest_end(nlh, nest); + default: + break; + } + return 0; +} + +static int +nfct_nat_seq_adj(struct nlmsghdr *nlh, const struct nf_conntrack *ct, int dir) +{ + mnl_attr_put_u32(nlh, CTA_NAT_SEQ_CORRECTION_POS, + htonl(ct->natseq[dir].correction_pos)); + mnl_attr_put_u32(nlh, CTA_NAT_SEQ_OFFSET_BEFORE, + htonl(ct->natseq[dir].offset_before)); + mnl_attr_put_u32(nlh, CTA_NAT_SEQ_OFFSET_AFTER, + htonl(ct->natseq[dir].offset_after)); + return 0; +} + +static int +nfct_build_nat_seq_adj(struct nlmsghdr *nlh, const struct nf_conntrack *ct, + int dir) +{ + int type = (dir == __DIR_ORIG) ? CTA_NAT_SEQ_ADJ_ORIG : + CTA_NAT_SEQ_ADJ_REPLY; + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, type); + nfct_nat_seq_adj(nlh, ct, dir); + mnl_attr_nest_end(nlh, nest); + + return 0; +} + +static int +nfct_build_protonat(struct nlmsghdr *nlh, const struct nf_conntrack *ct, + const struct __nfct_nat *nat) +{ + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, CTA_NAT_PROTO); + + switch (ct->head.orig.protonum) { + case IPPROTO_TCP: + case IPPROTO_UDP: + mnl_attr_put_u16(nlh, CTA_PROTONAT_PORT_MIN, + nat->l4min.tcp.port); + mnl_attr_put_u16(nlh, CTA_PROTONAT_PORT_MAX, + nat->l4max.tcp.port); + break; + } + mnl_attr_nest_end(nlh, nest); + return 0; +} + +static int +nfct_build_nat(struct nlmsghdr *nlh, const struct __nfct_nat *nat) +{ + mnl_attr_put_u32(nlh, CTA_NAT_MINIP, nat->min_ip); + return 0; +} + +static int +nfct_build_snat(struct nlmsghdr *nlh, const struct nf_conntrack *ct) +{ + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, CTA_NAT_SRC); + nfct_build_nat(nlh, &ct->snat); + nfct_build_protonat(nlh, ct, &ct->snat); + mnl_attr_nest_end(nlh, nest); + return 0; +} + +static int +nfct_build_snat_ipv4(struct nlmsghdr *nlh, const struct nf_conntrack *ct) +{ + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, CTA_NAT_SRC); + nfct_build_nat(nlh, &ct->snat); + mnl_attr_nest_end(nlh, nest); + return 0; +} + +static int +nfct_build_snat_port(struct nlmsghdr *nlh, const struct nf_conntrack *ct) +{ + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, CTA_NAT_SRC); + nfct_build_protonat(nlh, ct, &ct->snat); + mnl_attr_nest_end(nlh, nest); + return 0; +} + +static int +nfct_build_dnat(struct nlmsghdr *nlh, const struct nf_conntrack *ct) +{ + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, CTA_NAT_DST); + nfct_build_nat(nlh, &ct->dnat); + nfct_build_protonat(nlh, ct, &ct->dnat); + mnl_attr_nest_end(nlh, nest); + return 0; +} + +static int +nfct_build_dnat_ipv4(struct nlmsghdr *nlh, const struct nf_conntrack *ct) +{ + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, CTA_NAT_DST); + nfct_build_nat(nlh, &ct->dnat); + mnl_attr_nest_end(nlh, nest); + return 0; +} + +static int +nfct_build_dnat_port(struct nlmsghdr *nlh, const struct nf_conntrack *ct) +{ + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, CTA_NAT_DST); + nfct_build_protonat(nlh, ct, &ct->dnat); + mnl_attr_nest_end(nlh, nest); + return 0; +} + +static int +nfct_build_status(struct nlmsghdr *nlh, const struct nf_conntrack *ct) +{ + mnl_attr_put_u32(nlh, CTA_STATUS, htonl(ct->status | IPS_CONFIRMED)); + return 0; +} + +static int +nfct_build_timeout(struct nlmsghdr *nlh, const struct nf_conntrack *ct) +{ + mnl_attr_put_u32(nlh, CTA_TIMEOUT, htonl(ct->timeout)); + return 0; +} + +static int +nfct_build_mark(struct nlmsghdr *nlh, const struct nf_conntrack *ct) +{ + mnl_attr_put_u32(nlh, CTA_MARK, htonl(ct->mark)); + return 0; +} + +static int +nfct_build_secmark(struct nlmsghdr *nlh, const struct nf_conntrack *ct) +{ + mnl_attr_put_u32(nlh, CTA_SECMARK, htonl(ct->secmark)); + return 0; +} + +static int +nfct_build_helper_name(struct nlmsghdr *nlh, const struct nf_conntrack *ct) +{ + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, CTA_HELP); + mnl_attr_put_strz(nlh, CTA_HELP_NAME, ct->helper_name); + mnl_attr_nest_end(nlh, nest); + return 0; +} + +static int +nfct_build_zone(struct nlmsghdr *nlh, const struct nf_conntrack *ct) +{ + mnl_attr_put_u16(nlh, CTA_ZONE, htons(ct->zone)); + return 0; +} + +int +nfct_nlmsg_build(struct nlmsghdr *nlh, const struct nf_conntrack *ct) +{ + if (!test_bit(ATTR_ORIG_L3PROTO, ct->head.set)) { + errno = EINVAL; + return -1; + } + + if (test_bit(ATTR_ORIG_IPV4_SRC, ct->head.set) || + test_bit(ATTR_ORIG_IPV4_DST, ct->head.set) || + test_bit(ATTR_ORIG_IPV6_SRC, ct->head.set) || + test_bit(ATTR_ORIG_IPV6_DST, ct->head.set) || + test_bit(ATTR_ORIG_PORT_SRC, ct->head.set) || + test_bit(ATTR_ORIG_PORT_DST, ct->head.set) || + test_bit(ATTR_ORIG_L3PROTO, ct->head.set) || + test_bit(ATTR_ORIG_L4PROTO, ct->head.set) || + test_bit(ATTR_ICMP_TYPE, ct->head.set) || + test_bit(ATTR_ICMP_CODE, ct->head.set) || + test_bit(ATTR_ICMP_ID, ct->head.set)) { + nfct_build_tuple(nlh, &ct->head.orig, CTA_TUPLE_ORIG); + } + + if (test_bit(ATTR_REPL_IPV4_SRC, ct->head.set) || + test_bit(ATTR_REPL_IPV4_DST, ct->head.set) || + test_bit(ATTR_REPL_IPV6_SRC, ct->head.set) || + test_bit(ATTR_REPL_IPV6_DST, ct->head.set) || + test_bit(ATTR_REPL_PORT_SRC, ct->head.set) || + test_bit(ATTR_REPL_PORT_DST, ct->head.set) || + test_bit(ATTR_REPL_L3PROTO, ct->head.set) || + test_bit(ATTR_REPL_L4PROTO, ct->head.set) || + test_bit(ATTR_ICMP_TYPE, ct->head.set) || + test_bit(ATTR_ICMP_CODE, ct->head.set) || + test_bit(ATTR_ICMP_ID, ct->head.set)) { + nfct_build_tuple(nlh, &ct->repl, CTA_TUPLE_REPLY); + } + + if (test_bit(ATTR_MASTER_IPV4_SRC, ct->head.set) || + test_bit(ATTR_MASTER_IPV4_DST, ct->head.set) || + test_bit(ATTR_MASTER_IPV6_SRC, ct->head.set) || + test_bit(ATTR_MASTER_IPV6_DST, ct->head.set) || + test_bit(ATTR_MASTER_PORT_SRC, ct->head.set) || + test_bit(ATTR_MASTER_PORT_DST, ct->head.set) || + test_bit(ATTR_MASTER_L3PROTO, ct->head.set) || + test_bit(ATTR_MASTER_L4PROTO, ct->head.set)) { + nfct_build_tuple(nlh, &ct->master, CTA_TUPLE_MASTER); + } + + if (test_bit(ATTR_STATUS, ct->head.set)) + nfct_build_status(nlh, ct); + + if (test_bit(ATTR_TIMEOUT, ct->head.set)) + nfct_build_timeout(nlh, ct); + + if (test_bit(ATTR_MARK, ct->head.set)) + nfct_build_mark(nlh, ct); + + if (test_bit(ATTR_SECMARK, ct->head.set)) + nfct_build_secmark(nlh, ct); + + nfct_build_protoinfo(nlh, ct); + + if (test_bit(ATTR_SNAT_IPV4, ct->head.set) && + test_bit(ATTR_SNAT_PORT, ct->head.set)) { + nfct_build_snat(nlh, ct); + } else if (test_bit(ATTR_SNAT_IPV4, ct->head.set)) { + nfct_build_snat_ipv4(nlh, ct); + } else if (test_bit(ATTR_SNAT_PORT, ct->head.set)) { + nfct_build_snat_port(nlh, ct); + } + + if (test_bit(ATTR_DNAT_IPV4, ct->head.set) && + test_bit(ATTR_DNAT_PORT, ct->head.set)) { + nfct_build_dnat(nlh, ct); + } else if (test_bit(ATTR_DNAT_IPV4, ct->head.set)) { + nfct_build_dnat_ipv4(nlh, ct); + } else if (test_bit(ATTR_DNAT_PORT, ct->head.set)) { + nfct_build_dnat_port(nlh, ct); + } + + if (test_bit(ATTR_ORIG_NAT_SEQ_CORRECTION_POS, ct->head.set) && + test_bit(ATTR_ORIG_NAT_SEQ_OFFSET_BEFORE, ct->head.set) && + test_bit(ATTR_ORIG_NAT_SEQ_OFFSET_AFTER, ct->head.set)) { + nfct_build_nat_seq_adj(nlh, ct, __DIR_ORIG); + } + if (test_bit(ATTR_REPL_NAT_SEQ_CORRECTION_POS, ct->head.set) && + test_bit(ATTR_REPL_NAT_SEQ_OFFSET_BEFORE, ct->head.set) && + test_bit(ATTR_REPL_NAT_SEQ_OFFSET_AFTER, ct->head.set)) { + nfct_build_nat_seq_adj(nlh, ct, __DIR_REPL); + } + + if (test_bit(ATTR_HELPER_NAME, ct->head.set)) + nfct_build_helper_name(nlh, ct); + + if (test_bit(ATTR_ZONE, ct->head.set)) + nfct_build_zone(nlh, ct); + + return 0; +} diff --git a/src/conntrack/parse_mnl.c b/src/conntrack/parse_mnl.c new file mode 100644 index 0000000..a4955a0 --- /dev/null +++ b/src/conntrack/parse_mnl.c @@ -0,0 +1,934 @@ +/* + * (C) 2005-2012 by Pablo Neira Ayuso + * + * 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. + * + * This code has been sponsored by Vyatta Inc. + */ + +#include "internal/internal.h" +#include +#include + +static int +nfct_parse_ip_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, CTA_IP_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_IP_V4_SRC: + case CTA_IP_V4_DST: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + case CTA_IP_V6_SRC: + case CTA_IP_V6_DST: + if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, + sizeof(struct in6_addr)) < 0) { + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nfct_parse_ip(const struct nlattr *attr, struct __nfct_tuple *tuple, + const int dir, u_int32_t *set) +{ + struct nlattr *tb[CTA_IP_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nfct_parse_ip_attr_cb, tb) < 0) + return -1; + + if (tb[CTA_IP_V4_SRC]) { + tuple->src.v4 = mnl_attr_get_u32(tb[CTA_IP_V4_SRC]); + 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]) { + tuple->dst.v4 = mnl_attr_get_u32(tb[CTA_IP_V4_DST]); + 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]) { + memcpy(&tuple->src.v6, mnl_attr_get_payload(tb[CTA_IP_V6_SRC]), + 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]) { + memcpy(&tuple->dst.v6, mnl_attr_get_payload(tb[CTA_IP_V6_DST]), + 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; + } + } + return 0; +} + +static int +nfct_parse_proto_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_PROTO_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_PROTO_SRC_PORT: + case CTA_PROTO_DST_PORT: + case CTA_PROTO_ICMP_ID: + case CTA_PROTO_ICMPV6_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + break; + case CTA_PROTO_NUM: + case CTA_PROTO_ICMP_TYPE: + case CTA_PROTO_ICMP_CODE: + case CTA_PROTO_ICMPV6_TYPE: + case CTA_PROTO_ICMPV6_CODE: + if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) + return MNL_CB_ERROR; + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nfct_parse_proto(const struct nlattr *attr, struct __nfct_tuple *tuple, + const int dir, u_int32_t *set) +{ + struct nlattr *tb[CTA_PROTO_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nfct_parse_proto_attr_cb, tb) < 0) + return -1; + + if (tb[CTA_PROTO_NUM]) { + tuple->protonum = mnl_attr_get_u8(tb[CTA_PROTO_NUM]); + 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]) { + tuple->l4src.tcp.port = + mnl_attr_get_u16(tb[CTA_PROTO_SRC_PORT]); + 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]) { + tuple->l4dst.tcp.port = + mnl_attr_get_u16(tb[CTA_PROTO_DST_PORT]); + 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]) { + tuple->l4dst.icmp.type = + mnl_attr_get_u8(tb[CTA_PROTO_ICMP_TYPE]); + set_bit(ATTR_ICMP_TYPE, set); + } + + if (tb[CTA_PROTO_ICMP_CODE]) { + tuple->l4dst.icmp.code = + mnl_attr_get_u8(tb[CTA_PROTO_ICMP_CODE]); + set_bit(ATTR_ICMP_CODE, set); + } + + if (tb[CTA_PROTO_ICMP_ID]) { + tuple->l4src.icmp.id = + mnl_attr_get_u16(tb[CTA_PROTO_ICMP_ID]); + set_bit(ATTR_ICMP_ID, set); + } + + if (tb[CTA_PROTO_ICMPV6_TYPE]) { + tuple->l4dst.icmp.type = + mnl_attr_get_u8(tb[CTA_PROTO_ICMPV6_TYPE]); + set_bit(ATTR_ICMP_TYPE, set); + } + + if (tb[CTA_PROTO_ICMPV6_CODE]) { + tuple->l4dst.icmp.code = + mnl_attr_get_u8(tb[CTA_PROTO_ICMPV6_CODE]); + set_bit(ATTR_ICMP_CODE, set); + } + + if (tb[CTA_PROTO_ICMPV6_ID]) { + tuple->l4src.icmp.id = + mnl_attr_get_u16(tb[CTA_PROTO_ICMPV6_ID]); + set_bit(ATTR_ICMP_ID, set); + } + + return 0; +} + +static int nfct_parse_tuple_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_TUPLE_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_TUPLE_IP: + case CTA_TUPLE_PROTO: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + return MNL_CB_ERROR; + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nfct_parse_tuple(const struct nlattr *attr, struct __nfct_tuple *tuple, + int dir, u_int32_t *set) +{ + struct nlattr *tb[CTA_TUPLE_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nfct_parse_tuple_attr_cb, tb) < 0) + return -1; + + if (tb[CTA_TUPLE_IP]) { + if (nfct_parse_ip(tb[CTA_TUPLE_IP], tuple, dir, set) < 0) + return -1; + } + + if (tb[CTA_TUPLE_PROTO]) { + if (nfct_parse_proto(tb[CTA_TUPLE_PROTO], tuple, dir, set) < 0) + return -1; + } + + return 0; +} + +static int +nfct_parse_pinfo_tcp_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_PROTOINFO_TCP_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_PROTOINFO_TCP_STATE: + case CTA_PROTOINFO_TCP_WSCALE_ORIGINAL: + case CTA_PROTOINFO_TCP_WSCALE_REPLY: + if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) + return MNL_CB_ERROR; + break; + case CTA_PROTOINFO_TCP_FLAGS_ORIGINAL: + case CTA_PROTOINFO_TCP_FLAGS_REPLY: + if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, + sizeof(struct nf_ct_tcp_flags)) < 0) { + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nfct_parse_protoinfo_tcp(const struct nlattr *attr, struct nf_conntrack *ct) +{ + struct nlattr *tb[CTA_PROTOINFO_TCP_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nfct_parse_pinfo_tcp_attr_cb, tb) < 0) + return -1; + + if (tb[CTA_PROTOINFO_TCP_STATE]) { + ct->protoinfo.tcp.state = + mnl_attr_get_u8(tb[CTA_PROTOINFO_TCP_STATE]); + set_bit(ATTR_TCP_STATE, ct->head.set); + } + + if (tb[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL]) { + memcpy(&ct->protoinfo.tcp.wscale[__DIR_ORIG], + mnl_attr_get_payload(tb[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL]), + sizeof(uint8_t)); + set_bit(ATTR_TCP_WSCALE_ORIG, ct->head.set); + } + + if (tb[CTA_PROTOINFO_TCP_WSCALE_REPLY]) { + memcpy(&ct->protoinfo.tcp.wscale[__DIR_REPL], + mnl_attr_get_payload(tb[CTA_PROTOINFO_TCP_WSCALE_REPLY]), + sizeof(uint8_t)); + set_bit(ATTR_TCP_WSCALE_REPL, ct->head.set); + } + + if (tb[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL]) { + memcpy(&ct->protoinfo.tcp.flags[0], + mnl_attr_get_payload(tb[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL]), + 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]) { + memcpy(&ct->protoinfo.tcp.flags[1], + mnl_attr_get_payload(tb[CTA_PROTOINFO_TCP_FLAGS_REPLY]), + sizeof(struct nf_ct_tcp_flags)); + set_bit(ATTR_TCP_FLAGS_REPL, ct->head.set); + set_bit(ATTR_TCP_MASK_REPL, ct->head.set); + } + + return 0; +} + +static int +nfct_parse_pinfo_sctp_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_PROTOINFO_SCTP_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_PROTOINFO_SCTP_STATE: + if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) + return MNL_CB_ERROR; + break; + case CTA_PROTOINFO_SCTP_VTAG_ORIGINAL: + case CTA_PROTOINFO_SCTP_VTAG_REPLY: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nfct_parse_protoinfo_sctp(const struct nlattr *attr, struct nf_conntrack *ct) +{ + struct nlattr *tb[CTA_PROTOINFO_SCTP_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nfct_parse_pinfo_sctp_attr_cb, tb) < 0) + return -1; + + if (tb[CTA_PROTOINFO_SCTP_STATE]) { + ct->protoinfo.sctp.state = + mnl_attr_get_u8(tb[CTA_PROTOINFO_SCTP_STATE]); + set_bit(ATTR_SCTP_STATE, ct->head.set); + } + + if (tb[CTA_PROTOINFO_SCTP_VTAG_ORIGINAL]) { + ct->protoinfo.sctp.vtag[__DIR_ORIG] = + ntohl(mnl_attr_get_u32(tb[CTA_PROTOINFO_SCTP_VTAG_ORIGINAL])); + set_bit(ATTR_SCTP_VTAG_ORIG, ct->head.set); + } + + if (tb[CTA_PROTOINFO_SCTP_VTAG_REPLY]) { + ct->protoinfo.sctp.vtag[__DIR_REPL] = + ntohl(mnl_attr_get_u32(tb[CTA_PROTOINFO_SCTP_VTAG_REPLY])); + set_bit(ATTR_SCTP_VTAG_REPL, ct->head.set); + } + + return 0; +} + +static int +nfct_parse_pinfo_dccp_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_PROTOINFO_DCCP_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_PROTOINFO_DCCP_STATE: + case CTA_PROTOINFO_DCCP_ROLE: + if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) + return MNL_CB_ERROR; + break; + case CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) + return MNL_CB_ERROR; + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nfct_parse_protoinfo_dccp(const struct nlattr *attr, struct nf_conntrack *ct) +{ + struct nlattr *tb[CTA_PROTOINFO_DCCP_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nfct_parse_pinfo_dccp_attr_cb, tb) < 0) + return -1; + + if (tb[CTA_PROTOINFO_DCCP_STATE]) { + ct->protoinfo.dccp.state = mnl_attr_get_u8(tb[CTA_PROTOINFO_DCCP_STATE]); + set_bit(ATTR_DCCP_STATE, ct->head.set); + } + if (tb[CTA_PROTOINFO_DCCP_ROLE]) { + ct->protoinfo.dccp.role = mnl_attr_get_u8(tb[CTA_PROTOINFO_DCCP_ROLE]); + set_bit(ATTR_DCCP_ROLE, ct->head.set); + } + if (tb[CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ]) { + ct->protoinfo.dccp.handshake_seq = be64toh( + mnl_attr_get_u64(tb[CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ])); + set_bit(ATTR_DCCP_HANDSHAKE_SEQ, ct->head.set); + } + + return 0; +} + +static int +nfct_parse_protoinfo_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_PROTOINFO_TCP_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_PROTOINFO_TCP: + case CTA_PROTOINFO_SCTP: + case CTA_PROTOINFO_DCCP: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + return MNL_CB_ERROR; + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nfct_parse_protoinfo(const struct nlattr *attr, struct nf_conntrack *ct) +{ + struct nlattr *tb[CTA_PROTOINFO_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nfct_parse_protoinfo_attr_cb, tb) < 0) + return -1; + + if (tb[CTA_PROTOINFO_TCP]) + nfct_parse_protoinfo_tcp(tb[CTA_PROTOINFO_TCP], ct); + + if (tb[CTA_PROTOINFO_SCTP]) + nfct_parse_protoinfo_sctp(tb[CTA_PROTOINFO_SCTP], ct); + + if (tb[CTA_PROTOINFO_DCCP]) + nfct_parse_protoinfo_dccp(tb[CTA_PROTOINFO_DCCP], ct); + + return 0; +} + +static int nfct_parse_counters_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_COUNTERS_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_COUNTERS_PACKETS: + case CTA_COUNTERS_BYTES: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) + return MNL_CB_ERROR; + break; + case CTA_COUNTERS32_PACKETS: + case CTA_COUNTERS32_BYTES: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nfct_parse_counters(const struct nlattr *attr, struct nf_conntrack *ct, + int dir) +{ + struct nlattr *tb[CTA_COUNTERS_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nfct_parse_counters_attr_cb, tb) < 0) + return -1; + + if (tb[CTA_COUNTERS_PACKETS] || tb[CTA_COUNTERS32_PACKETS]) { + if (tb[CTA_COUNTERS32_PACKETS]) { + ct->counters[dir].packets = + ntohl(mnl_attr_get_u32(tb[CTA_COUNTERS32_PACKETS])); + } + if (tb[CTA_COUNTERS_PACKETS]) { + ct->counters[dir].packets = + be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_PACKETS])); + } + 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] || tb[CTA_COUNTERS32_BYTES]) { + if (tb[CTA_COUNTERS32_BYTES]) { + ct->counters[dir].bytes = + ntohl(mnl_attr_get_u32(tb[CTA_COUNTERS32_BYTES])); + } + if (tb[CTA_COUNTERS_BYTES]) { + ct->counters[dir].bytes = + be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_BYTES])); + } + + 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; + } + } + + return 0; +} + +static int +nfct_parse_nat_seq_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_NAT_SEQ_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_NAT_SEQ_CORRECTION_POS: + case CTA_NAT_SEQ_OFFSET_BEFORE: + case CTA_NAT_SEQ_OFFSET_AFTER: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nfct_parse_nat_seq(const struct nlattr *attr, struct nf_conntrack *ct, int dir) +{ + struct nlattr *tb[CTA_NAT_SEQ_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nfct_parse_nat_seq_attr_cb, tb) < 0) + return -1; + + if (tb[CTA_NAT_SEQ_CORRECTION_POS]) { + ct->natseq[dir].correction_pos = + ntohl(mnl_attr_get_u32(tb[CTA_NAT_SEQ_CORRECTION_POS])); + 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]) { + ct->natseq[dir].offset_before = + ntohl(mnl_attr_get_u32(tb[CTA_NAT_SEQ_OFFSET_BEFORE])); + 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]) { + ct->natseq[dir].offset_after = + ntohl(mnl_attr_get_u32(tb[CTA_NAT_SEQ_OFFSET_AFTER])); + 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; + } + } + + return 0; +} + +static int +nfct_parse_helper_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_HELP_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_HELP_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + return MNL_CB_ERROR; + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nfct_parse_helper(const struct nlattr *attr, struct nf_conntrack *ct) +{ + struct nlattr *tb[CTA_HELP_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nfct_parse_helper_attr_cb, tb) < 0) + return -1; + + if (!tb[CTA_HELP_NAME]) + return 0; + + strncpy(ct->helper_name, mnl_attr_get_str(tb[CTA_HELP_NAME]), + NFCT_HELPER_NAME_MAX); + ct->helper_name[NFCT_HELPER_NAME_MAX-1] = '\0'; + set_bit(ATTR_HELPER_NAME, ct->head.set); + + return 0; +} + +static int +nfct_parse_secctx_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_SECCTX_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_SECCTX_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + return MNL_CB_ERROR; + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nfct_parse_secctx(const struct nlattr *attr, struct nf_conntrack *ct) +{ + struct nlattr *tb[CTA_SECCTX_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nfct_parse_secctx_attr_cb, tb) < 0) + return -1; + + if (!tb[CTA_SECCTX_NAME]) + return 0; + + ct->secctx = strdup(NFA_DATA(tb[CTA_SECCTX_NAME])); + if (ct->secctx) + set_bit(ATTR_SECCTX, ct->head.set); + + return 0; +} + +static int +nfct_parse_timestamp_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_TIMESTAMP_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_TIMESTAMP_START: + case CTA_TIMESTAMP_STOP: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) + return MNL_CB_ERROR; + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nfct_parse_timestamp(const struct nlattr *attr, struct nf_conntrack *ct) +{ + struct nlattr *tb[CTA_TIMESTAMP_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nfct_parse_timestamp_attr_cb, tb) < 0) + return -1; + + if (tb[CTA_TIMESTAMP_START]) { + ct->timestamp.start = + be64toh(mnl_attr_get_u64(tb[CTA_TIMESTAMP_START])); + set_bit(ATTR_TIMESTAMP_START, ct->head.set); + } + if (tb[CTA_TIMESTAMP_STOP]) { + ct->timestamp.stop = + be64toh(mnl_attr_get_u64(tb[CTA_TIMESTAMP_START])); + set_bit(ATTR_TIMESTAMP_STOP, ct->head.set); + } + + return 0; +} + +static int +nfct_parse_conntrack_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_TUPLE_ORIG: + case CTA_TUPLE_REPLY: + case CTA_TUPLE_MASTER: + case CTA_NAT_SEQ_ADJ_ORIG: + case CTA_NAT_SEQ_ADJ_REPLY: + case CTA_PROTOINFO: + case CTA_COUNTERS_ORIG: + case CTA_COUNTERS_REPLY: + case CTA_HELP: + case CTA_SECCTX: + case CTA_TIMESTAMP: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + return MNL_CB_ERROR; + break; + case CTA_STATUS: + case CTA_TIMEOUT: + case CTA_MARK: + case CTA_SECMARK: + case CTA_USE: + case CTA_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + case CTA_ZONE: + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + break; + case CTA_NAT_SRC: + case CTA_NAT_DST: + /* deprecated */ + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +int +nfct_payload_parse(const void *payload, size_t payload_len, + uint16_t l3num, struct nf_conntrack *ct) +{ + struct nlattr *tb[CTA_MAX+1] = {}; + + if (mnl_attr_parse_payload(payload, payload_len, + nfct_parse_conntrack_attr_cb, tb) < 0) + return -1; + + if (tb[CTA_TUPLE_ORIG]) { + ct->head.orig.l3protonum = l3num; + set_bit(ATTR_ORIG_L3PROTO, ct->head.set); + + if (nfct_parse_tuple(tb[CTA_TUPLE_ORIG], &ct->head.orig, + __DIR_ORIG, ct->head.set) < 0) + return -1; + } + + if (tb[CTA_TUPLE_REPLY]) { + ct->repl.l3protonum = l3num; + set_bit(ATTR_REPL_L3PROTO, ct->head.set); + + if (nfct_parse_tuple(tb[CTA_TUPLE_REPLY], &ct->repl, + __DIR_REPL, ct->head.set) < 0) + return -1; + } + + if (tb[CTA_TUPLE_MASTER]) { + ct->master.l3protonum = l3num; + set_bit(ATTR_MASTER_L3PROTO, ct->head.set); + + if (nfct_parse_tuple(tb[CTA_TUPLE_MASTER], &ct->master, + __DIR_MASTER, ct->head.set) < 0) + return -1; + } + + if (tb[CTA_NAT_SEQ_ADJ_ORIG]) { + if (nfct_parse_nat_seq(tb[CTA_NAT_SEQ_ADJ_ORIG], + ct, __DIR_ORIG) < 0) + return -1; + } + + if (tb[CTA_NAT_SEQ_ADJ_REPLY]) { + if (nfct_parse_nat_seq(tb[CTA_NAT_SEQ_ADJ_REPLY], + ct, __DIR_REPL) < 0) + return -1; + } + + if (tb[CTA_STATUS]) { + ct->status = ntohl(mnl_attr_get_u32(tb[CTA_STATUS])); + set_bit(ATTR_STATUS, ct->head.set); + } + + if (tb[CTA_PROTOINFO]) { + if (nfct_parse_protoinfo(tb[CTA_PROTOINFO], ct) < 0) + return -1; + } + + if (tb[CTA_TIMEOUT]) { + ct->timeout = ntohl(mnl_attr_get_u32(tb[CTA_TIMEOUT])); + set_bit(ATTR_TIMEOUT, ct->head.set); + } + + if (tb[CTA_MARK]) { + ct->mark = ntohl(mnl_attr_get_u32(tb[CTA_MARK])); + set_bit(ATTR_MARK, ct->head.set); + } + + if (tb[CTA_SECMARK]) { + ct->secmark = ntohl(mnl_attr_get_u32(tb[CTA_SECMARK])); + set_bit(ATTR_SECMARK, ct->head.set); + } + + if (tb[CTA_COUNTERS_ORIG]) { + if (nfct_parse_counters(tb[CTA_COUNTERS_ORIG], + ct, __DIR_ORIG) < 0) + return -1; + } + + if (tb[CTA_COUNTERS_REPLY]) { + if (nfct_parse_counters(tb[CTA_COUNTERS_REPLY], + ct, __DIR_REPL) < 0) + return -1; + } + + if (tb[CTA_USE]) { + ct->use = ntohl(mnl_attr_get_u32(tb[CTA_USE])); + set_bit(ATTR_USE, ct->head.set); + } + + if (tb[CTA_ID]) { + ct->id = ntohl(mnl_attr_get_u32(tb[CTA_ID])); + set_bit(ATTR_ID, ct->head.set); + } + + if (tb[CTA_HELP]) { + if (nfct_parse_helper(tb[CTA_HELP], ct) < 0) + return -1; + } + + if (tb[CTA_ZONE]) { + ct->zone = ntohs(mnl_attr_get_u16(tb[CTA_ZONE])); + set_bit(ATTR_ZONE, ct->head.set); + } + + if (tb[CTA_SECCTX]) { + if (nfct_parse_secctx(tb[CTA_SECCTX], ct) < 0) + return -1; + } + + if (tb[CTA_TIMESTAMP]) { + if (nfct_parse_timestamp(tb[CTA_TIMESTAMP], ct) < 0) + return -1; + } + + return 0; +} + +int nfct_nlmsg_parse(const struct nlmsghdr *nlh, struct nf_conntrack *ct) +{ + struct nfgenmsg *nfhdr = mnl_nlmsg_get_payload(nlh); + + return nfct_payload_parse((uint8_t *)nfhdr + sizeof(struct nfgenmsg), + nlh->nlmsg_len, nfhdr->nfgen_family, ct); +} -- cgit v1.2.3