r5012 Significant part of IP-related code was rewritten for clarity, unification...
authorAlexey Andriyanov <alan@al-an.info>
Fri, 23 Mar 2012 11:28:42 +0000 (11:28 +0000)
committerAlexey Andriyanov <alan@al-an.info>
Fri, 23 Mar 2012 11:28:42 +0000 (11:28 +0000)
Visible changes:
 * IP tree rendering speed has been increased
 * IP tree filtering does not affect child networks of the filtered ones
 * In "do not show IPv4 usage" mode the network usage is availible by click through AJAX
 * Each arrow aside backtrace element in IP net pages is now clickable to display IP subtree with the corresponing net as root
 * Clicking on 'IP space' link in navigation bar from within IP net page resets saved filter
 * VLAN numbers in IP tree are rendered below CIDR, no more dedicated column
 * Auto scroll-down is performed when clicking on 'expand IP tree element' button
 * Row highlight color became nicer
 * 'knights' support for IPv6
 * IP log (allocation history) support for IPv6
 * 'collapse all' link in IP tree
 * '$spare_XX' and '$aggregate' autotags for IPv6 nets
 * v4 net usage progressbar displays both allocated and unallocated spaces
 * '$vlan_XX' autotags added to ipv4net/ipv6net cells
 * IPV4_TREE_RTR_AS_CELL config variable has another state 'none'
 * '$masklen_ge_XX' and '$masklen_le_XX' autotags removed from 'ipv4net' cells
 * Object's 'IPv4' and 'IPv6' tabs were merged into single 'IP' tab

Data structures changes:
 * all binary IPs of integer, class instance types were replaced by 4- and 16-bytes binary strings
 * range list items of scanIPv4Space parameter are now keyed by 'first' and 'last' instead if 'i32_first' and 'i32_last'
 * scanIPv6Space and scanIPv6Space results are indexed by binary string IP
 * most SQL queries were cleaned of INET_NTOA and INET_ATON - now they accept and return uint32 IPs
 * 'ip' key of getIPv4AddressSearchResult result item is now binary string
 * loadIPAddrList sets 'addrlist', 'own_addrlist', 'addrc', 'own_addrc'. No more 'addrt'.
 * findRouters output: no more 'addr', but 'ip_bin' key

 ipv4net, ipv6net entities:
 * no more 'parent_id' key
 * no more 'mask_bin_inv', 'db_first', 'db_last' keys
 * 'ip_bin', 'mask_bin' keys are now binary strings, nor integers neither IPv6Address objects
 * 'child_nets' key of ip nets is renamed to 'kidc'
 * new data structure IPRange with keys 'ip', 'ip_bin', 'mask', 'mask_bin'. ipv4net and ipv6 net are descendants of it
 * 'spare_ranges' key in ipv6net

Navigation changes:
 * new 'ip' object tab instead of 'ipv4' and 'ipv6' tabs
 * instead of updIPv4Allocation, addIPv4Allocation, delIPv4Allocation,
     updIPv6Allocation, addIPv6Allocation, delIPv6Allocation on 'ipv4' and 'ipv6' object tab, ops are named 'upd', 'add', 'del'.
 * the same for 'ipaddress' page
 * no more 'ipv6address' page, it is merged with 'ipaddress'
 * 'hl_ipaddress' and 'hl_ipv6address' http params were renamed to 'hl_ip'

Changed function list (+: new function, -: deleted function, ~: function prototype changed, *: function body changed)
- IPv6Address class
~ InvalidRequestArgException class is now a descendant of InvalidArgException
+ addIPLogEntry: v4/v6 wrapper
~ addIPv4LogEntry
+ addIPv6LogEntry
~ getAllIPv4Allocations
+ amplifyAllocationList: takes a list of allocs (returned by getObjectIPAllocationList) and fills ['addrinfo'] subarray for any IP address
+ bindIPToObject: v4/v6 wrapper
- bindIpToObject: renamed to bindIPv4ToObject
+ bindIPv4ToObject: renamed from bindIpToObject
~ bindIPv6ToObject
~ commitAddFile: now returns created id
~ commitAddObject: idem
~ commitCreateUserAccount: idem
- constructIPv4Address: replaced by constructIPAddress
- constructIPv6Address: idem
* createIPv4Prefix
* createIPv6Prefix
~ deletePortForwarding
+ fetchIPLogEntry: v4/v6 wrapper
~ fetchIPv4AddressNetworkRow
~ fetchIPv6AddressNetworkRow
~ fetchIPv4LogEntry
+ fetchIPv6LogEntry
* generateEntityAutoTags
+ getIPAddress: v4/v6 wrapper
~ getIPv4Address
~ getIPv6Address
+ getIPAddressNetworkId: v4/v6 wrapper
~ getIPv4AddressNetworkId
~ getIPv6AddressNetworkId
~ getIPv4AddressSearchResult
+ getObjectIPAllocationList: merges together the results of getObjectIPv4AllocationList and getObjectIPv6AllocationList
~ getObjectIPv4AllocationList
+ getObjectIPAllocations: v4/v6 wrapper
~ getObjectIPv4Allocations
~ getObjectIPv6Allocations
~ listCells
~ newPortForwarding
- produceTagsForLastRecord: removed if favor of produceTagsForNewRecord
+ produceTagsForNewRecord: the same as produceTagsForLastRecord, but requires new item id on input, does not call lastInsertID
+ scanIPSpace: v4/v6 wrapper
~ scanIPv4Space
~ scanIPv6Space
~ spotEntity
+ spotNetworkByIP: wrapper around getIPAddressNetworkId and spotEntity
- unbindIpFromObject: renamed into unbindIPv4FromObject
+ unbindIPFromObject: v4/v6 wrapper
+ unbindIPv4FromObject: it is the renamed unbindIpFromObject
~ unbindIPv6FromObject
~ updateAddress: v4/v6 wrapper
~ updateV4Address
~ updateV6Address
- updateBond: renamed into updateIPv4Bond
+ updateIPBond: v4/v6 wrapper
+ updateIPv4Bond: renamed from updateBond
~ updateIPv6Bond
~ updatePortForwarding

+ IPNetContains: returns TRUE if first range contains second
+ IPNetContainsOrEqual: returns TRUE if first range contains or equal to second
+ IPNetworkCmp: compares two IP ranges
- IPv4NetworkCmp: removed in favor of IPNetworkCmp
- IPv6NetworkCmp: idem
~ assertIPArg: now it returns binary IP
~ assertIPv4Arg: idem
~ assertIPv6Arg: idem
- binInvMaskFromDec: removed in favor of ~ip4_mask
- binMaskFromDec: removed in favor of ip4_mask
+ constructIPAddress: replaces constructIPv4Address and constructIPv6Address
+ constructIPRange: generates IP network array by IP and mask
- countOwnIPv4Addresses: replaced by loadIPAddrList
+ fillIPNetsCorrelation: renamed from fillIPv4NetsCorrelation
- fillIPv4SpareList: removed in favor of fillIPSpareListBstr
+ fillIPSpareListBstr: replacement of fillIPv4SpareList
- fillIPv4NetsCorrelation: renamed into fillIPNetsCorrelation
* findAllEndpoints
~ findRouters
* genericAssertion
* getCellFilter
* getEmployedVlans
~ getIPAddress
+ getIPv4OwnRangeSize: does what countOwnIPv4Addresses used to, but do not call this directly.
+ ip4_bin2db: part of IP format convertation toolkit
+ ip4_bin2int: idem
+ ip4_format: idem
+ ip4_int2bin: idem
+ ip4_parse: idem
+ ip4_checkparse: idem
+ ip6_checkparse: idem
+ ip_checkparse: idem
+ ip6_format: idem
+ ip6_parse: idem
+ ip_parse: idem
+ ip_format: idem
+ ip_mask: v4/v6 wrapper
+ ip4_mask: get binary mask from prefix length
+ ip6_mask: idem
+ ip4_range_size: part of IP range calculation toolkit
+ ip4_mask_size: idem
+ ip_last: idem
+ ip_next: idem
+ ip_prev: idem
- ip_long2quad: removed
- ip_quad2long: removed
~ iptree_construct: made v4/v6 compliant
~ iptree_embed: made v4/v6 compliant
* iptree_fill: made v4/v6 compliant
- ipv6tree_construct
- ipv6tree_embed
- ipv6tree_fill
* iptree_markup_collapsion
+ isCheckSet: function to check if HTML form's checkbox is set
+ loadIPAddrList: universal function instead of loadIPv4AddrList, loadIPv6AddrList, loadOwnIPv4Addresses, loadOwnIPv6Addresses, countOwnIPv4Addresses
- loadIPv4AddrList: removed in favor of loadIPAddrList
- loadIPv6AddrList: removed in favor of loadIPAddrList
- loadOwnIPv4Addresses: removed in favor of loadIPAddrList
- loadOwnIPv6Addresses: removed in favor of loadIPAddrList
+ makeIPTree: calculate parent_id for items in netlist
+ prepareIPTree: universal function instead of prepareIPv4Tree, prepareIPv6Tree
- prepareIPv4Tree: removed in favor of prepareIPTree
- prepareIPv6Tree removed in favor of prepareIPTree
~ searchEntitiesByText
+ set_word_value: former IPv6Address class method

+ getRenderedIPNetCapacity: v4/v6 wrapper
+ getRenderedIPv4NetCapacity
+ getRenderedIPv6NetCapacity
- formatIPv6NetUsage: code has gone into getRenderedIPv6NetCapacity
* renderProgressBar: moved from interface.php to interface-lib.php
* getProgressBar: idem
* dynamic_title_decoder
~ getPageNumOfIPv6: moved
~ getRenderedAlloc
* getRenderedIPNetBacktrace
* printIPNetInfoTDs
~ printRoutersTD
* renderCell
* renderDepot
~ renderEmptyIPv6
* renderIPAddress
* renderIPAddressAllocations
+ renderIPAddressLog: new universal renderer
- renderIPv4AddressLog: removed in favor of renderIPAddressLog
* renderIPAddressProperties
+ renderIPForObject: new universal renderer
- renderIPv4ForObject: removed in favor of renderIPTabForObject
- renderIPv6ForObject: removed in favor of renderIPTabForObjectt
+ renderIPNetworkAddresses
+ renderIPSpace: new universal renderer
- renderIPv4Space: removed in favor of renderIPv4Space
- renderIPv6Space: removed in favor of renderIPv6Space
+ renderIPSpaceEditor: new universal renderer
- renderIPv4SpaceEditor: removed in favor of renderIPSpaceEditor
- renderIPv6SpaceEditor:  removed in favor of renderIPv6SpaceEditor
+ renderIPNetwork: new universal renderer
- renderIPv4Network: removed in favor of renderIPNetwork
- renderIPv6Network: removed in favor of renderIPNetwork
+ renderIPv4NetworkAddresses: v4-specific part of renderIPv4Network gone here
* renderIPv6NetworkAddresses
+ renderIPSpaceRecords: replaces renderIPv4SpaceRecords and renderIPv6SpaceRecords
- renderIPv4SpaceRecords: removed in favor of renderIPSpaceRecords
- renderIPv6SpaceRecords: removed in favor of renderIPSpaceRecords
- renderIPTabForObject: removed in favor of renderIPForObject
* renderLivePTR
* renderNATv4ForObject
+ renderNetVLAN
* renderObject
~ renderRouterCell
* renderSearchResults
~ renderSeparator
* showPathAndSearch

* addFileWithoutLink
+ addIPAllocation: new universal ophandler
- addIPv4Allocation: removed in favor or addIPAllocation
- addIPv6Allocation: removed in favor or addIPAllocation
* addIPv4Prefix
* addIPv6Prefix
* addPortForwarding
* addRack
* addRealServer
* addRealServers
* addVService
* cloneRSPool
* createUser
+ delIPAllocation: new universal ophandler
- delIPv4Allocation: removed in favor or delIPAllocation
- delIPv6Allocation: removed in favor or delIPAllocation
* delIPv4Prefix
* delIPv6Prefix
* delPortForwarding
* editAddress: universal ophandler instead of v4-only
- editv6Address: removed in favor of editAddress
* importPTRData
+ updIPAllocation: new universal ophandler
- updIPv4Allocation: removed in favor or updIPAllocation
- updIPv6Allocation: removed in favor of updIPAllocation
* updPortForwarding
* updateObject
* updateRack
* updateRealServer
* updateVService

* renderSLBTriplets
~ addRStoRSPool
* commitCreateRSPool
~ commitUpdateRS
~ commitUpdateVS
+ renderProgressBar4Image: 4-fields progress-bar renderer
+ triggerIPAddressLog: universal trigger
- triggerIPv4AddressLog: removed in favor of triggerIPAddressLog
+ trigger_ip: universal trigger
- trigger_ipv4: removed in favor of trigger_ip
- trigger_ipv6: removed in favor of trigger_ip
* trigger_natv4
* executeUpgradeBatch

19 files changed:
Makefile
wwwroot/css/pi.css
wwwroot/inc/IPv6.php [deleted file]
wwwroot/inc/ajax-interface.php
wwwroot/inc/database.php
wwwroot/inc/exceptions.php
wwwroot/inc/functions.php
wwwroot/inc/init.php
wwwroot/inc/install.php
wwwroot/inc/interface-lib.php
wwwroot/inc/interface.php
wwwroot/inc/navigation.php
wwwroot/inc/ophandlers.php
wwwroot/inc/slb-interface.php
wwwroot/inc/slb.php
wwwroot/inc/solutions.php
wwwroot/inc/triggers.php
wwwroot/inc/upgrade.php
wwwroot/index.php

index 3d04bca..7c9824b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -16,6 +16,15 @@ INSTALL_DATA    := $(INSTALL) -m 644
 INSTALL_DIR     := $(INSTALL) -m 755 -d
 INSTALL_PROGRAM := $(INSTALL) -m 755
 
+push:
+       rsync -avz ./ noc:rt-svn/trunk-iptree/
+
+pull:
+       rsync -avz noc:rt-svn/trunk-iptree/ ./
+
+tags:
+       ctags -R .
+
 install-docs: COPYING ChangeLog LICENSE README
        $(INSTALL_DIR) $(DESTDIR)$(docdir)
        $(INSTALL_DATA) $^ $(DESTDIR)$(docdir)
@@ -40,3 +49,5 @@ install-index: wwwroot/index.php
        $(INSTALL_DATA) wwwroot/index.php $(DESTDIR)$(indexdir)
 
 install: install-helpers install-static install-applib install-index
+
+.PHONY: tags
index 2c33048..999372c 100644 (file)
@@ -165,7 +165,7 @@ td.sticker {
 }
 
 tr.port_highlight, td.port_highlight {
-       background-color: lime;
+       background-color: #A0FFA0;
 }
 
 .row_even {
@@ -551,11 +551,14 @@ div.tagselector td {
 a.toggleTreeMode {
 }
 
-.net-usage {
-       float: right;
+div.vlan {
        margin-left: 15px;
 }
 
+.slbcell div.vlan {
+       float: right;
+}
+
 .btns-8021q-sync li {
        display: inline;
        list-style-type: none;
@@ -730,5 +733,25 @@ a.noclick {
 }
 
 a.slb-highlighted {
-       background-color: lime;
+       background-color: #A0FFA0;
+}
+
+.net-usage.pending {
+       cursor: pointer;
+       border-bottom: dashed 1px;
+       display: inline;
+       padding: 0px 5px;
+}
+.slbcell .net-usage {
+       float: right;
+       margin-left: 15px;
+}
+
+.net-usage .title {
+       display: block;
+}
+
+.aac {
+       font-weight: bold;
+       margin-left: 1em;
 }
diff --git a/wwwroot/inc/IPv6.php b/wwwroot/inc/IPv6.php
deleted file mode 100644 (file)
index 9dc0491..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-<?php
-
-# This file is a part of RackTables, a datacenter and server room management
-# framework. See accompanying file "COPYING" for the full copyright and
-# licensing information.
-
-class IPv6Address
-{
-       const zero_address = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; // 16 bytes
-       protected $words = self::zero_address;
-
-function __construct ($bin_str = self::zero_address)
-{
-       if (strlen ($bin_str) < 16)
-               $bin_str .= substr (self::zero_address, 0, 16 - strlen ($bin_str));
-       elseif (strlen ($bin_str) > 16)
-               $bin_str = substr ($bin_str, 0, 16);
-       $this->words = $bin_str;
-}
-
-// returns 16-byte binary string
-function getBin ()
-{
-       return $this->words;
-}
-
-// returns string for PTR DNS query (reversed IPv6 address)
-function getArpa()
-{
-       $ret = '';
-       foreach (array_reverse (unpack ('C*', $this->words)) as $octet)
-       {
-               $ret .= dechex ($octet & 0xF) . ".";
-               $ret .= dechex ($octet >> 4) . ".";
-       }
-       return $ret . "ip6.arpa";
-}
-
-private static function set_word_value (&$haystack, $nword, $hexvalue)
-{
-       // check that $hexvalue is like /^[0-9a-fA-F]*$/
-       for ($i = 0; $i < strlen ($hexvalue); $i++)
-       {
-               $char = ord ($hexvalue[$i]);
-               if (! ($char >= 0x30 && $char <= 0x39 || $char >= 0x41 && $char <= 0x46 || $char >=0x61 && $char <= 0x66))
-                       return FALSE;
-       }
-       $haystack = substr_replace ($haystack, pack ('n', hexdec ($hexvalue)), $nword * 2, 2);
-       return TRUE;
-}
-
-// returns bool - was the object modified or not.
-// return true only if address syntax is completely correct.
-function parse ($str_ipv6)
-{
-       if (empty ($str_ipv6))
-               return FALSE;
-
-       $result = self::zero_address;
-       // remove one of double beginning/tailing colons
-       if (substr ($str_ipv6, 0, 2) == '::')
-               $str_ipv6 = substr ($str_ipv6, 1);
-       elseif (substr ($str_ipv6, -2, 2) == '::')
-               $str_ipv6 = substr ($str_ipv6, 0, strlen ($str_ipv6) - 1);
-
-       $tokens = explode (':', $str_ipv6);
-       $last_token = $tokens[count ($tokens) - 1];
-       $split = explode ('.', $last_token);
-       if (count ($split) == 4)
-       {
-               $hex_tokens = array();
-               $hex_tokens[] = dechex ($split[0] * 256 + $split[1]);
-               $hex_tokens[] = dechex ($split[2] * 256 + $split[3]);
-               array_splice ($tokens, -1, 1, $hex_tokens);
-       }
-       if (count ($tokens) > 8)
-               return FALSE;
-       for ($i = 0; $i < count ($tokens); $i++)
-       {
-               if ($tokens[$i] != '')
-               {
-                       if (! self::set_word_value ($result, $i, $tokens[$i]))
-                               return FALSE;
-               }
-               else
-               {
-                       $k = 8; //index in result string (last word)
-                       for ($j = count ($tokens) - 1; $j > $i; $j--) // $j is an index in $tokens for reverse walk
-                               if ($tokens[$j] == '')
-                                       break;
-                               elseif (! self::set_word_value ($result, --$k, $tokens[$j]))
-                                       return FALSE;
-                       if ($i != $j)
-                               return FALSE; //error, more than 1 '::' range
-                       break;
-               }
-       }
-       if (! isset ($k) && count ($tokens) != 8)
-               return FALSE;
-       $this->words = $result;
-       return TRUE;
-}
-
-function format ()
-{
-       // maybe this is IPv6-to-IPv4 address?
-       if (substr ($this->words, 0, 12) == "\0\0\0\0\0\0\0\0\0\0\xff\xff")
-               return '::ffff:' . implode ('.', unpack ('C*', substr ($this->words, 12, 4)));
-
-       $result = array();
-       $hole_index = NULL;
-       $max_hole_index = NULL;
-       $hole_length = 0;
-       $max_hole_length = 0;
-
-       for ($i = 0; $i < 8; $i++)
-       {
-               $unpacked = unpack ('n', substr ($this->words, $i * 2, 2));
-               $value = array_shift ($unpacked);
-               $result[] = dechex ($value & 0xffff);
-               if ($value != 0)
-               {
-                       unset ($hole_index);
-                       $hole_length = 0;
-               }
-               else
-               {
-                       if (! isset ($hole_index))
-                               $hole_index = $i;
-                       if (++$hole_length >= $max_hole_length)
-                       {
-                               $max_hole_index = $hole_index;
-                               $max_hole_length = $hole_length;
-                       }
-               }
-       }
-       if (isset ($max_hole_index))
-       {
-               array_splice ($result, $max_hole_index, $max_hole_length, array (''));
-               if ($max_hole_index == 0 && $max_hole_length == 8)
-                       return '::';
-               elseif ($max_hole_index == 0)
-                       return ':' . implode (':', $result);
-               elseif ($max_hole_index + $max_hole_length == 8)
-                       return implode (':', $result) . ':';
-       }
-       return implode (':', $result);
-}
-
-// returns new object with applied mask, or NULL if mask is incorrect
-function get_first_subnet_address ($prefix_length)
-{
-       if ($prefix_length < 0 || $prefix_length > 128)
-               return NULL;
-       $result = clone $this;
-       if ($prefix_length == 128)
-               return $result;
-       $char_num = intval ($prefix_length / 8);
-       if (0xff00 != $bitmask = 0xff00 >> ($prefix_length % 8))
-       {
-               $result->words[$char_num] = chr (ord ($result->words[$char_num]) & $bitmask);
-               ++$char_num;
-       }
-       for ($i = $char_num; $i < 16; $i++)
-               $result->words[$i] = "\0";
-       return $result;
-}
-
-// returns new object with applied mask, or NULL if mask is incorrect
-function get_last_subnet_address ($prefix_length)
-{
-       if ($prefix_length < 0 || $prefix_length > 128)
-               return NULL;
-       $result = clone $this;
-       if ($prefix_length == 128)
-               return $result;
-       $char_num = intval ($prefix_length / 8);
-       if (0xff != $bitmask = 0xff >> ($prefix_length % 8))
-       {
-               $result->words[$char_num] = chr (ord ($result->words[$char_num]) | $bitmask);
-               ++$char_num;
-       }
-       for ($i = $char_num; $i < 16; $i++)
-               $result->words[$i] = "\xff";
-       return $result;
-}
-
-// returns new object
-function next ()
-{
-       $result = clone $this;
-       for ($i = 15; $i >= 0; $i--)
-       {
-               if ($result->words[$i] == "\xff")
-                       $result->words[$i] = "\0";
-               else
-               {
-                       $result->words[$i] = chr (ord ($result->words[$i]) + 1);
-                       break;
-               }
-       }
-       return $result;
-}
-
-// returns new object
-function prev ()
-{
-       $result = clone $this;
-       for ($i = 15; $i >= 0; $i--)
-       {
-               if ($result->words[$i] == "\0")
-                       $result->words[$i] = "\xff";
-               else
-               {
-                       $result->words[$i] = chr (ord ($result->words[$i]) - 1);
-                       break;
-               }
-       }
-       return $result;
-}
-
-# $a == $b
-public static function eq (IPv6Address $a, IPv6Address $b)
-{
-       return $a->words === $b->words;
-}
-
-# $a > $b
-public static function gt (IPv6Address $a, IPv6Address $b)
-{
-       for ($i = 0; $i < 16; $i++)
-               if ($a->words[$i] > $b->words[$i])
-                       return TRUE;
-               elseif ($a->words[$i] < $b->words[$i])
-                       return FALSE;
-       return FALSE;
-}
-
-# $a < $b
-public static function lt (IPv6Address $a, IPv6Address $b)
-{
-       return ! self::eq ($a, $b) && ! self::gt ($a, $b);
-}
-
-# $a >= $b
-public static function ge (IPv6Address $a, IPv6Address $b)
-{
-       return self::eq ($a, $b) || self::gt ($a, $b);
-}
-
-# $a <= $b
-public static function le (IPv6Address $a, IPv6Address $b)
-{
-       return self::eq ($a, $b) || ! self::gt ($a, $b);
-}
-
-} // class IPv6Address
-
-?>
index aba864b..7075669 100644 (file)
@@ -208,27 +208,28 @@ function dispatchAJAXRequest()
        case 'upd-reservation-ip':
                global $sic;
                assertStringArg ('comment', TRUE);
-               $ip = assertIPArg ('id');
-               if (isset ($ip))
-               {
-                       $net_realm = 'ipv6net';
-                       $net_id = getIPv6AddressNetworkId ($ip);
-               }
-               else
-               {
-                       $ip = $sic['id'];
-                       $net_realm = 'ipv4net';
-                       $net_id = getIPv4AddressNetworkId ($ip);
-               }
-               $addr = getIPAddress ($ip);
+               $ip_bin = assertIPArg ('id');
+               $addr = getIPAddress ($ip_bin);
                if (! empty ($addr['allocs']) && empty ($addr['name']))
                        throw new RackTablesError ('Cant update IP comment: address is allocated');
-               if (isset ($net_id))
-                       fixContext (spotEntity ($net_realm, $net_id));
-               assertPermission ($net_realm, NULL, 'set_reserve_comment');
-               updateAddress ($ip, $sic['comment'], $addr['reserved']);
+               $net = spotNetworkByIP ($ip_bin);
+               if (isset ($net))
+                       fixContext ($net);
+               assertPermission ((strlen ($ip_bin) == 16 ? 'ipv6net' : 'ipv4net'), NULL, 'set_reserve_comment');
+               $reserved = (empty ($sic['comment']) ? 'no' : $addr['reserved']); // unset reservation if user clears comment
+               updateAddress ($ip_bin, $sic['comment'], $reserved);
                echo json_encode ('OK');
                break;
+       case 'net-usage':
+               assertStringArg ('net_id');
+               list ($ip, $mask) = explode ('/', $_REQUEST['net_id']);
+               $ip_bin = ip_parse ($ip);
+               $net = spotNetworkByIP ($ip_bin, $mask + 1);
+               if (! isset ($net) or $net['mask'] != $mask)
+                       $net = constructIPRange ($ip_bin, $mask);
+               loadIPAddrList ($net);
+               echo getRenderedIPNetCapacity ($net);
+               break;
        default:
                throw new InvalidRequestArgException ('ac', $_REQUEST['ac']);
        }
index 81a5822..9732b23 100644 (file)
@@ -52,11 +52,10 @@ $SQLSchema = array
                'columns' => array
                (
                        'id' => 'id',
-                       'ip' => 'INET_NTOA(IPv4Network.ip)',
+                       'ip_bin' => 'ip',
                        'mask' => 'mask',
                        'name' => 'name',
                        'comment' => 'comment',
-                       'parent_id' => '(SELECT id FROM IPv4Network AS subt WHERE IPv4Network.ip & (4294967295 >> (32 - subt.mask)) << (32 - subt.mask) = subt.ip and subt.mask < IPv4Network.mask ORDER BY subt.mask DESC limit 1)',
                        '8021q' => '(SELECT GROUP_CONCAT(CONCAT(domain_id, "-", vlan_id) ORDER BY domain_id) FROM VLANIPv4 WHERE ipv4net_id = id)',
                ),
                'keycolumn' => 'id',
@@ -72,7 +71,6 @@ $SQLSchema = array
                        'mask' => 'mask',
                        'name' => 'name',
                        'comment' => 'comment',
-                       'parent_id' => '(SELECT id FROM IPv6Network AS subt WHERE IPv6Network.ip >= subt.ip AND IPv6Network.last_ip <= subt.last_ip AND IPv6Network.mask > subt.mask ORDER BY subt.mask DESC limit 1)',
                        '8021q' => '(SELECT GROUP_CONCAT(CONCAT(domain_id, "-", vlan_id) ORDER BY domain_id) FROM VLANIPv6 WHERE ipv6net_id = id)',
                ),
                'keycolumn' => 'id',
@@ -445,28 +443,23 @@ function listCells ($realm, $parent_id = 0)
                        setDisplayedName ($entity);
                        break;
                case 'ipv4net':
+                       $entity = array_merge ($entity, constructIPRange (ip4_int2bin ($entity['ip_bin']), $entity['mask']));
                        processIPNetVlans ($entity);
-                       $entity['ip_bin'] = ip2long ($entity['ip']);
-                       $entity['mask_bin'] = binMaskFromDec ($entity['mask']);
-                       $entity['mask_bin_inv'] = binInvMaskFromDec ($entity['mask']);
-                       $entity['db_first'] = sprintf ('%u', 0x00000000 + $entity['ip_bin'] & $entity['mask_bin']);
-                       $entity['db_last'] = sprintf ('%u', 0x00000000 + $entity['ip_bin'] | ($entity['mask_bin_inv']));
                        $entity['spare_ranges'] = array();
-                       $entity['child_netc'] = 0;
+                       $entity['kidc'] = 0;
                        break;
                case 'ipv6net':
+                       $entity = array_merge ($entity, constructIPRange ($entity['ip_bin'], $entity['mask']));
                        processIPNetVlans ($entity);
-                       $entity['ip_bin'] = new IPv6Address ($entity['ip_bin']);
-                       $entity['ip'] = $entity['ip_bin']->format();
-                       $entity['db_first'] = $entity['ip_bin']->get_first_subnet_address($entity['mask']);
-                       $entity['db_last'] = $entity['ip_bin']->get_last_subnet_address($entity['mask']);
+                       $entity['spare_ranges'] = array();
+                       $entity['kidc'] = 0;
                        break;
                default:
                        break;
                }
        }
-       if ($realm == 'ipv4net')
-               fillIPv4NetsCorrelation ($ret);
+       if ($realm == 'ipv4net' or $realm == 'ipv6net')
+               fillIPNetsCorrelation ($ret);
 
        foreach (array_keys ($ret) as $entity_id)
        {
@@ -556,20 +549,15 @@ function spotEntity ($realm, $id, $ignore_cache = FALSE)
                break;
        case 'ipv4net':
                processIPNetVlans ($ret);
-               $ret['ip_bin'] = ip2long ($ret['ip']);
-               $ret['mask_bin'] = binMaskFromDec ($ret['mask']);
-               $ret['mask_bin_inv'] = binInvMaskFromDec ($ret['mask']);
-               $ret['db_first'] = sprintf ('%u', 0x00000000 + $ret['ip_bin'] & $ret['mask_bin']);
-               $ret['db_last'] = sprintf ('%u', 0x00000000 + $ret['ip_bin'] | ($ret['mask_bin_inv']));
+               $ret = array_merge ($ret, constructIPRange (ip4_int2bin ($ret['ip_bin']), $ret['mask']));
                $ret['spare_ranges'] = array();
-               $ret['child_netc'] = 0;
+               $ret['kidc'] = 0;
                break;
        case 'ipv6net':
                processIPNetVlans ($ret);
-               $ret['ip_bin'] = new IPv6Address ($ret['ip_bin']);
-               $ret['ip'] = $ret['ip_bin']->format();
-               $ret['db_first'] = $ret['ip_bin']->get_first_subnet_address($ret['mask']);
-               $ret['db_last'] = $ret['ip_bin']->get_last_subnet_address($ret['mask']);
+               $ret = array_merge ($ret, constructIPRange ($ret['ip_bin'], $ret['mask']));
+               $ret['spare_ranges'] = array();
+               $ret['kidc'] = 0;
                break;
        default:
                break;
@@ -578,32 +566,58 @@ function spotEntity ($realm, $id, $ignore_cache = FALSE)
        if ($realm == 'ipv4net')
        {
                $result = usePreparedSelectBlade ("
-SELECT n2.* FROM
+SELECT n2.id, n2.ip as ip_bin, n2.mask FROM
        IPv4Network n1,
        IPv4Network n2
 WHERE
        n1.id = ?
        AND n2.ip BETWEEN n1.ip AND (n1.ip + (1 << (32 - n1.mask)) - 1)
        AND n2.mask >= n1.mask
+ORDER BY n2.ip, n2.mask
 ", array ($id));
                $nets = $result->fetchAll (PDO::FETCH_ASSOC);
                foreach ($nets as &$net_row)
                {
-                       $net_row['db_first'] = sprintf ('%u', 0x00000000 + $net_row['ip'] & binMaskFromDec ($net_row['mask']));
-                       $net_row['db_last'] = sprintf ('%u', 0x00000000 + $net_row['ip'] | binInvMaskFromDec ($net_row['mask']));
+                       $net_row = array_merge ($net_row, constructIPRange (ip4_int2bin ($net_row['ip_bin']), $net_row['mask']));
                        $net_row['spare_ranges'] = array();
-                       $net_row['child_netc'] = 0;
+                       $net_row['kidc'] = 0;
                }
-               fillIPv4NetsCorrelation ($nets);
+               fillIPNetsCorrelation ($nets);
                if (is_array ($nets[0]) and $nets[0]['id'] == $id)
                {
                        $ret['spare_ranges'] = $nets[0]['spare_ranges'];
-                       $ret['child_netc'] = $nets[0]['child_netc'];
+                       $ret['kidc'] = $nets[0]['kidc'];
+               }
+               unset ($result);
+       }
+       elseif ($realm == 'ipv6net')
+       {
+               $result = usePreparedSelectBlade ("
+SELECT n2.id, n2.ip as ip_bin, n2.mask FROM
+       IPv6Network n1,
+       IPv6Network n2
+WHERE
+       n1.id = ?
+       AND n2.ip BETWEEN n1.ip AND n1.last_ip
+       AND n2.mask >= n1.mask
+ORDER BY n2.ip, n2.mask
+", array ($id));
+               $nets = $result->fetchAll (PDO::FETCH_ASSOC);
+               foreach ($nets as &$net_row)
+               {
+                       $net_row = array_merge ($net_row, constructIPRange ($net_row['ip_bin'], $net_row['mask']));
+                       $net_row['spare_ranges'] = array();
+                       $net_row['kidc'] = 0;
+               }
+               fillIPNetsCorrelation ($nets);
+               if (is_array ($nets[0]) and $nets[0]['id'] == $id)
+               {
+                       $ret['spare_ranges'] = $nets[0]['spare_ranges'];
+                       $ret['kidc'] = $nets[0]['kidc'];
                }
                unset ($result);
        }
        $ret['atags'] = generateEntityAutoTags ($ret);
-       
        $entityCache['partial'][$realm][$id] = $ret;
        return $ret;
 }
@@ -771,7 +785,7 @@ function commitAddObject ($new_name, $new_label, $new_type_id, $new_asset_no, $t
        // Do AutoPorts magic
        executeAutoPorts ($object_id, $new_type_id);
        // Now tags...
-       produceTagsForLastRecord ('object', $taglist, $object_id);
+       produceTagsForNewRecord ('object', $taglist, $object_id);
        recordObjectHistory ($object_id);
        return $object_id;
 }
@@ -1419,6 +1433,7 @@ function getAllIPv4Allocations ()
                "select object_id as object_id, ".
                "Object.name as object_name, ".
                "IPv4Allocation.name as name, ".
+               "IPv4Allocation.type as type, ".
                "INET_NTOA(ip) as ip ".
                "from IPv4Allocation join Object on id=object_id "
        );
@@ -1535,7 +1550,30 @@ function addPortLogEntry ($port_id, $message)
        );
 }
 
