Browse Source

[ipv6] Allow for multiple routers

Select the IPv6 source address and corresponding router (if any) using
a very simplified version of the algorithm from RFC6724:

- Ignore any source address that has a smaller scope than the
  destination address.  For example, do not use a link-local source
  address when sending to a global destination address.

- If we have a source address which is on the same link as the
  destination address, then use that source address.

- If we are left with multiple possible source addresses, then choose
  the address with the smallest scope.  For example, if we are sending
  to a site-local destination address and we have both a global source
  address and a site-local source address, then use the site-local
  source address.

- If we are still left with multiple possible source addresses, then
  choose the address with the longest matching prefix.

For the purposes of this algorithm, we treat RFC4193 Unique Local
Addresses as having organisation-local scope.  Since we use only
link-local scope for our multicast transmissions, this approximation
should remain valid in all practical situations.

Originally-implemented-by: Thomas Bächler <thomas@archlinux.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 7 years ago
parent
commit
a4c4f72297
4 changed files with 452 additions and 51 deletions
  1. 6
    2
      src/include/ipxe/in.h
  2. 39
    0
      src/include/ipxe/ipv6.h
  3. 126
    49
      src/net/ipv6.c
  4. 281
    0
      src/tests/ipv6_test.c

+ 6
- 2
src/include/ipxe/in.h View File

@@ -69,8 +69,12 @@ struct in6_addr {
69 69
 	( ( *( ( const uint16_t * ) (addr) ) & htons ( 0xffc0 ) ) ==	\
70 70
 	  htons ( 0xfe80 ) )
71 71
 
72
-#define IN6_IS_ADDR_NONGLOBAL( addr )					\
73
-	( IN6_IS_ADDR_LINKLOCAL (addr) || IN6_IS_ADDR_MULTICAST (addr) )
72
+#define IN6_IS_ADDR_SITELOCAL( addr )					\
73
+	( ( *( ( const uint16_t * ) (addr) ) & htons ( 0xffc0 ) ) ==	\
74
+	  htons ( 0xfec0 ) )
75
+
76
+#define IN6_IS_ADDR_ULA( addr )						\
77
+	( ( *( ( const uint8_t * ) (addr) ) & 0xfe ) == 0xfc )
74 78
 
75 79
 /**
76 80
  * IPv4 socket address

+ 39
- 0
src/include/ipxe/ipv6.h View File

@@ -158,6 +158,24 @@ struct ipv6_pseudo_header {
158 158
 	uint8_t next_header;
159 159
 } __attribute__ (( packed ));
160 160
 
161
+/** IPv6 address scopes */
162
+enum ipv6_address_scope {
163
+	/** Interface-local address scope */
164
+	IPV6_SCOPE_INTERFACE_LOCAL = 0x1,
165
+	/** Link-local address scope */
166
+	IPV6_SCOPE_LINK_LOCAL = 0x2,
167
+	/** Admin-local address scope */
168
+	INV6_SCOPE_ADMIN_LOCAL = 0x4,
169
+	/** Site-local address scope */
170
+	IPV6_SCOPE_SITE_LOCAL = 0x5,
171
+	/** Organisation-local address scope */
172
+	IPV6_SCOPE_ORGANISATION_LOCAL = 0x8,
173
+	/** Global address scope */
174
+	IPV6_SCOPE_GLOBAL = 0xe,
175
+	/** Maximum scope */
176
+	IPV6_SCOPE_MAX = 0xf,
177
+};
178
+
161 179
 /** An IPv6 address/routing table entry */
162 180
 struct ipv6_miniroute {
163 181
 	/** List of miniroutes */
@@ -174,6 +192,8 @@ struct ipv6_miniroute {
174 192
 	struct in6_addr prefix_mask;
175 193
 	/** Router address */
176 194
 	struct in6_addr router;
195
+	/** Scope */
196
+	unsigned int scope;
177 197
 	/** Flags */
178 198
 	unsigned int flags;
179 199
 };
@@ -244,6 +264,18 @@ static inline void ipv6_all_routers ( struct in6_addr *addr ) {
244 264
 	addr->s6_addr[15] = 2;
245 265
 }
246 266
 
267
+/**
268
+ * Get multicast address scope
269
+ *
270
+ * @v addr		Multicast address
271
+ * @ret scope		Address scope
272
+ */
273
+static inline unsigned int
274
+ipv6_multicast_scope ( const struct in6_addr *addr ) {
275
+
276
+	return ( addr->s6_addr[1] & 0x0f );
277
+}
278
+
247 279
 /** IPv6 settings sibling order */
248 280
 enum ipv6_settings_order {
249 281
 	/** No address */
@@ -264,6 +296,13 @@ extern struct list_head ipv6_miniroutes;
264 296
 extern struct net_protocol ipv6_protocol __net_protocol;
265 297
 
266 298
 extern int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr );
299
+extern int ipv6_add_miniroute ( struct net_device *netdev,
300
+				struct in6_addr *address,
301
+				unsigned int prefix_len,
302
+				struct in6_addr *router );
303
+extern void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute );
304
+extern struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
305
+					    struct in6_addr **dest );
267 306
 extern int parse_ipv6_setting ( const struct setting_type *type,
268 307
 				const char *value, void *buf, size_t len );
