Last active
December 23, 2015 02:59
-
-
Save ngorchilov/6570413 to your computer and use it in GitHub Desktop.
Kernel auto-port assignment error reproduction
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env php | |
<?php | |
$broken = TRUE; // TRUE: auto-assign port (doesn't work); FALSE: select port explicitely (works) | |
$local = TRUE; // TRUE: assing IPs to local interface; FALSE: use TPROXY mode. Doesn't make any difference in kernel behaviour | |
$ip_range = array('10.254.254.251', '10.254.254.252', '10.254.254.253', '10.254.254.254'); // put minimum two IPs that DO NOT belong to this machine already | |
$counters = $socks = array(); | |
// constants needed for setting a socket in TPROXY mode | |
define('SOL_IP', 0); | |
define('IP_TRANSPARENT', 19); | |
// parsing of /proc/sys/net/ipv4/ip_local_port_range | |
$port_range = file_get_contents('/proc/sys/net/ipv4/ip_local_port_range') or die("error loading /proc/sys/net/ipv4/ip_local_port_range\n"); | |
preg_match('/(\d+)\s+(\d+)/', $port_range, $matches) or die("error parsing /proc/sys/net/ipv4/ip_local_port_range\n"); | |
$port_min = $matches[1]; | |
$port_max = $matches[2]; | |
$port_cnt = $port_max - $port_min + 1; | |
echo "available ports: {$port_min} - {$port_max} ({$port_cnt} total)\n"; | |
// check max open files limit | |
$max_open_files = exec('ulimit -n') or die("can't get maximum open files\n"); | |
$open_files_needed = count($ip_range) * $port_cnt + 1000; // adding 1000 files extra just in case | |
if ($max_open_files < $open_files_needed) { | |
die("insufficient max open files ({$max_open_files}). run 'ulimit -n {$open_files_needed}; {$_SERVER['PHP_SELF']}'\n"); | |
} | |
// setup ips if needed | |
foreach($ip_range as $ip) { | |
if ($local) { | |
// assign IPs | |
passthru("ip a a {$ip}/32 dev eth0"); | |
} | |
$counters[$ip] = 0; | |
} | |
// creating sockets | |
echo "creating sockets\n"; | |
try { | |
for ($port = $port_min; $port <= $port_max; $port++) { | |
foreach ($ip_range as $ip) { | |
$counters[$ip]++; | |
$socks[] = socket_setup($ip, $broken ? 0 : $port); | |
} | |
} | |
echo "done...\n"; | |
} catch(Exception $e) { | |
echo 'exception on socket ' . (count($socks) + 1). ': ' . $e->getMessage() . PHP_EOL; | |
} | |
// remove ips | |
echo "port usage per ip:\n"; | |
foreach($ip_range as $ip) { | |
if ($local) { | |
// delete IPs | |
passthru("ip a d {$ip}/32 dev eth0"); | |
} | |
echo "\t$ip: {$counters[$ip]} ports assigned\n"; | |
} | |
// closing sockets | |
echo 'closing the remaining ' . count($socks) . " sockets\n"; | |
foreach($socks as $socket) { | |
@socket_close($socket); | |
} | |
echo "finish\n"; | |
function socket_setup($ip, $port = 0) { | |
global $local; | |
if(($socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === FALSE) { | |
throw new Exception(socket_strerror(socket_last_error()) . ' [' . socket_last_error() . '] (socket_create)'); | |
} elseif (@socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1) === FALSE) { | |
throw new Exception(socket_strerror(socket_last_error($socket)) . ' [' . socket_last_error($socket) . '] (socket_set_option: SO_REUSEADDR)'); | |
} elseif (!$local && @socket_set_option($socket, SOL_IP, IP_TRANSPARENT, 1) === FALSE) { | |
throw new Exception(socket_strerror(socket_last_error($socket)) . ' [' . socket_last_error($socket) . '] (socket_set_option: IP_TRANSPARENT)'); | |
} elseif (@socket_bind($socket, $ip, $port) === FALSE) { | |
throw new Exception(socket_strerror(socket_last_error($socket)) . ' [' . socket_last_error($socket) . '] (socket_bind)'); | |
} elseif (@socket_listen($socket, 0) === FALSE) { | |
throw new Exception(socket_strerror(socket_last_error($socket)) . ' [' . socket_last_error($socket) . '] (socket_listen)'); | |
} else { | |
return $socket; | |
} | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment