]>
Commit | Line | Data |
---|---|---|
4e9a2b57 MT |
1 | #!/usr/bin/perl -w |
2 | ############################################################################ | |
3 | # # | |
4 | # This file is part of the IPFire Firewall. # | |
5 | # # | |
6 | # IPFire is free software; you can redistribute it and/or modify # | |
7 | # it under the terms of the GNU General Public License as published by # | |
8 | # the Free Software Foundation; either version 2 of the License, or # | |
9 | # (at your option) any later version. # | |
10 | # # | |
11 | # IPFire is distributed in the hope that it will be useful, # | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
14 | # GNU General Public License for more details. # | |
15 | # # | |
16 | # You should have received a copy of the GNU General Public License # | |
17 | # along with IPFire; if not, write to the Free Software # | |
18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # | |
19 | # # | |
20 | # Copyright (C) 2014 IPFire Team <info@ipfire.org>. # | |
21 | # # | |
22 | ############################################################################ | |
23 | ||
24 | package Network; | |
25 | ||
26 | use Socket; | |
27 | ||
28 | my %PREFIX2NETMASK = ( | |
29 | 32 => "255.255.255.255", | |
30 | 31 => "255.255.255.254", | |
31 | 30 => "255.255.255.252", | |
32 | 29 => "255.255.255.248", | |
33 | 28 => "255.255.255.240", | |
34 | 27 => "255.255.255.224", | |
35 | 26 => "255.255.255.192", | |
36 | 25 => "255.255.255.128", | |
37 | 24 => "255.255.255.0", | |
38 | 23 => "255.255.254.0", | |
39 | 22 => "255.255.252.0", | |
40 | 21 => "255.255.248.0", | |
41 | 20 => "255.255.240.0", | |
42 | 19 => "255.255.224.0", | |
43 | 18 => "255.255.192.0", | |
44 | 17 => "255.255.128.0", | |
45 | 16 => "255.255.0.0", | |
46 | 15 => "255.254.0.0", | |
47 | 14 => "255.252.0.0", | |
48 | 13 => "255.248.0.0", | |
49 | 12 => "255.240.0.0", | |
50 | 11 => "255.224.0.0", | |
51 | 10 => "255.192.0.0", | |
52 | 9 => "255.128.0.0", | |
53 | 8 => "255.0.0.0", | |
54 | 7 => "254.0.0.0", | |
55 | 6 => "252.0.0.0", | |
56 | 5 => "248.0.0.0", | |
57 | 4 => "240.0.0.0", | |
58 | 3 => "224.0.0.0", | |
59 | 2 => "192.0.0.0", | |
60 | 1 => "128.0.0.0", | |
61 | 0 => "0.0.0.0" | |
62 | ); | |
63 | ||
64 | my %NETMASK2PREFIX = reverse(%PREFIX2NETMASK); | |
65 | ||
66 | # Takes an IP address in dotted decimal notation and | |
67 | # returns a 32 bit integer representing that IP addresss. | |
68 | # Will return undef for invalid inputs. | |
69 | sub ip2bin($) { | |
70 | my $address = shift; | |
71 | ||
72 | # This function returns undef for undefined input. | |
73 | if (!defined $address) { | |
74 | return undef; | |
75 | } | |
76 | ||
77 | my $address_bin = &Socket::inet_pton(AF_INET, $address); | |
78 | if ($address_bin) { | |
79 | $address_bin = unpack('N', $address_bin); | |
80 | } | |
81 | ||
82 | return $address_bin; | |
83 | } | |
84 | ||
85 | # Does the reverse of ip2bin(). | |
86 | # Will return undef for invalid inputs. | |
87 | sub bin2ip($) { | |
88 | my $address_bin = shift; | |
89 | ||
90 | # This function returns undef for undefined input. | |
91 | if (!defined $address_bin) { | |
92 | return undef; | |
93 | } | |
94 | ||
95 | my $address = pack('N', $address_bin); | |
96 | if ($address) { | |
97 | $address = &Socket::inet_ntop(AF_INET, $address); | |
98 | } | |
99 | ||
100 | return $address; | |
101 | } | |
102 | ||
103 | # Takes a network in either a.b.c.d/a.b.c.d or a.b.c.d/e notation | |
104 | # and will return an 32 bit integer representing the start | |
105 | # address and an other one representing the network mask. | |
106 | sub network2bin($) { | |
107 | my $network = shift; | |
108 | ||
109 | my ($address, $netmask) = split(/\//, $network, 2); | |
110 | ||
111 | if (&check_prefix($netmask)) { | |
112 | $netmask = &convert_prefix2netmask($netmask); | |
113 | } | |
114 | ||
115 | my $address_bin = &ip2bin($address); | |
116 | my $netmask_bin = &ip2bin($netmask); | |
117 | ||
118 | my $network_start = $address_bin & $netmask_bin; | |
119 | ||
120 | return ($network_start, $netmask_bin); | |
121 | } | |
122 | ||
123 | # Returns True for all valid IP addresses | |
124 | sub check_ip_address($) { | |
125 | my $address = shift; | |
126 | ||
127 | # Normalise the IP address and compare the result with | |
128 | # the input - which should obviously the same. | |
129 | my $normalised_address = &_normalise_ip_address($address); | |
130 | ||
131 | return ((defined $normalised_address) && ($address eq $normalised_address)); | |
132 | } | |
133 | ||
134 | # Returns True for all valid prefixes. | |
135 | sub check_prefix($) { | |
136 | my $prefix = shift; | |
137 | ||
138 | return (exists $PREFIX2NETMASK{$prefix}); | |
139 | } | |
140 | ||
141 | # Returns True for all valid subnet masks. | |
142 | sub check_netmask($) { | |
143 | my $netmask = shift; | |
144 | ||
145 | return (exists $NETMASK2PREFIX{$netmask}); | |
146 | } | |
147 | ||
148 | # Returns True for all valid inputs like a.b.c.d/a.b.c.d. | |
149 | sub check_ip_address_and_netmask($$) { | |
150 | my $network = shift; | |
151 | ||
152 | my ($address, $netmask) = split(/\//, $network, 2); | |
153 | ||
154 | # Check if the IP address is fine. | |
155 | # | |
156 | my $result = &check_ip_address($address); | |
157 | unless ($result) { | |
158 | return $result; | |
159 | } | |
160 | ||
161 | return &check_netmask($netmask); | |
162 | } | |
163 | ||
164 | # For internal use only. Will take an IP address and | |
165 | # return it in a normalised style. Like 8.8.8.010 -> 8.8.8.8. | |
166 | sub _normalise_ip_address($) { | |
167 | my $address = shift; | |
168 | ||
169 | my $address_bin = &ip2bin($address); | |
170 | if (!defined $address_bin) { | |
171 | return undef; | |
172 | } | |
173 | ||
174 | return &bin2ip($address_bin); | |
175 | } | |
176 | ||
177 | # Returns the prefix for the given subnet mask. | |
178 | sub convert_netmask2prefix($) { | |
179 | my $netmask = shift; | |
180 | ||
181 | if (exists $NETMASK2PREFIX{$netmask}) { | |
182 | return $NETMASK2PREFIX{$netmask}; | |
183 | } | |
184 | ||
185 | return undef; | |
186 | } | |
187 | ||
188 | # Returns the subnet mask for the given prefix. | |
189 | sub convert_prefix2netmask($) { | |
190 | my $prefix = shift; | |
191 | ||
192 | if (exists $PREFIX2NETMASK{$prefix}) { | |
193 | return $PREFIX2NETMASK{$prefix}; | |
194 | } | |
195 | ||
196 | return undef; | |
197 | } | |
198 | ||
199 | # Takes an IP address and an offset and | |
200 | # will return the offset'th IP address. | |
201 | sub find_next_ip_address($$) { | |
202 | my $address = shift; | |
203 | my $offset = shift; | |
204 | ||
205 | my $address_bin = &ip2bin($address); | |
206 | $address_bin += $offset; | |
207 | ||
208 | return &bin2ip($address_bin); | |
209 | } | |
210 | ||
211 | # Returns the network address of the given network. | |
212 | sub get_netaddress($) { | |
213 | my $network = shift; | |
214 | my ($network_bin, $netmask_bin) = &network2bin($network); | |
215 | ||
216 | if (defined $network_bin) { | |
217 | return &bin2ip($network_bin); | |
218 | } | |
219 | ||
220 | return undef; | |
221 | } | |
222 | ||
223 | # Returns the broadcast of the given network. | |
224 | sub get_broadcast($) { | |
225 | my $network = shift; | |
226 | my ($network_bin, $netmask_bin) = &network2bin($network); | |
227 | ||
228 | return &bin2ip($network_bin ^ ~$netmask_bin); | |
229 | } | |
230 | ||
231 | # Returns True if $address is in $network. | |
232 | sub ip_address_in_network($$) { | |
233 | my $address = shift; | |
234 | my $network = shift; | |
235 | ||
236 | my $address_bin = &ip2bin($address); | |
237 | return undef unless (defined $address_bin); | |
238 | ||
239 | my ($network_bin, $netmask_bin) = &network2bin($network); | |
240 | ||
241 | # Find end address | |
242 | my $broadcast_bin = $network_bin ^ ~$netmask_bin; | |
243 | ||
244 | return (($address_bin ge $network_bin) && ($address_bin le $broadcast_bin)); | |
245 | } | |
246 | ||
247 | 1; | |
248 | ||
249 | # Remove the next line to enable the testsuite | |
250 | __END__ | |
251 | ||
252 | sub assert($) { | |
253 | my $ret = shift; | |
254 | ||
255 | if ($ret) { | |
256 | return; | |
257 | } | |
258 | ||
259 | print "ASSERTION ERROR"; | |
260 | exit(1); | |
261 | } | |
262 | ||
263 | sub testsuite() { | |
264 | my $result; | |
265 | ||
266 | my $address1 = &ip2bin("8.8.8.8"); | |
267 | assert($address1 == 134744072); | |
268 | ||
269 | my $address2 = &bin2ip($address1); | |
270 | assert($address2 eq "8.8.8.8"); | |
271 | ||
272 | # Check if valid IP addresses are correctly recognised. | |
273 | foreach my $address ("1.2.3.4", "192.168.180.1", "127.0.0.1") { | |
274 | if (!&check_ip_address($address)) { | |
275 | print "$address is not correctly recognised as a valid IP address!\n"; | |
276 | exit 1; | |
277 | }; | |
278 | } | |
279 | ||
280 | # Check if invalid IP addresses are correctly found. | |
281 | foreach my $address ("456.2.3.4", "192.768.180.1", "127.1", "1", "a.b.c.d", "1.2.3.4.5", "1.2.3.4/12") { | |
282 | if (&check_ip_address($address)) { | |
283 | print "$address is recognised as a valid IP address!\n"; | |
284 | exit 1; | |
285 | }; | |
286 | } | |
287 | ||
288 | $result = &check_ip_address_and_netmask("192.168.180.0/255.255.255.0"); | |
289 | assert($result); | |
290 | ||
291 | $result = &convert_netmask2prefix("255.255.254.0"); | |
292 | assert($result == 23); | |
293 | ||
294 | $result = &convert_prefix2netmask(8); | |
295 | assert($result eq "255.0.0.0"); | |
296 | ||
297 | $result = &find_next_ip_address("1.2.3.4", 2); | |
298 | assert($result eq "1.2.3.6"); | |
299 | ||
300 | $result = &ip_address_in_network("10.0.1.4", "10.0.0.0/8"); | |
301 | assert($result); | |
302 | ||
303 | return 0; | |
304 | } | |
305 | ||
306 | &testsuite(); |