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