|
@@ -62,12 +62,13 @@ static int ndp_tx_neighbour ( struct net_device *netdev,
|
62
|
62
|
struct ll_protocol *ll_protocol = netdev->ll_protocol;
|
63
|
63
|
struct io_buffer *iobuf;
|
64
|
64
|
struct ndp_neighbour_header *neigh;
|
|
65
|
+ struct ndp_ll_addr_option *ll_addr_opt;
|
65
|
66
|
size_t option_len;
|
66
|
67
|
size_t len;
|
67
|
68
|
int rc;
|
68
|
69
|
|
69
|
70
|
/* Allocate and populate buffer */
|
70
|
|
- option_len = ( ( sizeof ( neigh->option[0] ) +
|
|
71
|
+ option_len = ( ( sizeof ( *ll_addr_opt ) +
|
71
|
72
|
ll_protocol->ll_addr_len + NDP_OPTION_BLKSZ - 1 ) &
|
72
|
73
|
~( NDP_OPTION_BLKSZ - 1 ) );
|
73
|
74
|
len = ( sizeof ( *neigh ) + option_len );
|
|
@@ -80,9 +81,10 @@ static int ndp_tx_neighbour ( struct net_device *netdev,
|
80
|
81
|
neigh->icmp.type = icmp_type;
|
81
|
82
|
neigh->flags = flags;
|
82
|
83
|
memcpy ( &neigh->target, target, sizeof ( neigh->target ) );
|
83
|
|
- neigh->option[0].type = option_type;
|
84
|
|
- neigh->option[0].blocks = ( option_len / NDP_OPTION_BLKSZ );
|
85
|
|
- memcpy ( neigh->option[0].value, netdev->ll_addr,
|
|
84
|
+ ll_addr_opt = &neigh->option[0].ll_addr;
|
|
85
|
+ ll_addr_opt->header.type = option_type;
|
|
86
|
+ ll_addr_opt->header.blocks = ( option_len / NDP_OPTION_BLKSZ );
|
|
87
|
+ memcpy ( ll_addr_opt->ll_addr, netdev->ll_addr,
|
86
|
88
|
ll_protocol->ll_addr_len );
|
87
|
89
|
neigh->icmp.chksum = tcpip_chksum ( neigh, len );
|
88
|
90
|
|
|
@@ -143,17 +145,18 @@ struct neighbour_discovery ndp_discovery = {
|
143
|
145
|
* @v netdev Network device
|
144
|
146
|
* @v sin6_src Source socket address
|
145
|
147
|
* @v ndp NDP packet
|
146
|
|
- * @v ll_addr Source link-layer address
|
147
|
|
- * @v ll_addr_len Source link-layer address length
|
|
148
|
+ * @v option NDP option
|
|
149
|
+ * @v len NDP option length
|
148
|
150
|
* @ret rc Return status code
|
149
|
151
|
*/
|
150
|
152
|
static int
|
151
|
153
|
ndp_rx_neighbour_solicitation_ll_source ( struct net_device *netdev,
|
152
|
154
|
struct sockaddr_in6 *sin6_src,
|
153
|
155
|
union ndp_header *ndp,
|
154
|
|
- const void *ll_addr,
|
155
|
|
- size_t ll_addr_len ) {
|
|
156
|
+ union ndp_option *option,
|
|
157
|
+ size_t len ) {
|
156
|
158
|
struct ndp_neighbour_header *neigh = &ndp->neigh;
|
|
159
|
+ struct ndp_ll_addr_option *ll_addr_opt = &option->ll_addr;
|
157
|
160
|
struct ll_protocol *ll_protocol = netdev->ll_protocol;
|
158
|
161
|
int rc;
|
159
|
162
|
|
|
@@ -164,20 +167,21 @@ ndp_rx_neighbour_solicitation_ll_source ( struct net_device *netdev,
|
164
|
167
|
return 0;
|
165
|
168
|
|
166
|
169
|
/* Sanity check */
|
167
|
|
- if ( ll_addr_len < ll_protocol->ll_addr_len ) {
|
|
170
|
+ if ( offsetof ( typeof ( *ll_addr_opt ),
|
|
171
|
+ ll_addr[ll_protocol->ll_addr_len] ) > len ) {
|
168
|
172
|
DBGC ( netdev, "NDP neighbour solicitation link-layer address "
|
169
|
|
- "too short at %zd bytes (min %d bytes)\n",
|
170
|
|
- ll_addr_len, ll_protocol->ll_addr_len );
|
|
173
|
+ "option too short at %zd bytes\n", len );
|
171
|
174
|
return -EINVAL;
|
172
|
175
|
}
|
173
|
176
|
|
174
|
177
|
/* Create or update neighbour cache entry */
|
175
|
178
|
if ( ( rc = neighbour_define ( netdev, &ipv6_protocol,
|
176
|
179
|
&sin6_src->sin6_addr,
|
177
|
|
- ll_addr ) ) != 0 ) {
|
|
180
|
+ ll_addr_opt->ll_addr ) ) != 0 ) {
|
178
|
181
|
DBGC ( netdev, "NDP could not define %s => %s: %s\n",
|
179
|
182
|
inet6_ntoa ( &sin6_src->sin6_addr ),
|
180
|
|
- ll_protocol->ntoa ( ll_addr ), strerror ( rc ) );
|
|
183
|
+ ll_protocol->ntoa ( ll_addr_opt->ll_addr ),
|
|
184
|
+ strerror ( rc ) );
|
181
|
185
|
return rc;
|
182
|
186
|
}
|
183
|
187
|
|
|
@@ -199,8 +203,8 @@ ndp_rx_neighbour_solicitation_ll_source ( struct net_device *netdev,
|
199
|
203
|
* @v netdev Network device
|
200
|
204
|
* @v sin6_src Source socket address
|
201
|
205
|
* @v ndp NDP packet
|
202
|
|
- * @v ll_addr Target link-layer address
|
203
|
|
- * @v ll_addr_len Target link-layer address length
|
|
206
|
+ * @v option NDP option
|
|
207
|
+ * @v len NDP option length
|
204
|
208
|
* @ret rc Return status code
|
205
|
209
|
*/
|
206
|
210
|
static int
|
|
@@ -208,26 +212,28 @@ ndp_rx_neighbour_advertisement_ll_target ( struct net_device *netdev,
|
208
|
212
|
struct sockaddr_in6 *sin6_src
|
209
|
213
|
__unused,
|
210
|
214
|
union ndp_header *ndp,
|
211
|
|
- const void *ll_addr,
|
212
|
|
- size_t ll_addr_len ) {
|
|
215
|
+ union ndp_option *option,
|
|
216
|
+ size_t len ) {
|
213
|
217
|
struct ndp_neighbour_header *neigh = &ndp->neigh;
|
|
218
|
+ struct ndp_ll_addr_option *ll_addr_opt = &option->ll_addr;
|
214
|
219
|
struct ll_protocol *ll_protocol = netdev->ll_protocol;
|
215
|
220
|
int rc;
|
216
|
221
|
|
217
|
222
|
/* Sanity check */
|
218
|
|
- if ( ll_addr_len < ll_protocol->ll_addr_len ) {
|
|
223
|
+ if ( offsetof ( typeof ( *ll_addr_opt ),
|
|
224
|
+ ll_addr[ll_protocol->ll_addr_len] ) > len ) {
|
219
|
225
|
DBGC ( netdev, "NDP neighbour advertisement link-layer address "
|
220
|
|
- "too short at %zd bytes (min %d bytes)\n",
|
221
|
|
- ll_addr_len, ll_protocol->ll_addr_len );
|
|
226
|
+ "option too short at %zd bytes\n", len );
|
222
|
227
|
return -EINVAL;
|
223
|
228
|
}
|
224
|
229
|
|
225
|
230
|
/* Update neighbour cache entry, if any */
|
226
|
231
|
if ( ( rc = neighbour_update ( netdev, &ipv6_protocol, &neigh->target,
|
227
|
|
- ll_addr ) ) != 0 ) {
|
|
232
|
+ ll_addr_opt->ll_addr ) ) != 0 ) {
|
228
|
233
|
DBGC ( netdev, "NDP could not update %s => %s: %s\n",
|
229
|
234
|
inet6_ntoa ( &neigh->target ),
|
230
|
|
- ll_protocol->ntoa ( ll_addr ), strerror ( rc ) );
|
|
235
|
+ ll_protocol->ntoa ( ll_addr_opt->ll_addr ),
|
|
236
|
+ strerror ( rc ) );
|
231
|
237
|
return rc;
|
232
|
238
|
}
|
233
|
239
|
|
|
@@ -240,40 +246,94 @@ ndp_rx_neighbour_advertisement_ll_target ( struct net_device *netdev,
|
240
|
246
|
* @v netdev Network device
|
241
|
247
|
* @v sin6_src Source socket address
|
242
|
248
|
* @v ndp NDP packet
|
243
|
|
- * @v ll_addr Target link-layer address
|
244
|
|
- * @v ll_addr_len Target link-layer address length
|
|
249
|
+ * @v option NDP option
|
|
250
|
+ * @v len NDP option length
|
245
|
251
|
* @ret rc Return status code
|
246
|
252
|
*/
|
247
|
253
|
static int
|
248
|
254
|
ndp_rx_router_advertisement_ll_source ( struct net_device *netdev,
|
249
|
255
|
struct sockaddr_in6 *sin6_src,
|
250
|
256
|
union ndp_header *ndp __unused,
|
251
|
|
- const void *ll_addr,
|
252
|
|
- size_t ll_addr_len ) {
|
|
257
|
+ union ndp_option *option, size_t len ) {
|
|
258
|
+ struct ndp_ll_addr_option *ll_addr_opt = &option->ll_addr;
|
253
|
259
|
struct ll_protocol *ll_protocol = netdev->ll_protocol;
|
254
|
260
|
int rc;
|
255
|
261
|
|
256
|
262
|
/* Sanity check */
|
257
|
|
- if ( ll_addr_len < ll_protocol->ll_addr_len ) {
|
|
263
|
+ if ( offsetof ( typeof ( *ll_addr_opt ),
|
|
264
|
+ ll_addr[ll_protocol->ll_addr_len] ) > len ) {
|
258
|
265
|
DBGC ( netdev, "NDP router advertisement link-layer address "
|
259
|
|
- "too short at %zd bytes (min %d bytes)\n",
|
260
|
|
- ll_addr_len, ll_protocol->ll_addr_len );
|
|
266
|
+ "option too short at %zd bytes\n", len );
|
261
|
267
|
return -EINVAL;
|
262
|
268
|
}
|
263
|
269
|
|
264
|
270
|
/* Define neighbour cache entry */
|
265
|
271
|
if ( ( rc = neighbour_define ( netdev, &ipv6_protocol,
|
266
|
272
|
&sin6_src->sin6_addr,
|
267
|
|
- ll_addr ) ) != 0 ) {
|
|
273
|
+ ll_addr_opt->ll_addr ) ) != 0 ) {
|
268
|
274
|
DBGC ( netdev, "NDP could not define %s => %s: %s\n",
|
269
|
275
|
inet6_ntoa ( &sin6_src->sin6_addr ),
|
270
|
|
- ll_protocol->ntoa ( ll_addr ), strerror ( rc ) );
|
|
276
|
+ ll_protocol->ntoa ( ll_addr_opt->ll_addr ),
|
|
277
|
+ strerror ( rc ) );
|
271
|
278
|
return rc;
|
272
|
279
|
}
|
273
|
280
|
|
274
|
281
|
return 0;
|
275
|
282
|
}
|
276
|
283
|
|
|
284
|
+/**
|
|
285
|
+ * Process NDP router advertisement prefix information option
|
|
286
|
+ *
|
|
287
|
+ * @v netdev Network device
|
|
288
|
+ * @v sin6_src Source socket address
|
|
289
|
+ * @v ndp NDP packet
|
|
290
|
+ * @v option NDP option
|
|
291
|
+ * @v len NDP option length
|
|
292
|
+ * @ret rc Return status code
|
|
293
|
+ */
|
|
294
|
+static int
|
|
295
|
+ndp_rx_router_advertisement_prefix ( struct net_device *netdev,
|
|
296
|
+ struct sockaddr_in6 *sin6_src,
|
|
297
|
+ union ndp_header *ndp,
|
|
298
|
+ union ndp_option *option, size_t len ) {
|
|
299
|
+ struct ndp_router_advertisement_header *radv = &ndp->radv;
|
|
300
|
+ struct ndp_prefix_information_option *prefix_opt = &option->prefix;
|
|
301
|
+ struct in6_addr *router = &sin6_src->sin6_addr;
|
|
302
|
+ int rc;
|
|
303
|
+
|
|
304
|
+ /* Sanity check */
|
|
305
|
+ if ( sizeof ( *prefix_opt ) > len ) {
|
|
306
|
+ DBGC ( netdev, "NDP router advertisement prefix option too "
|
|
307
|
+ "short at %zd bytes\n", len );
|
|
308
|
+ return -EINVAL;
|
|
309
|
+ }
|
|
310
|
+ DBGC ( netdev, "NDP found %sdefault router %s ",
|
|
311
|
+ ( radv->lifetime ? "" : "non-" ),
|
|
312
|
+ inet6_ntoa ( &sin6_src->sin6_addr ) );
|
|
313
|
+ DBGC ( netdev, "for %s-link %sautonomous prefix %s/%d\n",
|
|
314
|
+ ( ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) ? "on" : "off" ),
|
|
315
|
+ ( ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) ? "" : "non-" ),
|
|
316
|
+ inet6_ntoa ( &prefix_opt->prefix ),
|
|
317
|
+ prefix_opt->prefix_len );
|
|
318
|
+
|
|
319
|
+ /* Perform stateless address autoconfiguration, if applicable */
|
|
320
|
+ if ( ( prefix_opt->flags &
|
|
321
|
+ ( NDP_PREFIX_ON_LINK | NDP_PREFIX_AUTONOMOUS ) ) ==
|
|
322
|
+ ( NDP_PREFIX_ON_LINK | NDP_PREFIX_AUTONOMOUS ) ) {
|
|
323
|
+ if ( ( rc = ipv6_slaac ( netdev, &prefix_opt->prefix,
|
|
324
|
+ prefix_opt->prefix_len,
|
|
325
|
+ ( radv->lifetime ?
|
|
326
|
+ router : NULL ) ) ) != 0 ) {
|
|
327
|
+ DBGC ( netdev, "NDP could not autoconfigure prefix %s/"
|
|
328
|
+ "%d: %s\n", inet6_ntoa ( &prefix_opt->prefix ),
|
|
329
|
+ prefix_opt->prefix_len, strerror ( rc ) );
|
|
330
|
+ return rc;
|
|
331
|
+ }
|
|
332
|
+ }
|
|
333
|
+
|
|
334
|
+ return 0;
|
|
335
|
+}
|
|
336
|
+
|
277
|
337
|
/** An NDP option handler */
|
278
|
338
|
struct ndp_option_handler {
|
279
|
339
|
/** ICMPv6 type */
|
|
@@ -286,12 +346,12 @@ struct ndp_option_handler {
|
286
|
346
|
* @v netdev Network device
|
287
|
347
|
* @v sin6_src Source socket address
|
288
|
348
|
* @v ndp NDP packet
|
289
|
|
- * @v value Option value
|
290
|
|
- * @v len Option length
|
|
349
|
+ * @v option NDP option
|
291
|
350
|
* @ret rc Return status code
|
292
|
351
|
*/
|
293
|
352
|
int ( * rx ) ( struct net_device *netdev, struct sockaddr_in6 *sin6_src,
|
294
|
|
- union ndp_header *ndp, const void *value, size_t len );
|
|
353
|
+ union ndp_header *ndp, union ndp_option *option,
|
|
354
|
+ size_t len );
|
295
|
355
|
};
|
296
|
356
|
|
297
|
357
|
/** NDP option handlers */
|
|
@@ -311,6 +371,11 @@ static struct ndp_option_handler ndp_option_handlers[] = {
|
311
|
371
|
.option_type = NDP_OPT_LL_SOURCE,
|
312
|
372
|
.rx = ndp_rx_router_advertisement_ll_source,
|
313
|
373
|
},
|
|
374
|
+ {
|
|
375
|
+ .icmp_type = ICMPV6_ROUTER_ADVERTISEMENT,
|
|
376
|
+ .option_type = NDP_OPT_PREFIX,
|
|
377
|
+ .rx = ndp_rx_router_advertisement_prefix,
|
|
378
|
+ },
|
314
|
379
|
};
|
315
|
380
|
|
316
|
381
|
/**
|
|
@@ -319,15 +384,13 @@ static struct ndp_option_handler ndp_option_handlers[] = {
|
319
|
384
|
* @v netdev Network device
|
320
|
385
|
* @v sin6_src Source socket address
|
321
|
386
|
* @v ndp NDP packet
|
322
|
|
- * @v type Option type
|
323
|
|
- * @v value Option value
|
|
387
|
+ * @v option NDP option
|
324
|
388
|
* @v len Option length
|
325
|
389
|
* @ret rc Return status code
|
326
|
390
|
*/
|
327
|
391
|
static int ndp_rx_option ( struct net_device *netdev,
|
328
|
|
- struct sockaddr_in6 *sin6_src,
|
329
|
|
- union ndp_header *ndp, unsigned int type,
|
330
|
|
- const void *value, size_t len ) {
|
|
392
|
+ struct sockaddr_in6 *sin6_src, union ndp_header *ndp,
|
|
393
|
+ union ndp_option *option, size_t len ) {
|
331
|
394
|
struct ndp_option_handler *handler;
|
332
|
395
|
unsigned int i;
|
333
|
396
|
|
|
@@ -336,9 +399,9 @@ static int ndp_rx_option ( struct net_device *netdev,
|
336
|
399
|
sizeof ( ndp_option_handlers[0] ) ) ; i++ ) {
|
337
|
400
|
handler = &ndp_option_handlers[i];
|
338
|
401
|
if ( ( handler->icmp_type == ndp->icmp.type ) &&
|
339
|
|
- ( handler->option_type == type ) ) {
|
|
402
|
+ ( handler->option_type == option->header.type ) ) {
|
340
|
403
|
return handler->rx ( netdev, sin6_src, ndp,
|
341
|
|
- value, len );
|
|
404
|
+ option, len );
|
342
|
405
|
}
|
343
|
406
|
}
|
344
|
407
|
|
|
@@ -360,10 +423,9 @@ static int ndp_rx ( struct io_buffer *iobuf,
|
360
|
423
|
struct sockaddr_in6 *sin6_src,
|
361
|
424
|
size_t offset ) {
|
362
|
425
|
union ndp_header *ndp = iobuf->data;
|
363
|
|
- struct ndp_option *option;
|
|
426
|
+ union ndp_option *option;
|
364
|
427
|
size_t remaining;
|
365
|
428
|
size_t option_len;
|
366
|
|
- size_t option_value_len;
|
367
|
429
|
int rc;
|
368
|
430
|
|
369
|
431
|
/* Sanity check */
|
|
@@ -380,23 +442,21 @@ static int ndp_rx ( struct io_buffer *iobuf,
|
380
|
442
|
while ( remaining ) {
|
381
|
443
|
|
382
|
444
|
/* Sanity check */
|
383
|
|
- if ( ( remaining < sizeof ( *option ) ) ||
|
384
|
|
- ( option->blocks == 0 ) ||
|
385
|
|
- ( remaining < ( option->blocks * NDP_OPTION_BLKSZ ) ) ) {
|
|
445
|
+ if ( ( remaining < sizeof ( option->header ) ) ||
|
|
446
|
+ ( option->header.blocks == 0 ) ||
|
|
447
|
+ ( remaining < ( option->header.blocks *
|
|
448
|
+ NDP_OPTION_BLKSZ ) ) ) {
|
386
|
449
|
DBGC ( netdev, "NDP bad option length:\n" );
|
387
|
450
|
DBGC_HDA ( netdev, 0, option, remaining );
|
388
|
451
|
rc = -EINVAL;
|
389
|
452
|
goto done;
|
390
|
453
|
}
|
391
|
|
- option_len = ( option->blocks * NDP_OPTION_BLKSZ );
|
392
|
|
- option_value_len = ( option_len - sizeof ( *option ) );
|
|
454
|
+ option_len = ( option->header.blocks * NDP_OPTION_BLKSZ );
|
393
|
455
|
|
394
|
456
|
/* Handle option */
|
395
|
|
- if ( ( rc = ndp_rx_option ( netdev, sin6_src, ndp,
|
396
|
|
- option->type, option->value,
|
397
|
|
- option_value_len ) ) != 0 ) {
|
|
457
|
+ if ( ( rc = ndp_rx_option ( netdev, sin6_src, ndp, option,
|
|
458
|
+ option_len ) ) != 0 )
|
398
|
459
|
goto done;
|
399
|
|
- }
|
400
|
460
|
|
401
|
461
|
/* Move to next option */
|
402
|
462
|
option = ( ( ( void * ) option ) + option_len );
|