File: /usr/src/linux/net/ipv4/netfilter/ip_fw_compat_masq.c

1     /* Masquerading compatibility layer.
2     
3        Note that there are no restrictions on other programs binding to
4        ports 61000:65095 (in 2.0 and 2.2 they get EADDRINUSE).  Just DONT
5        DO IT.
6      */
7     #include <linux/skbuff.h>
8     #include <linux/in.h>
9     #include <linux/ip.h>
10     #include <linux/icmp.h>
11     #include <linux/udp.h>
12     #include <linux/netfilter_ipv4.h>
13     #include <linux/netdevice.h>
14     #include <linux/inetdevice.h>
15     #include <linux/proc_fs.h>
16     #include <linux/version.h>
17     #include <linux/module.h>
18     #include <net/route.h>
19     
20     #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock)
21     #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_conntrack_lock)
22     
23     #include <linux/netfilter_ipv4/ip_conntrack.h>
24     #include <linux/netfilter_ipv4/ip_conntrack_core.h>
25     #include <linux/netfilter_ipv4/ip_nat.h>
26     #include <linux/netfilter_ipv4/ip_nat_core.h>
27     #include <linux/netfilter_ipv4/listhelp.h>
28     
29     #if 0
30     #define DEBUGP printk
31     #else
32     #define DEBUGP(format, args...)
33     #endif
34     
35     unsigned int
36     do_masquerade(struct sk_buff **pskb, const struct net_device *dev)
37     {
38     	struct iphdr *iph = (*pskb)->nh.iph;
39     	struct ip_nat_info *info;
40     	enum ip_conntrack_info ctinfo;
41     	struct ip_conntrack *ct;
42     	unsigned int ret;
43     
44     	/* Sorry, only ICMP, TCP and UDP. */
45     	if (iph->protocol != IPPROTO_ICMP
46     	    && iph->protocol != IPPROTO_TCP
47     	    && iph->protocol != IPPROTO_UDP)
48     		return NF_DROP;
49     
50     	/* Feed it to connection tracking; in fact we're in NF_IP_FORWARD,
51                but connection tracking doesn't expect that */
52     	ret = ip_conntrack_in(NF_IP_POST_ROUTING, pskb, dev, NULL, NULL);
53     	if (ret != NF_ACCEPT) {
54     		DEBUGP("ip_conntrack_in returned %u.\n", ret);
55     		return ret;
56     	}
57     
58     	ct = ip_conntrack_get(*pskb, &ctinfo);
59     
60     	if (!ct) {
61     		DEBUGP("ip_conntrack_in set to invalid conntrack.\n");
62     		return NF_DROP;
63     	}
64     
65     	info = &ct->nat.info;
66     
67     	WRITE_LOCK(&ip_nat_lock);
68     	/* Setup the masquerade, if not already */
69     	if (!info->initialized) {
70     		u_int32_t newsrc;
71     		struct rtable *rt;
72     		struct ip_nat_multi_range range;
73     
74     		/* Pass 0 instead of saddr, since it's going to be changed
75     		   anyway. */
76     		if (ip_route_output(&rt, iph->daddr, 0, 0, 0) != 0) {
77     			DEBUGP("ipnat_rule_masquerade: Can't reroute.\n");
78     			return NF_DROP;
79     		}
80     		newsrc = inet_select_addr(rt->u.dst.dev, rt->rt_gateway,
81     					  RT_SCOPE_UNIVERSE);
82     		ip_rt_put(rt);
83     		range = ((struct ip_nat_multi_range)
84     			 { 1,
85     			   {{IP_NAT_RANGE_MAP_IPS|IP_NAT_RANGE_PROTO_SPECIFIED,
86     			     newsrc, newsrc,
87     			     { htons(61000) }, { htons(65095) } } } });
88     
89     		ret = ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING);
90     		if (ret != NF_ACCEPT) {
91     			WRITE_UNLOCK(&ip_nat_lock);
92     			return ret;
93     		}
94     
95     		place_in_hashes(ct, info);
96     		info->initialized = 1;
97     	} else
98     		DEBUGP("Masquerading already done on this conn.\n");
99     	WRITE_UNLOCK(&ip_nat_lock);
100     
101     	return do_bindings(ct, ctinfo, info, NF_IP_POST_ROUTING, pskb);
102     }
103     
104     void
105     check_for_masq_error(struct sk_buff *skb)
106     {
107     	enum ip_conntrack_info ctinfo;
108     	struct ip_conntrack *ct;
109     
110     	ct = ip_conntrack_get(skb, &ctinfo);
111     	/* Wouldn't be here if not tracked already => masq'ed ICMP
112                ping or error related to masq'd connection */
113     	IP_NF_ASSERT(ct);
114     	if (ctinfo == IP_CT_RELATED) {
115     		icmp_reply_translation(skb, ct, NF_IP_PRE_ROUTING,
116     				       CTINFO2DIR(ctinfo));
117     		icmp_reply_translation(skb, ct, NF_IP_POST_ROUTING,
118     				       CTINFO2DIR(ctinfo));
119     	}
120     }
121     
122     unsigned int
123     check_for_demasq(struct sk_buff **pskb)
124     {
125     	struct ip_conntrack_tuple tuple;
126     	struct iphdr *iph = (*pskb)->nh.iph;
127     	struct ip_conntrack_protocol *protocol;
128     	struct ip_conntrack_tuple_hash *h;
129     	enum ip_conntrack_info ctinfo;
130     	struct ip_conntrack *ct;
131     	int ret;
132     
133     	protocol = find_proto(iph->protocol);
134     
135     	/* We don't feed packets to conntrack system unless we know
136                they're part of an connection already established by an
137                explicit masq command. */
138     	switch (iph->protocol) {
139     	case IPPROTO_ICMP:
140     		/* ICMP errors. */
141     		ct = icmp_error_track(*pskb, &ctinfo, NF_IP_PRE_ROUTING);
142     		if (ct) {
143     			/* We only do SNAT in the compatibility layer.
144     			   So we can manipulate ICMP errors from
145     			   server here (== DNAT).  Do SNAT icmp manips
146     			   in POST_ROUTING handling. */
147     			if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
148     				icmp_reply_translation(*pskb, ct,
149     						       NF_IP_PRE_ROUTING,
150     						       CTINFO2DIR(ctinfo));
151     				icmp_reply_translation(*pskb, ct,
152     						       NF_IP_POST_ROUTING,
153     						       CTINFO2DIR(ctinfo));
154     			}
155     			return NF_ACCEPT;
156     		}
157     		/* Fall thru... */
158     	case IPPROTO_TCP:
159     	case IPPROTO_UDP:
160     		IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0);
161     
162     		if (!get_tuple(iph, (*pskb)->len, &tuple, protocol)) {
163     			if (net_ratelimit())
164     				printk("ip_fw_compat_masq: Can't get tuple\n");
165     			return NF_ACCEPT;
166     		}
167     		break;
168     
169     	default:
170     		/* Not ours... */
171     		return NF_ACCEPT;
172     	}
173     	h = ip_conntrack_find_get(&tuple, NULL);
174     
175     	/* MUST be found, and MUST be reply. */
176     	if (h && DIRECTION(h) == 1) {
177     		ret = ip_conntrack_in(NF_IP_PRE_ROUTING, pskb,
178     				      NULL, NULL, NULL);
179     
180     		/* Put back the reference gained from find_get */
181     		nf_conntrack_put(&h->ctrack->infos[0]);
182     		if (ret == NF_ACCEPT) {
183     			struct ip_conntrack *ct;
184     			ct = ip_conntrack_get(*pskb, &ctinfo);
185     
186     			if (ct) {
187     				struct ip_nat_info *info = &ct->nat.info;
188     
189     				do_bindings(ct, ctinfo, info,
190     					    NF_IP_PRE_ROUTING,
191     					    pskb);
192     			} else
193     				if (net_ratelimit()) 
194     					printk("ip_fw_compat_masq: conntrack"
195     					       " didn't like\n");
196     		}
197     	} else {
198     		if (h)
199     			/* Put back the reference gained from find_get */
200     			nf_conntrack_put(&h->ctrack->infos[0]);
201     		ret = NF_ACCEPT;
202     	}
203     
204     	return ret;
205     }
206     
207     int ip_fw_masq_timeouts(void *user, int len)
208     {
209     	printk("Sorry: masquerading timeouts set 5DAYS/2MINS/60SECS\n");
210     	return 0;
211     }
212     
213     static const char *masq_proto_name(u_int16_t protonum)
214     {
215     	switch (protonum) {
216     	case IPPROTO_TCP: return "TCP";
217     	case IPPROTO_UDP: return "UDP";
218     	case IPPROTO_ICMP: return "ICMP";
219     	default: return "MORE-CAFFIENE-FOR-RUSTY";
220     	}
221     }
222     
223     static unsigned int
224     print_masq(char *buffer, const struct ip_conntrack *conntrack)
225     {
226     	char temp[129];
227     
228     	/* This is for backwards compatibility, but ick!.
229     	   We should never export jiffies to userspace.
230     	*/
231     	sprintf(temp,"%s %08X:%04X %08X:%04X %04X %08X %6d %6d %7lu",
232     		masq_proto_name(conntrack->tuplehash[0].tuple.dst.protonum),
233     		ntohl(conntrack->tuplehash[0].tuple.src.ip),
234     		ntohs(conntrack->tuplehash[0].tuple.src.u.all),
235     		ntohl(conntrack->tuplehash[0].tuple.dst.ip),
236     		ntohs(conntrack->tuplehash[0].tuple.dst.u.all),
237     		ntohs(conntrack->tuplehash[1].tuple.dst.u.all),
238     		/* Sorry, no init_seq, delta or previous_delta (yet). */
239     		0, 0, 0,
240     		conntrack->timeout.expires - jiffies);
241     
242     	return sprintf(buffer, "%-127s\n", temp);
243     }
244     
245     /* Returns true when finished. */
246     static int
247     masq_iterate(const struct ip_conntrack_tuple_hash *hash,
248     	     char *buffer, off_t offset, off_t *upto,
249     	     unsigned int *len, unsigned int maxlen)
250     {
251     	unsigned int newlen;
252     
253     	IP_NF_ASSERT(hash->ctrack);
254     
255     	/* Only count originals */
256     	if (DIRECTION(hash))
257     		return 0;
258     
259     	if ((*upto)++ < offset)
260     		return 0;
261     
262     	newlen = print_masq(buffer + *len, hash->ctrack);
263     	if (*len + newlen > maxlen)
264     		return 1;
265     	else *len += newlen;
266     
267     	return 0;
268     }
269     
270     /* Everything in the hash is masqueraded. */
271     static int
272     masq_procinfo(char *buffer, char **start, off_t offset, int length)
273     {
274     	unsigned int i;
275     	int len = 0;
276     	off_t upto = 1;
277     
278     	/* Header: first record */
279     	if (offset == 0) {
280     		char temp[128];
281     
282     		sprintf(temp,
283     			"Prc FromIP   FPrt ToIP     TPrt Masq Init-seq  Delta PDelta Expires (free=0,0,0)");
284     		len = sprintf(buffer, "%-127s\n", temp);
285     		offset = 1;
286     	}
287     
288     	READ_LOCK(&ip_conntrack_lock);
289     	/* Traverse hash; print originals then reply. */
290     	for (i = 0; i < ip_conntrack_htable_size; i++) {
291     		if (LIST_FIND(&ip_conntrack_hash[i], masq_iterate,
292     			      struct ip_conntrack_tuple_hash *,
293     			      buffer, offset, &upto, &len, length))
294     			break;
295     	}
296     	READ_UNLOCK(&ip_conntrack_lock);
297     
298     	/* `start' hack - see fs/proc/generic.c line ~165 */
299     	*start = (char *)((unsigned int)upto - offset);
300     	return len;
301     }
302     
303     int __init masq_init(void)
304     {
305     	int ret;
306     	struct proc_dir_entry *proc;
307     
308     	ret = ip_conntrack_init();
309     	if (ret == 0) {
310     		ret = ip_nat_init();
311     		if (ret == 0) {
312     			proc = proc_net_create("ip_masquerade",
313     					       0, masq_procinfo);
314     			if (proc)
315     				proc->owner = THIS_MODULE;
316     			else {
317     				ip_nat_cleanup();
318     				ip_conntrack_cleanup();
319     				ret = -ENOMEM;
320     			}
321     		} else
322     			ip_conntrack_cleanup();
323     	}
324     
325     	return ret;
326     }
327     
328     void masq_cleanup(void)
329     {
330     	ip_nat_cleanup();
331     	ip_conntrack_cleanup();
332     	proc_net_remove("ip_masquerade");
333     }
334