aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Brunner <tobias@strongswan.org>2016-10-10 10:00:19 +0200
committerTobias Brunner <tobias@strongswan.org>2017-02-06 11:06:22 +0100
commit7a40162cb771ae481b11d874a63a421421b8b6bc (patch)
tree6a5a2fcad3c003b274552f79dff2f77fa7e5592d
parent965daa1df3ba55af0851257364c9ea488f8ef2af (diff)
downloadstrongswan-7a40162cb771ae481b11d874a63a421421b8b6bc.tar.bz2
strongswan-7a40162cb771ae481b11d874a63a421421b8b6bc.tar.xz
kernel-netlink: Prefer matching label when selecting IPv6 source addresses
This implements rule 6 of RFC 6724 using the default priority table, so that e.g. global addresses are preferred over ULAs (which also have global scope) when the destination is a global address. Fixes #2138.
-rw-r--r--src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c81
1 files changed, 78 insertions, 3 deletions
diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c
index 0132f7269..486e8dbf4 100644
--- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c
+++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c
@@ -795,6 +795,68 @@ static u_char get_scope(host_t *ip)
}
/**
+ * Determine the label of the given unicast IP address.
+ *
+ * We currently only support the default table given in RFC 6724:
+ *
+ * Prefix Precedence Label
+ * ::1/128 50 0
+ * ::/0 40 1
+ * ::ffff:0:0/96 35 4
+ * 2002::/16 30 2
+ * 2001::/32 5 5
+ * fc00::/7 3 13
+ * ::/96 1 3
+ * fec0::/10 1 11
+ * 3ffe::/16 1 12
+ */
+static u_char get_label(host_t *ip)
+{
+ struct {
+ chunk_t net;
+ u_char prefix;
+ u_char label;
+ } priorities[] = {
+ /* priority table ordered by prefix */
+ /* ::1/128 */
+ { chunk_from_chars(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01), 128, 0 },
+ /* ::ffff:0:0/96 */
+ { chunk_from_chars(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00), 96, 4 },
+ /* ::/96 */
+ { chunk_from_chars(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), 96, 3 },
+ /* 2001::/32 */
+ { chunk_from_chars(0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), 32, 5 },
+ /* 2002::/16 */
+ { chunk_from_chars(0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), 16, 2 },
+ /* 3ffe::/16 */
+ { chunk_from_chars(0x3f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), 16, 12 },
+ /* fec0::/10 */
+ { chunk_from_chars(0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), 10, 11 },
+ /* fc00::/7 */
+ { chunk_from_chars(0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), 7, 13 },
+ };
+ int i;
+
+ for (i = 0; i < countof(priorities); i++)
+ {
+ if (host_in_subnet(ip, priorities[i].net, priorities[i].prefix))
+ {
+ return priorities[i].label;
+ }
+ }
+ /* ::/0 */
+ return 1;
+}
+
+/**
* Returns the length of the common prefix in bits up to the length of a's
* prefix, defined by RFC 6724 as the portion of the address not including the
* interface ID, which is 64-bit for most unicast addresses (see RFC 4291).
@@ -829,7 +891,7 @@ static u_char common_prefix(host_t *a, host_t *b)
static bool is_address_better(private_kernel_netlink_net_t *this,
addr_entry_t *a, addr_entry_t *b, host_t *d)
{
- u_char sa, sb, sd, pa, pb;
+ u_char sa, sb, sd, la, lb, ld, pa, pb;
/* rule 2: prefer appropriate scope */
if (d)
@@ -858,9 +920,22 @@ static bool is_address_better(private_kernel_netlink_net_t *this,
/* rule 4 is not applicable as we don't know if an address is a home or
* care-of addresses.
* rule 5 does not apply as we only compare addresses from one interface
- * rule 6 requires a policy table (optionally configurable) to match
- * configurable labels
*/
+ /* rule 6: prefer matching label */
+ if (d)
+ {
+ la = get_label(a->ip);
+ lb = get_label(b->ip);
+ ld = get_label(d);
+ if (la == ld && lb != ld)
+ {
+ return FALSE;
+ }
+ else if (lb == ld && la != ld)
+ {
+ return TRUE;
+ }
+ }
/* rule 7: prefer temporary addresses (WE REVERSE THIS BY DEFAULT!) */
if ((a->flags & IFA_F_TEMPORARY) != (b->flags & IFA_F_TEMPORARY))
{