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