]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[ipoib] Attempt to generate ARPs as needed to repopulate REMAC cache
authorMichael Brown <mcb30@ipxe.org>
Mon, 29 Jun 2015 13:50:16 +0000 (14:50 +0100)
committerMichael Brown <mcb30@ipxe.org>
Mon, 29 Jun 2015 13:50:16 +0000 (14:50 +0100)
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>
src/drivers/net/ipoib.c
src/include/ipxe/arp.h
src/net/arp.c

index b72e8fc52682cd123ed8e04f043ba4f67ee7b338..bec4bbae92e3e37f122e8fc37754f4915c5a6d1c 100644 (file)
@@ -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>
@@ -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
 
@@ -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 */
@@ -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;
@@ -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 )
index 93195010a3b2db1187e14ce553346e68a399f4d2..5822fa095f34580aae3d92ae774286fe2be966df 100644 (file)
@@ -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 */
index 663dc00261dea677bdb80f0437be6c2794be438b..1e27c44e739309446c0ddb9748cf189dfe1bf9d8 100644 (file)
@@ -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;