]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.c
add assertion to pass K8 check.
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Ip4Dxe / Ip4Igmp.c
CommitLineData
772db4bb 1/** @file\r
3e8c18da 2 This file implements the RFC2236: IGMP v2.\r
3 \r
4Copyright (c) 2005 - 2006, Intel Corporation.<BR>\r
772db4bb 5All rights reserved. This program and the accompanying materials\r
6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php\r
9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
772db4bb 13**/\r
14\r
15#include "Ip4Impl.h"\r
16\r
17//\r
18// Route Alert option in IGMP report to direct routers to\r
19// examine the packet more closely.\r
20//\r
21UINT32 mRouteAlertOption = 0x00000494;\r
22\r
23\r
24/**\r
25 Init the IGMP control data of the IP4 service instance, configure\r
26 MNP to receive ALL SYSTEM multicast.\r
27\r
3e8c18da 28 @param[in, out] IpSb The IP4 service whose IGMP is to be initialized.\r
772db4bb 29\r
5405e9a6 30 @retval EFI_SUCCESS IGMP of the IpSb is successfully initialized.\r
772db4bb 31 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to initialize IGMP.\r
32 @retval Others Failed to initialize the IGMP of IpSb.\r
772db4bb 33\r
34**/\r
35EFI_STATUS\r
36Ip4InitIgmp (\r
5405e9a6 37 IN OUT IP4_SERVICE *IpSb\r
772db4bb 38 )\r
39{\r
40 IGMP_SERVICE_DATA *IgmpCtrl;\r
41 EFI_MANAGED_NETWORK_PROTOCOL *Mnp;\r
42 IGMP_GROUP *Group;\r
43 EFI_STATUS Status;\r
44\r
45 IgmpCtrl = &IpSb->IgmpCtrl;\r
46\r
47 //\r
48 // Configure MNP to receive ALL_SYSTEM multicast\r
49 //\r
e48e37fc 50 Group = AllocatePool (sizeof (IGMP_GROUP));\r
772db4bb 51\r
52 if (Group == NULL) {\r
53 return EFI_OUT_OF_RESOURCES;\r
54 }\r
55\r
56 Mnp = IpSb->Mnp;\r
57\r
58 Group->Address = IP4_ALLSYSTEM_ADDRESS;\r
59 Group->RefCnt = 1;\r
60 Group->DelayTime = 0;\r
61 Group->ReportByUs = FALSE;\r
62\r
63 Status = Ip4GetMulticastMac (Mnp, IP4_ALLSYSTEM_ADDRESS, &Group->Mac);\r
64\r
65 if (EFI_ERROR (Status)) {\r
66 goto ON_ERROR;\r
67 }\r
68\r
69 Status = Mnp->Groups (Mnp, TRUE, &Group->Mac);\r
70\r
71 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
72 goto ON_ERROR;\r
73 }\r
74\r
e48e37fc 75 InsertHeadList (&IgmpCtrl->Groups, &Group->Link);\r
772db4bb 76 return EFI_SUCCESS;\r
77\r
78ON_ERROR:\r
e48e37fc 79 gBS->FreePool (Group);\r
772db4bb 80 return Status;\r
81}\r
82\r
83\r
84/**\r
85 Find the IGMP_GROUP structure which contains the status of multicast\r
86 group Address in this IGMP control block\r
87\r
3e8c18da 88 @param[in] IgmpCtrl The IGMP control block to search from\r
89 @param[in] Address The multicast address to search\r
772db4bb 90\r
91 @return NULL if the multicast address isn't in the IGMP control block. Otherwise\r
5405e9a6 92 the point to the IGMP_GROUP which contains the status of multicast group\r
93 for Address.\r
772db4bb 94\r
95**/\r
96IGMP_GROUP *\r
97Ip4FindGroup (\r
98 IN IGMP_SERVICE_DATA *IgmpCtrl,\r
99 IN IP4_ADDR Address\r
100 )\r
101{\r
e48e37fc 102 LIST_ENTRY *Entry;\r
772db4bb 103 IGMP_GROUP *Group;\r
104\r
105 NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {\r
106 Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);\r
107\r
108 if (Group->Address == Address) {\r
109 return Group;\r
110 }\r
111 }\r
112\r
113 return NULL;\r
114}\r
115\r
116\r
117/**\r
118 Count the number of IP4 multicast groups that are mapped to the\r
119 same MAC address. Several IP4 multicast address may be mapped to\r
120 the same MAC address.\r
121\r
3e8c18da 122 @param[in] IgmpCtrl The IGMP control block to search in\r
123 @param[in] Mac The MAC address to search\r
772db4bb 124\r
125 @return The number of the IP4 multicast group that mapped to the same\r
5405e9a6 126 multicast group Mac.\r
772db4bb 127\r
128**/\r
129INTN\r
130Ip4FindMac (\r
131 IN IGMP_SERVICE_DATA *IgmpCtrl,\r
132 IN EFI_MAC_ADDRESS *Mac\r
133 )\r
134{\r
e48e37fc 135 LIST_ENTRY *Entry;\r
772db4bb 136 IGMP_GROUP *Group;\r
137 INTN Count;\r
138\r
139 Count = 0;\r
140\r
141 NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {\r
142 Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);\r
143\r
144 if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) {\r
145 Count++;\r
146 }\r
147 }\r
148\r
149 return Count;\r
150}\r
151\r
152\r
153/**\r
154 Send an IGMP protocol message to the Dst, such as IGMP v1 membership report.\r
155\r
3e8c18da 156 @param[in] IpSb The IP4 service instance that requests the\r
772db4bb 157 transmission\r
3e8c18da 158 @param[in] Dst The destinaton to send to\r
159 @param[in] Type The IGMP message type, such as IGMP v1 membership\r
772db4bb 160 report\r
3e8c18da 161 @param[in] Group The group address in the IGMP message head.\r
772db4bb 162\r
163 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message\r
164 @retval EFI_SUCCESS The IGMP message is successfully send\r
165 @retval Others Failed to send the IGMP message.\r
166\r
167**/\r
168EFI_STATUS\r
169Ip4SendIgmpMessage (\r
170 IN IP4_SERVICE *IpSb,\r
171 IN IP4_ADDR Dst,\r
172 IN UINT8 Type,\r
173 IN IP4_ADDR Group\r
174 )\r
175{\r
176 IP4_HEAD Head;\r
177 NET_BUF *Packet;\r
178 IGMP_HEAD *Igmp;\r
179\r
180 //\r
181 // Allocate a net buffer to hold the message\r
182 //\r
183 Packet = NetbufAlloc (IP4_MAX_HEADLEN + sizeof (IGMP_HEAD));\r
184\r
185 if (Packet == NULL) {\r
186 return EFI_OUT_OF_RESOURCES;\r
187 }\r
188\r
189 //\r
190 // Fill in the IGMP and IP header, then transmit the message\r
191 //\r
192 NetbufReserve (Packet, IP4_MAX_HEADLEN);\r
193\r
194 Igmp = (IGMP_HEAD *) NetbufAllocSpace (Packet, sizeof (IGMP_HEAD), FALSE);\r
195\r
196 Igmp->Type = Type;\r
197 Igmp->MaxRespTime = 0;\r
198 Igmp->Checksum = 0;\r
199 Igmp->Group = HTONL (Group);\r
687a2e5f 200 Igmp->Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Igmp, sizeof (IGMP_HEAD)));\r
772db4bb 201\r
202 Head.Tos = 0;\r
203 Head.Protocol = IP4_PROTO_IGMP;\r
204 Head.Ttl = 1;\r
205 Head.Fragment = 0;\r
206 Head.Dst = Dst;\r
207 Head.Src = IP4_ALLZERO_ADDRESS;\r
208\r
209 return Ip4Output (\r
210 IpSb,\r
211 NULL,\r
212 Packet,\r
213 &Head,\r
214 (UINT8 *) &mRouteAlertOption,\r
215 sizeof (UINT32),\r
216 IP4_ALLZERO_ADDRESS,\r
217 Ip4SysPacketSent,\r
218 NULL\r
219 );\r
220}\r
221\r
222\r
223/**\r
224 Send an IGMP membership report. Depends on whether the server is\r
225 v1 or v2, it will send either a V1 or V2 membership report.\r
226\r
3e8c18da 227 @param[in] IpSb The IP4 service instance that requests the\r
772db4bb 228 transmission.\r
3e8c18da 229 @param[in] Group The group address to report\r
772db4bb 230\r
231 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message\r
232 @retval EFI_SUCCESS The IGMP report message is successfully send\r
233 @retval Others Failed to send the report.\r
234\r
235**/\r
236EFI_STATUS\r
237Ip4SendIgmpReport (\r
238 IN IP4_SERVICE *IpSb,\r
239 IN IP4_ADDR Group\r
240 )\r
241{\r
242 if (IpSb->IgmpCtrl.Igmpv1QuerySeen != 0) {\r
243 return Ip4SendIgmpMessage (IpSb, Group, IGMP_V1_MEMBERSHIP_REPORT, Group);\r
244 } else {\r
245 return Ip4SendIgmpMessage (IpSb, Group, IGMP_V2_MEMBERSHIP_REPORT, Group);\r
246 }\r
247}\r
248\r
249\r
250/**\r
5405e9a6 251 Join the multicast group on behalf of this IP4 child\r
772db4bb 252\r
3e8c18da 253 @param[in] IpInstance The IP4 child that wants to join the group\r
254 @param[in] Address The group to join\r
772db4bb 255\r
256 @retval EFI_SUCCESS Successfully join the multicast group\r
257 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources\r
258 @retval Others Failed to join the multicast group.\r
259\r
260**/\r
261EFI_STATUS\r
262Ip4JoinGroup (\r
263 IN IP4_PROTOCOL *IpInstance,\r
264 IN IP4_ADDR Address\r
265 )\r
266{\r
267 EFI_MANAGED_NETWORK_PROTOCOL *Mnp;\r
268 IP4_SERVICE *IpSb;\r
269 IGMP_SERVICE_DATA *IgmpCtrl;\r
270 IGMP_GROUP *Group;\r
271 EFI_STATUS Status;\r
272\r
273 IpSb = IpInstance->Service;\r
274 IgmpCtrl = &IpSb->IgmpCtrl;\r
275 Mnp = IpSb->Mnp;\r
276\r
277 //\r
278 // If the IP service already is a member in the group, just\r
279 // increase the refernce count and return.\r
280 //\r
281 Group = Ip4FindGroup (IgmpCtrl, Address);\r
282\r
283 if (Group != NULL) {\r
284 Group->RefCnt++;\r
285 return EFI_SUCCESS;\r
286 }\r
287\r
288 //\r
289 // Otherwise, create a new IGMP_GROUP, Get the multicast's MAC address,\r
290 // send a report, then direct MNP to receive the multicast.\r
291 //\r
e48e37fc 292 Group = AllocatePool (sizeof (IGMP_GROUP));\r
772db4bb 293\r
294 if (Group == NULL) {\r
295 return EFI_OUT_OF_RESOURCES;\r
296 }\r
297\r
298 Group->Address = Address;\r
299 Group->RefCnt = 1;\r
300 Group->DelayTime = IGMP_UNSOLICIATED_REPORT;\r
301 Group->ReportByUs = TRUE;\r
302\r
303 Status = Ip4GetMulticastMac (Mnp, Address, &Group->Mac);\r
304\r
305 if (EFI_ERROR (Status)) {\r
306 goto ON_ERROR;\r
307 }\r
308\r
309 Status = Ip4SendIgmpReport (IpSb, Address);\r
310\r
311 if (EFI_ERROR (Status)) {\r
312 goto ON_ERROR;\r
313 }\r
314\r
315 Status = Mnp->Groups (Mnp, TRUE, &Group->Mac);\r
316\r
317 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
318 goto ON_ERROR;\r
319 }\r
320\r
e48e37fc 321 InsertHeadList (&IgmpCtrl->Groups, &Group->Link);\r
772db4bb 322 return EFI_SUCCESS;\r
323\r
324ON_ERROR:\r
e48e37fc 325 gBS->FreePool (Group);\r
772db4bb 326 return Status;\r
327}\r
328\r
329\r
330/**\r
5405e9a6 331 Leave the IP4 multicast group on behalf of IpInstance.\r
772db4bb 332\r
3e8c18da 333 @param[in] IpInstance The IP4 child that wants to leave the group\r
772db4bb 334 address\r
3e8c18da 335 @param[in] Address The group address to leave\r
772db4bb 336\r
337 @retval EFI_NOT_FOUND The IP4 service instance isn't in the group\r
338 @retval EFI_SUCCESS Successfully leave the multicast group.\r
339 @retval Others Failed to leave the multicast group.\r
340\r
341**/\r
342EFI_STATUS\r
343Ip4LeaveGroup (\r
344 IN IP4_PROTOCOL *IpInstance,\r
345 IN IP4_ADDR Address\r
346 )\r
347{\r
348 EFI_MANAGED_NETWORK_PROTOCOL *Mnp;\r
349 IP4_SERVICE *IpSb;\r
350 IGMP_SERVICE_DATA *IgmpCtrl;\r
351 IGMP_GROUP *Group;\r
352 EFI_STATUS Status;\r
353\r
354 IpSb = IpInstance->Service;\r
355 IgmpCtrl = &IpSb->IgmpCtrl;\r
356 Mnp = IpSb->Mnp;\r
357\r
358 Group = Ip4FindGroup (IgmpCtrl, Address);\r
359\r
360 if (Group == NULL) {\r
361 return EFI_NOT_FOUND;\r
362 }\r
363\r
364 //\r
365 // If more than one instance is in the group, decrease\r
366 // the RefCnt then return.\r
367 //\r
368 if (--Group->RefCnt > 0) {\r
369 return EFI_SUCCESS;\r
370 }\r
371\r
372 //\r
373 // If multiple IP4 group addresses are mapped to the same\r
374 // multicast MAC address, don't configure the MNP to leave\r
375 // the MAC.\r
376 //\r
377 if (Ip4FindMac (IgmpCtrl, &Group->Mac) == 1) {\r
378 Status = Mnp->Groups (Mnp, FALSE, &Group->Mac);\r
379\r
380 if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {\r
381 return Status;\r
382 }\r
383 }\r
384\r
385 //\r
386 // Send a leave report if the membership is reported by us\r
387 // and we are talking IGMPv2.\r
388 //\r
5405e9a6 389 if (Group->ReportByUs && IgmpCtrl->Igmpv1QuerySeen == 0) {\r
772db4bb 390 Ip4SendIgmpMessage (IpSb, IP4_ALLROUTER_ADDRESS, IGMP_LEAVE_GROUP, Group->Address);\r
391 }\r
392\r
e48e37fc 393 RemoveEntryList (&Group->Link);\r
394 gBS->FreePool (Group);\r
772db4bb 395\r
396 return EFI_SUCCESS;\r
397}\r
398\r
399\r
400/**\r
401 Handle the received IGMP message for the IP4 service instance.\r
402\r
3e8c18da 403 @param[in] IpSb The IP4 service instance that received the message\r
404 @param[in] Head The IP4 header of the received message\r
405 @param[in] Packet The IGMP message, without IP4 header\r
772db4bb 406\r
407 @retval EFI_INVALID_PARAMETER The IGMP message is malformated.\r
408 @retval EFI_SUCCESS The IGMP message is successfully processed.\r
409\r
410**/\r
411EFI_STATUS\r
412Ip4IgmpHandle (\r
413 IN IP4_SERVICE *IpSb,\r
414 IN IP4_HEAD *Head,\r
415 IN NET_BUF *Packet\r
416 )\r
417{\r
418 IGMP_SERVICE_DATA *IgmpCtrl;\r
419 IGMP_HEAD Igmp;\r
420 IGMP_GROUP *Group;\r
421 IP4_ADDR Address;\r
e48e37fc 422 LIST_ENTRY *Entry;\r
772db4bb 423\r
424 IgmpCtrl = &IpSb->IgmpCtrl;\r
425\r
426 //\r
427 // Must checksum over the whole packet, later IGMP version\r
428 // may employ message longer than 8 bytes. IP's header has\r
429 // already been trimmed off.\r
430 //\r
431 if ((Packet->TotalSize < sizeof (Igmp)) || (NetbufChecksum (Packet) != 0)) {\r
432 NetbufFree (Packet);\r
433 return EFI_INVALID_PARAMETER;\r
434 }\r
435\r
436 //\r
437 // Copy the packet in case it is fragmented\r
438 //\r
439 NetbufCopy (Packet, 0, sizeof (IGMP_HEAD), (UINT8 *)&Igmp);\r
440\r
441 switch (Igmp.Type) {\r
442 case IGMP_MEMBERSHIP_QUERY:\r
443 //\r
5405e9a6 444 // If MaxRespTime is zero, it is most likely that we are\r
772db4bb 445 // talking to a V1 router\r
446 //\r
447 if (Igmp.MaxRespTime == 0) {\r
448 IgmpCtrl->Igmpv1QuerySeen = IGMP_V1ROUTER_PRESENT;\r
449 Igmp.MaxRespTime = 100;\r
450 }\r
451\r
452 //\r
453 // Igmp is ticking once per second but MaxRespTime is in\r
454 // the unit of 100ms.\r
455 //\r
456 Igmp.MaxRespTime /= 10;\r
457 Address = NTOHL (Igmp.Group);\r
458\r
459 if (Address == IP4_ALLSYSTEM_ADDRESS) {\r
460 break;\r
461 }\r
462\r
463 NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {\r
464 Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);\r
465\r
466 //\r
467 // If address is all zero, all the memberships will be reported.\r
468 // otherwise only one is reported.\r
469 //\r
470 if ((Address == IP4_ALLZERO_ADDRESS) || (Address == Group->Address)) {\r
471 //\r
472 // If the timer is pending, only update it if the time left\r
473 // is longer than the MaxRespTime. TODO: randomize the DelayTime.\r
474 //\r
475 if ((Group->DelayTime == 0) || (Group->DelayTime > Igmp.MaxRespTime)) {\r
36ee91ca 476 Group->DelayTime = MAX (1, Igmp.MaxRespTime);\r
772db4bb 477 }\r
478 }\r
479 }\r
480\r
481 break;\r
482\r
483 case IGMP_V1_MEMBERSHIP_REPORT:\r
484 case IGMP_V2_MEMBERSHIP_REPORT:\r
485 Address = NTOHL (Igmp.Group);\r
486 Group = Ip4FindGroup (IgmpCtrl, Address);\r
487\r
488 if ((Group != NULL) && (Group->DelayTime > 0)) {\r
489 Group->DelayTime = 0;\r
490 Group->ReportByUs = FALSE;\r
491 }\r
492\r
493 break;\r
494 }\r
495\r
496 NetbufFree (Packet);\r
497 return EFI_SUCCESS;\r
498}\r
499\r
500\r
501/**\r
502 The periodical timer function for IGMP. It does the following\r
503 things:\r
504 1. Decrease the Igmpv1QuerySeen to make it possible to refresh\r
5405e9a6 505 the IGMP server type.\r
772db4bb 506 2. Decrease the report timer for each IGMP group in "delaying\r
5405e9a6 507 member" state.\r
772db4bb 508\r
3e8c18da 509 @param[in] IpSb The IP4 service instance that is ticking\r
772db4bb 510\r
511**/\r
512VOID\r
513Ip4IgmpTicking (\r
514 IN IP4_SERVICE *IpSb\r
515 )\r
516{\r
517 IGMP_SERVICE_DATA *IgmpCtrl;\r
e48e37fc 518 LIST_ENTRY *Entry;\r
772db4bb 519 IGMP_GROUP *Group;\r
520\r
521 IgmpCtrl = &IpSb->IgmpCtrl;\r
522\r
523 if (IgmpCtrl->Igmpv1QuerySeen > 0) {\r
524 IgmpCtrl->Igmpv1QuerySeen--;\r
525 }\r
526\r
527 //\r
528 // Decrease the report timer for each IGMP group in "delaying member"\r
529 //\r
530 NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {\r
531 Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);\r
532 ASSERT (Group->DelayTime >= 0);\r
533\r
534 if (Group->DelayTime > 0) {\r
535 Group->DelayTime--;\r
536\r
537 if (Group->DelayTime == 0) {\r
538 Ip4SendIgmpReport (IpSb, Group->Address);\r
539 Group->ReportByUs = TRUE;\r
540 }\r
541 }\r
542 }\r
543}\r
544\r
545\r
546/**\r
547 Add a group address to the array of group addresses.\r
548 The caller should make sure that no duplicated address\r
549 existed in the array. Although the function doesn't\r
550 assume the byte order of the both Source and Addr, the\r
551 network byte order is used by the caller.\r
552\r
3e8c18da 553 @param[in] Source The array of group addresses to add to\r
554 @param[in] Count The number of group addresses in the Source\r
555 @param[in] Addr The IP4 multicast address to add\r
772db4bb 556\r
557 @return NULL if failed to allocate memory for the new groups,\r
5405e9a6 558 otherwise the new combined group addresses.\r
772db4bb 559\r
560**/\r
561IP4_ADDR *\r
562Ip4CombineGroups (\r
563 IN IP4_ADDR *Source,\r
564 IN UINT32 Count,\r
565 IN IP4_ADDR Addr\r
566 )\r
567{\r
568 IP4_ADDR *Groups;\r
569\r
e48e37fc 570 Groups = AllocatePool (sizeof (IP4_ADDR) * (Count + 1));\r
772db4bb 571\r
572 if (Groups == NULL) {\r
573 return NULL;\r
574 }\r
575\r
e48e37fc 576 CopyMem (Groups, Source, Count * sizeof (IP4_ADDR));\r
772db4bb 577 Groups[Count] = Addr;\r
578\r
579 return Groups;\r
580}\r
581\r
582\r
583/**\r
5405e9a6 584 Remove a group address from the array of group addresses.\r
772db4bb 585 Although the function doesn't assume the byte order of the\r
586 both Groups and Addr, the network byte order is used by\r
587 the caller.\r
588\r
3e8c18da 589 @param Groups The array of group addresses to remove from\r
590 @param Count The number of group addresses in the Groups\r
591 @param Addr The IP4 multicast address to remove\r
772db4bb 592\r
593 @return The nubmer of group addresses in the Groups after remove.\r
5405e9a6 594 It is Count if the Addr isn't in the Groups.\r
772db4bb 595\r
596**/\r
597INTN\r
598Ip4RemoveGroupAddr (\r
5405e9a6 599 IN OUT IP4_ADDR *Groups,\r
600 IN UINT32 Count,\r
601 IN IP4_ADDR Addr\r
772db4bb 602 )\r
603{\r
604 UINT32 Index;\r
605\r
606 for (Index = 0; Index < Count; Index++) {\r
607 if (Groups[Index] == Addr) {\r
608 break;\r
609 }\r
610 }\r
611\r
612 while (Index < Count - 1) {\r
613 Groups[Index] = Groups[Index + 1];\r
614 Index++;\r
615 }\r
616\r
617 return Index;\r
618}\r