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