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