1/*
2 * ip_nat_proto_gre.c - Version 1.11
3 *
4 * NAT protocol helper module for GRE.
5 *
6 * GRE is a generic encapsulation protocol, which is generally not very
7 * suited for NAT, as it has no protocol-specific part as port numbers.
8 *
9 * It has an optional key field, which may help us distinguishing two 
10 * connections between the same two hosts.
11 *
12 * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 
13 *
14 * PPTP is built on top of a modified version of GRE, and has a mandatory
15 * field called "CallID", which serves us for the same purpose as the key
16 * field in plain GRE.
17 *
18 * Documentation about PPTP can be found in RFC 2637
19 *
20 * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
21 *
22 * Development of this code funded by Astaro AG (http://www.astaro.com/)
23 *
24 */
25
26#include <linux/config.h>
27#include <linux/module.h>
28#include <linux/ip.h>
29#include <linux/netfilter_ipv4/ip_nat.h>
30#include <linux/netfilter_ipv4/ip_nat_rule.h>
31#include <linux/netfilter_ipv4/ip_nat_protocol.h>
32#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
33
34MODULE_LICENSE("GPL");
35MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
36MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE");
37
38#define DEBUGP(x, args...)
39
40/* is key in given range between min and max */
41static int
42gre_in_range(const struct ip_conntrack_tuple *tuple,
43	     enum ip_nat_manip_type maniptype,
44	     const union ip_conntrack_manip_proto *min,
45	     const union ip_conntrack_manip_proto *max)
46{
47	return ntohl(tuple->src.u.gre.key) >= ntohl(min->gre.key)
48		&& ntohl(tuple->src.u.gre.key) <= ntohl(max->gre.key);
49}
50
51/* generate unique tuple ... */
52static int 
53gre_unique_tuple(struct ip_conntrack_tuple *tuple,
54		 const struct ip_nat_range *range,
55		 enum ip_nat_manip_type maniptype,
56		 const struct ip_conntrack *conntrack)
57{
58	u_int32_t min, i, range_size;
59	u_int32_t key = 0, *keyptr;
60
61	if (maniptype == IP_NAT_MANIP_SRC)
62		keyptr = &tuple->src.u.gre.key;
63	else
64		keyptr = &tuple->dst.u.gre.key;
65
66	if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) {
67
68		switch (tuple->dst.u.gre.version) {
69		case 0:
70			DEBUGP("NATing GRE version 0 (ct=%p)\n",
71				conntrack);
72			min = 1;
73			range_size = 0xffffffff;
74			break;
75		case GRE_VERSION_PPTP:
76			DEBUGP("%p: NATing GRE PPTP\n", 
77				conntrack);
78			min = 1;
79			range_size = 0xffff;
80			break;
81		default:
82			printk(KERN_WARNING "nat_gre: unknown GRE version\n");
83			return 0;
84			break;
85		}
86
87	} else {
88		min = ntohl(range->min.gre.key);
89		range_size = ntohl(range->max.gre.key) - min + 1;
90	}
91
92	DEBUGP("min = %u, range_size = %u\n", min, range_size); 
93
94	for (i = 0; i < range_size; i++, key++) {
95		*keyptr = htonl(min + key % range_size);
96		if (!ip_nat_used_tuple(tuple, conntrack))
97			return 1;
98	}
99
100	DEBUGP("%p: no NAT mapping\n", conntrack);
101
102	return 0;
103}
104
105/* manipulate a GRE packet according to maniptype */
106static void 
107gre_manip_pkt(struct iphdr *iph, size_t len, 
108	      const struct ip_conntrack_manip *manip,
109	      enum ip_nat_manip_type maniptype)
110{
111	struct gre_hdr *greh = (struct gre_hdr *)((u_int32_t *)iph+iph->ihl);
112	struct gre_hdr_pptp *pgreh = (struct gre_hdr_pptp *) greh;
113
114	/* we only have destination manip of a packet, since 'source key' 
115	 * is not present in the packet itself */
116	if (maniptype == IP_NAT_MANIP_DST) {
117		/* key manipulation is always dest */
118		switch (greh->version) {
119		case 0:
120			if (!greh->key) {
121				DEBUGP("can't nat GRE w/o key\n");
122				break;
123			}
124			if (greh->csum) {
125				*(gre_csum(greh)) = 
126					ip_nat_cheat_check(~*(gre_key(greh)),
127							manip->u.gre.key,
128							*(gre_csum(greh)));
129			}
130			*(gre_key(greh)) = manip->u.gre.key;
131			break;
132		case GRE_VERSION_PPTP:
133			DEBUGP("call_id -> 0x%04x\n", 
134				ntohl(manip->u.gre.key));
135			pgreh->call_id = htons(ntohl(manip->u.gre.key));
136			break;
137		default:
138			DEBUGP("can't nat unknown GRE version\n");
139			break;
140		}
141	}
142}
143
144/* print out a nat tuple */
145static unsigned int 
146gre_print(char *buffer, 
147	  const struct ip_conntrack_tuple *match,
148	  const struct ip_conntrack_tuple *mask)
149{
150	unsigned int len = 0;
151
152	if (mask->dst.u.gre.version)
153		len += sprintf(buffer + len, "version=%d ",
154				ntohs(match->dst.u.gre.version));
155
156	if (mask->dst.u.gre.protocol)
157		len += sprintf(buffer + len, "protocol=0x%x ",
158				ntohs(match->dst.u.gre.protocol));
159
160	if (mask->src.u.gre.key)
161		len += sprintf(buffer + len, "srckey=0x%x ", 
162				ntohl(match->src.u.gre.key));
163
164	if (mask->dst.u.gre.key)
165		len += sprintf(buffer + len, "dstkey=0x%x ",
166				ntohl(match->src.u.gre.key));
167
168	return len;
169}
170
171/* print a range of keys */
172static unsigned int 
173gre_print_range(char *buffer, const struct ip_nat_range *range)
174{
175	if (range->min.gre.key != 0 
176	    || range->max.gre.key != 0xFFFF) {
177		if (range->min.gre.key == range->max.gre.key)
178			return sprintf(buffer, "key 0x%x ",
179					ntohl(range->min.gre.key));
180		else
181			return sprintf(buffer, "keys 0x%u-0x%u ",
182					ntohl(range->min.gre.key),
183					ntohl(range->max.gre.key));
184	} else
185		return 0;
186}
187
188/* nat helper struct */
189static struct ip_nat_protocol gre = 
190	{ { NULL, NULL }, "GRE", IPPROTO_GRE,
191	  gre_manip_pkt,
192	  gre_in_range,
193	  gre_unique_tuple,
194	  gre_print,
195	  gre_print_range 
196	};
197				  
198static int __init init(void)
199{
200        if (ip_nat_protocol_register(&gre))
201                return -EIO;
202
203        return 0;
204}
205
206static void __exit fini(void)
207{
208        ip_nat_protocol_unregister(&gre);
209}
210
211module_init(init);
212module_exit(fini);
213