From 0917cb214b2bbf7f4b374c901c642987fc4ac63b Mon Sep 17 00:00:00 2001 From: Darren Embry Date: Thu, 5 Apr 2012 13:07:20 -0400 Subject: load balancer: allow IPv4 subnets up to 64 addresses in Pools and Virtual Servers (PEV-394754) --- etc/inc/util.inc | 37 +++++++++ etc/inc/vslb.inc | 92 ++++++++++++++-------- usr/local/www/load_balancer_pool_edit.php | 14 +++- .../www/load_balancer_virtual_server_edit.php | 6 +- 4 files changed, 108 insertions(+), 41 deletions(-) diff --git a/etc/inc/util.inc b/etc/inc/util.inc index 434862d..3660572 100644 --- a/etc/inc/util.inc +++ b/etc/inc/util.inc @@ -1048,6 +1048,43 @@ function alias_expand_urltable($name) { return null; } +function subnet_size ($subnet) { + if (is_subnetv4($subnet)) { + list ($ip, $bits) = explode("/", $subnet); + return round(exp(log(2) * (32 - $bits))); + } + else if (is_subnetv6($subnet)) { + list ($ip, $bits) = explode("/", $subnet); + return round(exp(log(2) * (128 - $bits))); + } + else { + return 0; + } +} + +function subnet_expand ($subnet) { + if (is_subnetv4($subnet)) { + return subnetv4_expand($subnet); + } else if (is_subnetv6($subnet)) { + return subnetv6_expand($subnet); + } else { + return $subnet; + } +} + +function subnetv4_expand ($subnet) { + $result = array(); + list ($ip, $bits) = explode("/", $subnet); + $net = ip2long($ip); + $mask = (0xffffffff << (32 - $bits)); + $net &= $mask; + $size = round(exp(log(2) * (32 - $bits))); + for ($i = 0; $i < $size; $i += 1) { + $result[] = long2ip($net | $i); + } + return $result; +} + /* find out whether two subnets overlap */ function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) { diff --git a/etc/inc/vslb.inc b/etc/inc/vslb.inc index 1fd9f77..d6d9931 100644 --- a/etc/inc/vslb.inc +++ b/etc/inc/vslb.inc @@ -217,11 +217,21 @@ function relayd_configure($kill_first=false) { if(is_array($pool_a[$i]['servers'])) { if (!empty($pool_a[$i]['retry'])) { $retrytext = " retry {$pool_a[$i]['retry']}"; - $srvtxt = implode("{$retrytext}, ", $pool_a[$i]['servers']) . "{$retrytext}"; } else { - $srvtxt = implode(", ", $pool_a[$i]['servers']); + $retrytext = ""; } - $conf .= "table <{$pool_a[$i]['name']}> { $srvtxt }\n"; + $conf .= "table <{$pool_a[$i]['name']}> {\n"; + foreach ($pool_a[$i]['servers'] as $server) { + if (is_subnetv4($server)) { + foreach (subnetv4_expand($server) as $ip) { + $conf .= "\t{$ip}{$retrytext}\n"; + } + } + else { + $conf .= "\t{$server}{$retrytext}\n"; + } + } + $conf .= "}\n"; /* Index by name for easier fetching when we loop through the virtual servers */ $pools[$pool_a[$i]['name']] = $pool_a[$i]; } @@ -244,43 +254,55 @@ function relayd_configure($kill_first=false) { // } $conf .= "dns protocol \"dnsproto\" {\n"; - $conf .= "\ttcp { nodelay, sack, socket buffer 1024, backlog 1000 }\n"; + $conf .= "\t" . "tcp { nodelay, sack, socket buffer 1024, backlog 1000 }\n"; $conf .= "}\n"; if(is_array($vs_a)) { for ($i = 0; isset($vs_a[$i]); $i++) { - if (($vs_a[$i]['mode'] == 'relay') || ($vs_a[$i]['relay_protocol'] == 'dns')) { - $conf .= "relay \"{$vs_a[$i]['name']}\" {\n"; - $conf .= " listen on {$vs_a[$i]['ipaddr']} port {$vs_a[$i]['port']}\n"; - - if ($vs_a[$i]['relay_protocol'] == "dns") { - $conf .= " protocol \"dnsproto\"\n"; - } else { - $conf .= " protocol \"{$vs_a[$i]['relay_protocol']}\"\n"; - } - $lbmode = ""; - if ( $pools[$vs_a[$i]['pool']]['mode'] == "loadbalance" ) { - $lbmode = "mode loadbalance"; + if (is_subnetv4($vs_a[$i]['ipaddr'])) { + $ip_list = subnetv4_expand($vs_a[$i]['ipaddr']); + $name_format = "%s_%d"; + } + else { + $ip_list = array($vs_a[$i]['ipaddr']); + $name_format = "%s"; /* in which case the second arg of the sprintf call below will be silently ignored */ + } + for ($j = 0; $j < count($ip_list); $j += 1) { + $ip = $ip_list[$j]; + $name = sprintf($name_format, $vs_a[$i]['name'], $j); + if (($vs_a[$i]['mode'] == 'relay') || ($vs_a[$i]['relay_protocol'] == 'dns')) { + $conf .= "relay \"{$name}\" {\n"; + $conf .= " listen on {$ip} port {$vs_a[$i]['port']}\n"; + + if ($vs_a[$i]['relay_protocol'] == "dns") { + $conf .= " protocol \"dnsproto\"\n"; + } else { + $conf .= " protocol \"{$vs_a[$i]['relay_protocol']}\"\n"; + } + $lbmode = ""; + if ( $pools[$vs_a[$i]['pool']]['mode'] == "loadbalance" ) { + $lbmode = "mode loadbalance"; + } + + $conf .= " forward to <{$vs_a[$i]['pool']}> port {$pools[$vs_a[$i]['pool']]['port']} {$lbmode} {$check_a[$pools[$vs_a[$i]['pool']]['monitor']]} \n"; + + if (isset($vs_a[$i]['sitedown']) && strlen($vs_a[$i]['sitedown']) > 0) + $conf .= " forward to <{$vs_a[$i]['sitedown']}> port {$pools[$vs_a[$i]['pool']]['port']} {$lbmode} {$check_a[$pools[$vs_a[$i]['pool']]['monitor']]} \n"; + $conf .= "}\n"; + } else { + $conf .= "redirect \"{$name}\" {\n"; + $conf .= " listen on {$ip} port {$vs_a[$i]['port']}\n"; + $conf .= " forward to <{$vs_a[$i]['pool']}> port {$pools[$vs_a[$i]['pool']]['port']} {$check_a[$pools[$vs_a[$i]['pool']]['monitor']]} \n"; + + if (isset($config['system']['lb_use_sticky'])) + $conf .= " sticky-address\n"; + + /* sitedown MUST use the same port as the primary pool - sucks, but it's a relayd thing */ + if (isset($vs_a[$i]['sitedown']) && strlen($vs_a[$i]['sitedown']) > 0) + $conf .= " forward to <{$vs_a[$i]['sitedown']}> port {$pools[$vs_a[$i]['pool']]['port']} {$check_a[$pools[$vs_a[$i]['pool']]['monitor']]} \n"; + + $conf .= "}\n"; } - - $conf .= " forward to <{$vs_a[$i]['pool']}> port {$pools[$vs_a[$i]['pool']]['port']} {$lbmode} {$check_a[$pools[$vs_a[$i]['pool']]['monitor']]} \n"; - - if (isset($vs_a[$i]['sitedown']) && strlen($vs_a[$i]['sitedown']) > 0) - $conf .= " forward to <{$vs_a[$i]['sitedown']}> port {$pools[$vs_a[$i]['pool']]['port']} {$lbmode} {$check_a[$pools[$vs_a[$i]['pool']]['monitor']]} \n"; - $conf .= "}\n"; - } else { - $conf .= "redirect \"{$vs_a[$i]['name']}\" {\n"; - $conf .= " listen on {$vs_a[$i]['ipaddr']} port {$vs_a[$i]['port']}\n"; - $conf .= " forward to <{$vs_a[$i]['pool']}> port {$pools[$vs_a[$i]['pool']]['port']} {$check_a[$pools[$vs_a[$i]['pool']]['monitor']]} \n"; - - if (isset($config['system']['lb_use_sticky'])) - $conf .= " sticky-address\n"; - - /* sitedown MUST use the same port as the primary pool - sucks, but it's a relayd thing */ - if (isset($vs_a[$i]['sitedown']) && strlen($vs_a[$i]['sitedown']) > 0) - $conf .= " forward to <{$vs_a[$i]['sitedown']}> port {$pools[$vs_a[$i]['pool']]['port']} {$check_a[$pools[$vs_a[$i]['pool']]['monitor']]} \n"; - - $conf .= "}\n"; } } } diff --git a/usr/local/www/load_balancer_pool_edit.php b/usr/local/www/load_balancer_pool_edit.php index d42e0b6..81a548c 100755 --- a/usr/local/www/load_balancer_pool_edit.php +++ b/usr/local/www/load_balancer_pool_edit.php @@ -94,15 +94,21 @@ if ($_POST) { if (is_array($_POST['servers'])) { foreach($pconfig['servers'] as $svrent) { - if (!is_ipaddr($svrent)) { - $input_errors[] = sprintf(gettext("%s is not a valid IP address (in \"enabled\" list)."), $svrent); + if (!is_ipaddr($svrent) && !is_subnetv4($svrent)) { + $input_errors[] = sprintf(gettext("%s is not a valid IP address or IPv4 subnet (in \"enabled\" list)."), $svrent); + } + else if (is_subnetv4($svrent) && subnet_size($svrent) > 64) { + $input_errors[] = sprintf(gettext("%s is a subnet containing more than 64 IP addresses (in \"enabled\" list)."), $svrent); } } } if (is_array($_POST['serversdisabled'])) { foreach($pconfig['serversdisabled'] as $svrent) { - if (!is_ipaddr($svrent)) { - $input_errors[] = sprintf(gettext("%s is not a valid IP address (in \"disabled\" list)."), $svrent); + if (!is_ipaddr($svrent) && !is_subnetv4($svrent)) { + $input_errors[] = sprintf(gettext("%s is not a valid IP address or IPv4 subnet (in \"disabled\" list)."), $svrent); + } + else if (is_subnetv4($svrent) && subnet_size($svrent) > 64) { + $input_errors[] = sprintf(gettext("%s is a subnet containing more than 64 IP addresses (in \"disabled\" list)."), $svrent); } } } diff --git a/usr/local/www/load_balancer_virtual_server_edit.php b/usr/local/www/load_balancer_virtual_server_edit.php index 7a2d792..ff59fd9 100755 --- a/usr/local/www/load_balancer_virtual_server_edit.php +++ b/usr/local/www/load_balancer_virtual_server_edit.php @@ -91,8 +91,10 @@ if ($_POST) { if (!is_port($_POST['port'])) $input_errors[] = gettext("The port must be an integer between 1 and 65535."); - if(!is_ipaddr($_POST['ipaddr'])) - $input_errors[] = sprintf(gettext("%s is not a valid IP address."), $_POST['ipaddr']); + if (!is_ipaddr($_POST['ipaddr']) && !is_subnetv4($_POST['ipaddr'])) + $input_errors[] = sprintf(gettext("%s is not a valid IP address or IPv4 subnet."), $_POST['ipaddr']); + else if (is_subnetv4($_POST['ipaddr']) && subnet_size($_POST['ipaddr']) > 64) + $input_errors[] = sprintf(gettext("%s is a subnet containing more than 64 IP addresses."), $_POST['ipaddr']); if (!$input_errors) { $vsent = array(); -- cgit v1.1