]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Network/PxeBcDxe/Pxe_bc_igmp.c
c5088dff326322877c0eb9ac6b5003760cf4ed3c
[mirror_edk2.git] / MdeModulePkg / Universal / Network / PxeBcDxe / Pxe_bc_igmp.c
1 /** @file
2
3 Copyright (c) 2004, 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 #define RAND_MAX 0x10000
17
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
53 /**
54
55
56 **/
57 STATIC
58 VOID
59 SetGroupTimer (
60 PXE_BASECODE_DEVICE *Private,
61 UINTN TimerId,
62 UINTN MaxRespTime
63 )
64 {
65 EFI_STATUS EfiStatus;
66
67 if (Private == NULL) {
68 return ;
69 }
70
71 if (TimerId >= Private->MCastGroupCount) {
72 return ;
73 }
74
75 if (Private->IgmpGroupEvent[TimerId] != NULL) {
76 gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]);
77 }
78
79 EfiStatus = gBS->CreateEvent (
80 EVT_TIMER,
81 TPL_CALLBACK,
82 NULL,
83 NULL,
84 &Private->IgmpGroupEvent[TimerId]
85 );
86
87 if (EFI_ERROR (EfiStatus)) {
88 Private->IgmpGroupEvent[TimerId] = NULL;
89 return ;
90 }
91
92 EfiStatus = gBS->SetTimer (
93 Private->IgmpGroupEvent[TimerId],
94 TimerRelative,
95 MaxRespTime * 1000000 + Random (Private) % RAND_MAX
96 );
97
98 if (EFI_ERROR (EfiStatus)) {
99 gBS->CloseEvent (Private->IgmpGroupEvent[TimerId]);
100 Private->IgmpGroupEvent[TimerId] = NULL;
101 }
102 }
103
104 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
105
106 /**
107
108
109 **/
110 STATIC
111 VOID
112 SendIgmpMessage (
113 PXE_BASECODE_DEVICE *Private,
114 UINT8 Type,
115 INTN GroupId
116 )
117 {
118 Private->IgmpMessage.Type = Type;
119 Private->IgmpMessage.MaxRespTime = 0;
120 Private->IgmpMessage.Checksum = 0;
121 Private->IgmpMessage.GroupAddress = Private->MCastGroup[GroupId];
122 Private->IgmpMessage.Checksum = IpChecksum (
123 (UINT16 *) &Private->IgmpMessage,
124 sizeof Private->IgmpMessage
125 );
126
127 Ipv4SendWOp (
128 Private,
129 0,
130 (UINT8 *) &Private->IgmpMessage,
131 sizeof Private->IgmpMessage,
132 PROT_IGMP,
133 RouterAlertOption,
134 sizeof RouterAlertOption,
135 ((Type == IGMP_TYPE_LEAVE_GROUP) ? AllRoutersGroup.L : Private->IgmpMessage.GroupAddress),
136 EFI_PXE_BASE_CODE_FUNCTION_IGMP
137 );
138 }
139
140 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
141
142 /**
143
144
145 **/
146 STATIC
147 VOID
148 ReportIgmp (
149 PXE_BASECODE_DEVICE *Private,
150 INTN GroupId
151 )
152 {
153 //
154 // if version 1 querier, send v1 report
155 //
156 UINT8 Type;
157
158 if (Private->Igmpv1TimeoutEvent != NULL) {
159 if (!EFI_ERROR (gBS->CheckEvent (Private->Igmpv1TimeoutEvent))) {
160 gBS->CloseEvent (Private->Igmpv1TimeoutEvent);
161 Private->Igmpv1TimeoutEvent = NULL;
162 Private->UseIgmpv1Reporting = TRUE;
163 }
164 }
165
166 Type = (UINT8) (Private->UseIgmpv1Reporting ? IGMP_TYPE_V1REPORT : IGMP_TYPE_REPORT);
167
168 SendIgmpMessage (Private, Type, GroupId);
169 ClearGroupTimer (Private, GroupId);
170 }
171
172 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
173
174 /**
175
176
177 **/
178 VOID
179 IgmpCheckTimers (
180 PXE_BASECODE_DEVICE *Private
181 )
182 {
183 UINTN GroupId;
184
185 if (Private == NULL) {
186 return ;
187 }
188
189 for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) {
190 if (Private->IgmpGroupEvent[GroupId] == NULL) {
191 continue;
192 }
193
194 if (!EFI_ERROR (gBS->CheckEvent (Private->IgmpGroupEvent[GroupId]))) {
195 //
196 // send a report
197 //
198 ReportIgmp (Private, GroupId);
199 }
200 }
201 }
202
203 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
204
205 /**
206
207 @return 0 := Group not found
208 @return other := Group ID#
209
210 **/
211 STATIC
212 INTN
213 FindMulticastGroup (
214 PXE_BASECODE_DEVICE *Private,
215 UINT32 GroupAddress
216 )
217 {
218 UINTN GroupId;
219
220 for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) {
221 if (Private->MCastGroup[GroupId] == GroupAddress) {
222 return GroupId + 1;
223 }
224 }
225
226 return 0;
227 }
228
229 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
230
231 /**
232
233
234 **/
235 VOID
236 IgmpJoinGroup (
237 PXE_BASECODE_DEVICE *Private,
238 EFI_IP_ADDRESS *GroupPtr
239 )
240 {
241 UINT32 Grp;
242
243 Grp = *(UINT32 *) GroupPtr;
244
245 //
246 // see if we already have it or if we can't take anymore
247 //
248 if (FindMulticastGroup (Private, Grp) || Private->MCastGroupCount == MAX_MCAST_GROUPS) {
249 return ;
250 }
251 //
252 // add the group
253 //
254 Private->MCastGroup[Private->MCastGroupCount] = Grp;
255
256 ReportIgmp (Private, Private->MCastGroupCount);
257 //
258 // send a report
259 // so it will get sent again per RFC 2236
260 //
261 SetGroupTimer (
262 Private,
263 Private->MCastGroupCount++,
264 UNSOLICITED_REPORT_INTERVAL * 10
265 );
266 }
267
268 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
269
270 /**
271
272
273 **/
274 VOID
275 IgmpLeaveGroup (
276 PXE_BASECODE_DEVICE *Private,
277 EFI_IP_ADDRESS *GroupPtr
278 )
279 {
280 UINT32 Grp;
281 UINTN GroupId;
282
283 Grp = *(UINT32 *) GroupPtr;
284
285 //
286 // if not in group, ignore
287 //
288 GroupId = FindMulticastGroup (Private, Grp);
289
290 if (GroupId == 0) {
291 return ;
292 }
293 //
294 // if not v1 querrier, send leave group IGMP message
295 //
296 if (Private->Igmpv1TimeoutEvent != NULL) {
297 if (!EFI_ERROR (gBS->CheckEvent (Private->Igmpv1TimeoutEvent))) {
298 gBS->CloseEvent (Private->Igmpv1TimeoutEvent);
299 Private->Igmpv1TimeoutEvent = NULL;
300 Private->UseIgmpv1Reporting = TRUE;
301 } else {
302 SendIgmpMessage (Private, IGMP_TYPE_LEAVE_GROUP, GroupId - 1);
303 }
304 }
305
306 while (GroupId < Private->MCastGroupCount) {
307 Private->MCastGroup[GroupId - 1] = Private->MCastGroup[GroupId];
308 Private->IgmpGroupEvent[GroupId - 1] = Private->IgmpGroupEvent[GroupId];
309 ++GroupId;
310 }
311
312 --Private->MCastGroupCount;
313 }
314
315 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
316
317 /**
318
319
320 **/
321 VOID
322 HandleIgmp (
323 PXE_BASECODE_DEVICE *Private,
324 IGMPV2_MESSAGE *IgmpMessagePtr,
325 UINTN IgmpLength
326 )
327 {
328 EFI_STATUS EfiStatus;
329 UINTN GroupId;
330 INTN MaxRespTime;
331
332 if (Private == NULL) {
333 return ;
334 }
335
336 if (Private->MCastGroupCount == 0) {
337 //
338 // if we don't belong to any multicast groups, ignore
339 //
340 return ;
341 }
342 //
343 // verify checksum
344 //
345 if (IpChecksum ((UINT16 *) IgmpMessagePtr, IgmpLength)) {
346 //
347 // bad checksum - ignore packet
348 //
349 return ;
350 }
351
352 switch (IgmpMessagePtr->Type) {
353 case IGMP_TYPE_QUERY:
354 //
355 // if a version 1 querier, note the fact and set max resp time
356 //
357 MaxRespTime = IgmpMessagePtr->MaxRespTime;
358
359 if (MaxRespTime == 0) {
360 Private->UseIgmpv1Reporting = TRUE;
361
362 if (Private->Igmpv1TimeoutEvent != NULL) {
363 gBS->CloseEvent (Private->Igmpv1TimeoutEvent);
364 }
365
366 EfiStatus = gBS->CreateEvent (
367 EVT_TIMER,
368 TPL_CALLBACK,
369 NULL,
370 NULL,
371 &Private->Igmpv1TimeoutEvent
372 );
373
374 if (EFI_ERROR (EfiStatus)) {
375 Private->Igmpv1TimeoutEvent = NULL;
376 } else {
377 EfiStatus = gBS->SetTimer (
378 Private->Igmpv1TimeoutEvent,
379 TimerRelative,
380 (UINT64) V1ROUTER_PRESENT_TIMEOUT * 10000000
381 );
382 }
383
384 MaxRespTime = IGMP_DEFAULT_MAX_RESPONSE_TIME * 10;
385 }
386 //
387 // if a general query (!GroupAddress), set all our group timers
388 //
389 if (!IgmpMessagePtr->GroupAddress) {
390 for (GroupId = 0; GroupId < Private->MCastGroupCount; ++GroupId) {
391 SetGroupTimer (Private, GroupId, MaxRespTime);
392 }
393 } else {
394 //
395 // specific query - set only specific group
396 //
397 GroupId = FindMulticastGroup (Private, IgmpMessagePtr->GroupAddress);
398
399 if (GroupId != 0) {
400 SetGroupTimer (Private, GroupId - 1, MaxRespTime);
401 }
402 }
403
404 break;
405
406 //
407 // if we have a timer running for this group, clear it
408 //
409 case IGMP_TYPE_V1REPORT:
410 case IGMP_TYPE_REPORT:
411 GroupId = FindMulticastGroup (Private, IgmpMessagePtr->GroupAddress);
412
413 if (GroupId != 0) {
414 ClearGroupTimer (Private, GroupId - 1);
415 }
416
417 break;
418 }
419 }
420
421 /* EOF - pxe_bc_igmp.c */