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
 	( ( *( ( const uint16_t * ) (addr) ) & htons ( 0xffc0 ) ) ==	\
69
 	( ( *( ( const uint16_t * ) (addr) ) & htons ( 0xffc0 ) ) ==	\
70
 	  htons ( 0xfe80 ) )
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
  * IPv4 socket address
80
  * IPv4 socket address

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

158
 	uint8_t next_header;
158
 	uint8_t next_header;
159
 } __attribute__ (( packed ));
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
 /** An IPv6 address/routing table entry */
179
 /** An IPv6 address/routing table entry */
162
 struct ipv6_miniroute {
180
 struct ipv6_miniroute {
163
 	/** List of miniroutes */
181
 	/** List of miniroutes */
174
 	struct in6_addr prefix_mask;
192
 	struct in6_addr prefix_mask;
175
 	/** Router address */
193
 	/** Router address */
176
 	struct in6_addr router;
194
 	struct in6_addr router;
195
+	/** Scope */
196
+	unsigned int scope;
177
 	/** Flags */
197
 	/** Flags */
178
 	unsigned int flags;
198
 	unsigned int flags;
179
 };
199
 };
244
 	addr->s6_addr[15] = 2;
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
 /** IPv6 settings sibling order */
279
 /** IPv6 settings sibling order */
248
 enum ipv6_settings_order {
280
 enum ipv6_settings_order {
249
 	/** No address */
281
 	/** No address */
264
 extern struct net_protocol ipv6_protocol __net_protocol;
296
 extern struct net_protocol ipv6_protocol __net_protocol;
265
 
297
 
266
 extern int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr );
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
 extern int parse_ipv6_setting ( const struct setting_type *type,
306
 extern int parse_ipv6_setting ( const struct setting_type *type,
268
 				const char *value, void *buf, size_t len );
307
 				const char *value, void *buf, size_t len );
269
 extern int format_ipv6_setting ( const struct setting_type *type,
308
 extern int format_ipv6_setting ( const struct setting_type *type,

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

23
 #include <stdio.h>
23
 #include <stdio.h>
24
 #include <stdlib.h>
24
 #include <stdlib.h>
25
 #include <string.h>
25
 #include <string.h>
26
+#include <strings.h>
26
 #include <errno.h>
27
 #include <errno.h>
27
 #include <assert.h>
28
 #include <assert.h>
28
 #include <byteswap.h>
29
 #include <byteswap.h>
78
 	return crc32_le ( 0, in, sizeof ( *in ) );
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
  * Dump IPv6 routing table entry
117
  * Dump IPv6 routing table entry
83
  *
118
  *
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
  * @v miniroute		Routing table entry
159
  * @v miniroute		Routing table entry
125
  * @v address		IPv6 address
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
 	unsigned int i;
166
 	unsigned int i;
167
+	uint32_t diff;
131
 
168
 
132
 	for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) /
169
 	for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) /
133
 			    sizeof ( address->s6_addr32[0] ) ) ; i++ ) {
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
 static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev,
192
 static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev,
149
 						struct in6_addr *address ) {
193
 						struct in6_addr *address ) {
150
 	struct ipv6_miniroute *miniroute;
194
 	struct ipv6_miniroute *miniroute;
195
+	unsigned int match_len;
151
 
196
 
152
 	list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
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
 	return NULL;
205
 	return NULL;
159
 }
206
 }
167
  * @v router		Router address (if any)
214
  * @v router		Router address (if any)
168
  * @ret rc		Return status code
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
 	struct ipv6_miniroute *miniroute;
219
 	struct ipv6_miniroute *miniroute;
175
 	uint8_t *prefix_mask;
220
 	uint8_t *prefix_mask;
176
 	unsigned int remaining;
221
 	unsigned int remaining;
178
 
223
 
179
 	/* Find or create routing table entry */
224
 	/* Find or create routing table entry */
180
 	miniroute = ipv6_miniroute ( netdev, address );
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
 		/* Create new routing table entry */
233
 		/* Create new routing table entry */
184
 		miniroute = zalloc ( sizeof ( *miniroute ) );
234
 		miniroute = zalloc ( sizeof ( *miniroute ) );
202
 		}
252
 		}
203
 		if ( remaining )
253
 		if ( remaining )
204
 			*prefix_mask <<= ( 8 - remaining );
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
 	/* Set or update address, if applicable */
260
 	/* Set or update address, if applicable */
211
 	for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) /
261
 	for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) /