269 308
 extern int format_ipv6_setting ( const struct setting_type *type,

+ 126
- 49
src/net/ipv6.c View File

@@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
23 23
 #include <stdio.h>
24 24
 #include <stdlib.h>
25 25
 #include <string.h>
26
+#include <strings.h>
26 27
 #include <errno.h>
27 28
 #include <assert.h>
28 29
 #include <byteswap.h>
@@ -78,6 +79,40 @@ static uint32_t ipv6col ( struct in6_addr *in ) {
78 79
 	return crc32_le ( 0, in, sizeof ( *in ) );
79 80
 }
80 81
 
82
+/**
83
+ * Determine IPv6 address scope
84
+ *
85
+ * @v addr		IPv6 address
86
+ * @ret scope		Address scope
87
+ */
88
+static unsigned int ipv6_scope ( const struct in6_addr *addr ) {
89
+
90
+	/* Multicast addresses directly include a scope field */
91
+	if ( IN6_IS_ADDR_MULTICAST ( addr ) )
92
+		return ipv6_multicast_scope ( addr );
93
+
94
+	/* Link-local addresses have link-local scope */
95
+	if ( IN6_IS_ADDR_LINKLOCAL ( addr ) )
96
+		return IPV6_SCOPE_LINK_LOCAL;
97
+
98
+	/* Site-local addresses have site-local scope */
99
+	if ( IN6_IS_ADDR_SITELOCAL ( addr ) )
100
+		return IPV6_SCOPE_SITE_LOCAL;
101
+
102
+	/* Unique local addresses do not directly map to a defined
103
+	 * scope.  They effectively have a scope which is wider than
104
+	 * link-local but narrower than global.  Since the only
105
+	 * multicast packets that we transmit are link-local, we can
106
+	 * simply choose an arbitrary scope between link-local and
107
+	 * global.
108
+	 */
109
+	if ( IN6_IS_ADDR_ULA ( addr ) )
110
+		return IPV6_SCOPE_ORGANISATION_LOCAL;
111
+
112
+	/* All other addresses are assumed to be global */
113
+	return IPV6_SCOPE_GLOBAL;
114
+}
115
+
81 116
 /**
82 117
  * Dump IPv6 routing table entry
83 118
  *
@@ -119,23 +154,32 @@ int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr ) {
119 154
 }
120 155
 
121 156
 /**
122
- * Check if IPv6 address is within a routing table entry's local network
157
+ * Count matching bits of an IPv6 routing table entry prefix
123 158
  *
124 159
  * @v miniroute		Routing table entry
125 160
  * @v address		IPv6 address
126
- * @ret is_on_link	Address is within this entry's local network
161
+ * @ret match_len	Number of matching prefix bits
127 162
  */
128
-static int ipv6_is_on_link ( struct ipv6_miniroute *miniroute,
129
-			     struct in6_addr *address ) {
163
+static unsigned int ipv6_match_len ( struct ipv6_miniroute *miniroute,
164
+				     struct in6_addr *address ) {
165
+	unsigned int match_len = 0;
130 166
 	unsigned int i;
167
+	uint32_t diff;
131 168
 
132 169
 	for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) /
133 170
 			    sizeof ( address->s6_addr32[0] ) ) ; i++ ) {
134
-		if ( (( address->s6_addr32[i] ^ miniroute->address.s6_addr32[i])
135
-		      & miniroute->prefix_mask.s6_addr32[i] ) != 0 )
136
-			return 0;
171
+
172
+		diff = ntohl ( ~( ( ~( address->s6_addr32[i] ^
173
+				       miniroute->address.s6_addr32[i] ) )
174
+				  & miniroute->prefix_mask.s6_addr32[i] ) );
175
+		match_len += 32;
176
+		if ( diff ) {
177
+			match_len -= flsl ( diff );
178
+			break;
179
+		}
137 180
 	}