-function addIPv4LogEntry ($ip, $message)
+function addIPLogEntry ($ip_bin, $message)
+{
+       switch (strlen ($ip_bin))
+       {
+               case 4:  return addIPv4LogEntry ($ip_bin, $message);
+               case 16: return addIPv6LogEntry ($ip_bin, $message);
+               default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       }
+}
+
+function addIPv4LogEntry ($ip_bin, $message)
+{
+       global $disable_logging;
+       if (isset ($disable_logging) && $disable_logging)
+               return;
+       global $remote_username;
+       usePreparedExecuteBlade
+       (
+               "INSERT INTO IPv4Log (ip, date, user, message) VALUES (?, NOW(), ?, ?)",
+               array (ip4_bin2db ($ip_bin), $remote_username, $message)
+       );
+}
+
+function addIPv6LogEntry ($ip_bin, $message)
 {
        global $disable_logging;
        if (isset ($disable_logging) && $disable_logging)
@@ -1543,21 +1581,51 @@ function addIPv4LogEntry ($ip, $message)
        global $remote_username;
        usePreparedExecuteBlade
        (
-               "INSERT INTO IPv4Log (ip, date, user, message) VALUES (INET_ATON(?), NOW(), ?, ?)",
-               array ($ip, $remote_username, $message)
+               "INSERT INTO IPv6Log (ip, date, user, message) VALUES (?, NOW(), ?, ?)",
+               array ($ip_bin, $remote_username, $message)
        );
 }
 
-function fetchIPv4LogEntry ($ip)
+function fetchIPLogEntry ($ip_bin)
+{
+       switch (strlen ($ip_bin))
+       {
+               case 4:  return fetchIPv4LogEntry ($ip_bin);
+               case 16: return fetchIPv6LogEntry ($ip_bin);
+               default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       }
+}
+
+function fetchIPv4LogEntry ($ip_bin)
 {
        $result = usePreparedSelectBlade
        (
-               "SELECT INET_NTOA(ip) as ip, date, user, message FROM IPv4Log WHERE ip = INET_ATON(?) ORDER BY date ASC",
-               array ($ip)
+               "SELECT date, user, message FROM IPv4Log WHERE ip = ? ORDER BY date ASC",
+               array (ip4_bin2db ($ip_bin))
        );
        return $result->fetchAll (PDO::FETCH_ASSOC);
 }
 
+function fetchIPv6LogEntry ($ip_bin)
+{
+       $result = usePreparedSelectBlade
+       (
+               "SELECT date, user, message FROM IPv6Log WHERE ip = ? ORDER BY date ASC",
+               array ($ip_bin)
+       );
+       return $result->fetchAll (PDO::FETCH_ASSOC);
+}
+
+// wrapper around getObjectIPv4AllocationList and getObjectIPv6AllocationList
+function getObjectIPAllocationList ($object_id)
+{
+       return array_merge
+       (
+               getObjectIPv4AllocationList ($object_id),
+               getObjectIPv6AllocationList ($object_id)
+       );
+}
+
 // Returns all IPv4 addresses allocated to object, but does not attach detailed info about address
 // Used instead of getObjectIPv4Allocations if you need perfomance but 'addrinfo' value
 function getObjectIPv4AllocationList ($object_id)
@@ -1565,30 +1633,12 @@ function getObjectIPv4AllocationList ($object_id)
        $ret = array();
        $result = usePreparedSelectBlade
        (
-               'SELECT name AS osif, type, inet_ntoa(ip) AS dottedquad FROM IPv4Allocation ' .
+               'SELECT name AS osif, type, ip FROM IPv4Allocation ' .
                'WHERE object_id = ?',
                array ($object_id)
        );
        while ($row = $result->fetch (PDO::FETCH_ASSOC))
-               $ret[$row['dottedquad']] = array ('osif' => $row['osif'], 'type' => $row['type']);
-       return $ret;
-}
-
-// Return all IPv4 addresses allocated to the objects sorted by allocation name.
-// Attach detailed info about address to each alocation records.
-// Index result by dotted-quad address.
-function getObjectIPv4Allocations ($object_id = 0)
-{
-       $ret = array();
-       $sorted = array();
-       foreach (getObjectIPv4AllocationList ($object_id) as $dottedquad => $alloc)
-               $sorted[$alloc['osif']][$dottedquad] = $alloc;
-       foreach (sortPortList ($sorted) as $osif => $subarray)
-               foreach ($subarray as $dottedquad => $alloc)
-               {
-                       $alloc['addrinfo'] = getIPv4Address ($dottedquad);
-                       $ret[$dottedquad] = $alloc;
-               }
+               $ret[ip4_int2bin ($row['ip'])] = array ('osif' => $row['osif'], 'type' => $row['type']);
        return $ret;
 }
 
@@ -1608,62 +1658,59 @@ function getObjectIPv6AllocationList ($object_id)
        return $ret;
 }
 
-// Return all IPv6 addresses allocated to the objects sorted by allocation name.
+// Return all IP addresses allocated to the object sorted by allocation name.
 // Attach detailed info about address to each alocation records.
-// Index result by binary string of IPv6 address
-function getObjectIPv6Allocations ($object_id = 0)
+// Index result by binary ip
+function getObjectIPAllocations ($object_id)
+{
+       return amplifyAllocationList (getObjectIPAllocationList ($object_id));
+}
+function getObjectIPv4Allocations ($object_id)
+{
+       return amplifyAllocationList (getObjectIPv4AllocationList ($object_id));
+}
+function getObjectIPv6Allocations ($object_id)
+{
+       return amplifyAllocationList (getObjectIPv6AllocationList ($object_id));
+}
+
+function amplifyAllocationList ($alloc_list)
 {
        $ret = array();
        $sorted = array();
-       foreach (getObjectIPv6AllocationList ($object_id) as $ip_bin => $alloc)
+       foreach ($alloc_list as $ip_bin => $alloc)
                $sorted[$alloc['osif']][$ip_bin] = $alloc;
        foreach (sortPortList ($sorted) as $osif => $subarray)
                foreach ($subarray as $ip_bin => $alloc)
                {
-                       $alloc['addrinfo'] = getIPv6Address (new IPv6Address ($ip_bin));
+                       $alloc['addrinfo'] = getIPAddress ($ip_bin);
                        $ret[$ip_bin] = $alloc;
                }
        return $ret;
 }
 
-// Return minimal IPv4 address, optionally with "ip" key set, if requested.
-function constructIPv4Address ($dottedquad = NULL)
+function scanIPSpace ($pairlist)
 {
-       $ret = array
-       (
-               'version' => 4,
-               'name' => '',
-               'reserved' => 'no',
-               'outpf' => array(),
-               'inpf' => array(),
-               'allocs' => array(),
-               'vslist' => array(),
-               'rsplist' => array(),
-       );
-       if ($dottedquad != NULL)
-               $ret['ip'] = $dottedquad;
-       return $ret;
-}
-
-// Return minimal IPv6 address, optionally with "ip" key set, if requested.
-function constructIPv6Address ($bin_ip = NULL)
-{
-       $ret = array
+       $v4_pairs = array();
+       $v6_pairs = array();
+       foreach ($pairlist as $pair)
+       {
+               if (strlen ($pair['first']) == 4)
+                       $v4_pairs[] = $pair;
+               elseif (strlen ($pair['first']) == 16)
+                       $v6_pairs[] = $pair;
+       }
+       return array_merge
        (
-               'version' => 6,
-               'name' => '',
-               'reserved' => 'no',
-               'allocs' => array(),
+               scanIPv4Space ($v4_pairs),
+               scanIPv6Space ($v6_pairs)
        );
-       if ($bin_ip != NULL)
-               $ret['ip'] = $bin_ip->format();
-       return $ret;
 }
 
 // Check the range requested for meaningful IPv4 records, build them
 // into a list and return. Return an empty list if nothing matched.
-// Both arguments are expected in signed int32 form. The resulting list
-// is keyed by uint32 form of each IP address, items aren't sorted.
+// Both arguments are expected in 4-byte binary string form. The resulting list
+// is keyed by 4-byte binary IPs, items aren't sorted.
 // LATER: accept a list of pairs and build WHERE sub-expression accordingly
 function scanIPv4Space ($pairlist)
 {
@@ -1682,8 +1729,6 @@ function scanIPv4Space ($pairlist)
        $qparams = array();
        foreach ($pairlist as $tmp)
        {
-               $db_first = sprintf ('%u', 0x00000000 + $tmp['i32_first']);
-               $db_last = sprintf ('%u', 0x00000000 + $tmp['i32_last']);
                $whereexpr1 .= $or . "ip between ? and ?";
                $whereexpr2 .= $or . "ip between ? and ?";
                $whereexpr3 .= $or . "vip between ? and ?";
@@ -1692,8 +1737,8 @@ function scanIPv4Space ($pairlist)
                $whereexpr5b .= $or . "localip between ? and ?";
                $whereexpr6 .= $or . "ip between ? and ?";
                $or = ' or ';
-               $qparams[] = $db_first;
-               $qparams[] = $db_last;
+               $qparams[] = ip4_bin2db ($tmp['first']);
+               $qparams[] = ip4_bin2db ($tmp['last']);
        }
        $whereexpr1 .= ')';
        $whereexpr2 .= ')';
@@ -1704,14 +1749,14 @@ function scanIPv4Space ($pairlist)
        $whereexpr6 .= ')';
 
        // 1. collect labels and reservations
-       $query = "select INET_NTOA(ip) as ip, name, reserved from IPv4Address ".
+       $query = "select ip, name, reserved from IPv4Address ".
                "where ${whereexpr1} and (reserved = 'yes' or name != '')";
        $result = usePreparedSelectBlade ($query, $qparams);
        while ($row = $result->fetch (PDO::FETCH_ASSOC))
        {
-               $ip_bin = ip2long ($row['ip']);
+               $ip_bin = ip4_int2bin ($row['ip']);
                if (!isset ($ret[$ip_bin]))
-                       $ret[$ip_bin] = constructIPv4Address ($row['ip']);
+                       $ret[$ip_bin] = constructIPAddress ($ip_bin);
                $ret[$ip_bin]['name'] = $row['name'];
                $ret[$ip_bin]['reserved'] = $row['reserved'];
        }
@@ -1719,7 +1764,7 @@ function scanIPv4Space ($pairlist)
 
        // 2. check for allocations
        $query =
-               "select INET_NTOA(ip) as ip, object_id, name, type " .
+               "select ip, object_id, name, type " .
                "from IPv4Allocation where ${whereexpr2} order by type";
        $result = usePreparedSelectBlade ($query, $qparams);
        // release DBX early to avoid issues with nested spotEntity() calls
@@ -1727,9 +1772,9 @@ function scanIPv4Space ($pairlist)
        unset ($result);
        foreach ($allRows as $row)
        {
-               $ip_bin = ip2long ($row['ip']);
+               $ip_bin = ip4_int2bin ($row['ip']);
                if (!isset ($ret[$ip_bin]))
-                       $ret[$ip_bin] = constructIPv4Address ($row['ip']);
+                       $ret[$ip_bin] = constructIPAddress ($ip_bin);
                $oinfo = spotEntity ('object', $row['object_id']);
                $ret[$ip_bin]['allocs'][] = array
                (
@@ -1741,35 +1786,37 @@ function scanIPv4Space ($pairlist)
        }
 
        // 3. look for virtual services
-       $query = "select id, vip as ip_bin, inet_ntoa(vip) as ip from IPv4VS where ${whereexpr3}";
+       $query = "select id, vip from IPv4VS where ${whereexpr3}";
        $result = usePreparedSelectBlade ($query, $qparams);
        $allRows = $result->fetchAll (PDO::FETCH_ASSOC);
        unset ($result);
        foreach ($allRows as $row)
        {
-               if (!isset ($ret[$row['ip_bin']]))
-                       $ret[$row['ip_bin']] = constructIPv4Address ($row['ip']);
-               $ret[$row['ip_bin']]['vslist'][] = $row['id'];
+               $ip_bin = ip4_int2bin ($row['vip']);
+               if (!isset ($ret[$ip_bin]))
+                       $ret[$ip_bin] = constructIPAddress ($ip_bin);
+               $ret[$ip_bin]['vslist'][] = $row['id'];
        }
 
        // 4. don't forget about real servers along with pools
-       $query = "select rsip as ip_bin, inet_ntoa(rsip) as ip, rspool_id from IPv4RS where ${whereexpr4}";
+       $query = "select rsip, rspool_id from IPv4RS where ${whereexpr4}";
        $result = usePreparedSelectBlade ($query, $qparams);
        while ($row = $result->fetch (PDO::FETCH_ASSOC))
        {
-               if (!isset ($ret[$row['ip_bin']]))
-                       $ret[$row['ip_bin']] = constructIPv4Address ($row['ip']);
-               $ret[$row['ip_bin']]['rsplist'][] = $row['rspool_id'];
+               $ip_bin = ip4_int2bin ($row['rsip']);
+               if (!isset ($ret[$ip_bin]))
+                       $ret[$ip_bin] = constructIPAddress ($ip_bin);
+               $ret[$ip_bin]['rsplist'][] = $row['rspool_id'];
        }
        unset ($result);
 
-       // 5. add NAT rules, part 1
+       // 5. add NAT rules, remote ip
        $query =
                "select " .
                "proto, " .
-               "INET_NTOA(localip) as localip, " .
+               "localip, " .
                "localport, " .
-               "INET_NTOA(remoteip) as remoteip, " .
+               "remoteip, " .
                "remoteport, " .
                "description " .
                "from IPv4NAT " .
@@ -1778,19 +1825,19 @@ function scanIPv4Space ($pairlist)
        $result = usePreparedSelectBlade ($query, $qparams);
        while ($row = $result->fetch (PDO::FETCH_ASSOC))
        {
-               $remoteip_bin = ip2long ($row['remoteip']);
-               if (!isset ($ret[$remoteip_bin]))
-                       $ret[$remoteip_bin] = constructIPv4Address ($row['remoteip']);
-               $ret[$remoteip_bin]['inpf'][] = $row;
+               $ip_bin = ip4_int2bin ($row['remoteip']);
+               if (!isset ($ret[$ip_bin]))
+                       $ret[$ip_bin] = constructIPAddress ($ip_bin);
+               $ret[$ip_bin]['inpf'][] = $row;
        }
        unset ($result);
-       // 5. add NAT rules, part 2
+       // 5. add NAT rules, local ip
        $query =
                "select " .
                "proto, " .
-               "INET_NTOA(localip) as localip, " .
+               "localip, " .
                "localport, " .
-               "INET_NTOA(remoteip) as remoteip, " .
+               "remoteip, " .
                "remoteport, " .
                "description " .
                "from IPv4NAT " .
@@ -1799,24 +1846,25 @@ function scanIPv4Space ($pairlist)
        $result = usePreparedSelectBlade ($query, $qparams);
        while ($row = $result->fetch (PDO::FETCH_ASSOC))
        {
-               $localip_bin = ip2long ($row['localip']);
-               if (!isset ($ret[$localip_bin]))
-                       $ret[$localip_bin] = constructIPv4Address ($row['localip']);
-               $ret[$localip_bin]['outpf'][] = $row;
+               $ip_bin = ip4_int2bin ($row['localip']);
+               if (!isset ($ret[$ip_bin]))
+                       $ret[$ip_bin] = constructIPAddress ($ip_bin);
+               $ret[$ip_bin]['outpf'][] = $row;
        }
        unset ($result);
        // 6. collect last log message
-       $query = "select INET_NTOA(l.ip) AS ip, l.user, UNIX_TIMESTAMP(l.date) AS time from IPv4Log l INNER JOIN " .
+       $query = "select l.ip, l.user, UNIX_TIMESTAMP(l.date) AS time from IPv4Log l INNER JOIN " .
                " (SELECT MAX(id) as id FROM IPv4Log GROUP BY ip) v USING (id)";
        $result = usePreparedSelectBlade ($query, $qparams);
        while ($row = $result->fetch (PDO::FETCH_ASSOC))
        {
-               $ip_bin = ip2long ($row['ip']);
+               $ip_bin = ip4_int2bin ($row['ip']);
                if (isset ($ret[$ip_bin]))
-               {
-                       unset ($row['ip']);
-                       $ret[$ip_bin]['last_log'] = $row;
-               }
+                       $ret[$ip_bin]['last_log'] = array
+                       (
+                               'user' => $row['user'],
+                               'time' => $row['time'],
+                       );
        }
        unset ($result);
 
@@ -1825,8 +1873,8 @@ function scanIPv4Space ($pairlist)
 
 // Check the range requested for meaningful IPv6 records, build them
 // into a list and return. Return an empty list if nothing matched.
-// Both arguments are expected as instances of IPv6Address class. The resulting list
-// is keyed by uint32 form of each IP address, items aren't sorted.
+// Both arguments are expected as 16-byte binary IPs. The resulting list
+// is keyed by 16-byte bynary IPs, items aren't sorted.
 function scanIPv6Space ($pairlist)
 {
        $ret = array();
@@ -1834,8 +1882,8 @@ function scanIPv6Space ($pairlist)
        foreach ($pairlist as $pair)
        {
                $wheres[] = "ip >= ? AND ip <= ?";
-               $qparams[] = $pair['first']->getBin();
-               $qparams[] = $pair['last']->getBin();
+               $qparams[] = $pair['first'];
+               $qparams[] = $pair['last'];
        }
        if (! count ($wheres))  // this is normal for a network completely divided into smaller parts
                return $ret;
@@ -1847,12 +1895,11 @@ function scanIPv6Space ($pairlist)
        $result = usePreparedSelectBlade ($query, $qparams);
        while ($row = $result->fetch (PDO::FETCH_ASSOC))
        {
-               $ip_bin = new IPv6Address ($row['ip']);
-               $key = $ip_bin->getBin();
-               if (!isset ($ret[$key]))
-                       $ret[$key] = constructIPv6Address ($ip_bin);
-               $ret[$key]['name'] = $row['name'];
-               $ret[$key]['reserved'] = $row['reserved'];
+               $ip_bin = $row['ip'];
+               if (!isset ($ret[$ip_bin]))
+                       $ret[$ip_bin] = constructIPAddress ($ip_bin);
+               $ret[$ip_bin]['name'] = $row['name'];
+               $ret[$ip_bin]['reserved'] = $row['reserved'];
        }
        unset ($result);
 
@@ -1866,12 +1913,11 @@ function scanIPv6Space ($pairlist)
        unset ($result);
        foreach ($allRows as $row)
        {
-               $ip_bin = new IPv6Address ($row['ip']);
-               $key = $ip_bin->getBin();
-               if (!isset ($ret[$key]))
-                       $ret[$key] = constructIPv6Address ($ip_bin);
+               $ip_bin = $row['ip'];
+               if (!isset ($ret[$ip_bin]))
+                       $ret[$ip_bin] = constructIPAddress ($ip_bin);
                $oinfo = spotEntity ('object', $row['object_id']);
-               $ret[$key]['allocs'][] = array
+               $ret[$ip_bin]['allocs'][] = array
                (
                        'type' => $row['type'],
                        'name' => $row['name'],
@@ -1879,88 +1925,112 @@ function scanIPv6Space ($pairlist)
                        'object_name' => $oinfo['dname'],
                );
        }
+
+       // 3. collect last log message
+       $query = "select l.ip, l.user, UNIX_TIMESTAMP(l.date) AS time from IPv6Log l INNER JOIN " .
+               " (SELECT MAX(id) as id FROM IPv6Log GROUP BY ip) v USING (id)";
+       $result = usePreparedSelectBlade ($query, $qparams);
+       while ($row = $result->fetch (PDO::FETCH_ASSOC))
+       {
+               $ip_bin = $row['ip'];
+               if (isset ($ret[$ip_bin]))
+                       $ret[$ip_bin]['last_log'] = array
+                       (
+                               'user' => $row['user'],
+                               'time' => $row['time'],
+                       );
+       }
+       unset ($result);
        return $ret;
 }
 
-// this is a wrapper around getIPv4Address and getIPv6Address
-// You can pass dotted IPv4, human representation of IPv6, or instance of IPv6Address
-function getIPAddress ($ip)
+function bindIPToObject ($ip_bin, $object_id = 0, $name = '', $type = '')
 {
-       if (is_a ($ip, 'IPv6Address'))
-               return getIPv6Address ($ip);
-       $ipv6 = new IPv6Address;
-       if ($ipv6->parse ($ip))
-               return getIPv6Address ($ipv6);
-       return getIPv4Address ($ip);
+       switch (strlen ($ip_bin))
+       {
+               case 4:
+                       $db_ip = ip4_bin2db ($ip_bin);
+                       $table = 'IPv4Allocation';
+                       break;
+               case 16:
+                       $db_ip = $ip_bin;
+                       $table = 'IPv6Allocation';
+                       break;
+               default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       }
+       usePreparedInsertBlade
+       (
+               $table,
+               array ('ip' => $db_ip, 'object_id' => $object_id, 'name' => $name, 'type' => $type)
+       );
+       // store history line
+       $cell = spotEntity ('object', $object_id);
+       setDisplayedName ($cell);
+       addIPLogEntry ($ip_bin, "Binded with ${cell['dname']}, ifname=$name");
 }
 
-function getIPv4Address ($dottedquad = '')
+function bindIPv4ToObject ($ip_bin, $object_id = 0, $name = '', $type = '')
 {
-       if ($dottedquad == '')
-               throw new InvalidArgException ('$dottedquad', $dottedquad);
-       $i32 = ip2long ($dottedquad); // signed 32 bit
-       $scanres = scanIPv4Space (array (array ('i32_first' => $i32, 'i32_last' => $i32)));
-       if (empty ($scanres))
-               //$scanres[$i32] = constructIPv4Address ($dottedquad); // XXX: this should be verified to not break things
-               return constructIPv4Address ($dottedquad);
-       markupIPAddrList ($scanres);
-       return $scanres[$i32];
+       if (strlen ($ip_bin) != 4)
+               throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       return bindIPToObject ($ip_bin, $object_id, $name, $type);
 }
 
-// returns the array of structure described by constructIPv6Address
-function getIPv6Address ($v6addr)
+function bindIPv6ToObject ($ip_bin, $object_id = 0, $name = '', $type = '')
 {
-       if (! is_object ($v6addr))
-               throw new InvalidArgException ('$v6addr', $v6addr);
-       $scanres = scanIPv6Space (array (array ('first' => $v6addr, 'last' => $v6addr)));
-       if (empty ($scanres))
-               return constructIPv6Address ($v6addr);
-       markupIPAddrList ($scanres);
-       return array_shift ($scanres);
+       if (strlen ($ip_bin) != 16)
+               throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       return bindIPToObject ($ip_bin, $object_id, $name, $type);
 }
 
-function bindIpToObject ($ip = '', $object_id = 0, $name = '', $type = '')
+// Universal v4/v6 wrapper around getIPv4AddressNetworkId and getIPv6AddressNetworkId.
+// Return the id of the smallest IP network containing the given IP address
+// or NULL, if nothing was found. When finding the covering network for
+// another network, it is important to filter out matched records with longer
+// masks (they aren't going to be the right pick).
+function getIPAddressNetworkId ($ip_bin, $masklen = NULL)
 {
-       usePreparedExecuteBlade ('DELETE FROM IPv4Address WHERE ip = INET_ATON(?)', array ($ip));
-       usePreparedExecuteBlade
-       (
-               'INSERT INTO IPv4Allocation (ip, object_id, name, type) VALUES (INET_ATON(?), ?, ?, ?)',
-               array ($ip, $object_id, $name, $type)
-       );
-       // store history line
-       $cell = spotEntity ('object', $object_id);
-       setDisplayedName ($cell);
-       addIPv4LogEntry ($ip, "Binded with ${cell['dname']}, ifname=$name");
+       switch (strlen ($ip_bin))
+       {
+               case 4:  return getIPv4AddressNetworkId ($ip_bin, isset ($masklen) ? $masklen : 32);
+               case 16: return getIPv6AddressNetworkId ($ip_bin, isset ($masklen) ? $masklen : 128);
+               default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       }
 }
 
-function bindIPv6ToObject ($ip, $object_id = 0, $name = '', $type = '')
+// Returns ipv4net or ipv6net entity, or NULL if no spanning network found.
+// Throws an exception if $ip_bin is not valid binary address;
+function spotNetworkByIP ($ip_bin, $masklen = NULL)
 {
-       usePreparedExecuteBlade ('DELETE FROM IPv6Address WHERE ip = ?', array ($ip->getBin()));
-       usePreparedInsertBlade
-       (
-               'IPv6Allocation',
-               array ('ip' => $ip->getBin(), 'object_id' => $object_id, 'name' => $name, 'type' => $type)
-       );
+       $net_id = getIPAddressNetworkId ($ip_bin, $masklen);
+       if (! $net_id)
+               return NULL;
+       switch (strlen ($ip_bin))
+       {
+               case 4:  return spotEntity ('ipv4net', $net_id);
+               case 16: return spotEntity ('ipv6net', $net_id);
+               default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       }
 }
 
 // Return the id of the smallest IPv4 network containing the given IPv4 address
 // or NULL, if nothing was found. When finding the covering network for
 // another network, it is important to filter out matched records with longer
 // masks (they aren't going to be the right pick).
-function getIPv4AddressNetworkId ($dottedquad, $masklen = 32)
+function getIPv4AddressNetworkId ($ip_bin, $masklen = 32)
 {
-       if ($row = fetchIPv4AddressNetworkRow ($dottedquad, $masklen))
+       if ($row = fetchIPv4AddressNetworkRow ($ip_bin, $masklen))
                return $row['id'];
        return NULL;
 }
 
-function fetchIPv4AddressNetworkRow ($dottedquad, $masklen = 32)
+function fetchIPv4AddressNetworkRow ($ip_bin, $masklen = 32)
 {
        $query = 'select * from IPv4Network where ' .
-               "inet_aton(?) & (4294967295 >> (32 - mask)) << (32 - mask) = ip " .
+               "? & (4294967295 >> (32 - mask)) << (32 - mask) = ip " .
                "and mask < ? " .
                'order by mask desc limit 1';
-       $result = usePreparedSelectBlade ($query, array ($dottedquad, $masklen));
+       $result = usePreparedSelectBlade ($query, array (ip4_bin2db ($ip_bin), $masklen));
        if ($row = $result->fetch (PDO::FETCH_ASSOC))
                return $row;
        return NULL;
@@ -1968,41 +2038,42 @@ function fetchIPv4AddressNetworkRow ($dottedquad, $masklen = 32)
 
 // Return the id of the smallest IPv6 network containing the given IPv6 address
 // ($ip is an instance of IPv4Address class) or NULL, if nothing was found.
-function getIPv6AddressNetworkId ($ip, $masklen = 128)
+function getIPv6AddressNetworkId ($ip_bin, $masklen = 128)
 {
-       if ($row = fetchIPv6AddressNetworkRow ($ip, $masklen))
+       if ($row = fetchIPv6AddressNetworkRow ($ip_bin, $masklen))
                return $row['id'];
        return NULL;
 }
 
-function fetchIPv6AddressNetworkRow ($ip, $masklen = 128)
+function fetchIPv6AddressNetworkRow ($ip_bin, $masklen = 128)
 {
        $query = 'select * from IPv6Network where ip <= ? AND last_ip >= ? and mask < ? order by mask desc limit 1';
-       $result = usePreparedSelectBlade ($query, array ($ip->getBin(), $ip->getBin(), $masklen));
+       $result = usePreparedSelectBlade ($query, array ($ip_bin, $ip_bin, $masklen));
        if ($row = $result->fetch (PDO::FETCH_ASSOC))
                return $row;
        return NULL;
 }
 
-// It is a wrapper around updateV4Address and updateV6Address.
-// You can pass dotted IPv4, human representation of IPv6, or instance of IPv6Address
-function updateAddress ($ip = 0, $name = '', $reserved = 'no')
-{
-       if (is_a ($ip, 'IPv6Address'))
-               return updateV6Address ($ip, $name, $reserved);
-       $ipv6 = new IPv6Address;
-       if ($ipv6->parse ($ip))
-               return updateV6Address ($ipv6, $name, $reserved);
-       return updateV4Address ($ip, $name, $reserved);
-}
-
 // This function is actually used not only to update, but also to create records,
 // that's why ON DUPLICATE KEY UPDATE was replaced by DELETE-INSERT pair
 // (MySQL 4.0 workaround).
-function updateV4Address ($ip, $name = '', $reserved = 'no')
+function updateAddress ($ip_bin, $name = '', $reserved = 'no')
 {
+       switch (strlen ($ip_bin))
+       {
+               case 4:
+                       $table = 'IPv4Address';
+                       $db_ip = ip4_bin2db ($ip_bin);
+                       break;
+               case 16:
+                       $table = 'IPv6Address';
+                       $db_ip = $ip_bin;
+                       break;
+               default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       }
+
        // compute update log message
-       $result = usePreparedSelectBlade ('SELECT name FROM IPv4Address WHERE ip = INET_ATON(?)', array ($ip));
+       $result = usePreparedSelectBlade ("SELECT name FROM $table WHERE ip = ?", array ($db_ip));
        $old_name = '';
        if ($row = $result->fetch (PDO::FETCH_ASSOC))
                $old_name = $row['name'];
@@ -2017,43 +2088,63 @@ function updateV4Address ($ip, $name = '', $reserved = 'no')
                else
                        $message = "Reservation changed from '$old_name' to '$name'";
        }
-       
-       usePreparedExecuteBlade ('DELETE FROM IPv4Address WHERE ip = INET_ATON(?)', array ($ip));
+
+       usePreparedDeleteBlade ($table, array ('ip' => $db_ip));
        // INSERT may appear not necessary.
        if ($name != '' or $reserved != 'no')
-               usePreparedExecuteBlade
+               usePreparedInsertBlade
                (
-                       'INSERT INTO IPv4Address (name, reserved, ip) VALUES (?, ?, INET_ATON(?))',
-                       array ($name, $reserved, $ip)
+                       $table,
+                       array ('name' => $name, 'reserved' => $reserved, 'ip' => $db_ip)
                );
        // store history line
        if (isset ($message))
-               addIPv4LogEntry ($ip, $message);
+               addIPLogEntry ($ip_bin, $message);
 }
 
-function updateV6Address ($ip, $name = '', $reserved = 'no')
+function updateV4Address ($ip_bin, $name = '', $reserved = 'no')
 {
-       usePreparedDeleteBlade ('IPv6Address', array ('ip' => $ip->getBin()));
-       // INSERT may appear not necessary.
-       if ($name == '' and $reserved == 'no')
-               return;
-       usePreparedInsertBlade
-       (
-               'IPv6Address',
-               array ('name' => $name, 'reserved' => $reserved, 'ip' => $ip->getBin())
-       );
+       if (strlen ($ip_bin) != 4)
+               throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       return updateAddress ($ip_bin, $name, $reserved);
 }
 
-function updateBond ($ip='', $object_id=0, $name='', $type='')
+function updateV6Address ($ip_bin, $name = '', $reserved = 'no')
 {
-       usePreparedExecuteBlade
+       if (strlen ($ip_bin) != 16)
+               throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       return updateAddress ($ip_bin, $name, $reserved);
+}
+
+function updateIPBond ($ip_bin, $object_id=0, $name='', $type='')
+{
+       switch (strlen ($ip_bin))
+       {
+               case 4:  return updateIPv4Bond ($ip_bin, $object_id, $name, $type);
+               case 16: return updateIPv6Bond ($ip_bin, $object_id, $name, $type);
+               default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       }
+}
+
+function updateIPv4Bond ($ip_bin, $object_id=0, $name='', $type='')
+{
+       usePreparedUpdateBlade
        (
-               'UPDATE IPv4Allocation SET name=?, type=? WHERE ip=INET_ATON(?) AND object_id=?',
-               array ($name, $type, $ip, $object_id)
+               'IPv4Allocation',
+               array
+               (
+                       'name' => $name,
+                       'type' => $type,
+               ),
+               array
+               (
+                       'ip' => ip4_bin2db ($ip_bin),
+                       'object_id' => $object_id,
+               )
        );
 }
 
-function updateIPv6Bond ($ip, $object_id=0, $name='', $type='')
+function updateIPv6Bond ($ip_bin, $object_id=0, $name='', $type='')
 {
        usePreparedUpdateBlade
        (
@@ -2065,35 +2156,54 @@ function updateIPv6Bond ($ip, $object_id=0, $name='', $type='')
                ),
                array
                (
-                       'ip' => $ip->getBin(),
+                       'ip' => $ip_bin,
                        'object_id' => $object_id,
                )
        );
 }
 
-function unbindIpFromObject ($ip, $object_id)
+
+function unbindIPFromObject ($ip_bin, $object_id)
 {
-       $n_deleted = usePreparedExecuteBlade
+       switch (strlen ($ip_bin))
+       {
+               case 4:
+                       $table = 'IPv4Allocation';
+                       $db_ip = ip4_bin2db ($ip_bin);
+                       break;
+               case 16:
+                       $table = 'IPv6Allocation';
+                       $db_ip = $ip_bin;
+                       break;
+               default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       }
+
+       $n_deleted = usePreparedDeleteBlade
        (
-               'DELETE FROM IPv4Allocation WHERE ip=INET_ATON(?) AND object_id=?',
-               array ($ip, $object_id)
+               $table,
+               array ('ip' => $db_ip, 'object_id' => $object_id)
        );
        if ($n_deleted)
        {
                // store history line
                $cell = spotEntity ('object', $object_id);
                setDisplayedName ($cell);
-               addIPv4LogEntry ($ip, "Removed from ${cell['dname']}");
+               addIPLogEntry ($ip_bin, "Removed from ${cell['dname']}");
        }
 }
 
-function unbindIPv6FromObject ($ip, $object_id)
+function unbindIPv4FromObject ($ip_bin, $object_id)
 {
-       usePreparedDeleteBlade
-       (
-               'IPv6Allocation',
-               array ('ip' => $ip->getBin(), 'object_id' => $object_id)
-       );
+       if (strlen ($ip_bin) != 4)
+               throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       return unbindIPFromObject ($ip_bin, $object_id);
+}
+
+function unbindIPv6FromObject ($ip_bin, $object_id)
+{
+       if (strlen ($ip_bin) != 16)
+               throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       return unbindIPFromObject ($ip_bin, $object_id);
 }
 
 function getIPv4PrefixSearchResult ($terms)
@@ -2130,7 +2240,7 @@ function getIPv6PrefixSearchResult ($terms)
 
 function getIPv4AddressSearchResult ($terms)
 {
-       $query = "select inet_ntoa(ip) as ip, name from IPv4Address where ";
+       $query = "select ip, name from IPv4Address where ";
        $or = '';
        $qparams = array();
        foreach (explode (' ', $terms) as $term)
@@ -2142,7 +2252,11 @@ function getIPv4AddressSearchResult ($terms)
        $result = usePreparedSelectBlade ($query, $qparams);
        $ret = array();
        while ($row = $result->fetch (PDO::FETCH_ASSOC))
-               $ret[$row['ip']] = $row;
+       {
+               $ip_bin = ip4_int2bin ($row['ip']);
+               $row['ip'] = $ip_bin;
+               $ret[$ip_bin] = $row;
+       }
        return $ret;
 }
 
@@ -2578,6 +2692,8 @@ function searchByMgmtHostname ($string)
        return $rows[0][0];
 }
 
+// returns user_id
+// throws an exception if error occured
 function commitCreateUserAccount ($username, $realname, $password)
 {
        usePreparedInsertBlade
@@ -2590,6 +2706,7 @@ function commitCreateUserAccount ($username, $realname, $password)
                        'user_password_hash' => $password,
                )
        );
+       return lastInsertID();
 }
 
 function commitUpdateUserAccount ($id, $new_username, $new_realname, $new_password)
