| /* |
| * (C) 2005-2012 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. |
| * |
| * This code has been sponsored by Vyatta Inc. <http://www.vyatta.com> |
| */ |
| |
| #include "internal/internal.h" |
| #include <libmnl/libmnl.h> |
| #include <limits.h> |
| #include <endian.h> |
| |
| 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, uint32_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, uint32_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; |
| case CTA_TUPLE_ZONE: |
| if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) |
| return MNL_CB_ERROR; |
| break; |
| } |
| |
| tb[type] = attr; |
| return MNL_CB_OK; |
| } |
| |
| int |
| nfct_parse_tuple(const struct nlattr *attr, struct __nfct_tuple *tuple, |
| int dir, uint32_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; |
| } |
| |
| if (tb[CTA_TUPLE_ZONE]) { |
| tuple->zone = ntohs(mnl_attr_get_u16(tb[CTA_TUPLE_ZONE])); |
| switch(dir) { |
| case __DIR_ORIG: |
| set_bit(ATTR_ORIG_ZONE, set); |
| break; |
| case __DIR_REPL: |
| set_bit(ATTR_REPL_ZONE, set); |
| break; |
| } |
| } |
| |
| 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); |
| |
| if (!tb[CTA_HELP_INFO]) |
| return 0; |
| |
| ct->helper_info_len = mnl_attr_get_payload_len(tb[CTA_HELP_INFO]); |
| ct->helper_info = calloc(1, ct->helper_info_len); |
| if (ct->helper_info == NULL) |
| return -1; |
| |
| memcpy(ct->helper_info, mnl_attr_get_payload(tb[CTA_HELP_INFO]), |
| ct->helper_info_len); |
| set_bit(ATTR_HELPER_INFO, 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_STOP])); |
| set_bit(ATTR_TIMESTAMP_STOP, ct->head.set); |
| } |
| |
| return 0; |
| } |
| |
| static int nfct_parse_labels(const struct nlattr *attr, struct nf_conntrack *ct) |
| { |
| uint16_t len = mnl_attr_get_payload_len(attr); |
| struct nfct_bitmask *mask; |
| uint32_t *bits; |
| |
| if (len == 0) |
| return 0; |
| |
| mask = nfct_bitmask_new((len * CHAR_BIT) - 1); |
| if (!mask) |
| return -1; |
| bits = mnl_attr_get_payload(attr); |
| |
| memcpy(mask->bits, bits, len); |
| nfct_set_attr(ct, ATTR_CONNLABELS, mask); |
| 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; |
| } |
| |
| if (tb[CTA_LABELS]) { |
| if (nfct_parse_labels(tb[CTA_LABELS], ct) < 0) |
| return -1; |
| } |
| /* CTA_LABELS_MASK: never sent by kernel */ |
| |
| 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), |
| mnl_nlmsg_get_payload_len(nlh) - sizeof(struct nfgenmsg), |
| nfhdr->nfgen_family, ct); |
| } |