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