| /* |
| * (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 __autocomplete(struct nf_conntrack *ct, int dir) |
| { |
| struct __nfct_tuple *this = NULL, *other = NULL; |
| |
| switch(dir) { |
| case __DIR_ORIG: |
| this = &ct->head.orig; |
| other = &ct->repl; |
| break; |
| case __DIR_REPL: |
| this = &ct->repl; |
| other = &ct->head.orig; |
| break; |
| } |
| |
| this->l3protonum = other->l3protonum; |
| this->protonum = other->protonum; |
| |
| memcpy(&this->src.v6, &other->dst.v6, sizeof(union __nfct_address)); |
| memcpy(&this->dst.v6, &other->src.v6, sizeof(union __nfct_address)); |
| |
| switch(this->protonum) { |
| case IPPROTO_UDP: |
| case IPPROTO_TCP: |
| case IPPROTO_SCTP: |
| case IPPROTO_DCCP: |
| case IPPROTO_GRE: |
| case IPPROTO_UDPLITE: |
| this->l4src.all = other->l4dst.all; |
| this->l4dst.all = other->l4src.all; |
| break; |
| case IPPROTO_ICMP: |
| case IPPROTO_ICMPV6: |
| /* the setter already autocompletes the reply tuple. */ |
| break; |
| } |
| |
| /* XXX: this is safe but better convert bitset to uint64_t */ |
| ct->head.set[0] |= TS_ORIG | TS_REPL; |
| } |
| |
| static void setobjopt_undo_snat(struct nf_conntrack *ct) |
| { |
| switch (ct->head.orig.l3protonum) { |
| case AF_INET: |
| ct->snat.min_ip.v4 = ct->repl.dst.v4; |
| ct->snat.max_ip.v4 = ct->snat.min_ip.v4; |
| ct->repl.dst.v4 = ct->head.orig.src.v4; |
| set_bit(ATTR_SNAT_IPV4, ct->head.set); |
| break; |
| case AF_INET6: |
| memcpy(&ct->snat.min_ip.v6, &ct->repl.dst.v6, |
| sizeof(struct in6_addr)); |
| memcpy(&ct->snat.max_ip.v6, &ct->snat.min_ip.v6, |
| sizeof(struct in6_addr)); |
| memcpy(&ct->repl.dst.v6, &ct->head.orig.src.v6, |
| sizeof(struct in6_addr)); |
| set_bit(ATTR_SNAT_IPV6, ct->head.set); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void setobjopt_undo_dnat(struct nf_conntrack *ct) |
| { |
| switch (ct->head.orig.l3protonum) { |
| case AF_INET: |
| ct->dnat.min_ip.v4 = ct->repl.src.v4; |
| ct->dnat.max_ip.v4 = ct->dnat.min_ip.v4; |
| ct->repl.src.v4 = ct->head.orig.dst.v4; |
| set_bit(ATTR_DNAT_IPV4, ct->head.set); |
| case AF_INET6: |
| memcpy(&ct->dnat.min_ip.v6, &ct->repl.src.v6, |
| sizeof(struct in6_addr)); |
| memcpy(&ct->dnat.max_ip.v6, &ct->dnat.min_ip.v6, |
| sizeof(struct in6_addr)); |
| memcpy(&ct->repl.src.v6, &ct->head.orig.dst.v6, |
| sizeof(struct in6_addr)); |
| set_bit(ATTR_DNAT_IPV6, ct->head.set); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void setobjopt_undo_spat(struct nf_conntrack *ct) |
| { |
| ct->snat.l4min.all = ct->repl.l4dst.tcp.port; |
| ct->snat.l4max.all = ct->snat.l4min.all; |
| ct->repl.l4dst.tcp.port = |
| ct->head.orig.l4src.tcp.port; |
| set_bit(ATTR_SNAT_PORT, ct->head.set); |
| } |
| |
| static void setobjopt_undo_dpat(struct nf_conntrack *ct) |
| { |
| ct->dnat.l4min.all = ct->repl.l4src.tcp.port; |
| ct->dnat.l4max.all = ct->dnat.l4min.all; |
| ct->repl.l4src.tcp.port = |
| ct->head.orig.l4dst.tcp.port; |
| set_bit(ATTR_DNAT_PORT, ct->head.set); |
| } |
| |
| static void setobjopt_setup_orig(struct nf_conntrack *ct) |
| { |
| __autocomplete(ct, __DIR_ORIG); |
| } |
| |
| static void setobjopt_setup_repl(struct nf_conntrack *ct) |
| { |
| __autocomplete(ct, __DIR_REPL); |
| } |
| |
| static const setobjopt setobjopt_array[__NFCT_SOPT_MAX] = { |
| [NFCT_SOPT_UNDO_SNAT] = setobjopt_undo_snat, |
| [NFCT_SOPT_UNDO_DNAT] = setobjopt_undo_dnat, |
| [NFCT_SOPT_UNDO_SPAT] = setobjopt_undo_spat, |
| [NFCT_SOPT_UNDO_DPAT] = setobjopt_undo_dpat, |
| [NFCT_SOPT_SETUP_ORIGINAL] = setobjopt_setup_orig, |
| [NFCT_SOPT_SETUP_REPLY] = setobjopt_setup_repl, |
| }; |
| |
| int __setobjopt(struct nf_conntrack *ct, unsigned int option) |
| { |
| if (unlikely(option > NFCT_SOPT_MAX)) |
| return -1; |
| |
| setobjopt_array[option](ct); |
| return 0; |
| } |
| |
| static int getobjopt_is_snat(const struct nf_conntrack *ct) |
| { |
| if (!(test_bit(ATTR_STATUS, ct->head.set))) |
| return 0; |
| |
| if (!(ct->status & IPS_SRC_NAT_DONE)) |
| return 0; |
| |
| switch (ct->head.orig.l3protonum) { |
| case AF_INET: |
| return ct->repl.dst.v4 != ct->head.orig.src.v4; |
| case AF_INET6: |
| if (memcmp(&ct->repl.dst.v6, &ct->head.orig.src.v6, |
| sizeof(struct in6_addr)) != 0) |
| return 1; |
| else |
| return 0; |
| default: |
| return 0; |
| } |
| } |
| |
| static int getobjopt_is_dnat(const struct nf_conntrack *ct) |
| { |
| if (!(test_bit(ATTR_STATUS, ct->head.set))) |
| return 0; |
| |
| if (!(ct->status & IPS_DST_NAT_DONE)) |
| return 0; |
| |
| switch (ct->head.orig.l3protonum) { |
| case AF_INET: |
| return ct->repl.src.v4 != ct->head.orig.dst.v4; |
| case AF_INET6: |
| if (memcmp(&ct->repl.src.v6, &ct->head.orig.dst.v6, |
| sizeof(struct in6_addr)) != 0) |
| return 1; |
| else |
| return 0; |
| default: |
| return 0; |
| } |
| } |
| |
| static int getobjopt_is_spat(const struct nf_conntrack *ct) |
| { |
| return ((test_bit(ATTR_STATUS, ct->head.set) ? |
| ct->status & IPS_SRC_NAT_DONE : 1) && |
| ct->repl.l4dst.tcp.port != |
| ct->head.orig.l4src.tcp.port); |
| } |
| |
| static int getobjopt_is_dpat(const struct nf_conntrack *ct) |
| { |
| return ((test_bit(ATTR_STATUS, ct->head.set) ? |
| ct->status & IPS_DST_NAT_DONE : 1) && |
| ct->repl.l4src.tcp.port != |
| ct->head.orig.l4dst.tcp.port); |
| } |
| |
| static const getobjopt getobjopt_array[__NFCT_GOPT_MAX] = { |
| [NFCT_GOPT_IS_SNAT] = getobjopt_is_snat, |
| [NFCT_GOPT_IS_DNAT] = getobjopt_is_dnat, |
| [NFCT_GOPT_IS_SPAT] = getobjopt_is_spat, |
| [NFCT_GOPT_IS_DPAT] = getobjopt_is_dpat, |
| }; |
| |
| int __getobjopt(const struct nf_conntrack *ct, unsigned int option) |
| { |
| if (unlikely(option > NFCT_GOPT_MAX)) |
| return -1; |
| |
| return getobjopt_array[option](ct); |
| } |