@@ -3266,33 +3383,28 @@ function generateEntityAutoTags ($cell)
                                        $ret[] = array ('tag' => "\$attr_{$attr_id}_{$attr_record['key']}");
                        break;
                case 'ipv4net':
-                       $ret[] = array ('tag' => '$ip4netid_' . $cell['id']);
+                       // v4-only rules
                        $ret[] = array ('tag' => '$ip4net-' . str_replace ('.', '-', $cell['ip']) . '-' . $cell['mask']);
+               case 'ipv6net':
+                       // common (v4 & v6) rules
+                       $ver = $cell['realm'] == 'ipv4net' ? 4 : 6;
+                       $ret[] = array ('tag' => "\$ip${ver}netid_" . $cell['id']);
+                       $ret[] = array ('tag' => "\$any_ip${ver}net");
+                       $ret[] = array ('tag' => '$any_net');
+
+                       $ret[] = array ('tag' => '$masklen_eq_' . $cell['mask']);
+
                        if ($cell['vlanc'])
                                $ret[] = array ('tag' => '$runs_8021Q');
-                       for ($i = 8; $i < 32; $i++)
-                       {
-                               # these conditions hit 1 to 3 times per each i
-                               if ($cell['mask'] >= $i)
-                                       $ret[] = array ('tag' => '$masklen_ge_' . $i);
-                               if ($cell['mask'] <= $i)
-                                       $ret[] = array ('tag' => '$masklen_le_' . $i);
-                               if ($cell['mask'] == $i)
-                                       $ret[] = array ('tag' => '$masklen_eq_' . $i);
-                       }
+
+                       foreach ($cell['8021q'] as $vlan_info)
+                               $ret[] = array ('tag' => '$vlan_' . $vlan_info['vlan_id']);
+
                        foreach (array_keys ($cell['spare_ranges']) as $mask)
-                               $ret[] = array ('tag' => '$hole_' . $mask);
-                       if ($cell['child_netc'] > 0)
+                               $ret[] = array ('tag' => '$spare_' . $mask);
+
+                       if ($cell['kidc'] > 0)
                                $ret[] = array ('tag' => '$aggregate');
-                       $ret[] = array ('tag' => '$any_ip4net');
-                       $ret[] = array ('tag' => '$any_net');
-                       break;
-               case 'ipv6net':
-                       $ret[] = array ('tag' => '$ip6netid_' . $cell['id']);
-                       if ($cell['vlanc'])
-                               $ret[] = array ('tag' => '$runs_8021Q');
-                       $ret[] = array ('tag' => '$any_ip6net');
-                       $ret[] = array ('tag' => '$any_net');
                        break;
                case 'ipv4vs':
                        $ret[] = array ('tag' => '$ipv4vsid_' . $cell['id']);
@@ -3459,12 +3571,10 @@ function rebuildTagChainForEntity ($realm, $entity_id, $extrachain = array(), $r
 }
 
 // Presume, that the target record has no tags attached.
-function produceTagsForLastRecord ($realm, $tagidlist, $last_insert_id = 0)
+function produceTagsForNewRecord ($realm, $tagidlist, $record_id)
 {
-       if (!$last_insert_id)
-               $last_insert_id = lastInsertID();
        foreach (getExplicitTagsOnly (buildTagChainFromIds ($tagidlist)) as $taginfo)
-               addTagForEntity ($realm, $last_insert_id, $taginfo['id']);
+               addTagForEntity ($realm, $record_id, $taginfo['id']);
 }
 
 function createIPv4Prefix ($range = '', $name = '', $is_connected = FALSE, $taglist = array(), $vlan_ck = NULL)