138
-	return 1;
181
+
182
+	return match_len;
139 183
 }
140 184
 
141 185
 /**
@@ -148,12 +192,15 @@ static int ipv6_is_on_link ( struct ipv6_miniroute *miniroute,
148 192
 static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev,
149 193
 						struct in6_addr *address ) {
150 194
 	struct ipv6_miniroute *miniroute;
195
+	unsigned int match_len;
151 196
 
152 197
 	list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
153
-		if ( ( miniroute->netdev == netdev ) &&
154
-		     ipv6_is_on_link ( miniroute, address ) ) {
155
-			return miniroute;
156
-		}
198
+		if ( miniroute->netdev != netdev )
199
+			continue;
200
+		match_len = ipv6_match_len ( miniroute, address );
201
+		if ( match_len < miniroute->prefix_len )
202
+			continue;
203
+		return miniroute;
157 204
 	}
158 205
 	return NULL;
159 206
 }
@@ -167,10 +214,8 @@ static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev,
167 214
  * @v router		Router address (if any)
168 215
  * @ret rc		Return status code
169 216
  */
170
-static int ipv6_add_miniroute ( struct net_device *netdev,
171
-				struct in6_addr *address,
172
-				unsigned int prefix_len,
173
-				struct in6_addr *router ) {
217
+int ipv6_add_miniroute ( struct net_device *netdev, struct in6_addr *address,
218
+			 unsigned int prefix_len, struct in6_addr *router ) {
174 219
 	struct ipv6_miniroute *miniroute;
175 220
 	uint8_t *prefix_mask;
176 221
 	unsigned int remaining;
@@ -178,7 +223,12 @@ static int ipv6_add_miniroute ( struct net_device *netdev,
178 223
 
179 224
 	/* Find or create routing table entry */
180 225
 	miniroute = ipv6_miniroute ( netdev, address );
181
-	if ( ! miniroute ) {
226
+	if ( miniroute ) {
227
+
228
+		/* Remove from existing position in routing table */
229
+		list_del ( &miniroute->list );
230
+
231
+	} else {
182 232
 
183 233
 		/* Create new routing table entry */
184 234
 		miniroute = zalloc ( sizeof ( *miniroute ) );
@@ -202,11 +252,11 @@ static int ipv6_add_miniroute ( struct net_device *netdev,
202 252
 		}
203 253
 		if ( remaining )
204 254
 			*prefix_mask <<= ( 8 - remaining );
205
-
206
-		/* Add to list of routes */
207
-		list_add ( &miniroute->list, &ipv6_miniroutes );
208 255
 	}
209 256
 
257
+	/* Add to start of routing table */
258
+	list_add ( &miniroute->list, &ipv6_miniroutes );
259
+
210 260
 	/* Set or update address, if applicable */
211 261
 	for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) /
212 262
 			    sizeof ( address->s6_addr32[0] ) ) ; i++ ) {
@@ -220,13 +270,14 @@ static int ipv6_add_miniroute ( struct net_device *netdev,
220 270
 	if ( miniroute->prefix_len == IPV6_MAX_PREFIX_LEN )
221 271
 		miniroute->flags |= IPV6_HAS_ADDRESS;
222 272
 
273
+	/* Update scope */
274
+	miniroute->scope = ipv6_scope ( &miniroute->address );
275
+
223 276
 	/* Set or update router, if applicable */
224 277
 	if ( router ) {
225 278
 		memcpy ( &miniroute->router, router,
226 279
 			 sizeof ( miniroute->router ) );
227 280
 		miniroute->flags |= IPV6_HAS_ROUTER;
228
-		list_del ( &miniroute->list );
229
-		list_add_tail ( &miniroute->list, &ipv6_miniroutes );
230 281
 	}
231 282
 
232 283
 	ipv6_dump_miniroute ( miniroute );
@@ -238,7 +289,7 @@ static int ipv6_add_miniroute ( struct net_device *netdev,
238 289
  *
239 290
  * @v miniroute		Routing table entry
240 291
  */
241
-static void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ) {
292
+void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ) {
242 293
 
243 294
 	netdev_put ( miniroute->netdev );
244 295
 	list_del ( &miniroute->list );
@@ -253,9 +304,17 @@ static void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ) {
253 304
  * @ret dest		Next hop destination address
254 305
  * @ret miniroute	Routing table entry to use, or NULL if no route
255 306
  */
256
-static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
257
-					    struct in6_addr **dest ) {
307
+struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
308
+				     struct in6_addr **dest ) {
258 309
 	struct ipv6_miniroute *miniroute;
310
+	struct ipv6_miniroute *chosen = NULL;
311
+	unsigned int best = 0;
312
+	unsigned int match_len;
313
+	unsigned int score;
314
+	unsigned int scope;
315
+
316
+	/* Calculate destination address scope */
317
+	scope = ipv6_scope ( *dest );
259 318
 
260 319
 	/* Find first usable route in routing table */
261 320
 	list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
@@ -264,37 +323,54 @@ static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
264 323
 		if ( ! netdev_is_open ( miniroute->netdev ) )
265 324
 			continue;
266 325
 
267
-		/* Skip routing table entries with no usable source address */
326
+		/* Skip entries with no usable source address */
268 327
 		if ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) )
269 328
 			continue;
270 329
 
271
-		if ( IN6_IS_ADDR_NONGLOBAL ( *dest ) ) {
330
+		/* Skip entries with a non-matching scope ID, if
331
+		 * destination specifies a scope ID.
332
+		 */
333
+		if ( scope_id && ( miniroute->netdev->index != scope_id ) )
334
+			continue;
272 335
 
273
-			/* If destination is non-global, and the scope ID
274
-			 * matches this network device, then use this route.
275
-			 */
276
-			if ( miniroute->netdev->index == scope_id )
277
-				return miniroute;
336
+		/* Skip entries that are out of scope */
337
+		if ( miniroute->scope < scope )
338
+			continue;
278 339
 
279
-		} else {
340
+		/* Calculate match length */
341
+		match_len = ipv6_match_len ( miniroute, *dest );
280 342
 
281
-			/* If destination is an on-link global
282
-			 * address, then use this route.
283
-			 */
284
-			if ( ipv6_is_on_link ( miniroute, *dest ) )
285
-				return miniroute;
286
-
287
-			/* If destination is an off-link global
288
-			 * address, and we have a default gateway,
289
-			 * then use this route.
290
-			 */
291
-			if ( miniroute->flags & IPV6_HAS_ROUTER ) {
292
-				*dest = &miniroute->router;
293
-				return miniroute;
294
-			}
343
+		/* If destination is on-link, then use this route */
344
+		if ( match_len >= miniroute->prefix_len )
345
+			return miniroute;
346
+
347
+		/* If destination is unicast, then skip off-link
348
+		 * entries with no router.
349
+		 */
350
+		if ( ! ( IN6_IS_ADDR_MULTICAST ( *dest ) ||
351
+			 ( miniroute->flags & IPV6_HAS_ROUTER ) ) )
352
+			continue;
353
+
354
+		/* Choose best route, defined as being the route with
355
+		 * the smallest viable scope.  If two routes both have
356
+		 * the same scope, then prefer the route with the
357
+		 * longest match length.
358
+		 */
359
+		score = ( ( ( IPV6_SCOPE_MAX + 1 - miniroute->scope ) << 8 )
360
+			  + match_len );
361
+		if ( score > best ) {
362
+			chosen = miniroute;
363
+			best = score;
295 364
 		}
296 365
 	}
297 366
 
367
+	/* Return chosen route, if any */
368
+	if ( chosen ) {
369
+		if ( ! IN6_IS_ADDR_MULTICAST ( *dest ) )
370
+			*dest = &chosen->router;
371
+		return chosen;
372
+	}
373
+
298 374
 	return NULL;
299 375
 }
300 376
 
@@ -880,7 +956,7 @@ static const char * ipv6_sock_ntoa ( struct sockaddr *sa ) {
880 956
 	const char *netdev_name;
881 957
 
882 958
 	/* Identify network device, if applicable */
883
-	if ( IN6_IS_ADDR_NONGLOBAL ( in ) ) {
959
+	if ( IN6_IS_ADDR_LINKLOCAL ( in ) || IN6_IS_ADDR_MULTICAST ( in ) ) {
884 960
 		netdev = find_netdev_by_index ( sin6->sin6_scope_id );
885 961
 		netdev_name = ( netdev ? netdev->name : "UNKNOWN" );
886 962
 	} else {
@@ -946,7 +1022,8 @@ static int ipv6_sock_aton ( const char *string, struct sockaddr *sa ) {
946 1022
 		}
947 1023
 		sin6->sin6_scope_id = netdev->index;
948 1024
 
949
-	} else if ( IN6_IS_ADDR_NONGLOBAL ( &in ) ) {
1025
+	} else if ( IN6_IS_ADDR_LINKLOCAL ( &in ) ||
1026
+		    IN6_IS_ADDR_MULTICAST ( &in ) ) {
950 1027
 
951 1028
 		/* If no network device is explicitly specified for a
952 1029
 		 * link-local or multicast address, default to using

+ 281
- 0
src/tests/ipv6_test.c View File

@@ -41,6 +41,38 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
41 41
 /** Define inline IPv6 address */
42 42
 #define IPV6(...) { __VA_ARGS__ }
43 43
 
44
+/** An IPv6 test routing table entry */
45
+struct ipv6_test_route {
46
+	/** Local address */
47
+	const char *address;
48
+	/** Prefix length */
49
+	unsigned int prefix_len;
50
+	/** Router address (if any) */
51
+	const char *router;
52
+};
53
+
54
+/** An IPv6 test routing table */
55
+struct ipv6_test_table {
56
+	/** Test routing table entries */
57
+	const struct ipv6_test_route *routes;
58
+	/** Number of table entries */
59
+	unsigned int count;
60
+	/** Constructed routing table */
61
+	struct list_head list;
62
+};
63
+
64
+/** Define a test routing table */
65
+#define TABLE( name, ... )						\
66
+	static const struct ipv6_test_route name ## _routes[] = {	\
67
+		__VA_ARGS__						\
68
+	};								\
69
+	static struct ipv6_test_table name = {				\
70
+		.routes = name ## _routes,				\
71
+		.count = ( sizeof ( name ## _routes ) /			\
72
+			   sizeof ( name ## _routes[0] ) ),		\
73
+		.list = LIST_HEAD_INIT ( name.list ),			\
74
+	};
75
+
44 76
 /** The unspecified IPv6 address */
45 77
 static const struct in6_addr sample_unspecified = {
46 78
 	.s6_addr = IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -53,6 +85,18 @@ static const struct in6_addr sample_link_local = {
53 85
 			  0x00, 0x00, 0x69, 0xff, 0xfe, 0x50, 0x58, 0x45 ),
54 86
 };
55 87
 
88
+/** A sample site-local IPv6 address */
89
+static const struct in6_addr sample_site_local = {
90
+	.s6_addr = IPV6 ( 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
91
+			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 ),
92
+};
93
+
94
+/** A sample ULA IPv6 address */
95
+static const struct in6_addr sample_ula = {
96
+	.s6_addr = IPV6 ( 0xfd, 0x44, 0x91, 0x12, 0x64, 0x42, 0x00, 0x00,
97
+			  0x00, 0x00, 0x69, 0xff, 0xfe, 0x50, 0x58, 0x45 ),
98
+};
99
+
56 100
 /** A sample global IPv6 address */
57 101
 static const struct in6_addr sample_global = {
58 102
 	.s6_addr = IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4,
@@ -65,6 +109,31 @@ static const struct in6_addr sample_multicast = {
65 109
 			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
66 110
 };
67 111
 
112
+/** Dummy network device used for routing tests */
113
+static struct net_device ipv6_test_netdev = {
114
+	.refcnt = REF_INIT ( ref_no_free ),
115
+	.index = 42,
116
+	.state = NETDEV_OPEN,
117
+};
118
+
119
+/** Routing table with only a link-local address */
120
+TABLE ( table_link_local,
121
+	{ "fe80::69ff:fe50:5845", 64, NULL } );
122
+
123
+/** Routing table with a global address */
124
+TABLE ( table_normal,
125
+	{ "fe80::69ff:fe50:5845", 64, NULL },
126
+	{ "2001:db8:3::1", 64, "fe80::1" } );
127
+
128
+/** Routing table with multiple addresses and routers */
129
+TABLE ( table_multi,
130
+	{ "fe80::69ff:fe50:5845", 64, NULL },
131
+	{ "2001:db8:3::1", 64, "fe80::1" },
132
+	{ "2001:db8:5::1", 64, NULL },
133
+	{ "2001:db8:42::1", 64, "fe80::2" },
134
+	{ "fd44:9112:6442::69ff:fe50:5845", 64, "fe80::1" },
135
+	{ "fd70:6ba9:50ae::69ff:fe50:5845", 64, "fe80::3" } );
136
+
68 137
 /**
69 138
  * Report an inet6_ntoa() test result
70 139
  *
@@ -133,6 +202,148 @@ static void inet6_aton_fail_okx ( const char *text, const char *file,
133 202
 #define inet6_aton_fail_ok( text )					\
134 203
 	inet6_aton_fail_okx ( text, __FILE__, __LINE__ )
135 204
 
205
+/**
206
+ * Create test routing table
207
+ *
208
+ * @v table		Test routing table
209
+ * @v file		Test code file
210
+ * @v line		Test code line
211
+ */
212
+static void ipv6_table_okx ( struct ipv6_test_table *table, const char *file,
213
+			     unsigned int line ) {
214
+	const struct ipv6_test_route *route;
215
+	struct in6_addr address;
216
+	struct in6_addr router;
217
+	struct list_head saved;
218
+	unsigned int i;
219
+
220
+	/* Sanity check */
221
+	okx ( list_empty ( &table->list ), file, line );
222
+
223
+	/* Save existing routing table */
224
+	INIT_LIST_HEAD ( &saved );
225
+	list_splice_init ( &ipv6_miniroutes, &saved );
226
+
227
+	/* Construct routing table */
228
+	for ( i = 0 ; i < table->count ; i++ ) {
229
+
230
+		/* Parse address and router (if applicable) */
231
+		route = &table->routes[i];
232
+		okx ( inet6_aton ( route->address, &address ) == 0,
233
+		      file, line );
234
+		if ( route->router ) {
235
+			okx ( inet6_aton ( route->router, &router ) == 0,
236
+			      file, line );
237
+		}
238
+
239
+		/* Add routing table entry */
240
+		okx ( ipv6_add_miniroute ( &ipv6_test_netdev, &address,
241
+					   route->prefix_len,
242
+					   ( route->router ?
243
+					     &router : NULL ) ) == 0,
244
+		      file, line );
245
+	}
246
+
247
+	/* Save constructed routing table */
248
+	list_splice_init ( &ipv6_miniroutes, &table->list );
249
+
250
+	/* Restore original routing table */
251
+	list_splice ( &saved, &ipv6_miniroutes );
252
+}
253
+#define ipv6_table_ok( table )						\
254
+	ipv6_table_okx ( table, __FILE__, __LINE__ )
255
+
256
+/**
257
+ * Report an ipv6_route() test result
258
+ *
259
+ * @v table		Test routing table
260
+ * @v dest		Destination address
261
+ * @v src		Expected source address, or NULL to expect failure
262
+ * @v next		Expected next hop address, or NULL to expect destination
263
+ * @v file		Test code file
264
+ * @v line		Test code line
265
+ */
266
+static void ipv6_route_okx ( struct ipv6_test_table *table, const char *dest,
267
+			     const char *src, const char *next,
268
+			     const char *file, unsigned int line ) {
269
+	struct in6_addr in_dest;
270
+	struct in6_addr in_src;
271
+	struct in6_addr in_next;
272
+	struct in6_addr *actual;
273
+	struct ipv6_miniroute *miniroute;
274
+	struct list_head saved;
275
+
276
+	/* Switch to test routing table */
277
+	INIT_LIST_HEAD ( &saved );
278
+	list_splice_init ( &ipv6_miniroutes, &saved );
279
+	list_splice_init ( &table->list, &ipv6_miniroutes );
280
+
281
+	/* Parse addresses */
282
+	okx ( inet6_aton ( dest, &in_dest ) == 0, file, line );
283
+	if ( src )
284
+		okx ( inet6_aton ( src, &in_src ) == 0, file, line );
285
+	if ( next ) {
286
+		okx ( inet6_aton ( next, &in_next ) == 0, file, line );
287
+	} else {
288
+		memcpy ( &in_next, &in_dest, sizeof ( in_next ) );
289
+	}
290
+
291
+	/* Perform routing */
292
+	actual = &in_dest;
293
+	miniroute = ipv6_route ( ipv6_test_netdev.index, &actual );
294
+
295
+	/* Validate result */
296
+	if ( src ) {
297
+
298
+		/* Check that a route was found */
299
+		okx ( miniroute != NULL, file, line );
300
+		DBG ( "ipv6_route ( %s ) = %s", dest, inet6_ntoa ( actual ) );
301
+		DBG ( " from %s\n", inet6_ntoa ( &miniroute->address ) );
302
+
303
+		/* Check that expected source address was used */
304
+		okx ( memcmp ( &miniroute->address, &in_src,
305
+			       sizeof ( in_src ) ) == 0, file, line );
306
+
307
+		/* Check that expected next hop address was used */
308
+		okx ( memcmp ( actual, &in_next, sizeof ( *actual ) ) == 0,
309
+		      file, line );
310
+
311
+	} else {
312
+
313
+		/* Routing is expected to fail */
314
+		okx ( miniroute == NULL, file, line );
315
+	}
316
+
317
+	/* Restore original routing table */
318
+	list_splice_init ( &ipv6_miniroutes, &table->list );
319
+	list_splice ( &saved, &ipv6_miniroutes );
320
+}
321
+#define ipv6_route_ok( table, dest, src, next )				\
322
+	ipv6_route_okx ( table, dest, src, next, __FILE__, __LINE__ )
323
+
324
+/**
325
+ * Destroy test routing table
326
+ *
327
+ * @v table		Test routing table
328
+ */
329
+static void ipv6_table_del ( struct ipv6_test_table *table ) {
330
+	struct ipv6_miniroute *miniroute;
331
+	struct ipv6_miniroute *tmp;
332
+	struct list_head saved;
333
+
334
+	/* Switch to test routing table */
335
+	INIT_LIST_HEAD ( &saved );
336
+	list_splice_init ( &ipv6_miniroutes, &saved );
337
+	list_splice_init ( &table->list, &ipv6_miniroutes );
338
+
339
+	/* Delete all existing routes */
340
+	list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list )
341
+		ipv6_del_miniroute ( miniroute );
342
+
343
+	/* Restore original routing table */
344
+	list_splice ( &saved, &ipv6_miniroutes );
345
+}
346
+
136 347
 /**
137 348
  * Perform IPv6 self-tests
138 349
  *
@@ -142,16 +353,34 @@ static void ipv6_test_exec ( void ) {
142 353
 	/* Address testing macros */
143 354
 	ok (   IN6_IS_ADDR_UNSPECIFIED ( &sample_unspecified ) );
144 355
 	ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_link_local ) );
356
+	ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_site_local ) );
357
+	ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_ula ) );
145 358
 	ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_global ) );
