summaryrefslogtreecommitdiff
path: root/minix/net/lwip/addrpol.c
blob: 144f02c1c979be554995fe54d27d598205c44f4f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* LWIP service - addrpol.c - address policy table and values */
/*
 * The main purpose of this module is to implement the address policy table
 * described in RFC 6724.  In general, the policy table is used for two
 * purposes: source address selection, which is part of this service, and
 * destination address selection, which is implemented in libc.  NetBSD 7, the
 * version that MINIX 3 is synced against at this moment, does not actually
 * implement the libc part yet, though.  That will change with NetBSD 8, where
 * libc uses sysctl(7) to obtain the kernel's policy table, which itself can be
 * changed with the new ip6addrctl(8) utility.  Once we resync to NetBSD 8, we
 * will also have to support this new functionality, and this module is where
 * it would be implemented.  Since NetBSD 7 is even lacking the necessary
 * definitions, we cannot do that ahead of time, though.  Thus, until then,
 * this module is rather simple, as it only implements a static policy table
 * used for source address selection.  No changes beyond this module should be
 * necessary, e.g. we are purposely not caching labels for local addresses.
 */

#include "lwip.h"

/*
 * Address policy table.  Currently hardcoded to the default of RFC 6724.
 * Sorted by prefix length, so that the first match is always also the longest.
 */
static const struct {
	ip_addr_t ipaddr;
	unsigned int prefix;
	int precedence;
	int label;
} addrpol_table[] = {
	{ IPADDR6_INIT_HOST(0, 0, 0, 1),		128, 50,  0 },
	{ IPADDR6_INIT_HOST(0, 0, 0x0000ffffUL, 0),	 96, 35,  4 },
	{ IPADDR6_INIT_HOST(0, 0, 0, 0),		 96,  1,  3 },
	{ IPADDR6_INIT_HOST(0x20010000UL, 0, 0, 0),	 32,  5,  5 },
	{ IPADDR6_INIT_HOST(0x20020000UL, 0, 0, 0),	 16, 30,  2 },
	{ IPADDR6_INIT_HOST(0x3ffe0000UL, 0, 0, 0),	 16,  1, 12 },
	{ IPADDR6_INIT_HOST(0xfec00000UL, 0, 0, 0),	 10,  1, 11 },
	{ IPADDR6_INIT_HOST(0xfc000000UL, 0, 0, 0),	  7,  3, 13 },
	{ IPADDR6_INIT_HOST(0, 0, 0, 0),		  0, 40,  1 }
};

/*
 * Obtain the label value for the given IP address from the address policy
 * table.  Currently only IPv6 addresses may be given.  This function is linear
 * in number of address policy table entries, requiring a relatively expensive
 * normalization operation for each entry, so it should not be called lightly.
 * Its results should not be cached beyond local contexts either, because the
 * policy table itself may be changed from userland (in the future).
 *
 * TODO: convert IPv4 addresses to IPv4-mapped IPv6 addresses.
 * TODO: embed the interface index in link-local addresses.
 */
int
addrpol_get_label(const ip_addr_t * iporig)
{
	ip_addr_t ipaddr;
	unsigned int i;

	assert(IP_IS_V6(iporig));

	/*
	 * The policy table is sorted by prefix length such that the first
	 * match is also the one with the longest prefix, and as such the best.
	 */
	for (i = 0; i < __arraycount(addrpol_table); i++) {
		addr_normalize(&ipaddr, iporig, addrpol_table[i].prefix);

		if (ip_addr_cmp(&addrpol_table[i].ipaddr, &ipaddr))
			return addrpol_table[i].label;
	}

	/*
	 * We cannot possibly get here with the default policy table, because
	 * the last entry will always match.  It is not clear what we should
	 * return if there is no matching entry, though.  For now, we return
	 * the default label value for the default (::/0) entry, which is 1.
	 */
	return 1;
}

/*
 * Return an opaque positive value (possibly zero) that represents the scope of
 * the given IP address.  A larger value indicates a wider scope.  The 'is_src'
 * flag indicates whether the address is a source or a destination address,
 * which affects the value returned for unknown addresses.  A scope is a direct
 * function of only the given address, so the result may be cached on a per-
 * address basis without risking invalidation at any point in time.
 */
int
addrpol_get_scope(const ip_addr_t * ipaddr, int is_src)
{
	const ip6_addr_t *ip6addr;

	/*
	 * For now, all IPv4 addresses are considered global.  This function is
	 * currently called only for IPv6 addresses anyway.
	 */
	if (IP_IS_V4(ipaddr))
		return IP6_MULTICAST_SCOPE_GLOBAL;

	assert(IP_IS_V6(ipaddr));

	ip6addr = ip_2_ip6(ipaddr);

	/*
	 * These are ordered not by ascending scope, but (roughly) by expected
	 * likeliness to match, for performance reasons.
	 */
	if (ip6_addr_isglobal(ip6addr))
		return IP6_MULTICAST_SCOPE_GLOBAL;

	if (ip6_addr_islinklocal(ip6addr) || ip6_addr_isloopback(ip6addr))
		return IP6_MULTICAST_SCOPE_LINK_LOCAL;

	/*
	 * We deliberately deviate from RFC 6724 Sec. 3.1 by considering
	 * Unique-Local Addresses (ULAs) to be of smaller scope than global
	 * addresses, to avoid that during source address selection, a
	 * preferred ULA is picked over a deprecated global address when given
	 * a global address as destination, as that would likely result in
	 * broken two-way communication.
	 */
	if (ip6_addr_isuniquelocal(ip6addr))
		return IP6_MULTICAST_SCOPE_ORGANIZATION_LOCAL;

	if (ip6_addr_ismulticast(ip6addr))
		return ip6_addr_multicast_scope(ip6addr);

	/* Site-local addresses are deprecated. */
	if (ip6_addr_issitelocal(ip6addr))
		return IP6_MULTICAST_SCOPE_SITE_LOCAL;

	/*
	 * If the address is a source address, give it a scope beyond global to
	 * make sure that a "real" global address is picked first.  If the
	 * address is a destination address, give it a global scope so as to
	 * pick "real" global addresses over unknown-scope source addresses.
	 */
	if (is_src)
		return IP6_MULTICAST_SCOPE_RESERVEDF; /* greater than GLOBAL */
	else
		return IP6_MULTICAST_SCOPE_GLOBAL;
}