Home | History | Annotate | Download | only in Host
      1 /**@file
      2  Berkeley Packet Filter implementation of the EMU_SNP_PROTOCOL that allows the
      3  emulator to get on real networks.
      4 
      5  Tested on Mac OS X.
      6 
      7 Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.<BR>
      8 Portitions copyright (c) 2011, Apple Inc. All rights reserved.
      9 
     10 This program and the accompanying materials
     11 are licensed and made available under the terms and conditions of the BSD License
     12 which accompanies this distribution.  The full text of the license may be found at
     13 http://opensource.org/licenses/bsd-license.php
     14 
     15 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     16 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     17 
     18 **/
     19 
     20 
     21 #include "Host.h"
     22 
     23 #ifdef __APPLE__
     24 
     25 
     26 #include <Library/NetLib.h>
     27 
     28 
     29 #define EMU_SNP_PRIVATE_SIGNATURE SIGNATURE_32('E', 'M', 's', 'n')
     30 typedef struct {
     31   UINTN                       Signature;
     32 
     33   EMU_IO_THUNK_PROTOCOL       *Thunk;
     34   EMU_SNP_PROTOCOL            EmuSnp;
     35   EFI_SIMPLE_NETWORK_MODE     *Mode;
     36 
     37   int                         BpfFd;
     38   char                        *InterfaceName;
     39   EFI_MAC_ADDRESS             MacAddress;
     40   u_int                       ReadBufferSize;
     41   VOID                        *ReadBuffer;
     42 
     43   //
     44   // Two walking pointers to manage the multiple packets that can be returned
     45   // in a single read.
     46   //
     47   VOID                        *CurrentReadPointer;
     48   VOID                        *EndReadPointer;
     49 
     50 	UINT32									    ReceivedPackets;
     51 	UINT32									    DroppedPackets;
     52 
     53 } EMU_SNP_PRIVATE;
     54 
     55 #define EMU_SNP_PRIVATE_DATA_FROM_THIS(a) \
     56          CR(a, EMU_SNP_PRIVATE, EmuSnp, EMU_SNP_PRIVATE_SIGNATURE)
     57 
     58 
     59 //
     60 // Strange, but there doesn't appear to be any structure for the Ethernet header in edk2...
     61 //
     62 
     63 typedef struct {
     64   UINT8   DstAddr[NET_ETHER_ADDR_LEN];
     65   UINT8   SrcAddr[NET_ETHER_ADDR_LEN];
     66   UINT16  Type;
     67 } ETHERNET_HEADER;
     68 
     69 /**
     70   Register storage for SNP Mode.
     71 
     72   @param  This Protocol instance pointer.
     73   @param  Mode SimpleNetworkProtocol Mode structure passed into driver.
     74 
     75   @retval EFI_SUCCESS           The network interface was started.
     76   @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
     77 
     78 **/
     79 EFI_STATUS
     80 EmuSnpCreateMapping (
     81   IN     EMU_SNP_PROTOCOL         *This,
     82   IN     EFI_SIMPLE_NETWORK_MODE  *Mode
     83   )
     84 {
     85   EMU_SNP_PRIVATE    *Private;
     86 
     87   Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
     88 
     89   Private->Mode = Mode;
     90 
     91   //
     92   // Set the broadcast address.
     93   //
     94   SetMem (&Mode->BroadcastAddress, sizeof (EFI_MAC_ADDRESS), 0xFF);
     95 
     96   CopyMem (&Mode->CurrentAddress, &Private->MacAddress, sizeof (EFI_MAC_ADDRESS));
     97   CopyMem (&Mode->PermanentAddress, &Private->MacAddress, sizeof (EFI_MAC_ADDRESS));
     98 
     99   //
    100   // Since the fake SNP is based on a real NIC, to avoid conflict with the host NIC
    101   // network stack, we use a different MAC address.
    102   // So just change the last byte of the MAC address for the real NIC.
    103   //
    104   Mode->CurrentAddress.Addr[NET_ETHER_ADDR_LEN - 1]++;
    105 
    106   return EFI_SUCCESS;
    107 }
    108 
    109 
    110 static struct bpf_insn mFilterInstructionTemplate[] = {
    111   // Load 4 bytes from the destination MAC address.
    112   BPF_STMT (BPF_LD + BPF_W + BPF_ABS, OFFSET_OF (ETHERNET_HEADER, DstAddr[0])),
    113 
    114   // Compare to first 4 bytes of fake MAC address.
    115   BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 0x12345678, 0, 3 ),
    116 
    117   // Load remaining 2 bytes from the destination MAC address.
    118   BPF_STMT (BPF_LD + BPF_H + BPF_ABS, OFFSET_OF( ETHERNET_HEADER, DstAddr[4])),
    119 
    120   // Compare to remaining 2 bytes of fake MAC address.
    121   BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 0x9ABC, 5, 0 ),
    122 
    123   // Load 4 bytes from the destination MAC address.
    124   BPF_STMT (BPF_LD + BPF_W + BPF_ABS, OFFSET_OF (ETHERNET_HEADER, DstAddr[0])),
    125 
    126   // Compare to first 4 bytes of broadcast MAC address.
    127   BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 0xFFFFFFFF, 0, 2),
    128 
    129   // Load remaining 2 bytes from the destination MAC address.
    130   BPF_STMT (BPF_LD + BPF_H + BPF_ABS, OFFSET_OF( ETHERNET_HEADER, DstAddr[4])),
    131 
    132   // Compare to remaining 2 bytes of broadcast MAC address.
    133   BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 0xFFFF, 1, 0),
    134 
    135   // Reject packet.
    136   BPF_STMT (BPF_RET + BPF_K, 0),
    137 
    138   // Receive entire packet.
    139   BPF_STMT (BPF_RET + BPF_K, -1)
    140 };
    141 
    142 
    143 EFI_STATUS
    144 OpenBpfFileDescriptor (
    145   IN EMU_SNP_PRIVATE  *Private,
    146   OUT int             *Fd
    147   )
    148 {
    149   char  BfpDeviceName[256];
    150   int   Index;
    151 
    152   //
    153   // Open a Berkeley Packet Filter device.  This must be done as root, so this is probably
    154   // the place which is most likely to fail...
    155   //
    156   for (Index = 0; TRUE; Index++ ) {
    157     snprintf (BfpDeviceName, sizeof (BfpDeviceName), "/dev/bpf%d", Index);
    158 
    159     *Fd = open (BfpDeviceName, O_RDWR, 0);
    160     if ( *Fd >= 0 ) {
    161       return EFI_SUCCESS;
    162     }
    163 
    164     if (errno == EACCES) {
    165       printf (
    166         "SNP: Permissions on '%s' are incorrect.  Fix with 'sudo chmod 666 %s'.\n",
    167         BfpDeviceName,
    168         BfpDeviceName
    169         );
    170     }
    171 
    172     if (errno != EBUSY) {
    173       break;
    174     }
    175   }
    176 
    177   return EFI_OUT_OF_RESOURCES;
    178 }
    179 
    180 
    181 /**
    182   Changes the state of a network interface from "stopped" to "started".
    183 
    184   @param  This Protocol instance pointer.
    185 
    186   @retval EFI_SUCCESS           The network interface was started.
    187   @retval EFI_ALREADY_STARTED   The network interface is already in the started state.
    188   @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
    189   @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
    190   @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
    191 
    192 **/
    193 EFI_STATUS
    194 EmuSnpStart (
    195   IN EMU_SNP_PROTOCOL  *This
    196   )
    197 {
    198   EFI_STATUS         Status;
    199   EMU_SNP_PRIVATE    *Private;
    200   struct ifreq       BoundIf;
    201   struct bpf_program BpfProgram;
    202   struct bpf_insn    *FilterProgram;
    203 	u_int							 Value;
    204 	u_int  						 ReadBufferSize;
    205   UINT16             Temp16;
    206   UINT32             Temp32;
    207 
    208   Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
    209 
    210   switch (Private->Mode->State) {
    211     case EfiSimpleNetworkStopped:
    212       break;
    213 
    214     case EfiSimpleNetworkStarted:
    215     case EfiSimpleNetworkInitialized:
    216       return EFI_ALREADY_STARTED;
    217       break;
    218 
    219     default:
    220       return EFI_DEVICE_ERROR;
    221       break;
    222   }
    223 
    224   Status = EFI_SUCCESS;
    225   if (Private->BpfFd == 0) {
    226     Status = OpenBpfFileDescriptor (Private, &Private->BpfFd);
    227     if (EFI_ERROR (Status)) {
    228       goto DeviceErrorExit;
    229     }
    230 
    231     //
    232 		// Get the read buffer size.
    233 		//
    234 		if (ioctl (Private->BpfFd, BIOCGBLEN, &ReadBufferSize) < 0) {
    235 			goto DeviceErrorExit;
    236 		}
    237 
    238 		//
    239 		// Default value from BIOCGBLEN is usually too small, so use a much larger size, if necessary.
    240 		//
    241 		if (ReadBufferSize < FixedPcdGet32 (PcdNetworkPacketFilterSize)) {
    242 			ReadBufferSize = FixedPcdGet32 (PcdNetworkPacketFilterSize);
    243 			if (ioctl (Private->BpfFd, BIOCSBLEN, &ReadBufferSize) < 0) {
    244 				goto DeviceErrorExit;
    245 			}
    246 		}
    247 
    248 		//
    249     // Associate our interface with this BPF file descriptor.
    250     //
    251     AsciiStrCpy (BoundIf.ifr_name, Private->InterfaceName);
    252     if (ioctl (Private->BpfFd, BIOCSETIF, &BoundIf) < 0) {
    253       goto DeviceErrorExit;
    254     }
    255 
    256     //
    257 		// Enable immediate mode.
    258     //
    259     Value = 1;
    260     if (ioctl (Private->BpfFd, BIOCIMMEDIATE, &Value) < 0) {
    261       goto DeviceErrorExit;
    262     }
    263 
    264     //
    265     // Enable non-blocking I/O.
    266     //
    267     if (fcntl (Private->BpfFd, F_GETFL, 0) == -1) {
    268       goto DeviceErrorExit;
    269     }
    270 
    271     Value |= O_NONBLOCK;
    272 
    273     if (fcntl (Private->BpfFd, F_SETFL, Value) == -1) {
    274       goto DeviceErrorExit;
    275     }
    276 
    277     //
    278     // Disable "header complete" flag.  This means the supplied source MAC address is
    279     // what goes on the wire.
    280     //
    281     Value = 1;
    282     if (ioctl (Private->BpfFd, BIOCSHDRCMPLT, &Value) < 0) {
    283       goto DeviceErrorExit;
    284     }
    285 
    286     //
    287     // Allocate read buffer.
    288     //
    289 		Private->ReadBufferSize = ReadBufferSize;
    290 		Private->ReadBuffer = malloc (Private->ReadBufferSize);
    291     if (Private->ReadBuffer == NULL) {
    292       goto ErrorExit;
    293     }
    294 
    295     Private->CurrentReadPointer = Private->EndReadPointer = Private->ReadBuffer;
    296 
    297     //
    298 		// Install our packet filter: successful reads should only produce broadcast or unicast
    299     // packets directed to our fake MAC address.
    300     //
    301     FilterProgram = malloc (sizeof (mFilterInstructionTemplate)) ;
    302     if ( FilterProgram == NULL ) {
    303       goto ErrorExit;
    304     }
    305 
    306     CopyMem (FilterProgram, &mFilterInstructionTemplate, sizeof (mFilterInstructionTemplate));
    307 
    308     //
    309     // Insert out fake MAC address into the filter.  The data has to be host endian.
    310     //
    311     CopyMem (&Temp32, &Private->Mode->CurrentAddress.Addr[0], sizeof (UINT32));
    312     FilterProgram[1].k = NTOHL (Temp32);
    313     CopyMem (&Temp16, &Private->Mode->CurrentAddress.Addr[4], sizeof (UINT16));
    314     FilterProgram[3].k = NTOHS (Temp16);
    315 
    316     BpfProgram.bf_len = sizeof (mFilterInstructionTemplate) / sizeof (struct bpf_insn);
    317     BpfProgram.bf_insns = FilterProgram;
    318 
    319     if (ioctl (Private->BpfFd, BIOCSETF, &BpfProgram) < 0) {
    320       goto DeviceErrorExit;
    321     }
    322 
    323     free (FilterProgram);
    324 
    325     //
    326     // Enable promiscuous mode.
    327     //
    328     if (ioctl (Private->BpfFd, BIOCPROMISC, 0) < 0) {
    329       goto DeviceErrorExit;
    330     }
    331 
    332 
    333     Private->Mode->State = EfiSimpleNetworkStarted;
    334   }
    335 
    336   return Status;
    337 
    338 DeviceErrorExit:
    339   Status = EFI_DEVICE_ERROR;
    340 ErrorExit:
    341   if (Private->ReadBuffer != NULL) {
    342     free (Private->ReadBuffer);
    343     Private->ReadBuffer = NULL;
    344   }
    345   return Status;
    346 }
    347 
    348 
    349 /**
    350   Changes the state of a network interface from "started" to "stopped".
    351 
    352   @param  This Protocol instance pointer.
    353 
    354   @retval EFI_SUCCESS           The network interface was stopped.
    355   @retval EFI_ALREADY_STARTED   The network interface is already in the stopped state.
    356   @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
    357   @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
    358   @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
    359 
    360 **/
    361 EFI_STATUS
    362 EmuSnpStop (
    363   IN EMU_SNP_PROTOCOL  *This
    364   )
    365 {
    366   EMU_SNP_PRIVATE    *Private;
    367 
    368   Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
    369 
    370   switch ( Private->Mode->State ) {
    371     case EfiSimpleNetworkStarted:
    372       break;
    373 
    374     case EfiSimpleNetworkStopped:
    375       return EFI_NOT_STARTED;
    376       break;
    377 
    378     default:
    379       return EFI_DEVICE_ERROR;
    380       break;
    381   }
    382 
    383   if (Private->BpfFd != 0) {
    384     close (Private->BpfFd);
    385     Private->BpfFd = 0;
    386   }
    387 
    388   if (Private->ReadBuffer != NULL) {
    389     free (Private->ReadBuffer );
    390     Private->CurrentReadPointer = Private->EndReadPointer = Private->ReadBuffer = NULL;
    391   }
    392 
    393   Private->Mode->State = EfiSimpleNetworkStopped;
    394 
    395   return EFI_SUCCESS;
    396 }
    397 
    398 
    399 /**
    400   Resets a network adapter and allocates the transmit and receive buffers
    401   required by the network interface; optionally, also requests allocation
    402   of additional transmit and receive buffers.
    403 
    404   @param  This              The protocol instance pointer.
    405   @param  ExtraRxBufferSize The size, in bytes, of the extra receive buffer space
    406                             that the driver should allocate for the network interface.
    407                             Some network interfaces will not be able to use the extra
    408                             buffer, and the caller will not know if it is actually
    409                             being used.
    410   @param  ExtraTxBufferSize The size, in bytes, of the extra transmit buffer space
    411                             that the driver should allocate for the network interface.
    412                             Some network interfaces will not be able to use the extra
    413                             buffer, and the caller will not know if it is actually
    414                             being used.
    415 
    416   @retval EFI_SUCCESS           The network interface was initialized.
    417   @retval EFI_NOT_STARTED       The network interface has not been started.
    418   @retval EFI_OUT_OF_RESOURCES  There was not enough memory for the transmit and
    419                                 receive buffers.
    420   @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
    421   @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
    422   @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
    423 
    424 **/
    425 EFI_STATUS
    426 EmuSnpInitialize (
    427   IN EMU_SNP_PROTOCOL                    *This,
    428   IN UINTN                               ExtraRxBufferSize  OPTIONAL,
    429   IN UINTN                               ExtraTxBufferSize  OPTIONAL
    430   )
    431 {
    432   EMU_SNP_PRIVATE    *Private;
    433 
    434   Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
    435 
    436   switch ( Private->Mode->State ) {
    437     case EfiSimpleNetworkStarted:
    438       break;
    439 
    440     case EfiSimpleNetworkStopped:
    441       return EFI_NOT_STARTED;
    442       break;
    443 
    444     default:
    445       return EFI_DEVICE_ERROR;
    446       break;
    447   }
    448 
    449   Private->Mode->MCastFilterCount = 0;
    450   Private->Mode->ReceiveFilterSetting = 0;
    451   ZeroMem (Private->Mode->MCastFilter, sizeof (Private->Mode->MCastFilter));
    452 
    453   Private->Mode->State = EfiSimpleNetworkInitialized;
    454 
    455   return EFI_SUCCESS;
    456 }
    457 
    458 
    459 /**
    460   Resets a network adapter and re-initializes it with the parameters that were
    461   provided in the previous call to Initialize().
    462 
    463   @param  This                 The protocol instance pointer.
    464   @param  ExtendedVerification Indicates that the driver may perform a more
    465                                exhaustive verification operation of the device
    466                                during reset.
    467 
    468   @retval EFI_SUCCESS           The network interface was reset.
    469   @retval EFI_NOT_STARTED       The network interface has not been started.
    470   @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
    471   @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
    472   @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
    473 
    474 **/
    475 EFI_STATUS
    476 EmuSnpReset (
    477   IN EMU_SNP_PROTOCOL   *This,
    478   IN BOOLEAN            ExtendedVerification
    479   )
    480 {
    481   EMU_SNP_PRIVATE    *Private;
    482 
    483   Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
    484 
    485   switch ( Private->Mode->State ) {
    486     case EfiSimpleNetworkInitialized:
    487       break;
    488 
    489     case EfiSimpleNetworkStopped:
    490       return EFI_NOT_STARTED;
    491       break;
    492 
    493     default:
    494       return EFI_DEVICE_ERROR;
    495       break;
    496   }
    497 
    498   return EFI_SUCCESS;
    499 }
    500 
    501 
    502 /**
    503   Resets a network adapter and leaves it in a state that is safe for
    504   another driver to initialize.
    505 
    506   @param  This Protocol instance pointer.
    507 
    508   @retval EFI_SUCCESS           The network interface was shutdown.
    509   @retval EFI_NOT_STARTED       The network interface has not been started.
    510   @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
    511   @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
    512   @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
    513 
    514 **/
    515 EFI_STATUS
    516 EmuSnpShutdown (
    517   IN EMU_SNP_PROTOCOL  *This
    518   )
    519 {
    520   EMU_SNP_PRIVATE    *Private;
    521 
    522   Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
    523 
    524   switch ( Private->Mode->State ) {
    525     case EfiSimpleNetworkInitialized:
    526       break;
    527 
    528     case EfiSimpleNetworkStopped:
    529       return EFI_NOT_STARTED;
    530       break;
    531 
    532     default:
    533       return EFI_DEVICE_ERROR;
    534       break;
    535   }
    536 
    537   Private->Mode->State = EfiSimpleNetworkStarted;
    538 
    539   Private->Mode->ReceiveFilterSetting = 0;
    540   Private->Mode->MCastFilterCount = 0;
    541   ZeroMem (Private->Mode->MCastFilter, sizeof (Private->Mode->MCastFilter));
    542 
    543   if (Private->BpfFd != 0) {
    544     close (Private->BpfFd);
    545     Private->BpfFd = 0;
    546   }
    547 
    548   if (Private->ReadBuffer != NULL) {
    549     free (Private->ReadBuffer);
    550     Private->CurrentReadPointer = Private->EndReadPointer = Private->ReadBuffer = NULL;
    551   }
    552 
    553   return EFI_SUCCESS;
    554 }
    555 
    556 /**
    557   Manages the multicast receive filters of a network interface.
    558 
    559   @param  This             The protocol instance pointer.
    560   @param  Enable           A bit mask of receive filters to enable on the network interface.
    561   @param  Disable          A bit mask of receive filters to disable on the network interface.
    562   @param  ResetMCastFilter Set to TRUE to reset the contents of the multicast receive
    563                            filters on the network interface to their default values.
    564   @param  McastFilterCnt   Number of multicast HW MAC addresses in the new
    565                            MCastFilter list. This value must be less than or equal to
    566                            the MCastFilterCnt field of EMU_SNP_MODE. This
    567                            field is optional if ResetMCastFilter is TRUE.
    568   @param  MCastFilter      A pointer to a list of new multicast receive filter HW MAC
    569                            addresses. This list will replace any existing multicast
    570                            HW MAC address list. This field is optional if
    571                            ResetMCastFilter is TRUE.
    572 
    573   @retval EFI_SUCCESS           The multicast receive filter list was updated.
    574   @retval EFI_NOT_STARTED       The network interface has not been started.
    575   @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
    576   @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
    577   @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
    578 
    579 **/
    580 EFI_STATUS
    581 EmuSnpReceiveFilters (
    582   IN EMU_SNP_PROTOCOL                             *This,
    583   IN UINT32                                       Enable,
    584   IN UINT32                                       Disable,
    585   IN BOOLEAN                                      ResetMCastFilter,
    586   IN UINTN                                        MCastFilterCnt     OPTIONAL,
    587   IN EFI_MAC_ADDRESS                              *MCastFilter OPTIONAL
    588   )
    589 {
    590   EMU_SNP_PRIVATE    *Private;
    591 
    592   Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
    593 
    594   // For now, just succeed...
    595   return EFI_SUCCESS;
    596 }
    597 
    598 
    599 /**
    600   Modifies or resets the current station address, if supported.
    601 
    602   @param  This  The protocol instance pointer.
    603   @param  Reset Flag used to reset the station address to the network interfaces
    604                 permanent address.
    605   @param  New   The new station address to be used for the network interface.
    606 
    607   @retval EFI_SUCCESS           The network interfaces station address was updated.
    608   @retval EFI_NOT_STARTED       The network interface has not been started.
    609   @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
    610   @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
    611   @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
    612 
    613 **/
    614 EFI_STATUS
    615 EmuSnpStationAddress (
    616   IN EMU_SNP_PROTOCOL            *This,
    617   IN BOOLEAN                     Reset,
    618   IN EFI_MAC_ADDRESS             *New OPTIONAL
    619   )
    620 {
    621   EMU_SNP_PRIVATE    *Private;
    622 
    623   Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
    624 
    625   return EFI_UNSUPPORTED;
    626 }
    627 
    628 
    629 /**
    630   Resets or collects the statistics on a network interface.
    631 
    632   @param  This            Protocol instance pointer.
    633   @param  Reset           Set to TRUE to reset the statistics for the network interface.
    634   @param  StatisticsSize  On input the size, in bytes, of StatisticsTable. On
    635                           output the size, in bytes, of the resulting table of
    636                           statistics.
    637   @param  StatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that
    638                           contains the statistics.
    639 
    640   @retval EFI_SUCCESS           The statistics were collected from the network interface.
    641   @retval EFI_NOT_STARTED       The network interface has not been started.
    642   @retval EFI_BUFFER_TOO_SMALL  The Statistics buffer was too small. The current buffer
    643                                 size needed to hold the statistics is returned in
    644                                 StatisticsSize.
    645   @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
    646   @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
    647   @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
    648 
    649 **/
    650 EFI_STATUS
    651 EmuSnpStatistics (
    652   IN EMU_SNP_PROTOCOL                     *This,
    653   IN BOOLEAN                              Reset,
    654   IN OUT UINTN                            *StatisticsSize   OPTIONAL,
    655   OUT EFI_NETWORK_STATISTICS              *StatisticsTable  OPTIONAL
    656   )
    657 {
    658   EMU_SNP_PRIVATE    *Private;
    659 
    660   Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
    661 
    662   return EFI_UNSUPPORTED;
    663 }
    664 
    665 
    666 /**
    667   Converts a multicast IP address to a multicast HW MAC address.
    668 
    669   @param  This The protocol instance pointer.
    670   @param  IPv6 Set to TRUE if the multicast IP address is IPv6 [RFC 2460]. Set
    671                to FALSE if the multicast IP address is IPv4 [RFC 791].
    672   @param  IP   The multicast IP address that is to be converted to a multicast
    673                HW MAC address.
    674   @param  MAC  The multicast HW MAC address that is to be generated from IP.
    675 
    676   @retval EFI_SUCCESS           The multicast IP address was mapped to the multicast
    677                                 HW MAC address.
    678   @retval EFI_NOT_STARTED       The network interface has not been started.
    679   @retval EFI_BUFFER_TOO_SMALL  The Statistics buffer was too small. The current buffer
    680                                 size needed to hold the statistics is returned in
    681                                 StatisticsSize.
    682   @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
    683   @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
    684   @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
    685 
    686 **/
    687 EFI_STATUS
    688 EmuSnpMCastIpToMac (
    689   IN EMU_SNP_PROTOCOL                     *This,
    690   IN BOOLEAN                              IPv6,
    691   IN EFI_IP_ADDRESS                       *IP,
    692   OUT EFI_MAC_ADDRESS                     *MAC
    693   )
    694 {
    695   EMU_SNP_PRIVATE    *Private;
    696 
    697   Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
    698 
    699   return EFI_UNSUPPORTED;
    700 }
    701 
    702 
    703 /**
    704   Performs read and write operations on the NVRAM device attached to a
    705   network interface.
    706 
    707   @param  This       The protocol instance pointer.
    708   @param  ReadWrite  TRUE for read operations, FALSE for write operations.
    709   @param  Offset     Byte offset in the NVRAM device at which to start the read or
    710                      write operation. This must be a multiple of NvRamAccessSize and
    711                      less than NvRamSize.
    712   @param  BufferSize The number of bytes to read or write from the NVRAM device.
    713                      This must also be a multiple of NvramAccessSize.
    714   @param  Buffer     A pointer to the data buffer.
    715 
    716   @retval EFI_SUCCESS           The NVRAM access was performed.
    717   @retval EFI_NOT_STARTED       The network interface has not been started.
    718   @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
    719   @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
    720   @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
    721 
    722 **/
    723 EFI_STATUS
    724 EmuSnpNvData (
    725   IN EMU_SNP_PROTOCOL                     *This,
    726   IN BOOLEAN                              ReadWrite,
    727   IN UINTN                                Offset,
    728   IN UINTN                                BufferSize,
    729   IN OUT VOID                             *Buffer
    730   )
    731 {
    732   EMU_SNP_PRIVATE    *Private;
    733 
    734   Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
    735 
    736   return EFI_UNSUPPORTED;
    737 }
    738 
    739 /**
    740   Reads the current interrupt status and recycled transmit buffer status from
    741   a network interface.
    742 
    743   @param  This            The protocol instance pointer.
    744   @param  InterruptStatus A pointer to the bit mask of the currently active interrupts
    745                           If this is NULL, the interrupt status will not be read from
    746                           the device. If this is not NULL, the interrupt status will
    747                           be read from the device. When the  interrupt status is read,
    748                           it will also be cleared. Clearing the transmit  interrupt
    749                           does not empty the recycled transmit buffer array.
    750   @param  TxBuf           Recycled transmit buffer address. The network interface will
    751                           not transmit if its internal recycled transmit buffer array
    752                           is full. Reading the transmit buffer does not clear the
    753                           transmit interrupt. If this is NULL, then the transmit buffer
    754                           status will not be read. If there are no transmit buffers to
    755                           recycle and TxBuf is not NULL, * TxBuf will be set to NULL.
    756 
    757   @retval EFI_SUCCESS           The status of the network interface was retrieved.
    758   @retval EFI_NOT_STARTED       The network interface has not been started.
    759   @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
    760   @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
    761   @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
    762 
    763 **/
    764 EFI_STATUS
    765 EmuSnpGetStatus (
    766   IN EMU_SNP_PROTOCOL                     *This,
    767   OUT UINT32                              *InterruptStatus OPTIONAL,
    768   OUT VOID                                **TxBuf OPTIONAL
    769   )
    770 {
    771   EMU_SNP_PRIVATE    *Private;
    772 
    773   Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
    774 
    775   if (TxBuf != NULL) {
    776     *((UINT8 **)TxBuf) =  (UINT8 *)1;
    777   }
    778 
    779   if ( InterruptStatus != NULL ) {
    780     *InterruptStatus = EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
    781   }
    782 
    783   return EFI_SUCCESS;
    784 }
    785 
    786 
    787 /**
    788   Places a packet in the transmit queue of a network interface.
    789 
    790   @param  This       The protocol instance pointer.
    791   @param  HeaderSize The size, in bytes, of the media header to be filled in by
    792                      the Transmit() function. If HeaderSize is non-zero, then it
    793                      must be equal to This->Mode->MediaHeaderSize and the DestAddr
    794                      and Protocol parameters must not be NULL.
    795   @param  BufferSize The size, in bytes, of the entire packet (media header and
    796                      data) to be transmitted through the network interface.
    797   @param  Buffer     A pointer to the packet (media header followed by data) to be
    798                      transmitted. This parameter cannot be NULL. If HeaderSize is zero,
    799                      then the media header in Buffer must already be filled in by the
    800                      caller. If HeaderSize is non-zero, then the media header will be
    801                      filled in by the Transmit() function.
    802   @param  SrcAddr    The source HW MAC address. If HeaderSize is zero, then this parameter
    803                      is ignored. If HeaderSize is non-zero and SrcAddr is NULL, then
    804                      This->Mode->CurrentAddress is used for the source HW MAC address.
    805   @param  DestAddr   The destination HW MAC address. If HeaderSize is zero, then this
    806                      parameter is ignored.
    807   @param  Protocol   The type of header to build. If HeaderSize is zero, then this
    808                      parameter is ignored. See RFC 1700, section "Ether Types", for
    809                      examples.
    810 
    811   @retval EFI_SUCCESS           The packet was placed on the transmit queue.
    812   @retval EFI_NOT_STARTED       The network interface has not been started.
    813   @retval EFI_NOT_READY         The network interface is too busy to accept this transmit request.
    814   @retval EFI_BUFFER_TOO_SMALL  The BufferSize parameter is too small.
    815   @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
    816   @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
    817   @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
    818 
    819 **/
    820 EFI_STATUS
    821 EmuSnpTransmit (
    822   IN EMU_SNP_PROTOCOL                     *This,
    823   IN UINTN                                HeaderSize,
    824   IN UINTN                                BufferSize,
    825   IN VOID                                 *Buffer,
    826   IN EFI_MAC_ADDRESS                      *SrcAddr  OPTIONAL,
    827   IN EFI_MAC_ADDRESS                      *DestAddr OPTIONAL,
    828   IN UINT16                               *Protocol OPTIONAL
    829   )
    830 {
    831   EMU_SNP_PRIVATE    *Private;
    832   ETHERNET_HEADER    *EnetHeader;
    833 
    834   Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
    835 
    836   if (Private->Mode->State < EfiSimpleNetworkStarted) {
    837     return EFI_NOT_STARTED;
    838   }
    839 
    840   if ( HeaderSize != 0 ) {
    841     if ((DestAddr == NULL) || (Protocol == NULL) || (HeaderSize != Private->Mode->MediaHeaderSize)) {
    842       return EFI_INVALID_PARAMETER;
    843     }
    844 
    845     if (SrcAddr == NULL) {
    846       SrcAddr = &Private->Mode->CurrentAddress;
    847     }
    848 
    849     EnetHeader = (ETHERNET_HEADER *) Buffer;
    850 
    851     CopyMem (EnetHeader->DstAddr, DestAddr, NET_ETHER_ADDR_LEN);
    852     CopyMem (EnetHeader->SrcAddr, SrcAddr, NET_ETHER_ADDR_LEN);
    853 
    854     EnetHeader->Type = HTONS(*Protocol);
    855   }
    856 
    857   if (write  (Private->BpfFd, Buffer, BufferSize) < 0) {
    858     return EFI_DEVICE_ERROR;
    859   }
    860 
    861   return EFI_SUCCESS;
    862 }
    863 
    864 /**
    865   Receives a packet from a network interface.
    866 
    867   @param  This       The protocol instance pointer.
    868   @param  HeaderSize The size, in bytes, of the media header received on the network
    869                      interface. If this parameter is NULL, then the media header size
    870                      will not be returned.
    871   @param  BufferSize On entry, the size, in bytes, of Buffer. On exit, the size, in
    872                      bytes, of the packet that was received on the network interface.
    873   @param  Buffer     A pointer to the data buffer to receive both the media header and
    874                      the data.
    875   @param  SrcAddr    The source HW MAC address. If this parameter is NULL, the
    876                      HW MAC source address will not be extracted from the media
    877                      header.
    878   @param  DestAddr   The destination HW MAC address. If this parameter is NULL,
    879                      the HW MAC destination address will not be extracted from the
    880                      media header.
    881   @param  Protocol   The media header type. If this parameter is NULL, then the
    882                      protocol will not be extracted from the media header. See
    883                      RFC 1700 section "Ether Types" for examples.
    884 
    885   @retval  EFI_SUCCESS           The received data was stored in Buffer, and BufferSize has
    886                                  been updated to the number of bytes received.
    887   @retval  EFI_NOT_STARTED       The network interface has not been started.
    888   @retval  EFI_NOT_READY         The network interface is too busy to accept this transmit
    889                                  request.
    890   @retval  EFI_BUFFER_TOO_SMALL  The BufferSize parameter is too small.
    891   @retval  EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
    892   @retval  EFI_DEVICE_ERROR      The command could not be sent to the network interface.
    893   @retval  EFI_UNSUPPORTED       This function is not supported by the network interface.
    894 
    895 **/
    896 EFI_STATUS
    897 EmuSnpReceive (
    898   IN EMU_SNP_PROTOCOL                     *This,
    899   OUT UINTN                               *HeaderSize OPTIONAL,
    900   IN OUT UINTN                            *BufferSize,
    901   OUT VOID                                *Buffer,
    902   OUT EFI_MAC_ADDRESS                     *SrcAddr    OPTIONAL,
    903   OUT EFI_MAC_ADDRESS                     *DestAddr   OPTIONAL,
    904   OUT UINT16                              *Protocol   OPTIONAL
    905   )
    906 {
    907   EMU_SNP_PRIVATE    *Private;
    908   struct bpf_hdr     *BpfHeader;
    909 	struct bpf_stat	   BpfStats;
    910   ETHERNET_HEADER    *EnetHeader;
    911   ssize_t            Result;
    912 
    913   Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
    914 
    915   if (Private->Mode->State < EfiSimpleNetworkStarted) {
    916     return EFI_NOT_STARTED;
    917   }
    918 
    919 	ZeroMem (&BpfStats, sizeof( BpfStats));
    920 
    921 	if (ioctl (Private->BpfFd, BIOCGSTATS, &BpfStats) == 0) {
    922 		Private->ReceivedPackets += BpfStats.bs_recv;
    923 		if (BpfStats.bs_drop > Private->DroppedPackets) {
    924 			printf (
    925 			  "SNP: STATS: RCVD = %d DROPPED = %d.  Probably need to increase BPF PcdNetworkPacketFilterSize?\n",
    926 				BpfStats.bs_recv,
    927 				BpfStats.bs_drop - Private->DroppedPackets
    928 				);
    929 			Private->DroppedPackets = BpfStats.bs_drop;
    930 		}
    931 	}
    932 
    933   //
    934   // Do we have any remaining packets from the previous read?
    935   //
    936   if (Private->CurrentReadPointer >= Private->EndReadPointer) {
    937     Result = read (Private->BpfFd, Private->ReadBuffer, Private->ReadBufferSize);
    938     if (Result < 0) {
    939       // EAGAIN means that there's no I/O outstanding against this file descriptor.
    940       return (errno == EAGAIN) ? EFI_NOT_READY : EFI_DEVICE_ERROR;
    941     }
    942 
    943     if (Result == 0) {
    944       return EFI_NOT_READY;
    945     }
    946 
    947     Private->CurrentReadPointer = Private->ReadBuffer;
    948     Private->EndReadPointer = Private->CurrentReadPointer + Result;
    949   }
    950 
    951   BpfHeader = Private->CurrentReadPointer;
    952   EnetHeader = Private->CurrentReadPointer + BpfHeader->bh_hdrlen;
    953 
    954   if (BpfHeader->bh_caplen > *BufferSize) {
    955     *BufferSize = BpfHeader->bh_caplen;
    956     return EFI_BUFFER_TOO_SMALL;
    957   }
    958 
    959   CopyMem (Buffer, EnetHeader, BpfHeader->bh_caplen);
    960   *BufferSize = BpfHeader->bh_caplen;
    961 
    962   if (HeaderSize != NULL) {
    963     *HeaderSize = sizeof (ETHERNET_HEADER);
    964   }
    965 
    966   if (DestAddr != NULL) {
    967     ZeroMem (DestAddr, sizeof (EFI_MAC_ADDRESS));
    968     CopyMem (DestAddr, EnetHeader->DstAddr, NET_ETHER_ADDR_LEN);
    969   }
    970 
    971   if (SrcAddr != NULL) {
    972     ZeroMem (SrcAddr, sizeof (EFI_MAC_ADDRESS));
    973     CopyMem (SrcAddr, EnetHeader->SrcAddr, NET_ETHER_ADDR_LEN);
    974   }
    975 
    976   if (Protocol != NULL) {
    977     *Protocol = NTOHS (EnetHeader->Type);
    978   }
    979 
    980   Private->CurrentReadPointer += BPF_WORDALIGN (BpfHeader->bh_hdrlen + BpfHeader->bh_caplen);
    981   return EFI_SUCCESS;
    982 }
    983 
    984 
    985 EMU_SNP_PROTOCOL gEmuSnpProtocol = {
    986   GasketSnpCreateMapping,
    987   GasketSnpStart,
    988   GasketSnpStop,
    989   GasketSnpInitialize,
    990   GasketSnpReset,
    991   GasketSnpShutdown,
    992   GasketSnpReceiveFilters,
    993   GasketSnpStationAddress,
    994   GasketSnpStatistics,
    995   GasketSnpMCastIpToMac,
    996   GasketSnpNvData,
    997   GasketSnpGetStatus,
    998   GasketSnpTransmit,
    999   GasketSnpReceive
   1000 };
   1001 
   1002 EFI_STATUS
   1003 GetInterfaceMacAddr (
   1004   EMU_SNP_PRIVATE    *Private
   1005   )
   1006 {
   1007 	EFI_STATUS				  Status;
   1008   struct ifaddrs      *IfAddrs;
   1009   struct ifaddrs      *If;
   1010   struct sockaddr_dl  *IfSdl;
   1011 
   1012   if (getifaddrs (&IfAddrs) != 0) {
   1013     return EFI_UNSUPPORTED;
   1014   }
   1015 
   1016   //
   1017   // Convert the interface name to ASCII so we can find it.
   1018   //
   1019   Private->InterfaceName = malloc (StrSize (Private->Thunk->ConfigString));
   1020   if (Private->InterfaceName == NULL) {
   1021     Status = EFI_OUT_OF_RESOURCES;
   1022     goto Exit;
   1023   }
   1024 
   1025   UnicodeStrToAsciiStr (Private->Thunk->ConfigString, Private->InterfaceName);
   1026 
   1027   Status = EFI_NOT_FOUND;
   1028   If = IfAddrs;
   1029   while (If != NULL) {
   1030     IfSdl = (struct sockaddr_dl *)If->ifa_addr;
   1031 
   1032     if (IfSdl->sdl_family == AF_LINK) {
   1033       if (!AsciiStrCmp( Private->InterfaceName, If->ifa_name)) {
   1034         CopyMem (&Private->MacAddress, LLADDR (IfSdl), NET_ETHER_ADDR_LEN);
   1035 
   1036         Status = EFI_SUCCESS;
   1037         break;
   1038       }
   1039     }
   1040 
   1041     If = If->ifa_next;
   1042   }
   1043 
   1044 Exit:
   1045   freeifaddrs (IfAddrs);
   1046   return Status;
   1047 }
   1048 
   1049 
   1050 EFI_STATUS
   1051 EmuSnpThunkOpen (
   1052   IN  EMU_IO_THUNK_PROTOCOL   *This
   1053   )
   1054 {
   1055   EMU_SNP_PRIVATE  *Private;
   1056 
   1057   if (This->Private != NULL) {
   1058     return EFI_ALREADY_STARTED;
   1059   }
   1060 
   1061   if (!CompareGuid (This->Protocol, &gEmuSnpProtocolGuid)) {
   1062     return EFI_UNSUPPORTED;
   1063   }
   1064 
   1065   Private = malloc (sizeof (EMU_SNP_PRIVATE));
   1066   if (Private == NULL) {
   1067     return EFI_OUT_OF_RESOURCES;
   1068   }
   1069 
   1070 
   1071   Private->Signature = EMU_SNP_PRIVATE_SIGNATURE;
   1072   Private->Thunk     = This;
   1073   CopyMem (&Private->EmuSnp, &gEmuSnpProtocol, sizeof (gEmuSnpProtocol));
   1074   GetInterfaceMacAddr (Private);
   1075 
   1076   This->Interface = &Private->EmuSnp;
   1077   This->Private   = Private;
   1078   return EFI_SUCCESS;
   1079 }
   1080 
   1081 
   1082 EFI_STATUS
   1083 EmuSnpThunkClose (
   1084   IN  EMU_IO_THUNK_PROTOCOL   *This
   1085   )
   1086 {
   1087   EMU_SNP_PRIVATE  *Private;
   1088 
   1089   if (!CompareGuid (This->Protocol, &gEmuSnpProtocolGuid)) {
   1090     return EFI_UNSUPPORTED;
   1091   }
   1092 
   1093   Private = This->Private;
   1094   free (Private);
   1095 
   1096   return EFI_SUCCESS;
   1097 }
   1098 
   1099 
   1100 
   1101 EMU_IO_THUNK_PROTOCOL gSnpThunkIo = {
   1102   &gEmuSnpProtocolGuid,
   1103   NULL,
   1104   NULL,
   1105   0,
   1106   GasketSnpThunkOpen,
   1107   GasketSnpThunkClose,
   1108   NULL
   1109 };
   1110 
   1111 #endif
   1112