@@ -3475,62 +3585,34 @@ function createIPv4Prefix ($range = '', $name = '', $is_connected = FALSE, $tagl
                throw new InvalidRequestArgException ('range', $range, 'Invalid IPv4 prefix');
        $ip = $rangeArray[0];
        $mask = $rangeArray[1];
+       $forbidden_ranges = array
+       (
+               constructIPRange ("\0\0\0\0", 8), # 0.0.0.0/8
+               constructIPRange ("\xF0\0\0\0", 4), # 240.0.0.0/4
+       );
+       $net = constructIPRange (ip4_parse ($ip), $mask);
+       foreach ($forbidden_ranges as $invalid_net)
+               if (IPNetContainsOrEqual ($invalid_net, $net))
+                       throw new InvalidArgException ('range', $range, 'Reserved IPv4 network');
 
-       if (!strlen ($ip) or !strlen ($mask))
-               throw new InvalidRequestArgException ('range', $range, 'Invalid IPv4 prefix');
-       $ipL = ip2long($ip);
-       # deny 0.0.0.0/8 and 240.0.0.0/4
-       preg_match ('/^(\d+)\.\d+\.\d+\.\d+$/', long2ip ($ipL), $m);
-       if ($m[1] == 0 or $m[1] >= 240)
-               throw new InvalidRequestArgException ('range', $range, 'Reserved IPv4 network');
-
-       $maskL = ip2long($mask);
-       if ($ipL == -1 || $ipL === FALSE)
-               throw new InvalidRequestArgException ('range', $range, 'Invalid IPv4 address');
-       if ($mask < 32 && $mask > 0)
-               $maskL = $mask;
-       else
-       {
-               $maskB = decbin($maskL);
-               if (strlen($maskB)!=32)
-                       throw new InvalidRequestArgException ('range', $range, 'Invalid netmask');
-               $ones=0;
-               $zeroes=FALSE;
-               foreach( str_split ($maskB) as $digit)
-               {
-                       if ($digit == '0')
-                               $zeroes = TRUE;
-                       if ($digit == '1')
-                       {
-                               $ones++;
-                               if ($zeroes == TRUE)
-                                       throw new InvalidRequestArgException ('range', $range, 'Invalid netmask');
-                       }
-               }
-               $maskL = $ones;
-       }
-       $binmask = binMaskFromDec($maskL);
-       $ipL = $ipL & $binmask;
        usePreparedInsertBlade
        (
                'IPv4Network',
                array
                (
-                       'ip' => sprintf ('%u', $ipL),
-                       'mask' => $maskL,
+                       'ip' => ip4_bin2db ($net['ip_bin']),
+                       'mask' => $mask,
                        'name' => $name
                )
        );
        $network_id = lastInsertID();
 
-       if ($is_connected and $maskL < 31)
+       if ($is_connected and $mask < 31)
        {
-               $network_addr = long2ip ($ipL);
-               $broadcast_addr = long2ip ($ipL | binInvMaskFromDec ($maskL));
-               updateV4Address ($network_addr, 'network', 'yes');
-               updateV4Address ($broadcast_addr, 'broadcast', 'yes');
+               updateV4Address ($net['ip_bin'], 'network', 'yes');
+               updateV4Address (ip_last ($net), 'broadcast', 'yes');
        }
-       produceTagsForLastRecord ('ipv4net', $taglist);
+       produceTagsForNewRecord ('ipv4net', $taglist, $network_id);
        if ($vlan_ck != NULL)
        {
                $ctx = getContext();
@@ -3550,20 +3632,14 @@ function createIPv6Prefix ($range = '', $name = '', $is_connected = FALSE, $tagl
                throw new InvalidRequestArgException ('range', $range, 'Invalid IPv6 prefix');
        $ip = $rangeArray[0];
        $mask = $rangeArray[1];
-       $address = new IPv6Address;
-       if (!strlen ($ip) or !strlen ($mask) or ! $address->parse ($ip))
-               throw new InvalidRequestArgException ('range', $range, 'Invalid IPv6 prefix');
-       $network_addr = $address->get_first_subnet_address ($mask);
-       $broadcast_addr = $address->get_last_subnet_address ($mask);
-       if (! $network_addr || ! $broadcast_addr)
-               throw new InvalidRequestArgException ('range', $range, 'Invalid netmask');
+       $net = constructIPRange (ip6_parse ($ip), $mask);
        usePreparedInsertBlade
        (
                'IPv6Network',
                array
                (
-                       'ip' => $network_addr->getBin(),
-                       'last_ip' => $broadcast_addr->getBin(),
+                       'ip' => $net['ip_bin'],
+                       'last_ip' => ip_last ($net),
                        'mask' => $mask,
                        'name' => $name
                )
@@ -3571,8 +3647,8 @@ function createIPv6Prefix ($range = '', $name = '', $is_connected = FALSE, $tagl
        $network_id = lastInsertID();
        # RFC3513 2.6.1 - Subnet-Router anycast
        if ($is_connected)
-               updateV6Address ($network_addr, 'Subnet-Router anycast', 'yes');
-       produceTagsForLastRecord ('ipv6net', $taglist);
+               updateV6Address ($net['ip_bin'], 'Subnet-Router anycast', 'yes');
+       produceTagsForNewRecord ('ipv6net', $taglist, $network_id);
        if ($vlan_ck != NULL)
        {
                $ctx = getContext();
@@ -3622,51 +3698,65 @@ function saveScript ($name = '', $text)
        );
 }
 
-function newPortForwarding ($object_id, $localip, $localport, $remoteip, $remoteport, $proto, $description)
+function newPortForwarding ($object_id, $localip_bin, $localport, $remoteip_bin, $remoteport, $proto, $description)
 {
-       if (NULL === getIPv4AddressNetworkId ($localip))
-               throw new InvalidArgException ('localip', $localip, "Non existant ip");
-       if (NULL === getIPv4AddressNetworkId ($remoteip))
-               throw new InvalidArgException ('remoteip', $remoteip, "Non existant ip");
+       if (NULL === getIPv4AddressNetworkId ($localip_bin))
+               throw new InvalidArgException ('localip_bin', $localip_bin, "Non-existant ip");
+       if (NULL === getIPv4AddressNetworkId ($remoteip_bin))
+               throw new InvalidArgException ('remoteip_bin', $remoteip_bin, "Non-existant ip");
        if ( ($localport <= 0) or ($localport >= 65536) )
                throw new InvalidArgException ('localport', $localport, "invaild port");
        if ( ($remoteport <= 0) or ($remoteport >= 65536) )
                throw new InvalidArgException ('remoteport', $remoteport, "invaild port");
 
-       usePreparedExecuteBlade
+       return usePreparedInsertBlade
        (
-               'INSERT INTO IPv4NAT (object_id, localip, remoteip, localport, remoteport, proto, description) ' .
-               'VALUES (?, INET_ATON(?), INET_ATON(?), ?, ?, ?, ?)',
+               'IPv4NAT',
                array
                (
-                       $object_id,
-                       $localip,
-                       $remoteip,
-                       $localport,
-                       $remoteport,
-                       $proto,
-                       $description,
+                       'object_id' => $object_id,
+                       'localip' => ip4_bin2db ($localip_bin),
+                       'localport' => $localport,
+                       'remoteip' => ip4_bin2db ($remoteip_bin),
+                       'remoteport' => $remoteport,
+                       'proto' => $proto,
+                       'description' => $description,
                )
        );
 }
 
-function deletePortForwarding ($object_id, $localip, $localport, $remoteip, $remoteport, $proto)
+function deletePortForwarding ($object_id, $localip_bin, $localport, $remoteip_bin, $remoteport, $proto)
 {
-       usePreparedExecuteBlade
+       return usePreparedDeleteBlade
        (
-               'DELETE FROM IPv4NAT WHERE object_id=? AND localip=INET_ATON(?) AND ' .
-               'remoteip=INET_ATON(?) AND localport=? AND remoteport=? AND proto=?',
-               array ($object_id, $localip, $remoteip, $localport, $remoteport, $proto)
+               'IPv4NAT',
+               array
+               (
+                       'object_id' => $object_id,
+                       'localip' => ip4_bin2db ($localip_bin),
+                       'localport' => $localport,
+                       'remoteip' => ip4_bin2db ($remoteip_bin),
+                       'remoteport' => $remoteport,
+                       'proto' => $proto,
+               )
        );
 }
 
-function updatePortForwarding ($object_id, $localip, $localport, $remoteip, $remoteport, $proto, $description)
+function updatePortForwarding ($object_id, $localip_bin, $localport, $remoteip_bin, $remoteport, $proto, $description)
 {
-       usePreparedExecuteBlade
+       return usePreparedUpdateBlade
        (
-               'UPDATE IPv4NAT SET description=? WHERE object_id=? AND localip=INET_ATON(?) AND remoteip=INET_ATON(?) ' .
-               'AND localport=? AND remoteport=? AND proto=?',
-               array ($description, $object_id, $localip, $remoteip, $localport, $remoteport, $proto)
+               'IPv4NAT',
+               array ('description' => $description),
+               array
+               (
+                       'object_id' => $object_id,
+                       'localip' => ip4_bin2db ($localip_bin),
+                       'localport' => $localport,
+                       'remoteip' => ip4_bin2db ($remoteip_bin),
+                       'remoteport' => $remoteport,
+                       'proto' => $proto,
+               )
        );
 }
 
@@ -3910,6 +4000,8 @@ function getFileStats ()
        return $ret;
 }
 
+// returns file id
+// throws an exception if error occured
 function commitAddFile ($name, $type, $contents, $comment)
 {
        global $dbxlink;
@@ -3928,7 +4020,9 @@ function commitAddFile ($name, $type, $contents, $comment)
                $query->bindParam (3, $contents, PDO::PARAM_LOB);
                $query->bindParam (4, $comment);
                $query->execute();
-               usePreparedExecuteBlade ('UPDATE File SET size = LENGTH(contents) WHERE id = ?', array (lastInsertID()));
+               $file_id = lastInsertID();
+               usePreparedExecuteBlade ('UPDATE File SET size = LENGTH(contents) WHERE id = ?', array ($file_id));
+               return $file_id;
        }
        catch (PDOException $e)
        {
index c4280c4..ab0ede7 100644 (file)
@@ -85,16 +85,8 @@ class InvalidArgException extends RackTablesError
 
 // this simplifies construction and helps in catching "soft"
 // errors (invalid input from the user)
-class InvalidRequestArgException extends RackTablesError
+class InvalidRequestArgException extends InvalidArgException
 {
-       function __construct ($name, $value, $reason=NULL)
-       {
-               $message = "Argument '${name}' of value " . var_export ($value, TRUE) . " is invalid";
-               if (!is_null($reason))
-                       $message .= ' ('.$reason.')';
-               $message .= '.';
-               parent::__construct ($message);
-       }
        public function dispatch()
        {
                RackTablesError::genHTMLPage ('Assertion failed', '<h2>Assertion failed</h2><br>' . $this->message);
index 0fa7b97..21856fa 100644 (file)
@@ -81,115 +81,6 @@ $location_obj_types = array
        1562
 );
 
-$netmaskbylen = array
-(
-       32 => '255.255.255.255',
-       31 => '255.255.255.254',
-       30 => '255.255.255.252',
-       29 => '255.255.255.248',
-       28 => '255.255.255.240',
-       27 => '255.255.255.224',
-       26 => '255.255.255.192',
-       25 => '255.255.255.128',
-       24 => '255.255.255.0',
-       23 => '255.255.254.0',
-       22 => '255.255.252.0',
-       21 => '255.255.248.0',
-       20 => '255.255.240.0',
-       19 => '255.255.224.0',
-       18 => '255.255.192.0',
-       17 => '255.255.128.0',
-       16 => '255.255.0.0',
-       15 => '255.254.0.0',
-       14 => '255.252.0.0',
-       13 => '255.248.0.0',
-       12 => '255.240.0.0',
-       11 => '255.224.0.0',
-       10 => '255.192.0.0',
-       9 => '255.128.0.0',
-       8 => '255.0.0.0',
-       7 => '254.0.0.0',
-       6 => '252.0.0.0',
-       5 => '248.0.0.0',
-       4 => '240.0.0.0',
-       3 => '224.0.0.0',
-       2 => '192.0.0.0',
-       1 => '128.0.0.0'
-);
-
-$wildcardbylen = array
-(
-       32 => '0.0.0.0',
-       31 => '0.0.0.1',
-       30 => '0.0.0.3',
-       29 => '0.0.0.7',
-       28 => '0.0.0.15',
-       27 => '0.0.0.31',
-       26 => '0.0.0.63',
-       25 => '0.0.0.127',
-       24 => '0.0.0.255',
-       23 => '0.0.1.255',
-       22 => '0.0.3.255',
-       21 => '0.0.7.255',
-       20 => '0.0.15.255',
-       19 => '0.0.31.255',
-       18 => '0.0.63.255',
-       17 => '0.0.127.255',
-       16 => '0.0.255.25',
-       15 => '0.1.255.255',
-       14 => '0.3.255.255',
-       13 => '0.7.255.255',
-       12 => '0.15.255.255',
-       11 => '0.31.255.255',
-       10 => '0.63.255.255',
-       9 => '0.127.255.255',
-       8 => '0.255.255.255',
-       7 => '1.255.255.255',
-       6 => '3.255.255.255',
-       5 => '7.255.255.255',
-       4 => '15.255.255.255',
-       3 => '31.255.255.255',
-       2 => '63.255.255.255',
-       1 => '127.255.255.255'
-);
-
-$masklenByDQ = array
-(
-       '255.255.255.255' => 32,
-       '255.255.255.254' => 31,
-       '255.255.255.252' => 30,
-       '255.255.255.248' => 29,
-       '255.255.255.240' => 28,
-       '255.255.255.224' => 27,
-       '255.255.255.192' => 26,
-       '255.255.255.128' => 25,
-       '255.255.255.0' => 24,
-       '255.255.254.0' => 23,
-       '255.255.252.0' => 22,
-       '255.255.248.0' => 21,
-       '255.255.240.0' => 20,
-       '255.255.224.0' => 19,
-       '255.255.192.0' => 18,
-       '255.255.128.0' => 17,
-       '255.255.0.0' => 16,
-       '255.254.0.0' => 15,
-       '255.252.0.0' => 14,
-       '255.248.0.0' => 13,
-       '255.240.0.0' => 12,
-       '255.224.0.0' => 11,
-       '255.192.0.0' => 10,
-       '255.128.0.0' => 9,
-       '255.0.0.0' => 8,
-       '254.0.0.0' => 7,
-       '252.0.0.0' => 6,
-       '248.0.0.0' => 5,
-       '240.0.0.0' => 4,
-       '224.0.0.0' => 3,
-       '192.0.0.0' => 2,
-       '128.0.0.0' => 1,
-       '0.0.0.0' => 0,
-);
-
 // 802.1Q deploy queue titles
 $dqtitle = array
 (
@@ -298,42 +189,44 @@ function assertBoolArg ($argname, $ok_if_empty = FALSE)
                throw new InvalidRequestArgException($argname, $_REQUEST[$argname], 'parameter is an empty string');
 }
 
-// function returns IPv6Address object, null if arg is correct IPv4, or throws an exception
-function assertIPArg ($argname, $ok_if_empty = FALSE)
+// function returns binary IP address, or throws an exception
+function assertIPArg ($argname)
 {
-       assertStringArg ($argname, $ok_if_empty);
-       $ip = $_REQUEST[$argname];
-       if (FALSE !== strpos ($ip, ':'))
+       assertStringArg ($argname, FALSE);
+       try
        {
-               $v6address = new IPv6Address;
-               $result = $v6address->parse ($ip);
-               $ret = $v6address;
+               return ip_parse ($_REQUEST[$argname]);
        }
-       else
+       catch (InvalidArgException $e)
        {
-               $result = long2ip (ip2long ($ip)) === $ip;
-               $ret = NULL;
+               throw new InvalidRequestArgException ($argname, $_REQUEST[$argname], $e->getMessage());
        }
-       if (! $result)
-               throw new InvalidRequestArgException ($argname, $ip, 'parameter is not a valid IPv4 or IPv6 address');
-       return $ret;
 }
 
-function assertIPv4Arg ($argname, $ok_if_empty = FALSE)
+// function returns binary IPv4 address, or throws an exception
+function assertIPv4Arg ($argname)
 {
-       assertStringArg ($argname, $ok_if_empty);
-       if (strlen ($_REQUEST[$argname]) and long2ip (ip2long ($_REQUEST[$argname])) !== $_REQUEST[$argname])
-               throw new InvalidRequestArgException($argname, $_REQUEST[$argname], 'parameter is not a valid ipv4 address');
+       try
+       {
+               return ip4_parse ($_REQUEST[$argname]);
+       }
+       catch (InvalidArgException $e)
+       {
+               throw new InvalidRequestArgException ($argname, $_REQUEST[$argname], $e->getMessage());
+       }
 }
 
-// function returns IPv6Address object, or throws an exception
-function assertIPv6Arg ($argname, $ok_if_empty = FALSE)
+// function returns binary IPv6 address, or throws an exception
+function assertIPv6Arg ($argname)
 {
-       assertStringArg ($argname, $ok_if_empty);
-       $ipv6 = new IPv6Address;
-       if (strlen ($_REQUEST[$argname]) and ! $ok_if_empty and ! $ipv6->parse ($_REQUEST[$argname]))
-               throw new InvalidRequestArgException($argname, $_REQUEST[$argname], 'parameter is not a valid ipv6 address');
-       return $ipv6;
+       try
+       {
+               return ip6_parse ($_REQUEST[$argname]);
+       }
+       catch (InvalidArgException $e)
+       {
+               throw new InvalidRequestArgException ($argname, $_REQUEST[$argname], $e->getMessage());
+       }
 }
 
 function assertPCREArg ($argname)
@@ -372,6 +265,9 @@ function genericAssertion ($argname, $argtype)
        case 'uint0':
                assertUIntArg ($argname, TRUE);
                break;
+       case 'inet':
+               assertIPArg ($argname);
+               break;
        case 'inet4':
                assertIPv4Arg ($argname);
                break;
@@ -433,8 +329,7 @@ function genericAssertion ($argname, $argtype)
                if (!array_key_exists ($sic[$argname], $vs_proto))
                        throw new InvalidRequestArgException ($argname, $sic[$argname], 'Unknown value');
                break;
-       case 'enum/inet4alloc':
-       case 'enum/inet6alloc':
+       case 'enum/alloc_type':
                assertStringArg ($argname);
                if (!in_array ($sic[$argname], array ('regular', 'shared', 'virtual', 'router')))
                        throw new InvalidRequestArgException ($argname, $sic[$argname], 'Unknown value');
@@ -470,6 +365,18 @@ function genericAssertion ($argname, $argtype)
        }
 }
 
+// return HTML form checkbox value (TRUE of FALSE) by name of its input control
+function isCheckSet ($input_name, $mode = 'bool')
+{
+       $value = isset ($_REQUEST[$input_name]) && $_REQUEST[$input_name] == 'on';
+       switch ($mode)
+       {
+               case 'bool' : return $value;
+               case 'yesno': return $value ? 'yes' : 'no';
+               default: throw new InvalidArgException ('mode', $mode);
+       }
+}
+
 // Validate and return "bypass" value for the current context, if one is
 // defined for it, or NULL otherwise.
 function getBypassValue()
@@ -711,86 +618,206 @@ function mergeGridFormToRack (&$rackData)
                }
 }
 
-// netmask conversion from length to number
-function binMaskFromDec ($maskL)
-{
-       $map_straight = array (
-               0  => 0x00000000,
-               1  => 0x80000000,
-               2  => 0xc0000000,
-               3  => 0xe0000000,
-               4  => 0xf0000000,
-               5  => 0xf8000000,
-               6  => 0xfc000000,
-               7  => 0xfe000000,
-               8  => 0xff000000,
-               9  => 0xff800000,
-               10 => 0xffc00000,
-               11 => 0xffe00000,
-               12 => 0xfff00000,
-               13 => 0xfff80000,
-               14 => 0xfffc0000,
-               15 => 0xfffe0000,
-               16 => 0xffff0000,
-               17 => 0xffff8000,
-               18 => 0xffffc000,
-               19 => 0xffffe000,
-               20 => 0xfffff000,
-               21 => 0xfffff800,
-               22 => 0xfffffc00,
-               23 => 0xfffffe00,
-               24 => 0xffffff00,
-               25 => 0xffffff80,
-               26 => 0xffffffc0,
-               27 => 0xffffffe0,
-               28 => 0xfffffff0,
-               29 => 0xfffffff8,
-               30 => 0xfffffffc,
-               31 => 0xfffffffe,
-               32 => 0xffffffff,
+// wrapper around ip4_mask and ip6_mask
+// netmask conversion from length to binary string
+// v4/v6 mode is toggled by $is_ipv6 parameter
+// Throws exception if $prefix_len is invalid
+function ip_mask ($prefix_len, $is_ipv6)
+{
+       if ($is_ipv6)
+               return ip6_mask ($prefix_len);
+       else
+               return ip4_mask ($prefix_len);
+}
+
+// netmask conversion from length to binary string
+// Throws exception if $prefix_len is invalid
+function ip4_mask ($prefix_len)
+{
+       static $mask = array
+       (
+               "\x00\x00\x00\x00", // 0
+               "\x80\x00\x00\x00", // 1
+               "\xC0\x00\x00\x00", // 2
+               "\xE0\x00\x00\x00", // 3
+               "\xF0\x00\x00\x00", // 4
+               "\xF8\x00\x00\x00", // 5
+               "\xFC\x00\x00\x00", // 6
+               "\xFE\x00\x00\x00", // 7
+               "\xFF\x00\x00\x00", // 8
+               "\xFF\x80\x00\x00", // 9
+               "\xFF\xC0\x00\x00", // 10
+               "\xFF\xE0\x00\x00", // 11
+               "\xFF\xF0\x00\x00", // 12
+               "\xFF\xF8\x00\x00", // 13
+               "\xFF\xFC\x00\x00", // 14
+               "\xFF\xFE\x00\x00", // 15
+               "\xFF\xFF\x00\x00", // 16
+               "\xFF\xFF\x80\x00", // 17
+               "\xFF\xFF\xC0\x00", // 18
+               "\xFF\xFF\xE0\x00", // 19
+               "\xFF\xFF\xF0\x00", // 20
+               "\xFF\xFF\xF8\x00", // 21
+               "\xFF\xFF\xFC\x00", // 22
+               "\xFF\xFF\xFE\x00", // 23
+               "\xFF\xFF\xFF\x00", // 24
+               "\xFF\xFF\xFF\x80", // 25
+               "\xFF\xFF\xFF\xC0", // 26
+               "\xFF\xFF\xFF\xE0", // 27
+               "\xFF\xFF\xFF\xF0", // 28
+               "\xFF\xFF\xFF\xF8", // 29
+               "\xFF\xFF\xFF\xFC", // 30
+               "\xFF\xFF\xFF\xFE", // 31
+               "\xFF\xFF\xFF\xFF", // 32
        );
-       return $map_straight[$maskL];
-}
-
-// complementary value
-function binInvMaskFromDec ($maskL)
-{
-       $map_compl = array (
-               0  => 0xffffffff,
-               1  => 0x7fffffff,
-               2  => 0x3fffffff,
-               3  => 0x1fffffff,
-               4  => 0x0fffffff,
-               5  => 0x07ffffff,
-               6  => 0x03ffffff,
-               7  => 0x01ffffff,
-               8  => 0x00ffffff,
-               9  => 0x007fffff,
-               10 => 0x003fffff,
-               11 => 0x001fffff,
-               12 => 0x000fffff,
-               13 => 0x0007ffff,
-               14 => 0x0003ffff,
-               15 => 0x0001ffff,
-               16 => 0x0000ffff,
-               17 => 0x00007fff,
-               18 => 0x00003fff,
-               19 => 0x00001fff,
-               20 => 0x00000fff,
-               21 => 0x000007ff,
-               22 => 0x000003ff,
-               23 => 0x000001ff,
-               24 => 0x000000ff,
-               25 => 0x0000007f,
-               26 => 0x0000003f,
-               27 => 0x0000001f,
-               28 => 0x0000000f,
-               29 => 0x00000007,
-               30 => 0x00000003,
-               31 => 0x00000001,
-               32 => 0x00000000,
+
+       if ($prefix_len >= 0 and $prefix_len <= 32)
+               return $mask[$prefix_len];
+       else
+               throw new InvalidArgException ('prefix_len', $prefix_len);
+}
+
+// netmask conversion from length to binary string
+// Throws exception if $prefix_len is invalid
+function ip6_mask ($prefix_len)
+{
+       static $mask = array
+       (
+               "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 0
+               "\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 1
+               "\xC0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 2
+               "\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 3
+               "\xF0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 4
+               "\xF8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 5
+               "\xFC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 6
+               "\xFE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 7
+               "\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 8
+               "\xFF\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 9
+               "\xFF\xC0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 10
+               "\xFF\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 11
+               "\xFF\xF0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 12
+               "\xFF\xF8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 13
+               "\xFF\xFC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 14
+               "\xFF\xFE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 15
+               "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 16
+               "\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 17
+               "\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 18
+               "\xFF\xFF\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 19
+               "\xFF\xFF\xF0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 20
+               "\xFF\xFF\xF8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 21
+               "\xFF\xFF\xFC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 22
+               "\xFF\xFF\xFE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 23
+               "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 24
+               "\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 25
+               "\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 26
+               "\xFF\xFF\xFF\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 27
+               "\xFF\xFF\xFF\xF0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 28
+               "\xFF\xFF\xFF\xF8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 29
+               "\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 30
+               "\xFF\xFF\xFF\xFE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 31
+               "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 32
+               "\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 33
+               "\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 34
+               "\xFF\xFF\xFF\xFF\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 35
+               "\xFF\xFF\xFF\xFF\xF0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 36
+               "\xFF\xFF\xFF\xFF\xF8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 37
+               "\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 38
+               "\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 39
+               "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 40
+               "\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 41
+               "\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 42
+               "\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 43
+               "\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 44
+               "\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 45
+               "\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 46
+               "\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 47
+               "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 48
+               "\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 49
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 50
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 51
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 52
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 53
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 54
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 55
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 56
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00\x00", // 57
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00\x00\x00", // 58
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00\x00\x00\x00\x00\x00\x00", // 59
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00\x00\x00\x00\x00\x00\x00", // 60
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00\x00\x00\x00\x00\x00\x00", // 61
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00\x00\x00\x00", // 62
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00\x00\x00\x00\x00", // 63
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00", // 64
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00", // 65
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00\x00", // 66
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00\x00\x00\x00\x00\x00", // 67
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00\x00\x00\x00\x00\x00", // 68
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00\x00\x00\x00\x00\x00", // 69
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00\x00\x00", // 70
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00\x00\x00\x00", // 71
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00", // 72
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00", // 73
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00", // 74
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00\x00\x00\x00\x00", // 75
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00\x00\x00\x00\x00", // 76
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00\x00\x00\x00\x00", // 77
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00\x00", // 78
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00\x00\x00", // 79
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00", // 80
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00", // 81
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00", // 82
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00\x00\x00\x00", // 83
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00\x00\x00\x00", // 84
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00\x00\x00\x00", // 85
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00", // 86
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00\x00", // 87
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00", // 88
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00", // 89
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00", // 90
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00\x00\x00", // 91
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00\x00\x00", // 92
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00\x00\x00", // 93
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00", // 94
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00", // 95
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00", // 96
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00", // 97
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00", // 98
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00\x00", // 99
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00\x00", // 100
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00\x00", // 101
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00\x00", // 102
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00", // 103
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00", // 104
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00", // 105
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00", // 106
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00", // 107
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00", // 108
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00", // 109
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00", // 110
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00", // 111
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00", // 112
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00", // 113
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00", // 114
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00", // 115
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00", // 116
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00", // 117
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00", // 118
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00", // 119
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", // 120
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80", // 121
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0", // 122
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0", // 123
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0", // 124
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8", // 125
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC", // 126
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE", // 127
+               "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", // 128
        );
-       return $map_compl[$maskL];
+
+       if ($prefix_len >= 0 and $prefix_len <= 128)
+               return $mask[$prefix_len];
+       else
+               throw new InvalidArgException ('prefix_len', $prefix_len);
 }
 
 // This function looks up 'has_problems' flag for 'T' atoms
@@ -944,9 +971,10 @@ function findAllEndpoints ($object_id, $fallback = '')
                if ($record['id'] == 3 && strlen ($record['value'])) // FQDN
                        return array ($record['value']);
        $regular = array();
-       foreach (getObjectIPv4Allocations ($object_id) as $dottedquad => $alloc)
+       foreach (getObjectIPv4AllocationList ($object_id) as $ip_bin => $alloc)
                if ($alloc['type'] == 'regular')
-                       $regular[] = $dottedquad;
+                       $regular[] = ip4_format ($ip_bin);
+       // FIXME: add IPv6 allocations to this list
        if (!count ($regular) && strlen ($fallback))
                return array ($fallback);
        return $regular;
@@ -1575,11 +1603,11 @@ function getCellFilter ()
        $andor_used = FALSE;
        @session_start();
        // if the page is submitted we get an andor value so we know they are trying to start a new filter or clearing the existing one.
-       if(isset($_REQUEST['andor']))
-       {
+       if (isset($_REQUEST['andor']))
                $andor_used = TRUE;
+       if ($andor_used || array_key_exists ('clear-cf', $_REQUEST))
                unset($_SESSION[$pageno]); // delete saved filter
-       }
+
        // otherwise inject saved filter to the $_REQUEST and $sic vars
        elseif (isset ($_SESSION[$pageno]['filter']) and is_array ($_SESSION[$pageno]['filter']) and getConfigVar ('STATIC_FILTER') == 'yes')
                foreach (array('andor', 'cfe', 'cft[]', 'cfp[]', 'nft[]', 'nfp[]') as $param)
@@ -1814,7 +1842,7 @@ function findRouters ($addrlist)
                                        'id' => $alloc['object_id'],
                                        'iface' => $alloc['name'],
                                        'dname' => $alloc['object_name'],
-                                       'addr' => $addr['ip']
+                                       'ip_bin' => $addr['ip_bin'],
                                );
        return $ret;
 }
@@ -1830,44 +1858,25 @@ function taginfoCmp ($tagA, $tagB)
 // "The comparison function must return an integer less than, equal to, or greater
 // than zero if the first argument is considered to be respectively less than,
 // equal to, or greater than the second." (c) PHP manual
-function IPv4NetworkCmp ($netA, $netB)
-{
-       // On 64-bit systems this function can be reduced to just this:
-       if (PHP_INT_SIZE == 8)
-               return $netA['ip_bin'] - $netB['ip_bin'];
-       // There's a problem just substracting one u32 integer from another,
-       // because the result may happen big enough to become a negative i32
-       // integer itself (PHP tries to cast everything it sees to signed int)
-       // The comparison below must treat positive and negative values of both
-       // arguments.
-       // Equal values give instant decision regardless of their [equal] sign.
-       if ($netA['ip_bin'] == $netB['ip_bin'])
-               return 0;
-       // Same-signed values compete arithmetically within one of i32 contiguous ranges:
-       // 0x00000001~0x7fffffff 1~2147483647
-       // 0 doesn't have any sign, and network 0.0.0.0 isn't allowed
-       // 0x80000000~0xffffffff -2147483648~-1
-       $signA = $netA['ip_bin'] / abs ($netA['ip_bin']);
-       $signB = $netB['ip_bin'] / abs ($netB['ip_bin']);
-       if ($signA == $signB)
-       {
-               if ($netA['ip_bin'] > $netB['ip_bin'])
-                       return 1;
-               else
-                       return -1;
-       }
-       else // With only one of two values being negative, it... wins!
-       {
-               if ($netA['ip_bin'] < $netB['ip_bin'])
-                       return 1;
-               else
-                       return -1;
-       }
+function IPNetworkCmp ($netA, $netB)
+{
+       $ret = strcmp ($netA['ip_bin'], $netB['ip_bin']);
+       if ($ret == 0)
+               $ret = $netA['mask'] < $netB['mask'] ? -1 : ($netA['mask'] > $netB['mask'] ? 1 : 0);
+       if ($ret == -1 and $netA['ip_bin'] === ($netB['ip_bin'] & $netA['mask_bin']))
+               $ret = -2;
+       return $ret;
 }
 
-function IPv6NetworkCmp ($netA, $netB)
+function IPNetContainsOrEqual ($netA, $netB)
 {
-       return strcmp ($netA['ip_bin']->getBin(), $netB['ip_bin']->getBin());
+       $res = IPNetworkCmp ($netA, $netB);
+       return ($res == -2 || $res == 0);
+}
+
+function IPNetContains ($netA, $netB)
+{
+       return (-2 == IPNetworkCmp ($netA, $netB));
 }
 
 // Modify the given tag tree so, that each level's items are sorted alphabetically.
@@ -1888,42 +1897,21 @@ function iptree_fill (&$netdata)
        if (!isset ($netdata['kids']) or !count ($netdata['kids']))
                return;
        // If we really have nested prefixes, they must fit into the tree.
-       $worktree = array
-       (
-               'ip_bin' => $netdata['ip_bin'],
-               'mask' => $netdata['mask']
-       );
+       $worktree = $netdata;
        foreach ($netdata['kids'] as $pfx)
                iptree_embed ($worktree, $pfx);
        $netdata['kids'] = iptree_construct ($worktree);
        $netdata['kidc'] = count ($netdata['kids']);
 }
 
-function ipv6tree_fill (&$netdata)
-{
-       if (!isset ($netdata['kids']) or !count ($netdata['kids']))
-               return;
-       // If we really have nested prefixes, they must fit into the tree.
-       $worktree = array
-       (
-               'ip_bin' => $netdata['ip_bin'],
-               'mask' => $netdata['mask']
-       );
-       foreach ($netdata['kids'] as $pfx)
-               ipv6tree_embed ($worktree, $pfx);
-       $netdata['kids'] = ipv6tree_construct ($worktree);
-       $netdata['kidc'] = count ($netdata['kids']);
-}
-
 function iptree_construct ($node)
 {
        $self = __FUNCTION__;
 
        if (!isset ($node['right']))
        {
-               if (!isset ($node['ip']))
+               if (!isset ($node['kids']))
                {
-                       $node['ip'] = long2ip ($node['ip_bin']);
                        $node['kids'] = array();
                        $node['kidc'] = 0;
                        $node['name'] = '';
@@ -1934,68 +1922,322 @@ function iptree_construct ($node)
                return array_merge ($self ($node['left']), $self ($node['right']));
 }
 
-function ipv6tree_construct ($node)
+function ip_format ($ip_bin)
 {
-       $self = __FUNCTION__;
+       switch (strlen ($ip_bin))
+       {
+               case 4:  return ip4_format ($ip_bin);
+               case 16: return ip6_format ($ip_bin);
+               default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       }
+}
 
-       if (!isset ($node['right']))
+function ip4_format ($ip_bin)
+{
+       if (4 != ($len = strlen ($ip_bin)))
+               throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       return implode ('.', unpack ('C*', $ip_bin));
+}
+
+function ip6_format ($ip_bin)
+{
+       // maybe this is IPv6-to-IPv4 address?
+       if (substr ($ip_bin, 0, 12) == "\0\0\0\0\0\0\0\0\0\0\xff\xff")
+               return '::ffff:' . implode ('.', unpack ('C*', substr ($ip_bin, 12, 4)));
+
+       $result = array();
+       $hole_index = NULL;
+       $max_hole_index = NULL;
+       $hole_length = 0;
+       $max_hole_length = 0;
+
+       for ($i = 0; $i < 8; $i++)
        {
-               if (!isset ($node['ip']))
+               $unpacked = unpack ('n', substr ($ip_bin, $i * 2, 2));
+               $value = array_shift ($unpacked);
+               $result[] = dechex ($value & 0xffff);
+               if ($value != 0)
                {
-                       $node['ip'] = $node['ip_bin']->format();
-                       $node['kids'] = array();
-                       $node['kidc'] = 0;
-                       $node['name'] = '';
+                       unset ($hole_index);
+                       $hole_length = 0;
                }
-               return array ($node);
+               else
+               {
+                       if (! isset ($hole_index))
+                               $hole_index = $i;
+                       if (++$hole_length >= $max_hole_length)
+                       {
+                               $max_hole_index = $hole_index;
+                               $max_hole_length = $hole_length;
+                       }
+               }
+       }
+       if (isset ($max_hole_index))
+       {
+               array_splice ($result, $max_hole_index, $max_hole_length, array (''));
+               if ($max_hole_index == 0 && $max_hole_length == 8)
+                       return '::';
+               elseif ($max_hole_index == 0)
+                       return ':' . implode (':', $result);
+               elseif ($max_hole_index + $max_hole_length == 8)
+                       return implode (':', $result) . ':';
        }
+       return implode (':', $result);
+}
+
+function ip_parse ($ip)
+{
+       if (FALSE !== strpos ($ip, ':'))
+               return ip6_parse ($ip);
        else
-               return array_merge ($self ($node['left']), $self ($node['right']));
+               return ip4_parse ($ip);
 }
 
-function iptree_embed (&$node, $pfx)
+function ip4_parse ($ip)
 {
-       $self = __FUNCTION__;
+       if (FALSE === ($int = ip2long ($ip)))
+               throw new InvalidArgException ('ip', $ip, "Invalid IPv4 address");
+       else
+               return pack ('N', $int);
+}
 
-       // hit?
-       if ($node['ip_bin'] == $pfx['ip_bin'] and $node['mask'] == $pfx['mask'])
+// returns 16-byte string ip_bin
+// throws exception if unable to parse
+function ip6_parse ($ip)
+{
+       do {
+               if (empty ($ip))
+                       break;
+
+               $result = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; // 16 bytes
+               // remove one of double beginning/tailing colons
+               if (substr ($ip, 0, 2) == '::')
+                       $ip = substr ($ip, 1);
+               elseif (substr ($ip, -2, 2) == '::')
+                       $ip = substr ($ip, 0, strlen ($ip) - 1);
+
+               $tokens = explode (':', $ip);
+               $last_token = $tokens[count ($tokens) - 1];
+               $split = explode ('.', $last_token);
+               if (count ($split) == 4)
+               {
+                       $hex_tokens = array();
+                       $hex_tokens[] = dechex ($split[0] * 256 + $split[1]);
+                       $hex_tokens[] = dechex ($split[2] * 256 + $split[3]);
+                       array_splice ($tokens, -1, 1, $hex_tokens);
+               }
+               if (count ($tokens) > 8)
+                       break;
+               for ($i = 0; $i < count ($tokens); $i++)
+               {
+                       if ($tokens[$i] != '')
+                       {
+                               if (! set_word_value ($result, $i, $tokens[$i]))
+                                       break;
+                       }
+                       else
+                       {
+                               $k = 8; //index in result string (last word)
+                               for ($j = count ($tokens) - 1; $j > $i; $j--) // $j is an index in $tokens for reverse walk
+                                       if ($tokens[$j] == '')
+                                               break;
+                                       elseif (! set_word_value ($result, --$k, $tokens[$j]))
+                                               break;
+                               if ($i != $j)
+                                       break; //error, more than 1 '::' range
+                               break;
+                       }
+               }
+               if (! isset ($k) && count ($tokens) != 8)
+                       break;
+               return $result;
+       } while (FALSE);
+
+       throw new InvalidArgException ('ip', $ip, "Invalid IPv6 address");
+}
+
+function set_word_value (&$haystack, $nword, $hexvalue)
+{
+       // check that $hexvalue is like /^[0-9a-fA-F]*$/
+       for ($i = 0; $i < strlen ($hexvalue); $i++)
        {
-               $node = $pfx;
-               return;
+               $char = ord ($hexvalue[$i]);
+               if (! ($char >= 0x30 && $char <= 0x39 || $char >= 0x41 && $char <= 0x46 || $char >=0x61 && $char <= 0x66))
+                       return FALSE;
        }
-       if ($node['mask'] == $pfx['mask'])
-               throw new RackTablesError ('the recurring loop lost control', RackTablesError::INTERNAL);
+       $haystack = substr_replace ($haystack, pack ('n', hexdec ($hexvalue)), $nword * 2, 2);
+       return TRUE;
+}
 
-       // split?
-       if (!isset ($node['right']))
+// returns binary IP or FALSE
+function ip_checkparse ($ip)
+{
+       try
+       {
+               return ip_parse ($ip);
+       }
+       catch (InvalidArgException $e)
        {
-               // Fill in db_first/db_last to make it possible to run scanIPv4Space() on the node.
-               $node['left']['mask'] = $node['mask'] + 1;
-               $node['left']['ip_bin'] = $node['ip_bin'];
-               $node['left']['db_first'] = sprintf ('%u', $node['left']['ip_bin']);
-               $node['left']['db_last'] = sprintf ('%u', $node['left']['ip_bin'] | binInvMaskFromDec ($node['left']['mask']));
+               return FALSE;
+       }
+}
 
-               $node['right']['mask'] = $node['mask'] + 1;
-               $node['right']['ip_bin'] = $node['ip_bin'] + binInvMaskFromDec ($node['mask'] + 1) + 1;
-               $node['right']['db_first'] = sprintf ('%u', $node['right']['ip_bin']);
-               $node['right']['db_last'] = sprintf ('%u', $node['right']['ip_bin'] | binInvMaskFromDec ($node['right']['mask']));
+// returns binary IP or FALSE
+function ip4_checkparse ($ip)
+{
+       try
+       {
+               return ip4_parse ($ip);
+       }
+       catch (InvalidArgException $e)
+       {
+               return FALSE;
        }
+}
 
-       // repeat!
-       if (($node['left']['ip_bin'] & binMaskFromDec ($node['left']['mask'])) == ($pfx['ip_bin'] & binMaskFromDec ($node['left']['mask'])))
-               $self ($node['left'], $pfx);
-       elseif (($node['right']['ip_bin'] & binMaskFromDec ($node['right']['mask'])) == ($pfx['ip_bin'] & binMaskFromDec ($node['left']['mask'])))
-               $self ($node['right'], $pfx);
+// returns binary IP or FALSE
+function ip6_checkparse ($ip)
+{
+       try
+       {
+               return ip6_parse ($ip);
+       }
+       catch (InvalidArgException $e)
+       {
+               return FALSE;
+       }
+}
+
+function ip4_int2bin ($ip_int)
+{
+       return pack ('N', $ip_int);
+}
+
+function ip4_bin2int ($ip_bin)
+{
+       if (4 != strlen ($ip_bin))
+               throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       $list = unpack ('N', $ip_bin);
+       return array_shift ($list);
+}
+
+// Use this function only when you need to export binary ip out of PHP running context (e.g., DB)
+// !DO NOT perform arithmetic and bitwise operations with the result of this function!
+function ip4_bin2db ($ip_bin)
+{
+       $ip_int = ip4_bin2int ($ip_bin);
+       if ($ip_int < 0)
+               return sprintf ('%u', 0x00000000 + $ip_int);
        else
-               throw new RackTablesError ('cannot decide between left and right', RackTablesError::INTERNAL);
+               return $ip_int;
+}
+
+function ip_last ($net)
+{
+       return $net['ip_bin'] | ~$net['mask_bin'];
+}
+
+function ip_next ($ip_bin)
+{
+       $ret = $ip_bin;
+       $p = 1;
+       for ($i = strlen ($ret) - 1; $i >= 0; $i--)
+       {
+               $oct = $p + ord ($ret[$i]);
+               $ret[$i] = chr ($oct & 0xff);
+               if ($oct <= 255 and $oct >= 0)
+                       break;
+       }
+       return $ret;
+}
+
+function ip_prev ($ip_bin)
+{
+       $ret = $ip_bin;
+       $p = -1;
+       for ($i = strlen ($ret) - 1; $i >= 0; $i--)
+       {
+               $oct = $p + ord ($ret[$i]);
+               $ret[$i] = chr ($oct & 0xff);
+               if ($oct <= 255 and $oct >= 0)
+                       break;
+       }
+       return $ret;
+}
+
+function ip4_range_size ($range)
+{
+       return ip4_mask_size ($range['mask']);
+}
+
+function ip4_mask_size ($mask)
+{
+       return (0xffffffff >> $mask) + 1;
+}
+
+// returns array with keys 'ip', 'ip_bin', 'mask', 'mask_bin'
+function constructIPRange ($ip_bin, $mask)
+{
+       $node = array();
+       switch (strlen ($ip_bin))
+       {
+               case 4: // IPv4
+                       if ($mask < 0 || $mask > 32)
+                               throw new InvalidArgException ('mask', $mask, "Invalid v4 prefix length");
+                       $node['mask_bin'] = ip4_mask ($mask);
+                       $node['mask'] = $mask;
+                       $node['ip_bin'] = $ip_bin & $node['mask_bin'];
+                       $node['ip'] = ip4_format ($node['ip_bin']);
+                       break;
+               case 16: // IPv6
+                       if ($mask < 0 || $mask > 128)
+                               throw new InvalidArgException ('mask', $mask, "Invalid v6 prefix length");
+                       $node['mask_bin'] = ip6_mask ($mask);
+                       $node['mask'] = $mask;
+                       $node['ip_bin'] = $ip_bin & $node['mask_bin'];
+                       $node['ip'] = ip6_format ($node['ip_bin']);
+                       break;
+               default:
+                       throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       }
+       return $node;
+}
+
+// Return minimal IP address structure
+function constructIPAddress ($ip_bin)
+{
+       // common v4/v6 part
+       $ret = array
+       (
+               'ip' => ip_format ($ip_bin),
+               'ip_bin' => $ip_bin,
+               'name' => '',
+               'reserved' => 'no',
+               'allocs' => array(),
+       );
+
+       // specific v4 part
+       if (strlen ($ip_bin) == 4)
+               $ret = array_merge
+               (
+                       $ret,
+                       array
+                       (
+                               'outpf' => array(),
+                               'inpf' => array(),
+                               'vslist' => array(),
+                               'rsplist' => array(),
+                       )
+               );
+       return $ret;
 }
 
-function ipv6tree_embed (&$node, $pfx)
+function iptree_embed (&$node, $pfx)
 {
        $self = __FUNCTION__;
 
        // hit?
-       if ($node['ip_bin'] == $pfx['ip_bin'] and $node['mask'] == $pfx['mask'])
+       if (0 == IPNetworkCmp ($node, $pfx))
        {
                $node = $pfx;
                return;
@@ -2006,21 +2248,13 @@ function ipv6tree_embed (&$node, $pfx)
        // split?
        if (!isset ($node['right']))
        {
-               $node['left']['mask'] = $node['mask'] + 1;
-               $node['left']['ip_bin'] = $node['ip_bin'];
-               $node['left']['db_first'] = $node['ip_bin']->get_first_subnet_address ($node['mask'] + 1);
-               $node['left']['db_last'] = $node['ip_bin']->get_last_subnet_address ($node['mask'] + 1);
-
-               $node['right']['mask'] = $node['mask'] + 1;
-               $node['right']['ip_bin'] = $node['ip_bin']->get_last_subnet_address ($node['mask'] + 1)->next();
-               $node['right']['db_first'] = $node['right']['ip_bin'];
-               $node['right']['db_last'] = $node['right']['ip_bin']->get_last_subnet_address ($node['mask'] + 1);
+               $node['left']  = constructIPRange ($node['ip_bin'], $node['mask'] + 1);
+               $node['right'] = constructIPRange (ip_last ($node), $node['mask'] + 1);
        }
 
-       // repeat!
-       if ($node['left']['db_first'] == $pfx['ip_bin']->get_first_subnet_address ($node['left']['mask']))
+       if (IPNetContainsOrEqual ($node['left'], $pfx))
                $self ($node['left'], $pfx);
-       elseif ($node['right']['db_first'] == $pfx['ip_bin']->get_first_subnet_address ($node['left']['mask']))
+       elseif (IPNetContainsOrEqual ($node['right'], $pfx))
                $self ($node['right'], $pfx);
        else
                throw new RackTablesError ('cannot decide between left and right', RackTablesError::INTERNAL);
@@ -2040,104 +2274,104 @@ function treeApplyFunc (&$tree, $func = '', $stopfunc = '')
        }
 }
 
-function loadIPv4AddrList (&$netinfo)
+function nodeIsCollapsed ($node)
 {
-       loadOwnIPv4Addresses ($netinfo);
-       markupIPAddrList ($netinfo['addrlist']);
+       return $node['symbol'] == 'node-collapsed';
 }
 
-function countOwnIPv4Addresses (&$node)
+function loadIPAddrList (&$node)
 {
-       $node['addrt'] = 0;
-       if (empty ($node['kids']))
-               $node['addrt'] = binInvMaskFromDec ($node['mask']) + 1;
+       $node['addrlist'] = scanIPSpace (array (array ('first' => $node['ip_bin'], 'last' => ip_last ($node))));
+
+       if (! isset ($node['id']))
+               $node['own_addrlist'] = $node['addrlist'];
        else
-               foreach ($node['kids'] as $nested)
-                       if (!isset ($nested['id'])) // spare
-                               $node['addrt'] += binInvMaskFromDec ($nested['mask']) + 1;
+       {
+               $node['own_addrlist'] = array();
+               if ($node['kidc'] != 0)
+                       // node has childs
+                       foreach ($node['spare_ranges'] as $mask => $spare_list)
+                               foreach ($spare_list as $spare_ip)
+                               {
+                                       $spare_range = constructIPRange ($spare_ip, $mask);
+                                       foreach ($node['addrlist'] as $bin_ip => $addr)
+                                               if (($bin_ip & $spare_range['mask_bin']) == $spare_range['ip_bin'])
+                                                       $node['own_addrlist'][$bin_ip] = $addr;
+                               }
+       }
+       $node['addrc'] = count ($node['addrlist']);
+       $node['own_addrc'] = count ($node['own_addrlist']);
 }
 
-function nodeIsCollapsed ($node)
+function getIPv4OwnRangeSize ($range)
 {
-       return $node['symbol'] == 'node-collapsed';
+       if (! isset ($range['id']))
+               return ip4_range_size ($range);
+       $ret = 0;
+       if (isset ($range['spare_ranges']))
+               foreach ($range['spare_ranges'] as $mask => $spare_list)
+                       $ret += count ($spare_list) * ip4_mask_size ($mask);
+       return $ret;
 }
 
-// implies countOwnIPv4Addresses
-function loadOwnIPv4Addresses (&$node)
+// returns the array of structure described by constructIPAddress
+function getIPAddress ($ip_bin)
 {
-       $toscan = array();
-       $node['addrt'] = 0;
-       if (!isset ($node['kids']) or !count ($node['kids']))
-       {
-               $toscan[] = array ('i32_first' => $node['db_first'], 'i32_last' => $node['db_last']);
-               $node['addrt'] = $node['db_last'] - $node['db_first'] + 1;
-       }
-       else
-       {
-               $node['addrt'] = 0;
-               foreach ($node['kids'] as $nested)
-                       if (!isset ($nested['id'])) // spare
-                       {
-                               $toscan[] = array ('i32_first' => $nested['db_first'], 'i32_last' => $nested['db_last']);
-                               $node['addrt'] += $nested['db_last'] - $nested['db_first'] + 1;
-                       }
-       }
-       $node['addrlist'] = scanIPv4Space ($toscan);
-       $node['addrc'] = count ($node['addrlist']);
+       $scanres = scanIPSpace (array (array ('first' => $ip_bin, 'last' => $ip_bin)));
+       if (empty ($scanres))
+               return constructIPAddress ($ip_bin);
+       markupIPAddrList ($scanres);
+       return $scanres[$ip_bin];
 }
 
-function loadIPv6AddrList (&$netinfo)
+function getIPv4Address ($ip_bin)
 {
-       loadOwnIPv6Addresses ($netinfo);
-       markupIPAddrList ($netinfo['addrlist']);
+       if (strlen ($ip_bin) != 4)
+               throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       return getIPAddress ($ip_bin);
 }
 
-function loadOwnIPv6Addresses (&$node)
+function getIPv6Address ($ip_bin)
 {
-       $toscan = array();
-       $node['addrt'] = 0;
-       if (empty ($node['kids']))
-               $toscan[] = array ('first' => $node['ip_bin'], 'last' => $node['ip_bin']->get_last_subnet_address ($node['mask']));
-       else
-               foreach ($node['kids'] as $nested)
-                       if (!isset ($nested['id'])) // spare
-                               $toscan[] = array ('first' => $nested['ip_bin'], 'last' => $nested['ip_bin']->get_last_subnet_address ($nested['mask']));
-       $node['addrlist'] = scanIPv6Space ($toscan);
-       $node['addrc'] = count ($node['addrlist']);
+       if (strlen ($ip_bin) != 16)
+               throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
+       return getIPAddress ($ip_bin);
 }
 
-function prepareIPv4Tree ($netlist, $expanded_id = 0)
+function makeIPTree ($netlist)
 {
        // treeFromList() requires parent_id to be correct for an item to get onto the tree,
-       // so perform necessary pre-processing to make orphans belong to root. This trick
-       // was earlier performed by getIPv4NetworkList().
-       $netids = array_keys ($netlist);
-       foreach ($netids as $cid)
-               if (!in_array ($netlist[$cid]['parent_id'], $netids))
-                       $netlist[$cid]['parent_id'] = NULL;
+       // so perform necessary pre-processing to calculate parent_id of each item of $netlist.
+       $stack = array();
+       foreach ($netlist as $net_id => &$net)
+       {
+               while (! empty ($stack))
+               {
+                       $top_id = $stack[count ($stack) - 1];
+                       if (! IPNetContains ($netlist[$top_id], $net)) // unless $net is a child of stack top
+                               array_pop ($stack);
+                       else
+                       {
+                               $net['parent_id'] = $top_id;
+                               break;
+                       }
+               }
+               if (empty ($stack))
+                       $net['parent_id'] = NULL;
+               array_push ($stack, $net_id);
+       }
+       unset ($stack);
+
        $tree = treeFromList ($netlist); // medium call
-       sortTree ($tree, 'IPv4NetworkCmp');
-       // complement the tree before markup to make the spare networks have "symbol" set
-       treeApplyFunc ($tree, 'iptree_fill');
-       iptree_markup_collapsion ($tree, getConfigVar ('TREE_THRESHOLD'), $expanded_id);
-       // count addresses after the markup to skip computation for hidden tree nodes
-       treeApplyFunc ($tree, 'countOwnIPv4Addresses', 'nodeIsCollapsed');
+       sortTree ($tree, 'IPNetworkCmp');
        return $tree;
 }
 
-function prepareIPv6Tree ($netlist, $expanded_id = 0)
+function prepareIPTree ($netlist, $expanded_id = 0)
 {
-       // treeFromList() requires parent_id to be correct for an item to get onto the tree,
-       // so perform necessary pre-processing to make orphans belong to root. This trick
-       // was earlier performed by getIPv4NetworkList().
-       $netids = array_keys ($netlist);
-       foreach ($netids as $cid)
-               if (!in_array ($netlist[$cid]['parent_id'], $netids))
-                       $netlist[$cid]['parent_id'] = NULL;
-       $tree = treeFromList ($netlist); // medium call
-       sortTree ($tree, 'IPv6NetworkCmp');
+       $tree = makeIPTree ($netlist);
        // complement the tree before markup to make the spare networks have "symbol" set
-       treeApplyFunc ($tree, 'ipv6tree_fill');
+       treeApplyFunc ($tree, 'iptree_fill');
        iptree_markup_collapsion ($tree, getConfigVar ('TREE_THRESHOLD'), $expanded_id);
        return $tree;
 }
@@ -2167,11 +2401,12 @@ function iptree_markup_collapsion (&$tree, $threshold = 1024, $target = 0)
        {
                $here = ($target === 'ALL' or ($target > 0 and isset ($tree[$key]['id']) and $tree[$key]['id'] == $target));
                $below = $self ($tree[$key]['kids'], $threshold, $target);
+               $expand_enabled = ($target !== 'NONE');
                if (!$tree[$key]['kidc']) // terminal node
                        $tree[$key]['symbol'] = 'spacer';
-               elseif ($tree[$key]['kidc'] < $threshold)
+               elseif ($expand_enabled and $tree[$key]['kidc'] < $threshold)
                        $tree[$key]['symbol'] = 'node-expanded-static';
-               elseif ($here or $below)
+               elseif ($expand_enabled and ($here or $below))
                        $tree[$key]['symbol'] = 'node-expanded';
                else
                        $tree[$key]['symbol'] = 'node-collapsed';
@@ -2277,16 +2512,6 @@ function convertToBytes ($value) {
        return $value;
 }
 
-function ip_quad2long ($ip)
-{
-      return sprintf("%u", ip2long($ip));
-}
-
-function ip_long2quad ($quad)
-{
-      return long2ip($quad);
-}
-
 // make "A" HTML element
 function mkA ($text, $nextpage, $bypass = NULL, $nexttab = NULL)
 {
@@ -3344,11 +3569,8 @@ function getEmployedVlans ($object_id, $domain_vlanlist)
        foreach (array ('ipv4', 'ipv6') as $family)
        {
                $seen_nets = array();
-               foreach ($cell[$family] as $ip => $allocation)
-               {
-                       if ($family == 'ipv6')
-                               $ip = new IPv6Address ($ip);
-                       if ($net_id = ($family == 'ipv6' ? getIPv6AddressNetworkId ($ip) : getIPv4AddressNetworkId ($ip)))
+               foreach ($cell[$family] as $ip_bin => $allocation)
+                       if ($net_id = getIPAddressNetworkId ($ip_bin))
                        {
                                if (! isset($seen_nets[$net_id]))
                                        $seen_nets[$net_id]     = 1;
@@ -3359,7 +3581,6 @@ function getEmployedVlans ($object_id, $domain_vlanlist)
                                        if (! isset ($employed[$vlan['vlan_id']]))
                                                $employed[$vlan['vlan_id']] = 1;
                        }
-               }
        }
        return array_keys ($employed);
 }
@@ -4253,32 +4474,31 @@ function questionMarks ($count = 0)
 function searchEntitiesByText ($terms)
 {
        $summary = array();
-       $ipv6 = new IPv6Address;
 
        if (preg_match (RE_IP4_ADDR, $terms))
        // Search for IPv4 address.
        {
-               if ($net_id = getIPv4AddressNetworkId ($terms))
+               if ($net_id = getIPv4AddressNetworkId (ip4_parse ($terms)))
                        $summary['ipv4addressbydq'][$terms] = array ('net_id' => $net_id, 'ip' => $terms);
                
        }
-       elseif ($ipv6->parse ($terms))
+       elseif (FALSE !== ($ip_bin = ip6_checkparse ($terms)))
        // Search for IPv6 address
        {
-               if ($net_id = getIPv6AddressNetworkId ($ipv6))
-                       $summary['ipv6addressbydq'][$net_id] = array ('net_id' => $net_id, 'ip' => $ipv6);
+               if ($net_id = getIPv6AddressNetworkId ($ip_bin))
+                       $summary['ipv6addressbydq'][$net_id] = array ('net_id' => $net_id, 'ip' => $ip_bin);
        }
        elseif (preg_match (RE_IP4_NET, $terms))
        // Search for IPv4 network
        {
                list ($base, $len) = explode ('/', $terms);
-               if (NULL !== ($net_id = getIPv4AddressNetworkId ($base, $len + 1)))
+               if (NULL !== ($net_id = getIPv4AddressNetworkId (ip4_parse ($base), $len + 1)))
                        $summary['ipv4network'][$net_id] = spotEntity('ipv4net', $net_id);
        }
-       elseif (preg_match ('@(.*)/(\d+)$@', $terms, $matches) && $ipv6->parse ($matches[1]))
+       elseif (preg_match ('@(.*)/(\d+)$@', $terms, $matches) && FALSE !== ($ip_bin = ip6_checkparse ($matches[1])))
        // Search for IPv6 network
        {
-               if (NULL !== ($net_id = getIPv6AddressNetworkId ($ipv6, $matches[2] + 1)))
+               if (NULL !== ($net_id = getIPv6AddressNetworkId ($ip_bin, $matches[2] + 1)))
                        $summary['ipv6network'][$net_id] = spotEntity('ipv6net', $net_id);
        }
        elseif ($found_id = searchByMgmtHostname ($terms))
@@ -4888,8 +5108,8 @@ function apply8021qChangeRequest ($switch_id, $changes, $verbose = TRUE, $mutex_
 }
 
 // takes a full sublist of ipv4net entities ordered by (ip,mask)
-// fills ['spare_ranges'] and ['child_netc'] fields of each item of $nets.
-function fillIPv4NetsCorrelation (&$nets)
+// fills ['spare_ranges'] and ['kidc'] fields of each item of $nets.
+function fillIPNetsCorrelation (&$nets)
 {
        $stack = array();
        foreach ($nets as &$net)
@@ -4898,15 +5118,14 @@ function fillIPv4NetsCorrelation (&$nets)
                while (count ($stack)) // spin stack leaving only the parents of the $net.
                {
                        $top = &$stack[count ($stack) - 1];
-                       if ($top['db_first'] <= $net['db_first'] && $top['db_last'] >= $net['db_last'])
+                       if (IPNetContains ($top, $net))
                        {
-                               // $net is nested into $top
-                               $top['child_netc']++;
+                               $top['kidc']++;
                                break;
                        }
                        if (isset ($last))
                                // possible hole in the end of $top
-                               fillIPv4SpareList ($top, $last['db_last'] + 1, $top['db_last']);
+                               fillIPSpareListBstr ($top, ip_next (ip_last ($last)), ip_last ($top));
                        $last = array_pop ($stack);
                }
                if (count ($stack))
@@ -4914,10 +5133,10 @@ function fillIPv4NetsCorrelation (&$nets)
                        $top = &$stack[count ($stack) - 1];
                        if (isset ($last))
                                // possible hole in the middle of $top
-                               fillIPv4SpareList ($top, $last['db_last'] + 1, $net['db_first'] - 1);
+                               fillIPSpareListBstr ($top, ip_next (ip_last ($last)), ip_prev ($net['ip_bin']));
                        else
                                // possible hole in the beginning of $top
-                               fillIPv4SpareList ($top, $top['db_first'], $net['db_first'] - 1);
+                               fillIPSpareListBstr ($top, $top['ip_bin'], ip_prev ($net['ip_bin']));
                }
                $stack[] = &$net;
        }
@@ -4927,36 +5146,46 @@ function fillIPv4NetsCorrelation (&$nets)
                $top = &$stack[count ($stack) - 1];
                if (isset ($last))
                        // possible hole in the end of $top
-                       fillIPv4SpareList ($top, $last['db_last'] + 1, $top['db_last']);
+                       fillIPSpareListBstr ($top, ip_next (ip_last ($last)), ip_last ($top));
                $last = array_pop ($stack);
        }
 }
 
-// $a, $b - long integers (IPv4 addresses) meaning the beginning and the end of IP range.
+// $a, $b - binary strings (IP addresses) meaning the beginning and the end of IP range.
 // $net is an entity for hole autotags to be set to.
-// returns non-zero integer (netmask) of spare network range, or 0
-function fillIPv4SpareList (&$net, $a, $b)
+function fillIPSpareListBstr (&$net, $a, $b)
 {
-       if ($a > $b)
-               return;
-       $b++;
-       while ($a < $b)
+       $len = strlen ($a) * 8;
+       while (0 >= strcmp ($a, $b))
        {
-               
-               $i = 0; // the number of binary 0s in the minor part of $a
-               $tmp = $a;
-               while ($tmp and ! ($tmp & 1))
+               $max_mask = 0; // the number of common binary bits in the major of $a and $b
+               $xor = $a ^ $b;
+               for ($i = 0; $i < strlen ($xor); $i++)
                {
-                       // while minor bit is zero
-                       $i++;
-                       $tmp >>= 1;
+                       $max_mask += 8;
+                       if ($xor[$i] != "\0")
+                       {
+                               $byte = ord ($xor[$i]);
+                               do
+                               {
+                                       $max_mask--;
+                                       $byte >>= 1;
+                               } while ($byte != 0);
+                               break;
+                       }
+               }
+
+               for ($mask = $max_mask; $mask <= $len; $mask++)
+               {
+                       $bmask = ip_mask ($mask, $len == 128);
+                       $last_a = $a | ~ $bmask;
+                       if ($a == ($a & $bmask) and 0 >= strcmp ($last_a, $b))
+                       {
+                               $net['spare_ranges'][$mask][] = $a;
+                               $a = ip_next ($last_a);
+                               break;
+                       }
                }
-               
-               while ($a + (1 << $i) > $b)
-                       $i--;
-               $mask = 32 - $i;
-               $net['spare_ranges'][$mask][] = $a;
-               $a += (1 << $i);
        }
 }
 
@@ -4968,15 +5197,10 @@ function isIPNetworkEmpty (&$netinfo)
        if (getConfigVar ('IPV4_JAYWALK') == 'yes')
                return TRUE;
        if (! isset ($netinfo['addrlist']))
-       {
-               if ($netinfo['realm'] == 'ipv4net')
-                       loadIPv4AddrList ($netinfo);
-               else
-                       loadIPv6AddrList ($netinfo);
-       }
+               loadIPAddrList ($netinfo);
        $pure_array = ($netinfo['realm'] == 'ipv4net') ?
-               array ($netinfo['db_first'] => 'network', $netinfo['db_last'] => 'broadcast') :
-               array ($netinfo['db_first']->getBin() => 'Subnet-Router anycast');
+               array ($netinfo['ip_bin'] => 'network', ip_last ($netinfo) => 'broadcast') : // v4
+               array ($netinfo['ip_bin'] => 'Subnet-Router anycast'); // v6
        $pure_auto = 0;
        foreach ($pure_array as $ip => $comment)
                if
@@ -4995,7 +5219,7 @@ function isIPNetworkEmpty (&$netinfo)
                        )
                )
                        $pure_auto++;
-       return (count ($netinfo['addrlist']) <= $pure_auto);
+       return ($netinfo['own_addrc'] <= $pure_auto);
 }
 
 ?>
index a244de5..02f4371 100644 (file)
@@ -23,7 +23,6 @@ require_once 'navigation.php';
 require_once 'triggers.php';
 require_once 'gateways.php';
 require_once 'remote.php';
-require_once 'IPv6.php';
 require_once 'caching.php';
 require_once 'slb.php';
 
index 7083e96..4c67323 100644 (file)
@@ -607,6 +607,16 @@ CREATE TABLE `IPv4Log` (
   KEY `ip-date` (`ip`,`date`)
 ) ENGINE=InnoDB;
 
+CREATE TABLE `IPv6Log` (
+  `id` int(10) NOT NULL AUTO_INCREMENT,
+  `ip` binary(16) NOT NULL,
+  `date` datetime NOT NULL,
+  `user` varchar(64) NOT NULL,
+  `message` text NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `ip-date` (`ip`,`date`)
+) ENGINE=InnoDB;
+
 CREATE TABLE `IPv4NAT` (
   `object_id` int(10) unsigned NOT NULL default '0',
   `proto` enum('TCP','UDP') NOT NULL default 'TCP',
index bb6516e..7f707d9 100644 (file)
@@ -570,6 +570,134 @@ function addCSS ($data, $inline = FALSE)
        }
 }
 
