Skip to content

Commit 76f7a9e

Browse files
committedFeb 24, 2011
[driver] Try to prevent spurious routing entries for iSCSI
If a gateway is specified in the iBFT, the Microsoft iSCSI initiator will create a static route to the iSCSI target via that gateway. (See http://support.microsoft.com/kb/960104 for details.) If the target is in the same subnet then this is undesirable, since it will mean duplicating every outbound packet on the network. Try to prevent this undesirable behaviour by adjusting the gateway address stored in the iBFT. Signed-off-by: Michael Brown <mbrown@fensystems.co.uk>
1 parent bed6bb0 commit 76f7a9e

File tree

1 file changed

+118
-32
lines changed

1 file changed

+118
-32
lines changed
 

‎src/driver/ibft.c

Lines changed: 118 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,30 @@ static LPSTR ibft_ipaddr ( PIBFT_IPADDR ipaddr ) {
9292
return inet_ntoa ( ipaddr->in );
9393
}
9494

95+
/**
96+
* Convert subnet mask prefix to subnet mask
97+
*
98+
* @v prefix Subnet mask prefix
99+
* @ret mask Subnet mask
100+
*/
101+
static ULONG ibft_subnet_mask ( UCHAR prefix ) {
102+
return RtlUlongByteSwap ( 0xffffffffUL << ( 32 - prefix ) );
103+
}
104+
105+
/**
106+
* Fill in an IP address field within iBFT
107+
*
108+
* @v ipaddr IP address field
109+
* @v in IPv4 address
110+
*/
111+
static void ibft_set_ipaddr ( PIBFT_IPADDR ipaddr, ULONG in ) {
112+
memset ( ipaddr, 0, sizeof ( *ipaddr ) );
113+
if ( in ) {
114+
ipaddr->in = in;
115+
ipaddr->ones = 0xffff;
116+
}
117+
}
118+
95119
/**
96120
* Parse iBFT initiator structure
97121
*
@@ -217,8 +241,7 @@ static NTSTATUS store_tcpip_parameters ( PDEVICE_OBJECT pdo,
217241
goto err_reg_store;
218242

219243
/* Store subnet mask */
220-
subnet_mask = RtlUlongByteSwap ( 0xffffffffUL <<
221-
( 32 - nic->subnet_mask_prefix ) );
244+
subnet_mask = ibft_subnet_mask ( nic->subnet_mask_prefix );
222245
status = store_ipv4_parameter_multi_sz ( reg_key, L"SubnetMask",
223246
subnet_mask );
224247
if ( ! NT_SUCCESS ( status ) )
@@ -257,6 +280,7 @@ static NTSTATUS store_tcpip_parameters ( PDEVICE_OBJECT pdo,
257280
*/
258281
static VOID parse_ibft_nic ( PIBFT_TABLE ibft, PIBFT_NIC nic ) {
259282
PIBFT_HEADER header = &nic->header;
283+
ULONG subnet_mask;
260284
NTSTATUS status;
261285

262286
/* Dump structure information */
@@ -270,8 +294,9 @@ static VOID parse_ibft_nic ( PIBFT_TABLE ibft, PIBFT_NIC nic ) {
270294
? ", global address" : ", link local address" ) );
271295
if ( ! ( header->flags & IBFT_FL_NIC_BLOCK_VALID ) )
272296
return;
273-
DbgPrint ( " IP = %s/%d\n", ibft_ipaddr ( &nic->ip_address ),
274-
nic->subnet_mask_prefix );
297+
DbgPrint ( " IP = %s/", ibft_ipaddr ( &nic->ip_address ) );
298+
subnet_mask = ibft_subnet_mask ( nic->subnet_mask_prefix );
299+
DbgPrint ( "%s\n", inet_ntoa ( subnet_mask ) );
275300
DbgPrint ( " Origin = %d\n", nic->origin );
276301
DbgPrint ( " Gateway = %s\n", ibft_ipaddr ( &nic->gateway ) );
277302
DbgPrint ( " DNS = %s", ibft_ipaddr ( &nic->dns[0] ) );
@@ -349,42 +374,103 @@ static VOID parse_ibft_target ( PIBFT_TABLE ibft, PIBFT_TARGET target ) {
349374
"<omitted>" : "" ) );
350375
}
351376

