| /* |
| * (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" |
| |
| static void __build_tuple_ip(struct nfnlhdr *req, |
| size_t size, |
| const struct __nfct_tuple *t) |
| { |
| struct nfattr *nest; |
| |
| nest = nfnl_nest(&req->nlh, size, CTA_TUPLE_IP); |
| |
| switch(t->l3protonum) { |
| case AF_INET: |
| nfnl_addattr_l(&req->nlh, size, CTA_IP_V4_SRC, &t->src.v4, |
| sizeof(uint32_t)); |
| nfnl_addattr_l(&req->nlh, size, CTA_IP_V4_DST, &t->dst.v4, |
| sizeof(uint32_t)); |
| break; |
| case AF_INET6: |
| nfnl_addattr_l(&req->nlh, size, CTA_IP_V6_SRC, &t->src.v6, |
| sizeof(struct in6_addr)); |
| nfnl_addattr_l(&req->nlh, size, CTA_IP_V6_DST, &t->dst.v6, |
| sizeof(struct in6_addr)); |
| break; |
| default: |
| break; |
| } |
| |
| nfnl_nest_end(&req->nlh, nest); |
| } |
| |
| static void __build_tuple_proto(struct nfnlhdr *req, |
| size_t size, |
| const struct __nfct_tuple *t) |
| { |
| struct nfattr *nest; |
| |
| nest = nfnl_nest(&req->nlh, size, CTA_TUPLE_PROTO); |
| |
| nfnl_addattr_l(&req->nlh, size, CTA_PROTO_NUM, &t->protonum, |
| sizeof(uint8_t)); |
| |
| switch(t->protonum) { |
| case IPPROTO_UDP: |
| case IPPROTO_TCP: |
| case IPPROTO_SCTP: |
| case IPPROTO_DCCP: |
| case IPPROTO_GRE: |
| case IPPROTO_UDPLITE: |
| nfnl_addattr_l(&req->nlh, size, CTA_PROTO_SRC_PORT, |
| &t->l4src.tcp.port, sizeof(uint16_t)); |
| nfnl_addattr_l(&req->nlh, size, CTA_PROTO_DST_PORT, |
| &t->l4dst.tcp.port, sizeof(uint16_t)); |
| break; |
| |
| case IPPROTO_ICMP: |
| nfnl_addattr_l(&req->nlh, size, CTA_PROTO_ICMP_CODE, |
| &t->l4dst.icmp.code, sizeof(uint8_t)); |
| nfnl_addattr_l(&req->nlh, size, CTA_PROTO_ICMP_TYPE, |
| &t->l4dst.icmp.type, sizeof(uint8_t)); |
| nfnl_addattr_l(&req->nlh, size, CTA_PROTO_ICMP_ID, |
| &t->l4src.icmp.id, sizeof(uint16_t)); |
| break; |
| |
| case IPPROTO_ICMPV6: |
| nfnl_addattr_l(&req->nlh, size, CTA_PROTO_ICMPV6_CODE, |
| &t->l4dst.icmp.code, sizeof(uint8_t)); |
| nfnl_addattr_l(&req->nlh, size, CTA_PROTO_ICMPV6_TYPE, |
| &t->l4dst.icmp.type, sizeof(uint8_t)); |
| nfnl_addattr_l(&req->nlh, size, CTA_PROTO_ICMPV6_ID, |
| &t->l4src.icmp.id, sizeof(uint16_t)); |
| break; |
| |
| default: |
| break; |
| } |
| |
| nfnl_nest_end(&req->nlh, nest); |
| } |
| |
| static void __build_tuple_raw(struct nfnlhdr *req, size_t size, |
| const struct __nfct_tuple *t) |
| { |
| __build_tuple_ip(req, size, t); |
| __build_tuple_proto(req, size, t); |
| } |
| |
| void __build_tuple(struct nfnlhdr *req, size_t size, |
| const struct __nfct_tuple *t, const int type) |
| { |
| struct nfattr *nest; |
| |
| nest = nfnl_nest(&req->nlh, size, type); |
| __build_tuple_raw(req, size, t); |
| nfnl_nest_end(&req->nlh, nest); |
| } |
| |
| static void __build_protoinfo(struct nfnlhdr *req, size_t size, |
| const struct nf_conntrack *ct) |
| { |
| struct nfattr *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 = nfnl_nest(&req->nlh, size, CTA_PROTOINFO); |
| nest_proto = nfnl_nest(&req->nlh, size, CTA_PROTOINFO_TCP); |
| if (test_bit(ATTR_TCP_STATE, ct->head.set)) |
| nfnl_addattr_l(&req->nlh, size, |
| CTA_PROTOINFO_TCP_STATE, |
| &ct->protoinfo.tcp.state, |
| sizeof(uint8_t)); |
| if (test_bit(ATTR_TCP_FLAGS_ORIG, ct->head.set) && |
| test_bit(ATTR_TCP_MASK_ORIG, ct->head.set)) |
| nfnl_addattr_l(&req->nlh, size, |
| CTA_PROTOINFO_TCP_FLAGS_ORIGINAL, |
| &ct->protoinfo.tcp.flags[0], |
| sizeof(struct nf_ct_tcp_flags)); |
| if (test_bit(ATTR_TCP_FLAGS_REPL, ct->head.set) && |
| test_bit(ATTR_TCP_MASK_REPL, ct->head.set)) |
| nfnl_addattr_l(&req->nlh, size, |
| CTA_PROTOINFO_TCP_FLAGS_REPLY, |
| &ct->protoinfo.tcp.flags[1], |
| sizeof(struct nf_ct_tcp_flags)); |
| if (test_bit(ATTR_TCP_WSCALE_ORIG, ct->head.set)) |
| nfnl_addattr_l(&req->nlh, size, |
| CTA_PROTOINFO_TCP_WSCALE_ORIGINAL, |
| &ct->protoinfo.tcp.wscale[__DIR_ORIG], |
| sizeof(uint8_t)); |
| if (test_bit(ATTR_TCP_WSCALE_REPL, ct->head.set)) |
| nfnl_addattr_l(&req->nlh, size, |
| CTA_PROTOINFO_TCP_WSCALE_REPLY, |
| &ct->protoinfo.tcp.wscale[__DIR_REPL], |
| sizeof(uint8_t)); |
| nfnl_nest_end(&req->nlh, nest_proto); |
| nfnl_nest_end(&req->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 = nfnl_nest(&req->nlh, size, CTA_PROTOINFO); |
| nest_proto = nfnl_nest(&req->nlh, size, CTA_PROTOINFO_SCTP); |
| if (test_bit(ATTR_SCTP_STATE, ct->head.set)) |
| nfnl_addattr_l(&req->nlh, size, |
| CTA_PROTOINFO_SCTP_STATE, |
| &ct->protoinfo.sctp.state, |
| sizeof(uint8_t)); |
| if (test_bit(ATTR_SCTP_VTAG_ORIG, ct->head.set)) |
| nfnl_addattr32(&req->nlh, size, |
| CTA_PROTOINFO_SCTP_VTAG_ORIGINAL, |
| htonl(ct->protoinfo.sctp.vtag[__DIR_ORIG])); |
| if (test_bit(ATTR_SCTP_VTAG_REPL, ct->head.set)) |
| nfnl_addattr32(&req->nlh, size, |
| CTA_PROTOINFO_SCTP_VTAG_REPLY, |
| htonl(ct->protoinfo.sctp.vtag[__DIR_REPL])); |
| nfnl_nest_end(&req->nlh, nest_proto); |
| nfnl_nest_end(&req->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 = nfnl_nest(&req->nlh, size, CTA_PROTOINFO); |
| nest_proto = nfnl_nest(&req->nlh, size, CTA_PROTOINFO_DCCP); |
| if (test_bit(ATTR_DCCP_STATE, ct->head.set)) |
| nfnl_addattr_l(&req->nlh, size, |
| CTA_PROTOINFO_DCCP_STATE, |
| &ct->protoinfo.dccp.state, |
| sizeof(uint8_t)); |
| if (test_bit(ATTR_DCCP_ROLE, ct->head.set)) |
| nfnl_addattr_l(&req->nlh, size, |
| CTA_PROTOINFO_DCCP_ROLE, |
| &ct->protoinfo.dccp.role, |
| sizeof(uint8_t)); |
| if (test_bit(ATTR_DCCP_HANDSHAKE_SEQ, ct->head.set)) { |
| /* FIXME: use __cpu_to_be64() instead which is the |
| * correct operation. This is a semantic abuse but |
| * we have no function to do it in libnfnetlink. */ |
| uint64_t handshake_seq = |
| __be64_to_cpu(ct->protoinfo.dccp.handshake_seq); |
| |
| nfnl_addattr_l(&req->nlh, size, |
| CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ, |
| &handshake_seq, |
| sizeof(uint64_t)); |
| } |
| nfnl_nest_end(&req->nlh, nest_proto); |
| nfnl_nest_end(&req->nlh, nest); |
| default: |
| break; |
| } |
| } |
| |
| static inline void |
| __nat_seq_adj(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct, |
| int dir) |
| { |
| nfnl_addattr32(&req->nlh, |
| size, |
| CTA_NAT_SEQ_CORRECTION_POS, |
| htonl(ct->natseq[dir].correction_pos)); |
| nfnl_addattr32(&req->nlh, |
| size, |
| CTA_NAT_SEQ_OFFSET_BEFORE, |
| htonl(ct->natseq[dir].offset_before)); |
| nfnl_addattr32(&req->nlh, |
| size, |
| CTA_NAT_SEQ_OFFSET_AFTER, |
| htonl(ct->natseq[dir].offset_after)); |
| } |
| |
| static void |
| __build_nat_seq_adj(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct, |
| int dir) |
| { |
| struct nfattr *nest; |
| int type = (dir == __DIR_ORIG) ? CTA_NAT_SEQ_ADJ_ORIG : |
| CTA_NAT_SEQ_ADJ_REPLY; |
| |
| nest = nfnl_nest(&req->nlh, size, type); |
| __nat_seq_adj(req, size, ct, dir); |
| nfnl_nest_end(&req->nlh, nest); |
| } |
| |
| static void __build_protonat(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct, |
| const struct __nfct_nat *nat) |
| { |
| struct nfattr *nest; |
| |
| nest = nfnl_nest(&req->nlh, size, CTA_NAT_PROTO); |
| |
| switch (ct->head.orig.protonum) { |
| case IPPROTO_TCP: |
| case IPPROTO_UDP: |
| nfnl_addattr_l(&req->nlh, size, CTA_PROTONAT_PORT_MIN, |
| &nat->l4min.tcp.port, sizeof(uint16_t)); |
| nfnl_addattr_l(&req->nlh, size, CTA_PROTONAT_PORT_MAX, |
| &nat->l4max.tcp.port, sizeof(uint16_t)); |
| break; |
| } |
| nfnl_nest_end(&req->nlh, nest); |
| } |
| |
| static void __build_nat(struct nfnlhdr *req, |
| size_t size, |
| const struct __nfct_nat *nat, |
| uint8_t l3protonum) |
| { |
| switch (l3protonum) { |
| case AF_INET: |
| nfnl_addattr_l(&req->nlh, size, CTA_NAT_MINIP, |
| &nat->min_ip.v4, sizeof(uint32_t)); |
| break; |
| case AF_INET6: |
| nfnl_addattr_l(&req->nlh, size, CTA_NAT_V6_MINIP, |
| &nat->min_ip.v6, sizeof(struct in6_addr)); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void __build_snat(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct, |
| uint8_t l3protonum) |
| { |
| struct nfattr *nest; |
| |
| nest = nfnl_nest(&req->nlh, size, CTA_NAT_SRC); |
| __build_nat(req, size, &ct->snat, l3protonum); |
| __build_protonat(req, size, ct, &ct->snat); |
| nfnl_nest_end(&req->nlh, nest); |
| } |
| |
| static void __build_snat_ipv4(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct) |
| { |
| struct nfattr *nest; |
| |
| nest = nfnl_nest(&req->nlh, size, CTA_NAT_SRC); |
| __build_nat(req, size, &ct->snat, AF_INET); |
| nfnl_nest_end(&req->nlh, nest); |
| } |
| |
| static void __build_snat_ipv6(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct) |
| { |
| struct nfattr *nest; |
| |
| nest = nfnl_nest(&req->nlh, size, CTA_NAT_SRC); |
| __build_nat(req, size, &ct->snat, AF_INET6); |
| nfnl_nest_end(&req->nlh, nest); |
| } |
| |
| static void __build_snat_port(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct) |
| { |
| struct nfattr *nest; |
| |
| nest = nfnl_nest(&req->nlh, size, CTA_NAT_SRC); |
| __build_protonat(req, size, ct, &ct->snat); |
| nfnl_nest_end(&req->nlh, nest); |
| } |
| |
| static void __build_dnat(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct, |
| uint8_t l3protonum) |
| { |
| struct nfattr *nest; |
| |
| nest = nfnl_nest(&req->nlh, size, CTA_NAT_DST); |
| __build_nat(req, size, &ct->dnat, l3protonum); |
| __build_protonat(req, size, ct, &ct->dnat); |
| nfnl_nest_end(&req->nlh, nest); |
| } |
| |
| static void __build_dnat_ipv4(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct) |
| { |
| struct nfattr *nest; |
| |
| nest = nfnl_nest(&req->nlh, size, CTA_NAT_DST); |
| __build_nat(req, size, &ct->dnat, AF_INET); |
| nfnl_nest_end(&req->nlh, nest); |
| } |
| |
| static void __build_dnat_ipv6(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct) |
| { |
| struct nfattr *nest; |
| |
| nest = nfnl_nest(&req->nlh, size, CTA_NAT_DST); |
| __build_nat(req, size, &ct->dnat, AF_INET6); |
| nfnl_nest_end(&req->nlh, nest); |
| } |
| |
| static void __build_dnat_port(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct) |
| { |
| struct nfattr *nest; |
| |
| nest = nfnl_nest(&req->nlh, size, CTA_NAT_DST); |
| __build_protonat(req, size, ct, &ct->dnat); |
| nfnl_nest_end(&req->nlh, nest); |
| } |
| |
| static void __build_status(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct) |
| { |
| nfnl_addattr32(&req->nlh, size, CTA_STATUS, |
| htonl(ct->status | IPS_CONFIRMED)); |
| } |
| |
| static void __build_timeout(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct) |
| { |
| nfnl_addattr32(&req->nlh, size, CTA_TIMEOUT, htonl(ct->timeout)); |
| } |
| |
| static void __build_mark(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct) |
| { |
| nfnl_addattr32(&req->nlh, size, CTA_MARK, htonl(ct->mark)); |
| } |
| |
| static void __build_secmark(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct) |
| { |
| nfnl_addattr32(&req->nlh, size, CTA_SECMARK, htonl(ct->secmark)); |
| } |
| |
| static void __build_helper_name(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct) |
| { |
| struct nfattr *nest; |
| |
| nest = nfnl_nest(&req->nlh, size, CTA_HELP); |
| nfnl_addattr_l(&req->nlh, |
| size, |
| CTA_HELP_NAME, |
| ct->helper_name, |
| strlen(ct->helper_name)+1); |
| nfnl_nest_end(&req->nlh, nest); |
| } |
| |
| static void __build_zone(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct) |
| { |
| nfnl_addattr16(&req->nlh, size, CTA_ZONE, htons(ct->zone)); |
| } |
| |
| static void __build_labels(struct nfnlhdr *req, |
| size_t size, |
| const struct nf_conntrack *ct) |
| { |
| struct nfct_bitmask *b = ct->connlabels; |
| unsigned int b_size = b->words * sizeof(b->bits[0]); |
| |
| nfnl_addattr_l(&req->nlh, |
| size, |
| CTA_LABELS, |
| b->bits, |
| b_size); |
| |
| if (test_bit(ATTR_CONNLABELS_MASK, ct->head.set)) { |
| b = ct->connlabels_mask; |
| if (b_size == (b->words * sizeof(b->bits[0]))) |
| nfnl_addattr_l(&req->nlh, |
| size, |
| CTA_LABELS_MASK, |
| b->bits, |
| b_size); |
| } |
| } |
| |
| int __build_conntrack(struct nfnl_subsys_handle *ssh, |
| struct nfnlhdr *req, |
| size_t size, |
| uint16_t type, |
| uint16_t flags, |
| const struct nf_conntrack *ct) |
| { |
| uint8_t l3num = ct->head.orig.l3protonum; |
| |
| if (!test_bit(ATTR_ORIG_L3PROTO, ct->head.set)) { |
| errno = EINVAL; |
| return -1; |
| } |
| |
| memset(req, 0, size); |
| |
| nfnl_fill_hdr(ssh, &req->nlh, 0, l3num, 0, type, flags); |
| |
| 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_ORIG_ZONE, 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)) { |
| const struct __nfct_tuple *t = &ct->head.orig; |
| struct nfattr *nest; |
| |
| nest = nfnl_nest(&req->nlh, size, CTA_TUPLE_ORIG); |
| __build_tuple_raw(req, size, t); |
| if (test_bit(ATTR_ORIG_ZONE, ct->head.set)) |
| nfnl_addattr16(&req->nlh, size, CTA_TUPLE_ZONE, |
| htons(t->zone)); |
| nfnl_nest_end(&req->nlh, nest); |
| } |
| |
| 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_REPL_ZONE, ct->head.set)) { |
| const struct __nfct_tuple *t = &ct->repl; |
| struct nfattr *nest; |
| |
| nest = nfnl_nest(&req->nlh, size, CTA_TUPLE_REPLY); |
| __build_tuple_raw(req, size, t); |
| if (test_bit(ATTR_REPL_ZONE, ct->head.set)) |
| nfnl_addattr16(&req->nlh, size, CTA_TUPLE_ZONE, |
| htons(t->zone)); |
| nfnl_nest_end(&req->nlh, nest); |
| } |
| |
| 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)) |
| __build_tuple(req, size, &ct->master, CTA_TUPLE_MASTER); |
| |
| if (test_bit(ATTR_STATUS, ct->head.set)) |
| __build_status(req, size, ct); |
| else { |
| /* build IPS_CONFIRMED if we're creating a new conntrack */ |
| if (type == IPCTNL_MSG_CT_NEW && flags & NLM_F_CREATE) |
| __build_status(req, size, ct); |
| } |
| |
| if (test_bit(ATTR_TIMEOUT, ct->head.set)) |
| __build_timeout(req, size, ct); |
| |
| if (test_bit(ATTR_MARK, ct->head.set)) |
| __build_mark(req, size, ct); |
| |
| if (test_bit(ATTR_SECMARK, ct->head.set)) |
| __build_secmark(req, size, ct); |
| |
| __build_protoinfo(req, size, ct); |
| |
| if (test_bit(ATTR_SNAT_IPV4, ct->head.set) && |
| test_bit(ATTR_SNAT_PORT, ct->head.set)) |
| __build_snat(req, size, ct, AF_INET); |
| else if (test_bit(ATTR_SNAT_IPV6, ct->head.set) && |
| test_bit(ATTR_SNAT_PORT, ct->head.set)) |
| __build_snat(req, size, ct, AF_INET6); |
| else if (test_bit(ATTR_SNAT_IPV4, ct->head.set)) |
| __build_snat_ipv4(req, size, ct); |
| else if (test_bit(ATTR_SNAT_IPV6, ct->head.set)) |
| __build_snat_ipv6(req, size, ct); |
| else if (test_bit(ATTR_SNAT_PORT, ct->head.set)) |
| __build_snat_port(req, size, ct); |
| |
| if (test_bit(ATTR_DNAT_IPV4, ct->head.set) && |
| test_bit(ATTR_DNAT_PORT, ct->head.set)) |
| __build_dnat(req, size, ct, AF_INET); |
| else if (test_bit(ATTR_DNAT_IPV6, ct->head.set) && |
| test_bit(ATTR_DNAT_PORT, ct->head.set)) |
| __build_dnat(req, size, ct, AF_INET6); |
| else if (test_bit(ATTR_DNAT_IPV4, ct->head.set)) |
| __build_dnat_ipv4(req, size, ct); |
| else if (test_bit(ATTR_DNAT_IPV6, ct->head.set)) |
| __build_dnat_ipv6(req, size, ct); |
| else if (test_bit(ATTR_DNAT_PORT, ct->head.set)) |
| __build_dnat_port(req, size, 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)) |
| __build_nat_seq_adj(req, size, 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)) |
| __build_nat_seq_adj(req, size, ct, __DIR_REPL); |
| |
| if (test_bit(ATTR_HELPER_NAME, ct->head.set)) |
| __build_helper_name(req, size, ct); |
| |
| if (test_bit(ATTR_ZONE, ct->head.set)) |
| __build_zone(req, size, ct); |
| |
| if (test_bit(ATTR_CONNLABELS, ct->head.set)) |
| __build_labels(req, size, ct); |
| |
| return 0; |
| } |