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