david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[ipv6] Automatically choose source for link-local and multicast destinations

When transmitting to a link-local or multicast destination address,
use the network device's link-local address as the source address if
no explicit source address has been specified.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2013-10-25 14:41:37 +01:00
parent fec127cb07
commit 1aa67eba16
1 changed files with 34 additions and 30 deletions

View File

@ -93,10 +93,10 @@ int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr ) {
*
* @v miniroute Routing table entry
* @v address IPv6 address
* @ret is_local Address is within this entry's local network
* @ret is_on_link Address is within this entry's local network
*/
static int ipv6_is_local ( struct ipv6_miniroute *miniroute,
struct in6_addr *address ) {
static int ipv6_is_on_link ( struct ipv6_miniroute *miniroute,
struct in6_addr *address ) {
unsigned int i;
for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) /
@ -192,7 +192,6 @@ static void del_ipv6_miniroute ( struct ipv6_miniroute *miniroute ) {
static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
struct in6_addr **dest ) {
struct ipv6_miniroute *miniroute;
int local;
/* Find first usable route in routing table */
list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
@ -201,25 +200,32 @@ static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
if ( ! netdev_is_open ( miniroute->netdev ) )
continue;
/* For link-local addresses, skip devices that are not
* the specified network device.
*/
if ( IN6_IS_ADDR_LINKLOCAL ( *dest ) &&
( miniroute->netdev->index != scope_id ) )
continue;
if ( IN6_IS_ADDR_LINKLOCAL ( *dest ) ||
IN6_IS_ADDR_MULTICAST ( *dest ) ) {
/* Skip non-gateway devices for which the prefix does
* not match.
*/
local = ipv6_is_local ( miniroute, *dest );
if ( ! ( local || miniroute->has_router ) )
continue;
/* If destination is non-global, and the scope ID
* matches this network device, then use this route.
*/
if ( miniroute->netdev->index == scope_id )
return miniroute;
/* Update next hop if applicable */
if ( ! local )
*dest = &miniroute->router;
} else {
return miniroute;
/* If destination is an on-link global
* address, then use this route.
*/
if ( ipv6_is_on_link ( miniroute, *dest ) )
return miniroute;
/* If destination is an off-link global
* address, and we have a default gateway,
* then use this route.
*/
if ( miniroute->has_router ) {
*dest = &miniroute->router;
return miniroute;
}
}
}
return NULL;
@ -363,6 +369,7 @@ static int ipv6_tx ( struct io_buffer *iobuf,
struct sockaddr_in6 *sin6_dest = ( ( struct sockaddr_in6 * ) st_dest );
struct ipv6_miniroute *miniroute;
struct ipv6_header *iphdr;
struct in6_addr *src = NULL;
struct in6_addr *next_hop;
uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
const void *ll_dest;
@ -381,15 +388,9 @@ static int ipv6_tx ( struct io_buffer *iobuf,
/* Use routing table to identify next hop and transmitting netdev */
next_hop = &iphdr->dest;
if ( sin6_src ) {
memcpy ( &iphdr->src, &sin6_src->sin6_addr,
sizeof ( iphdr->src ) );
}
if ( ( ! IN6_IS_ADDR_MULTICAST ( next_hop ) ) &&
( ( miniroute = ipv6_route ( sin6_dest->sin6_scope_id,
&next_hop ) ) != NULL ) ) {
memcpy ( &iphdr->src, &miniroute->address,
sizeof ( iphdr->src ) );
if ( ( miniroute = ipv6_route ( sin6_dest->sin6_scope_id,
&next_hop ) ) != NULL ) {
src = &miniroute->address;
netdev = miniroute->netdev;
}
if ( ! netdev ) {
@ -398,6 +399,9 @@ static int ipv6_tx ( struct io_buffer *iobuf,
rc = -ENETUNREACH;
goto err;
}
if ( sin6_src )
src = &sin6_src->sin6_addr;
memcpy ( &iphdr->src, src, sizeof ( iphdr->src ) );
/* Fix up checksums */
if ( trans_csum ) {
@ -897,7 +901,7 @@ int ipv6_slaac ( struct net_device *netdev, struct in6_addr *prefix,
/* Delete any existing SLAAC miniroutes for this prefix */
list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) {
if ( ipv6_is_local ( miniroute, &address ) )
if ( ipv6_is_on_link ( miniroute, &address ) )
del_ipv6_miniroute ( miniroute );
}