146 359
 	ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_multicast ) );
147 360
 	ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_unspecified ) );
148 361
 	ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_link_local ) );
362
+	ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_site_local ) );
363
+	ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_ula ) );
149 364
 	ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_global ) );
150 365
 	ok (   IN6_IS_ADDR_MULTICAST ( &sample_multicast ) );
151 366
 	ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_unspecified ) );
152 367
 	ok (   IN6_IS_ADDR_LINKLOCAL ( &sample_link_local ) );
368
+	ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_site_local ) );
369
+	ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_ula ) );
153 370
 	ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_global ) );
154 371
 	ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_multicast ) );
372
+	ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_unspecified ) );
373
+	ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_link_local ) );
374
+	ok (   IN6_IS_ADDR_SITELOCAL ( &sample_site_local ) );
375
+	ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_ula ) );
376
+	ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_global ) );
377
+	ok ( ! IN6_IS_ADDR_SITELOCAL ( &sample_multicast ) );
378
+	ok ( ! IN6_IS_ADDR_ULA ( &sample_unspecified ) );
379
+	ok ( ! IN6_IS_ADDR_ULA ( &sample_link_local ) );
380
+	ok ( ! IN6_IS_ADDR_ULA ( &sample_site_local ) );
381
+	ok (   IN6_IS_ADDR_ULA ( &sample_ula ) );
382
+	ok ( ! IN6_IS_ADDR_ULA ( &sample_global ) );
383
+	ok ( ! IN6_IS_ADDR_ULA ( &sample_multicast ) );
155 384
 
