diff options
author | Tobias Brunner <tobias@strongswan.org> | 2016-10-10 10:00:19 +0200 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2017-02-06 11:06:22 +0100 |
commit | 7a40162cb771ae481b11d874a63a421421b8b6bc (patch) | |
tree | 6a5a2fcad3c003b274552f79dff2f77fa7e5592d | |
parent | 965daa1df3ba55af0851257364c9ea488f8ef2af (diff) | |
download | strongswan-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.c | 81 |
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)) { |