Skip to content

Commit

Permalink
[ipoib] Attempt to generate ARPs as needed to repopulate REMAC cache
Browse files Browse the repository at this point in the history
The only way to map an eIPoIB MAC address (REMAC) to an IPoIB MAC
address is to intercept an incoming ARP request or reply.

If we do not have an REMAC cache entry for a particular destination
MAC address, then we cannot transmit the packet.  This can arise in at
least two situations:

 - An external program (e.g. a PXE NBP using the UNDI API) may attempt
   to transmit to a destination MAC address that has been obtained by
   some method other than ARP.

 - Memory pressure may have caused REMAC cache entries to be
   discarded.  This is fairly likely on a busy network, since REMAC
   cache entries are created for all received (broadcast) ARP
   requests.  (We can't sensibly avoid creating these cache entries,
   since they are required in order to send an ARP reply, and when we
   are being used via the UNDI API we may have no knowledge of which
   IP addresses are "ours".)

Attempt to ameliorate the situation by generating a semi-spurious ARP
request whenever we find a missing REMAC cache entry.  This will
hopefully trigger an ARP reply, which would then provide us with the
information required to populate the REMAC cache.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Jun 29, 2015
1 parent e213a00 commit 8829634
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 8 deletions.
55 changes: 50 additions & 5 deletions src/drivers/net/ipoib.c
Expand Up @@ -33,8 +33,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/errortab.h>
#include <ipxe/malloc.h>
#include <ipxe/if_arp.h>
#include <ipxe/arp.h>
#include <ipxe/if_ether.h>
#include <ipxe/ethernet.h>
#include <ipxe/ip.h>
#include <ipxe/iobuf.h>
#include <ipxe/netdevice.h>
#include <ipxe/infiniband.h>
Expand All @@ -48,6 +50,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* IP over Infiniband
*/

/* Disambiguate the various error causes */
#define ENXIO_ARP_REPLY __einfo_error ( EINFO_ENXIO_ARP_REPLY )
#define EINFO_ENXIO_ARP_REPLY \
__einfo_uniqify ( EINFO_ENXIO, 0x01, \
"Missing REMAC for ARP reply target address" )
#define ENXIO_NON_IPV4 __einfo_error ( EINFO_ENXIO_NON_IPV4 )
#define EINFO_ENXIO_NON_IPV4 \
__einfo_uniqify ( EINFO_ENXIO, 0x02, \
"Missing REMAC for non-IPv4 packet" )
#define ENXIO_ARP_SENT __einfo_error ( EINFO_ENXIO_ARP_SENT )
#define EINFO_ENXIO_ARP_SENT \
__einfo_uniqify ( EINFO_ENXIO, 0x03, \
"Missing REMAC for IPv4 packet (ARP sent)" )

/** Number of IPoIB send work queue entries */
#define IPOIB_NUM_SEND_WQES 2

Expand Down Expand Up @@ -336,8 +352,11 @@ static int ipoib_translate_tx_arp ( struct net_device *netdev,
/* Look up REMAC, if applicable */
if ( arphdr->ar_op == ARPOP_REPLY ) {
target_ha = ipoib_find_remac ( ipoib, arp_target_pa ( arphdr ));
if ( ! target_ha )
return -ENXIO;
if ( ! target_ha ) {
DBGC ( ipoib, "IPoIB %p no REMAC for %s ARP reply\n",
ipoib, eth_ntoa ( arp_target_pa ( arphdr ) ) );
return -ENXIO_ARP_REPLY;
}
}

/* Construct new packet */
Expand Down Expand Up @@ -473,6 +492,7 @@ static int ipoib_transmit ( struct net_device *netdev,
struct ipoib_device *ipoib = netdev->priv;
struct ib_device *ibdev = ipoib->ibdev;
struct ethhdr *ethhdr;
struct iphdr *iphdr;
struct ipoib_hdr *ipoib_hdr;
struct ipoib_mac *mac;
struct ib_address_vector dest;
Expand All @@ -497,9 +517,34 @@ static int ipoib_transmit ( struct net_device *netdev,
iob_pull ( iobuf, sizeof ( *ethhdr ) );

/* Identify destination address */
mac = ipoib_find_remac ( ipoib, ( ( void *) ethhdr->h_dest ) );
if ( ! mac )
return -ENXIO;
mac = ipoib_find_remac ( ipoib, ( ( void * ) ethhdr->h_dest ) );
if ( ! mac ) {
/* Generate a new ARP request (if possible) to trigger
* population of the REMAC cache entry.
*/
if ( ( net_proto != htons ( ETH_P_IP ) ) ||
( iob_len ( iobuf ) < sizeof ( *iphdr ) ) ) {
DBGC ( ipoib, "IPoIB %p no REMAC for %s non-IPv4 "
"packet type %04x\n", ipoib,
eth_ntoa ( ethhdr->h_dest ),
ntohs ( net_proto ) );
return -ENXIO_NON_IPV4;
}
iphdr = iobuf->data;
if ( ( rc = arp_tx_request ( netdev, &ipv4_protocol,
&iphdr->dest, &iphdr->src ) ) !=0){
DBGC ( ipoib, "IPoIB %p could not ARP for %s/%s/",
ipoib, eth_ntoa ( ethhdr->h_dest ),
inet_ntoa ( iphdr->dest ) );
DBGC ( ipoib, "%s: %s\n", inet_ntoa ( iphdr->src ),
strerror ( rc ) );
return rc;
}
DBGC ( ipoib, "IPoIB %p no REMAC for %s/%s/", ipoib,
eth_ntoa ( ethhdr->h_dest ), inet_ntoa ( iphdr->dest ) );
DBGC ( ipoib, "%s\n", inet_ntoa ( iphdr->src ) );
return -ENXIO_ARP_SENT;
}

/* Translate packet if applicable */
if ( ( rc = ipoib_translate_tx ( netdev, iobuf, net_proto ) ) != 0 )
Expand Down
4 changes: 4 additions & 0 deletions src/include/ipxe/arp.h
Expand Up @@ -57,4 +57,8 @@ static inline int arp_tx ( struct io_buffer *iobuf, struct net_device *netdev,
&arp_discovery, net_source, ll_source );
}

extern int arp_tx_request ( struct net_device *netdev,
struct net_protocol *net_protocol,
const void *net_dest, const void *net_source );

#endif /* _IPXE_ARP_H */
6 changes: 3 additions & 3 deletions src/net/arp.c
Expand Up @@ -56,9 +56,9 @@ struct net_protocol arp_protocol __net_protocol;
* @v net_source Source network-layer address
* @ret rc Return status code
*/
static int arp_tx_request ( struct net_device *netdev,
struct net_protocol *net_protocol,
const void *net_dest, const void *net_source ) {
int arp_tx_request ( struct net_device *netdev,
struct net_protocol *net_protocol,
const void *net_dest, const void *net_source ) {
struct ll_protocol *ll_protocol = netdev->ll_protocol;
struct io_buffer *iobuf;
struct arphdr *arphdr;
Expand Down

0 comments on commit 8829634

Please sign in to comment.