summaryrefslogtreecommitdiffstats
path: root/src/net/ipv6.c
diff options
context:
space:
mode:
authorMichael Brown2013-10-25 15:41:37 +0200
committerMichael Brown2013-10-25 15:44:06 +0200
commit1aa67eba161f0e1410d8028bd60ee6778ebfa977 (patch)
treee14acc8d98b62ec23954d343fb356a6abee73d6b /src/net/ipv6.c
parent[ipv6] Treat sin6_scope_id consistently (diff)
downloadipxe-1aa67eba161f0e1410d8028bd60ee6778ebfa977.tar.gz
ipxe-1aa67eba161f0e1410d8028bd60ee6778ebfa977.tar.xz
ipxe-1aa67eba161f0e1410d8028bd60ee6778ebfa977.zip
[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>
Diffstat (limited to 'src/net/ipv6.c')
-rw-r--r--src/net/ipv6.c64
1 files changed, 34 insertions, 30 deletions
diff --git a/src/net/ipv6.c b/src/net/ipv6.c
index dcaad371..540ed05b 100644
--- a/src/net/ipv6.c
+++ b/src/net/ipv6.c
@@ -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 );
}