util.rfc3484: New util implementing RFC3484 sorting
authorFlorian Zeitz <florob@babelmonkeys.de>
Sat, 22 Oct 2011 15:59:33 +0000 (17:59 +0200)
committerFlorian Zeitz <florob@babelmonkeys.de>
Sat, 22 Oct 2011 15:59:33 +0000 (17:59 +0200)
tests/test.lua
tests/test_util_rfc3484.lua [new file with mode: 0644]
util/rfc3484.lua [new file with mode: 0644]

index 000c3ee96a82c282fe392e172bcb7b3796e9f12c..db727ce157739265964312557af7ce937af5c19f 100644 (file)
@@ -12,6 +12,7 @@ function run_all_tests()
        package.loaded["net.connlisteners"] = { get = function () return {} end };
        dotest "util.jid"
        dotest "util.multitable"
+       dotest "util.rfc3484"
        dotest "net.http"
        dotest "core.modulemanager"
        dotest "core.stanza_router"
diff --git a/tests/test_util_rfc3484.lua b/tests/test_util_rfc3484.lua
new file mode 100644 (file)
index 0000000..18ae310
--- /dev/null
@@ -0,0 +1,51 @@
+-- Prosody IM
+-- Copyright (C) 2011 Florian Zeitz
+--
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+--
+
+function source(source)
+       local new_ip = require"util.ip".new_ip;
+       assert_equal(source(new_ip("2001::1", "IPv6"), {new_ip("3ffe::1", "IPv6"), new_ip("fe80::1", "IPv6")}).addr, "3ffe::1", "prefer appropriate scope");
+       assert_equal(source(new_ip("2001::1", "IPv6"), {new_ip("fe80::1", "IPv6"), new_ip("fec0::1", "IPv6")}).addr, "fec0::1", "prefer appropriate scope");
+       assert_equal(source(new_ip("fec0::1", "IPv6"), {new_ip("fe80::1", "IPv6"), new_ip("2001::1", "IPv6")}).addr, "2001::1", "prefer appropriate scope");
+       assert_equal(source(new_ip("ff05::1", "IPv6"), {new_ip("fe80::1", "IPv6"), new_ip("fec0::1", "IPv6"), new_ip("2001::1", "IPv6")}).addr, "fec0::1", "prefer appropriate scope");
+       assert_equal(source(new_ip("2001::1", "IPv6"), {new_ip("2001::1", "IPv6"), new_ip("2002::1", "IPv6")}).addr, "2001::1", "prefer same address");
+       assert_equal(source(new_ip("fec0::1", "IPv6"), {new_ip("fec0::2", "IPv6"), new_ip("2001::1", "IPv6")}).addr, "fec0::2", "prefer appropriate scope");
+       assert_equal(source(new_ip("2001::1", "IPv6"), {new_ip("2001::2", "IPv6"), new_ip("3ffe::2", "IPv6")}).addr, "2001::2", "longest matching prefix");
+       assert_equal(source(new_ip("2002:836b:2179::1", "IPv6"), {new_ip("2002:836b:2179::d5e3:7953:13eb:22e8", "IPv6"), new_ip("2001::2", "IPv6")}).addr, "2002:836b:2179::d5e3:7953:13eb:22e8", "prefer matching label");
+end
+
+function destination(dest)
+       local order;
+       local new_ip = require"util.ip".new_ip;
+       order = dest({new_ip("2001::1", "IPv6"), new_ip("131.107.65.121", "IPv4")}, {new_ip("2001::2", "IPv6"), new_ip("fe80::1", "IPv6"), new_ip("169.254.13.78", "IPv4")})
+       assert_equal(order[1].addr, "2001::1", "prefer matching scope");
+       assert_equal(order[2].addr, "131.107.65.121", "prefer matching scope")
+
+       order = dest({new_ip("2001::1", "IPv6"), new_ip("131.107.65.121", "IPv4")}, {new_ip("fe80::1", "IPv6"), new_ip("131.107.65.117", "IPv4")})
+       assert_equal(order[1].addr, "131.107.65.121", "prefer matching scope")
+       assert_equal(order[2].addr, "2001::1", "prefer matching scope")
+
+       order = dest({new_ip("2001::1", "IPv6"), new_ip("10.1.2.3", "IPv4")}, {new_ip("2001::2", "IPv6"), new_ip("fe80::1", "IPv6"), new_ip("10.1.2.4", "IPv4")})
+       assert_equal(order[1].addr, "2001::1", "prefer higher precedence");
+       assert_equal(order[2].addr, "10.1.2.3", "prefer higher precedence");
+
+       order = dest({new_ip("2001::1", "IPv6"), new_ip("fec0::1", "IPv6"), new_ip("fe80::1", "IPv6")}, {new_ip("2001::2", "IPv6"), new_ip("fec0::1", "IPv6"), new_ip("fe80::2", "IPv6")})
+       assert_equal(order[1].addr, "fe80::1", "prefer smaller scope");
+       assert_equal(order[2].addr, "fec0::1", "prefer smaller scope");
+       assert_equal(order[3].addr, "2001::1", "prefer smaller scope");
+
+       order = dest({new_ip("2001::1", "IPv6"), new_ip("3ffe::1", "IPv6")}, {new_ip("2001::2", "IPv6"), new_ip("3f44::2", "IPv6"), new_ip("fe80::2", "IPv6")})
+       assert_equal(order[1].addr, "2001::1", "longest matching prefix");
+       assert_equal(order[2].addr, "3ffe::1", "longest matching prefix");
+
+       order = dest({new_ip("2002:836b:4179::1", "IPv6"), new_ip("2001::1", "IPv6")}, {new_ip("2002:836b:4179::2", "IPv6"), new_ip("fe80::2", "IPv6")})
+       assert_equal(order[1].addr, "2002:836b:4179::1", "prefer matching label");
+       assert_equal(order[2].addr, "2001::1", "prefer matching label");
+
+       order = dest({new_ip("2002:836b:4179::1", "IPv6"), new_ip("2001::1", "IPv6")}, {new_ip("2002:836b:4179::2", "IPv6"), new_ip("2001::2", "IPv6"), new_ip("fe80::2", "IPv6")})
+       assert_equal(order[1].addr, "2001::1", "prefer higher precedence");
+       assert_equal(order[2].addr, "2002:836b:4179::1", "prefer higher precedence");
+end
diff --git a/util/rfc3484.lua b/util/rfc3484.lua
new file mode 100644 (file)
index 0000000..373d3c3
--- /dev/null
@@ -0,0 +1,124 @@
+-- Prosody IM
+-- Copyright (C) 2008-2011 Florian Zeitz
+--
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+--
+
+local t_sort = table.sort;
+local commonPrefixLength = require"util.ip".commonPrefixLength
+local new_ip = require"util.ip".new_ip;
+
+function source(dest, candidates)
+       local function comp(ipA, ipB)
+               -- Rule 1: Prefer same address
+               if dest == ipA then
+                       return true;
+               elseif dest == ipB then
+                       return false;
+               end
+
+               -- Rule 2: Prefer appropriate scope
+               if ipA.scope < ipB.scope then
+                       if ipA.scope < dest.scope then
+                               return false;
+                       else
+                               return true;
+                       end
+               elseif ipA.scope > ipB.scope then
+                       if ipB.scope < dest.scope then
+                               return true;
+                       else
+                               return false;
+                       end
+               end
+
+               -- Rule 3: Avoid deprecated addresses
+               -- XXX: No way to determine this
+               -- Rule 4: Prefer home addresses
+               -- XXX: Mobility Address related, no way to determine this
+               -- Rule 5: Prefer outgoing interface
+               -- XXX: Interface to address relation. No way to determine this
+               -- Rule 6: Prefer matching label
+               if ipA.label == dest.label and ipB.label ~= dest.label then
+                       return true;
+               elseif ipB.label == dest.label and ipA.label ~= dest.label then
+                       return false;
+               end
+
+               -- Rule 7: Prefer public addresses (over temporary ones)
+               -- XXX: No way to determine this
+               -- Rule 8: Use longest matching prefix
+               if commonPrefixLength(ipA, dest) > commonPrefixLength(ipB, dest) then
+                       return true;
+               else
+                       return false;
+               end
+       end
+
+       t_sort(candidates, comp);
+       return candidates[1];
+end
+
+function destination(candidates, sources)
+       local t_sort = table.sort;
+       local sourceAddrs = {};
+       local function comp(ipA, ipB)
+               local ipAsource = sourceAddrs[ipA];
+               local ipBsource = sourceAddrs[ipB];
+               -- Rule 1: Avoid unusable destinations
+               -- XXX: No such information
+               -- Rule 2: Prefer matching scope
+               if ipA.scope == ipAsource.scope and ipB.scope ~= ipBsource.scope then
+                       return true;
+               elseif ipA.scope ~= ipAsource.scope and ipB.scope == ipBsource.scope then
+                       return false;
+               end
+
+               -- Rule 3: Avoid deprecated addresses
+               -- XXX: No way to determine this
+               -- Rule 4: Prefer home addresses
+               -- XXX: Mobility Address related, no way to determine this
+               -- Rule 5: Prefer matching label
+               if ipAsource.label == ipA.label and ipBsource.label ~= ipB.label then
+                       return true;
+               elseif ipBsource.label == ipB.label and ipAsource.label ~= ipA.label then
+                       return false;
+               end
+
+               -- Rule 6: Prefer higher precedence
+               if ipA.precedence > ipB.precedence then
+                       return true;
+               elseif ipA.precedence < ipB.precedence then
+                       return false;
+               end
+
+               -- Rule 7: Prefer native transport
+               -- XXX: No way to determine this
+               -- Rule 8: Prefer smaller scope
+               if ipA.scope < ipB.scope then
+                       return true;
+               elseif ipA.scope > ipB.scope then
+                       return false;
+               end
+
+               -- Rule 9: Use longest matching prefix
+               if commonPrefixLength(ipA, ipAsource) > commonPrefixLength(ipB, ipBsource) then
+                       return true;
+               elseif commonPrefixLength(ipA, ipAsource) < commonPrefixLength(ipB, ipBsource) then
+                       return false;
+               end
+
+               -- Rule 10: Otherwise, leave order unchanged
+               return true;
+       end
+       for _, ip in ipairs(candidates) do
+               sourceAddrs[ip] = source(ip, sources);
+       end
+
+       t_sort(candidates, comp);
+       return candidates;
+end
+
+return {source = source,
+       destination = destination};