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