620b6314be37237f7f31c435e3b352794a1a521a
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Ip4Dxe / Ip4Input.c
1 /** @file\r
2 \r
3 Copyright (c) 2005 - 2007, Intel Corporation\r
4 All rights reserved. This program and the accompanying materials\r
5 are licensed and made available under the terms and conditions of the BSD License\r
6 which accompanies this distribution.  The full text of the license may be found at\r
7 http://opensource.org/licenses/bsd-license.php\r
8 \r
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11 \r
12 Module Name:\r
13 \r
14   Ip4Input.c\r
15 \r
16 Abstract:\r
17 \r
18   IP4 input process.\r
19 \r
20 \r
21 **/\r
22 \r
23 #include "Ip4Impl.h"\r
24 \r
25 \r
26 /**\r
27   Create a empty assemble entry for the packet identified by\r
28   (Dst, Src, Id, Protocol). The default life for the packet is\r
29   120 seconds.\r
30 \r
31   @param  Dst                    The destination address\r
32   @param  Src                    The source address\r
33   @param  Id                     The ID field in IP header\r
34   @param  Protocol               The protocol field in IP header\r
35 \r
36   @return NULL if failed to allocate memory for the entry, otherwise\r
37   @return the point to just created reassemble entry.\r
38 \r
39 **/\r
40 IP4_ASSEMBLE_ENTRY *\r
41 Ip4CreateAssembleEntry (\r
42   IN IP4_ADDR               Dst,\r
43   IN IP4_ADDR               Src,\r
44   IN UINT16                 Id,\r
45   IN UINT8                  Protocol\r
46   )\r
47 {\r
48 \r
49   IP4_ASSEMBLE_ENTRY        *Assemble;\r
50 \r
51   Assemble = AllocatePool (sizeof (IP4_ASSEMBLE_ENTRY));\r
52 \r
53   if (Assemble == NULL) {\r
54     return NULL;\r
55   }\r
56 \r
57   InitializeListHead (&Assemble->Link);\r
58   InitializeListHead (&Assemble->Fragments);\r
59 \r
60   Assemble->Dst      = Dst;\r
61   Assemble->Src      = Src;\r
62   Assemble->Id       = Id;\r
63   Assemble->Protocol = Protocol;\r
64   Assemble->TotalLen = 0;\r
65   Assemble->CurLen   = 0;\r
66   Assemble->Head     = NULL;\r
67   Assemble->Info     = NULL;\r
68   Assemble->Life     = IP4_FRAGMENT_LIFE;\r
69 \r
70   return Assemble;\r
71 }\r
72 \r
73 \r
74 /**\r
75   Release all the fragments of a packet, then free the assemble entry\r
76 \r
77   @param  Assemble               The assemble entry to free\r
78 \r
79   @return None\r
80 \r
81 **/\r
82 VOID\r
83 Ip4FreeAssembleEntry (\r
84   IN IP4_ASSEMBLE_ENTRY     *Assemble\r
85   )\r
86 {\r
87   LIST_ENTRY                *Entry;\r
88   LIST_ENTRY                *Next;\r
89   NET_BUF                   *Fragment;\r
90 \r
91   NET_LIST_FOR_EACH_SAFE (Entry, Next, &Assemble->Fragments) {\r
92     Fragment = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
93 \r
94     RemoveEntryList (Entry);\r
95     NetbufFree (Fragment);\r
96   }\r
97 \r
98   gBS->FreePool (Assemble);\r
99 }\r
100 \r
101 \r
102 /**\r
103   Initialize an already allocated assemble table. This is generally\r
104   the assemble table embedded in the IP4 service instance.\r
105 \r
106   @param  Table                  The assemble table to initialize.\r
107 \r
108   @return NONE\r
109 \r
110 **/\r
111 VOID\r
112 Ip4InitAssembleTable (\r
113   IN IP4_ASSEMBLE_TABLE     *Table\r
114   )\r
115 {\r
116   UINT32                    Index;\r
117 \r
118   for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) {\r
119     InitializeListHead (&Table->Bucket[Index]);\r
120   }\r
121 }\r
122 \r
123 \r
124 /**\r
125   Clean up the assemble table: remove all the fragments\r
126   and assemble entries.\r
127 \r
128   @param  Table                  The assemble table to clean up\r
129 \r
130   @return None\r
131 \r
132 **/\r
133 VOID\r
134 Ip4CleanAssembleTable (\r
135   IN IP4_ASSEMBLE_TABLE     *Table\r
136   )\r
137 {\r
138   LIST_ENTRY                *Entry;\r
139   LIST_ENTRY                *Next;\r
140   IP4_ASSEMBLE_ENTRY        *Assemble;\r
141   UINT32                    Index;\r
142 \r
143   for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) {\r
144     NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) {\r
145       Assemble = NET_LIST_USER_STRUCT (Entry, IP4_ASSEMBLE_ENTRY, Link);\r
146 \r
147       RemoveEntryList (Entry);\r
148       Ip4FreeAssembleEntry (Assemble);\r
149     }\r
150   }\r
151 }\r
152 \r
153 \r
154 /**\r
155   Trim the packet to fit in [Start, End), and update the per\r
156   packet information.\r
157 \r
158   @param  Packet                 Packet to trim\r
159   @param  Start                  The sequence of the first byte to fit in\r
160   @param  End                    One beyond the sequence of last byte to fit in.\r
161 \r
162   @return None\r
163 \r
164 **/\r
165 VOID\r
166 Ip4TrimPacket (\r
167   IN NET_BUF                *Packet,\r
168   IN INTN                   Start,\r
169   IN INTN                   End\r
170   )\r
171 {\r
172   IP4_CLIP_INFO             *Info;\r
173   INTN                      Len;\r
174 \r
175   Info = IP4_GET_CLIP_INFO (Packet);\r
176 \r
177   ASSERT (Info->Start + Info->Length == Info->End);\r
178   ASSERT ((Info->Start < End) && (Start < Info->End));\r
179 \r
180    if (Info->Start < Start) {\r
181     Len = Start - Info->Start;\r
182 \r
183     NetbufTrim (Packet, (UINT32) Len, NET_BUF_HEAD);\r
184     Info->Start   = Start;\r
185     Info->Length -= Len;\r
186   }\r
187 \r
188   if (End < Info->End) {\r
189     Len = End - Info->End;\r
190 \r
191     NetbufTrim (Packet, (UINT32) Len, NET_BUF_TAIL);\r
192     Info->End     = End;\r
193     Info->Length -= Len;\r
194   }\r
195 }\r
196 \r
197 \r
198 /**\r
199   Release all the fragments of the packet. This is the callback for\r
200   the assembled packet's OnFree. It will free the assemble entry,\r
201   which in turn will free all the fragments of the packet.\r
202 \r
203   @param  Arg                    The assemble entry to free\r
204 \r
205   @return None\r
206 \r
207 **/\r
208 VOID\r
209 Ip4OnFreeFragments (\r
210   IN VOID                   *Arg\r
211   )\r
212 {\r
213   Ip4FreeAssembleEntry ((IP4_ASSEMBLE_ENTRY *) Arg);\r
214 }\r
215 \r
216 \r
217 /**\r
218   Reassemble the IP fragments. If all the fragments of the packet\r
219   have been received, it will wrap the packet in a net buffer then\r
220   return it to caller. If the packet can't be assembled, NULL is\r
221   return.\r
222 \r
223   @param  Table                  The assemble table used.\r
224   @param  Packet                 The fragment to assemble\r
225 \r
226   @return NULL if the packet can't be reassemble. The point to just assembled\r
227   @return packet if all the fragments of the packet have arrived.\r
228 \r
229 **/\r
230 NET_BUF *\r
231 Ip4Reassemble (\r
232   IN IP4_ASSEMBLE_TABLE     *Table,\r
233   IN NET_BUF                *Packet\r
234   )\r
235 {\r
236   IP4_HEAD                  *IpHead;\r
237   IP4_CLIP_INFO             *This;\r
238   IP4_CLIP_INFO             *Node;\r
239   IP4_ASSEMBLE_ENTRY        *Assemble;\r
240   LIST_ENTRY                *Head;\r
241   LIST_ENTRY                *Prev;\r
242   LIST_ENTRY                *Cur;\r
243   NET_BUF                   *Fragment;\r
244   NET_BUF                   *NewPacket;\r
245   INTN                      Index;\r
246 \r
247   IpHead  = Packet->Ip;\r
248   This    = IP4_GET_CLIP_INFO (Packet);\r
249 \r
250   ASSERT (IpHead != NULL);\r
251 \r
252   //\r
253   // First: find the related assemble entry\r
254   //\r
255   Assemble  = NULL;\r
256   Index     = IP4_ASSEMBLE_HASH (IpHead->Dst, IpHead->Src, IpHead->Id, IpHead->Protocol);\r
257 \r
258   NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) {\r
259     Assemble = NET_LIST_USER_STRUCT (Cur, IP4_ASSEMBLE_ENTRY, Link);\r
260 \r
261     if ((Assemble->Dst == IpHead->Dst) && (Assemble->Src == IpHead->Src) &&\r
262         (Assemble->Id == IpHead->Id)   && (Assemble->Protocol == IpHead->Protocol)) {\r
263       break;\r
264     }\r
265   }\r
266 \r
267   //\r
268   // Create a new assemble entry if no assemble entry is related to this packet\r
269   //\r
270   if (Cur == &Table->Bucket[Index]) {\r
271     Assemble = Ip4CreateAssembleEntry (\r
272                  IpHead->Dst,\r
273                  IpHead->Src,\r
274                  IpHead->Id,\r
275                  IpHead->Protocol\r
276                  );\r
277 \r
278     if (Assemble == NULL) {\r
279       goto DROP;\r
280     }\r
281 \r
282     InsertHeadList (&Table->Bucket[Index], &Assemble->Link);\r
283   }\r
284 \r
285   //\r
286   // Find the point to insert the packet: before the first\r
287   // fragment with THIS.Start < CUR.Start. the previous one\r
288   // has PREV.Start <= THIS.Start < CUR.Start.\r
289   //\r
290   Head = &Assemble->Fragments;\r
291 \r
292   NET_LIST_FOR_EACH (Cur, Head) {\r
293     Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
294 \r
295     if (This->Start < IP4_GET_CLIP_INFO (Fragment)->Start) {\r
296       break;\r
297     }\r
298   }\r
299 \r
300   //\r
301   // Check whether the current fragment overlaps with the previous one.\r
302   // It holds that: PREV.Start <= THIS.Start < THIS.End. Only need to\r
303   // check whether THIS.Start < PREV.End for overlap. If two fragments\r
304   // overlaps, trim the overlapped part off THIS fragment.\r
305   //\r
306   if ((Prev = Cur->ForwardLink) != Head) {\r
307     Fragment  = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);\r
308     Node      = IP4_GET_CLIP_INFO (Fragment);\r
309 \r
310     if (This->Start < Node->End) {\r
311       if (This->End <= Node->End) {\r
312         NetbufFree (Packet);\r
313         return NULL;\r
314       }\r
315 \r
316       Ip4TrimPacket (Packet, Node->End, This->End);\r
317     }\r
318   }\r
319 \r
320   //\r
321   // Insert the fragment into the packet. The fragment may be removed\r
322   // from the list by the following checks.\r
323   //\r
324   NetListInsertBefore (Cur, &Packet->List);\r
325 \r
326   //\r
327   // Check the packets after the insert point. It holds that:\r
328   // THIS.Start <= NODE.Start < NODE.End. The equality holds\r
329   // if PREV and NEXT are continuous. THIS fragment may fill\r
330   // several holes. Remove the completely overlapped fragments\r
331   //\r
332   while (Cur != Head) {\r
333     Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
334     Node     = IP4_GET_CLIP_INFO (Fragment);\r
335 \r
336     //\r
337     // Remove fragments completely overlapped by this fragment\r
338     //\r
339     if (Node->End <= This->End) {\r
340       Cur = Cur->ForwardLink;\r
341 \r
342       RemoveEntryList (&Fragment->List);\r
343       Assemble->CurLen -= Node->Length;\r
344 \r
345       NetbufFree (Fragment);\r
346       continue;\r
347     }\r
348 \r
349     //\r
350     // The conditions are: THIS.Start <= NODE.Start, and THIS.End <\r
351     // NODE.End. Two fragments overlaps if NODE.Start < THIS.End.\r
352     // If two fragments start at the same offset, remove THIS fragment\r
353     // because ((THIS.Start == NODE.Start) && (THIS.End < NODE.End)).\r
354     //\r
355     if (Node->Start < This->End) {\r
356       if (This->Start == Node->Start) {\r
357         RemoveEntryList (&Packet->List);\r
358         goto DROP;\r
359       }\r
360 \r
361       Ip4TrimPacket (Packet, This->Start, Node->Start);\r
362     }\r
363 \r
364     break;\r
365   }\r
366 \r
367   //\r
368   // Update the assemble info: increase the current length. If it is\r
369   // the frist fragment, update the packet's IP head and per packet\r
370   // info. If it is the last fragment, update the total length.\r
371   //\r
372   Assemble->CurLen += This->Length;\r
373 \r
374   if (This->Start == 0) {\r
375     //\r
376     // Once the first fragment is enqueued, it can't be removed\r
377     // from the fragment list. So, Assemble->Head always point\r
378     // to valid memory area.\r
379     //\r
380     ASSERT (Assemble->Head == NULL);\r
381 \r
382     Assemble->Head  = IpHead;\r
383     Assemble->Info  = IP4_GET_CLIP_INFO (Packet);\r
384   }\r
385 \r
386   //\r
387   // Don't update the length more than once.\r
388   //\r
389   if (IP4_LAST_FRAGMENT (IpHead->Fragment) && (Assemble->TotalLen == 0)) {\r
390     Assemble->TotalLen = This->End;\r
391   }\r
392 \r
393   //\r
394   // Deliver the whole packet if all the fragments received.\r
395   // All fragments received if:\r
396   //  1. received the last one, so, the totoal length is know\r
397   //  2. received all the data. If the last fragment on the\r
398   //     queue ends at the total length, all data is received.\r
399   //\r
400   if ((Assemble->TotalLen != 0) && (Assemble->CurLen >= Assemble->TotalLen)) {\r
401 \r
402     RemoveEntryList (&Assemble->Link);\r
403 \r
404     //\r
405     // If the packet is properly formated, the last fragment's End\r
406     // equals to the packet's total length. Otherwise, the packet\r
407     // is a fake, drop it now.\r
408     //\r
409     Fragment = NET_LIST_USER_STRUCT (Head->BackLink, NET_BUF, List);\r
410 \r
411     if (IP4_GET_CLIP_INFO (Fragment)->End != Assemble->TotalLen) {\r
412       Ip4FreeAssembleEntry (Assemble);\r
413       return NULL;\r
414     }\r
415 \r
416     //\r
417     // Wrap the packet in a net buffer then deliver it up\r
418     //\r
419     NewPacket = NetbufFromBufList (\r
420                   &Assemble->Fragments,\r
421                   0,\r
422                   0,\r
423                   Ip4OnFreeFragments,\r
424                   Assemble\r
425                   );\r
426 \r
427     if (NewPacket == NULL) {\r
428       Ip4FreeAssembleEntry (Assemble);\r
429       return NULL;\r
430     }\r
431 \r
432     NewPacket->Ip                  = Assemble->Head;\r
433     CopyMem (IP4_GET_CLIP_INFO (NewPacket), Assemble->Info, sizeof (*IP4_GET_CLIP_INFO (NewPacket)));\r
434     return NewPacket;\r
435   }\r
436 \r
437   return NULL;\r
438 \r
439 DROP:\r
440   NetbufFree (Packet);\r
441   return NULL;\r
442 }\r
443 \r
444 \r
445 /**\r
446   The IP4 input routine. It is called by the IP4_INTERFACE when a\r
447   IP4 fragment is received from MNP.\r
448 \r
449   @param  Ip4Instance            The IP4 child that request the receive, most like\r
450                                  it is NULL.\r
451   @param  Packet                 The IP4 packet received.\r
452   @param  IoStatus               The return status of receive request.\r
453   @param  Flag                   The link layer flag for the packet received, such\r
454                                  as multicast.\r
455   @param  Context                The IP4 service instance that own the MNP.\r
456 \r
457   @return None\r
458 \r
459 **/\r
460 VOID\r
461 Ip4AccpetFrame (\r
462   IN IP4_PROTOCOL           *Ip4Instance,\r
463   IN NET_BUF                *Packet,\r
464   IN EFI_STATUS             IoStatus,\r
465   IN UINT32                 Flag,\r
466   IN VOID                   *Context\r
467   )\r
468 {\r
469   IP4_SERVICE               *IpSb;\r
470   IP4_CLIP_INFO             *Info;\r
471   IP4_HEAD                  *Head;\r
472   UINT32                    HeadLen;\r
473   UINT32                    OptionLen;\r
474   UINT32                    TotalLen;\r
475   UINT16                    Checksum;\r
476 \r
477   IpSb = (IP4_SERVICE *) Context;\r
478 \r
479   if (EFI_ERROR (IoStatus) || (IpSb->State == IP4_SERVICE_DESTORY)) {\r
480     goto DROP;\r
481   }\r
482 \r
483   //\r
484   // Check that the IP4 header is correctly formated\r
485   //\r
486   if (Packet->TotalSize < IP4_MIN_HEADLEN) {\r
487     goto RESTART;\r
488   }\r
489 \r
490   Head     = (IP4_HEAD *) NetbufGetByte (Packet, 0, NULL);\r
491   HeadLen  = (Head->HeadLen << 2);\r
492   TotalLen = NTOHS (Head->TotalLen);\r
493 \r
494   //\r
495   // Mnp may deliver frame trailer sequence up, trim it off.\r
496   //\r
497   if (TotalLen < Packet->TotalSize) {\r
498     NetbufTrim (Packet, Packet->TotalSize - TotalLen, FALSE);\r
499   }\r
500 \r
501   if ((Head->Ver != 4) || (HeadLen < IP4_MIN_HEADLEN) ||\r
502       (TotalLen < HeadLen) || (TotalLen != Packet->TotalSize)) {\r
503     goto RESTART;\r
504   }\r
505 \r
506   //\r
507   // Some OS may send IP packets without checksum.\r
508   //\r
509   Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Head, HeadLen));\r
510 \r
511   if ((Head->Checksum != 0) && (Checksum != 0)) {\r
512     goto RESTART;\r
513   }\r
514 \r
515   //\r
516   // Convert the IP header to host byte order, then get the per packet info.\r
517   //\r
518   Packet->Ip      = Ip4NtohHead (Head);\r
519 \r
520   Info            = IP4_GET_CLIP_INFO (Packet);\r
521   Info->LinkFlag  = Flag;\r
522   Info->CastType  = Ip4GetHostCast (IpSb, Head->Dst, Head->Src);\r
523   Info->Start     = (Head->Fragment & IP4_HEAD_OFFSET_MASK) << 3;\r
524   Info->Length    = Head->TotalLen - HeadLen;\r
525   Info->End       = Info->Start + Info->Length;\r
526   Info->Status    = EFI_SUCCESS;\r
527 \r
528   //\r
529   // The packet is destinated to us if the CastType is non-zero.\r
530   //\r
531   if ((Info->CastType == 0) || (Info->End > IP4_MAX_PACKET_SIZE)) {\r
532     goto RESTART;\r
533   }\r
534 \r
535   //\r
536   // Validate the options. Don't call the Ip4OptionIsValid if\r
537   // there is no option to save some CPU process.\r
538   //\r
539   OptionLen = HeadLen - IP4_MIN_HEADLEN;\r
540 \r
541   if ((OptionLen > 0) && !Ip4OptionIsValid ((UINT8 *) (Head + 1), OptionLen, TRUE)) {\r
542     goto RESTART;\r
543   }\r
544 \r
545   //\r
546   // Trim the head off, after this point, the packet is headless.\r
547   // and Packet->TotalLen == Info->Length.\r
548   //\r
549   NetbufTrim (Packet, HeadLen, TRUE);\r
550 \r
551   //\r
552   // Reassemble the packet if this is a fragment. The packet is a\r
553   // fragment if its head has MF (more fragment) set, or it starts\r
554   // at non-zero byte.\r
555   //\r
556   if ((Head->Fragment & IP4_HEAD_MF_MASK) || (Info->Start != 0)) {\r
557     //\r
558     // Drop the fragment if DF is set but it is fragmented. Gateway\r
559     // need to send a type 4 destination unreache ICMP message here.\r
560     //\r
561     if (Head->Fragment & IP4_HEAD_DF_MASK) {\r
562       goto RESTART;\r
563     }\r
564 \r
565     //\r
566     // The length of all but the last fragments is in the unit of 8 bytes.\r
567     //\r
568     if ((Head->Fragment & IP4_HEAD_MF_MASK) && (Info->Length % 8 != 0)) {\r
569       goto RESTART;\r
570     }\r
571 \r
572     Packet = Ip4Reassemble (&IpSb->Assemble, Packet);\r
573 \r
574     //\r
575     // Packet assembly isn't complete, start receive more packet.\r
576     //\r
577     if (Packet == NULL) {\r
578       goto RESTART;\r
579     }\r
580   }\r
581 \r
582   //\r
583   // Packet may have been changed. Head, HeadLen, TotalLen, and\r
584   // info must be reloaded bofore use. The ownership of the packet\r
585   // is transfered to the packet process logic.\r
586   //\r
587   Head  = Packet->Ip;\r
588   IP4_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS;\r
589 \r
590   switch (Head->Protocol) {\r
591   case IP4_PROTO_ICMP:\r
592     Ip4IcmpHandle (IpSb, Head, Packet);\r
593     break;\r
594 \r
595   case IP4_PROTO_IGMP:\r
596     Ip4IgmpHandle (IpSb, Head, Packet);\r
597     break;\r
598 \r
599   default:\r
600     Ip4Demultiplex (IpSb, Head, Packet);\r
601   }\r
602 \r
603   Packet = NULL;\r
604 \r
605   //\r
606   // Dispatch the DPCs queued by the NotifyFunction of the rx token's events\r
607   // which are signaled with received data.\r
608   //\r
609   NetLibDispatchDpc ();\r
610 \r
611 RESTART:\r
612   Ip4ReceiveFrame (IpSb->DefaultInterface, NULL, Ip4AccpetFrame, IpSb);\r
613 \r
614 DROP:\r
615   if (Packet != NULL) {\r
616     NetbufFree (Packet);\r
617   }\r
618 \r
619   return ;\r
620 }\r
621 \r
622 \r
623 /**\r
624   Check whether this IP child accepts the packet.\r
625 \r
626   @param  IpInstance             The IP child to check\r
627   @param  Head                   The IP header of the packet\r
628   @param  Packet                 The data of the packet\r
629 \r
630   @return TRUE if the child wants to receive the packet, otherwise return FALSE.\r
631 \r
632 **/\r
633 BOOLEAN\r
634 Ip4InstanceFrameAcceptable (\r
635   IN IP4_PROTOCOL           *IpInstance,\r
636   IN IP4_HEAD               *Head,\r
637   IN NET_BUF                *Packet\r
638   )\r
639 {\r
640   IP4_ICMP_ERROR_HEAD       Icmp;\r
641   EFI_IP4_CONFIG_DATA       *Config;\r
642   IP4_CLIP_INFO             *Info;\r
643   UINT16                    Proto;\r
644   UINT32                    Index;\r
645 \r
646   Config = &IpInstance->ConfigData;\r
647 \r
648   //\r
649   // Dirty trick for the Tiano UEFI network stack implmentation. If\r
650   // ReceiveTimeout == -1, the receive of the packet for this instance\r
651   // is disabled. The UEFI spec don't have such captibility. We add\r
652   // this to improve the performance because IP will make a copy of\r
653   // the received packet for each accepting instance. Some IP instances\r
654   // used by UDP/TCP only send packets, they don't wants to receive.\r
655   //\r
656   if (Config->ReceiveTimeout == (UINT32)(-1)) {\r
657     return FALSE;\r
658   }\r
659 \r
660   if (Config->AcceptPromiscuous) {\r
661     return TRUE;\r
662   }\r
663 \r
664   //\r
665   // Use protocol from the IP header embedded in the ICMP error\r
666   // message to filter, instead of ICMP itself. ICMP handle will\r
667   // can Ip4Demultiplex to deliver ICMP errors.\r
668   //\r
669   Proto = Head->Protocol;\r
670 \r
671   if (Proto == IP4_PROTO_ICMP) {\r
672     NetbufCopy (Packet, 0, sizeof (Icmp.Head), (UINT8 *) &Icmp.Head);\r
673 \r
674     if (mIcmpClass[Icmp.Head.Type].IcmpClass == ICMP_ERROR_MESSAGE) {\r
675       if (!Config->AcceptIcmpErrors) {\r
676         return FALSE;\r
677       }\r
678 \r
679       NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);\r
680       Proto = Icmp.IpHead.Protocol;\r
681     }\r
682   }\r
683 \r
684   //\r
685   // Match the protocol\r
686   //\r
687   if (!Config->AcceptAnyProtocol && (Proto != Config->DefaultProtocol)) {\r
688     return FALSE;\r
689   }\r
690 \r
691   //\r
692   // Check for broadcast, the caller has computed the packet's\r
693   // cast type for this child's interface.\r
694   //\r
695   Info = IP4_GET_CLIP_INFO (Packet);\r
696 \r
697   if (IP4_IS_BROADCAST (Info->CastType)) {\r
698     return Config->AcceptBroadcast;\r
699   }\r
700 \r
701   //\r
702   // If it is a multicast packet, check whether we are in the group.\r
703   //\r
704   if (Info->CastType == IP4_MULTICAST) {\r
705     //\r
706     // Receive the multicast if the instance wants to receive all packets.\r
707     //\r
708     if (!IpInstance->ConfigData.UseDefaultAddress && (IpInstance->Interface->Ip == 0)) {\r
709       return TRUE;\r
710     }\r
711 \r
712     for (Index = 0; Index < IpInstance->GroupCount; Index++) {\r
713       if (IpInstance->Groups[Index] == HTONL (Head->Dst)) {\r
714         break;\r
715       }\r
716     }\r
717 \r
718     return (BOOLEAN)(Index < IpInstance->GroupCount);\r
719   }\r
720 \r
721   return TRUE;\r
722 }\r
723 \r
724 \r
725 /**\r
726   Enqueue a shared copy of the packet to the IP4 child if the\r
727   packet is acceptable to it. Here the data of the packet is\r
728   shared, but the net buffer isn't.\r
729 \r
730   @param  IpInstance             The IP4 child to enqueue the packet to\r
731   @param  Head                   The IP header of the received packet\r
732   @param  Packet                 The data of the received packet\r
733 \r
734   @retval EFI_NOT_STARTED        The IP child hasn't been configured.\r
735   @retval EFI_INVALID_PARAMETER  The child doesn't want to receive the packet\r
736   @retval EFI_OUT_OF_RESOURCES   Failed to allocate some resource\r
737   @retval EFI_SUCCESS            A shared copy the packet is enqueued to the child.\r
738 \r
739 **/\r
740 EFI_STATUS\r
741 Ip4InstanceEnquePacket (\r
742   IN IP4_PROTOCOL           *IpInstance,\r
743   IN IP4_HEAD               *Head,\r
744   IN NET_BUF                *Packet\r
745   )\r
746 {\r
747   IP4_CLIP_INFO             *Info;\r
748   NET_BUF                   *Clone;\r
749 \r
750   //\r
751   // Check whether the packet is acceptable to this instance.\r
752   //\r
753   if (IpInstance->State != IP4_STATE_CONFIGED) {\r
754     return EFI_NOT_STARTED;\r
755   }\r
756 \r
757   if (!Ip4InstanceFrameAcceptable (IpInstance, Head, Packet)) {\r
758     return EFI_INVALID_PARAMETER;\r
759   }\r
760 \r
761   //\r
762   // Enque a shared copy of the packet.\r
763   //\r
764   Clone = NetbufClone (Packet);\r
765 \r
766   if (Clone == NULL) {\r
767     return EFI_OUT_OF_RESOURCES;\r
768   }\r
769 \r
770   //\r
771   // Set the receive time out for the assembled packet. If it expires,\r
772   // packet will be removed from the queue.\r
773   //\r
774   Info        = IP4_GET_CLIP_INFO (Clone);\r
775   Info->Life  = IP4_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout);\r
776 \r
777   InsertTailList (&IpInstance->Received, &Clone->List);\r
778   return EFI_SUCCESS;\r
779 }\r
780 \r
781 \r
782 /**\r
783   The signal handle of IP4's recycle event. It is called back\r
784   when the upper layer release the packet.\r
785 \r
786   @param  Event                  The IP4's recycle event.\r
787   @param  Context                The context of the handle, which is a\r
788                                  IP4_RXDATA_WRAP\r
789 \r
790   @return None\r
791 \r
792 **/\r
793 VOID\r
794 EFIAPI\r
795 Ip4OnRecyclePacket (\r
796   IN EFI_EVENT              Event,\r
797   IN VOID                   *Context\r
798   )\r
799 {\r
800   IP4_RXDATA_WRAP           *Wrap;\r
801 \r
802   Wrap = (IP4_RXDATA_WRAP *) Context;\r
803 \r
804   EfiAcquireLockOrFail (&Wrap->IpInstance->RecycleLock);\r
805   RemoveEntryList (&Wrap->Link);\r
806   EfiReleaseLock (&Wrap->IpInstance->RecycleLock);\r
807 \r
808   ASSERT (!NET_BUF_SHARED (Wrap->Packet));\r
809   NetbufFree (Wrap->Packet);\r
810 \r
811   gBS->CloseEvent (Wrap->RxData.RecycleSignal);\r
812   gBS->FreePool (Wrap);\r
813 }\r
814 \r
815 \r
816 /**\r
817   Wrap the received packet to a IP4_RXDATA_WRAP, which will be\r
818   delivered to the upper layer. Each IP4 child that accepts the\r
819   packet will get a not-shared copy of the packet which is wrapped\r
820   in the IP4_RXDATA_WRAP. The IP4_RXDATA_WRAP->RxData is passed\r
821   to the upper layer. Upper layer will signal the recycle event in\r
822   it when it is done with the packet.\r
823 \r
824   @param  IpInstance             The IP4 child to receive the packet\r
825   @param  Packet                 The packet to deliver up.\r
826 \r
827   @return NULL if failed to wrap the packet, otherwise the wrapper.\r
828 \r
829 **/\r
830 IP4_RXDATA_WRAP *\r
831 Ip4WrapRxData (\r
832   IN IP4_PROTOCOL           *IpInstance,\r
833   IN NET_BUF                *Packet\r
834   )\r
835 {\r
836   IP4_RXDATA_WRAP           *Wrap;\r
837   EFI_IP4_RECEIVE_DATA      *RxData;\r
838   EFI_STATUS                Status;\r
839 \r
840   Wrap = AllocatePool (IP4_RXDATA_WRAP_SIZE (Packet->BlockOpNum));\r
841 \r
842   if (Wrap == NULL) {\r
843     return NULL;\r
844   }\r
845 \r
846   InitializeListHead (&Wrap->Link);\r
847 \r
848   Wrap->IpInstance  = IpInstance;\r
849   Wrap->Packet      = Packet;\r
850   RxData            = &Wrap->RxData;\r
851 \r
852   ZeroMem (&RxData->TimeStamp, sizeof (EFI_TIME));\r
853 \r
854   Status = gBS->CreateEvent (\r
855                   EVT_NOTIFY_SIGNAL,\r
856                   TPL_NOTIFY,\r
857                   Ip4OnRecyclePacket,\r
858                   Wrap,\r
859                   &RxData->RecycleSignal\r
860                   );\r
861 \r
862   if (EFI_ERROR (Status)) {\r
863     gBS->FreePool (Wrap);\r
864     return NULL;\r
865   }\r
866 \r
867   ASSERT (Packet->Ip != NULL);\r
868 \r
869   //\r
870   // The application expects a network byte order header.\r
871   //\r
872   RxData->HeaderLength  = (Packet->Ip->HeadLen << 2);\r
873   RxData->Header        = (EFI_IP4_HEADER *) Ip4NtohHead (Packet->Ip);\r
874 \r
875   RxData->OptionsLength = RxData->HeaderLength - IP4_MIN_HEADLEN;\r
876   RxData->Options       = NULL;\r
877 \r
878   if (RxData->OptionsLength != 0) {\r
879     RxData->Options = (VOID *) (RxData->Header + 1);\r
880   }\r
881 \r
882   RxData->DataLength  = Packet->TotalSize;\r
883 \r
884   //\r
885   // Build the fragment table to be delivered up.\r
886   //\r
887   RxData->FragmentCount = Packet->BlockOpNum;\r
888   NetbufBuildExt (Packet, (NET_FRAGMENT *) RxData->FragmentTable, &RxData->FragmentCount);\r
889 \r
890   return Wrap;\r
891 }\r
892 \r
893 \r
894 /**\r
895   Deliver the received packets to upper layer if there are both received\r
896   requests and enqueued packets. If the enqueued packet is shared, it will\r
897   duplicate it to a non-shared packet, release the shared packet, then\r
898   deliver the non-shared packet up.\r
899 \r
900   @param  IpInstance             The IP child to deliver the packet up.\r
901 \r
902   @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources to deliver the\r
903                                  packets.\r
904   @retval EFI_SUCCESS            All the enqueued packets that can be delivered\r
905                                  are delivered up.\r
906 \r
907 **/\r
908 EFI_STATUS\r
909 Ip4InstanceDeliverPacket (\r
910   IN IP4_PROTOCOL           *IpInstance\r
911   )\r
912 {\r
913   EFI_IP4_COMPLETION_TOKEN  *Token;\r
914   IP4_RXDATA_WRAP           *Wrap;\r
915   NET_BUF                   *Packet;\r
916   NET_BUF                   *Dup;\r
917   UINT8                     *Head;\r
918 \r
919   //\r
920   // Deliver a packet if there are both a packet and a receive token.\r
921   //\r
922   while (!IsListEmpty (&IpInstance->Received) &&\r
923          !NetMapIsEmpty (&IpInstance->RxTokens)) {\r
924 \r
925     Packet = NET_LIST_HEAD (&IpInstance->Received, NET_BUF, List);\r
926 \r
927     if (!NET_BUF_SHARED (Packet)) {\r
928       //\r
929       // If this is the only instance that wants the packet, wrap it up.\r
930       //\r
931       Wrap = Ip4WrapRxData (IpInstance, Packet);\r
932 \r
933       if (Wrap == NULL) {\r
934         return EFI_OUT_OF_RESOURCES;\r
935       }\r
936 \r
937       RemoveEntryList (&Packet->List);\r
938 \r
939     } else {\r
940       //\r
941       // Create a duplicated packet if this packet is shared\r
942       //\r
943       Dup = NetbufDuplicate (Packet, NULL, IP4_MAX_HEADLEN);\r
944 \r
945       if (Dup == NULL) {\r
946         return EFI_OUT_OF_RESOURCES;\r
947       }\r
948 \r
949       //\r
950       // Copy the IP head over. The packet to deliver up is\r
951       // headless. Trim the head off after copy. The IP head\r
952       // may be not continuous before the data.\r
953       //\r
954       Head    = NetbufAllocSpace (Dup, IP4_MAX_HEADLEN, NET_BUF_HEAD);\r
955       Dup->Ip = (IP4_HEAD *) Head;\r
956 \r
957       CopyMem (Head, Packet->Ip, Packet->Ip->HeadLen << 2);\r
958       NetbufTrim (Dup, IP4_MAX_HEADLEN, TRUE);\r
959 \r
960       Wrap = Ip4WrapRxData (IpInstance, Dup);\r
961 \r
962       if (Wrap == NULL) {\r
963         NetbufFree (Dup);\r
964         return EFI_OUT_OF_RESOURCES;\r
965       }\r
966 \r
967       RemoveEntryList (&Packet->List);\r
968       NetbufFree (Packet);\r
969 \r
970       Packet = Dup;\r
971     }\r
972 \r
973     //\r
974     // Insert it into the delivered packet, then get a user's\r
975     // receive token, pass the wrapped packet up.\r
976     //\r
977     EfiAcquireLockOrFail (&IpInstance->RecycleLock);\r
978     InsertHeadList (&IpInstance->Delivered, &Wrap->Link);\r
979     EfiReleaseLock (&IpInstance->RecycleLock);\r
980 \r
981     Token                = NetMapRemoveHead (&IpInstance->RxTokens, NULL);\r
982     Token->Status        = IP4_GET_CLIP_INFO (Packet)->Status;\r
983     Token->Packet.RxData = &Wrap->RxData;\r
984 \r
985     gBS->SignalEvent (Token->Event);\r
986   }\r
987 \r
988   return EFI_SUCCESS;\r
989 }\r
990 \r
991 \r
992 /**\r
993   Enqueue a received packet to all the IP children that share\r
994   the same interface.\r
995 \r
996   @param  IpSb                   The IP4 service instance that receive the packet\r
997   @param  Head                   The header of the received packet\r
998   @param  Packet                 The data of the received packet\r
999   @param  IpIf                   The interface to enqueue the packet to\r
1000 \r
1001   @return The number of the IP4 children that accepts the packet\r
1002 \r
1003 **/\r
1004 INTN\r
1005 Ip4InterfaceEnquePacket (\r
1006   IN IP4_SERVICE            *IpSb,\r
1007   IN IP4_HEAD               *Head,\r
1008   IN NET_BUF                *Packet,\r
1009   IN IP4_INTERFACE          *IpIf\r
1010   )\r
1011 {\r
1012   IP4_PROTOCOL              *IpInstance;\r
1013   IP4_CLIP_INFO             *Info;\r
1014   LIST_ENTRY                *Entry;\r
1015   INTN                      Enqueued;\r
1016   INTN                      LocalType;\r
1017   INTN                      SavedType;\r
1018 \r
1019   //\r
1020   // First, check that the packet is acceptable to this interface\r
1021   // and find the local cast type for the interface. A packet sent\r
1022   // to say 192.168.1.1 should NOT be delliever to 10.0.0.1 unless\r
1023   // promiscuous receiving.\r
1024   //\r
1025   LocalType = 0;\r
1026   Info      = IP4_GET_CLIP_INFO (Packet);\r
1027 \r
1028   if ((Info->CastType == IP4_MULTICAST) || (Info->CastType == IP4_LOCAL_BROADCAST)) {\r
1029     //\r
1030     // If the CastType is multicast, don't need to filter against\r
1031     // the group address here, Ip4InstanceFrameAcceptable will do\r
1032     // that later.\r
1033     //\r
1034     LocalType = Info->CastType;\r
1035 \r
1036   } else {\r
1037     //\r
1038     // Check the destination againist local IP. If the station\r
1039     // address is 0.0.0.0, it means receiving all the IP destined\r
1040     // to local non-zero IP. Otherwise, it is necessary to compare\r
1041     // the destination to the interface's IP address.\r
1042     //\r
1043     if (IpIf->Ip == IP4_ALLZERO_ADDRESS) {\r
1044       LocalType = IP4_LOCAL_HOST;\r
1045 \r
1046     } else {\r
1047       LocalType = Ip4GetNetCast (Head->Dst, IpIf);\r
1048 \r
1049       if ((LocalType == 0) && IpIf->PromiscRecv) {\r
1050         LocalType = IP4_PROMISCUOUS;\r
1051       }\r
1052     }\r
1053   }\r
1054 \r
1055   if (LocalType == 0) {\r
1056     return 0;\r
1057   }\r
1058 \r
1059   //\r
1060   // Iterate through the ip instances on the interface, enqueue\r
1061   // the packet if filter passed. Save the original cast type,\r
1062   // and pass the local cast type to the IP children on the\r
1063   // interface. The global cast type will be restored later.\r
1064   //\r
1065   SavedType       = Info->CastType;\r
1066   Info->CastType  = LocalType;\r
1067 \r
1068   Enqueued        = 0;\r
1069 \r
1070   NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {\r
1071     IpInstance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);\r
1072     NET_CHECK_SIGNATURE (IpInstance, IP4_PROTOCOL_SIGNATURE);\r
1073 \r
1074     if (Ip4InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) {\r
1075       Enqueued++;\r
1076     }\r
1077   }\r
1078 \r
1079   Info->CastType = SavedType;\r
1080   return Enqueued;\r
1081 }\r
1082 \r
1083 \r
1084 /**\r
1085   Deliver the packet for each IP4 child on the interface.\r
1086 \r
1087   @param  IpSb                   The IP4 service instance that received the packet\r
1088   @param  IpIf                   The IP4 interface to deliver the packet.\r
1089 \r
1090   @retval EFI_SUCCESS            It always returns EFI_SUCCESS now\r
1091 \r
1092 **/\r
1093 EFI_STATUS\r
1094 Ip4InterfaceDeliverPacket (\r
1095   IN IP4_SERVICE            *IpSb,\r
1096   IN IP4_INTERFACE          *IpIf\r
1097   )\r
1098 {\r
1099   IP4_PROTOCOL              *Ip4Instance;\r
1100   LIST_ENTRY                *Entry;\r
1101 \r
1102   NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {\r
1103     Ip4Instance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);\r
1104     Ip4InstanceDeliverPacket (Ip4Instance);\r
1105   }\r
1106 \r
1107   return EFI_SUCCESS;\r
1108 }\r
1109 \r
1110 \r
1111 /**\r
1112   Demultiple the packet. the packet delivery is processed in two\r
1113   passes. The first pass will enque a shared copy of the packet\r
1114   to each IP4 child that accepts the packet. The second pass will\r
1115   deliver a non-shared copy of the packet to each IP4 child that\r
1116   has pending receive requests. Data is copied if more than one\r
1117   child wants to consume the packet bacause each IP child need\r
1118   its own copy of the packet to make changes.\r
1119 \r
1120   @param  IpSb                   The IP4 service instance that received the packet\r
1121   @param  Head                   The header of the received packet\r
1122   @param  Packet                 The data of the received packet\r
1123 \r
1124   @retval EFI_NOT_FOUND          No IP child accepts the packet\r
1125   @retval EFI_SUCCESS            The packet is enqueued or delivered to some IP\r
1126                                  children.\r
1127 \r
1128 **/\r
1129 EFI_STATUS\r
1130 Ip4Demultiplex (\r
1131   IN IP4_SERVICE            *IpSb,\r
1132   IN IP4_HEAD               *Head,\r
1133   IN NET_BUF                *Packet\r
1134   )\r
1135 {\r
1136   LIST_ENTRY                *Entry;\r
1137   IP4_INTERFACE             *IpIf;\r
1138   INTN                      Enqueued;\r
1139 \r
1140   //\r
1141   // Two pass delivery: first, enque a shared copy of the packet\r
1142   // to each instance that accept the packet.\r
1143   //\r
1144   Enqueued = 0;\r
1145 \r
1146   NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
1147     IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);\r
1148 \r
1149     if (IpIf->Configured) {\r
1150       Enqueued += Ip4InterfaceEnquePacket (IpSb, Head, Packet, IpIf);\r
1151     }\r
1152   }\r
1153 \r
1154   //\r
1155   // Second: deliver a duplicate of the packet to each instance.\r
1156   // Release the local reference first, so that the last instance\r
1157   // getting the packet will not copy the data.\r
1158   //\r
1159   NetbufFree (Packet);\r
1160 \r
1161   if (Enqueued == 0) {\r
1162     return EFI_NOT_FOUND;\r
1163   }\r
1164 \r
1165   NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
1166     IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);\r
1167 \r
1168     if (IpIf->Configured) {\r
1169       Ip4InterfaceDeliverPacket (IpSb, IpIf);\r
1170     }\r
1171   }\r
1172 \r
1173   return EFI_SUCCESS;\r
1174 }\r
1175 \r
1176 \r
1177 /**\r
1178   Timeout the fragment and enqueued packets.\r
1179 \r
1180   @param  IpSb                   The IP4 service instance to timeout\r
1181 \r
1182   @return None\r
1183 \r
1184 **/\r
1185 VOID\r
1186 Ip4PacketTimerTicking (\r
1187   IN IP4_SERVICE            *IpSb\r
1188   )\r
1189 {\r
1190   LIST_ENTRY                *InstanceEntry;\r
1191   LIST_ENTRY                *Entry;\r
1192   LIST_ENTRY                *Next;\r
1193   IP4_PROTOCOL              *IpInstance;\r
1194   IP4_ASSEMBLE_ENTRY        *Assemble;\r
1195   NET_BUF                   *Packet;\r
1196   IP4_CLIP_INFO             *Info;\r
1197   UINT32                    Index;\r
1198 \r
1199   //\r
1200   // First, time out the fragments. The packet's life is counting down\r
1201   // once the first-arrived fragment was received.\r
1202   //\r
1203   for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) {\r
1204     NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->Assemble.Bucket[Index]) {\r
1205       Assemble = NET_LIST_USER_STRUCT (Entry, IP4_ASSEMBLE_ENTRY, Link);\r
1206 \r
1207       if ((Assemble->Life > 0) && (--Assemble->Life == 0)) {\r
1208         RemoveEntryList (Entry);\r
1209         Ip4FreeAssembleEntry (Assemble);\r
1210       }\r
1211     }\r
1212   }\r
1213 \r
1214   NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) {\r
1215     IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP4_PROTOCOL, Link);\r
1216 \r
1217     //\r
1218     // Second, time out the assembled packets enqueued on each IP child.\r
1219     //\r
1220     NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpInstance->Received) {\r
1221       Packet = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
1222       Info   = IP4_GET_CLIP_INFO (Packet);\r
1223 \r
1224       if ((Info->Life > 0) && (--Info->Life == 0)) {\r
1225         RemoveEntryList (Entry);\r
1226         NetbufFree (Packet);\r
1227       }\r
1228     }\r
1229 \r
1230     //\r
1231     // Third: time out the transmitted packets.\r
1232     //\r
1233     NetMapIterate (&IpInstance->TxTokens, Ip4SentPacketTicking, NULL);\r
1234   }\r
1235 }\r