377+
/**
378+
* Iterate over entries in the iBFT
379+
*
380+
* @v entry Structure
381+
* @v TYPE Type of structure
382+
* @v ibft iBFT
383+
* @v offset Loop counter
384+
*/
385+
#define for_each_ibft_entry( entry, TYPE, ibft, offset ) \
386+
for ( offset = &ibft->control.extensions, \
387+
entry = ( ( PIBFT_ ## TYPE ) ( ( ( PUCHAR ) ibft ) + *offset ));\
388+
( ( PUCHAR ) offset ) < ( ( ( PUCHAR ) &ibft->control ) + \
389+
ibft->control.header.length ) ; \
390+
offset++, \
391+
entry = ( ( PIBFT_ ## TYPE ) ( ( ( PUCHAR ) ibft ) + *offset )))\
392+
if ( ( *offset == 0 ) || \
393+
( entry->header.structure_id != \
394+
IBFT_STRUCTURE_ID_ ## TYPE ) ) continue; \
395+
else
396+
352397
/**
353398
* Parse iBFT
354399
*
355400
* @v acpi ACPI description header
356401
*/
357402
VOID parse_ibft ( PACPI_DESCRIPTION_HEADER acpi ) {
358403
PIBFT_TABLE ibft = ( PIBFT_TABLE ) acpi;
359-
PIBFT_CONTROL control = &ibft->control;
360-
PUSHORT offset;
361-
PIBFT_HEADER header;
362-
363-
/* Scan through all entries in the Control structure */
364-
for ( offset = &control->extensions ;
365-
( ( PUCHAR ) offset ) <
366-
( ( ( PUCHAR ) control ) + control->header.length ) ;
367-
offset++ ) {
368-
if ( ! *offset )
404+
PUSHORT initiator_offset;
405+
PIBFT_INITIATOR initiator;
406+
PUSHORT nic_offset;
407+
PIBFT_NIC nic;
408+
PUSHORT target_offset;
409+
PIBFT_TARGET target;
410+
ULONG gateway;
411+
ULONG network;
412+
ULONG netmask;
413+
ULONG attached_targets;
414+
415+
/* Scan through all iBFT entries */
416+
for_each_ibft_entry ( initiator, INITIATOR, ibft, initiator_offset )
417+
parse_ibft_initiator ( ibft, initiator );
418+
for_each_ibft_entry ( nic, NIC, ibft, nic_offset )
419+
parse_ibft_nic ( ibft, nic );
420+
for_each_ibft_entry ( target, TARGET, ibft, target_offset )
421+
parse_ibft_target ( ibft, target );
422+
423+
/* If a gateway is specified in the iBFT, the Microsoft iSCSI
424+
* initiator will create a static route to the iSCSI target
425+
* via that gateway. (See the knowledge base article at
426+
* http://support.microsoft.com/kb/960104 for details.) If
427+
* the target is in the same subnet then this is undesirable,
428+
* since it will mean duplicating every outbound packet on the
429+
* network.
430+
*
431+
* We work around this by replacing the gateway address as
432+
* follows:
433+
*
434+
* a) if no targets are directly attached to this NIC's
435+
* subnet, we leave the gateway address as-is,
436+
*
437+
* b) if exactly one target is directly attached to this
438+
* NIC's subnet, we set the gateway address to the
439+
* target's own IP address, thus giving a target-
440+
* specific route that will remain correct even if the
441+
* subnet route is later removed.
442+
*
443+
* c) if more than one target is directly attached to this
444+
* NIC's subnet, we blank out the gateway address, thus
445+
* preventing the creation of the undesirable routes.
446+
*
447+
* Note that none of this affects the normal TCP/IP stack
448+
* configuration, which has already been carried out; this
449+
* affects only the dedicated routes created by the Microsoft
450+
* iSCSI initiator.
451+
*/
452+
for_each_ibft_entry ( nic, NIC, ibft, nic_offset ) {
453+
gateway = nic->gateway.in;
454+
if ( ! gateway )
369455
continue;
370-
header = ( ( PIBFT_HEADER ) ( ( ( PUCHAR ) ibft ) + *offset ));
371-
switch ( header->structure_id ) {
372-
case IBFT_STRUCTURE_ID_INITIATOR :
373-
parse_ibft_initiator ( ibft,
374-
( ( PIBFT_INITIATOR ) header ));
375-
break;
376-
case IBFT_STRUCTURE_ID_NIC :
377-
parse_ibft_nic ( ibft, ( ( PIBFT_NIC ) header ) );
378-
break;
379-
case IBFT_STRUCTURE_ID_TARGET :
380-
parse_ibft_target ( ibft,
381-
( ( PIBFT_TARGET ) header ) );
382-
break;
383-
default :
384-
DbgPrint ( "Ignoring unknown iBFT structure ID %d "
385-
"index %d\n", header->structure_id,
386-
header->index );
387-
break;
456+
netmask = ibft_subnet_mask ( nic->subnet_mask_prefix );
457+
network = ( nic->ip_address.in & netmask );
458+
attached_targets = 0;
459+
for_each_ibft_entry ( target, TARGET, ibft, target_offset ) {
460+
if ( ( target->ip_address.in & netmask ) != network )
461+
continue;
462+
gateway = ( ( attached_targets == 0 ) ?
463+
target->ip_address.in : 0 );
464+
attached_targets++;
465+
}
466+
DbgPrint ( "Found %d target(s) directly attached via iBFT NIC "
467+
"%d\n", attached_targets, nic->header.index );
468+
if ( gateway != nic->gateway.in ) {
469+
DbgPrint ( "Amending gateway for iBFT NIC %d from %s",
470+
nic->header.index,
471+
ibft_ipaddr ( &nic->gateway ) );
472+
ibft_set_ipaddr ( &nic->gateway, gateway );
473+
DbgPrint ( " to %s\n", ibft_ipaddr ( &nic->gateway ) );
388474
}
389475
}
390476
}

0 commit comments

Comments
 (0)