--- LuCI IP calculation library.
module( "luci.ip", package.seeall )
-require("bit")
-require("luci.util")
+require "nixio"
+local bit = nixio.bit
+local util = require "luci.util"
--- Boolean; true if system is little endian
-LITTLE_ENDIAN = not luci.util.bigendian()
+LITTLE_ENDIAN = not util.bigendian()
--- Boolean; true if system is big endian
BIG_ENDIAN = not LITTLE_ENDIAN
list = { unpack(x[2]) }
elseif type(x) == "table" then
- list = x
+ list = { unpack(x) }
end
assert( list, "Invalid operand" )
local data = {}
local prefix = address:match("/(.+)")
address = address:gsub("/.+","")
+ address = address:gsub("^%[(.*)%]$", "%1"):upper():gsub("^::FFFF:", "")
if netmask then
prefix = obj:prefix(netmask)
local data = {}
local prefix = address:match("/(.+)")
address = address:gsub("/.+","")
+ address = address:gsub("^%[(.*)%]$", "%1")
if netmask then
prefix = obj:prefix(netmask)
end
local borderl = address:sub(1, 1) == ":" and 2 or 1
- local borderh, zeroh, chunk, block
+ local borderh, zeroh, chunk, block, i
if #address > 45 then return nil end
block = tonumber(address:sub(borderl, borderh - 1), 16)
if block and block <= 0xFFFF then
- table.insert(data, block)
+ data[#data+1] = block
else
if zeroh or borderh - borderl > 1 then return nil end
zeroh = #data + 1
block = tonumber(chunk, 16)
if not block or block > 0xFFFF then return nil end
- table.insert(data, block)
+ data[#data+1] = block
elseif #chunk > 4 then
if #data == 7 or #chunk > 15 then return nil end
borderl = 1
if not block or block > 255 then return nil end
if i == 1 or i == 3 then
- table.insert(data, block * 256)
+ data[#data+1] = block * 256
else
data[#data] = data[#data] + block
end
local len = __maxlen(family)
local tmp = ""
local data = { }
+ local i
for i = 1, (len/4) - #hex do tmp = tmp .. '0' end
for i = 1, ( len / 4 ), 4 do
local n = tonumber( hex:sub( i, i+3 ), 16 )
if n then
- table.insert( data, n )
+ data[#data+1] = n
else
return nil
end
end
- return __bless({ family, data, len })
+ return __bless({ family, data, prefix })
end
-- @class module
-- @cstyle instance
-- @name luci.ip.cidr
-cidr = luci.util.class()
+cidr = util.class()
--- Test whether the instance is a IPv4 address.
-- @return Boolean indicating a IPv4 address type
return self[1] == FAMILY_INET4
end
+--- Test whether this instance is an IPv4 RFC1918 private address
+-- @return Boolean indicating whether this instance is an RFC1918 address
+function cidr.is4rfc1918( self )
+ if self[1] == FAMILY_INET4 then
+ return ((self[2][1] >= 0x0A00) and (self[2][1] <= 0x0AFF)) or
+ ((self[2][1] >= 0xAC10) and (self[2][1] <= 0xAC1F)) or
+ (self[2][1] == 0xC0A8)
+ end
+ return false
+end
+
+--- Test whether this instance is an IPv4 link-local address (Zeroconf)
+-- @return Boolean indicating whether this instance is IPv4 link-local
+function cidr.is4linklocal( self )
+ if self[1] == FAMILY_INET4 then
+ return (self[2][1] == 0xA9FE)
+ end
+ return false
+end
+
--- Test whether the instance is a IPv6 address.
-- @return Boolean indicating a IPv6 address type
-- @see cidr.is4
return self[1] == FAMILY_INET6
end
+--- Test whether this instance is an IPv6 link-local address
+-- @return Boolean indicating whether this instance is IPv6 link-local
+function cidr.is6linklocal( self )
+ if self[1] == FAMILY_INET6 then
+ return (self[2][1] >= 0xFE80) and (self[2][1] <= 0xFEBF)
+ end
+ return false
+end
+
--- Return a corresponding string representation of the instance.
-- If the prefix length is lower then the maximum possible prefix length for the
-- corresponding address type then the address is returned in CIDR notation,
-- @see cidr.equal
function cidr.lower( self, addr )
assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
+ local i
for i = 1, #self[2] do
if self[2][i] ~= addr[2][i] then
return self[2][i] < addr[2][i]
-- @see cidr.equal
function cidr.higher( self, addr )
assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
+ local i
for i = 1, #self[2] do
if self[2][i] ~= addr[2][i] then
return self[2][i] > addr[2][i]
-- @see cidr.higher
function cidr.equal( self, addr )
assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
+ local i
for i = 1, #self[2] do
if self[2][i] ~= addr[2][i] then
return false
end
--- Return the prefix length of this CIDR instance.
--- @return Prefix length in bit
+-- @param mask Override instance prefix with given netmask (optional)
+-- @return Prefix length in bit
function cidr.prefix( self, mask )
local prefix = self[3]
if mask then
prefix = 0
+
local stop = false
- local obj = self:is4() and IPv4(mask) or IPv6(mask)
+ local obj = type(mask) ~= "table"
+ and ( self:is4() and IPv4(mask) or IPv6(mask) ) or mask
- if not obj then
- return nil
- end
+ if not obj then return nil end
- for i, block in ipairs(obj[2]) do
- local pos = bit.lshift(1, 15)
- for i=15, 0, -1 do
- if bit.band(block, pos) == pos then
- if not stop then
- prefix = prefix + 1
- else
- return nil
- end
- else
- stop = true
+ local _, word
+ for _, word in ipairs(obj[2]) do
+ if word == 0xFFFF then
+ prefix = prefix + 16
+ else
+ local bitmask = bit.lshift(1, 15)
+ while bit.band(word, bitmask) == bitmask do
+ prefix = prefix + 1
+ bitmask = bit.lshift(1, 15 - (prefix % 16))
end
- pos = bit.rshift(pos, 1)
+
+ break
end
end
end
local data = { }
bits = bits or self[3]
+ local i
for i = 1, math.floor( bits / 16 ) do
- table.insert( data, self[2][i] )
+ data[#data+1] = self[2][i]
end
if #data < #self[2] then
- table.insert( data, bit.band( self[2][1+#data], __mask16(bits) ) )
+ data[#data+1] = bit.band( self[2][1+#data], __mask16(bits) )
for i = #data + 1, #self[2] do
- table.insert( data, 0 )
+ data[#data+1] = 0
end
end
-- @see cidr.broadcast
-- @see cidr.mask
function cidr.host( self )
- return __bless({ self[1], data, __maxlen(self[1]) })
+ return __bless({ self[1], self[2], __maxlen(self[1]) })
end
--- Return a corresponding CIDR representing the netmask of this instance.
bits = bits or self[3]
for i = 1, math.floor( bits / 16 ) do
- table.insert( data, 0xFFFF )
+ data[#data+1] = 0xFFFF
end
if #data < #self[2] then
- table.insert( data, __mask16(bits) )
+ data[#data+1] = __mask16(bits)
for i = #data + 1, #self[2] do
- table.insert( data, 0 )
+ data[#data+1] = 0
end
end
-- @return CIDR representing the new address or nil on overflow error
-- @see cidr.sub
function cidr.add( self, amount, inplace )
+ local pos
local data = { unpack(self[2]) }
local shorts = __array16( amount, self[1] )
- if shorts then
- for pos = #data, 1, -1 do
- local add = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0
- if ( data[pos] + add ) > 0xFFFF then
- data[pos] = ( data[pos] + add ) % 0xFFFF
- if pos > 1 then
- data[pos-1] = data[pos-1] + ( add - data[pos] )
- else
- return nil
- end
+ for pos = #data, 1, -1 do
+ local add = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0
+ if ( data[pos] + add ) > 0xFFFF then
+ data[pos] = ( data[pos] + add ) % 0xFFFF
+ if pos > 1 then
+ data[pos-1] = data[pos-1] + ( add - data[pos] )
else
- data[pos] = data[pos] + add
+ return nil
end
+ else
+ data[pos] = data[pos] + add
end
end
-- @return CIDR representing the new address or nil on underflow error
-- @see cidr.add
function cidr.sub( self, amount, inplace )
+ local pos
local data = { unpack(self[2]) }
local shorts = __array16( amount, self[1] )
- if shorts then
- for pos = #data, 1, -1 do
- local sub = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0
- if ( data[pos] - sub ) < 0 then
- data[pos] = ( sub - data[pos] ) % 0xFFFF
- if pos > 1 then
- data[pos-1] = data[pos-1] - ( sub + data[pos] )
- else
- return nil
- end
+ for pos = #data, 1, -1 do
+ local sub = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0
+ if ( data[pos] - sub ) < 0 then
+ data[pos] = ( sub - data[pos] ) % 0xFFFF
+ if pos > 1 then
+ data[pos-1] = data[pos-1] - ( sub + data[pos] )
else
- data[pos] = data[pos] - sub
+ return nil
end
+ else
+ data[pos] = data[pos] - sub
end
end
-- @see cidr.minhost
function cidr.maxhost( self )
if self[3] <= __sublen(self[1]) then
+ local i
local data = { unpack(self[2]) }
local offset = math.floor( self[3] / 16 ) + 1