1 /*
2  * (C) 2005-2011 by Pablo Neira Ayuso <pablo@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 
__autocomplete(struct nf_conntrack * ct,int dir)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 
setobjopt_undo_snat(struct nf_conntrack * ct)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 
setobjopt_undo_dnat(struct nf_conntrack * ct)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 
setobjopt_undo_spat(struct nf_conntrack * ct)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 
setobjopt_undo_dpat(struct nf_conntrack * ct)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 
setobjopt_setup_orig(struct nf_conntrack * ct)116 static void setobjopt_setup_orig(struct nf_conntrack *ct)
117 {
118 	__autocomplete(ct, __DIR_ORIG);
119 }
120 
setobjopt_setup_repl(struct nf_conntrack * ct)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 
__setobjopt(struct nf_conntrack * ct,unsigned int option)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 
getobjopt_is_snat(const struct nf_conntrack * ct)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 
getobjopt_is_dnat(const struct nf_conntrack * ct)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 
getobjopt_is_spat(const struct nf_conntrack * ct)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 
getobjopt_is_dpat(const struct nf_conntrack * ct)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 
__getobjopt(const struct nf_conntrack * ct,unsigned int option)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