Recently we wrote that we made IPv6 support in the anti-spam plugin. But not only spammers began to use IPv6, for other types of attacks on sites, attackers also use them.
We implemented support for IPv6 in the security plugin for WordPress Updated methods of IP addresses determination, storage and transfer of information to the cloud.
We had to teach the plugin to distinguish, standardize, search for subnets and store IPv6 addresses. Despite a lot of different ready-made solutions had to do its implementation and the main problem is that PHP can be compiled with different parameters, and in general may be an outdated version, so had to do everything from scratch.
As soon as we get the IP address we check whether it is valid and its type.
Then we define its belonging to the IP frequency range or the CDN range (If there are headings of a specific CDN). And here was the main difficulty, as it was necessary to realize all this independently, and at the same time to remind ourselves what is what.
It was decided to make the subnet search universal, so that it could receive IPv4 and IPv6 on the input, and if desired also IPv7, if we live. The only thing that is strictly prescribed is the X-theta base (octet for IPv4 and hexestate for IPv6). Naturally, recursion…
/*
* Check if the IP belong to mask. Recursive.
* Octet by octet for IPv4
* Hextet by hextet for IPv6
* @param ip string
* @param cird mixed (string|array of strings)
* @param ip_type string
* @param cird mixed (string|array of strings)
*/
static public function ip__mask_match($ip, $cidr, $ip_type = 'v4', $xtet_count = 0){
if(is_array($cidr)){
foreach($cidr as $curr_mask){
if(self::ip__mask_match($ip, $curr_mask, $ip_type)){
return true;
}
} unset($curr_mask);
return false;
}
if($ip_type == 'v4') $xtet_base = 8;
if($ip_type == 'v6') $xtet_base = 16;
// Calculate mask
$exploded = explode('/', $cidr);
// Exit condition
$xtet_end = ceil($exploded[1] / $xtet_base);
if($xtet_count == $xtet_end)
return true;
$mask = $exploded[1] - $xtet_base * $xtet_count >= 0 ? $xtet_base : $exploded[1] - $xtet_base * ($xtet_count - 1);
$mask = 4294967295 << ($xtet_base - $mask);
// Calculate first ip X-tet
$ip_xtet = explode($ip_type == 'v4' ? '.' : ':', $ip);
$ip_xtet = $ip_type == 'v4' ? $ip_xtet[$xtet_count] : hexdec($ip_xtet[$xtet_count]);
// Calculate first net X-tet
$net_xtet = explode($ip_type == 'v4' ? '.' : ':', $exploded[0]);
$net_xtet = $ip_type == 'v4' ? $net_xtet[$xtet_count] : hexdec($net_xtet[$xtet_count]);
$result = ($ip_xtet & $mask) == ($net_xtet & $mask);
if($result)
$result = self::ip__mask_match($ip, $cidr, $ip_type, $xtet_count + 1);
return $result;
}
It should be noted that the function takes only normalized IPv6 addresses and networks (a string or array of network strings). For example, Cloud Flare 2a06: 98c0 :: 0/29 does not work, it needs to be expanded to 2a06: 98c0: 0: 0: 0: 0: 0: 0/29
Because of the fact that from the local base of the firewall you have to select addresses on the fly, using DB tools, not using PHP. IPv6 address had to be divided into 4 columns, each of them 32 bits, corresponds to one IPv4 address. Accordingly, with this structure, you can use the old sampling method, with binary operations.
Changes were also made to the service control Panel and databases for correct processing and display of information.
Implemented support for IPv6 for personal blacklists, security firewall and functions Traffic Control (IP blocking when a specified number of requests per unit of time).