1 /* 2 * (C) 2005-2011 by Pablo Neira Ayuso <pablo (at) netfilter.org> 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 */ 9 10 #include "internal/internal.h" 11 12 static void __autocomplete(struct nf_conntrack *ct, int dir) 13 { 14 struct __nfct_tuple *this = NULL, *other = NULL; 15 16 switch(dir) { 17 case __DIR_ORIG: 18 this = &ct->head.orig; 19 other = &ct->repl; 20 break; 21 case __DIR_REPL: 22 this = &ct->repl; 23 other = &ct->head.orig; 24 break; 25 } 26 27 this->l3protonum = other->l3protonum; 28 this->protonum = other->protonum; 29 30 memcpy(&this->src.v6, &other->dst.v6, sizeof(union __nfct_address)); 31 memcpy(&this->dst.v6, &other->src.v6, sizeof(union __nfct_address)); 32 33 switch(this->protonum) { 34 case IPPROTO_UDP: 35 case IPPROTO_TCP: 36 case IPPROTO_SCTP: 37 case IPPROTO_DCCP: 38 case IPPROTO_GRE: 39 case IPPROTO_UDPLITE: 40 this->l4src.all = other->l4dst.all; 41 this->l4dst.all = other->l4src.all; 42 break; 43 case IPPROTO_ICMP: 44 case IPPROTO_ICMPV6: 45 /* the setter already autocompletes the reply tuple. */ 46 break; 47 } 48 49 /* XXX: this is safe but better convert bitset to uint64_t */ 50 ct->head.set[0] |= TS_ORIG | TS_REPL; 51 } 52 53 static void setobjopt_undo_snat(struct nf_conntrack *ct) 54 { 55 switch (ct->head.orig.l3protonum) { 56 case AF_INET: 57 ct->snat.min_ip.v4 = ct->repl.dst.v4; 58 ct->snat.max_ip.v4 = ct->snat.min_ip.v4; 59 ct->repl.dst.v4 = ct->head.orig.src.v4; 60 set_bit(ATTR_SNAT_IPV4, ct->head.set); 61 break; 62 case AF_INET6: 63 memcpy(&ct->snat.min_ip.v6, &ct->repl.dst.v6, 64 sizeof(struct in6_addr)); 65 memcpy(&ct->snat.max_ip.v6, &ct->snat.min_ip.v6, 66 sizeof(struct in6_addr)); 67 memcpy(&ct->repl.dst.v6, &ct->head.orig.src.v6, 68 sizeof(struct in6_addr)); 69 set_bit(ATTR_SNAT_IPV6, ct->head.set); 70 break; 71 default: 72 break; 73 } 74 } 75 76 static void setobjopt_undo_dnat(struct nf_conntrack *ct) 77 { 78 switch (ct->head.orig.l3protonum) { 79 case AF_INET: 80 ct->dnat.min_ip.v4 = ct->repl.src.v4; 81 ct->dnat.max_ip.v4 = ct->dnat.min_ip.v4; 82 ct->repl.src.v4 = ct->head.orig.dst.v4; 83 set_bit(ATTR_DNAT_IPV4, ct->head.set); 84 case AF_INET6: 85 memcpy(&ct->dnat.min_ip.v6, &ct->repl.src.v6, 86 sizeof(struct in6_addr)); 87 memcpy(&ct->dnat.max_ip.v6, &ct->dnat.min_ip.v6, 88 sizeof(struct in6_addr)); 89 memcpy(&ct->repl.src.v6, &ct->head.orig.dst.v6, 90 sizeof(struct in6_addr)); 91 set_bit(ATTR_DNAT_IPV6, ct->head.set); 92 break; 93 default: 94 break; 95 } 96 } 97 98 static void setobjopt_undo_spat(struct nf_conntrack *ct) 99 { 100 ct->snat.l4min.all = ct->repl.l4dst.tcp.port; 101 ct->snat.l4max.all = ct->snat.l4min.all; 102 ct->repl.l4dst.tcp.port = 103 ct->head.orig.l4src.tcp.port; 104 set_bit(ATTR_SNAT_PORT, ct->head.set); 105 } 106 107 static void setobjopt_undo_dpat(struct nf_conntrack *ct) 108 { 109 ct->dnat.l4min.all = ct->repl.l4src.tcp.port; 110 ct->dnat.l4max.all = ct->dnat.l4min.all; 111 ct->repl.l4src.tcp.port = 112 ct->head.orig.l4dst.tcp.port; 113 set_bit(ATTR_DNAT_PORT, ct->head.set); 114 } 115 116 static void setobjopt_setup_orig(struct nf_conntrack *ct) 117 { 118 __autocomplete(ct, __DIR_ORIG); 119 } 120 121 static void setobjopt_setup_repl(struct nf_conntrack *ct) 122 { 123 __autocomplete(ct, __DIR_REPL); 124 } 125 126 static const setobjopt setobjopt_array[__NFCT_SOPT_MAX] = { 127 [NFCT_SOPT_UNDO_SNAT] = setobjopt_undo_snat, 128 [NFCT_SOPT_UNDO_DNAT] = setobjopt_undo_dnat, 129 [NFCT_SOPT_UNDO_SPAT] = setobjopt_undo_spat, 130 [NFCT_SOPT_UNDO_DPAT] = setobjopt_undo_dpat, 131 [NFCT_SOPT_SETUP_ORIGINAL] = setobjopt_setup_orig, 132 [NFCT_SOPT_SETUP_REPLY] = setobjopt_setup_repl, 133 }; 134 135 int __setobjopt(struct nf_conntrack *ct, unsigned int option) 136 { 137 if (unlikely(option > NFCT_SOPT_MAX)) 138 return -1; 139 140 setobjopt_array[option](ct); 141 return 0; 142 } 143 144 static int getobjopt_is_snat(const struct nf_conntrack *ct) 145 { 146 if (!(test_bit(ATTR_STATUS, ct->head.set))) 147 return 0; 148 149 if (!(ct->status & IPS_SRC_NAT_DONE)) 150 return 0; 151 152 switch (ct->head.orig.l3protonum) { 153 case AF_INET: 154 return ct->repl.dst.v4 != ct->head.orig.src.v4; 155 case AF_INET6: 156 if (memcmp(&ct->repl.dst.v6, &ct->head.orig.src.v6, 157 sizeof(struct in6_addr)) != 0) 158 return 1; 159 else 160 return 0; 161 default: 162 return 0; 163 } 164 } 165 166 static int getobjopt_is_dnat(const struct nf_conntrack *ct) 167 { 168 if (!(test_bit(ATTR_STATUS, ct->head.set))) 169 return 0; 170 171 if (!(ct->status & IPS_DST_NAT_DONE)) 172 return 0; 173 174 switch (ct->head.orig.l3protonum) { 175 case AF_INET: 176 return ct->repl.src.v4 != ct->head.orig.dst.v4; 177 case AF_INET6: 178 if (memcmp(&ct->repl.src.v6, &ct->head.orig.dst.v6, 179 sizeof(struct in6_addr)) != 0) 180 return 1; 181 else 182 return 0; 183 default: 184 return 0; 185 } 186 } 187 188 static int getobjopt_is_spat(const struct nf_conntrack *ct) 189 { 190 return ((test_bit(ATTR_STATUS, ct->head.set) ? 191 ct->status & IPS_SRC_NAT_DONE : 1) && 192 ct->repl.l4dst.tcp.port != 193 ct->head.orig.l4src.tcp.port); 194 } 195 196 static int getobjopt_is_dpat(const struct nf_conntrack *ct) 197 { 198 return ((test_bit(ATTR_STATUS, ct->head.set) ? 199 ct->status & IPS_DST_NAT_DONE : 1) && 200 ct->repl.l4src.tcp.port != 201 ct->head.orig.l4dst.tcp.port); 202 } 203 204 static const getobjopt getobjopt_array[__NFCT_GOPT_MAX] = { 205 [NFCT_GOPT_IS_SNAT] = getobjopt_is_snat, 206 [NFCT_GOPT_IS_DNAT] = getobjopt_is_dnat, 207 [NFCT_GOPT_IS_SPAT] = getobjopt_is_spat, 208 [NFCT_GOPT_IS_DPAT] = getobjopt_is_dpat, 209 }; 210 211 int __getobjopt(const struct nf_conntrack *ct, unsigned int option) 212 { 213 if (unlikely(option > NFCT_GOPT_MAX)) 214 return -1; 215 216 return getobjopt_array[option](ct); 217 } 218