TianoCore EDK2 master
Loading...
Searching...
No Matches
Ip4Igmp.c
Go to the documentation of this file.
1
9#include "Ip4Impl.h"
10
11//
12// Route Alert option in IGMP report to direct routers to
13// examine the packet more closely.
14//
15UINT32 mRouteAlertOption = 0x00000494;
16
30 IN OUT IP4_SERVICE *IpSb
31 )
32{
33 IGMP_SERVICE_DATA *IgmpCtrl;
35 IGMP_GROUP *Group;
36 EFI_STATUS Status;
37
38 IgmpCtrl = &IpSb->IgmpCtrl;
39
40 //
41 // Configure MNP to receive ALL_SYSTEM multicast
42 //
43 Group = AllocatePool (sizeof (IGMP_GROUP));
44
45 if (Group == NULL) {
46 return EFI_OUT_OF_RESOURCES;
47 }
48
49 Mnp = IpSb->Mnp;
50
51 Group->Address = IP4_ALLSYSTEM_ADDRESS;
52 Group->RefCnt = 1;
53 Group->DelayTime = 0;
54 Group->ReportByUs = FALSE;
55
56 Status = Ip4GetMulticastMac (Mnp, IP4_ALLSYSTEM_ADDRESS, &Group->Mac);
57
58 if (EFI_ERROR (Status)) {
59 goto ON_ERROR;
60 }
61
62 Status = Mnp->Groups (Mnp, TRUE, &Group->Mac);
63
64 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
65 goto ON_ERROR;
66 }
67
68 InsertHeadList (&IgmpCtrl->Groups, &Group->Link);
69 return EFI_SUCCESS;
70
71ON_ERROR:
72 FreePool (Group);
73 return Status;
74}
75
90 IN IGMP_SERVICE_DATA *IgmpCtrl,
91 IN IP4_ADDR Address
92 )
93{
94 LIST_ENTRY *Entry;
95 IGMP_GROUP *Group;
96
97 NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {
98 Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);
99
100 if (Group->Address == Address) {
101 return Group;
102 }
103 }
104
105 return NULL;
106}
107
120INTN
122 IN IGMP_SERVICE_DATA *IgmpCtrl,
124 )
125{
126 LIST_ENTRY *Entry;
127 IGMP_GROUP *Group;
128 INTN Count;
129
130 Count = 0;
131
132 NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {
133 Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);
134
135 if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) {
136 Count++;
137 }
138 }
139
140 return Count;
141}
142
160 IN IP4_SERVICE *IpSb,
161 IN IP4_ADDR Dst,
162 IN UINT8 Type,
163 IN IP4_ADDR Group
164 )
165{
166 IP4_HEAD Head;
167 NET_BUF *Packet;
168 IGMP_HEAD *Igmp;
169
170 //
171 // Allocate a net buffer to hold the message
172 //
173 Packet = NetbufAlloc (IP4_MAX_HEADLEN + sizeof (IGMP_HEAD));
174
175 if (Packet == NULL) {
176 return EFI_OUT_OF_RESOURCES;
177 }
178
179 //
180 // Fill in the IGMP and IP header, then transmit the message
181 //
182 NetbufReserve (Packet, IP4_MAX_HEADLEN);
183
184 Igmp = (IGMP_HEAD *)NetbufAllocSpace (Packet, sizeof (IGMP_HEAD), FALSE);
185 if (Igmp == NULL) {
186 return EFI_OUT_OF_RESOURCES;
187 }
188
189 Igmp->Type = Type;
190 Igmp->MaxRespTime = 0;
191 Igmp->Checksum = 0;
192 Igmp->Group = HTONL (Group);
193 Igmp->Checksum = (UINT16)(~NetblockChecksum ((UINT8 *)Igmp, sizeof (IGMP_HEAD)));
194
195 Head.Tos = 0;
196 Head.Protocol = IP4_PROTO_IGMP;
197 Head.Ttl = 1;
198 Head.Fragment = 0;
199 Head.Dst = Dst;
200 Head.Src = IP4_ALLZERO_ADDRESS;
201
202 return Ip4Output (
203 IpSb,
204 NULL,
205 Packet,
206 &Head,
207 (UINT8 *)&mRouteAlertOption,
208 sizeof (UINT32),
209 IP4_ALLZERO_ADDRESS,
211 NULL
212 );
213}
214
230 IN IP4_SERVICE *IpSb,
231 IN IP4_ADDR Group
232 )
233{
234 if (IpSb->IgmpCtrl.Igmpv1QuerySeen != 0) {
235 return Ip4SendIgmpMessage (IpSb, Group, IGMP_V1_MEMBERSHIP_REPORT, Group);
236 } else {
237 return Ip4SendIgmpMessage (IpSb, Group, IGMP_V2_MEMBERSHIP_REPORT, Group);
238 }
239}
240
254 IN IP4_PROTOCOL *IpInstance,
255 IN IP4_ADDR Address
256 )
257{
259 IP4_SERVICE *IpSb;
260 IGMP_SERVICE_DATA *IgmpCtrl;
261 IGMP_GROUP *Group;
262 EFI_STATUS Status;
263
264 IpSb = IpInstance->Service;
265 IgmpCtrl = &IpSb->IgmpCtrl;
266 Mnp = IpSb->Mnp;
267
268 //
269 // If the IP service already is a member in the group, just
270 // increase the reference count and return.
271 //
272 Group = Ip4FindGroup (IgmpCtrl, Address);
273
274 if (Group != NULL) {
275 Group->RefCnt++;
276 return EFI_SUCCESS;
277 }
278
279 //
280 // Otherwise, create a new IGMP_GROUP, Get the multicast's MAC address,
281 // send a report, then direct MNP to receive the multicast.
282 //
283 Group = AllocatePool (sizeof (IGMP_GROUP));
284
285 if (Group == NULL) {
286 return EFI_OUT_OF_RESOURCES;
287 }
288
289 Group->Address = Address;
290 Group->RefCnt = 1;
291 Group->DelayTime = IGMP_UNSOLICIATED_REPORT;
292 Group->ReportByUs = TRUE;
293
294 Status = Ip4GetMulticastMac (Mnp, Address, &Group->Mac);
295
296 if (EFI_ERROR (Status)) {
297 goto ON_ERROR;
298 }
299
300 Status = Ip4SendIgmpReport (IpSb, Address);
301
302 if (EFI_ERROR (Status)) {
303 goto ON_ERROR;
304 }
305
306 Status = Mnp->Groups (Mnp, TRUE, &Group->Mac);
307
308 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
309 goto ON_ERROR;
310 }
311
312 InsertHeadList (&IgmpCtrl->Groups, &Group->Link);
313 return EFI_SUCCESS;
314
315ON_ERROR:
316 FreePool (Group);
317 return Status;
318}
319
334 IN IP4_PROTOCOL *IpInstance,
335 IN IP4_ADDR Address
336 )
337{
339 IP4_SERVICE *IpSb;
340 IGMP_SERVICE_DATA *IgmpCtrl;
341 IGMP_GROUP *Group;
342 EFI_STATUS Status;
343
344 IpSb = IpInstance->Service;
345 IgmpCtrl = &IpSb->IgmpCtrl;
346 Mnp = IpSb->Mnp;
347
348 Group = Ip4FindGroup (IgmpCtrl, Address);
349
350 if (Group == NULL) {
351 return EFI_NOT_FOUND;
352 }
353
354 //
355 // If more than one instance is in the group, decrease
356 // the RefCnt then return.
357 //
358 if (--Group->RefCnt > 0) {
359 return EFI_SUCCESS;
360 }
361
362 //
363 // If multiple IP4 group addresses are mapped to the same
364 // multicast MAC address, don't configure the MNP to leave
365 // the MAC.
366 //
367 if (Ip4FindMac (IgmpCtrl, &Group->Mac) == 1) {
368 Status = Mnp->Groups (Mnp, FALSE, &Group->Mac);
369
370 if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
371 return Status;
372 }
373 }
374
375 //
376 // Send a leave report if the membership is reported by us
377 // and we are talking IGMPv2.
378 //
379 if (Group->ReportByUs && (IgmpCtrl->Igmpv1QuerySeen == 0)) {
380 Ip4SendIgmpMessage (IpSb, IP4_ALLROUTER_ADDRESS, IGMP_LEAVE_GROUP, Group->Address);
381 }
382
383 RemoveEntryList (&Group->Link);
384 FreePool (Group);
385
386 return EFI_SUCCESS;
387}
388
402 IN IP4_SERVICE *IpSb,
403 IN IP4_HEAD *Head,
404 IN NET_BUF *Packet
405 )
406{
407 IGMP_SERVICE_DATA *IgmpCtrl;
408 IGMP_HEAD Igmp;
409 IGMP_GROUP *Group;
410 IP4_ADDR Address;
411 LIST_ENTRY *Entry;
412
413 IgmpCtrl = &IpSb->IgmpCtrl;
414
415 //
416 // Must checksum over the whole packet, later IGMP version
417 // may employ message longer than 8 bytes. IP's header has
418 // already been trimmed off.
419 //
420 if ((Packet->TotalSize < sizeof (Igmp)) || (NetbufChecksum (Packet) != 0)) {
421 NetbufFree (Packet);
422 return EFI_INVALID_PARAMETER;
423 }
424
425 //
426 // Copy the packet in case it is fragmented
427 //
428 NetbufCopy (Packet, 0, sizeof (IGMP_HEAD), (UINT8 *)&Igmp);
429
430 switch (Igmp.Type) {
431 case IGMP_MEMBERSHIP_QUERY:
432 //
433 // If MaxRespTime is zero, it is most likely that we are
434 // talking to a V1 router
435 //
436 if (Igmp.MaxRespTime == 0) {
437 IgmpCtrl->Igmpv1QuerySeen = IGMP_V1ROUTER_PRESENT;
438 Igmp.MaxRespTime = 100;
439 }
440
441 //
442 // Igmp is ticking once per second but MaxRespTime is in
443 // the unit of 100ms.
444 //
445 Igmp.MaxRespTime /= 10;
446 Address = NTOHL (Igmp.Group);
447
448 if (Address == IP4_ALLSYSTEM_ADDRESS) {
449 break;
450 }
451
452 NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {
453 Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);
454
455 //
456 // If address is all zero, all the memberships will be reported.
457 // otherwise only one is reported.
458 //
459 if ((Address == IP4_ALLZERO_ADDRESS) || (Address == Group->Address)) {
460 //
461 // If the timer is pending, only update it if the time left
462 // is longer than the MaxRespTime. TODO: randomize the DelayTime.
463 //
464 if ((Group->DelayTime == 0) || (Group->DelayTime > Igmp.MaxRespTime)) {
465 Group->DelayTime = MAX (1, Igmp.MaxRespTime);
466 }
467 }
468 }
469
470 break;
471
472 case IGMP_V1_MEMBERSHIP_REPORT:
473 case IGMP_V2_MEMBERSHIP_REPORT:
474 Address = NTOHL (Igmp.Group);
475 Group = Ip4FindGroup (IgmpCtrl, Address);
476
477 if ((Group != NULL) && (Group->DelayTime > 0)) {
478 Group->DelayTime = 0;
479 Group->ReportByUs = FALSE;
480 }
481
482 break;
483 }
484
485 NetbufFree (Packet);
486 return EFI_SUCCESS;
487}
488
500VOID
502 IN IP4_SERVICE *IpSb
503 )
504{
505 IGMP_SERVICE_DATA *IgmpCtrl;
506 LIST_ENTRY *Entry;
507 IGMP_GROUP *Group;
508
509 IgmpCtrl = &IpSb->IgmpCtrl;
510
511 if (IgmpCtrl->Igmpv1QuerySeen > 0) {
512 IgmpCtrl->Igmpv1QuerySeen--;
513 }
514
515 //
516 // Decrease the report timer for each IGMP group in "delaying member"
517 //
518 NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {
519 Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);
520 ASSERT (Group->DelayTime >= 0);
521
522 if (Group->DelayTime > 0) {
523 Group->DelayTime--;
524
525 if (Group->DelayTime == 0) {
526 Ip4SendIgmpReport (IpSb, Group->Address);
527 Group->ReportByUs = TRUE;
528 }
529 }
530 }
531}
532
548IP4_ADDR *
550 IN IP4_ADDR *Source,
551 IN UINT32 Count,
552 IN IP4_ADDR Addr
553 )
554{
555 IP4_ADDR *Groups;
556
557 Groups = AllocatePool (sizeof (IP4_ADDR) * (Count + 1));
558
559 if (Groups == NULL) {
560 return NULL;
561 }
562
563 CopyMem (Groups, Source, Count * sizeof (IP4_ADDR));
564 Groups[Count] = Addr;
565
566 return Groups;
567}
568
583INTN
585 IN OUT IP4_ADDR *Groups,
586 IN UINT32 Count,
587 IN IP4_ADDR Addr
588 )
589{
590 UINT32 Index;
591
592 for (Index = 0; Index < Count; Index++) {
593 if (Groups[Index] == Addr) {
594 break;
595 }
596 }
597
598 while (Index < Count - 1) {
599 Groups[Index] = Groups[Index + 1];
600 Index++;
601 }
602
603 return Index;
604}
INT64 INTN
LIST_ENTRY *EFIAPI InsertHeadList(IN OUT LIST_ENTRY *ListHead, IN OUT LIST_ENTRY *Entry)
Definition: LinkedList.c:218
LIST_ENTRY *EFIAPI RemoveEntryList(IN CONST LIST_ENTRY *Entry)
Definition: LinkedList.c:590
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
VOID EFIAPI FreePool(IN VOID *Buffer)
EFI_STATUS Ip4GetMulticastMac(IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp, IN IP4_ADDR Multicast, OUT EFI_MAC_ADDRESS *Mac)
Definition: Ip4Common.c:218
IGMP_GROUP * Ip4FindGroup(IN IGMP_SERVICE_DATA *IgmpCtrl, IN IP4_ADDR Address)
Definition: Ip4Igmp.c:89
EFI_STATUS Ip4IgmpHandle(IN IP4_SERVICE *IpSb, IN IP4_HEAD *Head, IN NET_BUF *Packet)
Definition: Ip4Igmp.c:401
EFI_STATUS Ip4JoinGroup(IN IP4_PROTOCOL *IpInstance, IN IP4_ADDR Address)
Definition: Ip4Igmp.c:253
VOID Ip4IgmpTicking(IN IP4_SERVICE *IpSb)
Definition: Ip4Igmp.c:501
EFI_STATUS Ip4InitIgmp(IN OUT IP4_SERVICE *IpSb)
Definition: Ip4Igmp.c:29
EFI_STATUS Ip4LeaveGroup(IN IP4_PROTOCOL *IpInstance, IN IP4_ADDR Address)
Definition: Ip4Igmp.c:333
INTN Ip4RemoveGroupAddr(IN OUT IP4_ADDR *Groups, IN UINT32 Count, IN IP4_ADDR Addr)
Definition: Ip4Igmp.c:584
EFI_STATUS Ip4SendIgmpMessage(IN IP4_SERVICE *IpSb, IN IP4_ADDR Dst, IN UINT8 Type, IN IP4_ADDR Group)
Definition: Ip4Igmp.c:159
INTN Ip4FindMac(IN IGMP_SERVICE_DATA *IgmpCtrl, IN EFI_MAC_ADDRESS *Mac)
Definition: Ip4Igmp.c:121
EFI_STATUS Ip4SendIgmpReport(IN IP4_SERVICE *IpSb, IN IP4_ADDR Group)
Definition: Ip4Igmp.c:229
IP4_ADDR * Ip4CombineGroups(IN IP4_ADDR *Source, IN UINT32 Count, IN IP4_ADDR Addr)
Definition: Ip4Igmp.c:549
VOID Ip4SysPacketSent(IP4_PROTOCOL *Ip4Instance, NET_BUF *Packet, EFI_STATUS IoStatus, UINT32 LinkFlag, VOID *Context)
Definition: Ip4Output.c:158
EFI_STATUS Ip4Output(IN IP4_SERVICE *IpSb, IN IP4_PROTOCOL *IpInstance OPTIONAL, IN NET_BUF *Packet, IN IP4_HEAD *Head, IN UINT8 *Option, IN UINT32 OptLen, IN IP4_ADDR GateWay, IN IP4_FRAME_CALLBACK Callback, IN VOID *Context)
Definition: Ip4Output.c:205
#define NULL
Definition: Base.h:319
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define MAX(a, b)
Definition: Base.h:992
VOID EFIAPI NetbufFree(IN NET_BUF *Nbuf)
Definition: NetBuffer.c:195
VOID EFIAPI NetbufReserve(IN OUT NET_BUF *Nbuf, IN UINT32 Len)
Definition: NetBuffer.c:984
UINT16 EFIAPI NetbufChecksum(IN NET_BUF *Nbuf)
Definition: NetBuffer.c:1687
UINT16 EFIAPI NetblockChecksum(IN UINT8 *Bulk, IN UINT32 Len)
Definition: NetBuffer.c:1615
UINT32 EFIAPI NetbufCopy(IN NET_BUF *Nbuf, IN UINT32 Offset, IN UINT32 Len, IN UINT8 *Dest)
Definition: NetBuffer.c:1206
NET_BUF *EFIAPI NetbufAlloc(IN UINT32 Len)
Definition: NetBuffer.c:89
UINT8 *EFIAPI NetbufAllocSpace(IN OUT NET_BUF *Nbuf, IN UINT32 Len, IN BOOLEAN FromHead)
Definition: NetBuffer.c:1015
VOID *EFIAPI AllocatePool(IN UINTN AllocationSize)
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
#define EFI_SUCCESS
Definition: UefiBaseType.h:112