aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2024-06-26 04:29:38 -0700
committerMichael Brown <mcb30@ipxe.org>2024-06-26 05:01:58 -0700
commit77acf6b41f705384593a057c2bea057283bf429b (patch)
tree1605971b7759d8f81964576dd92d0657efbd37a6
parent821bb326f87fbc000376fdc5371e9e53f666267a (diff)
downloadipxe-slash31.zip
ipxe-slash31.tar.gz
ipxe-slash31.tar.bz2
[ipv4] Support small subnets with no directed broadcast addressslash31
In a small subnet (with a /31 or /32 subnet mask), all addresses within the subnet are valid host addresses: there is no separate network address or directed broadcast address. The logic used in iPXE to determine whether or not to use a link-layer broadcast address will currently fail in these subnets. In a /31 subnet, the higher of the two host addresses (i.e. the address with all host bits set) will be treated as a broadcast address. In a /32 subnet, the single valid host address will be treated as a broadcast address. Fix by adding the concept of a host mask, defined such that an address in the local subnet with all of the mask bits set to zero represents the network address, and an address in the local subnet with all of the mask bits set to one represents the directed broadcast address. For most subnets, this is simply the inverse of the subnet mask. For small subnets (/31 or /32) we can obtain the desired behaviour by setting the host mask to all ones, so that only the local broadcast address 255.255.255.255 will be treated as a broadcast address. Originally-fixed-by: Lukas Stockner <lstockner@genesiscloud.com> Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/include/ipxe/in.h2
-rw-r--r--src/include/ipxe/ip.h22
-rw-r--r--src/net/ipv4.c18
3 files changed, 36 insertions, 6 deletions
diff --git a/src/include/ipxe/in.h b/src/include/ipxe/in.h
index 3044d63..05a8122 100644
--- a/src/include/ipxe/in.h
+++ b/src/include/ipxe/in.h
@@ -33,6 +33,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define IN_IS_MULTICAST( addr ) \
( ( (addr) & htonl ( 0xf0000000 ) ) == htonl ( 0xe0000000 ) )
+#define IN_IS_SMALL( mask ) ( (mask) & htonl ( 0x00000003 ) )
+
/**
* IP address structure
*/
diff --git a/src/include/ipxe/ip.h b/src/include/ipxe/ip.h
index 285be6d..b1b5cb2 100644
--- a/src/include/ipxe/ip.h
+++ b/src/include/ipxe/ip.h
@@ -64,9 +64,27 @@ struct ipv4_miniroute {
/** IPv4 address */
struct in_addr address;
- /** Subnet mask */
+ /** Subnet mask
+ *
+ * An address with all of these bits in common with our IPv4
+ * address is in the local subnet.
+ */
struct in_addr netmask;
- /** Gateway address */
+ /** Host mask
+ *
+ * An address in the local subnet with all of these bits set
+ * to zero represents the network address, and an address in
+ * the local subnet with all of these bits set to one
+ * represents the directed broadcast address. All other
+ * addresses in the local subnet are valid host addresses.
+ *
+ * For most subnets, this is the inverse of the subnet mask.
+ * In a small subnet (/31 or /32) there is no network address
+ * or directed broadcast address, and all addresses in the
+ * subnet are valid host addresses.
+ */
+ struct in_addr hostmask;
+ /** Gateway address, or zero for no gateway */
struct in_addr gateway;
};
diff --git a/src/net/ipv4.c b/src/net/ipv4.c
index b91fa2a..5d0cb0f 100644
--- a/src/net/ipv4.c
+++ b/src/net/ipv4.c
@@ -85,9 +85,18 @@ static int add_ipv4_miniroute ( struct net_device *netdev,
struct in_addr address, struct in_addr netmask,
struct in_addr gateway ) {
struct ipv4_miniroute *miniroute;
+ struct in_addr hostmask;
+ struct in_addr broadcast;
+ /* Calculate host mask */
+ hostmask.s_addr = ( IN_IS_SMALL ( netmask.s_addr ) ?
+ INADDR_NONE : ~netmask.s_addr );
+ broadcast.s_addr = ( address.s_addr | hostmask.s_addr );
+
+ /* Print debugging information */
DBGC ( netdev, "IPv4 add %s", inet_ntoa ( address ) );
DBGC ( netdev, "/%s ", inet_ntoa ( netmask ) );
+ DBGC ( netdev, "bc %s ", inet_ntoa ( broadcast ) );
if ( gateway.s_addr )
DBGC ( netdev, "gw %s ", inet_ntoa ( gateway ) );
DBGC ( netdev, "via %s\n", netdev->name );
@@ -103,8 +112,9 @@ static int add_ipv4_miniroute ( struct net_device *netdev,
miniroute->netdev = netdev_get ( netdev );
miniroute->address = address;
miniroute->netmask = netmask;
+ miniroute->hostmask = hostmask;
miniroute->gateway = gateway;
-
+
/* Add to end of list if we have a gateway, otherwise
* to start of list.
*/
@@ -310,7 +320,7 @@ static int ipv4_tx ( struct io_buffer *iobuf,
struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
struct ipv4_miniroute *miniroute;
struct in_addr next_hop;
- struct in_addr netmask = { .s_addr = 0 };
+ struct in_addr hostmask = { .s_addr = INADDR_NONE };
uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
const void *ll_dest;
int rc;
@@ -338,7 +348,7 @@ static int ipv4_tx ( struct io_buffer *iobuf,
( ( miniroute = ipv4_route ( sin_dest->sin_scope_id,
&next_hop ) ) != NULL ) ) {
iphdr->src = miniroute->address;
- netmask = miniroute->netmask;
+ hostmask = miniroute->hostmask;
netdev = miniroute->netdev;
}
if ( ! netdev ) {
@@ -373,7 +383,7 @@ static int ipv4_tx ( struct io_buffer *iobuf,
ntohs ( iphdr->chksum ) );
/* Calculate link-layer destination address, if possible */
- if ( ( ( next_hop.s_addr ^ INADDR_BROADCAST ) & ~netmask.s_addr ) == 0){
+ if ( ( ( ~next_hop.s_addr ) & hostmask.s_addr ) == 0 ) {
/* Broadcast address */
ipv4_stats.out_bcast_pkts++;
ll_dest = netdev->ll_broadcast;