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