+function getRenderedIPNetCapacity ($range)
+{
+       switch (strlen ($range['ip_bin']))
+       {
+               case 4:  return getRenderedIPv4NetCapacity ($range);
+               case 16: return getRenderedIPv6NetCapacity ($range);
+               default: throw new InvalidArgException ('range["ip_bin"]', $range['ip_bin'], "Invalid binary IP");
+       }
+}
+
+function getRenderedIPv4NetCapacity ($range)
+{
+       $class = 'net-usage';
+       if (isset ($range['addrc']))
+       {
+               // full mode
+               $total = ip4_range_size ($range);
+               $a_total = $total - getIPv4OwnRangeSize ($range);
+               $b_used = $range['own_addrc'];
+               $a_used = $range['addrc'] - $b_used;
+
+               // generate link to progress bar image
+               $width = 100;
+               if ($total > 0)
+               {
+                       $px_a = round ($a_total / $total * $width);
+                       $px1 = round ($a_used / $total * $width);
+                       $px2 = $px_a - $px1;
+                       $px3 = round ($b_used / $total * $width);
+                       if ($px3 + $px1 + $px2 > $width)
+                               $px3 = $width - $px1 - $px2;
+               }
+               else
+                       $px1 = $px2 = $px3 = 0;
+
+               $b_total = $total - $a_total;
+               $title_items = array();
+               $title2_items = array();
+               if ($a_total)
+               {
+                       $title_items[] = "$a_used / $a_total";
+                       $title2_items[] = sprintf ("%d%% used", $a_used / $a_total * 100);
+               }
+               if ($b_total)
+               {
+                       $title_items[] = ($b_used ? "$b_used / " : "") . $b_total;
+                       $title2_items[] = sprintf ("%d%% not allocated", $b_total / $total * 100);
+               }
+               $title = implode (', ', $title_items);
+               $title2 = implode (', ', $title2_items);
+               $text = "<img width='$width' height=10 border=0 title='$title2' src='?module=progressbar4&px1=$px1&px2=$px2&px3=$px3'>" .
+                       " <small class='title'>$title</small>";
+       }
+       else
+       {
+               // fast mode
+               $class .= ' pending';
+               addJS ('js/net-usage.js');
+
+               $free_text = '';
+               if (isset ($range['kidc']) and $range['kidc'] > 0)
+               {
+                       $free_masks = array_keys ($range['spare_ranges']);
+                       sort ($free_masks, SORT_NUMERIC);
+                       if ($mask = array_shift ($free_masks))
+                       {
+                               $cnt = count ($range['spare_ranges'][$mask]);
+                               $free_text = ', ' . ($cnt > 1 ? "<small>${cnt}x</small>" : "") . "/$mask free";
+                       }
+               }
+               $text =  ip4_range_size ($range) . $free_text;
+       }
+
+       $div_id = $range['ip'] . '/' . $range['mask'];
+
+       return "<div class=\"$class\" id=\"$div_id\">" . $text . "</div>";
+}
+
+function getRenderedIPv6NetCapacity ($range)
+{
+       $div_id = $range['ip'] . '/' . $range['mask'];
+       $class = 'net-usage';
+       if (isset ($range['addrc']))
+               $used = $range['addrc'];
+       else
+       {
+               $used = NULL;
+               $class .= ' pending';
+               addJS ('js/net-usage.js');
+       }
+
+       static $prefixes = array
+       (
+               0 =>  '',
+               3 =>  'k',
+               6 =>  'M',
+               9 =>  'G',
+               12 => 'T',
+               15 => 'P',
+               18 => 'E',
+               21 => 'Z',
+               24 => 'Y',
+       );
+
+       if ($range['mask'] <= 64)
+       {
+               $what = 'net';
+               $preposition = 'in';
+               $range['mask'] += 64;
+       }
+       else
+       {
+               $what = 'IP';
+               $preposition = 'of';
+       }
+       $what .= (0 == $range['mask'] % 64 ? '' : 's');
+       $addrc = isset ($used) ? "$used $preposition " : '';
+
+       $dec_order = intval ((128 - $range['mask']) / 10) * 3;
+       $mult = isset ($prefixes[$dec_order]) ? $prefixes[$dec_order] : '??';
+       
+       $cnt = 1 << ((128 - $range['mask']) % 10);
+       if ($cnt == 1 && $mult == '')
+               $cnt = '1';
+
+       return "<div class=\"$class\" id=\"$div_id\">" . "{$addrc}${cnt}${mult} ${what}" . "</div>";
+}
+
 // print part of HTML HEAD block
 function printPageHeaders ()
 {
@@ -733,4 +861,26 @@ function getOpLink ($params, $title,  $img_name = '', $comment = '', $class = ''
        return $ret;
 }
 
+function renderProgressBar ($percentage = 0, $theme = '', $inline = FALSE)
+{
+       echo getProgressBar ($percentage, $theme, $inline);
+}
+
+function getProgressBar ($percentage = 0, $theme = '', $inline = FALSE)
+{
+       $done = ((int) ($percentage * 100));
+       if (! $inline)
+               $src = "?module=progressbar&done=$done" . (empty ($theme) ? '' : "&theme=${theme}");
+       else
+       {
+               $bk_request = $_REQUEST;
+               $_REQUEST['theme'] = $theme;
+               $src = 'data:image/png;base64,' . chunk_split (base64_encode (getOutputOf ('renderProgressBarImage', $done)));
+               $_REQUEST = $bk_request;
+               header ('Content-type: text/html');
+       }
+       $ret = "<img width=100 height=10 border=0 title='${done}%' src='$src'>";
+       return $ret;
+}
+
 ?>
index 0bdb422..c38185c 100644 (file)
@@ -29,9 +29,9 @@ $aat = array
 $aac = array
 (
        'regular' => '',
-       'virtual' => '<strong>L</strong>',
-       'shared' => '<strong>S</strong>',
-       'router' => '<strong>R</strong>',
+       'virtual' => '<span class="aac">L</span>',
+       'shared' => '<span class="aac">S</span>',
+       'router' => '<span class="aac">R</span>',
 );
 // address allocation code, IPv4 networks view
 $aac2 = array
@@ -173,44 +173,18 @@ function getRenderedAlloc ($object_id, $alloc)
                'td_peers' => '',
        );
        $dottedquad = $alloc['addrinfo']['ip'];
+       $ip_bin = $alloc['addrinfo']['ip_bin'];
 
-       $hl_ip_addr = '';
-       if (isset ($_REQUEST['hl_ipv6_addr']))
+       $hl_ip_bin = NULL;
+       if (isset ($_REQUEST['hl_ip']))
        {
-               if ($hl_ipv6 = assertIPv6Arg ('hl_ipv6_addr'))
-                       $hl_ip_addr = $hl_ipv6->format();
-       }
-       elseif (isset ($_REQUEST['hl_ipv4_addr']))
-               $hl_ip_addr = $_REQUEST['hl_ipv4_addr'];
-       if ($hl_ip_addr)
-               addAutoScrollScript ("ip-$hl_ip_addr");
-
-       // prepare realm and network info
-       if ($alloc['addrinfo']['version'] == 6)
-       {
-               $ipv6_address = new IPv6Address();
-               $ipv6_address->parse ($dottedquad);
-               $addr_page_name = 'ipv6address';
-               if ($netid = getIPv6AddressNetworkId ($ipv6_address))
-               {
-                       $netinfo = spotEntity ('ipv6net', $netid);
-                       loadIPv6AddrList ($netinfo);
-               }
-       }
-       else
-       {
-               $addr_page_name = 'ipaddress';
-               if ($netid = getIPv4AddressNetworkId ($dottedquad))
-               {
-                       $netinfo = spotEntity ('ipv4net', $netid);
-                       loadIPv4AddrList ($netinfo);
-               }
+               $hl_ip_bin = ip_parse ($_REQUEST['hl_ip']);
+               addAutoScrollScript ("ip-" . $_REQUEST['hl_ip']);
        }
 
        $ret['tr_class'] = $alloc['addrinfo']['class'];
-       $td_class = 'tdleft';
-       if ($hl_ip_addr == $dottedquad)
-               $td_class .= ' port_highlight';
+       if ($hl_ip_bin === $ip_bin)
+               $ret['tr_class'] .= ' port_highlight';
        
        // render IP change history
        $ip_title = '';
@@ -229,28 +203,33 @@ function getRenderedAlloc ($object_id, $alloc)
 
        // render IP address td
        global $aac;
-       $ret['td_ip'] = "<td class='$td_class'>";
-       if (NULL !== $netid)
+       $netinfo = spotNetworkByIP ($ip_bin);
+       $ret['td_ip'] = "<td class='tdleft'>";
+       if (isset ($netinfo))
+       {
+               $title = $dottedquad;
+               if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
+                       $title .= '/' . $netinfo['mask'];
                $ret['td_ip'] .= "<a name='ip-$dottedquad' class='$ip_class' $ip_title href='" .
                        makeHref (
                                array
                                (
-                                       'page' => $addr_page_name,
+                                       'page' => 'ipaddress',
                                        'hl_object_id' => $object_id,
                                        'ip' => $dottedquad,
                                )
-                       ) . "'>" . $dottedquad . "</a>";
+                       ) . "'>$title</a>";
+       }
        else
                $ret['td_ip'] .= "<span class='$ip_class' $ip_title>$dottedquad</span>";
-       if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
-               $ret['td_ip'] .= '<small>/' . (NULL === $netid ? '??' : $netinfo['mask']) . '</small>';
-       $ret['td_ip'] .= '&nbsp;' . $aac[$alloc['type']];
+       $ret['td_ip'] .= $aac[$alloc['type']];
        if (strlen ($alloc['addrinfo']['name']))
                $ret['td_ip'] .= ' (' . niftyString ($alloc['addrinfo']['name']) . ')';
        $ret['td_ip'] .= '</td>';
 
        // render network and routed_by tds
-       if (NULL === $netid)
+       $td_class = 'tdleft';
+       if (! isset ($netinfo))
        {
                $ret['td_network'] = "<td class='$td_class sparenetwork'>N/A</td>";
                $ret['td_routed_by'] = $ret['td_network'];
@@ -261,8 +240,9 @@ function getRenderedAlloc ($object_id, $alloc)
                        getOutputOf ('renderCell', $netinfo) . '</td>';
 
                // filter out self-allocation
+               loadIPAddrList ($netinfo);
                $other_routers = array();
-               foreach (findRouters ($netinfo['addrlist']) as $router)
+               foreach (findRouters ($netinfo['own_addrlist']) as $router)
                        if ($router['id'] != $object_id)
                                $other_routers[] = $router;
                if (count ($other_routers))
@@ -1034,8 +1014,8 @@ function renderObject ($object_id)
                // group IP allocations by interface name instead of address family
                $allocs_by_iface = array();
                foreach (array ('ipv4', 'ipv6') as $ip_v)
-                       foreach ($info[$ip_v] as $dottedquad => $alloc)
-                               $allocs_by_iface[$alloc['osif']][$dottedquad] = $alloc;
+                       foreach ($info[$ip_v] as $ip_bin => $alloc)
+                               $allocs_by_iface[$alloc['osif']][$ip_bin] = $alloc;
                                
                // sort allocs array by portnames
                foreach (sortPortList ($allocs_by_iface) as $iface_name => $alloclist)
@@ -1093,7 +1073,7 @@ function renderObject ($object_id)
                                echo "<tr class='$class'>";
                                echo "<td>${pf['proto']}</td><td class=tdleft>${osif}<a href='".makeHref(array('page'=>'ipaddress', 'tab'=>'default', 'ip'=>$pf['localip']))."'>${pf['localip']}</a>:${pf['localport']}</td>";
                                echo "<td class=tdleft><a href='".makeHref(array('page'=>'ipaddress', 'tab'=>'default', 'ip'=>$pf['remoteip']))."'>${pf['remoteip']}</a>:${pf['remoteport']}</td>";
-                               $address = getIPv4Address ($pf['remoteip']);
+                               $address = getIPAddress (ip4_parse ($pf['remoteip']));
                                echo "<td class='description'>";
                                if (count ($address['allocs']))
                                        foreach($address['allocs'] as $bond)
@@ -1348,57 +1328,51 @@ function renderPortsForObject ($object_id)
        finishPortlet();
 }
 
-function renderIPTabForObject ($object_id, $ip_v)
+function renderIPForObject ($object_id)
 {
-       function getOpnameByIPFamily ($opname, $ip_v)
-       {
-               // do not assemble opnames from the peaces to be able to grep the code by opnames
-               switch ($opname . '-'. $ip_v)
-               {
-                       case 'add-4': return 'addIPv4Allocation';
-                       case 'add-6': return 'addIPv6Allocation';
-                       case 'upd-4': return 'updIPv4Allocation';
-                       case 'upd-6': return 'updIPv6Allocation';
-                       case 'del-4': return 'delIPv4Allocation';
-                       case 'del-6': return 'delIPv6Allocation';
-                       default: throw new InvalidArgException ('$opname or $ip_v', "$opname or $ip_v");
-               }
-       }
-       function printNewItemTR ($ip_v, $default_type)
+       function printNewItemTR ($default_type)
        {
                global $aat;
-               printOpFormIntro (getOpnameByIPFamily ('add', $ip_v));
-               echo "<tr><td>";
-               printImageHREF ('add', 'allocate', TRUE);
+               printOpFormIntro ('add');
+               echo "<tr><td>"; // left btn
+               printImageHREF ('add', 'allocate', TRUE); 
                echo "</td>";
-               echo "<td class=tdleft><input type='text' size='10' name='bond_name' tabindex=100></td>\n";
-               echo "<td class=tdleft><input type=text name='ip' tabindex=101></td>\n";
-               echo "<td colspan=2>&nbsp;</td><td>";
-               printSelect ($aat, array ('name' => 'bond_type', 'tabindex' => 102), $default_type);
-               echo "</td><td>&nbsp;</td><td>";
-               printImageHREF ('add', 'allocate', TRUE, 103);
+               echo "<td class=tdleft><input type='text' size='10' name='bond_name' tabindex=100></td>\n"; // if-name
+               echo "<td class=tdleft><input type=text name='ip' tabindex=101></td>\n"; // IP
+               if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
+                       echo "<td colspan=2>&nbsp;</td>"; // network, routed by
+               echo '<td>';
+               printSelect ($aat, array ('name' => 'bond_type', 'tabindex' => 102), $default_type); // type
+               echo "</td><td>&nbsp;</td><td>"; // misc
+               printImageHREF ('add', 'allocate', TRUE, 103); // right btn
                echo "</td></tr></form>";
        }
-       $focus = spotEntity ('object', $object_id);
-       amplifyCell ($focus);
        global $aat;
        startPortlet ('Allocations');
-       echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
-       echo '<tr><th>&nbsp;</th><th>OS interface</th><th>IP address</th>';
+       echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'><tr>\n";
+       echo '<th>&nbsp;</th>';
+       echo '<th>OS interface</th>';
+       echo '<th>IP address</th>';
        if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
-               echo '<th>network</th><th>routed by</th>';
-       echo '<th>type</th><th>misc</th><th>&nbsp</th></tr>';
+       {
+               echo '<th>network</th>';
+               echo '<th>routed by</th>';
+       }
+       echo '<th>type</th>';
+       echo '<th>misc</th>';
+       echo '<th>&nbsp</th>';
+       echo '</tr>';
 
        $alloc_list = ''; // most of the output is stored here
        $used_alloc_types = array();
-       foreach ($focus['ipv' . $ip_v] as $alloc) // ['ipv4'] or ['ipv6']
+       foreach (getObjectIPAllocations ($object_id) as $alloc)
        {
                if (! isset ($used_alloc_types[$alloc['type']]))
                        $used_alloc_types[$alloc['type']] = 0;
                $used_alloc_types[$alloc['type']]++;
 
                $rendered_alloc = callHook ('getRenderedAlloc', $object_id, $alloc);
-               $alloc_list .= getOutputOf ('printOpFormIntro', getOpnameByIPFamily ('upd', $ip_v), array ('ip' => $alloc['addrinfo']['ip']));
+               $alloc_list .= getOutputOf ('printOpFormIntro', 'upd', array ('ip' => $alloc['addrinfo']['ip']));
                $alloc_list .= "<tr class='${rendered_alloc['tr_class']}' valign=top>";
 
                $alloc_list .= "<td><a href='" .
@@ -1406,7 +1380,7 @@ function renderIPTabForObject ($object_id, $ip_v)
                        (
                                array
                                (
-                                       'op' => getOpnameByIPFamily ('del', $ip_v),
+                                       'op' => 'del',
                                        'ip' => $alloc['addrinfo']['ip'],
                                        'object_id' => $object_id
                                )
@@ -1431,7 +1405,7 @@ function renderIPTabForObject ($object_id, $ip_v)
 
        if ($list_on_top = (getConfigVar ('ADDNEW_AT_TOP') != 'yes'))
                echo $alloc_list;
-       printNewItemTR ($ip_v, $most_popular_type);
+       printNewItemTR ($most_popular_type);
        if (! $list_on_top)
                echo $alloc_list;
 
@@ -1439,16 +1413,6 @@ function renderIPTabForObject ($object_id, $ip_v)
        finishPortlet();
 }
 
-function renderIPv4ForObject ($object_id)
-{
-       renderIPTabForObject ($object_id, '4');
-}
-
-function renderIPv6ForObject ($object_id)
-{
-       renderIPTabForObject ($object_id, '6');
-}
-
 // This function is deprecated. Do not rely on its internals,
 // it will probably be removed in the next major relese.
 // Use new showError, showWarning, showSuccess functions.
@@ -1862,22 +1826,18 @@ function renderDepot ()
                        $mountinfo = getMountInfo ($idlist);
                        foreach ($objects as $obj)
                        {
-                               if (isset ($_REQUEST['hl_object_id']) and $_REQUEST['hl_object_id'] == $obj['id'])
-                                       $secondclass = 'tdleft port_highlight';
-                               else
-                                       $secondclass = 'tdleft';
-                               echo "<tr class=row_${order} valign=top><td class='${secondclass}'><a href='".makeHref(array('page'=>'object', 'object_id'=>$obj['id']))."'><strong>${obj['dname']}</strong></a>";
+                               echo "<tr class='row_${order} tdleft' valign=top><td><a href='".makeHref(array('page'=>'object', 'object_id'=>$obj['id']))."'><strong>${obj['dname']}</strong></a>";
                                if (count ($obj['etags']))
                                        echo '<br><small>' . serializeTags ($obj['etags'], makeHref(array('page'=>$pageno, 'tab'=>'default')) . '&') . '</small>';
-                               echo "</td><td class='${secondclass}'>${obj['label']}</td>";
-                               echo "<td class='${secondclass}'>${obj['asset_no']}</td>";
+                               echo "</td><td>${obj['label']}</td>";
+                               echo "<td>${obj['asset_no']}</td>";
                                $places = array();
                                if (! array_key_exists ($obj['id'], $mountinfo))
                                        $places[] = 'Unmounted';
                                else
                                        foreach ($mountinfo[$obj['id']] as $mi)
                                                $places[] = mkA ($mi['row_name'], 'row', $mi['row_id']) . '/' . mkA ($mi['rack_name'], 'rack', $mi['rack_id']);
-                               echo "<td class='${secondclass}'>" . implode (', ', $places) . '</td>';
+                               echo "<td>" . implode (', ', $places) . '</td>';
                                echo '</tr>';
                                $order = $nextorder[$order];
                        }
@@ -2010,12 +1970,10 @@ function renderRackspaceHistory ()
        echo '</td></tr></table>';
 }
 
-function renderIPv4SpaceRecords ($tree, $baseurl, $target = 0, $knight, $level = 1)
+function renderIPSpaceRecords ($tree, $baseurl, $target = 0, $level = 1)
 {
        $self = __FUNCTION__;
-       static $vdomlist = NULL;
-       if ($vdomlist == NULL and getConfigVar ('IPV4_TREE_SHOW_VLAN') == 'yes')
-               $vdomlist = getVLANDomainOptions();
+       $knight = (getConfigVar ('IPV4_ENABLE_KNIGHT') == 'yes');
 
        // scroll page to the highlighted item
        if ($target && isset ($_REQUEST['hl_net']))
@@ -2023,362 +1981,140 @@ function renderIPv4SpaceRecords ($tree, $baseurl, $target = 0, $knight, $level =
 
        foreach ($tree as $item)
        {
-               if (getConfigVar ('IPV4_TREE_SHOW_USAGE') == 'yes')
-                       loadIPv4AddrList ($item); // necessary to compute router list and address counter
-               else
-               {
-                       $item['addrlist'] = array();
-                       $item['addrc'] = 0;
-               }
-               $used = $item['addrc'];
-               $maxdirect = $item['addrt'];
-               $maxtotal = binInvMaskFromDec ($item['mask']) + 1;
+               if ($display_routers = (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none'))
+                       loadIPAddrList ($item); // necessary to compute router list and address counter
+
                if (isset ($item['id']))
                {
                        $decor = array ('indent' => $level);
                        if ($item['symbol'] == 'node-collapsed')
-                               $decor['symbolurl'] = "${baseurl}&eid=" . $item['id'];
+                               $decor['symbolurl'] = "${baseurl}&eid=${item['id']}&hl_net=1";
                        elseif ($item['symbol'] == 'node-expanded')
-                               $decor['symbolurl'] = $baseurl . ($item['parent_id'] ? "&eid=${item['parent_id']}" : '');
-                       echo "<tr valign=top>";
+                               $decor['symbolurl'] = $baseurl . ($item['parent_id'] ? "&eid=${item['parent_id']}&hl_net=1" : '');
+                       $tr_class = '';
                        if ($target == $item['id'] && isset ($_REQUEST['hl_net']))
-                               $decor['tdclass'] = 'port_highlight';
-                       printIPNetInfoTDs ($item, $decor);
-                       echo "<td class=tdcenter>";
-                       if (getConfigVar ('IPV4_TREE_SHOW_USAGE') == 'yes')
                        {
-                               renderProgressBar ($maxdirect ? $used/$maxdirect : 0);
-                               echo "<br><small>${used}/${maxdirect}" . ($maxdirect == $maxtotal ? '' : "/${maxtotal}") . '</small>';
+                               $decor['tdclass'] = ' port_highlight';
+                               $tr_class = $decor['tdclass'];
                        }
-                       else
-                               echo "<small>${maxdirect}</small>";
+                       echo "<tr valign=top class=\"$tr_class\">";
+                       printIPNetInfoTDs ($item, $decor);
+
+                       // capacity and usage
+                       echo "<td class=tdcenter>";
+                       echo getRenderedIPNetCapacity ($item);
                        echo "</td>";
-                       if (getConfigVar ('IPV4_TREE_SHOW_VLAN') == 'yes')
-                       {
-                               echo '<td class=tdleft>';
-                               if (count ($item['8021q']))
-                               {
-                                       echo '<ul>';
-                                       foreach ($item['8021q'] as $binding)
-                                       {
-                                               echo '<li><a href="' . makeHref (array ('page' => 'vlan', 'vlan_ck' => $binding['domain_id'] . '-' . $binding['vlan_id'])) . '">';
-                                               // FIXME: would formatVLANName() do this?
-                                               echo $binding['vlan_id'] . '@' . niftyString ($vdomlist[$binding['domain_id']], 15) . '</a></li>';
-                                       }
-                                       echo '</ul>';
-                               }
-                               echo '</td>';
-                       }
-                       if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
+
+                       if ($display_routers)
                                printRoutersTD (findRouters ($item['addrlist']), getConfigVar ('IPV4_TREE_RTR_AS_CELL'));
                        echo "</tr>";
                        if ($item['symbol'] == 'node-expanded' or $item['symbol'] == 'node-expanded-static')
-                               $self ($item['kids'], $baseurl, $target, $knight, $level + 1);
+                               $self ($item['kids'], $baseurl, $target, $level + 1);
                }
                else
                {
+                       // non-allocated (spare) IP range
                        echo "<tr valign=top>";
                        printIPNetInfoTDs ($item, array ('indent' => $level, 'knight' => $knight, 'tdclass' => 'sparenetwork'));
-                       echo "<td class=tdcenter>";
-                       if (getConfigVar ('IPV4_TREE_SHOW_USAGE') == 'yes')
-                       {
-                               renderProgressBar ($used/$maxtotal, 'sparenetwork');
-                               echo "<br><small>${used}/${maxtotal}</small>";
-                       }
-                       else
-                               echo "<small>${maxtotal}</small>";
-                       if (getConfigVar ('IPV4_TREE_SHOW_VLAN') == 'yes')
-                               echo '</td><td>&nbsp;</td>';
-                       echo "</td><td>&nbsp;</td></tr>";
-               }
-       }
-}
-
-function renderIPv6SpaceRecords ($tree, $baseurl, $target = 0, $knight, $level = 1)
-{
-       $self = __FUNCTION__;
-       static $vdomlist = NULL;
-       if ($vdomlist == NULL and getConfigVar ('IPV4_TREE_SHOW_VLAN') == 'yes')
-               $vdomlist = getVLANDomainOptions();
 
-       // scroll page to the highlighted item
-       if ($target && isset ($_REQUEST['hl_net']))
-               addAutoScrollScript ("net-$target");
-
-       foreach ($tree as $item)
-       {
-               if (getConfigVar ('IPV4_TREE_SHOW_USAGE') == 'yes')
-                       loadIPv6AddrList ($item); // necessary to compute router list and address counter
-               else
-               {
-                       $item['addrlist'] = array();
-                       $item['addrc'] = 0;
-               }
-               if (isset ($item['id']))
-               {
-                       $decor = array ('indent' => $level);
-                       if ($item['symbol'] == 'node-collapsed')
-                               $decor['symbolurl'] = "${baseurl}&eid=" . $item['id'];
-                       elseif ($item['symbol'] == 'node-expanded')
-                               $decor['symbolurl'] = $baseurl . ($item['parent_id'] ? "&eid=${item['parent_id']}#net6id${item['parent_id']}" : '');
-                       echo "<tr valign=top>";
-                       if ($target == $item['id'] && isset ($_REQUEST['hl_net']))
-                               $decor['tdclass'] = ' port_highlight';
-                       printIPNetInfoTDs ($item, $decor);
+                       // capacity and usage
                        echo "<td class=tdcenter>";
-                       // show net usage
-                       echo formatIPv6NetUsage ($item['addrc'], $item['mask']);
+                       echo getRenderedIPNetCapacity ($item);
                        echo "</td>";
-                       if (getConfigVar ('IPV4_TREE_SHOW_VLAN') == 'yes')
-                       {
-                               echo '<td class=tdleft>';
-                               if (count ($item['8021q']))
-                               {
-                                       echo '<ul>';
-                                       foreach ($item['8021q'] as $binding)
-                                       {
-                                               echo '<li><a href="' . makeHref (array ('page' => 'vlan', 'vlan_ck' => $binding['domain_id'] . '-' . $binding['vlan_id'])) . '">';
-                                               // FIXME: would formatVLANName() do this?
-                                               echo $binding['vlan_id'] . '@' . niftyString ($vdomlist[$binding['domain_id']], 15) . '</a></li>';
-                                       }
-                                       echo '</ul>';
-                               }
-                               echo '</td>';
-                       }
-                       if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
-                               printRoutersTD (findRouters ($item['addrlist']), getConfigVar ('IPV4_TREE_RTR_AS_CELL'));
+                       if ($display_routers)
+                               echo "<td></td>";
                        echo "</tr>";
-                       if ($item['symbol'] == 'node-expanded' or $item['symbol'] == 'node-expanded-static')
-                               $self ($item['kids'], $baseurl, $target, $knight, $level + 1);
                }
-               /* do not display spare networks
-               else
-               { // display spare networks
-                       echo "<tr valign=top>";
-                       printIPNetInfoTDs ($item, array ('indent' => $level, 'knight' => $knight, 'tdclass' => 'sparenetwork'));
-                       echo "<td class=tdcenter>";
-                       echo formatIPv6NetUsage ($item['addrc'], $item['mask']);
-                       if (getConfigVar ('IPV4_TREE_SHOW_VLAN') == 'yes')
-                               echo '</td><td>&nbsp;</td>';
-                       echo "</td><td>&nbsp;</td></tr>";
-               }*/
        }
 }
 
-// if $used is NULL, returns only human-formatted mask.
-// Otherwise returns "$used in/of " . human-formatted-mask
-function formatIPv6NetUsage ($used, $mask)
-{
-       $prefixes = array
-       (
-               0 =>  '',
-               3 =>  'k',
-               6 =>  'M',
-               9 =>  'G',
-               12 => 'T',
-               15 => 'P',
-               18 => 'E',
-               21 => 'Z',
-               24 => 'Y',
-       );
-
-       if ($mask <= 64)
-       {
-               $what = '/64 net';
-               $preposition = 'in';
-               $mask += 64;
-       }
-       else
-       {
-               $what = 'IP';
-               $preposition = 'of';
-       }
-       $what .= (0 == $mask % 64 ? '' : 's');
-       $addrc = isset ($used) ? "$used $preposition " : '';
-
-       $dec_order = intval ((128 - $mask) / 10) * 3;
-       $mult = isset ($prefixes[$dec_order]) ? $prefixes[$dec_order] : '??';
-       
-       $cnt = 1 << ((128 - $mask) % 10);
-       if ($cnt == 1 && $mult == '')
-               $cnt = 'single';
-
-       return "<small>${addrc}${cnt}${mult} ${what}</small>";
-}
-
-function renderIPv4Space ()
+function renderIPSpace()
 {
        global $pageno, $tabno;
+       $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
        $cellfilter = getCellFilter();
-       $netlist = listCells ('ipv4net');
-       $allcount = count ($netlist);
-       $netlist = filterCellList ($netlist, $cellfilter['expression']);
-
-       $netcount = count ($netlist);
-       // expand request can take either natural values or "ALL". Zero means no expanding.
-       $eid = isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0;
-       $tree = prepareIPv4Tree ($netlist, $eid);
-
-       echo "<table border=0 class=objectview>\n";
-       echo "<tr><td class=pcleft>";
-       if (! renderEmptyResults($cellfilter, 'IPv4 nets', count($tree)))
-       {
-               startPortlet ("networks (${netcount})");
-               echo '<h4>';
-               if ($eid === 0)
-                       echo 'auto-collapsing at threshold ' . getConfigVar ('TREE_THRESHOLD') .
-                               " (<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'ALL')) .
-                               $cellfilter['urlextra'] . "'>expand all</a>)";
-               elseif ($eid === 'ALL')
-                       echo "expanding all (<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno)) .
-                               $cellfilter['urlextra'] . "'>auto-collapse</a>)";
+       $top = NULL;
+       $netlist = array();
+       foreach (listCells ($realm) as $net)
+       {
+               if (isset ($top) and IPNetContains ($top, $net))
+                       ;
+               elseif (! count ($cellfilter['expression']) or judgeCell ($net, $cellfilter['expression']))
+                       $top = $net;
                else
-               {
-                       $netinfo = spotEntity ('ipv4net', $eid);
-                       echo "expanding ${netinfo['ip']}/${netinfo['mask']} (<a href='" .
-                               makeHref (array ('page' => $pageno, 'tab' => $tabno)) .
-                               $cellfilter['urlextra'] . "'>auto-collapse</a> / <a href='" .
-                               makeHref (array ('page' => $pageno, 'tab' => $tabno, 'eid' => 'ALL')) .
-                               $cellfilter['urlextra'] . "'>expand&nbsp;all</a>)";
-               }
-               echo "</h4><table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
-               echo "<tr><th>prefix</th><th>name/tags</th><th>capacity</th>";
-               if (getConfigVar ('IPV4_TREE_SHOW_VLAN') == 'yes')
-                       echo '<th>VLAN</th>';
-               if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
-                       echo "<th>routed by</th>";
-               echo "</tr>\n";
-               $baseurl = makeHref(array('page'=>$pageno, 'tab'=>$tabno)) . $cellfilter['urlextra'];
-               renderIPv4SpaceRecords ($tree, $baseurl, $eid, $netcount == $allcount and getConfigVar ('IPV4_ENABLE_KNIGHT') == 'yes');
-               echo "</table>\n";
-               finishPortlet();
+                       continue;
+               $netlist[$net['id']] = $net;
        }
-
-       echo '</td><td class=pcright>';
-       renderCellFilterPortlet ($cellfilter, 'ipv4net', $netlist);
-       echo "</td></tr></table>\n";
-}
-
-function renderIPv6Space ()
-{
-       global $pageno, $tabno;
-       $cellfilter = getCellFilter();
-       $netlist = listCells ('ipv6net');
-       $allcount = count ($netlist);
-       $netlist = filterCellList ($netlist, $cellfilter['expression']);
-
        $netcount = count ($netlist);
        // expand request can take either natural values or "ALL". Zero means no expanding.
        $eid = isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0;
-       $tree = prepareIPv6Tree ($netlist, $eid);
+       $tree = prepareIPTree ($netlist, $eid);
 
        echo "<table border=0 class=objectview>\n";
        echo "<tr><td class=pcleft>";
-       if (! renderEmptyResults($cellfilter, 'IPv6 nets', count($tree)))
+       if (! renderEmptyResults($cellfilter, 'IP nets', count($tree)))
        {
                startPortlet ("networks (${netcount})");
                echo '<h4>';
+               $all = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'ALL')) .
+                               $cellfilter['urlextra'] . "'>expand&nbsp;all</a>";
+               $none = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'NONE')) .
+                               $cellfilter['urlextra'] . "'>collapse&nbsp;all</a>";
+               $auto = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno)) .
+                       $cellfilter['urlextra'] . "'>auto-collapse</a>";
+
                if ($eid === 0)
-                       echo 'auto-collapsing at threshold ' . getConfigVar ('TREE_THRESHOLD') .
-                               " (<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'ALL')) .
-                               $cellfilter['urlextra'] . "'>expand all</a>)";
+                       echo 'auto-collapsing at threshold ' . getConfigVar ('TREE_THRESHOLD') . " ($all / $none)";
                elseif ($eid === 'ALL')
-                       echo "expanding all (<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno)) .
-                               $cellfilter['urlextra'] . "'>auto-collapse</a>)";
+                       echo "expanding all ($auto / $none)";
+               elseif ($eid === 'NONE')
+                       echo "collapsing all ($all / $auto)";
                else
                {
-                       $netinfo = spotEntity ('ipv6net', $eid);
-                       echo "expanding ${netinfo['ip']}/${netinfo['mask']} (<a href='" .
-                               makeHref (array ('page' => $pageno, 'tab' => $tabno)) .
-                               $cellfilter['urlextra'] . "'>auto-collapse</a> / <a href='" .
-                               makeHref (array ('page' => $pageno, 'tab' => $tabno, 'eid' => 'ALL')) .
-                               $cellfilter['urlextra'] . "'>expand&nbsp;all</a>)";
+                       $netinfo = spotEntity ($realm, $eid);
+                       echo "expanding ${netinfo['ip']}/${netinfo['mask']} ($auto / $all / $none)";
                }
                echo "</h4><table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
                echo "<tr><th>prefix</th><th>name/tags</th><th>capacity</th>";
-               if (getConfigVar ('IPV4_TREE_SHOW_VLAN') == 'yes')
-                       echo '<th>VLAN</th>';
-               if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
+               if (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none')
                        echo "<th>routed by</th>";
                echo "</tr>\n";
                $baseurl = makeHref(array('page'=>$pageno, 'tab'=>$tabno)) . $cellfilter['urlextra'];
-               renderIPv6SpaceRecords ($tree, $baseurl, $eid, $netcount == $allcount and getConfigVar ('IPV4_ENABLE_KNIGHT') == 'yes');
+               renderIPSpaceRecords ($tree, $baseurl, $eid);
                echo "</table>\n";
                finishPortlet();
        }
 
        echo '</td><td class=pcright>';
-       renderCellFilterPortlet ($cellfilter, 'ipv6net', $netlist);
+       renderCellFilterPortlet ($cellfilter, 'ipv4net', $netlist);
        echo "</td></tr></table>\n";
 }
 
