ntopng/scripts/lua/modules/ipv4_utils.lua
2024-01-12 11:44:18 +01:00

196 lines
4 KiB
Lua

--
-- (C) 2017-24 - ntop.org
--
local ipv4_utils = {}
local function ip_to_bit(ip)
local x = 0
local parts = string.split(ip, "%.")
for i=0,3 do
x = (x | (tonumber(parts[i+1]) << ((3-i)*8)))
end
return x
end
local function bit_to_ip(bits)
local parts = {}
for i=0, 3 do
parts[i+1] = ((bits >> ((3-i)*8)) & 0xFF)
end
return table.concat(parts, ".")
end
--
-- Get the number of bits allocated for the network specified by the ip netmask
-- nil is returned on error
function ipv4_utils.netmask(ip)
if not ipv4_utils.valid(ip) then
-- bad netmask
return nil
end
local parts = string.split(ip, "%.")
local netmask = 0
for i=1,4 do
local val = tonumber(parts[i])
local x = nil
if val == 255 then x = 8
elseif val == 254 then x = 7
elseif val == 252 then x = 6
elseif val == 248 then x = 5
elseif val == 240 then x = 4
elseif val == 224 then x = 3
elseif val == 192 then x = 2
elseif val == 128 then x = 1
elseif val == 0 then x = 0
end
if x == nil then
-- bad netmask
return nil
end
netmask = netmask + x
if val ~= 255 then
for j=i+1,4 do
if tonumber(parts[j]) ~= 0 then
-- bad netmask
return nil
end
end
break
end
end
return netmask
end
function ipv4_utils.cidr_2_addr(cidr)
local parts = split(cidr, "/")
if #parts ~= 2 then
return nil
end
local addr = parts[1]
local num_netmask = tonumber(parts[2])
local netmask = 0x100000000 - (1 << (32-num_netmask))
return addr, bit_to_ip(netmask)
end
--
-- Compares IPv4 addresses
-- Precondition: a and b are valid ipv4 addresses
-- Returns:
-- >0 if a > b
-- <0 if a < b
-- 0 if a == b
--
function ipv4_utils.cmp(a, b)
local a_parts = string.split(a, "%.")
local b_parts = string.split(b, "%.")
for i=1,4 do
local a_part = tonumber(a_parts[i])
local b_part = tonumber(b_parts[i])
if a_part > b_part then
return 1
elseif a_part < b_part then
return -1
end
end
return 0
end
-- Assumption: 32bit integer are used. This is already assumed by http://bitop.luajit.org/semantics.html
function ipv4_utils.ipToInt(ip)
local intrepr = 0
for byte in string.gmatch(ip, "%d+") do
intrepr = intrepr * 256 + byte
end
return intrepr
end
function ipv4_utils.intToIp(int)
local parts = {
((int >> 24) & 0xFF),
((int >> 16) & 0xFF),
((int >> 8) & 0xFF),
(int & 0xFF)
}
return table.concat(parts, ".")
end
-- Returns possible DHCP range
function ipv4_utils.get_possible_dhcp_range(ip, network, broadcast_addr)
local ip_int = ipv4_utils.ipToInt(ip)
local broadcast_int = ipv4_utils.ipToInt(broadcast_addr)
local first_ip
local last_ip
if ip_int >= broadcast_int - 1 then
first_ip = ipv4_utils.ipToInt(network) + 1
last_ip = ip_int - 1
else
first_ip = ip_int + 1
last_ip = broadcast_int - 1
end
return {
first_ip = ipv4_utils.intToIp(first_ip),
last_ip = ipv4_utils.intToIp(last_ip),
}
end
function ipv4_utils.includes(network, netmask, ip)
local lower = network
local upper = ipv4_utils.broadcast_address(network, netmask)
return (ipv4_utils.cmp(ip, lower) >= 0) and
(ipv4_utils.cmp(ip, upper) <= 0)
end
-- Get the broadcast address for the given netmask
function ipv4_utils.broadcast_address(ip, netmask)
local ipbit = ip_to_bit(ip)
local maskbit = ip_to_bit(netmask)
return bit_to_ip((~(maskbit) | ipbit))
end
-- https://stackoverflow.com/questions/10975935/lua-function-check-if-ipv4-or-ipv6-or-string
function ipv4_utils.valid(address)
local chunks = {address:match("(%d+)%.(%d+)%.(%d+)%.(%d+)$")}
if #chunks == 4 then
for _, v in pairs(chunks) do
if (tonumber(v) < 0) or (tonumber(v) > 255) then
return false
end
end
return true
end
end
function ipv4_utils.addressToNetwork(address, netmask)
local mask = ipv4_utils.netmask(netmask)
return ntop.networkPrefix(address, mask) .. "/" .. mask
end
return ipv4_utils