summaryrefslogtreecommitdiff
path: root/net/ipv4/netfilter/ip_nat_proto_gre.c
blob: 38acfdf540ebee653c74eb27a448697a43e75d83 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
 * ip_nat_proto_gre.c - Version 2.0
 *
 * NAT protocol helper module for GRE.
 *
 * GRE is a generic encapsulation protocol, which is generally not very
 * suited for NAT, as it has no protocol-specific part as port numbers.
 *
 * It has an optional key field, which may help us distinguishing two 
 * connections between the same two hosts.
 *
 * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 
 *
 * PPTP is built on top of a modified version of GRE, and has a mandatory
 * field called "CallID", which serves us for the same purpose as the key
 * field in plain GRE.
 *
 * Documentation about PPTP can be found in RFC 2637
 *
 * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
 *
 * Development of this code funded by Astaro AG (http://www.astaro.com/)
 *
 */

#include <linux/module.h>
#include <linux/ip.h>
#include <linux/netfilter_ipv4/ip_nat.h>
#include <linux/netfilter_ipv4/ip_nat_rule.h>
#include <linux/netfilter_ipv4/ip_nat_protocol.h>
#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE");

#if 0
#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, \
				       __FUNCTION__, ## args)
#else
#define DEBUGP(x, args...)
#endif

/* is key in given range between min and max */
static int
gre_in_range(const struct ip_conntrack_tuple *tuple,
	     enum ip_nat_manip_type maniptype,
	     const union ip_conntrack_manip_proto *min,
	     const union ip_conntrack_manip_proto *max)
{
	__be16 key;

	if (maniptype == IP_NAT_MANIP_SRC)
		key = tuple->src.u.gre.key;
	else
		key = tuple->dst.u.gre.key;

	return ntohs(key) >= ntohs(min->gre.key)
		&& ntohs(key) <= ntohs(max->gre.key);
}

/* generate unique tuple ... */
static int 
gre_unique_tuple(struct ip_conntrack_tuple *tuple,
		 const struct ip_nat_range *range,
		 enum ip_nat_manip_type maniptype,
		 const struct ip_conntrack *conntrack)
{
	static u_int16_t key;
	u_int16_t *keyptr;
	unsigned int min, i, range_size;

	if (maniptype == IP_NAT_MANIP_SRC)
		keyptr = &tuple->src.u.gre.key;
	else
		keyptr = &tuple->dst.u.gre.key;

	if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) {
		DEBUGP("%p: NATing GRE PPTP\n", conntrack);
		min = 1;
		range_size = 0xffff;
	} else {
		min = ntohs(range->min.gre.key);
		range_size = ntohs(range->max.gre.key) - min + 1;
	}

	DEBUGP("min = %u, range_size = %u\n", min, range_size); 

	for (i = 0; i < range_size; i++, key++) {
		*keyptr = htons(min + key % range_size);
		if (!ip_nat_used_tuple(tuple, conntrack))
			return 1;
	}

	DEBUGP("%p: no NAT mapping\n", conntrack);

	return 0;
}

/* manipulate a GRE packet according to maniptype */
static int
gre_manip_pkt(struct sk_buff **pskb,
	      unsigned int iphdroff,
	      const struct ip_conntrack_tuple *tuple,
	      enum ip_nat_manip_type maniptype)
{
	struct gre_hdr *greh;
	struct gre_hdr_pptp *pgreh;
	struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff);
	unsigned int hdroff = iphdroff + iph->ihl*4;

	/* pgreh includes two optional 32bit fields which are not required
	 * to be there.  That's where the magic '8' comes from */
	if (!skb_make_writable(pskb, hdroff + sizeof(*pgreh)-8))
		return 0;

	greh = (void *)(*pskb)->data + hdroff;
	pgreh = (struct gre_hdr_pptp *) greh;

	/* we only have destination manip of a packet, since 'source key' 
	 * is not present in the packet itself */
	if (maniptype == IP_NAT_MANIP_DST) {
		/* key manipulation is always dest */
		switch (greh->version) {
		case 0:
			if (!greh->key) {
				DEBUGP("can't nat GRE w/o key\n");
				break;
			}
			if (greh->csum) {
				/* FIXME: Never tested this code... */
				*(gre_csum(greh)) = 
					ip_nat_cheat_check(~*(gre_key(greh)),
							tuple->dst.u.gre.key,
							*(gre_csum(greh)));
			}
			*(gre_key(greh)) = tuple->dst.u.gre.key;
			break;
		case GRE_VERSION_PPTP:
			DEBUGP("call_id -> 0x%04x\n", 
				ntohs(tuple->dst.u.gre.key));
			pgreh->call_id = tuple->dst.u.gre.key;
			break;
		default:
			DEBUGP("can't nat unknown GRE version\n");
			return 0;
			break;
		}
	}
	return 1;
}

/* nat helper struct */
static struct ip_nat_protocol gre = { 
	.name		= "GRE", 
	.protonum	= IPPROTO_GRE,
	.manip_pkt	= gre_manip_pkt,
	.in_range	= gre_in_range,
	.unique_tuple	= gre_unique_tuple,
#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
    defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
	.range_to_nfattr	= ip_nat_port_range_to_nfattr,
	.nfattr_to_range	= ip_nat_port_nfattr_to_range,
#endif
};
				  
int __init ip_nat_proto_gre_init(void)
{
	return ip_nat_protocol_register(&gre);
}

void __exit ip_nat_proto_gre_fini(void)
{
	ip_nat_protocol_unregister(&gre);
}