-function renderIPv4SpaceEditor ()
-{
-       $addrspaceList = listCells ('ipv4net');
-       startPortlet ('Manage existing (' . count ($addrspaceList) . ')');
-       if (count ($addrspaceList))
-       {
-               echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
-               echo "<tr><th>&nbsp;</th><th>prefix</th><th>name</th><th>capacity</th></tr>";
-               $tree = prepareIPv4Tree ($addrspaceList, 'ALL');
-               // this is only called for having "trace" set
-               treeFromList ($addrspaceList);
-               foreach ($addrspaceList as $netinfo)
-               {
-                       $netinfo = peekNode ($tree, $netinfo['trace'], $netinfo['id']);
-                       // now we have all subnets listed in netinfo
-                       loadIPv4AddrList ($netinfo);
-                       $used = $netinfo['addrc'];
-                       $maxdirect = $netinfo['addrt'];
-                       $maxtotal = binInvMaskFromDec ($netinfo['mask']) + 1;
-                       echo "<tr valign=top><td>";
-                       if (! isIPNetworkEmpty ($netinfo))
-                               printImageHREF ('nodestroy', 'There are ' . count ($netinfo['addrlist']) . ' allocations inside');
-                       else
-                               echo getOpLink (array   ('op' => 'del', 'id' => $netinfo['id']), '', 'destroy', 'Delete this prefix');
-                       echo '</td><td class=tdleft><a href="' . makeHref (array ('page' => 'ipv4net', 'id' => $netinfo['id'])) . '">';
-                       echo "${netinfo['ip']}/${netinfo['mask']}</a></td>";
-                       echo '<td class=tdleft>' . niftyString ($netinfo['name']);
-                       if (count ($netinfo['etags']))
-                               echo '<br><small>' . serializeTags ($netinfo['etags']) . '</small>';
-                       echo '</td><td>';
-                       renderProgressBar ($maxdirect ? $used/$maxdirect : 0);
-                       echo "<br><small>${used}/${maxdirect}" . ($maxdirect == $maxtotal ? '' : "/${maxtotal}") . '</small></td>';
-                       echo '</tr>';
-               }
-               echo "</table>";
-               finishPortlet();
-       }
-}
-
-function renderIPv6SpaceEditor ()
+function renderIPSpaceEditor()
 {
-       $addrspaceList = listCells ('ipv6net');
+       global $pageno;
+       $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
+       $net_page = $realm; // 'ipv4net', 'ipv6net'
+       $addrspaceList = listCells ($realm);
        startPortlet ('Manage existing (' . count ($addrspaceList) . ')');
        if (count ($addrspaceList))
        {
                echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
                echo "<tr><th>&nbsp;</th><th>prefix</th><th>name</th><th>capacity</th></tr>";
-               $tree = prepareIPv6Tree ($addrspaceList, 'ALL');
-               // this is only called for having "trace" set
-               treeFromList ($addrspaceList);
                foreach ($addrspaceList as $netinfo)
                {
-                       $netinfo = peekNode ($tree, $netinfo['trace'], $netinfo['id']);
-                       // now we have all subnets listed in netinfo
-                       loadIPv6AddrList ($netinfo);
                        echo "<tr valign=top><td>";
                        if (! isIPNetworkEmpty ($netinfo))
                                printImageHREF ('nodestroy', 'There are ' . count ($netinfo['addrlist']) . ' allocations inside');
                        else
                                echo getOpLink (array   ('op' => 'del', 'id' => $netinfo['id']), '', 'destroy', 'Delete this prefix');
-                       echo '</td><td class=tdleft><a href="' . makeHref (array ('page' => 'ipv6net', 'id' => $netinfo['id'])) . '">';
+                       echo '</td><td class=tdleft><a href="' . makeHref (array ('page' => $net_page, 'id' => $netinfo['id'])) . '">';
                        echo "${netinfo['ip']}/${netinfo['mask']}</a></td>";
                        echo '<td class=tdleft>' . niftyString ($netinfo['name']);
                        if (count ($netinfo['etags']))
                                echo '<br><small>' . serializeTags ($netinfo['etags']) . '</small>';
                        echo '</td><td>';
-                       echo formatIPv6NetUsage ($netinfo['addrc'], $netinfo['mask']);
+                       echo getRenderedIPNetCapacity ($netinfo);
                        echo '</tr>';
                }
                echo "</table>";
@@ -2435,12 +2171,49 @@ END
        finishPortlet();
 }
 
-function renderIPv4Network ($id)
+function getRenderedIPNetBacktrace ($range)
 {
-       global $pageno, $tabno, $aac2, $netmaskbylen, $wildcardbylen;
+       if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
+               return array();
 
-       $range = spotEntity ('ipv4net', $id);
-       loadIPv4AddrList ($range);
+       $v = ($range['realm'] == 'ipv4net') ? 4 : 6;
+       $space = "ipv${v}space"; // ipv4space, ipv6space
+       $tag = "\$ip${v}netid_"; // $ip4netid_, $ip6netid_
+       
+       $ret = array();
+       // Build a backtrace from all parent networks.
+       $clen = $range['mask'];
+       $backtrace = array();
+       $backtrace['&rarr;'] = $range;
+       $key = '';
+       while (NULL !== ($upperid = getIPAddressNetworkId ($range['ip_bin'], $clen)))
+       {
+               $upperinfo = spotEntity ($range['realm'], $upperid);
+               $clen = $upperinfo['mask'];
+               $key .= '&uarr;';
+               $backtrace[$key] = $upperinfo;
+       }
+       foreach (array_reverse ($backtrace) as $arrow => $ainfo)
+       {
+               $link = '<a href="' . makeHref (array (
+                       'page' => $space,
+                       'tab' => 'default',
+                       'clear-cf' => '',
+                       'cfe' => '{' . $tag . $ainfo['id'] . '}',
+                       'hl_net' => 1,
+                       'eid' => $range['id'],
+               )) . '" title="View IP tree with this net as root">' . $arrow . '</a>';
+               $ret[] = array ($link, getOutputOf ('renderCell', $ainfo));
+       }
+       return $ret;
+}
+
+function renderIPNetwork ($id)
+{
+       global $pageno;
+       $realm = $pageno; // 'ipv4net', 'ipv6net'
+       $range = spotEntity ($realm, $id);
+       loadIPAddrList ($range);
        echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
        echo "<tr><td colspan=2 align=center><h1>${range['ip']}/${range['mask']}</h1><h2>";
        echo htmlspecialchars ($range['name'], ENT_QUOTES, 'UTF-8') . "</h2></td></tr>\n";
@@ -2449,36 +2222,14 @@ function renderIPv4Network ($id)
 
        // render summary portlet
        $summary = array();
-       $total = ($range['ip_bin'] | $range['mask_bin_inv']) - ($range['ip_bin'] & $range['mask_bin']) + 1;
-       $used = count ($range['addrlist']);
-       $summary['%% used'] = getProgressBar ($used/$total) . "&nbsp;${used}/${total}";
-       if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
+       $summary['%% used'] = getRenderedIPNetCapacity ($range);
+       $summary = array_merge ($summary, getRenderedIPNetBacktrace ($range));
+       if ($realm == 'ipv4net')
        {
-               // Build a backtrace from all parent networks.
-               $clen = $range['mask'];
-               $backtrace = array();
-               while (NULL !== ($upperid = getIPv4AddressNetworkId ($range['ip'], $clen)))
-               {
-                       $upperinfo = spotEntity ('ipv4net', $upperid);
-                       $clen = $upperinfo['mask'];
-                       $backtrace[] = $upperinfo;
-               }
-               $arrows = count ($backtrace);
-               foreach (array_reverse ($backtrace) as $ainfo)
-               {
-                       $name = '';
-                       for ($i = 0; $i < $arrows; $i++)
-                               $name .= '&uarr;';
-                       $arrows--;
-                       $summary[] = array ($name, getOutputOf ('renderCell', $ainfo));
-               }
-               $summary[] = array ('&rarr;', getOutputOf ('renderCell', $range));
-               // FIXME: get and display nested networks
-               // $theitem = pickLeaf ($ipv4tree, $id);
+               $summary[] = array ('Netmask:', ip4_format ($range['mask_bin']));
+               $summary[] = array ('Netmask:', "0x" . strtoupper (implode ('', unpack ('H*', $range['mask_bin']))));
+               $summary['Wildcard bits'] = ip4_format ( ~ $range['mask_bin']);
        }
-       $summary[] = array ('Netmask:', $netmaskbylen[$range['mask']]);
-       $summary[] = array ('Netmask:', sprintf ('0x%08X', binMaskFromDec ($range['mask'])));
-       $summary['Wildcard bits'] = $wildcardbylen[$range['mask']];
        
        foreach ($range['8021q'] as $item)
        {
@@ -2489,7 +2240,7 @@ function renderIPv4Network ($id)
        {
                $summary['Routed by'] = '';
                foreach ($routers as $rtr)
-                       $summary['Routed by'] .= getOutputOf ('renderRouterCell', $rtr['addr'], $rtr['iface'], spotEntity ('object', $rtr['id']));
+                       $summary['Routed by'] .= getOutputOf ('renderRouterCell', $rtr['ip_bin'], $rtr['iface'], spotEntity ('object', $rtr['id']));
        }
        $summary['tags'] = '';
        renderEntitySummary ($range, 'summary', $summary);
@@ -2501,21 +2252,80 @@ function renderIPv4Network ($id)
                finishPortlet ();
        }
 
-       renderFilesPortlet ('ipv4net', $id);
+       renderFilesPortlet ($realm, $id);
        echo "</td>\n";
 
        echo "<td class=pcright>";
        startPortlet ('details');
-       $startip = $range['ip_bin'] & $range['mask_bin'];
-       $endip = $range['ip_bin'] | $range['mask_bin_inv'];
-       $realstartip = $startip;
-       $realendip = $endip;
+       renderIPNetworkAddresses ($range);
+       finishPortlet();
+       echo "</td></tr></table>\n";
+}
 
-       if (isset ($_REQUEST['hl_ipv4_addr']))
+// Used solely by renderSeparator
+function renderEmptyIPv6 ($ip_bin, $hl_ip)
+{
+       $class = 'tdleft';
+       if ($ip_bin === $hl_ip)
+               $class .= ' port_highlight';
+       $fmt = ip6_format ($ip_bin);
+       echo "<tr class='$class'><td><a class='ancor' name='ip-$fmt' href='" . makeHref (array ('page' => 'ipaddress', 'ip' => $fmt)) . "'>" . $fmt;
+       echo "</a></td><td class='rsv-port'><span class='rsvtext'></span></td><td>&nbsp;</td></tr>\n";
+}
+
+// Renders empty table line to shrink empty IPv6 address ranges.
+// If the range consists of single address, renders the address instead of empty line.
+// Renders address $hl_ip inside the range.
+// Used solely by renderIPv6NetworkAddresses
+function renderSeparator ($first, $last, $hl_ip)
+{
+       $self = __FUNCTION__;
+       if (strcmp ($first, $last) > 0)
+               return;
+       if ($first == $last)
+               renderEmptyIPv6 ($first, $hl_ip);
+       elseif (isset ($hl_ip) && strcmp ($hl_ip, $first) >= 0 && strcmp ($hl_ip, $last) <= 0)
+       { // $hl_ip is inside the range $first - $last
+               $self ($first, ip_prev ($hl_ip), $hl_ip);
+               renderEmptyIPv6 ($hl_ip, $hl_ip);
+               $self (ip_next ($hl_ip), $last, $hl_ip);
+       }
+       else
+               echo "<tr><td colspan=3 class=tdleft></td></tr>\n";
+}
+
+// calculates page number which contains given $ip (used by renderIPv6NetworkAddresses)
+function getPageNumOfIPv6 ($list, $ip_bin, $maxperpage)
+{
+       if (intval ($maxperpage) <= 0 || count ($list) <= $maxperpage)
+               return 0;
+       $keys = array_keys ($list);
+       for ($i = 1; $i <= count ($keys); $i++)
+               if (strcmp ($keys[$i-1], $ip_bin) >= 0)
+                       return intval ($i / $maxperpage);
+       return intval (count ($list) / $maxperpage);
+}
+
+function renderIPNetworkAddresses ($range)
+{
+       switch (strlen ($range['ip_bin']))
+       {
+               case 4:  return renderIPv4NetworkAddresses ($range);
+               case 16: return renderIPv6NetworkAddresses ($range);
+               default: throw new InvalidArgException ("range['ip_bin']", $range['ip_bin']);
+       }
+}
+
+function renderIPv4NetworkAddresses ($range)
+{
+       global $pageno, $tabno, $aac2;
+       $startip = ip4_bin2int ($range['ip_bin']);
+       $endip = ip4_bin2int (ip_last ($range));
+
+       if (isset ($_REQUEST['hl_ip']))
        {
-               $hl_ip = ip2long ($_REQUEST['hl_ipv4_addr']);
-               $hl_dottedquad = ip_long2quad ($hl_ip);
-               addAutoScrollScript ("ip-$hl_dottedquad"); // scroll page to highlighted ip
+               $hl_ip = ip4_bin2int (ip4_parse ($_REQUEST['hl_ip']));
+               addAutoScrollScript ('ip-' . $_REQUEST['hl_ip']); // scroll page to highlighted ip
        }
 
        // pager
@@ -2528,12 +2338,12 @@ function renderIPv4Network ($id)
                $page = isset ($_REQUEST['pg']) ? $_REQUEST['pg'] : (isset ($hl_ip) ? intval (($hl_ip - $startip) / $maxperpage) : 0);
                if ($numpages = ceil ($address_count / $maxperpage))
                {
-                       echo '<h3>' . long2ip ($startip) . ' ~ ' . long2ip ($endip) . '</h3>';
+                       echo '<h3>' . ip4_format (ip4_int2bin ($startip)) . ' ~ ' . ip4_format (ip4_int2bin ($endip)) . '</h3>';
                        for ($i = 0; $i < $numpages; $i++)
                                if ($i == $page)
                                        $rendered_pager .= "<b>$i</b> ";
                                else
-                                       $rendered_pager .= "<a href='".makeHref (array ('page' => $pageno, 'tab' => $tabno, 'id' => $id, 'pg' => $i)) . "'>$i</a> ";
+                                       $rendered_pager .= "<a href='".makeHref (array ('page' => $pageno, 'tab' => $tabno, 'id' => $range['id'], 'pg' => $i)) . "'>$i</a> ";
                }
                $startip = $startip + $page * $maxperpage;
                $endip = min ($startip + $maxperpage - 1, $endip);
@@ -2543,16 +2353,19 @@ function renderIPv4Network ($id)
        echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center' width='100%'>\n";
        echo "<tr><th>Address</th><th>Name</th><th>Allocation</th></tr>\n";
 
-       for ($ip = $startip; $ip <= $endip; $ip++) :
-               $dottedquad = ip_long2quad($ip);
-               $secondstyle = 'tdleft' . (isset ($hl_ip) && $hl_ip == $ip ? ' port_highlight' : '');
-               if (!isset ($range['addrlist'][$ip]))
+       markupIPAddrList ($range['addrlist']);
+       for ($ip = $startip; $ip <= $endip; $ip++)
+       {
+               $ip_bin = ip4_int2bin ($ip);
+               $dottedquad = ip4_format ($ip_bin);
+               $tr_class = (isset ($hl_ip) && $hl_ip == $ip ? 'port_highlight' : '');
+               if (!isset ($range['addrlist'][$ip_bin]))
                {
-                       echo "<tr><td class=tdleft><a class='ancor' name='ip-$dottedquad' href='" . makeHref(array('page'=>'ipaddress', 'ip' => $dottedquad)) . "'>$dottedquad</a></td>";
-                       echo "<td class='rsv-port ${secondstyle}'><span class='rsvtext'></span></td><td class='${secondstyle}'>&nbsp;</td></tr>\n";
+                       echo "<tr class='tdleft $tr_class'><td class=tdleft><a class='ancor' name='ip-$dottedquad' href='" . makeHref(array('page'=>'ipaddress', 'ip' => $dottedquad)) . "'>$dottedquad</a></td>";
+                       echo "<td class='rsv-port'><span class='rsvtext'></span></td><td>&nbsp;</td></tr>\n";
                        continue;
                }
-               $addr = $range['addrlist'][$ip];
+               $addr = $range['addrlist'][$ip_bin];
                // render IP change history
                $title = '';
                $history_class = '';
@@ -2561,11 +2374,12 @@ function renderIPv4Network ($id)
                        $title = ' title="' . htmlspecialchars ($addr['last_log']['user'] . ', ' . formatAge ($addr['last_log']['time']) , ENT_QUOTES) . '"';
                        $history_class = 'hover-history underline';
                }
-               echo "<tr class='${addr['class']}'>";
-               echo "<td class=tdleft><a class='ancor $history_class' $title name='ip-$dottedquad' href='".makeHref(array('page'=>'ipaddress', 'ip'=>$addr['ip']))."'>${addr['ip']}</a></td>";
-               echo "<td class='${secondstyle} " .
+               $tr_class .= ' ' . $addr['class'];
+               echo "<tr class='tdleft $tr_class'>";
+               echo "<td><a class='ancor $history_class' $title name='ip-$dottedquad' href='".makeHref(array('page'=>'ipaddress', 'ip'=>$addr['ip']))."'>${addr['ip']}</a></td>";
+               echo "<td class='" .
                        (empty ($addr['allocs']) || !empty ($addr['name']) ? 'rsv-port' : '') .
-                       "'><span class='rsvtext'>${addr['name']}</span></td><td class='${secondstyle}'>";
+                       "'><span class='rsvtext'>${addr['name']}</span></td><td>";
                $delim = '';
                if ( $addr['reserved'] == 'yes')
                {
@@ -2575,7 +2389,7 @@ function renderIPv4Network ($id)
                foreach ($addr['allocs'] as $ref)
                {
                        echo $delim . $aac2[$ref['type']];
-                       echo "<a href='".makeHref(array('page'=>'object', 'object_id'=>$ref['object_id'], 'tab' => 'default', 'hl_ipv4_addr'=>$addr['ip']))."'>";
+                       echo "<a href='".makeHref(array('page'=>'object', 'object_id'=>$ref['object_id'], 'tab' => 'default', 'hl_ip'=>$addr['ip']))."'>";
                        echo $ref['name'] . (!strlen ($ref['name']) ? '' : '@');
                        echo "${ref['object_name']}</a>";
                        $delim = '; ';
@@ -2597,7 +2411,7 @@ function renderIPv4Network ($id)
                        $delim = '<br>';
                }
                echo "</td></tr>\n";
-       endfor;
+       }
        // end of iteration
        if (permitted (NULL, NULL, 'set_reserve_comment'))
                addJS ('js/inplace-edit.js');
@@ -2605,123 +2419,6 @@ function renderIPv4Network ($id)
        echo "</table>";
        if (! empty ($rendered_pager))
                echo '<p>' . $rendered_pager . '</p>';
-
-       finishPortlet();
-       echo "</td></tr></table>\n";
-}
-
-// based on renderIPv4Network
-function renderIPv6Network ($id)
-{
-       $range = spotEntity ('ipv6net', $id);
-       loadIPv6AddrList ($range);
-       echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
-       echo "<tr><td colspan=2 align=center><h1>${range['ip']}/${range['mask']}</h1><h2>";
-       echo htmlspecialchars ($range['name'], ENT_QUOTES, 'UTF-8') . "</h2></td></tr>\n";
-
-       echo "<tr><td class=pcleft width='50%'>";
-
-       // render summary portlet
-       $summary = array();
-       $summary['%% used'] = formatIPv6NetUsage (count ($range['addrlist']), $range['mask']);
-       if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
-       {
-               // Build a backtrace from all parent networks.
-               $backtrace = array();
-               $current = $range;
-               while ($current['parent_id'])
-               {
-                       $current = spotEntity ('ipv6net', $current['parent_id']);
-                       $backtrace[] = $current;
-               }
-               $arrows = count ($backtrace);
-               foreach (array_reverse ($backtrace) as $ainfo)
-               {
-                       $name = '';
-                       for ($i = 0; $i < $arrows; $i++)
-                               $name .= '&uarr;';
-                       $arrows--;
-                       $summary[] = array ($name, getOutputOf ('renderCell', $ainfo));
-               }
-               $summary[] = array ('&rarr;', getOutputOf ('renderCell', $range));
-               // FIXME: get and display nested networks
-       }
-
-       foreach ($range['8021q'] as $item)
-       {
-               $vlaninfo = getVLANInfo ($item['domain_id'] . '-' . $item['vlan_id']);
-               $summary[] = array ('VLAN:', '<a href="' . makeHref (array ('page' => 'vlan', 'vlan_ck' => $vlaninfo['vlan_ck'])) . '">' . formatVLANName ($vlaninfo, 'markup long') . '</a>');
-       }
-       if (getConfigVar ('EXT_IPV4_VIEW') == 'yes' and count ($routers = findRouters ($range['addrlist'])))
-       {
-               $summary['Routed by'] = '';
-               foreach ($routers as $rtr)
-                       $summary['Routed by'] .= getOutputOf ('renderRouterCell', $rtr['addr'], $rtr['iface'], spotEntity ('object', $rtr['id']));
-       }
-       $summary['tags'] = '';
-       renderEntitySummary ($range, 'summary', $summary);
-
-       if (strlen ($range['comment']))
-       {
-               startPortlet ('Comment');
-               echo '<div class=commentblock>' . string_insert_hrefs (htmlspecialchars ($range['comment'], ENT_QUOTES, 'UTF-8')) . '</div>';
-               finishPortlet ();
-       }
-
-       renderFilesPortlet ('ipv6net', $id);
-       echo "</td>\n";
-
-       // render address list
-       echo "<td class=pcright>";
-       startPortlet ('details');
-       renderIPv6NetworkAddresses ($range);
-       finishPortlet();
-       echo "</td></tr></table>\n";
-}
-
-// Used solely by renderSeparator
-function renderEmptyIPv6 ($ip, $hl_ip)
-{
-       $class = 'tdleft';
-       if (isset ($hl_ip) && $ip == $hl_ip)
-               $class .= ' port_highlight';
-       $fmt = $ip->format();
-       echo "<tr><td class=tdleft><a class='ancor' name='ip-$fmt' href='" . makeHref (array ('page' => 'ipv6address', 'ip' => $fmt)) . "'>" . $fmt;
-       echo "</a></td><td class='${class} rsv-port'><span class='rsvtext'></span></td><td class='${class}'>&nbsp;</td></tr>\n";
-}
-
-// Renders empty table line to shrink empty IPv6 address ranges.
-// If the range consists of single address, renders the address instead of empty line.
-// Renders address $hl_ip inside the range.
-// Used solely by renderIPv6NetworkAddresses
-function renderSeparator ($first, $after, $hl_ip)
-{
-       $self = __FUNCTION__;
-       if (strcmp ($first->getBin(), $after->getBin()) >= 0)
-               return;
-       if ($first->next() == $after)
-               renderEmptyIPv6 ($first, $hl_ip);
-       elseif (isset ($hl_ip) && strcmp ($hl_ip->getBin(), $first->getBin()) >= 0 && strcmp ($hl_ip->getBin(), $after->getBin()) < 0)
-       { // $hl_ip is inside the range $first - ($after-1)
-               $self ($first, $hl_ip, $hl_ip);
-               renderEmptyIPv6 ($hl_ip, $hl_ip);
-               $self ($hl_ip->next(), $after, $hl_ip);
-       }
-       else
-               echo "<tr><td colspan=3 class=tdleft></td></tr>\n";
-}
-
-// calculates page number which contains given $ip (used by renderIPv6NetworkAddresses)
-function getPageNumOfIPv6 ($list, $ip, $maxperpage)
-{
-       if (intval ($maxperpage) <= 0 || count ($list) <= $maxperpage)
-               return 0;
-       $bin_ip = $ip->getBin();
-       $keys = array_keys ($list);
-       for ($i = 1; $i <= count ($keys); $i++)
-               if (strcmp ($keys[$i-1], $bin_ip) >= 0)
-                       return intval ($i / $maxperpage);
-       return intval (count ($list) / $maxperpage);
 }
 
 function renderIPv6NetworkAddresses ($netinfo)
@@ -2730,15 +2427,16 @@ function renderIPv6NetworkAddresses ($netinfo)
        echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center' width='100%'>\n";
        echo "<tr><th>Address</th><th>Name</th><th>Allocation</th></tr>\n";
 
-       $hl_ip = new IPv6Address;
-       if (! isset ($_REQUEST['hl_ipv6_addr']) || ! $hl_ip->parse ($_REQUEST['hl_ipv6_addr']))
-               $hl_ip = NULL;
-       else
-               addAutoScrollScript ('ip-' . $hl_ip->format());
+       $hl_ip = NULL;
+       if (isset ($_REQUEST['hl_ip']))
+       {
+               $hl_ip = ip6_parse ($_REQUEST['hl_ip']);
+               addAutoScrollScript ('ip-' . ip6_format ($hl_ip));
+       }
 
-       $prev_ip = $netinfo['ip_bin']; // really this is the next to previosly seen ip.
        $addresses = $netinfo['addrlist'];
        ksort ($addresses);
+       markupIPAddrList ($addresses);
 
        // pager
        $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
@@ -2759,7 +2457,8 @@ function renderIPv6NetworkAddresses ($netinfo)
 
        $i = 0;
        $interruped = FALSE; 
-       foreach ($addresses as $bin_ip => $addr)
+       $prev_ip = ip_prev ($netinfo['ip_bin']);
+       foreach ($addresses as $ip_bin => $addr)
        {
                if (isset ($page))
                {
@@ -2773,19 +2472,25 @@ function renderIPv6NetworkAddresses ($netinfo)
                        }
                }
 
-               $ipv6 = new IPv6Address ($bin_ip);
-               if ($ipv6 != $prev_ip)
-                       renderSeparator ($prev_ip, $ipv6, $hl_ip);
-               $prev_ip = $ipv6->next();
-               
-               $secondstyle = 'tdleft';
-               if (isset ($hl_ip) && $hl_ip == $ipv6)
-                       $secondstyle .= ' port_highlight';
-               echo "<tr class='${addr['class']}'>";
-               echo "<td class=tdleft><a class='ancor' name='ip-${addr['ip']}' href='" . makeHref (array ('page' => 'ipv6address', 'ip' => $addr['ip'])) . "'>${addr['ip']}</a></td>";
-               echo "<td class='${secondstyle} " .
+               if ($ip_bin != ip_next ($prev_ip))
+                       renderSeparator (ip_next ($prev_ip), ip_prev ($ip_bin), $hl_ip);
+               $prev_ip = $ip_bin;
+
+               // render IP change history
+               $title = '';
+               $history_class = '';
+               if (isset ($addr['last_log']))
+               {
+                       $title = ' title="' . htmlspecialchars ($addr['last_log']['user'] . ', ' . formatAge ($addr['last_log']['time']) , ENT_QUOTES) . '"';
+                       $history_class = 'hover-history underline';
+               }
+
+               $tr_class = $addr['class'] . ' tdleft' . ($hl_ip === $ip_bin ? ' port_highlight' : '');
+               echo "<tr class='$tr_class'>";
+               echo "<td><a class='ancor $history_class' $title name='ip-${addr['ip']}' href='" . makeHref (array ('page' => 'ipaddress', 'ip' => $addr['ip'])) . "'>${addr['ip']}</a></td>";
+               echo "<td class='" .
                        (empty ($addr['allocs']) || !empty ($addr['name']) ? 'rsv-port' : '') .
-                       "'><span class='rsvtext'>${addr['name']}</span></td><td class='${secondstyle}'>";
+                       "'><span class='rsvtext'>${addr['name']}</span></td><td>";
                $delim = '';
                $prologue = '';
                if ( $addr['reserved'] == 'yes')
@@ -2796,7 +2501,7 @@ function renderIPv6NetworkAddresses ($netinfo)
                foreach ($addr['allocs'] as $ref)
                {
                        echo $delim . $aac2[$ref['type']];
-                       echo "<a href='" . makeHref (array ('page' => 'object', 'object_id' => $ref['object_id'], 'hl_ipv6_addr' => $addr['ip'])) . "'>";
+                       echo "<a href='" . makeHref (array ('page' => 'object', 'object_id' => $ref['object_id'], 'hl_ip' => $addr['ip'])) . "'>";
                        echo $ref['name'] . (!strlen ($ref['name']) ? '' : '@');
                        echo "${ref['object_name']}</a>";
                        $delim = '; ';
@@ -2809,7 +2514,7 @@ function renderIPv6NetworkAddresses ($netinfo)
                echo "</td></tr>\n";
        }
        if (! $interruped)
-               renderSeparator ($prev_ip, $netinfo['ip_bin']->get_last_subnet_address ($netinfo['mask'])->next(), $hl_ip);
+               renderSeparator (ip_next ($prev_ip), ip_last ($netinfo), $hl_ip);
        if (isset ($page))
        { // bottom pager
                echo "<tr><td colspan=3>";
@@ -2849,10 +2554,10 @@ function renderIPNetworkProperties ($id)
        echo '</center>';
 }
 
-function renderIPAddress ($dottedquad)
+function renderIPAddress ($ip)
 {
        global $aat, $nextorder;
-       $address = getIPAddress ($dottedquad);
+       $address = getIPAddress (ip_parse ($ip));
        echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
        echo "<tr><td colspan=2 align=center><h1>${address['ip']}</h1></td></tr>\n";
 
@@ -2863,15 +2568,14 @@ function renderIPAddress ($dottedquad)
                $summary['Comment'] = $address['name'];
        $summary['Reserved'] = $address['reserved'];
        $summary['Allocations'] = count ($address['allocs']);
-       if ($address['version'] == 4)
-       {
+       if (isset ($address['outpf']))
                $summary['Originated NAT connections'] = count ($address['outpf']);
+       if (isset ($address['inpf']))
                $summary['Arriving NAT connections'] = count ($address['inpf']);
-       }
        renderEntitySummary ($address, 'summary', $summary);
        
        // render SLB portlet
-       if ($address['version'] == 4 and (! empty ($address['vslist']) or ! empty ($address['rsplist'])))
+       if (! empty ($address['vslist']) or ! empty ($address['rsplist']))
        {
                startPortlet ("");
                if (! empty ($address['vslist']))
@@ -2897,23 +2601,23 @@ function renderIPAddress ($dottedquad)
                startPortlet ('allocations');
                echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center' width='100%'>\n";
                echo "<tr><th>object</th><th>OS interface</th><th>allocation type</th></tr>\n";
-               $class = $address['class'];
                // render all allocation records for this address the same way
                foreach ($address['allocs'] as $bond)
                {
+                       $tr_class = "${address['class']} tdleft";
                        if (isset ($_REQUEST['hl_object_id']) and $_REQUEST['hl_object_id'] == $bond['object_id'])
-                               $secondclass = 'tdleft port_highlight';
-                       else
-                               $secondclass = 'tdleft';
-                       echo "<tr class='$class'><td class=tdleft><a href='" . makeHref (array ('page' => 'object', 'object_id' => $bond['object_id'], 'tab' => 'default', 'hl_ipv' . $address['version'] . '_addr' => $address['ip'])) . "'>${bond['object_name']}</td><td class='${secondclass}'>${bond['name']}</td><td class='${secondclass}'><strong>";
-                       echo $aat[$bond['type']];
-                       echo "</strong></td></tr>\n";
+                               $tr_class .= ' port_highlight';
+                       echo "<tr class='$tr_class'>" .
+                               "<td><a href='" . makeHref (array ('page' => 'object', 'object_id' => $bond['object_id'], 'tab' => 'default', 'hl_ip' => $address['ip'])) . "'>${bond['object_name']}</td>" .
+                               "<td>${bond['name']}</td>" .
+                               "<td><strong>" . $aat[$bond['type']] . "</strong></td>" .
+                               "</tr>\n";
                }
                echo "</table><br><br>";
                finishPortlet();
        }
 
-       if ($address['version'] == 4)
+       if (! empty ($address['vslist']) or ! empty ($address['rsplist']))
                renderSLBTriplets ($address);
 
        if (! empty ($address['outpf']))
@@ -2942,9 +2646,9 @@ function renderIPAddress ($dottedquad)
        echo "</table>\n";
 }
 
-function renderIPAddressProperties ($dottedquad)
+function renderIPAddressProperties ($ip)
 {
-       $address = getIPAddress ($dottedquad);
+       $address = getIPAddress (ip_parse ($ip));
        echo "<center><h1>${address['ip']}</h1></center>\n";
 
        startPortlet ('update');
@@ -2968,12 +2672,12 @@ function renderIPAddressProperties ($dottedquad)
        finishPortlet();
 }
 
-function renderIPAddressAllocations ($dottedquad)
+function renderIPAddressAllocations ($ip)
 {
-       function printNewItemTR ($opname)
+       function printNewItemTR ()
        {
                global $aat;
-               printOpFormIntro ($opname);
+               printOpFormIntro ('add');
                echo "<tr><td>";
                printImageHREF ('add', 'allocate', TRUE);
                echo "</td><td>";
@@ -2986,14 +2690,13 @@ function renderIPAddressAllocations ($dottedquad)
        }
        global $aat;
 
-       $address = getIPAddress ($dottedquad);
-       $opname = $address['version'] == 6 ? 'addIPv6Allocation' : 'addIPv4Allocation';
+       $address = getIPAddress (ip_parse ($ip));
        echo "<center><h1>${address['ip']}</h1></center>\n";
        echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center'>\n";
        echo "<tr><th>&nbsp;</th><th>object</th><th>OS interface</th><th>allocation type</th><th>&nbsp;</th></tr>\n";
 
        if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
-               printNewItemTR($opname);
+               printNewItemTR();
        if (isset ($address['class']))
        {
                $class = $address['class'];
@@ -3002,17 +2705,13 @@ function renderIPAddressAllocations ($dottedquad)
                foreach ($address['allocs'] as $bond)
                {
                        echo "<tr class='$class'>";
-                       printOpFormIntro
-                       (
-                               $address['version'] == 6 ? 'updIPv6Allocation' : 'updIPv4Allocation',
-                               array ('object_id' => $bond['object_id'])
-                       );
+                       printOpFormIntro ('upd', array ('object_id' => $bond['object_id']));
                        echo "<td><a href='"
                                . makeHrefProcess
                                (
                                        array
                                        (
-                                               'op' => $address['version'] == 6 ? 'delIPv6Allocation' : 'delIPv4Allocation',
+                                               'op' => 'del',
                                                'ip' => $address['ip'],
                                                'object_id' => $bond['object_id']
                                        )
@@ -3020,7 +2719,7 @@ function renderIPAddressAllocations ($dottedquad)
                                . "'>";
                        printImageHREF ('delete', 'Unallocate address');
                        echo "</a></td>";
-                       echo "<td><a href='" . makeHref (array ('page' => 'object', 'object_id' => $bond['object_id'], 'hl_ipv' . $address['version'] . '_addr' => $address['ip'])) . "'>${bond['object_name']}</td>";
+                       echo "<td><a href='" . makeHref (array ('page' => 'object', 'object_id' => $bond['object_id'], 'hl_ip' => $address['ip'])) . "'>${bond['object_name']}</td>";
                        echo "<td><input type='text' name='bond_name' value='${bond['name']}' size=10></td><td>";
                        printSelect ($aat, array ('name' => 'bond_type'), $bond['type']);
                        echo "</td><td>";
@@ -3029,7 +2728,7 @@ function renderIPAddressAllocations ($dottedquad)
                }
        }
        if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
-               printNewItemTR($opname);
+               printNewItemTR();
        echo "</table><br><br>";
 }
 
@@ -3044,11 +2743,12 @@ function renderNATv4ForObject ($object_id)
                printSelect (array ('TCP' => 'TCP', 'UDP' => 'UDP'), array ('name' => 'proto'));
                echo "<select name='localip' tabindex=1>";
 
-               foreach ($alloclist as $dottedquad => $alloc)
+               foreach ($alloclist as $ip_bin => $alloc)
                {
+                       $ip = $alloc['addrinfo']['ip'];
                        $name = (!isset ($alloc['addrinfo']['name']) or !strlen ($alloc['addrinfo']['name'])) ? '' : (' (' . niftyString ($alloc['addrinfo']['name']) . ')');
                        $osif = (!isset ($alloc['osif']) or !strlen ($alloc['osif'])) ? '' : ($alloc['osif'] . ': ');
-                       echo "<option value='${dottedquad}'>${osif}${dottedquad}${name}</option>";
+                       echo "<option value='${ip}'>${osif}${ip}${name}</option>";
                }
 
                echo "</select>:<input type='text' name='localport' size='4' tabindex=2></td>";
@@ -3076,10 +2776,11 @@ function renderNATv4ForObject ($object_id)
        {
                $class = 'trerror';
                $osif = '';
-               if (isset ($focus['ipv4'][$pf['localip']]))
+               $localip_bin = ip4_parse ($pf['localip']);
+               if (isset ($focus['ipv4'][$localip_bin]))
                {
-                       $class = $focus['ipv4'][$pf['localip']]['addrinfo']['class'];
-                       $osif = $focus['ipv4'][$pf['localip']]['osif'] . ': ';
+                       $class = $focus['ipv4'][$localip_bin]['addrinfo']['class'];
+                       $osif = $focus['ipv4'][$localip_bin]['osif'] . ': ';
                }
 
                echo "<tr class='$class'>";
@@ -3101,7 +2802,7 @@ function renderNATv4ForObject ($object_id)
                echo "</td>";
                echo "<td><a href='".makeHref(array('page'=>'ipaddress', 'tab'=>'default', 'ip'=>$pf['remoteip']))."'>${pf['remoteip']}</a>:${pf['remoteport']}</td>";
 
-               $address = getIPv4Address ($pf['remoteip']);
+               $address = getIPAddress (ip4_parse ($pf['remoteip']));
 
                echo "<td class='description'>";
                if (count ($address['allocs']))
@@ -3284,24 +2985,28 @@ function renderSearchResults ($terms, $summary)
                {
                        case 'ipv4addressbydq':
                                if ($record['net_id'] !== NULL)
-                                       echo "<script language='Javascript'>document.location='index.php?page=ipv4net&tab=default&id=${record['net_id']}&hl_ipv4_addr=${record['ip']}';//</script>";
+                                       echo "<script language='Javascript'>document.location='index.php?page=ipv4net&tab=default&id=${record['net_id']}&hl_ip=${record['ip']}';//</script>";
                                break;
                        case 'ipv6addressbydq':
-                               $v6_ip_dq = $record['ip']->format();
+                               $fmt = ip6_format ($record['ip']);
                                if ($record['net_id'] !== NULL)
-                                       echo "<script language='Javascript'>document.location='index.php?page=ipv6net&tab=default&id=${record['net_id']}&hl_ipv6_addr=${v6_ip_dq}';//</script>";
+                                       echo "<script language='Javascript'>document.location='index.php?page=ipv6net&tab=default&id=${record['net_id']}&hl_ip=${fmt}';//</script>";
                                break;
                        case 'ipv4addressbydescr':
                                $parentnet = getIPv4AddressNetworkId ($record['ip']);
                                if ($parentnet !== NULL)
-                                       echo "<script language='Javascript'>document.location='index.php?page=ipv4net&tab=default&id=${parentnet}&hl_ipv4_addr=${record['ip']}';//</script>";
+                               {
+                                       $fmt = ip4_format ($record['ip']);
+                                       echo "<script language='Javascript'>document.location='index.php?page=ipv4net&tab=default&id=${parentnet}&hl_ip=${fmt}';//</script>";
+                               }
                                break;
                        case 'ipv6addressbydescr':
-                               $v6_ip = new IPv6Address ($record['ip']);
-                               $v6_ip_dq = $v6_ip->format();
-                               $parentnet = getIPv6AddressNetworkId ($v6_ip);
+                               $parentnet = getIPv6AddressNetworkId ($record['ip']);
                                if ($parentnet !== NULL)
-                                       echo "<script language='Javascript'>document.location='index.php?page=ipv6net&tab=default&id=${parentnet}&hl_ipv6_addr=${v6_ip_dq}';//</script>";
+                               {
+                                       $fmt = ip6_format ($record['ip']);
+                                       echo "<script language='Javascript'>document.location='index.php?page=ipv6net&tab=default&id=${parentnet}&hl_ip=${fmt}';//</script>";
+                               }
                                break;
                        case 'ipv4network':
                                echo "<script language='Javascript'>document.location='index.php?page=ipv4net";
@@ -3459,11 +3164,12 @@ function renderSearchResults ($terms, $summary)
                                        foreach ($what as $addr)
                                        {
                                                echo "<tr class=row_${order}><td class=tdleft>";
+                                               $fmt = ip4_format ($addr['ip']);
                                                $parentnet = getIPv4AddressNetworkId ($addr['ip']);
                                                if ($parentnet !== NULL)
-                                                       echo "<a href='index.php?page=ipv4net&tab=default&id=${parentnet}&hl_ipv4_addr=${addr['ip']}'>${addr['ip']}</a></td>";
+                                                       echo "<a href='index.php?page=ipv4net&tab=default&id=${parentnet}&hl_ip=${fmt}'>${fmt}</a></td>";
                                                else
-                                                       echo "<a href='index.php?page=ipaddress&ip=${addr['ip']}'>${addr['ip']}</a></td>";
+                                                       echo "<a href='index.php?page=ipaddress&ip=${fmt}'>${fmt}</a></td>";
                                                echo "<td class=tdleft>${addr['name']}</td></tr>";
                                                $order = $nextorder[$order];
                                        }
@@ -3478,13 +3184,12 @@ function renderSearchResults ($terms, $summary)
                                        foreach ($what as $addr)
                                        {
                                                echo "<tr class=row_${order}><td class=tdleft>";
-                                               $v6_ip = new IPv6Address ($addr['ip']);
-                                               $v6_ip_dq = $v6_ip->format();
-                                               $parentnet = getIPv6AddressNetworkId ($v6_ip);
+                                               $fmt = ip6_format ($addr['ip']);
+                                               $parentnet = getIPv6AddressNetworkId ($addr['ip']);
                                                if ($parentnet !== NULL)
-                                                       echo "<a href='index.php?page=ipv6net&tab=default&id=${parentnet}&hl_ipv6_addr=${v6_ip_dq}'>${v6_ip_dq}</a></td>";
+                                                       echo "<a href='index.php?page=ipv6net&tab=default&id=${parentnet}&hl_ip=${fmt}'>${fmt}</a></td>";
                                                else
-                                                       echo "<a href='index.php?page=ipaddress&ip=${v6_ip_dq}'>${v6_ip_dq}</a></td>";
+                                                       echo "<a href='index.php?page=ipaddress&ip=${fmt}'>${fmt}</a></td>";
                                                echo "<td class=tdleft>${addr['name']}</td></tr>";
                                                $order = $nextorder[$order];
                                        }
@@ -4762,21 +4467,6 @@ function renderUIResetForm()
        echo "</form>";
 }
 
-function renderProgressBar ($percentage = 0, $theme = '')
-{
-       echo getProgressBar ($percentage, $theme);
-}
-
-function getProgressBar ($percentage = 0, $theme = '')
-{
-       $done = ((int) ($percentage * 100));
-       $ret = "<img width=100 height=10 border=0 title='${done}%' src='?module=progressbar&done=${done}";
-       if ($theme != '')
-               $ret .= "&theme=${theme}";
-       $ret .= "'>";
-       return $ret;
-}
-
 function renderLivePTR ($id)
 {
        if (isset($_REQUEST['pg']))
@@ -4786,15 +4476,13 @@ function renderLivePTR ($id)
        global $pageno, $tabno;
        $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
        $range = spotEntity ('ipv4net', $id);
-       loadIPv4AddrList ($range);
+       loadIPAddrList ($range);
        echo "<center><h1>${range['ip']}/${range['mask']}</h1><h2>${range['name']}</h2></center>\n";
 
        echo "<table class=objview border=0 width='100%'><tr><td class=pcleft>";
        startPortlet ('current records');
-       $startip = $range['ip_bin'] & $range['mask_bin'];
-       $endip = $range['ip_bin'] | $range['mask_bin_inv'];
-       $realstartip = $startip;
-       $realendip = $endip;
+       $startip = ip4_bin2int ($range['ip_bin']);
+       $endip = ip4_bin2int (ip_last ($range));
        $numpages = 0;
        if ($endip - $startip > $maxperpage)
        {
@@ -4804,7 +4492,7 @@ function renderLivePTR ($id)
        }
        echo "<center>";
        if ($numpages)
-               echo '<h3>' . long2ip ($startip) . ' ~ ' . long2ip ($endip) . '</h3>';
+               echo '<h3>' . ip4_format (ip4_int2bin ($startip)) . ' ~ ' . ip4_format (ip4_int2bin ($endip)) . '</h3>';
        for ($i=0; $i<$numpages; $i++)
                if ($i == $page)
                        echo "<b>$i</b> ";
@@ -4824,8 +4512,9 @@ function renderLivePTR ($id)
        {
                // Find the (optional) DB name and the (optional) DNS record, then
                // compare values and produce a table row depending on the result.
-               $addr = isset ($range['addrlist'][$ip]) ? $range['addrlist'][$ip] : array ('name' => '', 'reserved' => 'no');
-               $straddr = long2ip ($ip);
+               $ip_bin = ip4_int2bin ($ip);
+               $addr = isset ($range['addrlist'][$ip_bin]) ? $range['addrlist'][$ip_bin] : array ('name' => '', 'reserved' => 'no');
+               $straddr = ip4_format ($ip_bin);
                $ptrname = gethostbyaddr ($straddr);
                if ($ptrname == $straddr)
                        $ptrname = '';
@@ -4858,8 +4547,8 @@ function renderLivePTR ($id)
                        $cnt_mismatch++;
                }
                echo "><td class='tdleft";
-               if (isset ($range['addrlist'][$ip]['class']) and strlen ($range['addrlist'][$ip]['class']))
-                       echo ' ' . $range['addrlist'][$ip]['class'];
+               if (isset ($range['addrlist'][$ip_bin]['class']) and strlen ($range['addrlist'][$ip_bin]['class']))
+                       echo ' ' . $range['addrlist'][$ip_bin]['class'];
                echo "'><a href='".makeHref(array('page'=>'ipaddress', 'ip'=>$straddr))."'>${straddr}</a></td>";
                echo "<td class=tdleft>${addr['name']}</td><td class=tdleft>${ptrname}</td><td>";
                if ($print_cbox)
@@ -5767,7 +5456,7 @@ function printRoutersTD ($rlist, $as_cell = 'yes')
        $rtrclass = 'tdleft';
        foreach ($rlist as $rtr)
        {
-               $tmp = getIPAddress ($rtr['addr']);
+               $tmp = getIPAddress ($rtr['ip_bin']);
                if ($tmp['class'] == 'trerror')
                {
                        $rtrclass = 'tdleft trerror';
@@ -5780,7 +5469,7 @@ function printRoutersTD ($rlist, $as_cell = 'yes')
        {
                $rinfo = spotEntity ('object', $rtr['id']);
                if ($as_cell == 'yes')
-                       renderRouterCell ($rtr['addr'], $rtr['iface'], $rinfo);
+                       renderRouterCell ($rtr['ip_bin'], $rtr['iface'], $rinfo);
                else
                        echo $pfx . '<a href="' . makeHref (array ('page' => 'object', 'object_id' => $rinfo['id'])) . '">' . $rinfo['dname'] . '</a>';
                $pfx = "<br>\n";
@@ -5791,7 +5480,7 @@ function printRoutersTD ($rlist, $as_cell = 'yes')
 // Same as for routers, but produce two TD cells to lay the content out better.
 function printIPNetInfoTDs ($netinfo, $decor = array())
 {
-       $ip_ver = is_a ($netinfo['ip_bin'], 'IPv6Address') ? 6 : 4;
+       $ip_ver = strlen ($netinfo['ip_bin']) == 16 ? 6 : 4;
        $formatted = $netinfo['ip'] . "/" . $netinfo['mask'];
        if ($netinfo['symbol'] == 'spacer')
        {
@@ -5815,6 +5504,11 @@ function printIPNetInfoTDs ($netinfo, $decor = array())
        echo $formatted;
        if (isset ($netinfo['id']))
                echo '</a>';
+       if (getConfigVar ('IPV4_TREE_SHOW_VLAN') == 'yes' and ! empty ($netinfo['8021q']))
+       {
+               echo '<br>';
+               renderNetVLAN ($netinfo);
+       }
        echo '</td><td class="tdleft';
        if (array_key_exists ('tdclass', $decor))
                echo ' ' . $decor['tdclass'];
@@ -5843,6 +5537,22 @@ function printIPNetInfoTDs ($netinfo, $decor = array())
        echo "</td>";
 }
 
+function renderNetVLAN ($cell)
+{
+       if (! empty ($cell['8021q']))
+       {
+               $seen = array();
+               foreach ($cell['8021q'] as $vlan_info)
+                       $seen[$vlan_info['vlan_id']] = $vlan_info['domain_id'] . '-' . $vlan_info['vlan_id'];
+               echo '<div class="vlan"><strong><small>VLAN' . (count ($seen) > 1 ? 'S' : '') . '</small> ';
+               $links = array();
+               foreach ($seen as $vlan_id => $vlan_ck)
+                       $links[] = '<a href="' . makeHref (array ('page' => 'vlan', 'vlan_ck' => $vlan_ck)) . '">' . $vlan_id . '</a>';
+               echo implode (', ', $links);
+               echo '</strong></div>';
+       }
+}
+
 function renderCell ($cell)
 {
        switch ($cell['realm'])
@@ -5906,27 +5616,7 @@ function renderCell ($cell)
                printImageHREF ('NET');
                echo '</td>';
                echo "<td><a href='index.php?page={$cell['realm']}&id=${cell['id']}'>${cell['ip']}/${cell['mask']}</a>";
-               if (getConfigVar ('IPV4_TREE_SHOW_USAGE') == 'yes')
-               {
-                       echo '<div class="net-usage">';
-                       if ($cell['realm'] == 'ipv4net')
-                       {
-                               loadOwnIPv4Addresses ($cell);
-                               $used = $cell['addrc'];
-                               $maxdirect = $cell['addrt'];
-                               
-                               echo "<small>$used/$maxdirect</small> ";
-                               renderProgressBar ($maxdirect ? $used/$maxdirect : 0);
-                               
-                       }
-                       elseif ($cell['realm'] == 'ipv6net')
-                       {
-                               loadOwnIPv6Addresses ($cell);
-                               $used = $cell['addrc'];
-                               echo formatIPv6NetUsage ($used, $cell['mask']);
-                       }
-                       echo '</div>';
-               }
+               echo getRenderedIPNetCapacity ($cell);
                echo '</td></tr>';
 
                echo "<tr><td>";
@@ -5935,19 +5625,7 @@ function renderCell ($cell)
                else
                        echo "<span class=sparenetwork>no name</span>";
                // render VLAN
-               if (! empty ($cell['8021q']))
-               {
-                       $seen = array();
-                       foreach ($cell['8021q'] as $vlan_info)
-                               $seen[$vlan_info['vlan_id']] = $vlan_info['domain_id'] . '-' . $vlan_info['vlan_id'];
-                       echo '<div class="net-usage"><strong><small>VLAN' . (count ($seen) > 1 ? 'S' : '') . '</small> ';
-                       $links = array();
-                       foreach ($seen as $vlan_id => $vlan_ck)
-                               $links[] = '<a href="' . makeHref (array ('page' => 'vlan', 'vlan_ck' => $vlan_ck)) . '">' . $vlan_id . '</a>';
-                       echo implode (', ', $links);
-                       echo '</strong></div>';
-               }
-               
+               renderNetVLAN ($cell);
                echo "</td></tr>";
                echo '<tr><td>';
                echo count ($cell['etags']) ? ("<small>" . serializeTags ($cell['etags']) . "</small>") : '&nbsp;';
@@ -5982,15 +5660,14 @@ function renderCell ($cell)
        }
 }
 
-function renderRouterCell ($dottedquad, $ifname, $cell)
+function renderRouterCell ($ip_bin, $ifname, $cell)
 {
+       $dottedquad = ip_format ($ip_bin);
        echo "<table class=slbcell><tr><td rowspan=3>${dottedquad}";
        if (strlen ($ifname))
                echo '@' . $ifname;
        echo "</td>";
-       $ipv6 = new IPv6Address;
-       $ip_type = $ipv6->parse ($dottedquad) ? 'ipv6' : 'ipv4';
-       echo "<td><a href='index.php?page=object&object_id=${cell['id']}&hl_${ip_type}_addr=${dottedquad}'><strong>${cell['dname']}</strong></a></td>";
+       echo "<td><a href='index.php?page=object&object_id=${cell['id']}&hl_ip=${dottedquad}'><strong>${cell['dname']}</strong></a></td>";
        echo "</td></tr><tr><td>";
        printImageHREF ('router');
        echo "</td></tr><tr><td>";
@@ -6074,7 +5751,15 @@ function showPathAndSearch ($pageno)
                global $page;
                $path = array();
                // Recursion breaks at first parentless page.
-               if (!isset ($page[$targetno]['parent']))
+               if ($targetno == 'ipaddress')
+               {
+                       // case ipaddress is a universal v4/v6 page, it has two parents and requires special handling
+                       $ip_bin = ip_parse ($_REQUEST['ip']);
+                       $parent = (strlen ($ip_bin) == 16 ? 'ipv6net' : 'ipv4net');
+                       $path = $self ($parent);
+                       $path[] = $targetno;
+               }
+               elseif (!isset ($page[$targetno]['parent']))
                        $path = array ($targetno);
                else
                {
@@ -6180,7 +5865,7 @@ function showTabs ($pageno, $tabno)
 // fires for both row and its racks. Use pageno for decision in such cases.
 function dynamic_title_decoder ($path_position)
 {
-       global $sic;
+       global $sic, $page_by_realm;
        static $net_id;
        switch ($path_position)
        {
@@ -6274,14 +5959,7 @@ function dynamic_title_decoder ($path_position)
                        'params' => array ('file_id' => $_REQUEST['file_id'])
                );
        case 'ipaddress':
-               $address = getIPv4Address ($_REQUEST['ip']);
-               return array
-               (
-                       'name' => niftyString ($_REQUEST['ip'] . ($address['name'] != '' ? ' (' . $address['name'] . ')' : ''), 50, FALSE),
-                       'params' => array ('ip' => $_REQUEST['ip'])
-               );
-       case 'ipv6address':
-               $address = getIPv6Address (assertIPArg ('ip'));
+               $address = getIPAddress (ip_parse ($_REQUEST['ip']));
                return array
                (
                        'name' => niftyString ($address['ip'] . ($address['name'] != '' ? ' (' . $address['name'] . ')' : ''), 50, FALSE),
@@ -6293,48 +5971,48 @@ function dynamic_title_decoder ($path_position)
         switch ($pageno)
                {
                        case 'ipaddress':
-                               $range = spotEntity ('ipv4net', getIPv4AddressNetworkId ($_REQUEST['ip']));
-                               $net_id = $range['id'];
-                               return array
-                               (
-                                       'name' => $range['ip'] . '/' . $range['mask'],
-                                       'params' => array
-                                       (
-                                               'id' => $range['id'],
-                                               'page' => 'ipv4net',
-                                               'hl_ipv4_addr' => $_REQUEST['ip']
-                                       )
-                               );
-                       case 'ipv6address':
-                               $ipv6 = assertIPArg ('ip');
-                               $range = spotEntity ('ipv6net', getIPv6AddressNetworkId ($ipv6));
-                               $net_id = $range['id'];
-                               return array
+                               $net = spotNetworkByIP (ip_parse ($_REQUEST['ip']));
+                               $ret = array
                                (
-                                       'name' => $range['ip'] . '/' . $range['mask'],
+                                       'name' => $net['ip'] . '/' . $net['mask'],
                                        'params' => array
                                        (
-                                               'id' => $range['id'],
-                                               'page' => 'ipv6net',
-                                               'hl_ipv6_addr' => $_REQUEST['ip']
+                                               'id' => $net['id'],
+                                               'page' => $net['realm'], // 'ipv4net', 'ipv6net'
+                                               'hl_ip' => $_REQUEST['ip'],
                                        )
                                );
+                               return ($ret);
                        default:
                                assertUIntArg ('id');
-                               $range = spotEntity ($path_position, $_REQUEST['id']);
-                               $net_id = $range['id'];
+                               $net = spotEntity ($path_position, $_REQUEST['id']);
                                return array
                                (
-                                       'name' => $range['ip'] . '/' . $range['mask'],
-                                       'params' => array ('id' => $_REQUEST['id'])
+                                       'name' => $net['ip'] . '/' . $net['mask'],
+                                       'params' => array ('id' => $net['id'])
                                );
                }
+               break;
        case 'ipv4space':
        case 'ipv6space':
                global $pageno;
-               $ip_ver = preg_replace ('/[^\d]*/', '', $path_position);
-               $params = isset ($net_id) ? array ('eid' => $net_id, 'hl_net' => 1) : array();
+        switch ($pageno)
+               {
+                       case 'ipaddress':
+                               $net_id = getIPAddressNetworkId (ip_parse ($_REQUEST['ip']));
+                               break;
+                       case 'ipv4net':
+                       case 'ipv6net':
+                               $net_id = $_REQUEST['id'];
+                               break;
+                       default:
+                               $net_id = NULL;
+               }
+               $params = array();
+               if (isset ($net_id))
+                       $params = array ('eid' => $net_id, 'hl_net' => 1, 'clear-cf' => '');
                unset ($net_id);
+               $ip_ver = preg_replace ('/[^\d]*/', '', $path_position);
                return array
                (
                        'name' => "IPv$ip_ver space",
@@ -8554,9 +8232,9 @@ function formatVLANPackDiff ($old, $current)
        return $ret;
 }
 
-function renderIPv4AddressLog ()
+function renderIPAddressLog ()
 {
-       assertIPv4Arg('ip');
+       $ip_bin = assertIPArg ('ip');
        startPortlet ('Log messages');
        echo '<table class="widetable" cellspacing="0" cellpadding="5" align="center" width="50%"><tr>';
        echo '<th>Date &uarr;</th>';
@@ -8564,7 +8242,7 @@ function renderIPv4AddressLog ()
        echo '<th>Log message</th>';
        echo '</tr>';
        $odd = FALSE;
-       foreach (array_reverse (fetchIPv4LogEntry($_REQUEST['ip'])) as $line)
+       foreach (array_reverse (fetchIPLogEntry ($ip_bin)) as $line)
        {
                $tr_class = $odd ? 'row_odd' : 'row_even';
                echo "<tr class='$tr_class'>";
index d44f81c..fa20c9b 100644 (file)
@@ -104,15 +104,14 @@ $ophandler['rack']['files']['unlinkFile'] = 'unlinkFile';
 
 $page['object']['bypass'] = 'object_id';
 $page['object']['bypass_type'] = 'uint';
-$page['object']['bypass_tabs'] = array ('hl_port_id');
+$page['object']['bypass_tabs'] = array ('hl_port_id', 'hl_ip');
 $page['object']['parent'] = 'depot';
 $tab['object']['default'] = 'View';
 $tab['object']['edit'] = 'Properties';
 $tab['object']['log'] = 'Log';
 $tab['object']['rackspace'] = 'Rackspace';
 $tab['object']['ports'] = 'Ports';
-$tab['object']['ipv4'] = 'IPv4';
-$tab['object']['ipv6'] = 'IPv6';
+$tab['object']['ip'] = 'IP';
 $tab['object']['nat4'] = 'NATv4';
 $tab['object']['livevlans'] = 'Live VLANs';
 $tab['object']['liveports'] = 'Live ports';
@@ -133,8 +132,7 @@ $tabhandler['object']['edit'] = 'renderEditObjectForm';
 $tabhandler['object']['log'] = 'renderObjectLogEditor';
 $tabhandler['object']['rackspace'] = 'renderRackSpaceForObject';
 $tabhandler['object']['ports'] = 'renderPortsForObject';
-$tabhandler['object']['ipv4'] = 'renderIPv4ForObject';
-$tabhandler['object']['ipv6'] = 'renderIPv6ForObject';
+$tabhandler['object']['ip'] = 'renderIPForObject';
 $tabhandler['object']['nat4'] = 'renderNATv4ForObject';
 $tabhandler['object']['livevlans'] = 'renderVLANMembership';
 $tabhandler['object']['liveports'] = 'renderPortsInfo';
@@ -152,8 +150,7 @@ $tabhandler['object']['8021qsync'] = 'renderObject8021QSync';
 $tabhandler['object']['cacti'] = 'renderObjectCactiGraphs';
 $trigger['object']['rackspace'] = 'trigger_rackspace';
 $trigger['object']['ports'] = 'trigger_ports';
-$trigger['object']['ipv4'] = 'trigger_ipv4';
-$trigger['object']['ipv6'] = 'trigger_ipv6';
+$trigger['object']['ip'] = 'trigger_ip';
 $trigger['object']['nat4'] = 'trigger_natv4';
 $trigger['object']['livevlans'] = 'trigger_livevlans';
 $trigger['object']['liveports'] = 'trigger_liveports';
@@ -180,12 +177,9 @@ $ophandler['object']['ports']['useup'] = 'useupPort';
 $ophandler['object']['ports']['delPort'] = 'tableHandler';
 $ophandler['object']['ports']['deleteAll'] = 'tableHandler';
 $ophandler['object']['ports']['unlinkPort'] = 'unlinkPort';
-$ophandler['object']['ipv4']['updIPv4Allocation'] = 'updIPv4Allocation';
-$ophandler['object']['ipv4']['addIPv4Allocation'] = 'addIPv4Allocation';
-$ophandler['object']['ipv4']['delIPv4Allocation'] = 'delIPv4Allocation';
-$ophandler['object']['ipv6']['updIPv6Allocation'] = 'updIPv6Allocation';
-$ophandler['object']['ipv6']['addIPv6Allocation'] = 'addIPv6Allocation';
-$ophandler['object']['ipv6']['delIPv6Allocation'] = 'delIPv6Allocation';
+$ophandler['object']['ip']['upd'] = 'updIPAllocation';
+$ophandler['object']['ip']['add'] = 'addIPAllocation';
+$ophandler['object']['ip']['del'] = 'delIPAllocation';
 $ophandler['object']['edit']['clearSticker'] = 'clearSticker';
 $ophandler['object']['edit']['update'] = 'updateObject';
 $ophandler['object']['edit']['resetObject'] = 'resetObject';
@@ -225,9 +219,9 @@ $page['ipv4space']['parent'] = 'index';
 $tab['ipv4space']['default'] = 'Browse';
 $tab['ipv4space']['newrange'] = 'Add';
 $tab['ipv4space']['manage'] = 'Delete';
-$tabhandler['ipv4space']['default'] = 'renderIPv4Space';
+$tabhandler['ipv4space']['default'] = 'renderIPSpace';
 $tabhandler['ipv4space']['newrange'] = 'renderIPNewNetForm';
-$tabhandler['ipv4space']['manage'] = 'renderIPv4SpaceEditor';
+$tabhandler['ipv4space']['manage'] = 'renderIPSpaceEditor';
 $ophandler['ipv4space']['newrange']['add'] = 'addIPv4Prefix';
 $ophandler['ipv4space']['manage']['del'] = 'delIPv4Prefix';
 
@@ -235,9 +229,9 @@ $page['ipv6space']['parent'] = 'index';
 $tab['ipv6space']['default'] = 'Browse';
 $tab['ipv6space']['newrange'] = 'Add';
 $tab['ipv6space']['manage'] = 'Delete';
-$tabhandler['ipv6space']['default'] = 'renderIPv6Space';
+$tabhandler['ipv6space']['default'] = 'renderIPSpace';
 $tabhandler['ipv6space']['newrange'] = 'renderIPNewNetForm';
-$tabhandler['ipv6space']['manage'] = 'renderIPv6SpaceEditor';
+$tabhandler['ipv6space']['manage'] = 'renderIPSpaceEditor';
 $ophandler['ipv6space']['newrange']['add'] = 'addIPv6Prefix';
 $ophandler['ipv6space']['manage']['del'] = 'delIPv6Prefix';
 
@@ -250,7 +244,7 @@ $tab['ipv4net']['liveptr'] = 'Live PTR';
 $tab['ipv4net']['tags'] = 'Tags';
 $tab['ipv4net']['files'] = 'Files';
 $tab['ipv4net']['8021q'] = '802.1Q';
-$tabhandler['ipv4net']['default'] = 'renderIPv4Network';
+$tabhandler['ipv4net']['default'] = 'renderIPNetwork';
 $tabhandler['ipv4net']['properties'] = 'renderIPNetworkProperties';
 $tabhandler['ipv4net']['liveptr'] = 'renderLivePTR';
 $tabhandler['ipv4net']['tags'] = 'renderEntityTags';
@@ -276,7 +270,7 @@ $tab['ipv6net']['properties'] = 'Properties';
 $tab['ipv6net']['tags'] = 'Tags';
 $tab['ipv6net']['files'] = 'Files';
 $tab['ipv6net']['8021q'] = '802.1Q';
-$tabhandler['ipv6net']['default'] = 'renderIPv6Network';
+$tabhandler['ipv6net']['default'] = 'renderIPNetwork';
 $tabhandler['ipv6net']['properties'] = 'renderIPNetworkProperties';
 $tabhandler['ipv6net']['tags'] = 'renderEntityTags';
 $tabhandler['ipv6net']['files'] = 'renderFilesForEntity';
@@ -292,9 +286,9 @@ $ophandler['ipv6net']['files']['unlinkFile'] = 'unlinkFile';
 $ophandler['ipv6net']['8021q']['bind'] = 'bindVLANtoIPv6';
 $ophandler['ipv6net']['8021q']['unbind'] = 'unbindVLANfromIPv6';
 
-$page['ipaddress']['parent'] = 'ipv4net';
+//$page['ipaddress']['parent'] = 'ipnet'; - this is commened intensinally, there is a special hack in getPath
 $page['ipaddress']['bypass'] = 'ip';
-$page['ipaddress']['bypass_type'] = 'inet4';
+$page['ipaddress']['bypass_type'] = 'inet';
 $tab['ipaddress']['default'] = 'Browse';
 $tab['ipaddress']['properties'] = 'Properties';
 $tab['ipaddress']['assignment'] = 'Allocation';
@@ -302,26 +296,12 @@ $tab['ipaddress']['log'] = 'Change log';
 $tabhandler['ipaddress']['default'] = 'renderIPAddress';
 $tabhandler['ipaddress']['properties'] = 'renderIPAddressProperties';
 $tabhandler['ipaddress']['assignment'] = 'renderIPAddressAllocations';
-$tabhandler['ipaddress']['log'] = 'renderIPv4AddressLog';
-$trigger['ipaddress']['log'] = 'triggerIPv4AddressLog';
+$tabhandler['ipaddress']['log'] = 'renderIPAddressLog';
+$trigger['ipaddress']['log'] = 'triggerIPAddressLog';
 $ophandler['ipaddress']['properties']['editAddress'] = 'editAddress';
-$ophandler['ipaddress']['assignment']['delIPv4Allocation'] = 'delIPv4Allocation';
-$ophandler['ipaddress']['assignment']['updIPv4Allocation'] = 'updIPv4Allocation';
-$ophandler['ipaddress']['assignment']['addIPv4Allocation'] = 'addIPv4Allocation';
-
-$page['ipv6address']['parent'] = 'ipv6net';
-$page['ipv6address']['bypass'] = 'ip';
-$page['ipv6address']['bypass_type'] = 'string';
-$tab['ipv6address']['default'] = 'Browse';
-$tab['ipv6address']['properties'] = 'Properties';
-$tab['ipv6address']['assignment'] = 'Allocation';
-$tabhandler['ipv6address']['default'] = 'renderIPAddress';
-$tabhandler['ipv6address']['properties'] = 'renderIPAddressProperties';
-$tabhandler['ipv6address']['assignment'] = 'renderIPAddressAllocations';
-$ophandler['ipv6address']['properties']['editAddress'] = 'editv6Address';
-$ophandler['ipv6address']['assignment']['delIPv6Allocation'] = 'delIPv6Allocation';
-$ophandler['ipv6address']['assignment']['updIPv6Allocation'] = 'updIPv6Allocation';
-$ophandler['ipv6address']['assignment']['addIPv6Allocation'] = 'addIPv6Allocation';
+$ophandler['ipaddress']['assignment']['del'] = 'delIPAllocation';
+$ophandler['ipaddress']['assignment']['upd'] = 'updIPAllocation';
+$ophandler['ipaddress']['assignment']['add'] = 'addIPAllocation';
 
 $page['ipv4slb']['title'] = 'IPv4 SLB';
 $page['ipv4slb']['parent'] = 'index';
index 7d18aa3..667bfcf 100644 (file)
@@ -460,8 +460,8 @@ $msgcode['addPortForwarding']['OK'] = 48;
 function addPortForwarding ()
 {
        assertUIntArg ('object_id');
-       assertIPv4Arg ('localip');
-       assertIPv4Arg ('remoteip');
+       $localip_bin = assertIPv4Arg ('localip');
+       $remoteip_bin = assertIPv4Arg ('remoteip');
        assertUIntArg ('localport');
        assertStringArg ('proto');
        assertStringArg ('description', TRUE);
@@ -472,9 +472,9 @@ function addPortForwarding ()
        newPortForwarding
        (
                $_REQUEST['object_id'],
-               $_REQUEST['localip'],
+               $localip_bin,
                $_REQUEST['localport'],
-               $_REQUEST['remoteip'],
+               $remoteip_bin,
                $remoteport,
                $_REQUEST['proto'],
                $_REQUEST['description']
@@ -487,8 +487,8 @@ $msgcode['delPortForwarding']['OK'] = 49;
 function delPortForwarding ()
 {
        assertUIntArg ('object_id');
-       assertIPv4Arg ('localip');
-       assertIPv4Arg ('remoteip');
+       $localip_bin = assertIPv4Arg ('localip');
+       $remoteip_bin = assertIPv4Arg ('remoteip');
        assertUIntArg ('localport');
        assertUIntArg ('remoteport');
        assertStringArg ('proto');
@@ -496,9 +496,9 @@ function delPortForwarding ()
        deletePortForwarding
        (
                $_REQUEST['object_id'],
-               $_REQUEST['localip'],
+               $localip_bin,
                $_REQUEST['localport'],
-               $_REQUEST['remoteip'],
+               $remoteip_bin,
                $_REQUEST['remoteport'],
                $_REQUEST['proto']
        );
@@ -509,8 +509,8 @@ $msgcode['updPortForwarding']['OK'] = 51;
 function updPortForwarding ()
 {
        assertUIntArg ('object_id');
-       assertIPv4Arg ('localip');
-       assertIPv4Arg ('remoteip');
+       $localip_bin = assertIPv4Arg ('localip');
+       $remoteip_bin = assertIPv4Arg ('remoteip');
        assertUIntArg ('localport');
        assertUIntArg ('remoteport');
        assertStringArg ('proto');
@@ -519,9 +519,9 @@ function updPortForwarding ()
        updatePortForwarding
        (
                $_REQUEST['object_id'],
-               $_REQUEST['localip'],
+               $localip_bin,
                $_REQUEST['localport'],
-               $_REQUEST['remoteip'],
+               $remoteip_bin,
                $_REQUEST['remoteport'],
                $_REQUEST['proto'],
                $_REQUEST['description']
@@ -715,95 +715,42 @@ function addBulkPorts ()
        return showFuncMessage (__FUNCTION__, 'OK', array ($added_count, $error_count));
 }
 
-$msgcode['updIPv4Allocation']['OK'] = 51;
-function updIPv4Allocation ()
+$msgcode['updIPAllocation']['OK'] = 51;
+function updIPAllocation ()
 {
-       assertIPv4Arg ('ip');
+       $ip_bin = assertIPArg ('ip');
        assertUIntArg ('object_id');
        assertStringArg ('bond_name', TRUE);
-       genericAssertion ('bond_type', 'enum/inet4alloc');
-
-       updateBond ($_REQUEST['ip'], $_REQUEST['object_id'], $_REQUEST['bond_name'], $_REQUEST['bond_type']);
-       return showFuncMessage (__FUNCTION__, 'OK');
-}
-
-$msgcode['updIPv6Allocation']['OK'] = 51;
-function updIPv6Allocation ()
-{
-       $ipv6 = assertIPv6Arg ('ip');
-       assertUIntArg ('object_id');
-       assertStringArg ('bond_name', TRUE);
-       genericAssertion ('bond_type', 'enum/inet6alloc');
-
-       updateIPv6Bond ($ipv6, $_REQUEST['object_id'], $_REQUEST['bond_name'], $_REQUEST['bond_type']);
-       return showFuncMessage (__FUNCTION__, 'OK');
+       genericAssertion ('bond_type', 'enum/alloc_type');
+       updateIPBond ($ip_bin, $_REQUEST['object_id'], $_REQUEST['bond_name'], $_REQUEST['bond_type']);
+       showFuncMessage (__FUNCTION__, 'OK');
+       return buildRedirectURL (NULL, NULL, array ('hl_ip' => ip_format ($ip_bin)));
 }
 
-$msgcode['delIPv4Allocation']['OK'] = 49;
-function delIPv4Allocation ()
+$msgcode['delIPAllocation']['OK'] = 49;
+function delIPAllocation ()
 {
-       assertIPv4Arg ('ip');
+       $ip_bin = assertIPArg ('ip');
        assertUIntArg ('object_id');
 
-       unbindIpFromObject ($_REQUEST['ip'], $_REQUEST['object_id']);
-       return showFuncMessage (__FUNCTION__, 'OK');
-}
-
-$msgcode['delIPv6Allocation']['OK'] = 49;
-function delIPv6Allocation ()
-{
-       assertUIntArg ('object_id');
-       $ipv6 = assertIPv6Arg ('ip');
-       unbindIPv6FromObject ($ipv6, $_REQUEST['object_id']);
+       unbindIPFromObject ($ip_bin, $_REQUEST['object_id']);
        return showFuncMessage (__FUNCTION__, 'OK');
 }
 
-$msgcode['addIPv4Allocation']['OK'] = 48;
-$msgcode['addIPv4Allocation']['ERR1'] = 170;
-function addIPv4Allocation ()
+$msgcode['addIPAllocation']['OK'] = 48;
+$msgcode['addIPAllocation']['ERR1'] = 170;
+function addIPAllocation ()
 {
-       assertIPv4Arg ('ip');
+       $ip_bin = assertIPArg ('ip');
        assertUIntArg ('object_id');
        assertStringArg ('bond_name', TRUE);
-       genericAssertion ('bond_type', 'enum/inet4alloc');
+       genericAssertion ('bond_type', 'enum/alloc_type');
 
-       // Strip masklen.
-       $ip = preg_replace ('@/[[:digit:]]+$@', '', $_REQUEST['ip']);
-       if  (getConfigVar ('IPV4_JAYWALK') != 'yes' and NULL === getIPv4AddressNetworkId ($ip))
-               return showFuncMessage (__FUNCTION__, 'ERR1', array ($ip));
-       
-       bindIpToObject ($ip, $_REQUEST['object_id'], $_REQUEST['bond_name'], $_REQUEST['bond_type']);
-       $address = getIPv4Address ($ip);
-       if ($address['reserved'] == 'yes' or strlen ($address['name']))
-       {
-               $release = getConfigVar ('IPV4_AUTO_RELEASE');
-               if ($release >= 1)
-                       $address['reserved'] = 'no';
-               if ($release >= 2)
-                       $address['name'] = '';
-               updateAddress ($ip, $address['name'], $address['reserved']);
-       }
-       return showFuncMessage (__FUNCTION__, 'OK');
-}
+       if  (getConfigVar ('IPV4_JAYWALK') != 'yes' and NULL === getIPAddressNetworkId ($ip_bin))
+               return showFuncMessage (__FUNCTION__, 'ERR1', array (ip_format ($ip_bin)));
 
-$msgcode['addIPv6Allocation']['OK'] = 48;
-$msgcode['addIPv6Allocation']['ERR1'] = 170;
-function addIPv6Allocation ()
-{
-       assertUIntArg ('object_id');
-       assertStringArg ('bond_name', TRUE);
-       genericAssertion ('bond_type', 'enum/inet6alloc');
-
-       // Strip masklen.
-       $ipv6 = new IPv6Address;
-       if (! $ipv6->parse (preg_replace ('@/\d+$@', '', $_REQUEST['ip'])))
-               throw new InvalidRequestArgException('ip', $_REQUEST['ip'], 'parameter is not a valid ipv6 address');
-
-       if  (getConfigVar ('IPV4_JAYWALK') != 'yes' and NULL === getIPv6AddressNetworkId ($ipv6))
-               return showFuncMessage (__FUNCTION__, 'ERR1', array ($ip));
-
-       bindIPv6ToObject ($ipv6, $_REQUEST['object_id'], $_REQUEST['bond_name'], $_REQUEST['bond_type']);
-       $address = getIPv6Address ($ipv6);
+       bindIPToObject ($ip_bin, $_REQUEST['object_id'], $_REQUEST['bond_name'], $_REQUEST['bond_type']);
+       $address = getIPAddress ($ip_bin);
        if ($address['reserved'] == 'yes' or strlen ($address['name']))
        {
                $release = getConfigVar ('IPV4_AUTO_RELEASE');
@@ -811,9 +758,10 @@ function addIPv6Allocation ()
                        $address['reserved'] = 'no';
                if ($release >= 2)
                        $address['name'] = '';
-               updateAddress ($ipv6, $address['name'], $address['reserved']);
+               updateAddress ($ip_bin, $address['name'], $address['reserved']);
        }
-       return showFuncMessage (__FUNCTION__, 'OK');
+       showFuncMessage (__FUNCTION__, 'OK');
+       return buildRedirectURL (NULL, NULL, array ('hl_ip' => ip_format ($ip_bin)));
 }
 
 function addIPv4Prefix ()
@@ -822,10 +770,9 @@ function addIPv4Prefix ()
        assertStringArg ('name', TRUE);
 
        $taglist = isset ($_REQUEST['taglist']) ? $_REQUEST['taglist'] : array();
-       $is_connected = isset ($_REQUEST['is_connected']) ? $_REQUEST['is_connected'] : 'off';
        global $sic;
        $vlan_ck = empty ($sic['vlan_ck']) ? NULL : $sic['vlan_ck'];
-       $net_id = createIPv4Prefix ($_REQUEST['range'], $sic['name'], $is_connected == 'on', $taglist, $vlan_ck);
+       $net_id = createIPv4Prefix ($_REQUEST['range'], $sic['name'], isCheckSet ('is_connected'), $taglist, $vlan_ck);
        showSuccess
        (
                'IP network <a href="' .
@@ -840,10 +787,9 @@ function addIPv6Prefix ()
        assertStringArg ('name', TRUE);
 
        $taglist = isset ($_REQUEST['taglist']) ? $_REQUEST['taglist'] : array();
-       $is_connected = isset ($_REQUEST['is_connected']) ? ($_REQUEST['is_connected'] == 'on') : FALSE;
        global $sic;
        $vlan_ck = empty ($sic['vlan_ck']) ? NULL : $sic['vlan_ck'];
-       $net_id = createIPv6Prefix ($_REQUEST['range'], $sic['name'], $is_connected, $taglist, $vlan_ck);
+       $net_id = createIPv6Prefix ($_REQUEST['range'], $sic['name'], isCheckSet ('is_connected'), $taglist, $vlan_ck);
        showSuccess
        (
                'IP network <a href="' .
@@ -857,13 +803,14 @@ function delIPv4Prefix ()
 {
        assertUIntArg ('id');
        $netinfo = spotEntity ('ipv4net', $_REQUEST['id']);
-       loadIPv4AddrList ($netinfo);
+       loadIPAddrList ($netinfo);
        if (! isIPNetworkEmpty ($netinfo))
                return showError ("There are allocations within prefix, delete forbidden");
-       if (array_key_exists ($netinfo['db_first'], $netinfo['addrlist']))
-               updateAddress ($netinfo['addrlist'][$netinfo['db_first']]['ip'], '', 'no');
-       if (array_key_exists ($netinfo['db_last'], $netinfo['addrlist']))
-               updateAddress ($netinfo['addrlist'][$netinfo['db_last']]['ip'], '', 'no');
+       if (array_key_exists ($netinfo['ip_bin'], $netinfo['addrlist']))
+               updateV4Address ($netinfo['ip_bin'], '', 'no');
+       $last_ip = ip_last ($netinfo);
+       if (array_key_exists ($last_ip, $netinfo['addrlist']))
+               updateV4Address ($last_ip, '', 'no');
        destroyIPv4Prefix ($_REQUEST['id']);
        showFuncMessage (__FUNCTION__, 'OK');
        global $pageno;
@@ -876,11 +823,11 @@ function delIPv6Prefix ()
 {
        assertUIntArg ('id');
        $netinfo = spotEntity ('ipv6net', $_REQUEST['id']);
-       loadIPv6AddrList ($netinfo);
+       loadIPAddrList ($netinfo);
        if (! isIPNetworkEmpty ($netinfo))
                return showError ("There are allocations within prefix, delete forbidden");
-       if (array_key_exists ($netinfo['db_first']->getBin(), $netinfo['addrlist']))
-               updateAddress ($netinfo['db_first'], '', 'no');
+       if (array_key_exists ($netinfo['ip_bin'], $netinfo['addrlist']))
+               updateV6Address ($netinfo['ip_bin'], '', 'no');
        destroyIPv6Prefix ($_REQUEST['id']);
        showFuncMessage (__FUNCTION__, 'OK');
        global $pageno;
@@ -892,26 +839,8 @@ $msgcode['editAddress']['OK'] = 51;
 function editAddress ()
 {
        assertStringArg ('name', TRUE);
-
-       if (isset ($_REQUEST['reserved']))
-               $reserved = $_REQUEST['reserved'];
-       else
-               $reserved = 'off';
-       updateAddress ($_REQUEST['ip'], $_REQUEST['name'], $reserved == 'on' ? 'yes' : 'no');
-       return showFuncMessage (__FUNCTION__, 'OK');
-}
-
-$msgcode['editv6Address']['OK'] = 51;
-function editv6Address ()
-{
-       $ipv6 = assertIPArg ('ip');
-       assertStringArg ('name', TRUE);
-
-       if (isset ($_REQUEST['reserved']))
-               $reserved = $_REQUEST['reserved'];
-       else
-               $reserved = 'off';
-       updateAddress ($ipv6, $_REQUEST['name'], $reserved == 'on' ? 'yes' : 'no');
+       $ip_bin = assertIPArg ('ip');
+       updateAddress ($ip_bin, $_REQUEST['name'], isCheckSet ('reserved', 'yesno'));
        return showFuncMessage (__FUNCTION__, 'OK');
 }
 
@@ -923,9 +852,9 @@ function createUser ()
        assertStringArg ('password');
        $username = $_REQUEST['username'];
        $password = sha1 ($_REQUEST['password']);
-       commitCreateUserAccount ($username, $_REQUEST['realname'], $password);
+       $user_id = commitCreateUserAccount ($username, $_REQUEST['realname'], $password);
        if (isset ($_REQUEST['taglist']))
-               produceTagsForLastRecord ('user', $_REQUEST['taglist']);
+               produceTagsForNewRecord ('user', $_REQUEST['taglist'], $user_id);
        return showFuncMessage (__FUNCTION__, 'OK', array ($username));
 }
 
@@ -1125,10 +1054,6 @@ function updateObject ()
        genericAssertion ('object_asset_no', 'string0');
        genericAssertion ('object_comment', 'string0');
        genericAssertion ('object_type_id', 'uint');
-       if (array_key_exists ('object_has_problems', $_REQUEST) and $_REQUEST['object_has_problems'] == 'on')
-               $has_problems = 'yes';
-       else
-               $has_problems = 'no';
        $object_id = getBypassValue();
 
        global $dbxlink, $sic;
@@ -1138,7 +1063,7 @@ function updateObject ()
                $object_id,
                $_REQUEST['object_name'],
                $_REQUEST['object_label'],
-               $has_problems,
+               isCheckSet ('object_has_problems', 'yesno'),
                $_REQUEST['object_asset_no'],
                $_REQUEST['object_comment']
        );
@@ -1452,16 +1377,16 @@ $msgcode['addRealServer']['OK'] = 48;
 function addRealServer ()
 {
        global $sic;
-       assertIPv4Arg ('rsip');
+       $rsip_bin = assertIPv4Arg ('rsip');
        assertStringArg ('rsport', TRUE);
        assertStringArg ('rsconfig', TRUE);
        assertStringArg ('comment', TRUE);
        addRStoRSPool
        (
                getBypassValue(),
-               $_REQUEST['rsip'],
+               $rsip_bin,
                $_REQUEST['rsport'],
-               (isset ($_REQUEST['inservice']) and $_REQUEST['inservice'] == 'on') ? 'yes' : 'no',
+               isCheckSet ('inservice', 'yesno'),
                $sic['rsconfig'],
                $sic['comment']
        );
@@ -1487,22 +1412,22 @@ function addRealServers ()
                        case 'ipvs_2': // address and port only
                                if (!preg_match ('/^  -&gt; ([0-9\.]+):([0-9]+) /', $line, $match))
                                        continue;
-                               addRStoRSPool (getBypassValue(), $match[1], $match[2], getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), '');
+                               addRStoRSPool (getBypassValue(), ip4_parse ($match[1]), $match[2], getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), '');
                                break;
                        case 'ipvs_3': // address, port and weight
                                if (!preg_match ('/^  -&gt; ([0-9\.]+):([0-9]+) +[a-zA-Z]+ +([0-9]+) /', $line, $match))
                                        continue;
-                               addRStoRSPool (getBypassValue(), $match[1], $match[2], getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), 'weight ' . $match[3]);
+                               addRStoRSPool (getBypassValue(), ip4_parse ($match[1]), $match[2], getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), 'weight ' . $match[3]);
                                break;
                        case 'ssv_2': // IP address and port
                                if (!preg_match ('/^([0-9\.]+) ([0-9]+)$/', $line, $match))
                                        continue;
-                               addRStoRSPool (getBypassValue(), $match[1], $match[2], getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), '');
+                               addRStoRSPool (getBypassValue(), ip4_parse ($match[1]), $match[2], getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), '');
                                break;
                        case 'ssv_1': // IP address
                                if (!preg_match ('/^([0-9\.]+)$/', $line, $match))
                                        continue;
-                               addRStoRSPool (getBypassValue(), $match[1], 0, getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), '');
+                               addRStoRSPool (getBypassValue(), ip4_parse ($match[1]), 0, getConfigVar ('DEFAULT_IPV4_RS_INSERVICE'), '');
                                break;
                        default:
                                return showFuncMessage (__FUNCTION__, 'ERR1');
@@ -1516,7 +1441,7 @@ $msgcode['addVService']['OK'] = 48;
 function addVService ()
 {
        global $sic;
-       assertIPv4Arg ('vip');
+       $vip_bin = assertIPv4Arg ('vip');
        genericAssertion ('proto', 'enum/ipproto');
        assertStringArg ('name', TRUE);
        assertStringArg ('vsconfig', TRUE);
@@ -1528,20 +1453,22 @@ function addVService ()
                assertUIntArg ('vport');
                $vport = $_REQUEST['vport'];
        }
-       usePreparedExecuteBlade
+       usePreparedInsertBlade
        (
-               'INSERT INTO IPv4VS (vip, vport, proto, name, vsconfig, rsconfig) VALUES (INET_ATON(?), ?, ?, ?, ?, ?)',
+               'IPv4VS',
                array
                (
-                       $_REQUEST['vip'],
-                       $vport,
-                       $_REQUEST['proto'],
-                       !mb_strlen ($_REQUEST['name']) ? NULL : $_REQUEST['name'],
-                       !strlen ($sic['vsconfig']) ? NULL : $sic['vsconfig'],
-                       !strlen ($sic['rsconfig']) ? NULL : $sic['rsconfig'],
+                       'vip' => ip4_bin2db ($vip_bin),
+                       'vport' => $vport,
+                       'proto' => $_REQUEST['proto'],
+                       'name' => !mb_strlen ($_REQUEST['name']) ? NULL : $_REQUEST['name'],
+                       'vsconfig' => !strlen ($sic['vsconfig']) ? NULL : $sic['vsconfig'],
+                       'rsconfig' => !strlen ($sic['rsconfig']) ? NULL : $sic['rsconfig'],
                )
        );
-       produceTagsForLastRecord ('ipv4vs', isset ($_REQUEST['taglist']) ? $_REQUEST['taglist'] : array());
+       $vs_id = lastInsertID();
+       if (isset ($_REQUEST['taglist']))
+               produceTagsForNewRecord ('ipv4vs', $_REQUEST['taglist'], $vs_id);
        return showFuncMessage (__FUNCTION__, 'OK');
 }
 
@@ -1573,15 +1500,15 @@ function updateRealServer ()
 {
        global $sic;
        assertUIntArg ('rs_id');
-       assertIPv4Arg ('rsip');
+       $rsip_bin = assertIPv4Arg ('rsip');
        assertStringArg ('rsport', TRUE);
        assertStringArg ('rsconfig', TRUE);
        assertStringArg ('comment', TRUE);
        commitUpdateRS (
                $_REQUEST['rs_id'],
-               $_REQUEST['rsip'],
+               $rsip_bin,
                $_REQUEST['rsport'],
-               (isset ($_REQUEST['inservice']) and $_REQUEST['inservice'] == 'on') ? 'yes' : 'no',
+               isCheckSet ('inservice', 'yesno'),
                $sic['rsconfig'],
                $sic['comment']
        );
@@ -1593,7 +1520,7 @@ function updateVService ()
 {
        global $sic;
        assertUIntArg ('vs_id');
-       assertIPv4Arg ('vip');
+       $vip_bin = assertIPv4Arg ('vip');
        assertUIntArg ('vport');
        genericAssertion ('proto', 'enum/ipproto');
        assertStringArg ('name', TRUE);
@@ -1601,7 +1528,7 @@ function updateVService ()
        assertStringArg ('rsconfig', TRUE);
        commitUpdateVS (
                $_REQUEST['vs_id'],
-               $_REQUEST['vip'],
+               $vip_bin,
                $_REQUEST['vport'],
                $_REQUEST['proto'],
                $_REQUEST['name'],
@@ -1666,19 +1593,19 @@ function importPTRData ()
 {
        assertUIntArg ('addrcount');
        $nbad = $ngood = 0;
-       for ($i = 0; $i < $_REQUEST['addrcount']; $i++)
+       for ($i = 1; $i <= $_REQUEST['addrcount']; $i++)
        {
                $inputname = "import_${i}";
-               if (!isset ($_REQUEST[$inputname]) or $_REQUEST[$inputname] != 'on')
+               if (! isCheckSet ($inputname))
                        continue;
-               assertIPv4Arg ("addr_${i}");
+               $ip_bin = assertIPv4Arg ("addr_${i}");
                assertStringArg ("descr_${i}", TRUE);
                assertStringArg ("rsvd_${i}");
                // Non-existent addresses will not have this argument set in request.
                $rsvd = 'no';
                if ($_REQUEST["rsvd_${i}"] == 'yes')
                        $rsvd = 'yes';
-               if (updateAddress ($_REQUEST["addr_${i}"], $_REQUEST["descr_${i}"], $rsvd) == '')
+               if (updateV4Address ($ip_bin, $_REQUEST["descr_${i}"], $rsvd) == '')
                        $ngood++;
                else
                        $nbad++;
@@ -1859,7 +1786,7 @@ function addRack ()
                assertUIntArg ('height1');
                assertStringArg ('asset_no', TRUE);
                $rack_id = commitAddObject (NULL, $_REQUEST['name'], 1560, $_REQUEST['asset_no'], $taglist);
-               produceTagsForLastRecord ('rack', $taglist, $rack_id);
+               produceTagsForNewRecord ('rack', $taglist, $rack_id);
 
                // Update the height
                commitUpdateAttrValue ($rack_id, 27, $_REQUEST['height1']);
@@ -1888,7 +1815,7 @@ function addRack ()
                foreach ($names2 as $cname)
                {
                        $rack_id = commitAddObject (NULL, $cname, 1560, NULL, $taglist);
-                       produceTagsForLastRecord ('rack', $taglist, $rack_id);
+                       produceTagsForNewRecord ('rack', $taglist, $rack_id);
 
                        // Update the height
                        commitUpdateAttrValue ($rack_id, 27, $_REQUEST['height2']);
@@ -1921,13 +1848,21 @@ function updateRack ()
        assertUIntArg ('row_id');
        assertStringArg ('name');
        assertUIntArg ('height');
-       $has_problems = (isset ($_REQUEST['has_problems']) and $_REQUEST['has_problems'] == 'on') ? 'yes' : 'no';
        assertStringArg ('asset_no', TRUE);
        assertStringArg ('comment', TRUE);
 
        $rack_id = getBypassValue();
        usePreparedDeleteBlade ('RackThumbnail', array ('rack_id' => $rack_id));
-       commitUpdateRack ($rack_id, $_REQUEST['row_id'], $_REQUEST['name'], $_REQUEST['height'], $has_problems, $_REQUEST['asset_no'], $_REQUEST['comment']);
+       commitUpdateRack
+       (
+               $rack_id,
+               $_REQUEST['row_id'],
+               $_REQUEST['name'],
+               $_REQUEST['height'],
+               isCheckSet ('has_problems', 'yesno'),
+               $_REQUEST['asset_no'],
+               $_REQUEST['comment']
+       );
 
        // Update optional attributes
        $oldvalues = getAttrValues ($rack_id);
@@ -2069,9 +2004,9 @@ function addFileWithoutLink ()
 
        $fp = fopen($_FILES['file']['tmp_name'], 'rb');
        global $sic;
-       commitAddFile ($_FILES['file']['name'], $_FILES['file']['type'], $fp, $sic['comment']);
+       $file_id = commitAddFile ($_FILES['file']['name'], $_FILES['file']['type'], $fp, $sic['comment']);
        if (isset ($_REQUEST['taglist']))
-               produceTagsForLastRecord ('file', $_REQUEST['taglist']);
+               produceTagsForNewRecord ('file', $_REQUEST['taglist'], $file_id);
        return showFuncMessage (__FUNCTION__, 'OK', array (htmlspecialchars ($_FILES['file']['name'])));
 }
 
@@ -2806,7 +2741,7 @@ function cloneRSPool()
                $tagidlist[] = $taginfo['id'];
        $new_id = commitCreateRSPool ($pool['name'] . ' (copy)', $pool['vsconfig'], $pool['rsconfig'], $tagidlist);
        foreach ($rs_list as $rs)
-               addRStoRSPool ($new_id, $rs['rsip'], $rs['rsport'], $rs['inservice'], $rs['rsconfig'], $rs['comment']);
+               addRStoRSPool ($new_id, ip4_parse ($rs['rsip']), $rs['rsport'], $rs['inservice'], $rs['rsconfig'], $rs['comment']);
        showSuccess ("Created a copy of pool <a href='" . makeHref (array ('page' => 'ipv4rspool', 'tab' => 'default', 'pool_id' => $pool['id'])) . "'>${pool['name']}</a>");
        return buildRedirectURL ('ipv4rspool', 'default', array ('pool_id' => $new_id));
 }
index 0f15ab5..ccb2546 100644 (file)
@@ -106,7 +106,7 @@ function renderNewSLBItemForm ($realm1, $realm2)
 // supports object, ipv4vs, ipv4rspool, ipaddress cell types
 function renderSLBTriplets ($cell)
 {
-       $is_cell_ip = (isset ($cell['ip']) and isset ($cell['version']));
+       $is_cell_ip = (isset ($cell['ip']) && isset ($cell['vslist']));
        $additional_js_params = $is_cell_ip ? '' : ", {'" . $cell['realm'] . "': " . $cell['id'] . '}';
        $triplets = SLBTriplet::getTriplets ($cell);
        if (count ($triplets))
index c5b549f..0c3ad1c 100644 (file)
@@ -47,7 +47,7 @@ class SLBTriplet
 
        public static function getTriplets ($cell)
        {
-               if (isset ($cell['ip']) and isset ($cell['version']) and $cell['version'] == 4)
+               if (isset ($cell['ip']) and isset ($cell['vslist']))
                        return self::getTripletsByIP ($cell['ip']);
                $ret = array();
                switch ($cell['realm'])
@@ -90,7 +90,7 @@ class SLBTriplet
        private static function getTripletsByIP ($ip)
        {
                $ret = array();
-               $bin_ip = ip_quad2long ($ip);
+               $db_ip = ip4_bin2db (ip4_parse($ip));
                $result = usePreparedSelectBlade ("
 SELECT DISTINCT IPv4LB.* 
 FROM 
@@ -100,7 +100,7 @@ WHERE
        rsip = ? OR vip = ?
 ORDER BY
        vs_id
-       ", array ($bin_ip, $bin_ip)
+       ", array ($db_ip, $db_ip)
                );
                $rows = $result->fetchAll (PDO::FETCH_ASSOC);
                unset ($result);
@@ -136,7 +136,7 @@ ORDER BY
                if ($this->vs['proto'] == 'MARK')
                {
                        $parser->addMacro ('PROTO', 'TCP');
-                       $mark = ip_quad2long ($this->vs['vip']);
+                       $mark = ip4_bin2db (ip4_parse ($this->vs['vip']));
                        $parser->addMacro ('MARK', $mark);
                        $parser->addMacro ('VS_HEADER', "fwmark $mark");
                }
@@ -357,19 +357,19 @@ function getIPv4RSPoolOptions ()
        return $ret;
 }
 
-function addRStoRSPool ($pool_id = 0, $rsip = '', $rsport = 0, $inservice = 'no', $rsconfig = '', $comment = '')
+function addRStoRSPool ($pool_id, $rsip_bin, $rsport = 0, $inservice = 'no', $rsconfig = '', $comment = '')
 {
-       return usePreparedExecuteBlade
+       return usePreparedInsertBlade
        (
-               'INSERT INTO IPv4RS (rsip, rsport, rspool_id, inservice, rsconfig, comment) VALUES (INET_ATON(?), ?, ?, ?, ?, ?)',
+               'IPv4RS',
                array
                (
-                       $rsip,
-                       (!strlen ($rsport) or $rsport === 0) ? NULL : $rsport,
-                       $pool_id,
-                       $inservice == 'yes' ? 'yes' : 'no',
-                       !strlen ($rsconfig) ? NULL : $rsconfig,
-                       !strlen ($comment) ? NULL : $comment,
+                       'rspool_id' => $pool_id,
+                       'rsip' => ip4_bin2db ($rsip_bin),
+                       'rsport' => (!strlen ($rsport) or $rsport === 0) ? NULL : $rsport,
+                       'inservice' => $inservice == 'yes' ? 'yes' : 'no', 
+                       'rsconfig' => !strlen ($rsconfig) ? NULL : $rsconfig,
+                       'comment' => !strlen ($comment) ? NULL : $comment,
                )
        );
 }
@@ -398,16 +398,14 @@ function commitDeleteVS ($id = 0)
        usePreparedDeleteBlade ('IPv4VS', array ('id' => $id));
 }
 
-function commitUpdateRS ($rsid = 0, $rsip = '', $rsport = 0, $inservice = 'yes', $rsconfig = '', $comment = '')
+function commitUpdateRS ($rsid, $rsip_bin, $rsport = 0, $inservice = 'yes', $rsconfig = '', $comment = '')
 {
-       if (long2ip (ip2long ($rsip)) !== $rsip)
-               throw new InvalidArgException ('$rsip', $rsip);
        usePreparedExecuteBlade
        (
-               'UPDATE IPv4RS SET rsip=INET_ATON(?), rsport=?, inservice=?, rsconfig=?, comment=? WHERE id=?',
+               'UPDATE IPv4RS SET rsip=?, rsport=?, inservice=?, rsconfig=?, comment=? WHERE id=?',
                array
                (
-                       $rsip,
+                       ip4_bin2db ($rsip_bin),
                        (!strlen ($rsport) or $rsport === 0) ? NULL : $rsport,
                        $inservice,
                        !strlen ($rsconfig) ? NULL : $rsconfig,
@@ -417,27 +415,25 @@ function commitUpdateRS ($rsid = 0, $rsip = '', $rsport = 0, $inservice = 'yes',
        );
 }
 
-function commitUpdateVS ($vsid = 0, $vip = '', $vport = 0, $proto = '', $name = '', $vsconfig = '', $rsconfig = '')
+function commitUpdateVS ($vsid, $vip_bin, $vport = 0, $proto = '', $name = '', $vsconfig = '', $rsconfig = '')
 {
-       if (!strlen ($vip))
-               throw new InvalidArgException ('$vip', $vip);
        if ($vport <= 0)
                throw new InvalidArgException ('$vport', $vport);
        if (!strlen ($proto))
                throw new InvalidArgException ('$proto', $proto);
-       usePreparedExecuteBlade
+       return usePreparedUpdateBlade
        (
-               'UPDATE IPv4VS SET vip=INET_ATON(?), vport=?, proto=?, name=?, vsconfig=?, rsconfig=? WHERE id=?',
+               'IPv4VS',
                array
                (
-                       $vip,
-                       $vport,
-                       $proto,
-                       !strlen ($name) ? NULL : $name,
-                       !strlen ($vsconfig) ? NULL : $vsconfig,
-                       !strlen ($rsconfig) ? NULL : $rsconfig,
-                       $vsid,
-               )
+                       'vip' => ip4_bin2db ($vip_bin),
+                       'vport' => $vport,
+                       'proto' => $proto,
+                       'name' => !strlen ($name) ? NULL : $name,
+                       'vsconfig' => !strlen ($vsconfig) ? NULL : $vsconfig,
+                       'rsconfig' => !strlen ($rsconfig) ? NULL : $rsconfig,
+               ),
+               array ('id' => $vsid)
        );
 }
 
@@ -455,7 +451,7 @@ function commitCreateRSPool ($name = '', $vsconfig = '', $rsconfig = '', $tagidl
                )
        ))
                $new_pool_id = lastInsertID();
-       produceTagsForLastRecord ('ipv4rspool', $tagidlist, $new_pool_id);
+       produceTagsForNewRecord ('ipv4rspool', $tagidlist, $new_pool_id);
        return $new_pool_id;
 }
 
index e2497dc..413e773 100644 (file)
@@ -207,6 +207,67 @@ function renderProgressBarImage ($done)
        imagedestroy ($img);
 }
 
+function renderProgressBar4Image ($px1, $px2, $px3)
+{
+       $width = 100;
+       $height = 10;
+       $img = @imagecreatetruecolor ($width, 10);
+       $offsets = array ($px1, $px2, $px3, $width - $px1 - $px2 - $px3);
+       $colors = array
+       (
+               colorFromHex ($img, '408080'),
+               colorFromHex ($img, '8fbfbf'),
+               colorFromHex ($img, '808080'),
+               colorFromHex ($img, 'c0c0c0'),
+       );
+       $pos =  0;
+       for ($i = 0; $i < count ($offsets); $i++)
+       {
+               $off = $offsets[$i];
+               $clr = $colors[$i];
+               if ($pos + $off > $width or $off < 0)
+                       throw new InvalidArgException ('px' . $i, $offsets[$i]);
+               if ($off > 0)
+                       imagefilledrectangle ($img, $pos, 0, $pos + $off, $height, $clr);
+               $pos += $off;
+       }
+
+       for ($x = $width / 5; $x < $width; $x += $width / 5)
+       {
+               $p = 0; $k = count ($offsets) - 1;
+               for ($j = 0; $j < count ($offsets); $j++)
+                       if ($x < ($p += $offsets[$j]))
+                       {
+                               $k = $j;
+                               break;
+                       }
+               switch ($k)
+               {
+                       case 0:
+                               $cc = 1;
+                               break;
+                       case 1:
+                               $cc = 0;
+                               break;
+                       case 2:
+                               $cc = 3;
+                               break;
+                       case 3:
+                               $cc = 2;
+                               break;
+               }
+               imagesetpixel ($img, $x, 0, $colors[$cc]);
+               imagesetpixel ($img, $x, 1, $colors[$cc]);
+               imagesetpixel ($img, $x, 4, $colors[$cc]);
+               imagesetpixel ($img, $x, 5, $colors[$cc]);
+               imagesetpixel ($img, $x, 8, $colors[$cc]);
+               imagesetpixel ($img, $x, 9, $colors[$cc]);
+       }
+       header("Content-type: image/png");
+       imagepng ($img);
+       imagedestroy ($img);
+}
+
 function renderProgressBarError()
 {
        header ('Content-type: image/png');
index d92047a..4fe7680 100644 (file)
@@ -134,19 +134,10 @@ function trigger_isloadbalancer ()
        return considerConfiguredConstraint (spotEntity ('object', $_REQUEST['object_id']), 'IPV4LB_LISTSRC') ? 'std' : '';
 }
 
-function trigger_ipv4 ()
+function trigger_ip ()
 {
        assertUIntArg ('object_id');