small-package/lua-ipops/src/ipops.lua

498 lines
11 KiB
Lua

local function _lshift(a, i)
return math.floor(a * 2^i)
end
local function _rshift(a, i)
return math.floor(a / 2^i)
end
local function _band(a, b)
local r = 0
for i = 0, 31 do
if _rshift(a, 31 - i) % 0x2 == 1 and _rshift(b, 31 - i) % 0x2 == 1 then
r = r * 2 + 1
else
r = r * 2
end
end
return r
end
local function _bor(a, b)
local r = 0
for i = 0, 31 do
if _rshift(a, 31 - i) % 0x2 == 1 or _rshift(b, 31 - i) % 0x2 == 1 then
r = r * 2 + 1
else
r = r * 2
end
end
return r
end
local function _bxor(a, b)
local r = 0
for i = 0, 31 do
if _rshift(a, 31 - i) % 0x2 ~= _rshift(b, 31 - i) % 0x2 then
r = r * 2 + 1
else
r = r * 2
end
end
return r
end
local function _bnot(a)
local r = 0
for i = 0, 31 do
if _rshift(a, 31 - i) % 0x2 == 0x0 then
r = r * 2 + 1
else
r = r * 2
end
end
return r
end
local function get_parts_as_number(str)
local t = {}
for part in string.gmatch(str, "%d+") do
table.insert(t, tonumber(part, 10))
end
return t
end
-- ipstr: a.b.c.d
local function ipstr2int(ipstr)
local ip = get_parts_as_number(ipstr)
if #ip == 4 then
return (((ip[1] * 0x100 + ip[2]) * 0x100 + ip[3]) * 0x100 + ip[4])
end
return 0
end
local function int2ipstr(x)
local a = _rshift(x, 24) % 0x100
local b = _rshift(x, 16) % 0x100
local c = _rshift(x, 8) % 0x100
local d = _rshift(x, 0) % 0x100
return string.format("%u.%u.%u.%u", a, b, c, d)
end
-- cidr: n
local function cidr2int(cidr)
if cidr == 0 then return 0 end
local x = 0
for i = 0, cidr - 1 do
x = x + _lshift(1, 31 - i)
end
return x
end
local function int2cidr(x)
for i = 0, 31 do
if _band(x, _lshift(1, 31 - i)) == 0 then
return i
end
end
return 32
end
local function cidr2maskstr(cidr)
return int2ipstr(cidr2int(cidr))
end
local function maskstr2cidr(maskstr)
return int2cidr(ipstr2int(maskstr))
end
-- ipaddr: a.b.c.d, a.b.c.d/cidr
-- return ip_int, mask_int
local function get_ip_and_mask(ipaddr)
local n = get_parts_as_number(ipaddr)
return (((n[1] * 256 + n[2]) * 256 + n[3]) * 256 + n[4]), cidr2int(n[5] or 32)
end
-- return ip_str, mask_str
local function get_ipstr_and_maskstr(ipaddr)
local ip, mask = get_ip_and_mask(ipaddr)
return int2ipstr(ip), int2ipstr(mask)
end
-- netString: ipaddr, a.b.c.d-e.f.g.h, a.b.c.d/m1.m2.m3.m4
-- return: range: [n1, n2] where n1 <= n2
local function netString2range(netString)
local ip = get_parts_as_number(netString)
if #ip == 4 then
local i = (((ip[1] * 256 + ip[2]) * 256 + ip[3]) * 256 + ip[4])
return {i, i}
end
if #ip == 5 and ip[5] >= 0 and ip[5] <= 32 then
local i = (((ip[1] * 256 + ip[2]) * 256 + ip[3]) * 256 + ip[4])
local m = cidr2int(ip[5])
local s = _band(i, m)
local e = _bor(i, _bnot(m))
return {s, e}
end
if #ip == 8 then
local i = (((ip[1] * 256 + ip[2]) * 256 + ip[3]) * 256 + ip[4])
local m = (((ip[5] * 256 + ip[6]) * 256 + ip[7]) * 256 + ip[8])
if netString:match('/') then
local s = _band(i, m)
local e = _bor(s, _bnot(m))
if s <= e then
return {s, e}
end
else
if i <= m then
return {i, m}
end
end
end
return nil
end
local function range2netString(range)
if range[1] <= range[2] then
return int2ipstr(range[1]) .. "-" .. int2ipstr(range[2])
end
return nil
end
-- rangeSet: [range, ...]
local function rangeSet_add_range(rangeSet, range)
rangeSet = rangeSet or {}
if not range then
return rangeSet
end
if #rangeSet == 0 then
table.insert(rangeSet, range)
return rangeSet
end
local rangeSet_new = {}
for _, r in ipairs(rangeSet) do
if range[1] < r[1] then
if range[2] < r[1] then
if range[2] + 1 < r[1] then
table.insert(rangeSet_new, range)
range = r
else -- range[2] == r[1]
range = {range[1], r[2]}
end
elseif range[2] <= r[2] then
range = {range[1], r[2]}
end
elseif range[1] <= r[2] then
if range[2] <= r[2] then
range = {r[1], r[2]}
elseif range[2] > r[2] then
range = {r[1], range[2]}
end
elseif range[1] == r[2] + 1 then
range = {r[1], range[2]}
else -- range[1] > r[2] + 1
table.insert(rangeSet_new, r)
end
end
table.insert(rangeSet_new, range)
return rangeSet_new
end
local function rangeSet_del_range(rangeSet, range)
rangeSet = rangeSet or {}
if not range then
return rangeSet
end
if #rangeSet == 0 then
return rangeSet
end
local rangeSet_new = {}
for _, r in ipairs(rangeSet) do
if r[2] < range[1] then
table.insert(rangeSet_new, r)
else --r[2] >= range[1]
if r[1] < range[1] then
table.insert(rangeSet_new, {r[1], range[1] - 1})
--else --r[1] >= range[1]
end
if r[2] > range[2] then
if r[1] > range[2] then
table.insert(rangeSet_new, r)
else --r[1] <= range[2]
table.insert(rangeSet_new, {range[2] + 1, r[2]})
end
--else --r[2] == range[2]
end
end
end
return rangeSet_new
end
local function rangeSet_sub_rangeSet(rangeSetA, rangeSetB)
rangeSetA = rangeSetA or {}
if #rangeSetA == 0 then
return rangeSetA
end
for _, range in ipairs(rangeSetB) do
rangeSetA = rangeSet_del_range(rangeSetA, range)
end
return rangeSetA
end
local function range_in_rangeSet(range, rangeSet)
for _, r in ipairs(rangeSet) do
if range[1] >= r[1] and range[2] <= r[2] then
return true
end
end
return false
end
local function rangeSet_in_rangeSet(rangeSetA, rangeSetB)
rangeSetA = rangeSetA or {}
if #rangeSetA == 0 then
return true
end
for _, range in ipairs(rangeSetA) do
if not range_in_rangeSet(range, rangeSetB) then
return false
end
end
return true
end
-- netStringSet: [netString, ...]
local function netStringSet2rangeSet(netStringSet)
local rangeSet = {}
for _, netString in ipairs(netStringSet) do
rangeSet = rangeSet_add_range(rangeSet, netString2range(netString))
end
return rangeSet
end
local function rangeSet2netStringSet(rangeSet)
local netStringSet = {}
for _, range in ipairs(rangeSet) do
table.insert(netStringSet, string.format("%s-%s", int2ipstr(range[1]), int2ipstr(range[2])))
end
return netStringSet
end
--ipcidr: a.b.c.d/cidr
--ipcidrSet: [ipcidr, ...], yes it is a netStringSet
local function rangeSet2ipcidrSet(rangeSet)
local ipcidrSet = {}
for _, range in ipairs(rangeSet) do
while range[1] <= range[2] do
for cidr = 0, 32 do
local m = cidr2int(cidr)
local s = _band(range[1], m)
local e = _bor(s, _bnot(m))
if s == range[1] and e <= range[2] then
table.insert(ipcidrSet, int2ipstr(s) .. '/' .. cidr)
range[1] = e + 1
break
end
end
end
end
return ipcidrSet
end
--[[DEBUG]]
--[[
local netStringSet = {
"1.1.1.1-2.2.2.2",
"192.168.0.0/16",
"192.168.0.1-192.168.0.2",
"192.168.255.254-192.169.0.100",
"172.16.0.1-172.16.0.100",
"172.168.0.0/255.255.0.0",
"192.168.11.6/24",
"192.168.0.1-192.168.0.22",
"192.168.0.33-192.168.0.52",
}
print("dump netStringSet")
for _, netString in ipairs(netStringSet) do
print(netString, range2netString(netString2range(netString)))
end
print("netStringSet to rangeSet")
local rangeSet = netStringSet2rangeSet(netStringSet)
for _, r in ipairs(rangeSet) do
print(r[1], r[2])
end
print("rangeSet to netStringSet")
netStringSet = rangeSet2netStringSet(rangeSet)
for _, netString in ipairs(netStringSet) do
print(netString)
end
print("rangeSet to ipcidrSet")
local ipcidrSet = rangeSet2ipcidrSet(rangeSet)
for _, ipcidr in ipairs(ipcidrSet) do
print(ipcidr)
end
print("ipcidrSet to rangeSet")
rangeSet = netStringSet2rangeSet(ipcidrSet)
for _, r in ipairs(rangeSet) do
print(r[1], r[2])
end
print("rangeSet to netStringSet")
netStringSet = rangeSet2netStringSet(rangeSet)
for _, netString in ipairs(netStringSet) do
print(netString)
end
print("get_ipstr_and_maskstr")
local ip, mask = get_ipstr_and_maskstr("1.2.3.4")
print(ip, mask)
]]
local __func__ = {
ipstr2int = ipstr2int,
int2ipstr = int2ipstr,
cidr2int = cidr2int,
int2cidr = int2cidr,
cidr2maskstr = cidr2maskstr,
maskstr2cidr = maskstr2cidr,
get_ip_and_mask = get_ip_and_mask,
get_ipstr_and_maskstr = get_ipstr_and_maskstr,
lshift = _lshift,
rshift = _rshift,
b32and = _band,
b32or = _bor,
b32xor = _bxor,
b32not = _bnot,
netString2range = netString2range,
netStringSet2rangeSet = netStringSet2rangeSet,
range2netString = range2netString,
rangeSet2netStringSet = rangeSet2netStringSet,
rangeSet2ipcidrSet = rangeSet2ipcidrSet,
rangeSet_add_range = rangeSet_add_range,
rangeSet_del_range = rangeSet_del_range,
rangeSet_sub_rangeSet = rangeSet_sub_rangeSet,
rangeSet_in_rangeSet = rangeSet_in_rangeSet,
}
-- api for test_func
-- argv = [ "netString,netString" ]
-- return: exit code
-- eg: lua ipops.lua netStrings2ipcidrStrings "1.2.3.4,192.168.1.0/24,192.168.100.100-192.168.200.222"
local function netStrings2ipcidrStrings(argv)
local rangeSet = {}
local netString
local netStrings = argv[1]
if not netStrings then
return -1
end
for netString in netStrings:gmatch("[^,]+") do
rangeSet = rangeSet_add_range(rangeSet, netString2range(netString))
end
local ipcidrSet = rangeSet2ipcidrSet(rangeSet)
print(table.concat(ipcidrSet, ','))
return 0
end
-- eg: lua ipops.lua netStrings_sub_netStrings "0.0.0.0/0" "1.2.3.4,192.168.1.0/24,192.168.100.100-192.168.200.222"
local function netStrings_sub_netStrings(argv)
local netString
local rangeSetA = {}
local rangeSetB = {}
local netStringsA, netStringsB = argv[1], argv[2]
if not netStringsA or not netStringsB then
return -1
end
for netString in netStringsA:gmatch("[^,]+") do
rangeSetA = rangeSet_add_range(rangeSetA, netString2range(netString))
end
for netString in netStringsB:gmatch("[^,]+") do
rangeSetB = rangeSet_add_range(rangeSetB, netString2range(netString))
end
rangeSetA = rangeSet_sub_rangeSet(rangeSetA, rangeSetB)
local ipcidrSet = rangeSet2ipcidrSet(rangeSetA)
print(table.concat(ipcidrSet, ','))
return 0
end
-- eg: lua ipops.lua netStrings_test_netStrings "192.168.15.0/24" "192.168.15.0/29"
local function netStrings_test_netStrings(argv)
local netString
local rangeSetA = {}
local rangeSetB = {}
local netStringsA, netStringsB = argv[1], argv[2]
if not netStringsA or not netStringsB then
return -1
end
for netString in netStringsA:gmatch("[^,]+") do
rangeSetA = rangeSet_add_range(rangeSetA, netString2range(netString))
end
for netString in netStringsB:gmatch("[^,]+") do
rangeSetB = rangeSet_add_range(rangeSetB, netString2range(netString))
end
if (rangeSet_in_rangeSet(rangeSetB, rangeSetA)) then
return 0
end
return 1
end
local test_func = {
netStrings2ipcidrStrings = {
argc = 1,
func = netStrings2ipcidrStrings
},
netStrings_sub_netStrings = {
argc = 2,
func = netStrings_sub_netStrings
},
netStrings_test_netStrings = {
argc = 2;
func = netStrings_test_netStrings
}
}
function test_main(...)
if arg[1] and test_func[arg[1]] and test_func[arg[1]].func then
local argc = test_func[arg[1]].argc or 0
local func = test_func[arg[1]].func
local argv = {}
if argc > 0 then
for i = 1, argc do
table.insert(argv, arg[1 + i])
end
end
return true, func(argv)
end
return false
end
local test, ret = test_main(...)
if test then
os.exit(ret)
end
return __func__