]> git.proxmox.com Git - mirror_edk2.git/blob - EdkModulePkg/Universal/Network/PxeBc/Dxe/pxe_bc_igmp.c
6737ede9b870fa92026fa7c1eea53d16bd916881
[mirror_edk2.git] / EdkModulePkg / Universal / Network / PxeBc / Dxe / pxe_bc_igmp.c
1 /*++
2
3 Copyright (c) 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
14
15
16
17 #define RAND_MAX 0x10000
18 #include "bc.h"
19
20 //
21 // Definitions for internet group management protocol version 2 message
22 // structure Per RFC 2236, November 1997
23 //
24 STATIC UINT8 RouterAlertOption[4] = { 0x80 | 20, 4, 0, 0 };
25 STATIC IPV4_ADDR AllRoutersGroup = { { 224, 0, 0, 2 } };
26
27 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
28 STATIC
29 VOID
30 ClearGroupTimer (
31 PXE_BASECODE_DEVICE *Private,
32 UINTN TimerId
33 )
34 {
35 if (Private == NULL) {
36 return ;
37 }
38
39 if (TimerId >= Private->MCastGroupCount) {
40 return ;
41 }
42
43 if (Private->IgmpGroupEvent[TimerId] == NULL) {
44 return ;
45 }
46
47 gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]);
48 Private->IgmpGroupEvent[TimerId] = NULL;
49 }
50
51 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
52 STATIC
53 VOID
54 SetGroupTimer (
55 PXE_BASECODE_DEVICE *Private,
56 UINTN TimerId,
57 UINTN MaxRespTime
58 )
59 /*++
60 Routine description:
61 Set IGMP response timeout value.
62
63 Parameters:
64 Private := Pointer to PxeBc interface
65 TimerId := Timer ID#
66 MaxRespTime := Base response timeout value in tenths of seconds
67
68 Returns:
69 --*/
70 {
71 EFI_STATUS EfiStatus;
72
73 if (Private == NULL) {
74 return ;
75 }
76
77 if (TimerId >= Private->MCastGroupCount) {
78 return ;
79 }
80
81 if (Private->IgmpGroupEvent[TimerId] != NULL) {
82 gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]);
83 }
84
85 EfiStatus = gBS->CreateEvent (
86 EFI_EVENT_TIMER,
87 EFI_TPL_CALLBACK,
88 NULL,
89 NULL,
90 &Private->IgmpGroupEvent[TimerId]
91 );
92
93 if (EFI_ERROR (EfiStatus)) {
94 Private->IgmpGroupEvent[TimerId] = NULL;
95 return ;
96 }
97
98 EfiStatus = gBS->SetTimer (
99 Private->IgmpGroupEvent[TimerId],
100 TimerRelative,
101 MaxRespTime * 1000000 + Random (Private) % RAND_MAX
102 );
103
104 if (EFI_ERROR (EfiStatus)) {
105 gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]);
106 Private->IgmpGroupEvent[TimerId] = NULL;
107 }
108 }
109
110 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
111 STATIC
112 VOID
113 SendIgmpMessage (
114 PXE_BASECODE_DEVICE *Private,
115 UINT8 Type,
116 INTN GroupId
117 )
118 /*++
119 Routine description:
120 Send an IGMP message
121
122 Parameters:
123 Private := Pointer to PxeBc interface
124 Type := Message type opcode
125 GroupId := Group ID#
126
127 Returns:
128 --*/
129 {
130 Private->IgmpMessage.Type = Type;
131 Private->IgmpMessage.MaxRespTime = 0;
132 Private->IgmpMessage.Checksum = 0;
133 Private->IgmpMessage.GroupAddress = Private->MCastGroup[GroupId];
134 Private->IgmpMessage.Checksum = IpChecksum (
135 (UINT16 *) &Private->IgmpMessage,
136 sizeof Private->IgmpMessage
137 );
138
139 Ipv4SendWOp (
140 Private,
141 0,
142 (UINT8 *) &Private->IgmpMessage,
143 sizeof Private->IgmpMessage,
144 PROT_IGMP,
145 RouterAlertOption,
146 sizeof RouterAlertOption,
147 ((Type == IGMP_TYPE_LEAVE_GROUP) ? AllRoutersGroup.L : Private->IgmpMessage.GroupAddress),
148 EFI_PXE_BASE_CODE_FUNCTION_IGMP
149 );
150 }
151
152 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
153 STATIC
154 VOID
155 ReportIgmp (
156 PXE_BASECODE_DEVICE *Private,
157 INTN GroupId
158 )
159 /*++
160 Routine description:
161 Send an IGMP report message.
162
163 Parameters:
164 Private := Pointer to PxeBc interface
165 GroupId := Group ID#
166
167 Returns:
168 --*/
169 {
170 //
171 // if version 1 querier, send v1 report
172 //
173 UINT8 Type;
174
175 if (Private->Igmpv1TimeoutEvent != NULL) {
176 if (!EFI_ERROR (gBS->CheckEvent (Private->Igmpv1TimeoutEvent))) {
177 gBS->CloseEvent (Private->Igmpv1TimeoutEvent);
178 Private->Igmpv1TimeoutEvent = NULL;
179 Private->UseIgmpv1Reporting = TRUE;
180 }
181 }
182
183 Type = (UINT8) (Private->UseIgmpv1Reporting ? IGMP_TYPE_V1REPORT : IGMP_TYPE_REPORT);
184
185 SendIgmpMessage (Private, Type, GroupId);
186 ClearGroupTimer (Private, GroupId);
187 }
188
189 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
190 VOID
191 IgmpCheckTimers (
192 PXE_BASECODE_DEVICE *Private
193 )
194 /*++
195 Routine description:
196 Check IGMP timers and send reports for all groups that have expired.
197 Parameters:
198 Private := Pointer to PxeBc interface
199
200 Returns:
201 --*/
202 {
203 UINTN GroupId;
204
205 if (Private == NULL) {
206 return ;
207 }
208
209 for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) {
210 if (Private->IgmpGroupEvent[GroupId] == NULL) {
211 continue;
212 }
213
214 if (!EFI_ERROR (gBS->CheckEvent (Private->IgmpGroupEvent[GroupId]))) {
215 //
216 // send a report
217 //
218 ReportIgmp (Private, GroupId);
219 }
220 }
221 }
222
223 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
224 STATIC
225 INTN
226 FindMulticastGroup (
227 PXE_BASECODE_DEVICE *Private,
228 UINT32 GroupAddress
229 )
230 /*++
231 Routine description:
232 Fund group ID# (index).
233
234 Parameters:
235 Private := Pointer to PxeBc interface
236 GroupAddress := Group multicast address
237
238 Returns:
239 0 := Group not found
240 other := Group ID#
241 --*/
242 {
243 UINTN GroupId;
244
245 for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) {
246 if (Private->MCastGroup[GroupId] == GroupAddress) {
247 return GroupId + 1;
248 }
249 }
250
251 return 0;
252 }
253
254 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
255 VOID
256 IgmpJoinGroup (
257 PXE_BASECODE_DEVICE *Private,
258 EFI_IP_ADDRESS *GroupPtr
259 )
260 /*++
261 Routine description:
262 Join multicast group.
263
264 Parameters:
265 Private := Pointer to PxeBc interface
266 GroupPtr := Pointer to group mutlicast IP address.
267
268 Returns:
269 --*/
270 {
271 UINT32 Grp;
272
273 Grp = *(UINT32 *) GroupPtr;
274
275 #if SUPPORT_IPV6
276 if (Private->EfiBc.Mode->UsingIpv6) {
277 //
278 // TBD
279 //
280 }
281 #endif
282 //
283 // see if we already have it or if we can't take anymore
284 //
285 if (FindMulticastGroup (Private, Grp) || Private->MCastGroupCount == MAX_MCAST_GROUPS) {
286 return ;
287 }
288 //
289 // add the group
290 //
291 Private->MCastGroup[Private->MCastGroupCount] = Grp;
292
293 ReportIgmp (Private, Private->MCastGroupCount);
294 //
295 // send a report
296 // so it will get sent again per RFC 2236
297 //
298 SetGroupTimer (
299 Private,
300 Private->MCastGroupCount++,
301 UNSOLICITED_REPORT_INTERVAL * 10
302 );
303 }
304
305 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
306 VOID
307 IgmpLeaveGroup (
308 PXE_BASECODE_DEVICE *Private,
309 EFI_IP_ADDRESS *GroupPtr
310 )
311 /*++
312 Routine description:
313 Leave multicast group.
314
315 Parameters:
316 Private := Pointer to PxeBc interface
317 GroupPtr := Mutlicast group IP address.
318
319 Returns:
320 --*/
321 {
322 UINT32 Grp;
323 UINTN GroupId;
324
325 Grp = *(UINT32 *) GroupPtr;
326
327 #if SUPPORT_IPV6
328 if (Private->EfiBc.Mode->UsingIpv6) {
329 //
330 // TBD
331 //
332 }
333 #endif
334 //
335 // if not in group, ignore
336 //
337 GroupId = FindMulticastGroup (Private, Grp);
338
339 if (GroupId == 0) {
340 return ;
341 }
342 //
343 // if not v1 querrier, send leave group IGMP message
344 //
345 if (Private->Igmpv1TimeoutEvent != NULL) {
346 if (!EFI_ERROR (gBS->CheckEvent (Private->Igmpv1TimeoutEvent))) {
347 gBS->CloseEvent (Private->Igmpv1TimeoutEvent);
348 Private->Igmpv1TimeoutEvent = NULL;
349 Private->UseIgmpv1Reporting = TRUE;
350 } else {
351 SendIgmpMessage (Private, IGMP_TYPE_LEAVE_GROUP, GroupId - 1);
352 }
353 }
354
355 while (GroupId < Private->MCastGroupCount) {
356 Private->MCastGroup[GroupId - 1] = Private->MCastGroup[GroupId];
357 Private->IgmpGroupEvent[GroupId - 1] = Private->IgmpGroupEvent[GroupId];
358 ++GroupId;
359 }
360
361 --Private->MCastGroupCount;
362 }
363
364 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
365 VOID
366 HandleIgmp (
367 PXE_BASECODE_DEVICE *Private,
368 IGMPV2_MESSAGE *IgmpMessagePtr,
369 UINTN IgmpLength
370 )
371 /*++
372 Routine description:
373 Handle received IGMP packet
374
375 Parameters:
376 Private := Pointer to PxeBc interface
377 IgmpMessagePtr := Pointer to IGMP packet
378 IgmpLength := packet length in bytes
379
380 Returns:
381 --*/
382 {
383 EFI_STATUS EfiStatus;
384 UINTN GroupId;
385 INTN MaxRespTime;
386
387 if (Private == NULL) {
388 return ;
389 }
390
391 if (Private->MCastGroupCount == 0) {
392 //
393 // if we don't belong to any multicast groups, ignore
394 //
395 return ;
396 }
397 //
398 // verify checksum
399 //
400 if (IpChecksum ((UINT16 *) IgmpMessagePtr, IgmpLength)) {
401 //
402 // bad checksum - ignore packet
403 //
404 return ;
405 }
406
407 switch (IgmpMessagePtr->Type) {
408 case IGMP_TYPE_QUERY:
409 //
410 // if a version 1 querier, note the fact and set max resp time
411 //
412 MaxRespTime = IgmpMessagePtr->MaxRespTime;
413
414 if (MaxRespTime == 0) {
415 Private->UseIgmpv1Reporting = TRUE;
416
417 if (Private->Igmpv1TimeoutEvent != NULL) {
418 gBS->CloseEvent (Private->Igmpv1TimeoutEvent);
419 }
420
421 EfiStatus = gBS->CreateEvent (
422 EFI_EVENT_TIMER,
423 EFI_TPL_CALLBACK,
424 NULL,
425 NULL,
426 &Private->Igmpv1TimeoutEvent
427 );
428
429 if (EFI_ERROR (EfiStatus)) {
430 Private->Igmpv1TimeoutEvent = NULL;
431 } else {
432 EfiStatus = gBS->SetTimer (
433 Private->Igmpv1TimeoutEvent,
434 TimerRelative,
435 (UINT64) V1ROUTER_PRESENT_TIMEOUT * 10000000
436 );
437 }
438
439 MaxRespTime = IGMP_DEFAULT_MAX_RESPONSE_TIME * 10;
440 }
441 //
442 // if a general query (!GroupAddress), set all our group timers
443 //
444 if (!IgmpMessagePtr->GroupAddress) {
445 for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) {
446 SetGroupTimer (Private, GroupId, MaxRespTime);
447 }
448 } else {
449 //
450 // specific query - set only specific group
451 //
452 GroupId = FindMulticastGroup (Private, IgmpMessagePtr->GroupAddress);
453
454 if (GroupId != 0) {
455 SetGroupTimer (Private, GroupId - 1, MaxRespTime);
456 }
457 }
458
459 break;
460
461 //
462 // if we have a timer running for this group, clear it
463 //
464 case IGMP_TYPE_V1REPORT:
465 case IGMP_TYPE_REPORT:
466 GroupId = FindMulticastGroup (Private, IgmpMessagePtr->GroupAddress);
467
468 if (GroupId != 0) {
469 ClearGroupTimer (Private, GroupId - 1);
470 }
471
472 break;
473 }
474 }
475
476 /* EOF - pxe_bc_igmp.c */