2 -- Copyright (C) 2008-2011 Florian Zeitz
4 -- This project is MIT/X11 licensed. Please see the
5 -- COPYING file in the source package for more information.
8 local net
= require
"util.net";
9 local hex
= require
"util.hex";
11 local ip_methods
= {};
14 __index
= function (ip
, key
)
15 local method
= ip_methods
[key
];
16 if not method
then return nil; end
17 local ret
= method(ip
);
21 __tostring
= function (ip
) return ip
.addr
; end,
22 __eq
= function (ipA
, ipB
) return ipA
.packed
== ipB
.packed
; end
26 ["0"] = "0000", ["1"] = "0001", ["2"] = "0010", ["3"] = "0011",
27 ["4"] = "0100", ["5"] = "0101", ["6"] = "0110", ["7"] = "0111",
28 ["8"] = "1000", ["9"] = "1001", ["A"] = "1010", ["B"] = "1011",
29 ["C"] = "1100", ["D"] = "1101", ["E"] = "1110", ["F"] = "1111",
32 local function new_ip(ipStr
, proto
)
34 if (not proto
or proto
== "IPv6") and ipStr
:find('%', 1, true) then
35 ipStr
, zone
= ipStr
:match("^(.-)%%(.*)");
38 local packed
, err
= net
.pton(ipStr
);
39 if not packed
then return packed
, err
end
40 if proto
== "IPv6" and #packed
~= 16 then
41 return nil, "invalid-ipv6";
42 elseif proto
== "IPv4" and #packed
~= 4 then
43 return nil, "invalid-ipv4";
47 elseif #packed
== 4 then
50 return nil, "unknown protocol";
52 elseif proto
~= "IPv6" and proto
~= "IPv4" then
53 return nil, "invalid protocol";
56 return setmetatable({ addr
= ipStr
, packed
= packed
, proto
= proto
, zone
= zone
}, ip_mt
);
59 function ip_methods
:normal()
60 return net
.ntop(self
.packed
);
63 function ip_methods
.bits(ip
)
64 return hex
.to(ip
.packed
):upper():gsub(".", hex2bits
);
67 function ip_methods
.bits_full(ip
)
68 if ip
.proto
== "IPv4" then
76 local function commonPrefixLength(ipA
, ipB
)
77 ipA
, ipB
= ipA
.bits_full
, ipB
.bits_full
;
79 if ipA
:sub(i
,i
) ~= ipB
:sub(i
,i
) then
87 local loopback
= new_ip("::1");
88 local loopback4
= new_ip("127.0.0.0");
89 local sixtofour
= new_ip("2002::");
90 local teredo
= new_ip("2001::");
91 local linklocal
= new_ip("fe80::");
92 local linklocal4
= new_ip("169.254.0.0");
93 local uniquelocal
= new_ip("fc00::");
94 local sitelocal
= new_ip("fec0::");
95 local sixbone
= new_ip("3ffe::");
96 local defaultunicast
= new_ip("::");
97 local multicast
= new_ip("ff00::");
98 local ipv6mapped
= new_ip("::ffff:0:0");
100 local function v4scope(ip
)
101 if match(ip
, loopback4
, 8) then
103 elseif match(ip
, linklocal4
) then
105 else -- Global unicast
110 local function v6scope(ip
)
111 if ip
== loopback
then
113 elseif match(ip
, linklocal
, 10) then
115 elseif match(ip
, sitelocal
, 10) then
117 elseif match(ip
, multicast
, 10) then
118 return ip
.packed
:byte(2) % 0x10;
119 else -- Global unicast
124 local function label(ip
)
125 if ip
== loopback
then
127 elseif match(ip
, sixtofour
, 16) then
129 elseif match(ip
, teredo
, 32) then
131 elseif match(ip
, uniquelocal
, 7) then
133 elseif match(ip
, sitelocal
, 10) then
135 elseif match(ip
, sixbone
, 16) then
137 elseif match(ip
, defaultunicast
, 96) then
139 elseif match(ip
, ipv6mapped
, 96) then
146 local function precedence(ip
)
147 if ip
== loopback
then
149 elseif match(ip
, sixtofour
, 16) then
151 elseif match(ip
, teredo
, 32) then
153 elseif match(ip
, uniquelocal
, 7) then
155 elseif match(ip
, sitelocal
, 10) then
157 elseif match(ip
, sixbone
, 16) then
159 elseif match(ip
, defaultunicast
, 96) then
161 elseif match(ip
, ipv6mapped
, 96) then
168 function ip_methods
:toV4mapped()
169 if self
.proto
~= "IPv4" then return nil, "No IPv4 address" end
170 local value
= new_ip("::ffff:" .. self
.normal
);
174 function ip_methods
:label()
175 if self
.proto
== "IPv4" then
176 return label(self
.toV4mapped
);
182 function ip_methods
:precedence()
183 if self
.proto
== "IPv4" then
184 return precedence(self
.toV4mapped
);
186 return precedence(self
);
190 function ip_methods
:scope()
191 if self
.proto
== "IPv4" then
192 return v4scope(self
);
194 return v6scope(self
);
198 local rfc1918_8
= new_ip("10.0.0.0");
199 local rfc1918_12
= new_ip("172.16.0.0");
200 local rfc1918_16
= new_ip("192.168.0.0");
201 local rfc6598
= new_ip("100.64.0.0");
203 function ip_methods
:private()
204 local private
= self
.scope
~= 0xE;
205 if not private
and self
.proto
== "IPv4" then
206 return match(self
, rfc1918_8
, 8) or match(self
, rfc1918_12
, 12) or match(self
, rfc1918_16
, 16) or match(self
, rfc6598
, 10);
211 local function parse_cidr(cidr
)
213 local ip_len
= cidr
:find("/", 1, true);
215 bits
= tonumber(cidr
:sub(ip_len
+1, -1));
216 cidr
= cidr
:sub(1, ip_len
-1);
218 return new_ip(cidr
), bits
;
221 function match(ipA
, ipB
, bits
)
222 if not bits
or bits
>= 128 or ipB
.proto
== "IPv4" and bits
>= 32 then
227 if ipA
.proto
~= ipB
.proto
then
228 if ipA
.proto
== "IPv4" then
229 ipA
= ipA
.toV4mapped
;
230 elseif ipB
.proto
== "IPv4" then
231 ipB
= ipB
.toV4mapped
;
232 bits
= bits
+ (128 - 32);
235 return ipA
.bits
:sub(1, bits
) == ipB
.bits
:sub(1, bits
);
240 commonPrefixLength
= commonPrefixLength
,
241 parse_cidr
= parse_cidr
,