* luci/libs: added broadcast(), minhost() and maxhost() to luci.ip, allow various datatypes as add() and sub() operands, extended add() and sub() to modify data inplace when flag is set

This commit is contained in:
Jo-Philipp Wich 2008-08-09 01:06:28 +00:00
parent d1998ce924
commit 648126f6d0

View file

@ -41,21 +41,49 @@ local function __bless(x)
} ) } )
end end
local function __mask16(bits) local function __array16( x, family )
return bit.lshift( local list
bit.rshift( 0xFFFF, 16 - bits % 16 ),
16 - bits % 16 if type(x) == "number" then
) list = { bit.rshift(x, 16), bit.band(x, 0xFFFF) }
elseif type(x) == "string" then
if x:find(":") then x = IPv6(x) else x = IPv4(x) end
if x then
assert( x[1] == family, "Can't mix IPv4 and IPv6 addresses" )
list = { unpack(x[2]) }
end end
local function __length(family) elseif type(x) == "table" and type(x[2]) == "table" then
if family == FAMILY_INET4 then assert( x[1] == family, "Can't mix IPv4 and IPv6 addresses" )
return 32 list = { unpack(x[2]) }
else
return 128 elseif type(x) == "table" then
list = x
end end
assert( list, "Invalid operand" )
return list
end end
local function __mask16(bits)
return bit.lshift( bit.rshift( 0xFFFF, 16 - bits % 16 ), 16 - bits % 16 )
end
local function __not16(bits)
return bit.band( bit.bnot( __mask16(bits) ), 0xFFFF )
end
local function __maxlen(family)
return ( family == FAMILY_INET4 ) and 32 or 128
end
local function __sublen(family)
return ( family == FAMILY_INET4 ) and 30 or 127
end
--- Convert given short value to network byte order on little endian hosts --- Convert given short value to network byte order on little endian hosts
-- @param x Unsigned integer value between 0x0000 and 0xFFFF -- @param x Unsigned integer value between 0x0000 and 0xFFFF
-- @return Byte-swapped value -- @return Byte-swapped value
@ -255,9 +283,9 @@ end
function Hex( hex, prefix, family, swap ) function Hex( hex, prefix, family, swap )
family = ( family ~= nil ) and family or FAMILY_INET4 family = ( family ~= nil ) and family or FAMILY_INET4
swap = ( swap == nil ) and true or swap swap = ( swap == nil ) and true or swap
prefix = prefix or __length(family) prefix = prefix or __maxlen(family)
local len = __length(family) local len = __maxlen(family)
local tmp = "" local tmp = ""
local data = { } local data = { }
@ -417,6 +445,7 @@ end
-- @param bits Override prefix length of this instance (optional) -- @param bits Override prefix length of this instance (optional)
-- @return CIDR instance containing the network address -- @return CIDR instance containing the network address
-- @see host -- @see host
-- @see broadcast
-- @see mask -- @see mask
function cidr.network( self, bits ) function cidr.network( self, bits )
local data = { } local data = { }
@ -434,16 +463,17 @@ function cidr.network( self, bits )
end end
end end
return __bless({ self[1], data, __length(self[1]) }) return __bless({ self[1], data, __maxlen(self[1]) })
end end
--- Return a corresponding CIDR representing the host address of this --- Return a corresponding CIDR representing the host address of this
-- instance. This is intended to extract the host address from larger subnet. -- instance. This is intended to extract the host address from larger subnet.
-- @return CIDR instance containing the network address -- @return CIDR instance containing the network address
-- @see network -- @see network
-- @see broadcast
-- @see mask -- @see mask
function cidr.host( self ) function cidr.host( self )
return __bless({ self[1], data, __length(self[1]) }) return __bless({ self[1], data, __maxlen(self[1]) })
end end
--- Return a corresponding CIDR representing the netmask of this instance. --- Return a corresponding CIDR representing the netmask of this instance.
@ -451,6 +481,7 @@ end
-- @return CIDR instance containing the netmask -- @return CIDR instance containing the netmask
-- @see network -- @see network
-- @see host -- @see host
-- @see broadcast
function cidr.mask( self, bits ) function cidr.mask( self, bits )
local data = { } local data = { }
bits = bits or self[3] bits = bits or self[3]
@ -467,7 +498,28 @@ function cidr.mask( self, bits )
end end
end end
return __bless({ self[1], data, __length(self[1]) }) return __bless({ self[1], data, __maxlen(self[1]) })
end
--- Return CIDR containing the broadcast address of this instance.
-- @param bits Override prefix length of this instance (optional)
-- @return CIDR instance containing the netmask, always nil for IPv6
-- @see network
-- @see host
-- @see mask
function cidr.broadcast( self )
-- IPv6 has no broadcast addresses (XXX: assert() instead?)
if self[1] == FAMILY_INET4 then
local data = { unpack(self[2]) }
local offset = math.floor( self[3] / 16 ) + 1
if offset <= #data then
data[offset] = bit.bor( data[offset], __not16(self[3]) )
for i = offset + 1, #data do data[i] = 0xFFFF end
return __bless({ self[1], data, __maxlen(self[1]) })
end
end
end end
--- Test whether this instance fully contains the given CIDR instance. --- Test whether this instance fully contains the given CIDR instance.
@ -485,12 +537,14 @@ end
--- Add specified amount of hosts to this instance. --- Add specified amount of hosts to this instance.
-- @param amount Number of hosts to add to this instance -- @param amount Number of hosts to add to this instance
-- @param inplace Boolen indicating whether to alter values inplace (optional)
-- @return CIDR representing the new address or nil on overflow error -- @return CIDR representing the new address or nil on overflow error
-- @see sub -- @see sub
function cidr.add( self, amount ) function cidr.add( self, amount, inplace )
local shorts = { bit.rshift(amount, 16), bit.band(amount, 0xFFFF) }
local data = { unpack(self[2]) } local data = { unpack(self[2]) }
local shorts = __array16( amount, self[1] )
if shorts then
for pos = #data, 1, -1 do for pos = #data, 1, -1 do
local add = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0 local add = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0
if ( data[pos] + add ) > 0xFFFF then if ( data[pos] + add ) > 0xFFFF then
@ -504,18 +558,26 @@ function cidr.add( self, amount )
data[pos] = data[pos] + add data[pos] = data[pos] + add
end end
end end
end
if inplace then
self[2] = data
return self
else
return __bless({ self[1], data, self[3] }) return __bless({ self[1], data, self[3] })
end end
end
--- Substract specified amount of hosts from this instance. --- Substract specified amount of hosts from this instance.
-- @param amount Number of hosts to substract from this instance -- @param amount Number of hosts to substract from this instance
-- @param inplace Boolen indicating whether to alter values inplace (optional)
-- @return CIDR representing the new address or nil on underflow error -- @return CIDR representing the new address or nil on underflow error
-- @see add -- @see add
function cidr.sub( self, amount ) function cidr.sub( self, amount, inplace )
local shorts = { bit.rshift(amount, 16), bit.band(amount, 0xFFFF) }
local data = { unpack(self[2]) } local data = { unpack(self[2]) }
local shorts = __array16( amount, self[1] )
if shorts then
for pos = #data, 1, -1 do for pos = #data, 1, -1 do
local sub = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0 local sub = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0
if ( data[pos] - sub ) < 0 then if ( data[pos] - sub ) < 0 then
@ -529,6 +591,41 @@ function cidr.sub( self, amount )
data[pos] = data[pos] - sub data[pos] = data[pos] - sub
end end
end end
end
if inplace then
self[2] = data
return self
else
return __bless({ self[1], data, self[3] }) return __bless({ self[1], data, self[3] })
end end
end
--- Return CIDR containing the lowest available host address within this subnet.
-- @return CIDR containing the host address, nil if subnet is too small
-- @see maxhost
function cidr.minhost( self )
if self[3] <= __sublen(self[1]) then
-- 1st is Network Address in IPv4 and Subnet-Router Anycast Adresse in IPv6
return self:network():add(1, true)
end
end
--- Return CIDR containing the highest available host address within the subnet.
-- @return CIDR containing the host address, nil if subnet is too small
-- @see minhost
function cidr.maxhost( self )
if self[3] <= __sublen(self[1]) then
local data = { unpack(self[2]) }
local offset = math.floor( self[3] / 16 ) + 1
data[offset] = bit.bor( data[offset], __not16(self[3]) )
for i = offset + 1, #data do data[i] = 0xFFFF end
data = __bless({ self[1], data, __maxlen(self[1]) })
-- Last address in reserved for Broadcast Address in IPv4
if data[1] == FAMILY_INET4 then data:sub(1, true) end
return data
end
end