aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/include/ipxe/in.h11
-rw-r--r--src/include/ipxe/tcpip.h9
-rw-r--r--src/net/ipv4.c45
3 files changed, 50 insertions, 15 deletions
diff --git a/src/include/ipxe/in.h b/src/include/ipxe/in.h
index 2a49b00bb..0ebf441c2 100644
--- a/src/include/ipxe/in.h
+++ b/src/include/ipxe/in.h
@@ -85,6 +85,11 @@ struct sockaddr_in {
uint16_t sin_flags;
/** TCP/IP port (part of struct @c sockaddr_tcpip) */
uint16_t sin_port;
+ /** Scope ID (part of struct @c sockaddr_tcpip)
+ *
+ * For multicast addresses, this is the network device index.
+ */
+ uint16_t sin_scope_id;
/** IPv4 address */
struct in_addr sin_addr;
/** Padding
@@ -96,6 +101,7 @@ struct sockaddr_in {
( sizeof ( sa_family_t ) /* sin_family */ +
sizeof ( uint16_t ) /* sin_flags */ +
sizeof ( uint16_t ) /* sin_port */ +
+ sizeof ( uint16_t ) /* sin_scope_id */ +
sizeof ( struct in_addr ) /* sin_addr */ ) ];
} __attribute__ (( packed, may_alias ));
@@ -112,9 +118,10 @@ struct sockaddr_in6 {
uint16_t sin6_flags;
/** TCP/IP port (part of struct @c sockaddr_tcpip) */
uint16_t sin6_port;
- /** Scope ID
+ /** Scope ID (part of struct @c sockaddr_tcpip)
*
- * For link-local addresses, this is the network device index.
+ * For link-local or multicast addresses, this is the network
+ * device index.
*/
uint16_t sin6_scope_id;
/** IPv6 address */
diff --git a/src/include/ipxe/tcpip.h b/src/include/ipxe/tcpip.h
index 73a7bffd3..3cfc8e3ac 100644
--- a/src/include/ipxe/tcpip.h
+++ b/src/include/ipxe/tcpip.h
@@ -48,6 +48,12 @@ struct sockaddr_tcpip {
uint16_t st_flags;
/** TCP/IP port */
uint16_t st_port;
+ /** Scope ID
+ *
+ * For link-local or multicast addresses, this is the network
+ * device index.
+ */
+ uint16_t st_scope_id;
/** Padding
*
* This ensures that a struct @c sockaddr_tcpip is large
@@ -57,7 +63,8 @@ struct sockaddr_tcpip {
char pad[ sizeof ( struct sockaddr ) -
( sizeof ( sa_family_t ) /* st_family */ +
sizeof ( uint16_t ) /* st_flags */ +
- sizeof ( uint16_t ) /* st_port */ ) ];
+ sizeof ( uint16_t ) /* st_port */ +
+ sizeof ( uint16_t ) /* st_scope_id */ ) ];
} __attribute__ (( packed, may_alias ));
/**
diff --git a/src/net/ipv4.c b/src/net/ipv4.c
index 3552cc49e..a54784049 100644
--- a/src/net/ipv4.c
+++ b/src/net/ipv4.c
@@ -139,6 +139,7 @@ static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) {
/**
* Perform IPv4 routing
*
+ * @v scope_id Destination address scope ID
* @v dest Final destination address
* @ret dest Next hop destination address
* @ret miniroute Routing table entry to use, or NULL if no route
@@ -146,22 +147,42 @@ static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) {
* If the route requires use of a gateway, the next hop destination
* address will be overwritten with the gateway address.
*/
-static struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) {
+static struct ipv4_miniroute * ipv4_route ( unsigned int scope_id,
+ struct in_addr *dest ) {
struct ipv4_miniroute *miniroute;
- int local;
- int has_gw;
/* Find first usable route in routing table */
list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
+
+ /* Skip closed network devices */
if ( ! netdev_is_open ( miniroute->netdev ) )
continue;
- local = ( ( ( dest->s_addr ^ miniroute->address.s_addr )
- & miniroute->netmask.s_addr ) == 0 );
- has_gw = ( miniroute->gateway.s_addr );
- if ( local || has_gw ) {
- if ( ! local )
+
+ if ( IN_IS_MULTICAST ( dest->s_addr ) ) {
+
+ /* 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;
+
+ } else {
+
+ /* If destination is an on-link global
+ * address, then use this route.
+ */
+ if ( ( ( dest->s_addr ^ miniroute->address.s_addr )
+ & miniroute->netmask.s_addr ) == 0 )
+ return miniroute;
+
+ /* If destination is an off-link global
+ * address, and we have a default gateway,
+ * then use this route.
+ */
+ if ( miniroute->gateway.s_addr ) {
*dest = miniroute->gateway;
- return miniroute;
+ return miniroute;
+ }
}
}
@@ -180,7 +201,7 @@ static struct net_device * ipv4_netdev ( struct sockaddr_tcpip *st_dest ) {
struct ipv4_miniroute *miniroute;
/* Find routing table entry */
- miniroute = ipv4_route ( &dest );
+ miniroute = ipv4_route ( sin_dest->sin_scope_id, &dest );
if ( ! miniroute )
return NULL;
@@ -314,8 +335,8 @@ static int ipv4_tx ( struct io_buffer *iobuf,
if ( sin_src )
iphdr->src = sin_src->sin_addr;
if ( ( next_hop.s_addr != INADDR_BROADCAST ) &&
- ( ! IN_IS_MULTICAST ( next_hop.s_addr ) ) &&
- ( ( miniroute = ipv4_route ( &next_hop ) ) != NULL ) ) {
+ ( ( miniroute = ipv4_route ( sin_dest->sin_scope_id,
+ &next_hop ) ) != NULL ) ) {
iphdr->src = miniroute->address;
netmask = miniroute->netmask;
netdev = miniroute->netdev;