Rev 65 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 65 | Rev 97 | ||
---|---|---|---|
1 | <?php |
1 | <?php |
2 | 2 | ||
3 | /* |
3 | /* |
4 | * IPv6 functions for PHP |
4 | * IPv6 functions for PHP |
5 | * Copyright 2012-2021 Daniel Marschall, ViaThinkSoft |
5 | * Copyright 2012-2022 Daniel Marschall, ViaThinkSoft |
6 | * Version 2021-05-21 |
6 | * Version 2022-09-22 |
7 | * |
7 | * |
8 | * Licensed under the Apache License, Version 2.0 (the "License"); |
8 | * Licensed under the Apache License, Version 2.0 (the "License"); |
9 | * you may not use this file except in compliance with the License. |
9 | * you may not use this file except in compliance with the License. |
10 | * You may obtain a copy of the License at |
10 | * You may obtain a copy of the License at |
11 | * |
11 | * |
12 | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | * http://www.apache.org/licenses/LICENSE-2.0 |
13 | * |
13 | * |
14 | * Unless required by applicable law or agreed to in writing, software |
14 | * Unless required by applicable law or agreed to in writing, software |
15 | * distributed under the License is distributed on an "AS IS" BASIS, |
15 | * distributed under the License is distributed on an "AS IS" BASIS, |
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
17 | * See the License for the specific language governing permissions and |
17 | * See the License for the specific language governing permissions and |
18 | * limitations under the License. |
18 | * limitations under the License. |
19 | */ |
19 | */ |
20 | 20 | ||
21 | # This library requires either the GMP extension (or BCMath if gmp_supplement.inc.php is present) |
21 | # This library requires either the GMP extension (or BCMath if gmp_supplement.inc.php is present) |
22 | 22 | ||
23 | // TODO: oop, exceptions? |
23 | // TODO: oop, exceptions? |
24 | // TODO: variant without gmp ? |
24 | // TODO: variant without gmp ? |
25 | // TODO: IPv6 resolution 'ffff::192.168.69.1' -> 'ffff:0000:0000:0000:0000:0000:c0a8:4501' does not work! |
25 | // TODO: IPv6 resolution 'ffff::192.168.69.1' -> 'ffff:0000:0000:0000:0000:0000:c0a8:4501' does not work! |
26 | 26 | ||
27 | if (file_exists(__DIR__ . '/gmp_supplement.inc.php')) include_once __DIR__ . '/gmp_supplement.inc.php'; |
27 | if (file_exists(__DIR__ . '/gmp_supplement.inc.php')) include_once __DIR__ . '/gmp_supplement.inc.php'; |
28 | 28 | ||
29 | define('GMP_ONE', gmp_init('1')); |
29 | define('GMP_ONE', gmp_init('1')); |
30 | 30 | ||
31 | // Very small self-test: |
31 | // Very small self-test: |
32 | /* |
32 | /* |
33 | function ipv6_selftest() { |
33 | function ipv6_selftest() { |
34 | $iv_b = 'c0ff:ee00::'; |
34 | $iv_b = 'c0ff:ee00::'; |
35 | $iv_m = 32; |
35 | $iv_m = 32; |
36 | $r = ipv6_cidr2range($iv_b, $iv_m); |
36 | $r = ipv6_cidr2range($iv_b, $iv_m); |
37 | echo "$iv_b/$iv_m => $r[0] - $r[1]\n"; |
37 | echo "$iv_b/$iv_m => $r[0] - $r[1]\n"; |
38 | 38 | ||
39 | $rev = ipv6_range2cidr($r[0], $r[1]); |
39 | $rev = ipv6_range2cidr($r[0], $r[1]); |
40 | $rev = implode("\n", $rev); |
40 | $rev = implode("\n", $rev); |
41 | echo "$r[0] - $r[1] => $rev ["; |
41 | echo "$r[0] - $r[1] => $rev ["; |
42 | $ok = $rev == "$iv_b/$iv_m"; |
42 | $ok = $rev == "$iv_b/$iv_m"; |
43 | echo $ok ? 'OK' : 'Mismatch'; |
43 | echo $ok ? 'OK' : 'Mismatch'; |
44 | echo "]\n"; |
44 | echo "]\n"; |
45 | echo "In-CIDR-Test: "; |
45 | echo "In-CIDR-Test: "; |
46 | echo ipv6_in_cidr("$iv_b/$iv_m", "$iv_b/$iv_m") ? 'OK' : 'Fail'; |
46 | echo ipv6_in_cidr("$iv_b/$iv_m", "$iv_b/$iv_m") ? 'OK' : 'Fail'; |
47 | echo "\n"; |
47 | echo "\n"; |
48 | } |
48 | } |
49 | ipv6_selftest(); |
49 | ipv6_selftest(); |
50 | */ |
50 | */ |
51 | 51 | ||
52 | $cache_ipv6_cidr2range = array(); |
52 | $cache_ipv6_cidr2range = array(); |
53 | function ipv6_cidr2range($baseip_or_cidr, $subnet='') { |
53 | function ipv6_cidr2range($baseip_or_cidr, $subnet='') { |
54 | # (C) 2012 ViaThinkSoft |
54 | # (C) 2012 ViaThinkSoft |
55 | # Version 1.1 |
55 | # Version 1.1 |
56 | # This function converts an CIDR notation <baseip>/<subnet> into an IPv6 address block array($low_ip, $high_ip) |
56 | # This function converts an CIDR notation <baseip>/<subnet> into an IPv6 address block array($low_ip, $high_ip) |
57 | 57 | ||
58 | global $cache_ipv6_cidr2range; |
58 | global $cache_ipv6_cidr2range; |
59 | $vvv = $baseip_or_cidr.'|'.$subnet; |
59 | $vvv = $baseip_or_cidr.'|'.$subnet; |
60 | if (isset($cache_ipv6_cidr2range[$vvv])) return $cache_ipv6_cidr2range[$vvv]; |
60 | if (isset($cache_ipv6_cidr2range[$vvv])) return $cache_ipv6_cidr2range[$vvv]; |
61 | 61 | ||
62 | if (strpos($baseip_or_cidr, '/') !== false) { |
62 | if (strpos($baseip_or_cidr, '/') !== false) { |
63 | $tmp = explode('/', $baseip_or_cidr, 2); |
63 | $tmp = explode('/', $baseip_or_cidr, 2); |
64 | $baseip_or_cidr = $tmp[0]; |
64 | $baseip_or_cidr = $tmp[0]; |
65 | $subnet = $tmp[1]; |
65 | $subnet = $tmp[1]; |
66 | unset($tmp); |
66 | unset($tmp); |
67 | } |
67 | } |
68 | 68 | ||
69 | if (($subnet < 0) || ($subnet > 128)) { |
69 | if (($subnet < 0) || ($subnet > 128)) { |
70 | $cache_ipv6_cidr2range[$vvv] = false; |
70 | $cache_ipv6_cidr2range[$vvv] = false; |
71 | return false; |
71 | return false; |
72 | } |
72 | } |
73 | 73 | ||
74 | $maxint128 = gmp_sub(gmp_pow('2', 128), GMP_ONE); # TODO: GMP_TWO ? |
74 | $maxint128 = gmp_sub(gmp_pow('2', 128), GMP_ONE); # TODO: GMP_TWO ? |
75 | $netmask = gmp_shiftl($maxint128, 128-$subnet); |
75 | $netmask = gmp_shiftl($maxint128, 128-$subnet); |
76 | $netmask = gmp_and($netmask, $maxint128); // crop to 128 bit |
76 | $netmask = gmp_and($netmask, $maxint128); // crop to 128 bit |
77 | $wildcard = gmp_xor($maxint128, $netmask); |
77 | $wildcard = gmp_xor($maxint128, $netmask); |
78 | 78 | ||
79 | $x = gmp_and(ip2long6($baseip_or_cidr), $netmask); |
79 | $x = gmp_and(ip2long6($baseip_or_cidr), $netmask); |
80 | $nums = $wildcard; |
80 | $nums = $wildcard; |
81 | $low = long2ip6($x); |
81 | $low = long2ip6($x); |
82 | $high = long2ip6(gmp_add($x, $nums)); |
82 | $high = long2ip6(gmp_add($x, $nums)); |
83 | 83 | ||
84 | $out = array($low, $high); |
84 | $out = array($low, $high); |
85 | $cache_ipv6_cidr2range[$vvv] = $out; |
85 | $cache_ipv6_cidr2range[$vvv] = $out; |
86 | return $out; |
86 | return $out; |
87 | } |
87 | } |
88 | 88 | ||
89 | $cache_ipv6_range2cidr = array(); |
89 | $cache_ipv6_range2cidr = array(); |
90 | function ipv6_range2cidr($baseip, $topip) { |
90 | function ipv6_range2cidr($baseip, $topip) { |
91 | # (C) 2012 ViaThinkSoft |
91 | # (C) 2012 ViaThinkSoft |
92 | # Version 1.0 |
92 | # Version 1.0 |
93 | # This function converts an IPv6 address block into valid CIDR blocks (There may be multiple blocks!) |
93 | # This function converts an IPv6 address block into valid CIDR blocks (There may be multiple blocks!) |
94 | 94 | ||
95 | global $cache_ipv6_range2cidr; |
95 | global $cache_ipv6_range2cidr; |
96 | $vvv = $baseip.'|'.$topip; |
96 | $vvv = $baseip.'|'.$topip; |
97 | if (isset($cache_ipv6_range2cidr[$vvv])) return $cache_ipv6_range2cidr[$vvv]; |
97 | if (isset($cache_ipv6_range2cidr[$vvv])) return $cache_ipv6_range2cidr[$vvv]; |
98 | 98 | ||
99 | $out = array(); |
99 | $out = array(); |
100 | if (ipv6_cmp($baseip, $topip) > 0) { |
100 | if (ipv6_cmp($baseip, $topip) > 0) { |
101 | $cache_ipv6_range2cidr[$vvv] = false; |
101 | $cache_ipv6_range2cidr[$vvv] = false; |
102 | return false; |
102 | return false; |
103 | } |
103 | } |
104 | while (gmp_cmp(gmp_sub(ip2long6($baseip), GMP_ONE), ip2long6($topip)) != 0) { |
104 | while (gmp_cmp(gmp_sub(ip2long6($baseip), GMP_ONE), ip2long6($topip)) != 0) { |
105 | $i = -1; |
105 | $i = -1; |
106 | do { |
106 | do { |
107 | $i++; |
107 | $i++; |
108 | $range = ipv6_cidr2range($baseip, $i); |
108 | $range = ipv6_cidr2range($baseip, $i); |
109 | $l = $range[0]; |
109 | $l = $range[0]; |
110 | $t = $range[1]; |
110 | $t = $range[1]; |
111 | } while ((ipv6_cmp($l, $baseip) != 0) || (ipv6_cmp($t, $topip) > 0)); |
111 | } while ((ipv6_cmp($l, $baseip) != 0) || (ipv6_cmp($t, $topip) > 0)); |
112 | 112 | ||
113 | $out[] = "$baseip/$i"; |
113 | $out[] = "$baseip/$i"; |
114 | $baseip = ipv6_add($t, GMP_ONE); |
114 | $baseip = ipv6_add($t, GMP_ONE); |
115 | } |
115 | } |
116 | 116 | ||
117 | $cache_ipv6_range2cidr[$vvv] = $out; |
117 | $cache_ipv6_range2cidr[$vvv] = $out; |
118 | return $out; |
118 | return $out; |
119 | } |
119 | } |
120 | 120 | ||
121 | function ipv6_add($baseip, $num) { |
121 | function ipv6_add($baseip, $num) { |
122 | # (C) 2012 ViaThinkSoft |
122 | # (C) 2012 ViaThinkSoft |
123 | # Version 1.0 |
123 | # Version 1.0 |
124 | 124 | ||
125 | return long2ip6(gmp_add(ip2long6($baseip), $num)); |
125 | return long2ip6(gmp_add(ip2long6($baseip), $num)); |
126 | } |
126 | } |
127 | 127 | ||
128 | function ipv6_sub($baseip, $num) { |
128 | function ipv6_sub($baseip, $num) { |
129 | # (C) 2012 ViaThinkSoft |
129 | # (C) 2012 ViaThinkSoft |
130 | # Version 1.0 |
130 | # Version 1.0 |
131 | 131 | ||
132 | return long2ip6(gmp_sub(ip2long6($baseip), $num)); |
132 | return long2ip6(gmp_sub(ip2long6($baseip), $num)); |
133 | } |
133 | } |
134 | 134 | ||
135 | function ipv6_cmp($a, $b) { |
135 | function ipv6_cmp($a, $b) { |
136 | # (C) 2012 ViaThinkSoft |
136 | # (C) 2012 ViaThinkSoft |
137 | # Version 1.0 |
137 | # Version 1.0 |
138 | 138 | ||
139 | return gmp_cmp(ip2long6($a), ip2long6($b)); |
139 | return gmp_cmp(ip2long6($a), ip2long6($b)); |
140 | } |
140 | } |
141 | 141 | ||
142 | $cache_ipv6_in_cidr = array(); |
142 | $cache_ipv6_in_cidr = array(); |
143 | function ipv6_in_cidr($haystack, $needle) { |
143 | function ipv6_in_cidr($haystack, $needle) { |
144 | # (C) 2012 ViaThinkSoft |
144 | # (C) 2012 ViaThinkSoft |
145 | # Version 1.1 |
145 | # Version 1.1 |
146 | 146 | ||
147 | global $cache_ipv6_in_cidr; |
147 | global $cache_ipv6_in_cidr; |
148 | $vvv = $haystack.'|'.$needle; |
148 | $vvv = $haystack.'|'.$needle; |
149 | if (isset($cache_ipv6_in_cidr[$vvv])) return $cache_ipv6_in_cidr[$vvv]; |
149 | if (isset($cache_ipv6_in_cidr[$vvv])) return $cache_ipv6_in_cidr[$vvv]; |
150 | 150 | ||
151 | $x = explode('/', $haystack); |
151 | $x = explode('/', $haystack); |
152 | $ha = ipv6_cidr2range($x[0], $x[1]); |
152 | $ha = ipv6_cidr2range($x[0], $x[1]); |
153 | 153 | ||
154 | $x = explode('/', $needle); |
154 | $x = explode('/', $needle); |
155 | if (!isset($x[1])) $x[1] = 128; // single IP |
155 | if (!isset($x[1])) $x[1] = 128; // single IP |
156 | $ne = ipv6_cidr2range($x[0], $x[1]); |
156 | $ne = ipv6_cidr2range($x[0], $x[1]); |
157 | 157 | ||
158 | $ha_low = ip2long6($ha[0]); |
158 | $ha_low = ip2long6($ha[0]); |
159 | $ha_hig = ip2long6($ha[1]); |
159 | $ha_hig = ip2long6($ha[1]); |
160 | $ne_low = ip2long6($ne[0]); |
160 | $ne_low = ip2long6($ne[0]); |
161 | $ne_hig = ip2long6($ne[1]); |
161 | $ne_hig = ip2long6($ne[1]); |
162 | 162 | ||
163 | # HA: low[ ]high |
163 | # HA: low[ ]high |
164 | # NE: low[ ]high |
164 | # NE: low[ ]high |
165 | 165 | ||
166 | $out = (gmp_cmp($ne_low, $ha_low) >= 0) && (gmp_cmp($ne_hig, $ha_hig) <= 0); |
166 | $out = (gmp_cmp($ne_low, $ha_low) >= 0) && (gmp_cmp($ne_hig, $ha_hig) <= 0); |
167 | $cache_ipv6_in_cidr[$vvv] = $out; |
167 | $cache_ipv6_in_cidr[$vvv] = $out; |
168 | return $out; |
168 | return $out; |
169 | } |
169 | } |
170 | 170 | ||
171 | // IMPORTANT! $cmp_ary[x]=y MUST HAVE x<=y ! |
171 | // IMPORTANT! $cmp_ary[x]=y MUST HAVE x<=y ! |
172 | function ipv6_merge_address_blocks($data, $debug = false) { |
172 | function ipv6_merge_address_blocks($data, $debug = false) { |
173 | # (C) 2012-2013 ViaThinkSoft |
173 | # (C) 2012-2013 ViaThinkSoft |
174 | # Version 2.2 |
174 | # Version 2.2 |
175 | 175 | ||
176 | if ($debug !== false) $STARTZEIT = time(); |
176 | if ($debug !== false) $STARTZEIT = time(); |
177 | 177 | ||
178 | // 1. Convert IPs to numbers |
178 | // 1. Convert IPs to numbers |
179 | 179 | ||
180 | $cmp_ary = array(); |
180 | $cmp_ary = array(); |
181 | foreach ($data as $a => &$b) { |
181 | foreach ($data as $a => &$b) { |
182 | $a = ip2long6($a); |
182 | $a = ip2long6($a); |
183 | $b = ip2long6($b); |
183 | $b = ip2long6($b); |
184 | 184 | ||
185 | $cmp_ary[gmp_strval($a)] = gmp_strval($b); |
185 | $cmp_ary[gmp_strval($a)] = gmp_strval($b); |
186 | unset($a); |
186 | unset($a); |
187 | unset($b); |
187 | unset($b); |
188 | } |
188 | } |
189 | 189 | ||
190 | // 2. Sort array |
190 | // 2. Sort array |
191 | 191 | ||
192 | ksort($cmp_ary); |
192 | ksort($cmp_ary); |
193 | 193 | ||
194 | // 3. Merge the blocks in an intelligent way (and remove redundant blocks) |
194 | // 3. Merge the blocks in an intelligent way (and remove redundant blocks) |
195 | 195 | ||
196 | # Merge overlapping blocks |
196 | # Merge overlapping blocks |
197 | # [ ] |
197 | # [ ] |
198 | # [ ] -> [ ] |
198 | # [ ] -> [ ] |
199 | 199 | ||
200 | # Merge neighbor blocks |
200 | # Merge neighbor blocks |
201 | # [ ][ ] -> [ ] |
201 | # [ ][ ] -> [ ] |
202 | 202 | ||
203 | # Remove redundant blocks |
203 | # Remove redundant blocks |
204 | # [ ] -> [ ] |
204 | # [ ] -> [ ] |
205 | # [ ] |
205 | # [ ] |
206 | 206 | ||
207 | $merge_count = 0; |
207 | $merge_count = 0; |
208 | $redundant_deleted_count = 0; |
208 | $redundant_deleted_count = 0; |
209 | $round_count = 0; |
209 | $round_count = 0; |
210 | do { |
210 | do { |
211 | if ($debug !== false) { |
211 | if ($debug !== false) { |
212 | $LAUFZEIT = time() - $STARTZEIT; |
212 | $LAUFZEIT = time() - $STARTZEIT; |
213 | echo $debug."Merging... $round_count rounds; merged $merge_count blocks; deleted $redundant_deleted_count redundant blocks; time: $LAUFZEIT seconds\r"; |
213 | echo $debug."Merging... $round_count rounds; merged $merge_count blocks; deleted $redundant_deleted_count redundant blocks; time: $LAUFZEIT seconds\r"; |
214 | } |
214 | } |
215 | 215 | ||
216 | $round_count++; |
216 | $round_count++; |
217 | 217 | ||
218 | $clean = true; |
218 | $clean = true; |
219 | 219 | ||
220 | foreach ($cmp_ary as $a => &$b) { |
220 | foreach ($cmp_ary as $a => &$b) { |
221 | foreach ($cmp_ary as $x => &$y) { |
221 | foreach ($cmp_ary as $x => &$y) { |
222 | // x in range [a+1..b+1] ? |
222 | // x in range [a+1..b+1] ? |
223 | if (gmp_cmp(gmp_init($x), gmp_init($a)) <= 0) continue; |
223 | if (gmp_cmp(gmp_init($x), gmp_init($a)) <= 0) continue; |
224 | if (gmp_cmp(gmp_init($x), gmp_add(gmp_init($b), GMP_ONE)) > 0) break; |
224 | if (gmp_cmp(gmp_init($x), gmp_add(gmp_init($b), GMP_ONE)) > 0) break; |
225 | 225 | ||
226 | // Merge |
226 | // Merge |
227 | $clean = false; |
227 | $clean = false; |
228 | if (gmp_cmp(gmp_init($y), gmp_init($b)) > 0) { |
228 | if (gmp_cmp(gmp_init($y), gmp_init($b)) > 0) { |
229 | $merge_count++; |
229 | $merge_count++; |
230 | $b = $y; |
230 | $b = $y; |
231 | unset($cmp_ary[$x]); |
231 | unset($cmp_ary[$x]); |
232 | } else { |
232 | } else { |
233 | $redundant_deleted_count++; |
233 | $redundant_deleted_count++; |
234 | unset($cmp_ary[$x]); |
234 | unset($cmp_ary[$x]); |
235 | } |
235 | } |
236 | } |
236 | } |
237 | } |
237 | } |
238 | } while (!$clean); |
238 | } while (!$clean); |
239 | 239 | ||
240 | if ($debug !== false) { |
240 | if ($debug !== false) { |
241 | $LAUFZEIT = time() - $STARTZEIT; |
241 | $LAUFZEIT = time() - $STARTZEIT; |
242 | echo $debug."Merge completed. $round_count rounds; merged $merge_count blocks; deleted $redundant_deleted_count redundant blocks; time: $LAUFZEIT seconds\n"; |
242 | echo $debug."Merge completed. $round_count rounds; merged $merge_count blocks; deleted $redundant_deleted_count redundant blocks; time: $LAUFZEIT seconds\n"; |
243 | } |
243 | } |
244 | 244 | ||
245 | // 4. Convert back to IPs |
245 | // 4. Convert back to IPs |
246 | 246 | ||
247 | $out_ary = array(); |
247 | $out_ary = array(); |
248 | foreach ($cmp_ary as $a => &$b) { |
248 | foreach ($cmp_ary as $a => &$b) { |
249 | $a = long2ip6(gmp_init($a)); |
249 | $a = long2ip6(gmp_init($a)); |
250 | $b = long2ip6(gmp_init($b)); |
250 | $b = long2ip6(gmp_init($b)); |
251 | $out_ary[$a] = $b; |
251 | $out_ary[$a] = $b; |
252 | } |
252 | } |
253 | 253 | ||
254 | return $out_ary; |
254 | return $out_ary; |
255 | } |
255 | } |
256 | 256 | ||
257 | function ipv6_merge_arrays($data_a, $data_b) { |
257 | function ipv6_merge_arrays($data_a, $data_b) { |
258 | # (C) 2012 ViaThinkSoft |
258 | # (C) 2012 ViaThinkSoft |
259 | # Version 1.2 |
259 | # Version 1.2 |
260 | 260 | ||
261 | $normalized_data_a = array(); |
261 | $normalized_data_a = array(); |
262 | foreach ($data_a as $from => &$to) { |
262 | foreach ($data_a as $from => &$to) { |
263 | $normalized_data_a[ipv6_normalize($from)] = ipv6_normalize($to); |
263 | $normalized_data_a[ipv6_normalize($from)] = ipv6_normalize($to); |
264 | } |
264 | } |
265 | 265 | ||
266 | $normalized_data_b = array(); |
266 | $normalized_data_b = array(); |
267 | foreach ($data_b as $from => &$to) { |
267 | foreach ($data_b as $from => &$to) { |
268 | $normalized_data_b[ipv6_normalize($from)] = ipv6_normalize($to); |
268 | $normalized_data_b[ipv6_normalize($from)] = ipv6_normalize($to); |
269 | } |
269 | } |
270 | 270 | ||
271 | $data = array(); |
271 | $data = array(); |
272 | 272 | ||
273 | foreach ($normalized_data_a as $from => &$to) { |
273 | foreach ($normalized_data_a as $from => &$to) { |
274 | if (isset($normalized_data_b[$from])) { |
274 | if (isset($normalized_data_b[$from])) { |
275 | $data[$from] = ipv6_max($to, $normalized_data_b[$from]); |
275 | $data[$from] = ipv6_max($to, $normalized_data_b[$from]); |
276 | } else { |
276 | } else { |
277 | $data[$from] = $to; |
277 | $data[$from] = $to; |
278 | } |
278 | } |
279 | } |
279 | } |
280 | 280 | ||
281 | foreach ($normalized_data_b as $from => &$to) { |
281 | foreach ($normalized_data_b as $from => &$to) { |
282 | if (!isset($normalized_data_a[$from])) { |
282 | if (!isset($normalized_data_a[$from])) { |
283 | $data[$from] = $to; |
283 | $data[$from] = $to; |
284 | } |
284 | } |
285 | } |
285 | } |
286 | 286 | ||
287 | return $data; |
287 | return $data; |
288 | } |
288 | } |
289 | 289 | ||
290 | function ipv6_valid($ip) { |
290 | function ipv6_valid($ip) { |
291 | # (C) 2012 ViaThinkSoft |
291 | # (C) 2012 ViaThinkSoft |
292 | # Version 1.0 |
292 | # Version 1.0 |
293 | 293 | ||
294 | return ip2long6($ip) !== false; |
294 | return ip2long6($ip) !== false; |
295 | } |
295 | } |
296 | 296 | ||
297 | function ipv6_normalize($ip) { |
297 | function ipv6_normalize($ip) { |
298 | # (C) 2012 ViaThinkSoft |
298 | # (C) 2012 ViaThinkSoft |
299 | # Version 1.0 |
299 | # Version 1.0 |
300 | 300 | ||
301 | # Example: |
301 | # Example: |
302 | # 2001:0000:0000::1 -> 2001::1 |
302 | # 2001:0000:0000::1 -> 2001::1 |
303 | 303 | ||
304 | $long = ip2long6($ip); |
304 | $long = ip2long6($ip); |
305 | if ($long == -1 || $long === FALSE) return false; |
305 | if ($long == -1 || $long === FALSE) return false; |
306 | return long2ip6($long); |
306 | return long2ip6($long); |
307 | } |
307 | } |
308 | 308 | ||
309 | function ipv6_expand($ip) { |
309 | function ipv6_expand($ip) { |
310 | # (C) 2012 ViaThinkSoft |
310 | # (C) 2012 ViaThinkSoft |
311 | # Version 1.0 |
311 | # Version 1.0 |
312 | 312 | ||
313 | # Example: |
313 | # Example: |
314 | # 2001::1 -> 2001:0000:0000:0000:0000:0000:0000:0000 |
314 | # 2001::1 -> 2001:0000:0000:0000:0000:0000:0000:0000 |
315 | 315 | ||
316 | $long = ip2long6($ip); |
316 | $long = ip2long6($ip); |
317 | if ($long == -1 || $long === FALSE) return false; |
317 | if ($long == -1 || $long === FALSE) return false; |
318 | return long2ip6($long, false); |
318 | return long2ip6($long, false); |
319 | } |
319 | } |
320 | 320 | ||
321 | function ipv6_min($ip_a, $ip_b) { |
321 | function ipv6_min($ip_a, $ip_b) { |
322 | # (C) 2012 ViaThinkSoft |
322 | # (C) 2012 ViaThinkSoft |
323 | # Version 1.0 |
323 | # Version 1.0 |
324 | 324 | ||
325 | if (ipv6_cmp($ip_a, $ip_b) == -1) { |
325 | if (ipv6_cmp($ip_a, $ip_b) == -1) { |
326 | return $ip_a; |
326 | return $ip_a; |
327 | } else { |
327 | } else { |
328 | return $ip_b; |
328 | return $ip_b; |
329 | } |
329 | } |
330 | } |
330 | } |
331 | 331 | ||
332 | function ipv6_max($ip_a, $ip_b) { |
332 | function ipv6_max($ip_a, $ip_b) { |
333 | # (C) 2012 ViaThinkSoft |
333 | # (C) 2012 ViaThinkSoft |
334 | # Version 1.0 |
334 | # Version 1.0 |
335 | 335 | ||
336 | if (ipv6_cmp($ip_a, $ip_b) == 1) { |
336 | if (ipv6_cmp($ip_a, $ip_b) == 1) { |
337 | return $ip_a; |
337 | return $ip_a; |
338 | } else { |
338 | } else { |
339 | return $ip_b; |
339 | return $ip_b; |
340 | } |
340 | } |
341 | } |
341 | } |
342 | 342 | ||
343 | function ipv6_ipcount($data) { |
343 | function ipv6_ipcount($data) { |
344 | # (C) 2012 ViaThinkSoft |
344 | # (C) 2012 ViaThinkSoft |
345 | # Version 1.0 |
345 | # Version 1.0 |
346 | 346 | ||
347 | $cnt = gmp_init('0'); |
347 | $cnt = gmp_init('0'); |
348 | 348 | ||
349 | foreach ($data as $from => &$to) { |
349 | foreach ($data as $from => &$to) { |
350 | $cnt = gmp_add($cnt, gmp_sub(ip2long6($to), ip2long6($from))); |
350 | $cnt = gmp_add($cnt, gmp_sub(ip2long6($to), ip2long6($from))); |
351 | } |
351 | } |
352 | 352 | ||
353 | return gmp_strval($cnt, 10); |
353 | return gmp_strval($cnt, 10); |
354 | } |
354 | } |
355 | 355 | ||
356 | function ipv6_read_file($file) { |
356 | function ipv6_read_file($file) { |
357 | # (C) 2012 ViaThinkSoft |
357 | # (C) 2012 ViaThinkSoft |
358 | # Version 1.0 |
358 | # Version 1.0 |
359 | 359 | ||
360 | $data = array(); |
360 | $data = array(); |
361 | 361 | ||
362 | $lines = file($file); |
362 | $lines = file($file); |
363 | foreach ($lines as &$line) { |
363 | foreach ($lines as &$line) { |
364 | $rng = ipv6_line2range($line); |
364 | $rng = ipv6_line2range($line); |
365 | $data[$rng[0]] = $rng[1]; |
365 | $data[$rng[0]] = $rng[1]; |
366 | } |
366 | } |
367 | 367 | ||
368 | return $data; |
368 | return $data; |
369 | } |
369 | } |
370 | 370 | ||
371 | function ipv6_line2range($line) { |
371 | function ipv6_line2range($line) { |
372 | # (C) 2012 ViaThinkSoft |
372 | # (C) 2012 ViaThinkSoft |
373 | # Version 1.0 |
373 | # Version 1.0 |
374 | 374 | ||
375 | $line = trim($line); |
375 | $line = trim($line); |
376 | 376 | ||
377 | if (strpos($line, '/') !== false) { |
377 | if (strpos($line, '/') !== false) { |
378 | $rng = ipv6_cidr2range($line); |
378 | $rng = ipv6_cidr2range($line); |
379 | } else { |
379 | } else { |
380 | $rng = explode('-', $line); |
380 | $rng = explode('-', $line); |
381 | $rng[0] = trim($rng[0]); |
- | |
382 | $rng[1] = trim($rng[1]); |
- | |
383 | $rng[0] = ipv6_normalize($rng[0]); |
381 | $rng[0] = ipv6_normalize(trim($rng[0])); |
384 | if (!isset($rng[1])) $rng[1] = $rng[0]; |
382 | $rng[1] = isset($rng[1]) ? ipv6_normalize(trim($rng[1])) : $rng[0]; |
385 | $rng[1] = ipv6_normalize($rng[1]); |
- | |
386 | } |
383 | } |
387 | 384 | ||
388 | return $rng; |
385 | return $rng; |
389 | } |
386 | } |
390 | 387 | ||
391 | # --- |
388 | # --- |
392 | 389 | ||
393 | if (!function_exists('gmp_shiftl')) { |
390 | if (!function_exists('gmp_shiftl')) { |
394 | function gmp_shiftl($x, $n) { // shift left |
391 | function gmp_shiftl($x, $n) { // shift left |
395 | // http://www.php.net/manual/en/ref.gmp.php#99788 |
392 | // http://www.php.net/manual/en/ref.gmp.php#99788 |
396 | return gmp_mul($x, gmp_pow('2', $n)); |
393 | return gmp_mul($x, gmp_pow('2', $n)); |
397 | } |
394 | } |
398 | } |
395 | } |
399 | 396 | ||
400 | if (!function_exists('gmp_shiftr')) { |
397 | if (!function_exists('gmp_shiftr')) { |
401 | function gmp_shiftr($x, $n) { // shift right |
398 | function gmp_shiftr($x, $n) { // shift right |
402 | // http://www.php.net/manual/en/ref.gmp.php#99788 |
399 | // http://www.php.net/manual/en/ref.gmp.php#99788 |
403 | return gmp_div($x, gmp_pow('2', $n)); |
400 | return gmp_div($x, gmp_pow('2', $n)); |
404 | } |
401 | } |
405 | } |
402 | } |
406 | 403 | ||
407 | $cache_ip2long6 = array(); |
404 | $cache_ip2long6 = array(); |
408 | function ip2long6($ipv6) { |
405 | function ip2long6($ipv6) { |
409 | // Source: |
406 | // Source: |
410 | // http://www.netz-guru.de/2009/11/07/php-ipv6-ip2long-und-long2ip-funktionen/ |
407 | // http://www.netz-guru.de/2009/11/07/php-ipv6-ip2long-und-long2ip-funktionen/ |
411 | // Slightly modified |
408 | // Slightly modified |
412 | 409 | ||
413 | global $cache_ip2long6; |
410 | global $cache_ip2long6; |
414 | if (isset($cache_ip2long6[$ipv6])) return $cache_ip2long6[$ipv6]; |
411 | if (isset($cache_ip2long6[$ipv6])) return $cache_ip2long6[$ipv6]; |
415 | 412 | ||
416 | if ($ipv6 == '') $ipv6 = '::'; |
413 | if ($ipv6 == '') $ipv6 = '::'; |
417 | 414 | ||
418 | if (filter_var($ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { |
415 | if (filter_var($ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { |
419 | $cache_ip2long6[$ipv6] = false; |
416 | $cache_ip2long6[$ipv6] = false; |
420 | return false; |
417 | return false; |
421 | } |
418 | } |
422 | 419 | ||
423 | $ip_n = @inet_pton($ipv6); |
420 | $ip_n = @inet_pton($ipv6); |
424 | if ($ip_n === false) { |
421 | if ($ip_n === false) { |
425 | $cache_ip2long6[$ipv6] = false; |
422 | $cache_ip2long6[$ipv6] = false; |
426 | return false; // modified |
423 | return false; // modified |
427 | } |
424 | } |
428 | $bytes = 16; // 16 bytes x 8 bit/byte = 128bit |
425 | $bytes = 16; // 16 bytes x 8 bit/byte = 128bit |
429 | $ipv6long = ''; |
426 | $ipv6long = ''; |
430 | 427 | ||
431 | while ($bytes > 0) { |
428 | while ($bytes > 0) { |
432 | $bin = sprintf('%08b',(ord($ip_n[$bytes-1]))); |
429 | $bin = sprintf('%08b',(ord($ip_n[$bytes-1]))); |
433 | $ipv6long = $bin.$ipv6long; |
430 | $ipv6long = $bin.$ipv6long; |
434 | $bytes--; |
431 | $bytes--; |
435 | } |
432 | } |
436 | 433 | ||
437 | // $out = gmp_strval(gmp_init($ipv6long, 2), 10); |
434 | // $out = gmp_strval(gmp_init($ipv6long, 2), 10); |
438 | $out = gmp_init($ipv6long, 2); |
435 | $out = gmp_init($ipv6long, 2); |
439 | $cache_ip2long6[$ipv6] = $out; |
436 | $cache_ip2long6[$ipv6] = $out; |
440 | return $out; |
437 | return $out; |
441 | } |
438 | } |
442 | 439 | ||
443 | $cache_long2ip6 = array(); |
440 | $cache_long2ip6 = array(); |
444 | function long2ip6($ipv6long, $compress=true) { |
441 | function long2ip6($ipv6long, $compress=true) { |
445 | // Source: |
442 | // Source: |
446 | // http://www.netz-guru.de/2009/11/07/php-ipv6-ip2long-und-long2ip-funktionen/ |
443 | // http://www.netz-guru.de/2009/11/07/php-ipv6-ip2long-und-long2ip-funktionen/ |
447 | // Slightly modified |
444 | // Slightly modified |
448 | 445 | ||
449 | global $cache_long2ip6; |
446 | global $cache_long2ip6; |
450 | $vvv = ($compress ? 'T' : 'F').$ipv6long; |
447 | $vvv = ($compress ? 'T' : 'F').$ipv6long; |
451 | if (isset($cache_long2ip6[$vvv])) return $cache_long2ip6[$vvv]; |
448 | if (isset($cache_long2ip6[$vvv])) return $cache_long2ip6[$vvv]; |
452 | 449 | ||
453 | // $bin = gmp_strval(gmp_init($ipv6long, 10), 2); |
450 | // $bin = gmp_strval(gmp_init($ipv6long, 10), 2); |
454 | $bin = gmp_strval($ipv6long, 2); |
451 | $bin = gmp_strval($ipv6long, 2); |
455 | if (strlen($bin) < 128) { |
452 | $bin = str_pad($bin, 128, '0', STR_PAD_LEFT); |
456 | $pad = 128 - strlen($bin); |
- | |
457 | for ($i = 1; $i <= $pad; $i++) { |
- | |
458 | $bin = '0'.$bin; |
- | |
459 | } |
- | |
460 | } |
- | |
461 | 453 | ||
462 | $bytes = 0; |
454 | $bytes = 0; |
463 | $ipv6 = ''; |
455 | $ipv6 = ''; |
464 | while ($bytes < 8) { // 16 bytes x 8 bit/byte = 128bit |
456 | while ($bytes < 8) { // 16 bytes x 8 bit/byte = 128bit |
465 | $bin_part = substr($bin,($bytes*16),16); |
457 | $bin_part = substr($bin,($bytes*16),16); |
466 | $part = dechex(bindec($bin_part)); |
458 | $part = dechex(bindec($bin_part)); |
467 | if (!$compress) { |
459 | if (!$compress) { |
468 | $part = str_pad($part, 4, '0', STR_PAD_LEFT); |
460 | $part = str_pad($part, 4, '0', STR_PAD_LEFT); |
469 | } |
461 | } |
470 | $ipv6 .= $part.':'; |
462 | $ipv6 .= $part.':'; |
471 | $bytes++; |
463 | $bytes++; |
472 | } |
464 | } |
473 | 465 | ||
474 | if ($compress) { |
466 | if ($compress) { |
475 | $out = inet_ntop(inet_pton(substr($ipv6, 0, -1))); |
467 | $out = inet_ntop(inet_pton(substr($ipv6, 0, -1))); |
476 | } else { |
468 | } else { |
477 | $out = substr($ipv6, 0, strlen($ipv6)-1); |
469 | $out = substr($ipv6, 0, strlen($ipv6)-1); |
478 | } |
470 | } |
479 | $cache_long2ip6[$vvv] = $out; |
471 | $cache_long2ip6[$vvv] = $out; |
480 | return $out; |
472 | return $out; |
481 | } |
473 | } |
482 | 474 | ||
483 | # --- New 16,12,12 |
475 | # --- New 16,12,12 |
484 | 476 | ||
485 | define('IPV6_BITS', 128); |
477 | define('IPV6_BITS', 128); |
486 | 478 | ||
487 | $global_ipv6_distance = array(); |
479 | $global_ipv6_distance = array(); |
488 | function ipv6_distance($ipOrCIDR_Searchterm, $ipOrCIDR_Candidate) { |
480 | function ipv6_distance($ipOrCIDR_Searchterm, $ipOrCIDR_Candidate) { |
489 | global $global_ipv6_distance; |
481 | global $global_ipv6_distance; |
490 | $vvv = $ipOrCIDR_Searchterm.'|'.$ipOrCIDR_Candidate; |
482 | $vvv = $ipOrCIDR_Searchterm.'|'.$ipOrCIDR_Candidate; |
491 | if (isset($global_ipv6_distance[$vvv])) return $global_ipv6_distance[$vvv]; |
483 | if (isset($global_ipv6_distance[$vvv])) return $global_ipv6_distance[$vvv]; |
492 | 484 | ||
493 | $ary = ipv6_cidr_split($ipOrCIDR_Searchterm); |
485 | $ary = ipv6_cidr_split($ipOrCIDR_Searchterm); |
494 | $ip = $ary[0]; |
486 | $ip = $ary[0]; |
495 | 487 | ||
496 | if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { |
488 | if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { |
497 | $global_ipv6_distance[$vvv] = false; |
489 | $global_ipv6_distance[$vvv] = false; |
498 | return false; |
490 | return false; |
499 | } |
491 | } |
500 | 492 | ||
501 | $ary = ipv6_cidr_split($ipOrCIDR_Candidate); |
493 | $ary = ipv6_cidr_split($ipOrCIDR_Candidate); |
502 | $ip = $ary[0]; |
494 | $ip = $ary[0]; |
503 | $cidr_bits = $ary[1]; |
495 | $cidr_bits = $ary[1]; |
504 | if ($cidr_bits > IPV6_BITS) { |
496 | if ($cidr_bits > IPV6_BITS) { |
505 | $global_ipv6_distance[$vvv] = false; |
497 | $global_ipv6_distance[$vvv] = false; |
506 | return false; // throw new Exception('CIDR bits > '.IPV6_BITS); |
498 | return false; // throw new Exception('CIDR bits > '.IPV6_BITS); |
507 | } |
499 | } |
508 | if (!is_numeric($cidr_bits)) return false; |
500 | if (!is_numeric($cidr_bits)) return false; |
509 | 501 | ||
510 | if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { |
502 | if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { |
511 | $global_ipv6_distance[$vvv] = false; |
503 | $global_ipv6_distance[$vvv] = false; |
512 | return false; |
504 | return false; |
513 | } |
505 | } |
514 | 506 | ||
515 | $x = ipv6_trackdown($ipOrCIDR_Searchterm); |
507 | $x = ipv6_trackdown($ipOrCIDR_Searchterm); |
516 | 508 | ||
517 | if (ipv6_in_cidr($x[0], $ip.'/'.$cidr_bits)) { |
509 | if (ipv6_in_cidr($x[0], $ip.'/'.$cidr_bits)) { |
518 | $ary = ipv6_cidr_split($x[0]); |
510 | $ary = ipv6_cidr_split($x[0]); |
519 | $cidr_bits2 = $ary[1]; |
511 | $cidr_bits2 = $ary[1]; |
520 | if ($cidr_bits2 > IPV6_BITS) { |
512 | if ($cidr_bits2 > IPV6_BITS) { |
521 | $global_ipv6_distance[$vvv] = false; |
513 | $global_ipv6_distance[$vvv] = false; |
522 | return false; // throw new Exception('CIDR bits > '.IPV6_BITS); |
514 | return false; // throw new Exception('CIDR bits > '.IPV6_BITS); |
523 | } |
515 | } |
524 | $out = $cidr_bits2-$cidr_bits; |
516 | $out = $cidr_bits2-$cidr_bits; |
525 | $global_ipv6_distance[$vvv] = $out; |
517 | $global_ipv6_distance[$vvv] = $out; |
526 | return $out; |
518 | return $out; |
527 | } |
519 | } |
528 | 520 | ||
529 | $i = 0; |
521 | $i = 0; |
530 | $max = false; |
522 | $max = false; |
531 | foreach ($x as &$y) { |
523 | foreach ($x as &$y) { |
532 | if (ipv6_in_cidr($ip.'/'.$cidr_bits, $y)) { |
524 | if (ipv6_in_cidr($ip.'/'.$cidr_bits, $y)) { |
533 | $max = $i; |
525 | $max = $i; |
534 | } |
526 | } |
535 | $i++; |
527 | $i++; |
536 | } |
528 | } |
537 | 529 | ||
538 | $global_ipv6_distance[$vvv] = $max; |
530 | $global_ipv6_distance[$vvv] = $max; |
539 | return $max; |
531 | return $max; |
540 | } |
532 | } |
541 | 533 | ||
542 | function ipv6_cidr_split($ipOrCIDR) { |
534 | function ipv6_cidr_split($ipOrCIDR) { |
543 | $ary = explode('/', $ipOrCIDR, 2); |
535 | $ary = explode('/', $ipOrCIDR, 2); |
544 | $cidr_bits = isset($ary[1]) ? $ary[1] : IPV6_BITS; |
536 | $cidr_bits = isset($ary[1]) ? $ary[1] : IPV6_BITS; |
545 | if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS); |
537 | if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS); |
546 | if (!is_numeric($cidr_bits)) return false; |
538 | if (!is_numeric($cidr_bits)) return false; |
547 | $ip = $ary[0]; |
539 | $ip = $ary[0]; |
548 | return array($ip, $cidr_bits); |
540 | return array($ip, $cidr_bits); |
549 | } |
541 | } |
550 | 542 | ||
551 | function ipv6_equals($ipOrCIDRA, $ipOrCIDRB) { |
543 | function ipv6_equals($ipOrCIDRA, $ipOrCIDRB) { |
552 | return ipv6_normalize_range($ipOrCIDRA) == ipv6_normalize_range($ipOrCIDRB); |
544 | return ipv6_normalize_range($ipOrCIDRA) == ipv6_normalize_range($ipOrCIDRB); |
553 | } |
545 | } |
554 | 546 | ||
555 | function ipv6_cidr_min_ip($ipOrCIDR) { |
547 | function ipv6_cidr_min_ip($ipOrCIDR) { |
556 | $ary = ipv6_cidr_split($ipOrCIDR); |
548 | $ary = ipv6_cidr_split($ipOrCIDR); |
557 | $ipOrCIDR = $ary[0]; |
549 | $ipOrCIDR = $ary[0]; |
558 | $cidr_bits = $ary[1]; |
550 | $cidr_bits = $ary[1]; |
559 | if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS); |
551 | if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS); |
560 | if (!is_numeric($cidr_bits)) return false; |
552 | if (!is_numeric($cidr_bits)) return false; |
561 | 553 | ||
562 | $m = ip2bin($ipOrCIDR); |
554 | $m = ip2bin($ipOrCIDR); |
563 | $m = substr($m, 0, $cidr_bits) . str_repeat('0', IPV6_BITS-$cidr_bits); |
555 | $m = substr($m, 0, $cidr_bits) . str_repeat('0', IPV6_BITS-$cidr_bits); |
564 | 556 | ||
565 | return bin2ip($m); |
557 | return bin2ip($m); |
566 | } |
558 | } |
567 | 559 | ||
568 | function ipv6_cidr_max_ip($ipOrCIDR) { |
560 | function ipv6_cidr_max_ip($ipOrCIDR) { |
569 | $ary = ipv6_cidr_split($ipOrCIDR); |
561 | $ary = ipv6_cidr_split($ipOrCIDR); |
570 | $ipOrCIDR = $ary[0]; |
562 | $ipOrCIDR = $ary[0]; |
571 | $cidr_bits = $ary[1]; |
563 | $cidr_bits = $ary[1]; |
572 | if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS); |
564 | if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS); |
573 | if (!is_numeric($cidr_bits)) return false; |
565 | if (!is_numeric($cidr_bits)) return false; |
574 | 566 | ||
575 | $m = ip2bin($ipOrCIDR); |
567 | $m = ip2bin($ipOrCIDR); |
576 | $m = substr($m, 0, $cidr_bits) . str_repeat('1', IPV6_BITS-$cidr_bits); |
568 | $m = substr($m, 0, $cidr_bits) . str_repeat('1', IPV6_BITS-$cidr_bits); |
577 | 569 | ||
578 | return bin2ip($m); |
570 | return bin2ip($m); |
579 | } |
571 | } |
580 | 572 | ||
581 | function ipv6_normalize_range($ipOrCIDR) { |
573 | function ipv6_normalize_range($ipOrCIDR) { |
582 | # 2001:1800::1/21 |
574 | # 2001:1800::1/21 |
583 | # --> 2001:1800::/21 |
575 | # --> 2001:1800::/21 |
584 | 576 | ||
585 | # 2001:1af8:4100:a061:0001::1337 |
577 | # 2001:1af8:4100:a061:0001::1337 |
586 | # --> 2001:1af8:4100:a061:1::1337/128 |
578 | # --> 2001:1af8:4100:a061:1::1337/128 |
587 | 579 | ||
588 | $ary = ipv6_cidr_split($ipOrCIDR); |
580 | $ary = ipv6_cidr_split($ipOrCIDR); |
589 | $ipOrCIDR = $ary[0]; |
581 | $ipOrCIDR = $ary[0]; |
590 | $cidr_bits = $ary[1]; |
582 | $cidr_bits = $ary[1]; |
591 | if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS); |
583 | if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS); |
592 | if (!is_numeric($cidr_bits)) return false; |
584 | if (!is_numeric($cidr_bits)) return false; |
593 | 585 | ||
594 | $m = ip2bin($ipOrCIDR); |
586 | $m = ip2bin($ipOrCIDR); |
595 | $m = substr($m, 0, $cidr_bits) . str_repeat('0', IPV6_BITS-$cidr_bits); |
587 | $m = substr($m, 0, $cidr_bits) . str_repeat('0', IPV6_BITS-$cidr_bits); |
596 | 588 | ||
597 | return bin2ip($m) . '/' . $cidr_bits; |
589 | return bin2ip($m) . '/' . $cidr_bits; |
598 | } |
590 | } |
599 | 591 | ||
600 | function ipv6_trackdown($ipOrCIDR) { |
592 | function ipv6_trackdown($ipOrCIDR) { |
601 | $ary = ipv6_cidr_split($ipOrCIDR); |
593 | $ary = ipv6_cidr_split($ipOrCIDR); |
602 | $ipOrCIDR = $ary[0]; |
594 | $ipOrCIDR = $ary[0]; |
603 | $cidr_bits = $ary[1]; |
595 | $cidr_bits = $ary[1]; |
604 | if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS); |
596 | if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS); |
605 | if (!is_numeric($cidr_bits)) return false; |
597 | if (!is_numeric($cidr_bits)) return false; |
606 | 598 | ||
607 | $out = array(); |
599 | $out = array(); |
608 | $m = ip2bin($ipOrCIDR); |
600 | $m = ip2bin($ipOrCIDR); |
609 | for ($i=$cidr_bits; $i>=0; $i--) { |
601 | for ($i=$cidr_bits; $i>=0; $i--) { |
610 | $m = substr($m, 0, $i) . str_repeat('0', IPV6_BITS-$i); |
602 | $m = substr($m, 0, $i) . str_repeat('0', IPV6_BITS-$i); |
611 | $out[] = bin2ip($m) . '/' . $i; |
603 | $out[] = bin2ip($m) . '/' . $i; |
612 | } |
604 | } |
613 | 605 | ||
614 | return $out; |
606 | return $out; |
615 | } |
607 | } |
616 | 608 | ||
617 | function ipv6_sort($ary) { |
609 | function ipv6_sort($ary) { |
618 | $f = array(); |
610 | $f = array(); |
619 | foreach ($ary as $c) { |
611 | foreach ($ary as $c) { |
620 | $a = explode('/', $c); |
612 | $a = explode('/', $c); |
621 | $ip = $a[0]; |
613 | $ip = $a[0]; |
622 | $bits = isset($a[1]) ? $a[1] : 128; |
614 | $bits = isset($a[1]) ? $a[1] : 128; |
623 | 615 | ||
624 | $d = ip2bin($ip); |
616 | $d = ip2bin($ip); |
625 | 617 | ||
626 | # ord('*') must be smaller than ord('0') |
618 | # ord('*') must be smaller than ord('0') |
627 | $d = substr($d, 0, $bits).str_repeat('*', 128-$bits); |
619 | $d = substr($d, 0, $bits).str_repeat('*', 128-$bits); |
628 | 620 | ||
629 | $f[$d] = $c; |
621 | $f[$d] = $c; |
630 | } |
622 | } |
631 | 623 | ||
632 | return $f; |
624 | return $f; |
633 | } |
625 | } |
634 | 626 | ||
635 | function ipv6_make_tree($ary) { |
627 | function ipv6_make_tree($ary) { |
636 | $ary = ipv6_sort($ary); |
628 | $ary = ipv6_sort($ary); |
637 | 629 | ||
638 | if (count($ary) == 0) return array(); |
630 | if (count($ary) == 0) return array(); |
639 | 631 | ||
640 | $sub_begin = ''; |
632 | $sub_begin = ''; |
641 | $sub_begin_ip = ''; |
633 | $sub_begin_ip = ''; |
642 | foreach ($ary as $n => $d) { |
634 | foreach ($ary as $n => $d) { |
643 | $sub_begin = substr($n, 0, strpos($n, '*')); |
635 | $sub_begin = substr($n, 0, strpos($n, '*')); |
644 | $sub_begin_ip = $d; |
636 | $sub_begin_ip = $d; |
645 | unset($ary[$n]); |
637 | unset($ary[$n]); |
646 | break; |
638 | break; |
647 | } |
639 | } |
648 | 640 | ||
649 | $sub = array(); |
641 | $sub = array(); |
650 | $nonsub = array(); |
642 | $nonsub = array(); |
651 | foreach ($ary as $n => $d) { |
643 | foreach ($ary as $n => $d) { |
652 | if (substr($n, 0, strlen($sub_begin)) == $sub_begin) { |
644 | if (substr($n, 0, strlen($sub_begin)) == $sub_begin) { |
653 | $sub[$n] = $d; |
645 | $sub[$n] = $d; |
654 | } else { |
646 | } else { |
655 | $nonsub[$n] = $d; |
647 | $nonsub[$n] = $d; |
656 | } |
648 | } |
657 | } |
649 | } |
658 | 650 | ||
659 | $out = array(); |
651 | $out = array(); |
660 | $out[$sub_begin_ip] = ipv6_make_tree($sub); |
652 | $out[$sub_begin_ip] = ipv6_make_tree($sub); |
661 | 653 | ||
662 | $a = ipv6_make_tree($nonsub); |
654 | $a = ipv6_make_tree($nonsub); |
663 | 655 | ||
664 | $out = array_merge($out, $a); |
656 | $out = array_merge($out, $a); |
665 | 657 | ||
666 | return $out; |
658 | return $out; |
667 | } |
659 | } |
668 | 660 | ||
669 | # --- |
661 | # --- |
670 | 662 | ||
671 | if (!function_exists('ip2bin')) { |
663 | if (!function_exists('ip2bin')) { |
672 | $cache_ip2bin = array(); |
664 | $cache_ip2bin = array(); |
673 | function ip2bin($ip) { |
665 | function ip2bin($ip) { |
674 | # Source: http://php.net/manual/en/function.ip2long.php#104163 |
666 | # Source: http://php.net/manual/en/function.ip2long.php#104163 |
675 | # modified by VTS |
667 | # modified by VTS |
676 | 668 | ||
677 | global $cache_ip2bin; |
669 | global $cache_ip2bin; |
678 | if (isset($cache_ip2bin[$ip])) return $cache_ip2bin[$ip]; |
670 | if (isset($cache_ip2bin[$ip])) return $cache_ip2bin[$ip]; |
679 | 671 | ||
680 | if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) { |
672 | if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) { |
681 | $iplong = ip2long($ip); |
673 | $iplong = ip2long($ip); |
682 | assert($iplong !== false); |
674 | assert($iplong !== false); |
683 | $out = base_convert((string)$iplong, 10, 2); |
675 | $out = base_convert((string)$iplong, 10, 2); |
684 | $cache_ip2bin[$ip] = $out; |
676 | $cache_ip2bin[$ip] = $out; |
685 | return $out; |
677 | return $out; |
686 | } |
678 | } |
687 | if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { |
679 | if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { |
688 | $cache_ip2bin[$ip] = false; |
680 | $cache_ip2bin[$ip] = false; |
689 | return false; |
681 | return false; |
690 | } |
682 | } |
691 | if (($ip_n = inet_pton($ip)) === false) { |
683 | if (($ip_n = inet_pton($ip)) === false) { |
692 | $cache_ip2bin[$ip] = false; |
684 | $cache_ip2bin[$ip] = false; |
693 | return false; |
685 | return false; |
694 | } |
686 | } |
695 | $bits = 15; // 16 x 8 bit = 128bit (ipv6) |
687 | $bits = 15; // 16 x 8 bit = 128bit (ipv6) |
696 | $ipbin = ''; # added by vts to avoid warning |
688 | $ipbin = ''; # added by vts to avoid warning |
697 | while ($bits >= 0) { |
689 | while ($bits >= 0) { |
698 | $bin = sprintf('%08b', (ord($ip_n[$bits]))); |
690 | $bin = sprintf('%08b', (ord($ip_n[$bits]))); |
699 | $ipbin = $bin.$ipbin; |
691 | $ipbin = $bin.$ipbin; |
700 | $bits--; |
692 | $bits--; |
701 | } |
693 | } |
702 | 694 | ||
703 | $cache_ip2bin[$ip] = $ipbin; |
695 | $cache_ip2bin[$ip] = $ipbin; |
704 | return $ipbin; |
696 | return $ipbin; |
705 | } |
697 | } |
706 | } |
698 | } |
707 | 699 | ||
708 | if (!function_exists('bin2ip')) { |
700 | if (!function_exists('bin2ip')) { |
709 | $cache_bin2ip = array(); |
701 | $cache_bin2ip = array(); |
710 | function bin2ip($bin) { |
702 | function bin2ip($bin) { |
711 | # Source: http://php.net/manual/en/function.ip2long.php#104163 |
703 | # Source: http://php.net/manual/en/function.ip2long.php#104163 |
712 | # modified by VTS |
704 | # modified by VTS |
713 | 705 | ||
714 | global $cache_bin2ip; |
706 | global $cache_bin2ip; |
715 | if (isset($cache_bin2ip[$bin])) return $cache_bin2ip[$bin]; |
707 | if (isset($cache_bin2ip[$bin])) return $cache_bin2ip[$bin]; |
716 | 708 | ||
717 | if (strlen($bin) <= 32) { // 32bits (ipv4) |
709 | if (strlen($bin) <= 32) { // 32bits (ipv4) |
718 | $iplong = base_convert($bin, 2, 10); |
710 | $iplong = base_convert($bin, 2, 10); |
719 | $out = long2ip(intval($iplong)); |
711 | $out = long2ip(intval($iplong)); |
720 | $cache_bin2ip[$bin] = $out; |
712 | $cache_bin2ip[$bin] = $out; |
721 | return $out; |
713 | return $out; |
722 | } |
714 | } |
723 | if (strlen($bin) != 128) { |
715 | if (strlen($bin) != 128) { |
724 | $cache_bin2ip[$bin] = false; |
716 | $cache_bin2ip[$bin] = false; |
725 | return false; |
717 | return false; |
726 | } |
718 | } |
727 | $pad = 128 - strlen($bin); |
719 | //$bin = str_pad($bin, 128, '0', STR_PAD_LEFT); |
728 | for ($i = 1; $i <= $pad; $i++) { |
- | |
729 | $bin = '0'.$bin; |
- | |
730 | } |
- | |
731 | $bits = 0; |
720 | $bits = 0; |
732 | $ipv6 = ''; # added by vts to avoid warning |
721 | $ipv6 = ''; # added by vts to avoid warning |
733 | while ($bits <= 7) { |
722 | while ($bits <= 7) { |
734 | $bin_part = substr($bin,($bits*16),16); |
723 | $bin_part = substr($bin,($bits*16),16); |
735 | $ipv6 .= dechex(bindec($bin_part)) . ':'; |
724 | $ipv6 .= dechex(bindec($bin_part)) . ':'; |
736 | $bits++; |
725 | $bits++; |
737 | } |
726 | } |
738 | 727 | ||
739 | $out = inet_ntop(inet_pton(substr($ipv6, 0, -1))); |
728 | $out = inet_ntop(inet_pton(substr($ipv6, 0, -1))); |
740 | $cache_bin2ip[$bin] = $out; |
729 | $cache_bin2ip[$bin] = $out; |
741 | return $out; |
730 | return $out; |
742 | } |
731 | } |
743 | } |
732 | } |
744 | 733 | ||
745 | # --- TEST |
734 | # --- TEST |
746 | 735 | ||
747 | /* |
736 | /* |
748 | assert(ipv6_normalize('2001:0000:0000::1') == '2001::1'); |
737 | assert(ipv6_normalize('2001:0000:0000::1') == '2001::1'); |
749 | 738 | ||
750 | assert(ipv6_distance('2001:1ae0::/27', '2001:1af8::/29') == -2); |
739 | assert(ipv6_distance('2001:1ae0::/27', '2001:1af8::/29') == -2); |
751 | assert(ipv6_distance('2001:1af0::/28', '2001:1af8::/29') == -1); |
740 | assert(ipv6_distance('2001:1af0::/28', '2001:1af8::/29') == -1); |
752 | assert(ipv6_distance('2001:1af8::/29', '2001:1af8::/29') == 0); |
741 | assert(ipv6_distance('2001:1af8::/29', '2001:1af8::/29') == 0); |
753 | assert(ipv6_distance('2001:1af8::/30', '2001:1af8::/29') == 1); |
742 | assert(ipv6_distance('2001:1af8::/30', '2001:1af8::/29') == 1); |
754 | assert(ipv6_distance('2001:1af8::/31', '2001:1af8::/29') == 2); |
743 | assert(ipv6_distance('2001:1af8::/31', '2001:1af8::/29') == 2); |
755 | 744 | ||
756 | assert(ipv6_distance('2001:1af8:4100:a061:0001::1336/127', '2001:1af8:4100:a061:0001::1335/127') === false); |
745 | assert(ipv6_distance('2001:1af8:4100:a061:0001::1336/127', '2001:1af8:4100:a061:0001::1335/127') === false); |
757 | assert(ipv6_distance('2001:1af8:4100:a061:0001::1336/128', '2001:1af8:4100:a061:0001::1337/128') === false); |
746 | assert(ipv6_distance('2001:1af8:4100:a061:0001::1336/128', '2001:1af8:4100:a061:0001::1337/128') === false); |
758 | assert(ipv6_distance('2001:1af8:4100:a061:0001::1336', '2001:1af8:4100:a061:0001::1337') === false); |
747 | assert(ipv6_distance('2001:1af8:4100:a061:0001::1336', '2001:1af8:4100:a061:0001::1337') === false); |
759 | */ |
748 | */ |
760 | 749 | ||
761 | /* |
750 | /* |
762 | $test = '2001:1af8:4100:a061:0001::1337'; |
751 | $test = '2001:1af8:4100:a061:0001::1337'; |
763 | $x = ipv6_trackdown($test); |
752 | $x = ipv6_trackdown($test); |
764 | foreach ($x as &$cidr) { |
753 | foreach ($x as &$cidr) { |
765 | $min = ipv6_cidr_min_ip($cidr); |
754 | $min = ipv6_cidr_min_ip($cidr); |
766 | $max = ipv6_cidr_max_ip($cidr); |
755 | $max = ipv6_cidr_max_ip($cidr); |
767 | echo "$cidr ($min - $max)\n"; |
756 | echo "$cidr ($min - $max)\n"; |
768 | } |
757 | } |
769 | */ |
758 | */ |
770 | 759 |