156 385
 	/* inet6_ntoa() tests */
157 386
 	inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4,
@@ -228,6 +457,58 @@ static void ipv6_test_exec ( void ) {
228 457
 	inet6_aton_fail_ok ( "2001:db8::1::2" );
229 458
 	inet6_aton_fail_ok ( "2001:ba8:0:1d4:::6950:5845" );
230 459
 	inet6_aton_fail_ok ( ":::" );
460
+
461
+	/* Create test routing tables */
462
+	ipv6_table_ok ( &table_link_local );
463
+	ipv6_table_ok ( &table_normal );
464
+	ipv6_table_ok ( &table_multi );
465
+
466
+	/* Routing table with only a link-local address */
467
+	ipv6_route_ok ( &table_link_local, "fe80::1",
468
+			"fe80::69ff:fe50:5845", NULL );
469
+	ipv6_route_ok ( &table_link_local, "2001:db8:1::1",
470
+			NULL, NULL );
471
+	ipv6_route_ok ( &table_link_local, "ff02::1",
472
+			"fe80::69ff:fe50:5845", NULL );
473
+
474
+	/** Routing table with a global address */
475
+	ipv6_route_ok ( &table_normal, "fe80::1",
476
+			"fe80::69ff:fe50:5845", NULL );
477
+	ipv6_route_ok ( &table_normal, "2001:db8:3::42",
478
+			"2001:db8:3::1", NULL );
479
+	ipv6_route_ok ( &table_normal, "2001:ba8:0:1d4::6950:5845",
480
+			"2001:db8:3::1", "fe80::1" );
481
+	ipv6_route_ok ( &table_normal, "ff02::1",
482
+			"fe80::69ff:fe50:5845", NULL );
483
+	ipv6_route_ok ( &table_normal, "ff0e::1",
484
+			"2001:db8:3::1", NULL );
485
+
486
+	/** Routing table with multiple addresses and routers */
487
+	ipv6_route_ok ( &table_multi, "fe80::1",
488
+			"fe80::69ff:fe50:5845", NULL );
489
+	ipv6_route_ok ( &table_multi, "2001:db8:3::17",
490
+			"2001:db8:3::1", NULL );
491
+	ipv6_route_ok ( &table_multi, "2001:db8:5::92",
492
+			"2001:db8:5::1", NULL );
493
+	ipv6_route_ok ( &table_multi, "2001:db8:42::17",
494
+			"2001:db8:42::1", NULL );
495
+	ipv6_route_ok ( &table_multi, "2001:db8:5:1::17",
496
+			"2001:db8:3::1", "fe80::1" );
497
+	ipv6_route_ok ( &table_multi, "fd44:9112:6442::1",
498
+			"fd44:9112:6442::69ff:fe50:5845", NULL );
499
+	ipv6_route_ok ( &table_multi, "fd70:6ba9:50ae::1",
500
+			"fd70:6ba9:50ae::69ff:fe50:5845", NULL );
501
+	ipv6_route_ok ( &table_multi, "fd40::3",
502
+			"fd44:9112:6442::69ff:fe50:5845", "fe80::1" );
503
+	ipv6_route_ok ( &table_multi, "fd70::2",
504
+			"fd70:6ba9:50ae::69ff:fe50:5845", "fe80::3" );
505
+	ipv6_route_ok ( &table_multi, "ff02::1",
506
+			"fe80::69ff:fe50:5845", NULL );
507
+
508
+	/* Destroy test routing tables */
509
+	ipv6_table_del ( &table_link_local );
510
+	ipv6_table_del ( &table_normal );
511
+	ipv6_table_del ( &table_multi );
231 512
 }
232 513
 
233 514
 /** IPv6 self-test */

Loading…
Cancel
Save