diff --git a/src/include/ipxe/in.h b/src/include/ipxe/in.h index 2a49b00b..0ebf441c 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 73a7bffd..3cfc8e3a 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 3552cc49..a5478404 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;