Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* Same. Just like SNAT, only try to make the connections |
2 | * between client A and server B always have the same source ip. | |
3 | * | |
4 | * (C) 2000 Paul `Rusty' Russell | |
5 | * (C) 2001 Martin Josefsson | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
1da177e4 LT |
10 | */ |
11 | #include <linux/types.h> | |
12 | #include <linux/ip.h> | |
13 | #include <linux/timer.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/netfilter.h> | |
16 | #include <linux/netdevice.h> | |
17 | #include <linux/if.h> | |
18 | #include <linux/inetdevice.h> | |
19 | #include <net/protocol.h> | |
20 | #include <net/checksum.h> | |
21 | #include <linux/netfilter_ipv4.h> | |
6709dbbb | 22 | #include <linux/netfilter/x_tables.h> |
5b1158e9 | 23 | #include <net/netfilter/nf_nat_rule.h> |
1da177e4 LT |
24 | #include <linux/netfilter_ipv4/ipt_SAME.h> |
25 | ||
26 | MODULE_LICENSE("GPL"); | |
27 | MODULE_AUTHOR("Martin Josefsson <gandalf@wlug.westbo.se>"); | |
28 | MODULE_DESCRIPTION("iptables special SNAT module for consistent sourceip"); | |
29 | ||
e1931b78 | 30 | static bool |
d3c5ee6d JE |
31 | same_tg_check(const char *tablename, const void *e, |
32 | const struct xt_target *target, void *targinfo, | |
33 | unsigned int hook_mask) | |
1da177e4 LT |
34 | { |
35 | unsigned int count, countess, rangeip, index = 0; | |
36 | struct ipt_same_info *mr = targinfo; | |
37 | ||
38 | mr->ipnum = 0; | |
39 | ||
1da177e4 | 40 | if (mr->rangesize < 1) { |
0d53778e | 41 | pr_debug("same_check: need at least one dest range.\n"); |
e1931b78 | 42 | return false; |
1da177e4 LT |
43 | } |
44 | if (mr->rangesize > IPT_SAME_MAX_RANGE) { | |
0d53778e PM |
45 | pr_debug("same_check: too many ranges specified, maximum " |
46 | "is %u ranges\n", IPT_SAME_MAX_RANGE); | |
e1931b78 | 47 | return false; |
1da177e4 LT |
48 | } |
49 | for (count = 0; count < mr->rangesize; count++) { | |
50 | if (ntohl(mr->range[count].min_ip) > | |
51 | ntohl(mr->range[count].max_ip)) { | |
0d53778e PM |
52 | pr_debug("same_check: min_ip is larger than max_ip in " |
53 | "range `%u.%u.%u.%u-%u.%u.%u.%u'.\n", | |
54 | NIPQUAD(mr->range[count].min_ip), | |
55 | NIPQUAD(mr->range[count].max_ip)); | |
e1931b78 | 56 | return false; |
1da177e4 LT |
57 | } |
58 | if (!(mr->range[count].flags & IP_NAT_RANGE_MAP_IPS)) { | |
0d53778e | 59 | pr_debug("same_check: bad MAP_IPS.\n"); |
e1931b78 | 60 | return false; |
1da177e4 | 61 | } |
e905a9ed | 62 | rangeip = (ntohl(mr->range[count].max_ip) - |
1da177e4 LT |
63 | ntohl(mr->range[count].min_ip) + 1); |
64 | mr->ipnum += rangeip; | |
e905a9ed | 65 | |
0d53778e | 66 | pr_debug("same_check: range %u, ipnum = %u\n", count, rangeip); |
1da177e4 | 67 | } |
0d53778e | 68 | pr_debug("same_check: total ipaddresses = %u\n", mr->ipnum); |
e905a9ed | 69 | |
1da177e4 LT |
70 | mr->iparray = kmalloc((sizeof(u_int32_t) * mr->ipnum), GFP_KERNEL); |
71 | if (!mr->iparray) { | |
0d53778e PM |
72 | pr_debug("same_check: Couldn't allocate %Zu bytes " |
73 | "for %u ipaddresses!\n", | |
74 | (sizeof(u_int32_t) * mr->ipnum), mr->ipnum); | |
e1931b78 | 75 | return false; |
1da177e4 | 76 | } |
0d53778e PM |
77 | pr_debug("same_check: Allocated %Zu bytes for %u ipaddresses.\n", |
78 | (sizeof(u_int32_t) * mr->ipnum), mr->ipnum); | |
e905a9ed | 79 | |
1da177e4 LT |
80 | for (count = 0; count < mr->rangesize; count++) { |
81 | for (countess = ntohl(mr->range[count].min_ip); | |
82 | countess <= ntohl(mr->range[count].max_ip); | |
83 | countess++) { | |
84 | mr->iparray[index] = countess; | |
0d53778e PM |
85 | pr_debug("same_check: Added ipaddress `%u.%u.%u.%u' " |
86 | "in index %u.\n", HIPQUAD(countess), index); | |
1da177e4 LT |
87 | index++; |
88 | } | |
89 | } | |
e1931b78 | 90 | return true; |
1da177e4 LT |
91 | } |
92 | ||
d3c5ee6d | 93 | static void same_tg_destroy(const struct xt_target *target, void *targinfo) |
1da177e4 LT |
94 | { |
95 | struct ipt_same_info *mr = targinfo; | |
96 | ||
97 | kfree(mr->iparray); | |
e905a9ed | 98 | |
0d53778e PM |
99 | pr_debug("same_destroy: Deallocated %Zu bytes for %u ipaddresses.\n", |
100 | (sizeof(u_int32_t) * mr->ipnum), mr->ipnum); | |
1da177e4 LT |
101 | } |
102 | ||
103 | static unsigned int | |
d3c5ee6d JE |
104 | same_tg(struct sk_buff *skb, const struct net_device *in, |
105 | const struct net_device *out, unsigned int hooknum, | |
106 | const struct xt_target *target, const void *targinfo) | |
1da177e4 | 107 | { |
587aa641 | 108 | struct nf_conn *ct; |
1da177e4 | 109 | enum ip_conntrack_info ctinfo; |
6a19d614 AV |
110 | u_int32_t tmpip, aindex; |
111 | __be32 new_ip; | |
1da177e4 | 112 | const struct ipt_same_info *same = targinfo; |
587aa641 PM |
113 | struct nf_nat_range newrange; |
114 | const struct nf_conntrack_tuple *t; | |
1da177e4 | 115 | |
6e23ae2a PM |
116 | NF_CT_ASSERT(hooknum == NF_INET_PRE_ROUTING || |
117 | hooknum == NF_INET_POST_ROUTING); | |
3db05fea | 118 | ct = nf_ct_get(skb, &ctinfo); |
1da177e4 LT |
119 | |
120 | t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; | |
121 | ||
122 | /* Base new source on real src ip and optionally dst ip, | |
123 | giving some hope for consistency across reboots. | |
124 | Here we calculate the index in same->iparray which | |
125 | holds the ipaddress we should use */ | |
e905a9ed | 126 | |
5b1158e9 JK |
127 | tmpip = ntohl(t->src.u3.ip); |
128 | ||
129 | if (!(same->info & IPT_SAME_NODST)) | |
130 | tmpip += ntohl(t->dst.u3.ip); | |
1da177e4 LT |
131 | aindex = tmpip % same->ipnum; |
132 | ||
133 | new_ip = htonl(same->iparray[aindex]); | |
134 | ||
0d53778e PM |
135 | pr_debug("ipt_SAME: src=%u.%u.%u.%u dst=%u.%u.%u.%u, " |
136 | "new src=%u.%u.%u.%u\n", | |
137 | NIPQUAD(t->src.u3.ip), NIPQUAD(t->dst.u3.ip), NIPQUAD(new_ip)); | |
1da177e4 LT |
138 | |
139 | /* Transfer from original range. */ | |
587aa641 | 140 | newrange = ((struct nf_nat_range) |
1da177e4 LT |
141 | { same->range[0].flags, new_ip, new_ip, |
142 | /* FIXME: Use ports from correct range! */ | |
143 | same->range[0].min, same->range[0].max }); | |
144 | ||
145 | /* Hand modified range to generic setup. */ | |
587aa641 | 146 | return nf_nat_setup_info(ct, &newrange, hooknum); |
1da177e4 LT |
147 | } |
148 | ||
d3c5ee6d | 149 | static struct xt_target same_tg_reg __read_mostly = { |
1da177e4 | 150 | .name = "SAME", |
6709dbbb | 151 | .family = AF_INET, |
d3c5ee6d | 152 | .target = same_tg, |
1d5cd909 PM |
153 | .targetsize = sizeof(struct ipt_same_info), |
154 | .table = "nat", | |
6e23ae2a PM |
155 | .hooks = (1 << NF_INET_PRE_ROUTING) | |
156 | (1 << NF_INET_POST_ROUTING), | |
d3c5ee6d JE |
157 | .checkentry = same_tg_check, |
158 | .destroy = same_tg_destroy, | |
1da177e4 LT |
159 | .me = THIS_MODULE, |
160 | }; | |
161 | ||
d3c5ee6d | 162 | static int __init same_tg_init(void) |
1da177e4 | 163 | { |
d3c5ee6d | 164 | return xt_register_target(&same_tg_reg); |
1da177e4 LT |
165 | } |
166 | ||
d3c5ee6d | 167 | static void __exit same_tg_exit(void) |
1da177e4 | 168 | { |
d3c5ee6d | 169 | xt_unregister_target(&same_tg_reg); |
1da177e4 LT |
170 | } |
171 | ||
d3c5ee6d JE |
172 | module_init(same_tg_init); |
173 | module_exit(same_tg_exit); | |
1da177e4 | 174 |