212
 			    sizeof ( address->s6_addr32[0] ) ) ; i++ ) {
262
 			    sizeof ( address->s6_addr32[0] ) ) ; i++ ) {
220
 	if ( miniroute->prefix_len == IPV6_MAX_PREFIX_LEN )
270
 	if ( miniroute->prefix_len == IPV6_MAX_PREFIX_LEN )
221
 		miniroute->flags |= IPV6_HAS_ADDRESS;
271
 		miniroute->flags |= IPV6_HAS_ADDRESS;
222
 
272
 
273
+	/* Update scope */
274
+	miniroute->scope = ipv6_scope ( &miniroute->address );
275
+
223
 	/* Set or update router, if applicable */
276
 	/* Set or update router, if applicable */
224
 	if ( router ) {
277
 	if ( router ) {
225
 		memcpy ( &miniroute->router, router,
278
 		memcpy ( &miniroute->router, router,
226
 			 sizeof ( miniroute->router ) );
279
 			 sizeof ( miniroute->router ) );
227
 		miniroute->flags |= IPV6_HAS_ROUTER;
280
 		miniroute->flags |= IPV6_HAS_ROUTER;
228
-		list_del ( &miniroute->list );
229
-		list_add_tail ( &miniroute->list, &ipv6_miniroutes );
230
 	}
281
 	}
231
 
282
 
232
 	ipv6_dump_miniroute ( miniroute );
283
 	ipv6_dump_miniroute ( miniroute );
238
  *
289
  *
239
  * @v miniroute		Routing table entry
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
 	netdev_put ( miniroute->netdev );
294
 	netdev_put ( miniroute->netdev );
244
 	list_del ( &miniroute->list );
295
 	list_del ( &miniroute->list );
253
  * @ret dest		Next hop destination address
304
  * @ret dest		Next hop destination address
254
  * @ret miniroute	Routing table entry to use, or NULL if no route
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
 	struct ipv6_miniroute *miniroute;
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
 	/* Find first usable route in routing table */
319
 	/* Find first usable route in routing table */
261
 	list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
320
 	list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
264
 		if ( ! netdev_is_open ( miniroute->netdev ) )
323
 		if ( ! netdev_is_open ( miniroute->netdev ) )
265
 			continue;
324
 			continue;
266
 
325
 
267
-		/* Skip routing table entries with no usable source address */
326
+		/* Skip entries with no usable source address */
268
 		if ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) )
327
 		if ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) )
269
 			continue;
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
 	return NULL;
374
 	return NULL;
299
 }
375
 }
300
 
376
 
880
 	const char *netdev_name;
956
 	const char *netdev_name;
881
 
957
 
882
 	/* Identify network device, if applicable */
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
 		netdev = find_netdev_by_index ( sin6->sin6_scope_id );
960
 		netdev = find_netdev_by_index ( sin6->sin6_scope_id );
885
 		netdev_name = ( netdev ? netdev->name : "UNKNOWN" );
961
 		netdev_name = ( netdev ? netdev->name : "UNKNOWN" );
886
 	} else {
962
 	} else {
946
 		}
1022
 		}
947
 		sin6->sin6_scope_id = netdev->index;
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
 		/* If no network device is explicitly specified for a
1028
 		/* If no network device is explicitly specified for a
952
 		 * link-local or multicast address, default to using
1029
 		 * link-local or multicast address, default to using

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

41
 /** Define inline IPv6 address */
41
 /** Define inline IPv6 address */
42
 #define IPV6(...) { __VA_ARGS__ }
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
 /** The unspecified IPv6 address */
76
 /** The unspecified IPv6 address */
45
 static const struct in6_addr sample_unspecified = {
77
 static const struct in6_addr sample_unspecified = {
46
 	.s6_addr = IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
78
 	.s6_addr = IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53
 			  0x00, 0x00, 0x69, 0xff, 0xfe, 0x50, 0x58, 0x45 ),
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
 /** A sample global IPv6 address */
100
 /** A sample global IPv6 address */
57
 static const struct in6_addr sample_global = {
101
 static const struct in6_addr sample_global = {
58
 	.s6_addr = IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4,
102
 	.s6_addr = IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4,
65
 			  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
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
  * Report an inet6_ntoa() test result
138
  * Report an inet6_ntoa() test result
70
  *
139
  *
133
 #define inet6_aton_fail_ok( text )					\
202
 #define inet6_aton_fail_ok( text )					\
134
 	inet6_aton_fail_okx ( text, __FILE__, __LINE__ )
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
  * Perform IPv6 self-tests
348
  * Perform IPv6 self-tests
138
  *
349
  *
142
 	/* Address testing macros */
353
 	/* Address testing macros */
143
 	ok (   IN6_IS_ADDR_UNSPECIFIED ( &sample_unspecified ) );
354
 	ok (   IN6_IS_ADDR_UNSPECIFIED ( &sample_unspecified ) );
144
 	ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_link_local ) );
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
 	ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_global ) );
358
 	ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_global ) );
146
 	ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_multicast ) );
359
 	ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_multicast ) );
147
 	ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_unspecified ) );
360
 	ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_unspecified ) );
148
 	ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_link_local ) );
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
 	ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_global ) );
364
 	ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_global ) );
150
 	ok (   IN6_IS_ADDR_MULTICAST ( &sample_multicast ) );
365
 	ok (   IN6_IS_ADDR_MULTICAST ( &sample_multicast ) );
151
 	ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_unspecified ) );
366
 	ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_unspecified ) );
152
 	ok (   IN6_IS_ADDR_LINKLOCAL ( &sample_link_local ) );
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
 	ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_global ) );
370
 	ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_global ) );
154
 	ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_multicast ) );
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
 	/* inet6_ntoa() tests */
385
 	/* inet6_ntoa() tests */
157
 	inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4,
386
 	inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4,
228
 	inet6_aton_fail_ok ( "2001:db8::1::2" );
457
 	inet6_aton_fail_ok ( "2001:db8::1::2" );
229
 	inet6_aton_fail_ok ( "2001:ba8:0:1d4:::6950:5845" );
458
 	inet6_aton_fail_ok ( "2001:ba8:0:1d4:::6950:5845" );
230
 	inet6_aton_fail_ok ( ":::" );
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
 /** IPv6 self-test */
514
 /** IPv6 self-test */

Loading…
Cancel
Save